From 943b60476782740d69dc66049bb27016980166f3 Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Fri, 19 Nov 2021 11:23:11 -0800 Subject: [PATCH 001/600] Bump plugin Android compileSdkVersions to 31 (#4502) --- packages/android_alarm_manager/CHANGELOG.md | 1 + .../example/android/app/build.gradle | 2 +- packages/android_intent/CHANGELOG.md | 1 + .../example/android/app/build.gradle | 2 +- packages/battery/battery/CHANGELOG.md | 1 + .../battery/example/android/app/build.gradle | 2 +- .../connectivity/connectivity/CHANGELOG.md | 1 + .../example/android/app/build.gradle | 2 +- packages/device_info/device_info/CHANGELOG.md | 4 ++ .../example/android/app/build.gradle | 2 +- packages/espresso/CHANGELOG.md | 4 ++ .../espresso/example/android/app/build.gradle | 2 +- .../CHANGELOG.md | 4 ++ .../example/android/app/build.gradle | 2 +- .../google_maps_flutter/CHANGELOG.md | 4 ++ .../example/android/app/build.gradle | 2 +- .../google_sign_in/CHANGELOG.md | 4 ++ .../example/android/app/build.gradle | 2 +- .../image_picker/image_picker/CHANGELOG.md | 1 + .../example/android/app/build.gradle | 2 +- .../in_app_purchase/CHANGELOG.md | 2 + .../example/android/app/build.gradle | 2 +- .../in_app_purchase_android/CHANGELOG.md | 4 ++ .../example/android/app/build.gradle | 2 +- packages/local_auth/CHANGELOG.md | 4 ++ .../example/android/app/build.gradle | 2 +- packages/package_info/CHANGELOG.md | 2 +- .../example/android/app/build.gradle | 2 +- .../path_provider/path_provider/CHANGELOG.md | 4 ++ .../example/android/app/build.gradle | 2 +- .../path_provider_android/CHANGELOG.md | 5 ++ .../pathprovider/PathProviderPlugin.java | 4 +- .../example/android/app/build.gradle | 2 +- .../path_provider_android/pubspec.yaml | 2 +- .../quick_actions/quick_actions/CHANGELOG.md | 5 ++ .../quickactions/MethodCallHandlerImpl.java | 55 +++++++++++++++++-- .../example/android/app/build.gradle | 2 +- .../quick_actions/quick_actions/pubspec.yaml | 2 +- packages/sensors/CHANGELOG.md | 1 + .../sensors/example/android/app/build.gradle | 2 +- packages/share/CHANGELOG.md | 1 + .../share/example/android/app/build.gradle | 2 +- .../shared_preferences/CHANGELOG.md | 2 + .../example/android/app/build.gradle | 2 +- .../url_launcher/url_launcher/CHANGELOG.md | 4 ++ .../example/android/app/build.gradle | 2 +- .../video_player/video_player/CHANGELOG.md | 4 ++ .../example/android/app/build.gradle | 2 +- .../webview_flutter/CHANGELOG.md | 4 ++ .../example/android/app/build.gradle | 2 +- .../webview_flutter_android/CHANGELOG.md | 4 ++ .../example/android/app/build.gradle | 2 +- .../wifi_info_flutter/CHANGELOG.md | 1 + .../example/android/app/build.gradle | 2 +- 54 files changed, 151 insertions(+), 34 deletions(-) diff --git a/packages/android_alarm_manager/CHANGELOG.md b/packages/android_alarm_manager/CHANGELOG.md index 2c067a31326a..7d90afbe4de2 100644 --- a/packages/android_alarm_manager/CHANGELOG.md +++ b/packages/android_alarm_manager/CHANGELOG.md @@ -3,6 +3,7 @@ * Remove support for the V1 Android embedding. * Updated Android lint settings. * Removed `-Werror` in Android builds. +* Updates example app Android compileSdkVersion to 31. ## 2.0.2 diff --git a/packages/android_alarm_manager/example/android/app/build.gradle b/packages/android_alarm_manager/example/android/app/build.gradle index 9722ec280205..4efe095b7760 100644 --- a/packages/android_alarm_manager/example/android/app/build.gradle +++ b/packages/android_alarm_manager/example/android/app/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 29 + compileSdkVersion 31 lintOptions { disable 'InvalidPackage' diff --git a/packages/android_intent/CHANGELOG.md b/packages/android_intent/CHANGELOG.md index c30b4839f4c6..7bad13fc3dbd 100644 --- a/packages/android_intent/CHANGELOG.md +++ b/packages/android_intent/CHANGELOG.md @@ -4,6 +4,7 @@ * Updated Android lint settings. * Specify Java 8 for Android build. * Removed `-Werror` in Android builds. +* Updates example app Android compileSdkVersion to 31. ## 2.0.2 diff --git a/packages/android_intent/example/android/app/build.gradle b/packages/android_intent/example/android/app/build.gradle index a309dc2e2d5c..3591be7c7943 100644 --- a/packages/android_intent/example/android/app/build.gradle +++ b/packages/android_intent/example/android/app/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 29 + compileSdkVersion 31 lintOptions { disable 'InvalidPackage' diff --git a/packages/battery/battery/CHANGELOG.md b/packages/battery/battery/CHANGELOG.md index d78c5d3eaf23..f55da97d9f90 100644 --- a/packages/battery/battery/CHANGELOG.md +++ b/packages/battery/battery/CHANGELOG.md @@ -2,6 +2,7 @@ * Remove references to the Android v1 embedding. * Updated Android lint settings. +* Updates example app Android compileSdkVersion to 31. * Recreated Android example from current template. ## 2.0.3 diff --git a/packages/battery/battery/example/android/app/build.gradle b/packages/battery/battery/example/android/app/build.gradle index 4fcd6ba0049e..1b529a52c76c 100644 --- a/packages/battery/battery/example/android/app/build.gradle +++ b/packages/battery/battery/example/android/app/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 29 + compileSdkVersion 31 lintOptions { disable 'InvalidPackage' diff --git a/packages/connectivity/connectivity/CHANGELOG.md b/packages/connectivity/connectivity/CHANGELOG.md index 81d2eff08006..ce65faf4c3fb 100644 --- a/packages/connectivity/connectivity/CHANGELOG.md +++ b/packages/connectivity/connectivity/CHANGELOG.md @@ -4,6 +4,7 @@ * Updated Android lint settings. * Specify Java 8 for Android build. * Removed `-Werror` in Android builds. +* Updates example app Android compileSdkVersion to 31. ## 3.0.6 diff --git a/packages/connectivity/connectivity/example/android/app/build.gradle b/packages/connectivity/connectivity/example/android/app/build.gradle index 99e360558af8..209178fcd798 100644 --- a/packages/connectivity/connectivity/example/android/app/build.gradle +++ b/packages/connectivity/connectivity/example/android/app/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 29 + compileSdkVersion 31 lintOptions { disable 'InvalidPackage' diff --git a/packages/device_info/device_info/CHANGELOG.md b/packages/device_info/device_info/CHANGELOG.md index cdcb906da2fa..a9c3b7a1dffe 100644 --- a/packages/device_info/device_info/CHANGELOG.md +++ b/packages/device_info/device_info/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates example app Android compileSdkVersion to 31. + ## 2.0.3 * Remove references to the Android V1 embedding. diff --git a/packages/device_info/device_info/example/android/app/build.gradle b/packages/device_info/device_info/example/android/app/build.gradle index eb0c628330be..9371207aba2d 100644 --- a/packages/device_info/device_info/example/android/app/build.gradle +++ b/packages/device_info/device_info/example/android/app/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 29 + compileSdkVersion 31 lintOptions { disable 'InvalidPackage' diff --git a/packages/espresso/CHANGELOG.md b/packages/espresso/CHANGELOG.md index 88976c88b668..c2afa54780f5 100644 --- a/packages/espresso/CHANGELOG.md +++ b/packages/espresso/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates example app Android compileSdkVersion to 31. + ## 0.1.0+4 * Updated Android lint settings. diff --git a/packages/espresso/example/android/app/build.gradle b/packages/espresso/example/android/app/build.gradle index 6def13f65898..5ed5eedb0175 100644 --- a/packages/espresso/example/android/app/build.gradle +++ b/packages/espresso/example/android/app/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 29 + compileSdkVersion 31 lintOptions { disable 'InvalidPackage' diff --git a/packages/flutter_plugin_android_lifecycle/CHANGELOG.md b/packages/flutter_plugin_android_lifecycle/CHANGELOG.md index 27cca016960e..414cf8e1aeda 100644 --- a/packages/flutter_plugin_android_lifecycle/CHANGELOG.md +++ b/packages/flutter_plugin_android_lifecycle/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates example app Android compileSdkVersion to 31. + ## 2.0.4 * Updated Android lint settings. diff --git a/packages/flutter_plugin_android_lifecycle/example/android/app/build.gradle b/packages/flutter_plugin_android_lifecycle/example/android/app/build.gradle index da10d611c704..bc87d03f1aeb 100644 --- a/packages/flutter_plugin_android_lifecycle/example/android/app/build.gradle +++ b/packages/flutter_plugin_android_lifecycle/example/android/app/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 29 + compileSdkVersion 31 lintOptions { disable 'InvalidPackage' diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index 1171dcbf25ae..b5e739aa01ee 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates example app Android compileSdkVersion to 31. + ## 2.1.1 * Suppresses unchecked cast warning. diff --git a/packages/google_maps_flutter/google_maps_flutter/example/android/app/build.gradle b/packages/google_maps_flutter/google_maps_flutter/example/android/app/build.gradle index d850810db651..e46f18b41c22 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/android/app/build.gradle +++ b/packages/google_maps_flutter/google_maps_flutter/example/android/app/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 29 + compileSdkVersion 31 lintOptions { disable 'InvalidPackage' diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index b16b5f9f5e79..739983b2916d 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates example app Android compileSdkVersion to 31. + ## 5.2.1 Change the placeholder of the GoogleUserCircleAvatar to a transparent image. diff --git a/packages/google_sign_in/google_sign_in/example/android/app/build.gradle b/packages/google_sign_in/google_sign_in/example/android/app/build.gradle index 5d574a2c6a51..8ac99fe56f3a 100644 --- a/packages/google_sign_in/google_sign_in/example/android/app/build.gradle +++ b/packages/google_sign_in/google_sign_in/example/android/app/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 29 + compileSdkVersion 31 lintOptions { disable 'InvalidPackage' diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index 23871aad6b23..688cee3eed01 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates example app Android compileSdkVersion to 31. * Fix iOS RunnerUITests search paths. ## 0.8.4+4 diff --git a/packages/image_picker/image_picker/example/android/app/build.gradle b/packages/image_picker/image_picker/example/android/app/build.gradle index f7fbaae4c9fd..e73e3fe01003 100755 --- a/packages/image_picker/image_picker/example/android/app/build.gradle +++ b/packages/image_picker/image_picker/example/android/app/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 29 + compileSdkVersion 31 testOptions.unitTests.includeAndroidResources = true lintOptions { diff --git a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md index b8fc7f248b9b..6b615fe7470d 100644 --- a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md @@ -1,5 +1,7 @@ ## NEXT +* Updates example app Android compileSdkVersion to 31. + * **BREAKING CHANGES**: * Renames `in_app_purchase_ios` to `in_app_purchase_storekit`. * Renames `InAppPurchaseIosPlatform` to `InAppPurchaseStoreKitPlatform`. diff --git a/packages/in_app_purchase/in_app_purchase/example/android/app/build.gradle b/packages/in_app_purchase/in_app_purchase/example/android/app/build.gradle index c95804685219..772b8839be59 100644 --- a/packages/in_app_purchase/in_app_purchase/example/android/app/build.gradle +++ b/packages/in_app_purchase/in_app_purchase/example/android/app/build.gradle @@ -63,7 +63,7 @@ android { } } - compileSdkVersion 29 + compileSdkVersion 31 lintOptions { disable 'InvalidPackage' diff --git a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md index 78aa8db43bcc..7724dd3d7f14 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates example app Android compileSdkVersion to 31. + ## 0.2.0 * BREAKING CHANGE : Refactor to handle new `PurchaseStatus` named `canceled`. This means developers diff --git a/packages/in_app_purchase/in_app_purchase_android/example/android/app/build.gradle b/packages/in_app_purchase/in_app_purchase_android/example/android/app/build.gradle index 373d4a87c6e6..8ddcaf74ef47 100644 --- a/packages/in_app_purchase/in_app_purchase_android/example/android/app/build.gradle +++ b/packages/in_app_purchase/in_app_purchase_android/example/android/app/build.gradle @@ -63,7 +63,7 @@ android { } } - compileSdkVersion 29 + compileSdkVersion 31 lintOptions { disable 'InvalidPackage' diff --git a/packages/local_auth/CHANGELOG.md b/packages/local_auth/CHANGELOG.md index f4129f77f5d4..a824d8a6e0ea 100644 --- a/packages/local_auth/CHANGELOG.md +++ b/packages/local_auth/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates example app Android compileSdkVersion to 31. + ## 1.1.8 * Update minimum Flutter SDK to 2.5 and iOS deployment target to 9.0. diff --git a/packages/local_auth/example/android/app/build.gradle b/packages/local_auth/example/android/app/build.gradle index 34b3fe2d69e3..d1cef4bf53a9 100644 --- a/packages/local_auth/example/android/app/build.gradle +++ b/packages/local_auth/example/android/app/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 29 + compileSdkVersion 31 lintOptions { disable 'InvalidPackage' diff --git a/packages/package_info/CHANGELOG.md b/packages/package_info/CHANGELOG.md index 7abe1c2694e1..3cf675170513 100644 --- a/packages/package_info/CHANGELOG.md +++ b/packages/package_info/CHANGELOG.md @@ -2,7 +2,7 @@ * Remove references to the Android v1 embedding. * Updated Android lint settings. -* Update Android compileSdkVersion to 30. +* Updates example app Android compileSdkVersion to 31. ## 2.0.2 diff --git a/packages/package_info/example/android/app/build.gradle b/packages/package_info/example/android/app/build.gradle index 0c55bdb1ef31..60fc74107b0f 100644 --- a/packages/package_info/example/android/app/build.gradle +++ b/packages/package_info/example/android/app/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 30 + compileSdkVersion 31 lintOptions { disable 'InvalidPackage' diff --git a/packages/path_provider/path_provider/CHANGELOG.md b/packages/path_provider/path_provider/CHANGELOG.md index 28c233266042..237815de6ce4 100644 --- a/packages/path_provider/path_provider/CHANGELOG.md +++ b/packages/path_provider/path_provider/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates example app Android compileSdkVersion to 31. + ## 2.0.7 * Moved Android and iOS implementations to federated packages. diff --git a/packages/path_provider/path_provider/example/android/app/build.gradle b/packages/path_provider/path_provider/example/android/app/build.gradle index e7f1bfb111a2..6ef1396ef1ac 100644 --- a/packages/path_provider/path_provider/example/android/app/build.gradle +++ b/packages/path_provider/path_provider/example/android/app/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 29 + compileSdkVersion 31 lintOptions { disable 'InvalidPackage' diff --git a/packages/path_provider/path_provider_android/CHANGELOG.md b/packages/path_provider/path_provider_android/CHANGELOG.md index eb155d138218..ed14cb703d41 100644 --- a/packages/path_provider/path_provider_android/CHANGELOG.md +++ b/packages/path_provider/path_provider_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.8 + +* Updates example app Android compileSdkVersion to 31. +* Fixes typing build warning. + ## 2.0.7 * Fixes link in README. diff --git a/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java index 983f6ff8719f..3ff2416527d0 100644 --- a/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java +++ b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java @@ -243,7 +243,7 @@ private String getPathProviderStorageDirectory() { } private List getPathProviderExternalCacheDirectories() { - final List paths = new ArrayList<>(); + final List paths = new ArrayList(); if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) { for (File dir : context.getExternalCacheDirs()) { @@ -262,7 +262,7 @@ private List getPathProviderExternalCacheDirectories() { } private List getPathProviderExternalStorageDirectories(String type) { - final List paths = new ArrayList<>(); + final List paths = new ArrayList(); if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) { for (File dir : context.getExternalFilesDirs(type)) { diff --git a/packages/path_provider/path_provider_android/example/android/app/build.gradle b/packages/path_provider/path_provider_android/example/android/app/build.gradle index e7f1bfb111a2..6ef1396ef1ac 100644 --- a/packages/path_provider/path_provider_android/example/android/app/build.gradle +++ b/packages/path_provider/path_provider_android/example/android/app/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 29 + compileSdkVersion 31 lintOptions { disable 'InvalidPackage' diff --git a/packages/path_provider/path_provider_android/pubspec.yaml b/packages/path_provider/path_provider_android/pubspec.yaml index 2fd72efa195d..2275316ecadd 100644 --- a/packages/path_provider/path_provider_android/pubspec.yaml +++ b/packages/path_provider/path_provider_android/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_android description: Android implementation of the path_provider plugin. repository: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.7 +version: 2.0.8 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/quick_actions/quick_actions/CHANGELOG.md b/packages/quick_actions/quick_actions/CHANGELOG.md index d2d628cad428..c49f1149daab 100644 --- a/packages/quick_actions/quick_actions/CHANGELOG.md +++ b/packages/quick_actions/quick_actions/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.6.0+8 + +* Updates example app Android compileSdkVersion to 31. +* Moves method call to background thread to fix CI failure. + ## 0.6.0+7 * Update minimum Flutter SDK to 2.5 and iOS deployment target to 9.0. diff --git a/packages/quick_actions/quick_actions/android/src/main/java/io/flutter/plugins/quickactions/MethodCallHandlerImpl.java b/packages/quick_actions/quick_actions/android/src/main/java/io/flutter/plugins/quickactions/MethodCallHandlerImpl.java index 2d89352f3e09..34563cbefeda 100644 --- a/packages/quick_actions/quick_actions/android/src/main/java/io/flutter/plugins/quickactions/MethodCallHandlerImpl.java +++ b/packages/quick_actions/quick_actions/android/src/main/java/io/flutter/plugins/quickactions/MethodCallHandlerImpl.java @@ -13,11 +13,17 @@ import android.content.res.Resources; import android.graphics.drawable.Icon; import android.os.Build; +import android.os.Handler; +import android.os.Looper; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.concurrent.Executor; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; class MethodCallHandlerImpl implements MethodChannel.MethodCallHandler { protected static final String EXTRA_ACTION = "some unique action key"; @@ -47,10 +53,42 @@ public void onMethodCall(MethodCall call, MethodChannel.Result result) { (ShortcutManager) context.getSystemService(Context.SHORTCUT_SERVICE); switch (call.method) { case "setShortcutItems": - List> serializedShortcuts = call.arguments(); - List shortcuts = deserializeShortcuts(serializedShortcuts); - shortcutManager.setDynamicShortcuts(shortcuts); - break; + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) { + List> serializedShortcuts = call.arguments(); + List shortcuts = deserializeShortcuts(serializedShortcuts); + + Executor uiThreadExecutor = new UiThreadExecutor(); + ThreadPoolExecutor executor = + new ThreadPoolExecutor( + 0, 1, 1, TimeUnit.SECONDS, new LinkedBlockingQueue()); + + executor.execute( + () -> { + boolean dynamicShortcutsSet = false; + try { + shortcutManager.setDynamicShortcuts(shortcuts); + dynamicShortcutsSet = true; + } catch (Exception e) { + // Leave dynamicShortcutsSet as false + } + + final boolean didSucceed = dynamicShortcutsSet; + + // TODO(camsim99): Move re-dispatch below to background thread when Flutter 2.8+ is stable. + uiThreadExecutor.execute( + () -> { + if (didSucceed) { + result.success(null); + } else { + result.error( + "quick_action_setshortcutitems_failure", + "Exception thrown when setting dynamic shortcuts", + null); + } + }); + }); + } + return; case "clearShortcutItems": shortcutManager.removeAllDynamicShortcuts(); break; @@ -127,4 +165,13 @@ private Intent getIntentToOpenMainActivity(String type) { .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); } + + private static class UiThreadExecutor implements Executor { + private final Handler handler = new Handler(Looper.getMainLooper()); + + @Override + public void execute(Runnable command) { + handler.post(command); + } + } } diff --git a/packages/quick_actions/quick_actions/example/android/app/build.gradle b/packages/quick_actions/quick_actions/example/android/app/build.gradle index 485ae5511063..465b4a62a9cd 100644 --- a/packages/quick_actions/quick_actions/example/android/app/build.gradle +++ b/packages/quick_actions/quick_actions/example/android/app/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 29 + compileSdkVersion 31 lintOptions { disable 'InvalidPackage' diff --git a/packages/quick_actions/quick_actions/pubspec.yaml b/packages/quick_actions/quick_actions/pubspec.yaml index 9531b7027cdf..137156d8d9a2 100644 --- a/packages/quick_actions/quick_actions/pubspec.yaml +++ b/packages/quick_actions/quick_actions/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for creating shortcuts on home screen, also known as Quick Actions on iOS and App Shortcuts on Android. repository: https://github.com/flutter/plugins/tree/master/packages/quick_actions/quick_actions issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+quick_actions%22 -version: 0.6.0+7 +version: 0.6.0+8 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/sensors/CHANGELOG.md b/packages/sensors/CHANGELOG.md index acea470855fb..3dbc5d2f7df1 100644 --- a/packages/sensors/CHANGELOG.md +++ b/packages/sensors/CHANGELOG.md @@ -2,6 +2,7 @@ * Remove references to the Android V1 embedding. * Updated Android lint settings. +* Updates example app Android compileSdkVersion to 31. ## 2.0.3 diff --git a/packages/sensors/example/android/app/build.gradle b/packages/sensors/example/android/app/build.gradle index d9c1e41f0759..c5dd855a64b7 100644 --- a/packages/sensors/example/android/app/build.gradle +++ b/packages/sensors/example/android/app/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 29 + compileSdkVersion 31 lintOptions { disable 'InvalidPackage' diff --git a/packages/share/CHANGELOG.md b/packages/share/CHANGELOG.md index c9a468d925a7..1c93b487e27b 100644 --- a/packages/share/CHANGELOG.md +++ b/packages/share/CHANGELOG.md @@ -2,6 +2,7 @@ * Remove references to the Android V1 embedding. * Updated Android lint settings. +* Updates example app Android compileSdkVersion to 31. ## 2.0.4 diff --git a/packages/share/example/android/app/build.gradle b/packages/share/example/android/app/build.gradle index 5b7b30bbad26..c0f1e05f2639 100644 --- a/packages/share/example/android/app/build.gradle +++ b/packages/share/example/android/app/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 29 + compileSdkVersion 31 lintOptions { disable 'InvalidPackage' diff --git a/packages/shared_preferences/shared_preferences/CHANGELOG.md b/packages/shared_preferences/shared_preferences/CHANGELOG.md index 458be254e493..f0a3e395e888 100644 --- a/packages/shared_preferences/shared_preferences/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences/CHANGELOG.md @@ -1,6 +1,8 @@ ## NEXT * Fixes newly enabled analyzer options. +* Updates example app Android compileSdkVersion to 31. + * Moved Android and iOS implementations to federated packages. ## 2.0.8 diff --git a/packages/shared_preferences/shared_preferences/example/android/app/build.gradle b/packages/shared_preferences/shared_preferences/example/android/app/build.gradle index 857ac4730a77..4cbb7307769c 100644 --- a/packages/shared_preferences/shared_preferences/example/android/app/build.gradle +++ b/packages/shared_preferences/shared_preferences/example/android/app/build.gradle @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 30 + compileSdkVersion 31 sourceSets { main.java.srcDirs += 'src/main/kotlin' diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index 5aa0154dafd1..aaa4d4e50f5b 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates example app Android compileSdkVersion to 31. + ## 6.0.13 * Fixed extracting browser headers when they are null error. diff --git a/packages/url_launcher/url_launcher/example/android/app/build.gradle b/packages/url_launcher/url_launcher/example/android/app/build.gradle index 8280da86f124..ca41cb14b6f4 100644 --- a/packages/url_launcher/url_launcher/example/android/app/build.gradle +++ b/packages/url_launcher/url_launcher/example/android/app/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 30 + compileSdkVersion 31 lintOptions { disable 'InvalidPackage' diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index b0db4dcc9909..d6b50d1cf87d 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates example app Android compileSdkVersion to 31. + ## 2.2.7 * Fixes a regression where dragging a [VideoProgressIndicator] while playing diff --git a/packages/video_player/video_player/example/android/app/build.gradle b/packages/video_player/video_player/example/android/app/build.gradle index 5f1f3afa6352..b1aaf8b45a8b 100644 --- a/packages/video_player/video_player/example/android/app/build.gradle +++ b/packages/video_player/video_player/example/android/app/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 29 + compileSdkVersion 31 lintOptions { disable 'InvalidPackage' diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index 57d32efcab2b..21240328af84 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates example app Android compileSdkVersion to 31. + ## 2.3.1 * Add iOS-specific note to set `JavascriptMode.unrestricted` in order to set `zoomEnabled: false`. diff --git a/packages/webview_flutter/webview_flutter/example/android/app/build.gradle b/packages/webview_flutter/webview_flutter/example/android/app/build.gradle index 9a43699afb2b..0193946331cd 100644 --- a/packages/webview_flutter/webview_flutter/example/android/app/build.gradle +++ b/packages/webview_flutter/webview_flutter/example/android/app/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 29 + compileSdkVersion 31 lintOptions { disable 'InvalidPackage' diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 09a797f4a1ed..b978b088d365 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates example app Android compileSdkVersion to 31. + ## 2.3.0 * Replaces platform implementation with API built with pigeon. diff --git a/packages/webview_flutter/webview_flutter_android/example/android/app/build.gradle b/packages/webview_flutter/webview_flutter_android/example/android/app/build.gradle index 1dcd363c9a44..932aba4eb5a8 100644 --- a/packages/webview_flutter/webview_flutter_android/example/android/app/build.gradle +++ b/packages/webview_flutter/webview_flutter_android/example/android/app/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 29 + compileSdkVersion 31 lintOptions { disable 'InvalidPackage' diff --git a/packages/wifi_info_flutter/wifi_info_flutter/CHANGELOG.md b/packages/wifi_info_flutter/wifi_info_flutter/CHANGELOG.md index 3d5599743710..3248b203173b 100644 --- a/packages/wifi_info_flutter/wifi_info_flutter/CHANGELOG.md +++ b/packages/wifi_info_flutter/wifi_info_flutter/CHANGELOG.md @@ -2,6 +2,7 @@ * Updated Android lint settings. * Updated package description. +* Updates example app Android compileSdkVersion to 31. ## 2.0.2 diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/build.gradle b/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/build.gradle index 86cf517168ef..8f96b53e2663 100644 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/build.gradle +++ b/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 29 + compileSdkVersion 31 lintOptions { disable 'InvalidPackage' From d237127bb7751ad6195367cef7a3195026b9e00d Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 19 Nov 2021 14:53:28 -0500 Subject: [PATCH 002/600] [shared_preferences] Publish fully federated version (#4526) Restores the app-facing package to publishable state, pointing at the newly-published federated Android and iOS implementation packages. Part of Part of flutter/flutter#68498 --- .../shared_preferences/CHANGELOG.md | 2 +- .../shared_preferences/pubspec.yaml | 12 +++--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/shared_preferences/shared_preferences/CHANGELOG.md b/packages/shared_preferences/shared_preferences/CHANGELOG.md index f0a3e395e888..818266f92597 100644 --- a/packages/shared_preferences/shared_preferences/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences/CHANGELOG.md @@ -1,4 +1,4 @@ -## NEXT +## 2.0.9 * Fixes newly enabled analyzer options. * Updates example app Android compileSdkVersion to 31. diff --git a/packages/shared_preferences/shared_preferences/pubspec.yaml b/packages/shared_preferences/shared_preferences/pubspec.yaml index 8fe9093f4801..732a62adc5dc 100644 --- a/packages/shared_preferences/shared_preferences/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/pubspec.yaml @@ -3,10 +3,7 @@ description: Flutter plugin for reading and writing simple key-value pairs. Wraps NSUserDefaults on iOS and SharedPreferences on Android. repository: https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.8 -# Temporarily disable publishing to allow moving Android and iOS -# implementations. -publish_to: none +version: 2.0.9 environment: sdk: ">=2.14.0 <3.0.0" @@ -32,11 +29,8 @@ dependencies: flutter: sdk: flutter meta: ^1.3.0 - # Temporary path dependencies to allow moving Android and iOS implementations. - shared_preferences_android: - path: ../shared_preferences_android - shared_preferences_ios: - path: ../shared_preferences_ios + shared_preferences_android: ^2.0.8 + shared_preferences_ios: ^2.0.8 shared_preferences_linux: ^2.0.0 shared_preferences_macos: ^2.0.0 shared_preferences_platform_interface: ^2.0.0 From fe1a3c49f29947c42c22e07169baa98ce796ccdd Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Fri, 19 Nov 2021 23:28:05 +0100 Subject: [PATCH 003/600] [in_app_purchase] Emit empty list when no transactions to restore. (#4523) --- .../in_app_purchase_storekit/CHANGELOG.md | 4 + .../in_app_purchase_storekit_platform.dart | 28 +++++++ .../in_app_purchase_storekit/pubspec.yaml | 2 +- .../test/fakes/fake_storekit_platform.dart | 29 +++---- ...n_app_purchase_storekit_platform_test.dart | 78 ++++++++++++++++++- 5 files changed, 119 insertions(+), 22 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md index ff5bffd94a11..e565812bf6ee 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.0 + +* **BREAKING CHANGE:** `InAppPurchaseStoreKitPlatform.restorePurchase()` emits an empty instance of `List` when there were no transactions to restore, indicating that the restore procedure has finished. + ## 0.2.1 * Renames `in_app_purchase_ios` to `in_app_purchase_storekit` to facilitate diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart index 65b1a3033077..e912dd606673 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart @@ -153,11 +153,19 @@ class InAppPurchaseStoreKitPlatform extends InAppPurchasePlatform { } } +enum _TransactionRestoreState { + notRunning, + waitingForTransactions, + receivedTransaction, +} + class _TransactionObserver implements SKTransactionObserverWrapper { final StreamController> purchaseUpdatedController; Completer? _restoreCompleter; late String _receiptData; + _TransactionRestoreState _transactionRestoreState = + _TransactionRestoreState.notRunning; _TransactionObserver(this.purchaseUpdatedController); @@ -165,6 +173,7 @@ class _TransactionObserver implements SKTransactionObserverWrapper { required SKPaymentQueueWrapper queue, String? applicationUserName, }) { + _transactionRestoreState = _TransactionRestoreState.waitingForTransactions; _restoreCompleter = Completer(); queue.restoreTransactions(applicationUserName: applicationUserName); return _restoreCompleter!.future; @@ -176,6 +185,14 @@ class _TransactionObserver implements SKTransactionObserverWrapper { void updatedTransactions( {required List transactions}) async { + if (_transactionRestoreState == + _TransactionRestoreState.waitingForTransactions && + transactions.any((transaction) => + transaction.transactionState == + SKPaymentTransactionStateWrapper.restored)) { + _transactionRestoreState = _TransactionRestoreState.receivedTransaction; + } + String receiptData = await getReceiptData(); List purchases = transactions .map((SKPaymentTransactionWrapper transaction) => @@ -191,10 +208,21 @@ class _TransactionObserver implements SKTransactionObserverWrapper { /// Triggered when there is an error while restoring transactions. void restoreCompletedTransactionsFailed({required SKError error}) { _restoreCompleter!.completeError(error); + _transactionRestoreState = _TransactionRestoreState.notRunning; } void paymentQueueRestoreCompletedTransactionsFinished() { _restoreCompleter!.complete(); + + // If no restored transactions were received during the restore session + // emit an empty list of purchase details to inform listeners that the + // restore session finished without any results. + if (_transactionRestoreState == + _TransactionRestoreState.waitingForTransactions) { + purchaseUpdatedController.add([]); + } + + _transactionRestoreState = _TransactionRestoreState.notRunning; } bool shouldAddStorePayment( diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml index bfc55b94b247..1fe291d4b3f2 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_storekit description: An implementation for the iOS platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/plugins/tree/master/packages/in_app_purchase/in_app_purchase_storekit issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.2.1 +version: 0.3.0 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart index c4254c29cf0d..352ba3206145 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart @@ -47,24 +47,6 @@ class FakeStoreKitPlatform { validProducts[validID] = SKProductWrapper.fromJson(productWrapperMap); } - SKPaymentTransactionWrapper tran1 = SKPaymentTransactionWrapper( - transactionIdentifier: '123', - payment: dummyPayment, - originalTransaction: dummyTransaction, - transactionTimeStamp: 123123123.022, - transactionState: SKPaymentTransactionStateWrapper.restored, - error: null, - ); - SKPaymentTransactionWrapper tran2 = SKPaymentTransactionWrapper( - transactionIdentifier: '1234', - payment: dummyPayment, - originalTransaction: dummyTransaction, - transactionTimeStamp: 123123123.022, - transactionState: SKPaymentTransactionStateWrapper.restored, - error: null, - ); - - transactions.addAll([tran1, tran2]); finishedTransactions = []; testRestoredTransactionsNull = false; testTransactionFail = false; @@ -123,6 +105,17 @@ class FakeStoreKitPlatform { originalTransaction: null); } + SKPaymentTransactionWrapper createRestoredTransaction( + String productId, String transactionId) { + return SKPaymentTransactionWrapper( + payment: SKPaymentWrapper(productIdentifier: productId), + transactionState: SKPaymentTransactionStateWrapper.restored, + transactionTimeStamp: 123123.121, + transactionIdentifier: transactionId, + error: null, + originalTransaction: null); + } + Future onMethodCall(MethodCall call) { switch (call.method) { case '-[SKPaymentQueue canMakePayments:]': diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_test.dart index 8ac4b0427ddf..12595bbf0ed0 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_test.dart @@ -78,14 +78,18 @@ void main() { group('restore purchases', () { test('should emit restored transactions on purchase stream', () async { + fakeStoreKitPlatform.transactions.insert( + 0, fakeStoreKitPlatform.createRestoredTransaction('foo', 'RT1')); + fakeStoreKitPlatform.transactions.insert( + 1, fakeStoreKitPlatform.createRestoredTransaction('foo', 'RT2')); Completer completer = Completer(); Stream> stream = iapStoreKitPlatform.purchaseStream; late StreamSubscription subscription; subscription = stream.listen((purchaseDetailsList) { if (purchaseDetailsList.first.status == PurchaseStatus.restored) { - completer.complete(purchaseDetailsList); subscription.cancel(); + completer.complete(purchaseDetailsList); } }); @@ -109,15 +113,37 @@ void main() { } }); + test( + 'should emit empty transaction list on purchase stream when there is nothing to restore', + () async { + fakeStoreKitPlatform.testRestoredTransactionsNull = true; + Completer completer = Completer(); + Stream> stream = iapStoreKitPlatform.purchaseStream; + + late StreamSubscription subscription; + subscription = stream.listen((purchaseDetailsList) { + expect(purchaseDetailsList.isEmpty, true); + subscription.cancel(); + completer.complete(); + }); + + await iapStoreKitPlatform.restorePurchases(); + await completer.future; + }); + test('should not block transaction updates', () async { fakeStoreKitPlatform.transactions.insert( - 0, fakeStoreKitPlatform.createPurchasedTransaction('foo', 'bar')); + 0, fakeStoreKitPlatform.createRestoredTransaction('foo', 'RT1')); + fakeStoreKitPlatform.transactions.insert( + 1, fakeStoreKitPlatform.createPurchasedTransaction('foo', 'bar')); + fakeStoreKitPlatform.transactions.insert( + 2, fakeStoreKitPlatform.createRestoredTransaction('foo', 'RT2')); Completer completer = Completer(); Stream> stream = iapStoreKitPlatform.purchaseStream; late StreamSubscription subscription; subscription = stream.listen((purchaseDetailsList) { - if (purchaseDetailsList.first.status == PurchaseStatus.purchased) { + if (purchaseDetailsList[1].status == PurchaseStatus.purchased) { completer.complete(purchaseDetailsList); subscription.cancel(); } @@ -145,8 +171,54 @@ void main() { } }); + test( + 'should emit empty transaction if transactions array does not contain a transaction with PurchaseStatus.restored status.', + () async { + fakeStoreKitPlatform.transactions.insert( + 0, fakeStoreKitPlatform.createPurchasedTransaction('foo', 'bar')); + Completer completer = Completer(); + Stream> stream = iapStoreKitPlatform.purchaseStream; + List> purchaseDetails = []; + + late StreamSubscription subscription; + subscription = stream.listen((purchaseDetailsList) { + purchaseDetails.add(purchaseDetailsList); + + if (purchaseDetails.length == 2) { + completer.complete(purchaseDetails); + subscription.cancel(); + } + }); + await iapStoreKitPlatform.restorePurchases(); + final details = await completer.future; + expect(details.length, 2); + expect(details[0], []); + for (int i = 0; i < fakeStoreKitPlatform.transactions.length; i++) { + SKPaymentTransactionWrapper expected = + fakeStoreKitPlatform.transactions[i]; + PurchaseDetails actual = details[1][i]; + + expect(actual.purchaseID, expected.transactionIdentifier); + expect(actual.verificationData, isNotNull); + expect( + actual.status, + SKTransactionStatusConverter() + .toPurchaseStatus(expected.transactionState, expected.error), + ); + expect(actual.verificationData.localVerificationData, + fakeStoreKitPlatform.receiptData); + expect(actual.verificationData.serverVerificationData, + fakeStoreKitPlatform.receiptData); + expect(actual.pendingCompletePurchase, true); + } + }); + test('receipt error should populate null to verificationData.data', () async { + fakeStoreKitPlatform.transactions.insert( + 0, fakeStoreKitPlatform.createRestoredTransaction('foo', 'RT1')); + fakeStoreKitPlatform.transactions.insert( + 1, fakeStoreKitPlatform.createRestoredTransaction('foo', 'RT2')); fakeStoreKitPlatform.receiptData = null; Completer completer = Completer(); Stream> stream = iapStoreKitPlatform.purchaseStream; From e5156e9ab70ab58d76f7b9819b03bcfdff5c2843 Mon Sep 17 00:00:00 2001 From: Emmanuel Garcia Date: Fri, 19 Nov 2021 17:56:32 -0800 Subject: [PATCH 004/600] Enable Android integration tests in remaining plugins (#4514) --- packages/camera/camera/CHANGELOG.md | 5 + .../io/flutter/plugins/camera/Camera.java | 22 ++++- .../flutter/plugins/DartIntegrationTest.java | 0 .../cameraexample/FlutterActivityTest.java | 0 .../example/integration_test/camera_test.dart | 94 ++++++++++--------- packages/camera/camera/pubspec.yaml | 2 +- .../in_app_purchase/CHANGELOG.md | 1 + .../flutter/plugins/DartIntegrationTest.java | 0 .../FlutterActivityTest.java | 0 .../org.mockito.plugins.MockMaker | 1 - .../in_app_purchase_test.dart | 7 ++ .../flutter/plugins/DartIntegrationTest.java | 0 .../FlutterActivityTest.java | 0 .../FlutterActivityTest.java | 19 ++++ .../android/app/src/debug/AndroidManifest.xml | 7 -- .../example/android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../url_launcher/url_launcher/CHANGELOG.md | 4 +- packages/url_launcher/url_launcher/README.md | 7 +- .../plugins/urllauncher/UrlLauncher.java | 14 ++- .../flutter/plugins/DartIntegrationTest.java | 0 .../FlutterActivityTest.java} | 0 .../android/app/src/main/AndroidManifest.xml | 4 + .../integration_test/url_launcher_test.dart | 1 + .../url_launcher/url_launcher/pubspec.yaml | 2 +- .../video_player/video_player/CHANGELOG.md | 1 + .../flutter/plugins/DartIntegrationTest.java | 14 +++ .../FlutterActivityTest.java | 19 ++++ .../configs/exclude_integration_android.yaml | 7 -- 29 files changed, 165 insertions(+), 70 deletions(-) rename packages/camera/camera/example/android/app/src/{androidTestDebug => androidTest}/java/io/flutter/plugins/DartIntegrationTest.java (100%) rename packages/camera/camera/example/android/app/src/{androidTestDebug => androidTest}/java/io/flutter/plugins/cameraexample/FlutterActivityTest.java (100%) rename packages/in_app_purchase/in_app_purchase/example/android/app/src/{main => androidTest}/java/io/flutter/plugins/DartIntegrationTest.java (100%) rename packages/in_app_purchase/in_app_purchase/example/android/app/src/{main => androidTest}/java/io/flutter/plugins/inapppurchaseexample/FlutterActivityTest.java (100%) delete mode 100644 packages/in_app_purchase/in_app_purchase/example/android/app/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker rename packages/in_app_purchase/in_app_purchase_android/example/android/app/src/{main => androidTest}/java/io/flutter/plugins/DartIntegrationTest.java (100%) rename packages/in_app_purchase/in_app_purchase_android/example/android/app/src/{main => androidTest}/java/io/flutter/plugins/inapppurchaseexample/FlutterActivityTest.java (100%) create mode 100644 packages/shared_preferences/shared_preferences/example/android/app/src/androidTest/java/io/flutter/plugins/sharedpreferencesexample/FlutterActivityTest.java delete mode 100644 packages/shared_preferences/shared_preferences/example/android/app/src/debug/AndroidManifest.xml rename packages/url_launcher/url_launcher/example/android/app/src/{androidTestDebug => androidTest}/java/io/flutter/plugins/DartIntegrationTest.java (100%) rename packages/url_launcher/url_launcher/example/android/app/src/{androidTestDebug/java/io/flutter/plugins/urllauncherexample/MainActivityTest.java => androidTest/java/io/flutter/plugins/urllauncherexample/FlutterActivityTest.java} (100%) create mode 100644 packages/video_player/video_player/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java create mode 100644 packages/video_player/video_player/example/android/app/src/androidTest/java/io/flutter/plugins/videoplayerexample/FlutterActivityTest.java diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 929feeac6b69..1a6eceb957b1 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.9.4+5 + +* Fixes bug where calling a method after the camera was closed resulted in a Java `IllegalStateException` exception. +* Fixes integration tests. + ## 0.9.4+4 * Change Android compileSdkVersion to 31. diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java index e31903955b4e..6a70ea0d10ea 100644 --- a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -281,8 +281,10 @@ public void onOpened(@NonNull CameraDevice device) { public void onClosed(@NonNull CameraDevice camera) { Log.i(TAG, "open | onClosed"); + // Prevents calls to methods that would otherwise result in IllegalStateException exceptions. + cameraDevice = null; + closeCaptureSession(); dartMessenger.sendCameraClosingEvent(); - super.onClosed(camera); } @Override @@ -364,10 +366,13 @@ private void createCaptureSession( // Prepare the callback. CameraCaptureSession.StateCallback callback = new CameraCaptureSession.StateCallback() { + boolean captureSessionClosed = false; + @Override public void onConfigured(@NonNull CameraCaptureSession session) { + Log.i(TAG, "CameraCaptureSession onConfigured"); // Camera was already closed. - if (cameraDevice == null) { + if (cameraDevice == null || captureSessionClosed) { dartMessenger.sendCameraErrorEvent("The camera was closed during configuration."); return; } @@ -382,8 +387,15 @@ public void onConfigured(@NonNull CameraCaptureSession session) { @Override public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) { + Log.i(TAG, "CameraCaptureSession onConfigureFailed"); dartMessenger.sendCameraErrorEvent("Failed to configure camera session."); } + + @Override + public void onClosed(@NonNull CameraCaptureSession session) { + Log.i(TAG, "CameraCaptureSession onClosed"); + captureSessionClosed = true; + } }; // Start the session. @@ -427,10 +439,12 @@ private void createCaptureSession( // Send a repeating request to refresh capture session. private void refreshPreviewCaptureSession( @Nullable Runnable onSuccessCallback, @NonNull ErrorCallback onErrorCallback) { + Log.i(TAG, "refreshPreviewCaptureSession"); + if (captureSession == null) { Log.i( TAG, - "[refreshPreviewCaptureSession] captureSession not yet initialized, " + "refreshPreviewCaptureSession: captureSession not yet initialized, " + "skipping preview capture session refresh."); return; } @@ -445,6 +459,8 @@ private void refreshPreviewCaptureSession( onSuccessCallback.run(); } + } catch (IllegalStateException e) { + onErrorCallback.onError("cameraAccess", "Camera is closed: " + e.getMessage()); } catch (CameraAccessException e) { onErrorCallback.onError("cameraAccess", e.getMessage()); } diff --git a/packages/camera/camera/example/android/app/src/androidTestDebug/java/io/flutter/plugins/DartIntegrationTest.java b/packages/camera/camera/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java similarity index 100% rename from packages/camera/camera/example/android/app/src/androidTestDebug/java/io/flutter/plugins/DartIntegrationTest.java rename to packages/camera/camera/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java diff --git a/packages/camera/camera/example/android/app/src/androidTestDebug/java/io/flutter/plugins/cameraexample/FlutterActivityTest.java b/packages/camera/camera/example/android/app/src/androidTest/java/io/flutter/plugins/cameraexample/FlutterActivityTest.java similarity index 100% rename from packages/camera/camera/example/android/app/src/androidTestDebug/java/io/flutter/plugins/cameraexample/FlutterActivityTest.java rename to packages/camera/camera/example/android/app/src/androidTest/java/io/flutter/plugins/cameraexample/FlutterActivityTest.java diff --git a/packages/camera/camera/example/integration_test/camera_test.dart b/packages/camera/camera/example/integration_test/camera_test.dart index 00a1714f9a5a..d09300a3f906 100644 --- a/packages/camera/camera/example/integration_test/camera_test.dart +++ b/packages/camera/camera/example/integration_test/camera_test.dart @@ -71,28 +71,32 @@ void main() { expectedSize, Size(image.height.toDouble(), image.width.toDouble())); } - testWidgets('Capture specific image resolutions', - (WidgetTester tester) async { - final List cameras = await availableCameras(); - if (cameras.isEmpty) { - return; - } - for (CameraDescription cameraDescription in cameras) { - bool previousPresetExactlySupported = true; - for (MapEntry preset - in presetExpectedSizes.entries) { - final CameraController controller = - CameraController(cameraDescription, preset.key); - await controller.initialize(); - final bool presetExactlySupported = - await testCaptureImageResolution(controller, preset.key); - assert(!(!previousPresetExactlySupported && presetExactlySupported), - 'The camera took higher resolution pictures at a lower resolution.'); - previousPresetExactlySupported = presetExactlySupported; - await controller.dispose(); + testWidgets( + 'Capture specific image resolutions', + (WidgetTester tester) async { + final List cameras = await availableCameras(); + if (cameras.isEmpty) { + return; } - } - }, skip: !Platform.isAndroid); + for (CameraDescription cameraDescription in cameras) { + bool previousPresetExactlySupported = true; + for (MapEntry preset + in presetExpectedSizes.entries) { + final CameraController controller = + CameraController(cameraDescription, preset.key); + await controller.initialize(); + final bool presetExactlySupported = + await testCaptureImageResolution(controller, preset.key); + assert(!(!previousPresetExactlySupported && presetExactlySupported), + 'The camera took higher resolution pictures at a lower resolution.'); + previousPresetExactlySupported = presetExactlySupported; + await controller.dispose(); + } + } + }, + // TODO(egarciad): Fix https://github.com/flutter/flutter/issues/93686. + skip: true, + ); // This tests that the capture is no bigger than the preset, since we have // automatic code to fall back to smaller sizes when we need to. Returns @@ -121,29 +125,33 @@ void main() { expectedSize, Size(video.height, video.width)); } - testWidgets('Capture specific video resolutions', - (WidgetTester tester) async { - final List cameras = await availableCameras(); - if (cameras.isEmpty) { - return; - } - for (CameraDescription cameraDescription in cameras) { - bool previousPresetExactlySupported = true; - for (MapEntry preset - in presetExpectedSizes.entries) { - final CameraController controller = - CameraController(cameraDescription, preset.key); - await controller.initialize(); - await controller.prepareForVideoRecording(); - final bool presetExactlySupported = - await testCaptureVideoResolution(controller, preset.key); - assert(!(!previousPresetExactlySupported && presetExactlySupported), - 'The camera took higher resolution pictures at a lower resolution.'); - previousPresetExactlySupported = presetExactlySupported; - await controller.dispose(); + testWidgets( + 'Capture specific video resolutions', + (WidgetTester tester) async { + final List cameras = await availableCameras(); + if (cameras.isEmpty) { + return; } - } - }, skip: !Platform.isAndroid); + for (CameraDescription cameraDescription in cameras) { + bool previousPresetExactlySupported = true; + for (MapEntry preset + in presetExpectedSizes.entries) { + final CameraController controller = + CameraController(cameraDescription, preset.key); + await controller.initialize(); + await controller.prepareForVideoRecording(); + final bool presetExactlySupported = + await testCaptureVideoResolution(controller, preset.key); + assert(!(!previousPresetExactlySupported && presetExactlySupported), + 'The camera took higher resolution pictures at a lower resolution.'); + previousPresetExactlySupported = presetExactlySupported; + await controller.dispose(); + } + } + }, + // TODO(egarciad): Fix https://github.com/flutter/flutter/issues/93686. + skip: true, + ); testWidgets('Pause and resume video recording', (WidgetTester tester) async { final List cameras = await availableCameras(); diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 905ec4392dd7..58e1ca3ca98c 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/master/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+4 +version: 0.9.4+5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md index 6b615fe7470d..39d8ce8ad6e8 100644 --- a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Fixes integration tests. * Updates example app Android compileSdkVersion to 31. * **BREAKING CHANGES**: diff --git a/packages/in_app_purchase/in_app_purchase/example/android/app/src/main/java/io/flutter/plugins/DartIntegrationTest.java b/packages/in_app_purchase/in_app_purchase/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java similarity index 100% rename from packages/in_app_purchase/in_app_purchase/example/android/app/src/main/java/io/flutter/plugins/DartIntegrationTest.java rename to packages/in_app_purchase/in_app_purchase/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java diff --git a/packages/in_app_purchase/in_app_purchase/example/android/app/src/main/java/io/flutter/plugins/inapppurchaseexample/FlutterActivityTest.java b/packages/in_app_purchase/in_app_purchase/example/android/app/src/androidTest/java/io/flutter/plugins/inapppurchaseexample/FlutterActivityTest.java similarity index 100% rename from packages/in_app_purchase/in_app_purchase/example/android/app/src/main/java/io/flutter/plugins/inapppurchaseexample/FlutterActivityTest.java rename to packages/in_app_purchase/in_app_purchase/example/android/app/src/androidTest/java/io/flutter/plugins/inapppurchaseexample/FlutterActivityTest.java diff --git a/packages/in_app_purchase/in_app_purchase/example/android/app/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/packages/in_app_purchase/in_app_purchase/example/android/app/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker deleted file mode 100644 index 1f0955d450f0..000000000000 --- a/packages/in_app_purchase/in_app_purchase/example/android/app/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker +++ /dev/null @@ -1 +0,0 @@ -mock-maker-inline diff --git a/packages/in_app_purchase/in_app_purchase/example/integration_test/in_app_purchase_test.dart b/packages/in_app_purchase/in_app_purchase/example/integration_test/in_app_purchase_test.dart index 437ee99e9f36..55ad1223f548 100644 --- a/packages/in_app_purchase/in_app_purchase/example/integration_test/in_app_purchase_test.dart +++ b/packages/in_app_purchase/in_app_purchase/example/integration_test/in_app_purchase_test.dart @@ -2,14 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io'; + import 'package:flutter_test/flutter_test.dart'; import 'package:in_app_purchase/in_app_purchase.dart'; +import 'package:in_app_purchase_android/in_app_purchase_android.dart'; import 'package:integration_test/integration_test.dart'; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('Can create InAppPurchase instance', (WidgetTester tester) async { + if (Platform.isAndroid) { + // https://github.com/flutter/flutter/issues/93837 + InAppPurchaseAndroidPlatformAddition.enablePendingPurchases(); + } final InAppPurchase iapInstance = InAppPurchase.instance; expect(iapInstance, isNotNull); }); diff --git a/packages/in_app_purchase/in_app_purchase_android/example/android/app/src/main/java/io/flutter/plugins/DartIntegrationTest.java b/packages/in_app_purchase/in_app_purchase_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java similarity index 100% rename from packages/in_app_purchase/in_app_purchase_android/example/android/app/src/main/java/io/flutter/plugins/DartIntegrationTest.java rename to packages/in_app_purchase/in_app_purchase_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java diff --git a/packages/in_app_purchase/in_app_purchase_android/example/android/app/src/main/java/io/flutter/plugins/inapppurchaseexample/FlutterActivityTest.java b/packages/in_app_purchase/in_app_purchase_android/example/android/app/src/androidTest/java/io/flutter/plugins/inapppurchaseexample/FlutterActivityTest.java similarity index 100% rename from packages/in_app_purchase/in_app_purchase_android/example/android/app/src/main/java/io/flutter/plugins/inapppurchaseexample/FlutterActivityTest.java rename to packages/in_app_purchase/in_app_purchase_android/example/android/app/src/androidTest/java/io/flutter/plugins/inapppurchaseexample/FlutterActivityTest.java diff --git a/packages/shared_preferences/shared_preferences/example/android/app/src/androidTest/java/io/flutter/plugins/sharedpreferencesexample/FlutterActivityTest.java b/packages/shared_preferences/shared_preferences/example/android/app/src/androidTest/java/io/flutter/plugins/sharedpreferencesexample/FlutterActivityTest.java new file mode 100644 index 000000000000..3d4ea2b1edba --- /dev/null +++ b/packages/shared_preferences/shared_preferences/example/android/app/src/androidTest/java/io/flutter/plugins/sharedpreferencesexample/FlutterActivityTest.java @@ -0,0 +1,19 @@ +// 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.sharedpreferencesexample; + +import androidx.test.rule.ActivityTestRule; +import dev.flutter.plugins.integration_test.FlutterTestRunner; +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.plugins.DartIntegrationTest; +import org.junit.Rule; +import org.junit.runner.RunWith; + +@DartIntegrationTest +@RunWith(FlutterTestRunner.class) +public class FlutterActivityTest { + @Rule + public ActivityTestRule rule = new ActivityTestRule<>(FlutterActivity.class); +} diff --git a/packages/shared_preferences/shared_preferences/example/android/app/src/debug/AndroidManifest.xml b/packages/shared_preferences/shared_preferences/example/android/app/src/debug/AndroidManifest.xml deleted file mode 100644 index d60d6f69a862..000000000000 --- a/packages/shared_preferences/shared_preferences/example/android/app/src/debug/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/packages/shared_preferences/shared_preferences/example/android/build.gradle b/packages/shared_preferences/shared_preferences/example/android/build.gradle index 24047dce5d43..21d50697b9e9 100644 --- a/packages/shared_preferences/shared_preferences/example/android/build.gradle +++ b/packages/shared_preferences/shared_preferences/example/android/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' + classpath 'com.android.tools.build:gradle:7.0.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/packages/shared_preferences/shared_preferences/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/shared_preferences/shared_preferences/example/android/gradle/wrapper/gradle-wrapper.properties index bc6a58afdda2..b8793d3c0d69 100644 --- a/packages/shared_preferences/shared_preferences/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/shared_preferences/shared_preferences/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index aaa4d4e50f5b..e69083defc0e 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,5 +1,7 @@ -## NEXT +## 6.0.14 +* Updates readme to indicate that sending SMS messages on Android 11 requires to add a query to AndroidManifest.xml. +* Fixes integration tests. * Updates example app Android compileSdkVersion to 31. ## 6.0.13 diff --git a/packages/url_launcher/url_launcher/README.md b/packages/url_launcher/url_launcher/README.md index c649b5c0fe7b..aa161320ea9b 100644 --- a/packages/url_launcher/url_launcher/README.md +++ b/packages/url_launcher/url_launcher/README.md @@ -72,7 +72,12 @@ for examples of other queries. - + + + + + + diff --git a/packages/url_launcher/url_launcher/android/src/main/java/io/flutter/plugins/urllauncher/UrlLauncher.java b/packages/url_launcher/url_launcher/android/src/main/java/io/flutter/plugins/urllauncher/UrlLauncher.java index 07f7ef3ee7dc..c3a563a9c137 100644 --- a/packages/url_launcher/url_launcher/android/src/main/java/io/flutter/plugins/urllauncher/UrlLauncher.java +++ b/packages/url_launcher/url_launcher/android/src/main/java/io/flutter/plugins/urllauncher/UrlLauncher.java @@ -12,11 +12,14 @@ import android.net.Uri; import android.os.Bundle; import android.provider.Browser; +import android.util.Log; import androidx.annotation.Nullable; /** Launches components for URLs. */ class UrlLauncher { + private static final String TAG = "UrlLauncher"; private final Context applicationContext; + @Nullable private Activity activity; /** @@ -40,9 +43,14 @@ boolean canLaunch(String url) { ComponentName componentName = launchIntent.resolveActivity(applicationContext.getPackageManager()); - return componentName != null - && !"{com.android.fallback/com.android.fallback.Fallback}" - .equals(componentName.toShortString()); + if (componentName == null) { + Log.i(TAG, "component name for " + url + " is null"); + return false; + } else { + Log.i(TAG, "component name for " + url + " is " + componentName.toShortString()); + return !"{com.android.fallback/com.android.fallback.Fallback}" + .equals(componentName.toShortString()); + } } /** diff --git a/packages/url_launcher/url_launcher/example/android/app/src/androidTestDebug/java/io/flutter/plugins/DartIntegrationTest.java b/packages/url_launcher/url_launcher/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java similarity index 100% rename from packages/url_launcher/url_launcher/example/android/app/src/androidTestDebug/java/io/flutter/plugins/DartIntegrationTest.java rename to packages/url_launcher/url_launcher/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java diff --git a/packages/url_launcher/url_launcher/example/android/app/src/androidTestDebug/java/io/flutter/plugins/urllauncherexample/MainActivityTest.java b/packages/url_launcher/url_launcher/example/android/app/src/androidTest/java/io/flutter/plugins/urllauncherexample/FlutterActivityTest.java similarity index 100% rename from packages/url_launcher/url_launcher/example/android/app/src/androidTestDebug/java/io/flutter/plugins/urllauncherexample/MainActivityTest.java rename to packages/url_launcher/url_launcher/example/android/app/src/androidTest/java/io/flutter/plugins/urllauncherexample/FlutterActivityTest.java diff --git a/packages/url_launcher/url_launcher/example/android/app/src/main/AndroidManifest.xml b/packages/url_launcher/url_launcher/example/android/app/src/main/AndroidManifest.xml index 918c29ee2dca..fa149f94adf0 100644 --- a/packages/url_launcher/url_launcher/example/android/app/src/main/AndroidManifest.xml +++ b/packages/url_launcher/url_launcher/example/android/app/src/main/AndroidManifest.xml @@ -17,6 +17,10 @@ + + + + =2.14.0 <3.0.0" diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index d6b50d1cf87d..7e9dfff39c69 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Fixes integration tests. * Updates example app Android compileSdkVersion to 31. ## 2.2.7 diff --git a/packages/video_player/video_player/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java b/packages/video_player/video_player/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java new file mode 100644 index 000000000000..0f4298dca155 --- /dev/null +++ b/packages/video_player/video_player/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java @@ -0,0 +1,14 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface DartIntegrationTest {} diff --git a/packages/video_player/video_player/example/android/app/src/androidTest/java/io/flutter/plugins/videoplayerexample/FlutterActivityTest.java b/packages/video_player/video_player/example/android/app/src/androidTest/java/io/flutter/plugins/videoplayerexample/FlutterActivityTest.java new file mode 100644 index 000000000000..45cf5c6e9903 --- /dev/null +++ b/packages/video_player/video_player/example/android/app/src/androidTest/java/io/flutter/plugins/videoplayerexample/FlutterActivityTest.java @@ -0,0 +1,19 @@ +// 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.videoplayerexample; + +import androidx.test.rule.ActivityTestRule; +import dev.flutter.plugins.integration_test.FlutterTestRunner; +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.plugins.DartIntegrationTest; +import org.junit.Rule; +import org.junit.runner.RunWith; + +@DartIntegrationTest +@RunWith(FlutterTestRunner.class) +public class FlutterActivityTest { + @Rule + public ActivityTestRule rule = new ActivityTestRule<>(FlutterActivity.class); +} diff --git a/script/configs/exclude_integration_android.yaml b/script/configs/exclude_integration_android.yaml index e6ef826b6069..e06b062fd68f 100644 --- a/script/configs/exclude_integration_android.yaml +++ b/script/configs/exclude_integration_android.yaml @@ -1,10 +1,3 @@ -# Currently missing harness files: https://github.com/flutter/flutter/issues/86749 -- camera/camera -- in_app_purchase/in_app_purchase -- in_app_purchase_android -- url_launcher/url_launcher -- video_player/video_player - # Deprecated; no plan to backfill the missing files - android_intent - connectivity/connectivity From e714f1b80d95918becd708d629d54a7d1951daa5 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 19 Nov 2021 23:27:11 -0500 Subject: [PATCH 005/600] [url_launcher] Improve README and example (#4529) Fixes the README and example to properly demonstrate using `canLaunch` to adjust UI, rather than as a direct wrapper around `launch` (which serves no useful purpose given that `launch` can already return failure). Also makes some minor organizational improvements to the README (e.g., fixing the fact that the discussions of iOS and Android configuration were separated by the inline example for some reason). --- .../url_launcher/url_launcher/CHANGELOG.md | 7 ++ packages/url_launcher/url_launcher/README.md | 54 ++++---- .../url_launcher/example/lib/main.dart | 116 ++++++++++-------- .../url_launcher/lib/url_launcher.dart | 11 +- .../url_launcher/url_launcher/pubspec.yaml | 2 +- 5 files changed, 106 insertions(+), 84 deletions(-) diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index e69083defc0e..7aab66301abc 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,3 +1,10 @@ +## 6.0.15 + +* Updates README: + * Improves organization. + * Clarifies how `canLaunch` should be used. +* Updates example application to demonstrate intended use of `canLaunch`. + ## 6.0.14 * Updates readme to indicate that sending SMS messages on Android 11 requires to add a query to AndroidManifest.xml. diff --git a/packages/url_launcher/url_launcher/README.md b/packages/url_launcher/url_launcher/README.md index aa161320ea9b..393bbd904158 100644 --- a/packages/url_launcher/url_launcher/README.md +++ b/packages/url_launcher/url_launcher/README.md @@ -6,23 +6,8 @@ A Flutter plugin for launching a URL. Supports iOS, Android, web, Windows, macOS, and Linux. ## Usage -To use this plugin, add `url_launcher` as a [dependency in your pubspec.yaml file](https://flutter.dev/platform-plugins/). - -## Installation - -### iOS -Add any URL schemes passed to `canLaunch` as `LSApplicationQueriesSchemes` entries in your Info.plist file. -Example: -``` -LSApplicationQueriesSchemes - - https - http - -``` - -See [`-[UIApplication canOpenURL:]`](https://developer.apple.com/documentation/uikit/uiapplication/1622952-canopenurl) for more details. +To use this plugin, add `url_launcher` as a [dependency in your pubspec.yaml file](https://flutter.dev/platform-plugins/). ### Example @@ -30,7 +15,7 @@ See [`-[UIApplication canOpenURL:]`](https://developer.apple.com/documentation/u import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; -const _url = 'https://flutter.dev'; +const String _url = 'https://flutter.dev'; void main() => runApp( const MaterialApp( @@ -45,10 +30,29 @@ void main() => runApp( ), ); -void _launchURL() async => - await canLaunch(_url) ? await launch(_url) : throw 'Could not launch $_url'; +void _launchURL() async { + if (!await launch(_url)) throw 'Could not launch $_url'; +} ``` +See the example app for more complex examples. + +## Configuration + +### iOS +Add any URL schemes passed to `canLaunch` as `LSApplicationQueriesSchemes` entries in your Info.plist file. + +Example: +``` +LSApplicationQueriesSchemes + + https + http + +``` + +See [`-[UIApplication canOpenURL:]`](https://developer.apple.com/documentation/uikit/uiapplication/1622952-canopenurl) for more details. + ### Android Starting from API 30 Android requires package visibility configuration in your @@ -138,20 +142,20 @@ than `Uri`'s `queryParameters` constructor argument, due to encodes query parameters. Using `queryParameters` will result in spaces being converted to `+` in many cases. -## Handling missing URL receivers +### Handling missing URL receivers A particular mobile device may not be able to receive all supported URL schemes. For example, a tablet may not have a cellular radio and thus no support for launching a URL using the `sms` scheme, or a device may not have an email app -and thus no support for launching a URL using the `email` scheme. +and thus no support for launching a URL using the `mailto` scheme. We recommend checking which URL schemes are supported using the [`canLaunch`](https://pub.dev/documentation/url_launcher/latest/url_launcher/canLaunch.html) -method prior to calling `launch`. If the `canLaunch` method returns false, as a +in most cases. If the `canLaunch` method returns false, as a best practice we suggest adjusting the application UI so that the unsupported -URL is never triggered; for example, if the `email` scheme is not supported, a -UI button that would have sent email can be changed to redirect the user to a -web page using a URL following the `http` scheme. +URL is never triggered; for example, if the `mailto` scheme is not supported, a +UI button that would have sent feedback email could be changed to instead open +a web-based feedback form using an `https` URL. ## Browser vs In-app Handling By default, Android opens up a browser when handling URLs. You can pass diff --git a/packages/url_launcher/url_launcher/example/lib/main.dart b/packages/url_launcher/url_launcher/example/lib/main.dart index d593e6d5e001..7e4d18a7cb13 100644 --- a/packages/url_launcher/url_launcher/example/lib/main.dart +++ b/packages/url_launcher/url_launcher/example/lib/main.dart @@ -36,74 +36,76 @@ class MyHomePage extends StatefulWidget { } class _MyHomePageState extends State { + bool _hasCallSupport = false; Future? _launched; String _phone = ''; + @override + void initState() { + super.initState(); + // Check for phone call support. + canLaunch('tel:123').then((bool result) { + setState(() { + _hasCallSupport = result; + }); + }); + } + Future _launchInBrowser(String url) async { - if (await canLaunch(url)) { - await launch( - url, - forceSafariVC: false, - forceWebView: false, - headers: {'my_header_key': 'my_header_value'}, - ); - } else { + if (!await launch( + url, + forceSafariVC: false, + forceWebView: false, + headers: {'my_header_key': 'my_header_value'}, + )) { throw 'Could not launch $url'; } } Future _launchInWebViewOrVC(String url) async { - if (await canLaunch(url)) { - await launch( - url, - forceSafariVC: true, - forceWebView: true, - headers: {'my_header_key': 'my_header_value'}, - ); - } else { + if (!await launch( + url, + forceSafariVC: true, + forceWebView: true, + headers: {'my_header_key': 'my_header_value'}, + )) { throw 'Could not launch $url'; } } Future _launchInWebViewWithJavaScript(String url) async { - if (await canLaunch(url)) { - await launch( - url, - forceSafariVC: true, - forceWebView: true, - enableJavaScript: true, - ); - } else { + if (!await launch( + url, + forceSafariVC: true, + forceWebView: true, + enableJavaScript: true, + )) { throw 'Could not launch $url'; } } Future _launchInWebViewWithDomStorage(String url) async { - if (await canLaunch(url)) { - await launch( - url, - forceSafariVC: true, - forceWebView: true, - enableDomStorage: true, - ); - } else { + if (!await launch( + url, + forceSafariVC: true, + forceWebView: true, + enableDomStorage: true, + )) { throw 'Could not launch $url'; } } Future _launchUniversalLinkIos(String url) async { - if (await canLaunch(url)) { - final bool nativeAppLaunchSucceeded = await launch( + final bool nativeAppLaunchSucceeded = await launch( + url, + forceSafariVC: false, + universalLinksOnly: true, + ); + if (!nativeAppLaunchSucceeded) { + await launch( url, - forceSafariVC: false, - universalLinksOnly: true, + forceSafariVC: true, ); - if (!nativeAppLaunchSucceeded) { - await launch( - url, - forceSafariVC: true, - ); - } } } @@ -115,16 +117,22 @@ class _MyHomePageState extends State { } } - Future _makePhoneCall(String url) async { - if (await canLaunch(url)) { - await launch(url); - } else { - throw 'Could not launch $url'; - } + Future _makePhoneCall(String phoneNumber) async { + // Use `Uri` to ensure that `phoneNumber` is properly URL-encoded. + // Just using 'tel:$phoneNumber' would create invalid URLs in some cases, + // such as spaces in the input, which would cause `launch` to fail on some + // platforms. + final Uri launchUri = Uri( + scheme: 'tel', + path: phoneNumber, + ); + await launch(launchUri.toString()); } @override Widget build(BuildContext context) { + // onPressed calls using this URL are not gated on a 'canLaunch' check + // because the assumption is that every device can launch a web URL. const String toLaunch = 'https://www.cylog.org/headers/'; return Scaffold( appBar: AppBar( @@ -143,10 +151,14 @@ class _MyHomePageState extends State { hintText: 'Input the phone number to launch')), ), ElevatedButton( - onPressed: () => setState(() { - _launched = _makePhoneCall('tel:$_phone'); - }), - child: const Text('Make phone call'), + onPressed: _hasCallSupport + ? () => setState(() { + _launched = _makePhoneCall(_phone); + }) + : null, + child: _hasCallSupport + ? const Text('Make phone call') + : const Text('Calling not supported'), ), const Padding( padding: EdgeInsets.all(16.0), diff --git a/packages/url_launcher/url_launcher/lib/url_launcher.dart b/packages/url_launcher/url_launcher/lib/url_launcher.dart index 300f96f4a179..f28c460cce4f 100644 --- a/packages/url_launcher/url_launcher/lib/url_launcher.dart +++ b/packages/url_launcher/url_launcher/lib/url_launcher.dart @@ -122,12 +122,11 @@ Future launch( /// Checks whether the specified URL can be handled by some app installed on the /// device. /// -/// On Android (from API 30), [canLaunch] will return `false` when the required -/// visibility configuration is not provided in the AndroidManifest.xml file. -/// For more information see the -/// [Package visibility filtering on Android](https://developer.android.com/training/basics/intents/package-visibility) -/// article in the Android documentation or the url_launcher example app's -/// [AndroidManifest.xml's queries element](https://github.com/flutter/plugins/blob/master/packages/url_launcher/url_launcher/example/android/app/src/main/AndroidManifest.xml). +/// On some systems, such as recent versions of Android and iOS, this will +/// always return false unless the application has been configuration to allow +/// querying the system for launch support. See +/// [the README](https://pub.dev/packages/url_launcher#configuration) for +/// details. Future canLaunch(String urlString) async { return await UrlLauncherPlatform.instance.canLaunch(urlString); } diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index 32967144dff7..9e7c8b27ba9f 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes. repository: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.0.14 +version: 6.0.15 environment: sdk: ">=2.14.0 <3.0.0" From 55e246bfa0fd43ff16dc0041084449e606d0fb3c Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Sat, 20 Nov 2021 10:56:58 -0500 Subject: [PATCH 006/600] [ci] Update labeler for special cases (#4528) Not all iOS implementation packages use `_ios` now. This adds `_storekit` and `_wkwebview` as known patterns for iOS implementation packages so that they will be labelled correctly. --- .github/labeler.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/labeler.yml b/.github/labeler.yml index 38ee94c004f9..d155607e6789 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -85,6 +85,8 @@ 'platform-ios': - packages/*/*_ios/**/* + - packages/*/*_storekit/**/* + - packages/*/*_wkwebview/**/* - packages/**/ios/**/* 'platform-linux': From 7f9baddb965092aaf71785271032db605b23e814 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 22 Nov 2021 13:46:37 -0500 Subject: [PATCH 007/600] [url_launcher] Federate mobile implementations (#4515) Fully federates the plugin by moving the existing mobile implementations to their own packages, per planned repo structure. Temporarily marks `url_launcher` as unpublishable to allow the implementations to be moved, rather than copied and deleted, in order to better preserve git history. A follow-up PR will restore it to publishable form. Part of https://github.com/flutter/flutter/issues/68498 --- .../url_launcher/url_launcher/CHANGELOG.md | 4 + .../url_launcher/android/settings.gradle | 1 - .../url_launcher/example/ios/Podfile | 3 - .../ios/Runner.xcodeproj/project.pbxproj | 251 ------ .../url_launcher/url_launcher/pubspec.yaml | 18 +- .../url_launcher/url_launcher_android/AUTHORS | 66 ++ .../url_launcher_android/CHANGELOG.md | 3 + .../url_launcher/url_launcher_android/LICENSE | 25 + .../url_launcher_android/README.md | 11 + .../android/build.gradle | 2 +- .../android/settings.gradle | 1 + .../android/src/main/AndroidManifest.xml | 0 .../urllauncher/MethodCallHandlerImpl.java | 0 .../plugins/urllauncher/UrlLauncher.java | 0 .../urllauncher/UrlLauncherPlugin.java | 0 .../plugins/urllauncher/WebViewActivity.java | 0 .../MethodCallHandlerImplTest.java | 0 .../urllauncher/WebViewActivityTest.java | 0 .../url_launcher_android/example/README.md | 8 + .../example/android/app/build.gradle | 60 ++ .../gradle/wrapper/gradle-wrapper.properties | 5 + .../flutter/plugins/DartIntegrationTest.java | 14 + .../FlutterActivityTest.java | 19 + .../android/app/src/main/AndroidManifest.xml | 39 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../example/android/build.gradle | 29 + .../example/android/gradle.properties | 4 + .../gradle/wrapper/gradle-wrapper.properties | 6 + .../example/android/settings.gradle | 15 + .../integration_test/url_launcher_test.dart | 23 + .../example/lib/main.dart | 207 +++++ .../url_launcher_android/example/pubspec.yaml | 30 + .../example/test_driver/integration_test.dart | 7 + .../url_launcher_android/pubspec.yaml | 31 + .../url_launcher/url_launcher_ios/AUTHORS | 66 ++ .../url_launcher_ios/CHANGELOG.md | 3 + .../url_launcher/url_launcher_ios/LICENSE | 25 + .../url_launcher/url_launcher_ios/README.md | 11 + .../url_launcher_ios/example/README.md | 8 + .../integration_test/url_launcher_test.dart | 26 + .../ios/Flutter/AppFrameworkInfo.plist | 30 + .../example/ios/Flutter/Debug.xcconfig | 2 + .../example/ios/Flutter/Release.xcconfig | 2 + .../url_launcher_ios/example/ios/Podfile | 41 + .../ios/Runner.xcodeproj/project.pbxproj | 718 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/xcschemes/Runner.xcscheme | 107 +++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/ios/Runner/AppDelegate.h | 10 + .../example/ios/Runner/AppDelegate.m | 17 + .../AppIcon.appiconset/Contents.json | 116 +++ .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 564 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 1588 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 1025 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 1716 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 1920 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 1895 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 3831 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 1888 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 3294 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 3612 bytes .../Runner/Base.lproj/LaunchScreen.storyboard | 27 + .../ios/Runner/Base.lproj/Main.storyboard | 26 + .../example/ios/Runner/Info.plist | 49 ++ .../example/ios/Runner/main.m | 13 + .../example/ios/RunnerTests/Info.plist | 0 .../ios/RunnerTests/URLLauncherTests.m | 2 +- .../example/ios/RunnerUITests/Info.plist | 0 .../ios/RunnerUITests/URLLauncherUITests.m | 0 .../url_launcher_ios/example/lib/main.dart | 241 ++++++ .../url_launcher_ios/example/pubspec.yaml | 30 + .../example/test_driver/integration_test.dart | 7 + .../ios/Assets/.gitkeep | 0 .../ios/Classes/FLTURLLauncherPlugin.h | 0 .../ios/Classes/FLTURLLauncherPlugin.m | 0 .../ios/url_launcher_ios.podspec} | 7 +- .../url_launcher_ios/pubspec.yaml | 30 + 86 files changed, 2252 insertions(+), 269 deletions(-) delete mode 100644 packages/url_launcher/url_launcher/android/settings.gradle create mode 100644 packages/url_launcher/url_launcher_android/AUTHORS create mode 100644 packages/url_launcher/url_launcher_android/CHANGELOG.md create mode 100644 packages/url_launcher/url_launcher_android/LICENSE create mode 100644 packages/url_launcher/url_launcher_android/README.md rename packages/url_launcher/{url_launcher => url_launcher_android}/android/build.gradle (98%) create mode 100644 packages/url_launcher/url_launcher_android/android/settings.gradle rename packages/url_launcher/{url_launcher => url_launcher_android}/android/src/main/AndroidManifest.xml (100%) rename packages/url_launcher/{url_launcher => url_launcher_android}/android/src/main/java/io/flutter/plugins/urllauncher/MethodCallHandlerImpl.java (100%) rename packages/url_launcher/{url_launcher => url_launcher_android}/android/src/main/java/io/flutter/plugins/urllauncher/UrlLauncher.java (100%) rename packages/url_launcher/{url_launcher => url_launcher_android}/android/src/main/java/io/flutter/plugins/urllauncher/UrlLauncherPlugin.java (100%) rename packages/url_launcher/{url_launcher => url_launcher_android}/android/src/main/java/io/flutter/plugins/urllauncher/WebViewActivity.java (100%) rename packages/url_launcher/{url_launcher => url_launcher_android}/android/src/test/java/io/flutter/plugins/urllauncher/MethodCallHandlerImplTest.java (100%) rename packages/url_launcher/{url_launcher => url_launcher_android}/android/src/test/java/io/flutter/plugins/urllauncher/WebViewActivityTest.java (100%) create mode 100644 packages/url_launcher/url_launcher_android/example/README.md create mode 100644 packages/url_launcher/url_launcher_android/example/android/app/build.gradle create mode 100644 packages/url_launcher/url_launcher_android/example/android/app/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/url_launcher/url_launcher_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java create mode 100644 packages/url_launcher/url_launcher_android/example/android/app/src/androidTest/java/io/flutter/plugins/urllauncherexample/FlutterActivityTest.java create mode 100644 packages/url_launcher/url_launcher_android/example/android/app/src/main/AndroidManifest.xml create mode 100644 packages/url_launcher/url_launcher_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 packages/url_launcher/url_launcher_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 packages/url_launcher/url_launcher_android/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 packages/url_launcher/url_launcher_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 packages/url_launcher/url_launcher_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 packages/url_launcher/url_launcher_android/example/android/build.gradle create mode 100644 packages/url_launcher/url_launcher_android/example/android/gradle.properties create mode 100644 packages/url_launcher/url_launcher_android/example/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/url_launcher/url_launcher_android/example/android/settings.gradle create mode 100644 packages/url_launcher/url_launcher_android/example/integration_test/url_launcher_test.dart create mode 100644 packages/url_launcher/url_launcher_android/example/lib/main.dart create mode 100644 packages/url_launcher/url_launcher_android/example/pubspec.yaml create mode 100644 packages/url_launcher/url_launcher_android/example/test_driver/integration_test.dart create mode 100644 packages/url_launcher/url_launcher_android/pubspec.yaml create mode 100644 packages/url_launcher/url_launcher_ios/AUTHORS create mode 100644 packages/url_launcher/url_launcher_ios/CHANGELOG.md create mode 100644 packages/url_launcher/url_launcher_ios/LICENSE create mode 100644 packages/url_launcher/url_launcher_ios/README.md create mode 100644 packages/url_launcher/url_launcher_ios/example/README.md create mode 100644 packages/url_launcher/url_launcher_ios/example/integration_test/url_launcher_test.dart create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Flutter/AppFrameworkInfo.plist create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Flutter/Debug.xcconfig create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Flutter/Release.xcconfig create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Podfile create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Runner.xcodeproj/project.pbxproj create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Runner/AppDelegate.h create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Runner/AppDelegate.m create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Runner/Base.lproj/Main.storyboard create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Runner/Info.plist create mode 100644 packages/url_launcher/url_launcher_ios/example/ios/Runner/main.m rename packages/url_launcher/{url_launcher => url_launcher_ios}/example/ios/RunnerTests/Info.plist (100%) rename packages/url_launcher/{url_launcher => url_launcher_ios}/example/ios/RunnerTests/URLLauncherTests.m (93%) rename packages/url_launcher/{url_launcher => url_launcher_ios}/example/ios/RunnerUITests/Info.plist (100%) rename packages/url_launcher/{url_launcher => url_launcher_ios}/example/ios/RunnerUITests/URLLauncherUITests.m (100%) create mode 100644 packages/url_launcher/url_launcher_ios/example/lib/main.dart create mode 100644 packages/url_launcher/url_launcher_ios/example/pubspec.yaml create mode 100644 packages/url_launcher/url_launcher_ios/example/test_driver/integration_test.dart rename packages/url_launcher/{url_launcher => url_launcher_ios}/ios/Assets/.gitkeep (100%) rename packages/url_launcher/{url_launcher => url_launcher_ios}/ios/Classes/FLTURLLauncherPlugin.h (100%) rename packages/url_launcher/{url_launcher => url_launcher_ios}/ios/Classes/FLTURLLauncherPlugin.m (100%) rename packages/url_launcher/{url_launcher/ios/url_launcher.podspec => url_launcher_ios/ios/url_launcher_ios.podspec} (89%) create mode 100644 packages/url_launcher/url_launcher_ios/pubspec.yaml diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index 7aab66301abc..a7feb1162163 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Moves Android and iOS implementations to federated packages. + ## 6.0.15 * Updates README: diff --git a/packages/url_launcher/url_launcher/android/settings.gradle b/packages/url_launcher/url_launcher/android/settings.gradle deleted file mode 100644 index 6620cd7dfb8b..000000000000 --- a/packages/url_launcher/url_launcher/android/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'url_launcher' diff --git a/packages/url_launcher/url_launcher/example/ios/Podfile b/packages/url_launcher/url_launcher/example/ios/Podfile index 3924e59aa0f9..f7d6a5e68c3a 100644 --- a/packages/url_launcher/url_launcher/example/ios/Podfile +++ b/packages/url_launcher/url_launcher/example/ios/Podfile @@ -29,9 +29,6 @@ flutter_ios_podfile_setup target 'Runner' do flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) - target 'RunnerTests' do - inherit! :search_paths - end end post_install do |installer| diff --git a/packages/url_launcher/url_launcher/example/ios/Runner.xcodeproj/project.pbxproj b/packages/url_launcher/url_launcher/example/ios/Runner.xcodeproj/project.pbxproj index 595f85d9a75b..7855640c017e 100644 --- a/packages/url_launcher/url_launcher/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/url_launcher/url_launcher/example/ios/Runner.xcodeproj/project.pbxproj @@ -15,28 +15,8 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - B8140773523F70A044426500 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 487A1B5A2ECB3E406FD62FE3 /* libPods-RunnerTests.a */; }; - F7151F4B26604CFB0028CB91 /* URLLauncherTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F7151F4A26604CFB0028CB91 /* URLLauncherTests.m */; }; - F7151F5926604D060028CB91 /* URLLauncherUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = F7151F5826604D060028CB91 /* URLLauncherUITests.m */; }; /* End PBXBuildFile section */ -/* Begin PBXContainerItemProxy section */ - F7151F4D26604CFB0028CB91 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 97C146E61CF9000F007C117D /* Project object */; - proxyType = 1; - remoteGlobalIDString = 97C146ED1CF9000F007C117D; - remoteInfo = Runner; - }; - F7151F5B26604D060028CB91 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 97C146E61CF9000F007C117D /* Project object */; - proxyType = 1; - remoteGlobalIDString = 97C146ED1CF9000F007C117D; - remoteInfo = Runner; - }; -/* End PBXContainerItemProxy section */ - /* Begin PBXCopyFilesBuildPhase section */ 9705A1C41CF9048500538489 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; @@ -71,12 +51,6 @@ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; A84BFEE343F54B983D1B67EB /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; D25C434271ACF6555E002440 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; - F7151F4826604CFB0028CB91 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - F7151F4A26604CFB0028CB91 /* URLLauncherTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = URLLauncherTests.m; sourceTree = ""; }; - F7151F4C26604CFB0028CB91 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - F7151F5626604D060028CB91 /* RunnerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - F7151F5826604D060028CB91 /* URLLauncherUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = URLLauncherUITests.m; sourceTree = ""; }; - F7151F5A26604D060028CB91 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -88,21 +62,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - F7151F4526604CFB0028CB91 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - B8140773523F70A044426500 /* libPods-RunnerTests.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F7151F5326604D060028CB91 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -135,8 +94,6 @@ 2D92223E1EC1DA93007564B0 /* GeneratedPluginRegistrant.m */, 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, - F7151F4926604CFB0028CB91 /* RunnerTests */, - F7151F5726604D060028CB91 /* RunnerUITests */, 97C146EF1CF9000F007C117D /* Products */, 840012C8B5EDBCF56B0E4AC1 /* Pods */, CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, @@ -147,8 +104,6 @@ isa = PBXGroup; children = ( 97C146EE1CF9000F007C117D /* Runner.app */, - F7151F4826604CFB0028CB91 /* RunnerTests.xctest */, - F7151F5626604D060028CB91 /* RunnerUITests.xctest */, ); name = Products; sourceTree = ""; @@ -184,24 +139,6 @@ name = Frameworks; sourceTree = ""; }; - F7151F4926604CFB0028CB91 /* RunnerTests */ = { - isa = PBXGroup; - children = ( - F7151F4A26604CFB0028CB91 /* URLLauncherTests.m */, - F7151F4C26604CFB0028CB91 /* Info.plist */, - ); - path = RunnerTests; - sourceTree = ""; - }; - F7151F5726604D060028CB91 /* RunnerUITests */ = { - isa = PBXGroup; - children = ( - F7151F5826604D060028CB91 /* URLLauncherUITests.m */, - F7151F5A26604D060028CB91 /* Info.plist */, - ); - path = RunnerUITests; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -226,43 +163,6 @@ productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; }; - F7151F4726604CFB0028CB91 /* RunnerTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = F7151F5126604CFB0028CB91 /* Build configuration list for PBXNativeTarget "RunnerTests" */; - buildPhases = ( - DD4687403C4F35FCD2994FDE /* [CP] Check Pods Manifest.lock */, - F7151F4426604CFB0028CB91 /* Sources */, - F7151F4526604CFB0028CB91 /* Frameworks */, - F7151F4626604CFB0028CB91 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - F7151F4E26604CFB0028CB91 /* PBXTargetDependency */, - ); - name = RunnerTests; - productName = RunnerTests; - productReference = F7151F4826604CFB0028CB91 /* RunnerTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - F7151F5526604D060028CB91 /* RunnerUITests */ = { - isa = PBXNativeTarget; - buildConfigurationList = F7151F5D26604D060028CB91 /* Build configuration list for PBXNativeTarget "RunnerUITests" */; - buildPhases = ( - F7151F5226604D060028CB91 /* Sources */, - F7151F5326604D060028CB91 /* Frameworks */, - F7151F5426604D060028CB91 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - F7151F5C26604D060028CB91 /* PBXTargetDependency */, - ); - name = RunnerUITests; - productName = RunnerUITests; - productReference = F7151F5626604D060028CB91 /* RunnerUITests.xctest */; - productType = "com.apple.product-type.bundle.ui-testing"; - }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -276,16 +176,6 @@ CreatedOnToolsVersion = 7.3.1; DevelopmentTeam = S8QB4VV633; }; - F7151F4726604CFB0028CB91 = { - CreatedOnToolsVersion = 12.5; - ProvisioningStyle = Automatic; - TestTargetID = 97C146ED1CF9000F007C117D; - }; - F7151F5526604D060028CB91 = { - CreatedOnToolsVersion = 12.5; - ProvisioningStyle = Automatic; - TestTargetID = 97C146ED1CF9000F007C117D; - }; }; }; buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; @@ -302,8 +192,6 @@ projectRoot = ""; targets = ( 97C146ED1CF9000F007C117D /* Runner */, - F7151F4726604CFB0028CB91 /* RunnerTests */, - F7151F5526604D060028CB91 /* RunnerUITests */, ); }; /* End PBXProject section */ @@ -320,20 +208,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - F7151F4626604CFB0028CB91 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F7151F5426604D060028CB91 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -383,28 +257,6 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - DD4687403C4F35FCD2994FDE /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -418,37 +270,8 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - F7151F4426604CFB0028CB91 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - F7151F4B26604CFB0028CB91 /* URLLauncherTests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F7151F5226604D060028CB91 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - F7151F5926604D060028CB91 /* URLLauncherUITests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXTargetDependency section */ - F7151F4E26604CFB0028CB91 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 97C146ED1CF9000F007C117D /* Runner */; - targetProxy = F7151F4D26604CFB0028CB91 /* PBXContainerItemProxy */; - }; - F7151F5C26604D060028CB91 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 97C146ED1CF9000F007C117D /* Runner */; - targetProxy = F7151F5B26604D060028CB91 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - /* Begin PBXVariantGroup section */ 97C146FA1CF9000F007C117D /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -617,62 +440,6 @@ }; name = Release; }; - F7151F4F26604CFB0028CB91 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 666BCD7C181C34F8BE58929B /* Pods-RunnerTests.debug.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = RunnerTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; - }; - name = Debug; - }; - F7151F5026604CFB0028CB91 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D25C434271ACF6555E002440 /* Pods-RunnerTests.release.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = RunnerTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; - }; - name = Release; - }; - F7151F5E26604D060028CB91 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = RunnerUITests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_TARGET_NAME = Runner; - }; - name = Debug; - }; - F7151F5F26604D060028CB91 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = RunnerUITests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_TARGET_NAME = Runner; - }; - name = Release; - }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -694,24 +461,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - F7151F5126604CFB0028CB91 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F7151F4F26604CFB0028CB91 /* Debug */, - F7151F5026604CFB0028CB91 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - F7151F5D26604D060028CB91 /* Build configuration list for PBXNativeTarget "RunnerUITests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F7151F5E26604D060028CB91 /* Debug */, - F7151F5F26604D060028CB91 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; /* End XCConfigurationList section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index 9e7c8b27ba9f..48cf5d2ba437 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -4,6 +4,9 @@ description: Flutter plugin for launching a URL. Supports repository: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 version: 6.0.15 +# Temporarily disable publishing to allow moving Android and iOS +# implementations. +publish_to: none environment: sdk: ">=2.14.0 <3.0.0" @@ -13,10 +16,9 @@ flutter: plugin: platforms: android: - package: io.flutter.plugins.urllauncher - pluginClass: UrlLauncherPlugin + default_package: url_launcher_android ios: - pluginClass: FLTURLLauncherPlugin + default_package: url_launcher_ios linux: default_package: url_laucher_linux macos: @@ -30,11 +32,11 @@ dependencies: flutter: sdk: flutter meta: ^1.3.0 - # The design on https://flutter.dev/go/federated-plugins was to leave - # implementation constraints as "any". We cannot do it right now as it fails pub publish - # validation, so we set a ^ constraint. - # TODO(amirh): Revisit this (either update this part in the design or the pub tool). - # https://github.com/flutter/flutter/issues/46264 + # Temporary path dependencies to allow moving Android and iOS implementations. + url_launcher_android: + path: ../url_launcher_android + url_launcher_ios: + path: ../url_launcher_ios url_launcher_linux: ^2.0.0 url_launcher_macos: ^2.0.0 url_launcher_platform_interface: ^2.0.3 diff --git a/packages/url_launcher/url_launcher_android/AUTHORS b/packages/url_launcher/url_launcher_android/AUTHORS new file mode 100644 index 000000000000..493a0b4ef9c2 --- /dev/null +++ b/packages/url_launcher/url_launcher_android/AUTHORS @@ -0,0 +1,66 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> diff --git a/packages/url_launcher/url_launcher_android/CHANGELOG.md b/packages/url_launcher/url_launcher_android/CHANGELOG.md new file mode 100644 index 000000000000..8b336a0d9a15 --- /dev/null +++ b/packages/url_launcher/url_launcher_android/CHANGELOG.md @@ -0,0 +1,3 @@ +## 6.0.13 + +* Splits from `shared_preferences` as a federated implementation. diff --git a/packages/url_launcher/url_launcher_android/LICENSE b/packages/url_launcher/url_launcher_android/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/url_launcher/url_launcher_android/LICENSE @@ -0,0 +1,25 @@ +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/url_launcher/url_launcher_android/README.md b/packages/url_launcher/url_launcher_android/README.md new file mode 100644 index 000000000000..bd3263a30e53 --- /dev/null +++ b/packages/url_launcher/url_launcher_android/README.md @@ -0,0 +1,11 @@ +# url\_launcher\_android + +The Android implementation of [`url_launcher`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use `url_launcher` +normally. This package will be automatically included in your app when you do. + +[1]: https://pub.dev/packages/url_launcher +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin diff --git a/packages/url_launcher/url_launcher/android/build.gradle b/packages/url_launcher/url_launcher_android/android/build.gradle similarity index 98% rename from packages/url_launcher/url_launcher/android/build.gradle rename to packages/url_launcher/url_launcher_android/android/build.gradle index d374d40534c3..180d7b2bdd9c 100644 --- a/packages/url_launcher/url_launcher/android/build.gradle +++ b/packages/url_launcher/url_launcher_android/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { minSdkVersion 16 diff --git a/packages/url_launcher/url_launcher_android/android/settings.gradle b/packages/url_launcher/url_launcher_android/android/settings.gradle new file mode 100644 index 000000000000..d8b7cc47172c --- /dev/null +++ b/packages/url_launcher/url_launcher_android/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'url_launcher_android' diff --git a/packages/url_launcher/url_launcher/android/src/main/AndroidManifest.xml b/packages/url_launcher/url_launcher_android/android/src/main/AndroidManifest.xml similarity index 100% rename from packages/url_launcher/url_launcher/android/src/main/AndroidManifest.xml rename to packages/url_launcher/url_launcher_android/android/src/main/AndroidManifest.xml diff --git a/packages/url_launcher/url_launcher/android/src/main/java/io/flutter/plugins/urllauncher/MethodCallHandlerImpl.java b/packages/url_launcher/url_launcher_android/android/src/main/java/io/flutter/plugins/urllauncher/MethodCallHandlerImpl.java similarity index 100% rename from packages/url_launcher/url_launcher/android/src/main/java/io/flutter/plugins/urllauncher/MethodCallHandlerImpl.java rename to packages/url_launcher/url_launcher_android/android/src/main/java/io/flutter/plugins/urllauncher/MethodCallHandlerImpl.java diff --git a/packages/url_launcher/url_launcher/android/src/main/java/io/flutter/plugins/urllauncher/UrlLauncher.java b/packages/url_launcher/url_launcher_android/android/src/main/java/io/flutter/plugins/urllauncher/UrlLauncher.java similarity index 100% rename from packages/url_launcher/url_launcher/android/src/main/java/io/flutter/plugins/urllauncher/UrlLauncher.java rename to packages/url_launcher/url_launcher_android/android/src/main/java/io/flutter/plugins/urllauncher/UrlLauncher.java diff --git a/packages/url_launcher/url_launcher/android/src/main/java/io/flutter/plugins/urllauncher/UrlLauncherPlugin.java b/packages/url_launcher/url_launcher_android/android/src/main/java/io/flutter/plugins/urllauncher/UrlLauncherPlugin.java similarity index 100% rename from packages/url_launcher/url_launcher/android/src/main/java/io/flutter/plugins/urllauncher/UrlLauncherPlugin.java rename to packages/url_launcher/url_launcher_android/android/src/main/java/io/flutter/plugins/urllauncher/UrlLauncherPlugin.java diff --git a/packages/url_launcher/url_launcher/android/src/main/java/io/flutter/plugins/urllauncher/WebViewActivity.java b/packages/url_launcher/url_launcher_android/android/src/main/java/io/flutter/plugins/urllauncher/WebViewActivity.java similarity index 100% rename from packages/url_launcher/url_launcher/android/src/main/java/io/flutter/plugins/urllauncher/WebViewActivity.java rename to packages/url_launcher/url_launcher_android/android/src/main/java/io/flutter/plugins/urllauncher/WebViewActivity.java diff --git a/packages/url_launcher/url_launcher/android/src/test/java/io/flutter/plugins/urllauncher/MethodCallHandlerImplTest.java b/packages/url_launcher/url_launcher_android/android/src/test/java/io/flutter/plugins/urllauncher/MethodCallHandlerImplTest.java similarity index 100% rename from packages/url_launcher/url_launcher/android/src/test/java/io/flutter/plugins/urllauncher/MethodCallHandlerImplTest.java rename to packages/url_launcher/url_launcher_android/android/src/test/java/io/flutter/plugins/urllauncher/MethodCallHandlerImplTest.java diff --git a/packages/url_launcher/url_launcher/android/src/test/java/io/flutter/plugins/urllauncher/WebViewActivityTest.java b/packages/url_launcher/url_launcher_android/android/src/test/java/io/flutter/plugins/urllauncher/WebViewActivityTest.java similarity index 100% rename from packages/url_launcher/url_launcher/android/src/test/java/io/flutter/plugins/urllauncher/WebViewActivityTest.java rename to packages/url_launcher/url_launcher_android/android/src/test/java/io/flutter/plugins/urllauncher/WebViewActivityTest.java diff --git a/packages/url_launcher/url_launcher_android/example/README.md b/packages/url_launcher/url_launcher_android/example/README.md new file mode 100644 index 000000000000..c200da8974d1 --- /dev/null +++ b/packages/url_launcher/url_launcher_android/example/README.md @@ -0,0 +1,8 @@ +# url_launcher_example + +Demonstrates how to use the url_launcher plugin. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](https://flutter.dev/). diff --git a/packages/url_launcher/url_launcher_android/example/android/app/build.gradle b/packages/url_launcher/url_launcher_android/example/android/app/build.gradle new file mode 100644 index 000000000000..ca41cb14b6f4 --- /dev/null +++ b/packages/url_launcher/url_launcher_android/example/android/app/build.gradle @@ -0,0 +1,60 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 31 + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + applicationId "io.flutter.plugins.urllauncherexample" + minSdkVersion 16 + targetSdkVersion 30 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' +} diff --git a/packages/url_launcher/url_launcher_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/url_launcher/url_launcher_android/example/android/app/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..9a4163a4f5ee --- /dev/null +++ b/packages/url_launcher/url_launcher_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/packages/url_launcher/url_launcher_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java b/packages/url_launcher/url_launcher_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java new file mode 100644 index 000000000000..0f4298dca155 --- /dev/null +++ b/packages/url_launcher/url_launcher_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java @@ -0,0 +1,14 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface DartIntegrationTest {} diff --git a/packages/url_launcher/url_launcher_android/example/android/app/src/androidTest/java/io/flutter/plugins/urllauncherexample/FlutterActivityTest.java b/packages/url_launcher/url_launcher_android/example/android/app/src/androidTest/java/io/flutter/plugins/urllauncherexample/FlutterActivityTest.java new file mode 100644 index 000000000000..67f15efb10aa --- /dev/null +++ b/packages/url_launcher/url_launcher_android/example/android/app/src/androidTest/java/io/flutter/plugins/urllauncherexample/FlutterActivityTest.java @@ -0,0 +1,19 @@ +// 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.urllauncherexample; + +import androidx.test.rule.ActivityTestRule; +import dev.flutter.plugins.integration_test.FlutterTestRunner; +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.plugins.DartIntegrationTest; +import org.junit.Rule; +import org.junit.runner.RunWith; + +@DartIntegrationTest +@RunWith(FlutterTestRunner.class) +public class FlutterActivityTest { + @Rule + public ActivityTestRule rule = new ActivityTestRule<>(FlutterActivity.class); +} diff --git a/packages/url_launcher/url_launcher_android/example/android/app/src/main/AndroidManifest.xml b/packages/url_launcher/url_launcher_android/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..918c29ee2dca --- /dev/null +++ b/packages/url_launcher/url_launcher_android/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/url_launcher/url_launcher_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/url_launcher/url_launcher_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/packages/url_launcher/url_launcher_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/url_launcher/url_launcher_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/packages/url_launcher/url_launcher_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/url_launcher/url_launcher_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/packages/url_launcher/url_launcher_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/url_launcher/url_launcher_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/packages/url_launcher/url_launcher_android/example/android/build.gradle b/packages/url_launcher/url_launcher_android/example/android/build.gradle new file mode 100644 index 000000000000..328175bb6ac5 --- /dev/null +++ b/packages/url_launcher/url_launcher_android/example/android/build.gradle @@ -0,0 +1,29 @@ +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:4.2.1' + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/packages/url_launcher/url_launcher_android/example/android/gradle.properties b/packages/url_launcher/url_launcher_android/example/android/gradle.properties new file mode 100644 index 000000000000..a6738207fd15 --- /dev/null +++ b/packages/url_launcher/url_launcher_android/example/android/gradle.properties @@ -0,0 +1,4 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true +android.enableR8=true diff --git a/packages/url_launcher/url_launcher_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/url_launcher/url_launcher_android/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..4ae10e927b38 --- /dev/null +++ b/packages/url_launcher/url_launcher_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Jul 31 20:16:04 BRT 2019 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip diff --git a/packages/url_launcher/url_launcher_android/example/android/settings.gradle b/packages/url_launcher/url_launcher_android/example/android/settings.gradle new file mode 100644 index 000000000000..115da6cb4f4d --- /dev/null +++ b/packages/url_launcher/url_launcher_android/example/android/settings.gradle @@ -0,0 +1,15 @@ +include ':app' + +def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + +def plugins = new Properties() +def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') +if (pluginsFile.exists()) { + pluginsFile.withInputStream { stream -> plugins.load(stream) } +} + +plugins.each { name, path -> + def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() + include ":$name" + project(":$name").projectDir = pluginDirectory +} diff --git a/packages/url_launcher/url_launcher_android/example/integration_test/url_launcher_test.dart b/packages/url_launcher/url_launcher_android/example/integration_test/url_launcher_test.dart new file mode 100644 index 000000000000..ac9f64c36ebc --- /dev/null +++ b/packages/url_launcher/url_launcher_android/example/integration_test/url_launcher_test.dart @@ -0,0 +1,23 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('canLaunch', (WidgetTester _) async { + UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + + expect(await launcher.canLaunch('randomstring'), false); + + // Generally all devices should have some default browser. + expect(await launcher.canLaunch('http://flutter.dev'), true); + + // sms:, tel:, and mailto: links may not be openable on every device, so + // aren't tested here. + }); +} diff --git a/packages/url_launcher/url_launcher_android/example/lib/main.dart b/packages/url_launcher/url_launcher_android/example/lib/main.dart new file mode 100644 index 000000000000..08992323f19c --- /dev/null +++ b/packages/url_launcher/url_launcher_android/example/lib/main.dart @@ -0,0 +1,207 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'URL Launcher', + theme: ThemeData( + primarySwatch: Colors.blue, + ), + home: MyHomePage(title: 'URL Launcher'), + ); + } +} + +class MyHomePage extends StatefulWidget { + MyHomePage({Key? key, required this.title}) : super(key: key); + final String title; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + Future? _launched; + String _phone = ''; + + Future _launchInBrowser(String url) async { + UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + if (await launcher.canLaunch(url)) { + await launcher.launch( + url, + useSafariVC: false, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: {'my_header_key': 'my_header_value'}, + ); + } else { + throw 'Could not launch $url'; + } + } + + Future _launchInWebViewOrVC(String url) async { + UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + if (await launcher.canLaunch(url)) { + await launcher.launch( + url, + useSafariVC: true, + useWebView: true, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: {'my_header_key': 'my_header_value'}, + ); + } else { + throw 'Could not launch $url'; + } + } + + Future _launchInWebViewWithJavaScript(String url) async { + UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + if (await launcher.canLaunch(url)) { + await launcher.launch( + url, + useSafariVC: true, + useWebView: true, + enableJavaScript: true, + enableDomStorage: false, + universalLinksOnly: false, + headers: {}, + ); + } else { + throw 'Could not launch $url'; + } + } + + Future _launchInWebViewWithDomStorage(String url) async { + UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + if (await launcher.canLaunch(url)) { + await launcher.launch( + url, + useSafariVC: true, + useWebView: true, + enableJavaScript: false, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + ); + } else { + throw 'Could not launch $url'; + } + } + + Widget _launchStatus(BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.hasError) { + return Text('Error: ${snapshot.error}'); + } else { + return const Text(''); + } + } + + Future _makePhoneCall(String url) async { + UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + if (await launcher.canLaunch(url)) { + await launcher.launch( + url, + useSafariVC: false, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: true, + headers: {}, + ); + } else { + throw 'Could not launch $url'; + } + } + + @override + Widget build(BuildContext context) { + const String toLaunch = 'https://www.cylog.org/headers/'; + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: ListView( + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: TextField( + onChanged: (String text) => _phone = text, + decoration: const InputDecoration( + hintText: 'Input the phone number to launch')), + ), + ElevatedButton( + onPressed: () => setState(() { + _launched = _makePhoneCall('tel:$_phone'); + }), + child: const Text('Make phone call'), + ), + const Padding( + padding: EdgeInsets.all(16.0), + child: Text(toLaunch), + ), + ElevatedButton( + onPressed: () => setState(() { + _launched = _launchInBrowser(toLaunch); + }), + child: const Text('Launch in browser'), + ), + const Padding(padding: EdgeInsets.all(16.0)), + ElevatedButton( + onPressed: () => setState(() { + _launched = _launchInWebViewOrVC(toLaunch); + }), + child: const Text('Launch in app'), + ), + ElevatedButton( + onPressed: () => setState(() { + _launched = _launchInWebViewWithJavaScript(toLaunch); + }), + child: const Text('Launch in app(JavaScript ON)'), + ), + ElevatedButton( + onPressed: () => setState(() { + _launched = _launchInWebViewWithDomStorage(toLaunch); + }), + child: const Text('Launch in app(DOM storage ON)'), + ), + const Padding(padding: EdgeInsets.all(16.0)), + ElevatedButton( + onPressed: () => setState(() { + _launched = _launchInWebViewOrVC(toLaunch); + Timer(const Duration(seconds: 5), () { + print('Closing WebView after 5 seconds...'); + UrlLauncherPlatform.instance.closeWebView(); + }); + }), + child: const Text('Launch in app + close after 5 seconds'), + ), + const Padding(padding: EdgeInsets.all(16.0)), + FutureBuilder(future: _launched, builder: _launchStatus), + ], + ), + ], + ), + ); + } +} diff --git a/packages/url_launcher/url_launcher_android/example/pubspec.yaml b/packages/url_launcher/url_launcher_android/example/pubspec.yaml new file mode 100644 index 000000000000..db22fd31b368 --- /dev/null +++ b/packages/url_launcher/url_launcher_android/example/pubspec.yaml @@ -0,0 +1,30 @@ +name: url_launcher_example +description: Demonstrates how to use the url_launcher plugin. +publish_to: none + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.5.0" + +dependencies: + flutter: + sdk: flutter + url_launcher_android: + # When depending on this package from a real application you should use: + # url_launcher_android: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + +dev_dependencies: + integration_test: + sdk: flutter + flutter_driver: + sdk: flutter + pedantic: ^1.10.0 + mockito: ^5.0.0 + plugin_platform_interface: ^2.0.0 + +flutter: + uses-material-design: true diff --git a/packages/url_launcher/url_launcher_android/example/test_driver/integration_test.dart b/packages/url_launcher/url_launcher_android/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4f10f2a522f3 --- /dev/null +++ b/packages/url_launcher/url_launcher_android/example/test_driver/integration_test.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); diff --git a/packages/url_launcher/url_launcher_android/pubspec.yaml b/packages/url_launcher/url_launcher_android/pubspec.yaml new file mode 100644 index 000000000000..0abed02d923e --- /dev/null +++ b/packages/url_launcher/url_launcher_android/pubspec.yaml @@ -0,0 +1,31 @@ +name: url_launcher_android +description: Android implementation of the url_launcher plugin. +repository: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_android +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 +version: 6.0.13 + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.5.0" + +flutter: + plugin: + implements: url_launcher + platforms: + android: + package: io.flutter.plugins.urllauncher + pluginClass: UrlLauncherPlugin + +dependencies: + flutter: + sdk: flutter + meta: ^1.3.0 + url_launcher_platform_interface: ^2.0.3 + +dev_dependencies: + flutter_test: + sdk: flutter + mockito: ^5.0.0 + pedantic: ^1.10.0 + plugin_platform_interface: ^2.0.0 + test: ^1.16.3 diff --git a/packages/url_launcher/url_launcher_ios/AUTHORS b/packages/url_launcher/url_launcher_ios/AUTHORS new file mode 100644 index 000000000000..493a0b4ef9c2 --- /dev/null +++ b/packages/url_launcher/url_launcher_ios/AUTHORS @@ -0,0 +1,66 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> diff --git a/packages/url_launcher/url_launcher_ios/CHANGELOG.md b/packages/url_launcher/url_launcher_ios/CHANGELOG.md new file mode 100644 index 000000000000..931d85e0af9f --- /dev/null +++ b/packages/url_launcher/url_launcher_ios/CHANGELOG.md @@ -0,0 +1,3 @@ +## 6.0.13 + +* Splits from `url_launcher` as a federated implementation. diff --git a/packages/url_launcher/url_launcher_ios/LICENSE b/packages/url_launcher/url_launcher_ios/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/url_launcher/url_launcher_ios/LICENSE @@ -0,0 +1,25 @@ +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/url_launcher/url_launcher_ios/README.md b/packages/url_launcher/url_launcher_ios/README.md new file mode 100644 index 000000000000..56beaff77d6f --- /dev/null +++ b/packages/url_launcher/url_launcher_ios/README.md @@ -0,0 +1,11 @@ +# url\_launcher\_ios + +The iOS implementation of [`url_launcher`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use `url_launcher` +normally. This package will be automatically included in your app when you do. + +[1]: https://pub.dev/packages/url_launcher +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin diff --git a/packages/url_launcher/url_launcher_ios/example/README.md b/packages/url_launcher/url_launcher_ios/example/README.md new file mode 100644 index 000000000000..c200da8974d1 --- /dev/null +++ b/packages/url_launcher/url_launcher_ios/example/README.md @@ -0,0 +1,8 @@ +# url_launcher_example + +Demonstrates how to use the url_launcher plugin. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](https://flutter.dev/). diff --git a/packages/url_launcher/url_launcher_ios/example/integration_test/url_launcher_test.dart b/packages/url_launcher/url_launcher_ios/example/integration_test/url_launcher_test.dart new file mode 100644 index 000000000000..f7cec29f9f22 --- /dev/null +++ b/packages/url_launcher/url_launcher_ios/example/integration_test/url_launcher_test.dart @@ -0,0 +1,26 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('canLaunch', (WidgetTester _) async { + UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + + expect(await launcher.canLaunch('randomstring'), false); + + // Generally all devices should have some default browser. + expect(await launcher.canLaunch('http://flutter.dev'), true); + + // SMS handling is available by default on test devices. + expect(await launcher.canLaunch('sms:5555555555'), true); + + // tel: and mailto: links may not be openable on every device. iOS + // simulators notably can't open these link types. + }); +} diff --git a/packages/url_launcher/url_launcher_ios/example/ios/Flutter/AppFrameworkInfo.plist b/packages/url_launcher/url_launcher_ios/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 000000000000..3a9c234f96d4 --- /dev/null +++ b/packages/url_launcher/url_launcher_ios/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + UIRequiredDeviceCapabilities + + arm64 + + MinimumOSVersion + 9.0 + + diff --git a/packages/url_launcher/url_launcher_ios/example/ios/Flutter/Debug.xcconfig b/packages/url_launcher/url_launcher_ios/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 000000000000..9803018ca79d --- /dev/null +++ b/packages/url_launcher/url_launcher_ios/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "Generated.xcconfig" +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" diff --git a/packages/url_launcher/url_launcher_ios/example/ios/Flutter/Release.xcconfig b/packages/url_launcher/url_launcher_ios/example/ios/Flutter/Release.xcconfig new file mode 100644 index 000000000000..a4a8c604e13d --- /dev/null +++ b/packages/url_launcher/url_launcher_ios/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include "Generated.xcconfig" +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" diff --git a/packages/url_launcher/url_launcher_ios/example/ios/Podfile b/packages/url_launcher/url_launcher_ios/example/ios/Podfile new file mode 100644 index 000000000000..3924e59aa0f9 --- /dev/null +++ b/packages/url_launcher/url_launcher_ios/example/ios/Podfile @@ -0,0 +1,41 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/packages/url_launcher/url_launcher_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/url_launcher/url_launcher_ios/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..595f85d9a75b --- /dev/null +++ b/packages/url_launcher/url_launcher_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,718 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 2D92223F1EC1DA93007564B0 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D92223E1EC1DA93007564B0 /* GeneratedPluginRegistrant.m */; }; + 2E37D9A274B2EACB147AC51B /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 856D0913184F79C678A42603 /* libPods-Runner.a */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; + 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + B8140773523F70A044426500 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 487A1B5A2ECB3E406FD62FE3 /* libPods-RunnerTests.a */; }; + F7151F4B26604CFB0028CB91 /* URLLauncherTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F7151F4A26604CFB0028CB91 /* URLLauncherTests.m */; }; + F7151F5926604D060028CB91 /* URLLauncherUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = F7151F5826604D060028CB91 /* URLLauncherUITests.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + F7151F4D26604CFB0028CB91 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; + F7151F5B26604D060028CB91 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 2D92223D1EC1DA93007564B0 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GeneratedPluginRegistrant.h; path = Runner/GeneratedPluginRegistrant.h; sourceTree = ""; }; + 2D92223E1EC1DA93007564B0 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GeneratedPluginRegistrant.m; path = Runner/GeneratedPluginRegistrant.m; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 487A1B5A2ECB3E406FD62FE3 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 666BCD7C181C34F8BE58929B /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 836316F9AEA584411312E29F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 856D0913184F79C678A42603 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A84BFEE343F54B983D1B67EB /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + D25C434271ACF6555E002440 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + F7151F4826604CFB0028CB91 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + F7151F4A26604CFB0028CB91 /* URLLauncherTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = URLLauncherTests.m; sourceTree = ""; }; + F7151F4C26604CFB0028CB91 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + F7151F5626604D060028CB91 /* RunnerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + F7151F5826604D060028CB91 /* URLLauncherUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = URLLauncherUITests.m; sourceTree = ""; }; + F7151F5A26604D060028CB91 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2E37D9A274B2EACB147AC51B /* libPods-Runner.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F7151F4526604CFB0028CB91 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + B8140773523F70A044426500 /* libPods-RunnerTests.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F7151F5326604D060028CB91 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 840012C8B5EDBCF56B0E4AC1 /* Pods */ = { + isa = PBXGroup; + children = ( + 836316F9AEA584411312E29F /* Pods-Runner.debug.xcconfig */, + A84BFEE343F54B983D1B67EB /* Pods-Runner.release.xcconfig */, + 666BCD7C181C34F8BE58929B /* Pods-RunnerTests.debug.xcconfig */, + D25C434271ACF6555E002440 /* Pods-RunnerTests.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 2D92223D1EC1DA93007564B0 /* GeneratedPluginRegistrant.h */, + 2D92223E1EC1DA93007564B0 /* GeneratedPluginRegistrant.m */, + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + F7151F4926604CFB0028CB91 /* RunnerTests */, + F7151F5726604D060028CB91 /* RunnerUITests */, + 97C146EF1CF9000F007C117D /* Products */, + 840012C8B5EDBCF56B0E4AC1 /* Pods */, + CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + F7151F4826604CFB0028CB91 /* RunnerTests.xctest */, + F7151F5626604D060028CB91 /* RunnerUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 97C146F11CF9000F007C117D /* Supporting Files */, + ); + path = Runner; + sourceTree = ""; + }; + 97C146F11CF9000F007C117D /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 97C146F21CF9000F007C117D /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + CF3B75C9A7D2FA2A4C99F110 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 856D0913184F79C678A42603 /* libPods-Runner.a */, + 487A1B5A2ECB3E406FD62FE3 /* libPods-RunnerTests.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + F7151F4926604CFB0028CB91 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + F7151F4A26604CFB0028CB91 /* URLLauncherTests.m */, + F7151F4C26604CFB0028CB91 /* Info.plist */, + ); + path = RunnerTests; + sourceTree = ""; + }; + F7151F5726604D060028CB91 /* RunnerUITests */ = { + isa = PBXGroup; + children = ( + F7151F5826604D060028CB91 /* URLLauncherUITests.m */, + F7151F5A26604D060028CB91 /* Info.plist */, + ); + path = RunnerUITests; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + AB1344B0443C71CD721E1BB7 /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; + F7151F4726604CFB0028CB91 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = F7151F5126604CFB0028CB91 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + DD4687403C4F35FCD2994FDE /* [CP] Check Pods Manifest.lock */, + F7151F4426604CFB0028CB91 /* Sources */, + F7151F4526604CFB0028CB91 /* Frameworks */, + F7151F4626604CFB0028CB91 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + F7151F4E26604CFB0028CB91 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = F7151F4826604CFB0028CB91 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + F7151F5526604D060028CB91 /* RunnerUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = F7151F5D26604D060028CB91 /* Build configuration list for PBXNativeTarget "RunnerUITests" */; + buildPhases = ( + F7151F5226604D060028CB91 /* Sources */, + F7151F5326604D060028CB91 /* Frameworks */, + F7151F5426604D060028CB91 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + F7151F5C26604D060028CB91 /* PBXTargetDependency */, + ); + name = RunnerUITests; + productName = RunnerUITests; + productReference = F7151F5626604D060028CB91 /* RunnerUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1100; + ORGANIZATIONNAME = "The Flutter Authors"; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + DevelopmentTeam = S8QB4VV633; + }; + F7151F4726604CFB0028CB91 = { + CreatedOnToolsVersion = 12.5; + ProvisioningStyle = Automatic; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + F7151F5526604D060028CB91 = { + CreatedOnToolsVersion = 12.5; + ProvisioningStyle = Automatic; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + F7151F4726604CFB0028CB91 /* RunnerTests */, + F7151F5526604D060028CB91 /* RunnerUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F7151F4626604CFB0028CB91 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F7151F5426604D060028CB91 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + AB1344B0443C71CD721E1BB7 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + DD4687403C4F35FCD2994FDE /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, + 97C146F31CF9000F007C117D /* main.m in Sources */, + 2D92223F1EC1DA93007564B0 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F7151F4426604CFB0028CB91 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F7151F4B26604CFB0028CB91 /* URLLauncherTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F7151F5226604D060028CB91 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F7151F5926604D060028CB91 /* URLLauncherUITests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + F7151F4E26604CFB0028CB91 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = F7151F4D26604CFB0028CB91 /* PBXContainerItemProxy */; + }; + F7151F5C26604D060028CB91 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = F7151F5B26604D060028CB91 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.urlLauncher; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.urlLauncher; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + F7151F4F26604CFB0028CB91 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 666BCD7C181C34F8BE58929B /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = RunnerTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Debug; + }; + F7151F5026604CFB0028CB91 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D25C434271ACF6555E002440 /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = RunnerTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Release; + }; + F7151F5E26604D060028CB91 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = RunnerUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_TARGET_NAME = Runner; + }; + name = Debug; + }; + F7151F5F26604D060028CB91 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = RunnerUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_TARGET_NAME = Runner; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F7151F5126604CFB0028CB91 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F7151F4F26604CFB0028CB91 /* Debug */, + F7151F5026604CFB0028CB91 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F7151F5D26604D060028CB91 /* Build configuration list for PBXNativeTarget "RunnerUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F7151F5E26604D060028CB91 /* Debug */, + F7151F5F26604D060028CB91 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/packages/url_launcher/url_launcher_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/url_launcher/url_launcher_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..919434a6254f --- /dev/null +++ b/packages/url_launcher/url_launcher_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/url_launcher/url_launcher_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/url_launcher/url_launcher_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000000..c5f1a9de4a30 --- /dev/null +++ b/packages/url_launcher/url_launcher_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/url_launcher/url_launcher_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/url_launcher/url_launcher_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..21a3cc14c74e --- /dev/null +++ b/packages/url_launcher/url_launcher_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/url_launcher/url_launcher_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/url_launcher/url_launcher_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/url_launcher/url_launcher_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/url_launcher/url_launcher_ios/example/ios/Runner/AppDelegate.h b/packages/url_launcher/url_launcher_ios/example/ios/Runner/AppDelegate.h new file mode 100644 index 000000000000..0681d288bb70 --- /dev/null +++ b/packages/url_launcher/url_launcher_ios/example/ios/Runner/AppDelegate.h @@ -0,0 +1,10 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import + +@interface AppDelegate : FlutterAppDelegate + +@end diff --git a/packages/url_launcher/url_launcher_ios/example/ios/Runner/AppDelegate.m b/packages/url_launcher/url_launcher_ios/example/ios/Runner/AppDelegate.m new file mode 100644 index 000000000000..83f0621aceba --- /dev/null +++ b/packages/url_launcher/url_launcher_ios/example/ios/Runner/AppDelegate.m @@ -0,0 +1,17 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "AppDelegate.h" +#include "GeneratedPluginRegistrant.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [GeneratedPluginRegistrant registerWithRegistry:self]; + [super application:application didFinishLaunchingWithOptions:launchOptions]; + return YES; +} + +@end diff --git a/packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000000..d22f10b2ab63 --- /dev/null +++ b/packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..28c6bf03016f6c994b70f38d1b7346e5831b531f GIT binary patch literal 564 zcmV-40?Yl0P)Px$?ny*JR5%f>l)FnDQ543{x%ZCiu33$Wg!pQFfT_}?5Q|_VSlIbLC`dpoMXL}9 zHfd9&47Mo(7D231gb+kjFxZHS4-m~7WurTH&doVX2KI5sU4v(sJ1@T9eCIKPjsqSr z)C01LsCxk=72-vXmX}CQD#BD;Cthymh&~=f$Q8nn0J<}ZrusBy4PvRNE}+1ceuj8u z0mW5k8fmgeLnTbWHGwfKA3@PdZxhn|PypR&^p?weGftrtCbjF#+zk_5BJh7;0`#Wr zgDpM_;Ax{jO##IrT`Oz;MvfwGfV$zD#c2xckpcXC6oou4ML~ezCc2EtnsQTB4tWNg z?4bkf;hG7IMfhgNI(FV5Gs4|*GyMTIY0$B=_*mso9Ityq$m^S>15>-?0(zQ<8Qy<_TjHE33(?_M8oaM zyc;NxzRVK@DL6RJnX%U^xW0Gpg(lXp(!uK1v0YgHjs^ZXSQ|m#lV7ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 literal 0 HcmV?d00001 diff --git a/packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..f091b6b0bca859a3f474b03065bef75ba58a9e4c GIT binary patch literal 1588 zcmV-42Fv-0P)C1SqPt}wig>|5Crh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)y zRAZ>eDe#*r`yDAVgB_R*LB*MAc)8(b{g{9McCXW!lq7r(btRoB9!8B-#AI6JMb~YFBEvdsV)`mEQO^&#eRKx@b&x- z5lZm*!WfD8oCLzfHGz#u7sT0^VLMI1MqGxF^v+`4YYnVYgk*=kU?HsSz{v({E3lb9 z>+xILjBN)t6`=g~IBOelGQ(O990@BfXf(DRI5I$qN$0Gkz-FSc$3a+2fX$AedL4u{ z4V+5Ong(9LiGcIKW?_352sR;LtDPmPJXI{YtT=O8=76o9;*n%_m|xo!i>7$IrZ-{l z-x3`7M}qzHsPV@$v#>H-TpjDh2UE$9g6sysUREDy_R(a)>=eHw-WAyfIN z*qb!_hW>G)Tu8nSw9yn#3wFMiLcfc4pY0ek1}8(NqkBR@t4{~oC>ryc-h_ByH(Cg5 z>ao-}771+xE3um9lWAY1FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zm zZQj(aA_HeBY&OC^jj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5Kh zX*|AU4QE#~SgPzOXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&g3j|zDgC+}2Q_v%YfDax z!?umcN^n}KYQ|a$Lr+51Nf9dkkYFSjZZjkma$0KOj+;aQ&721~t7QUKx61J3(P4P1 zstI~7-wOACnWP4=8oGOwz%vNDqD8w&Q`qcNGGrbbf&0s9L0De{4{mRS?o0MU+nR_! zrvshUau0G^DeMhM_v{5BuLjb#Hh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG z;P%vktHcXwTj~+IE(~px)3*MY77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC z(zYC$g|@+I+kY;dg_dE>scBf&bP1Nc@Hz<3R)V`=AGkc;8CXqdi=B4l2k|g;2%#m& z*jfX^%b!A8#bI!j9-0Fi0bOXl(-c^AB9|nQaE`*)Hw+o&jS9@7&Gov#HbD~#d{twV zXd^Tr^mWLfFh$@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)y zI9C9*oUga6=hxw6QasLPnee@3^Rr*M{CdaL5=R41nLs(AHk_=Y+A9$2&H(B7!_pURs&8aNw7?`&Z&xY_Ye z)~D5Bog^td-^QbUtkTirdyK^mTHAOuptDflut!#^lnKqU md>ggs(5nOWAqO?umG&QVYK#ibz}*4>0000U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~cTNYR~@Y9KepMPrrB8cABapAFa=`H+UGhkXUZV1GnwR1*lPyZ;*K(i~2gp|@bzp8}og7e*#% zEnr|^CWdVV!-4*Y_7rFvlww2Ze+>j*!Z!pQ?2l->4q#nqRu9`ELo6RMS5=br47g_X zRw}P9a7RRYQ%2Vsd0Me{_(EggTnuN6j=-?uFS6j^u69elMypu?t>op*wBx<=Wx8?( ztpe^(fwM6jJX7M-l*k3kEpWOl_Vk3@(_w4oc}4YF4|Rt=2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX?F&rExl^W|G8Z8;`5z-k}OGNZ0#WVb$WCpQu-_YsiqKP?BB# vzVHS-CTUF4Ozn5G+mq_~Qqto~ahA+K`|lyv3(-e}00000NkvXXu0mjfd`9t{ literal 0 HcmV?d00001 diff --git a/packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d0ef06e7edb86cdfe0d15b4b0d98334a86163658 GIT binary patch literal 1716 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 literal 0 HcmV?d00001 diff --git a/packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c8f9ed8f5cee1c98386d13b17e89f719e83555b2 GIT binary patch literal 1895 zcmV-t2blPYP)FQtfgmafE#=YDCq`qUBt#QpG%*H6QHY765~R=q zZ6iudfM}q!Pz#~9JgOi8QJ|DSu?1-*(kSi1K4#~5?#|rh?sS)(-JQqX*}ciXJ56_H zdw=^s_srbAdqxlvGyrgGet#6T7_|j;95sL%MtM;q86vOxKM$f#puR)Bjv9Zvz9-di zXOTSsZkM83)E9PYBXC<$6(|>lNLVBb&&6y{NByFCp%6+^ALR@NCTse_wqvNmSWI-m z!$%KlHFH2omF!>#%1l3LTZg(s7eof$7*xB)ZQ0h?ejh?Ta9fDv59+u#MokW+1t8Zb zgHv%K(u9G^Lv`lh#f3<6!JVTL3(dCpxHbnbA;kKqQyd1~^Xe0VIaYBSWm6nsr;dFj z4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3QU4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKubPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`Q zykI#*+rQ=z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRe zt3L_uNyQ*cE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=ky zx=~RKa4{iTm{_>_vSCm?$Ej=i6@=m%@VvAITnigVg{&@!7CDgs908761meDK5azA} z4?=NOH|PdvabgJ&fW2{Mo$Q0CcD8Qc84%{JPYt5EiG{MdLIAeX%T=D7NIP4%Hw}p9 zg)==!2Lbp#j{u_}hMiao9=!VSyx0gHbeCS`;q&vzeq|fs`y&^X-lso(Ls@-706qmA z7u*T5PMo_w3{se1t2`zWeO^hOvTsohG_;>J0wVqVe+n)AbQCx)yh9;w+J6?NF5Lmo zecS@ieAKL8%bVd@+-KT{yI|S}O>pYckUFs;ry9Ow$CD@ztz5K-*D$^{i(_1llhSh^ zEkL$}tsQt5>QA^;QgjgIfBDmcOgi5YDyu?t6vSnbp=1+@6D& z5MJ}B8q;bRlVoxasyhcUF1+)o`&3r0colr}QJ3hcSdLu;9;td>kf@Tcn<@9sIx&=m z;AD;SCh95=&p;$r{Xz3iWCO^MX83AGJ(yH&eTXgv|0=34#-&WAmw{)U7OU9!Wz^!7 zZ%jZFi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i h0WYBP*#0Ks^FNSabJA*5${_#%002ovPDHLkV1oKhTl@e3 literal 0 HcmV?d00001 diff --git a/packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..75b2d164a5a98e212cca15ea7bf2ab5de5108680 GIT binary patch literal 3831 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 literal 0 HcmV?d00001 diff --git a/packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/url_launcher/url_launcher_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..c4df70d39da7941ef3f6dcb7f06a192d8dcb308d GIT binary patch literal 1888 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 iW8 + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/url_launcher/url_launcher_ios/example/ios/Runner/Base.lproj/Main.storyboard b/packages/url_launcher/url_launcher_ios/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 000000000000..f3c28516fb38 --- /dev/null +++ b/packages/url_launcher/url_launcher_ios/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/url_launcher/url_launcher_ios/example/ios/Runner/Info.plist b/packages/url_launcher/url_launcher_ios/example/ios/Runner/Info.plist new file mode 100644 index 000000000000..80aec052fa79 --- /dev/null +++ b/packages/url_launcher/url_launcher_ios/example/ios/Runner/Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + url_launcher_example + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/packages/url_launcher/url_launcher_ios/example/ios/Runner/main.m b/packages/url_launcher/url_launcher_ios/example/ios/Runner/main.m new file mode 100644 index 000000000000..f97b9ef5c8a1 --- /dev/null +++ b/packages/url_launcher/url_launcher_ios/example/ios/Runner/main.m @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import +#import "AppDelegate.h" + +int main(int argc, char* argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/packages/url_launcher/url_launcher/example/ios/RunnerTests/Info.plist b/packages/url_launcher/url_launcher_ios/example/ios/RunnerTests/Info.plist similarity index 100% rename from packages/url_launcher/url_launcher/example/ios/RunnerTests/Info.plist rename to packages/url_launcher/url_launcher_ios/example/ios/RunnerTests/Info.plist diff --git a/packages/url_launcher/url_launcher/example/ios/RunnerTests/URLLauncherTests.m b/packages/url_launcher/url_launcher_ios/example/ios/RunnerTests/URLLauncherTests.m similarity index 93% rename from packages/url_launcher/url_launcher/example/ios/RunnerTests/URLLauncherTests.m rename to packages/url_launcher/url_launcher_ios/example/ios/RunnerTests/URLLauncherTests.m index 746089425f7a..a94bd88f24fe 100644 --- a/packages/url_launcher/url_launcher/example/ios/RunnerTests/URLLauncherTests.m +++ b/packages/url_launcher/url_launcher_ios/example/ios/RunnerTests/URLLauncherTests.m @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@import url_launcher; +@import url_launcher_ios; @import XCTest; @interface URLLauncherTests : XCTestCase diff --git a/packages/url_launcher/url_launcher/example/ios/RunnerUITests/Info.plist b/packages/url_launcher/url_launcher_ios/example/ios/RunnerUITests/Info.plist similarity index 100% rename from packages/url_launcher/url_launcher/example/ios/RunnerUITests/Info.plist rename to packages/url_launcher/url_launcher_ios/example/ios/RunnerUITests/Info.plist diff --git a/packages/url_launcher/url_launcher/example/ios/RunnerUITests/URLLauncherUITests.m b/packages/url_launcher/url_launcher_ios/example/ios/RunnerUITests/URLLauncherUITests.m similarity index 100% rename from packages/url_launcher/url_launcher/example/ios/RunnerUITests/URLLauncherUITests.m rename to packages/url_launcher/url_launcher_ios/example/ios/RunnerUITests/URLLauncherUITests.m diff --git a/packages/url_launcher/url_launcher_ios/example/lib/main.dart b/packages/url_launcher/url_launcher_ios/example/lib/main.dart new file mode 100644 index 000000000000..a16a4dd70890 --- /dev/null +++ b/packages/url_launcher/url_launcher_ios/example/lib/main.dart @@ -0,0 +1,241 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'URL Launcher', + theme: ThemeData( + primarySwatch: Colors.blue, + ), + home: MyHomePage(title: 'URL Launcher'), + ); + } +} + +class MyHomePage extends StatefulWidget { + MyHomePage({Key? key, required this.title}) : super(key: key); + final String title; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + Future? _launched; + String _phone = ''; + + Future _launchInBrowser(String url) async { + UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + if (await launcher.canLaunch(url)) { + await launcher.launch( + url, + useSafariVC: false, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: {'my_header_key': 'my_header_value'}, + ); + } else { + throw 'Could not launch $url'; + } + } + + Future _launchInWebViewOrVC(String url) async { + UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + if (await launcher.canLaunch(url)) { + await launcher.launch( + url, + useSafariVC: true, + useWebView: true, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: {'my_header_key': 'my_header_value'}, + ); + } else { + throw 'Could not launch $url'; + } + } + + Future _launchInWebViewWithJavaScript(String url) async { + UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + if (await launcher.canLaunch(url)) { + await launcher.launch( + url, + useSafariVC: true, + useWebView: true, + enableJavaScript: true, + enableDomStorage: false, + universalLinksOnly: false, + headers: {}, + ); + } else { + throw 'Could not launch $url'; + } + } + + Future _launchInWebViewWithDomStorage(String url) async { + UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + if (await launcher.canLaunch(url)) { + await launcher.launch( + url, + useSafariVC: true, + useWebView: true, + enableJavaScript: false, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + ); + } else { + throw 'Could not launch $url'; + } + } + + Future _launchUniversalLinkIos(String url) async { + UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + if (await launcher.canLaunch(url)) { + final bool nativeAppLaunchSucceeded = await launcher.launch( + url, + useSafariVC: false, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: true, + headers: {}, + ); + if (!nativeAppLaunchSucceeded) { + await launcher.launch( + url, + useSafariVC: true, + useWebView: true, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: true, + headers: {}, + ); + } + } + } + + Widget _launchStatus(BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.hasError) { + return Text('Error: ${snapshot.error}'); + } else { + return const Text(''); + } + } + + Future _makePhoneCall(String url) async { + UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + if (await launcher.canLaunch(url)) { + await launcher.launch( + url, + useSafariVC: false, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: true, + headers: {}, + ); + } else { + throw 'Could not launch $url'; + } + } + + @override + Widget build(BuildContext context) { + const String toLaunch = 'https://www.cylog.org/headers/'; + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: ListView( + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: TextField( + onChanged: (String text) => _phone = text, + decoration: const InputDecoration( + hintText: 'Input the phone number to launch')), + ), + ElevatedButton( + onPressed: () => setState(() { + _launched = _makePhoneCall('tel:$_phone'); + }), + child: const Text('Make phone call'), + ), + const Padding( + padding: EdgeInsets.all(16.0), + child: Text(toLaunch), + ), + ElevatedButton( + onPressed: () => setState(() { + _launched = _launchInBrowser(toLaunch); + }), + child: const Text('Launch in browser'), + ), + const Padding(padding: EdgeInsets.all(16.0)), + ElevatedButton( + onPressed: () => setState(() { + _launched = _launchInWebViewOrVC(toLaunch); + }), + child: const Text('Launch in app'), + ), + ElevatedButton( + onPressed: () => setState(() { + _launched = _launchInWebViewWithJavaScript(toLaunch); + }), + child: const Text('Launch in app(JavaScript ON)'), + ), + ElevatedButton( + onPressed: () => setState(() { + _launched = _launchInWebViewWithDomStorage(toLaunch); + }), + child: const Text('Launch in app(DOM storage ON)'), + ), + const Padding(padding: EdgeInsets.all(16.0)), + ElevatedButton( + onPressed: () => setState(() { + _launched = _launchUniversalLinkIos(toLaunch); + }), + child: const Text( + 'Launch a universal link in a native app, fallback to Safari.(Youtube)'), + ), + const Padding(padding: EdgeInsets.all(16.0)), + ElevatedButton( + onPressed: () => setState(() { + _launched = _launchInWebViewOrVC(toLaunch); + Timer(const Duration(seconds: 5), () { + print('Closing WebView after 5 seconds...'); + UrlLauncherPlatform.instance.closeWebView(); + }); + }), + child: const Text('Launch in app + close after 5 seconds'), + ), + const Padding(padding: EdgeInsets.all(16.0)), + FutureBuilder(future: _launched, builder: _launchStatus), + ], + ), + ], + ), + ); + } +} diff --git a/packages/url_launcher/url_launcher_ios/example/pubspec.yaml b/packages/url_launcher/url_launcher_ios/example/pubspec.yaml new file mode 100644 index 000000000000..2af807dbba6b --- /dev/null +++ b/packages/url_launcher/url_launcher_ios/example/pubspec.yaml @@ -0,0 +1,30 @@ +name: url_launcher_example +description: Demonstrates how to use the url_launcher plugin. +publish_to: none + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.5.0" + +dependencies: + flutter: + sdk: flutter + url_launcher_ios: + # When depending on this package from a real application you should use: + # url_launcher_ios: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + +dev_dependencies: + integration_test: + sdk: flutter + flutter_driver: + sdk: flutter + pedantic: ^1.10.0 + mockito: ^5.0.0 + plugin_platform_interface: ^2.0.0 + +flutter: + uses-material-design: true diff --git a/packages/url_launcher/url_launcher_ios/example/test_driver/integration_test.dart b/packages/url_launcher/url_launcher_ios/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4f10f2a522f3 --- /dev/null +++ b/packages/url_launcher/url_launcher_ios/example/test_driver/integration_test.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); diff --git a/packages/url_launcher/url_launcher/ios/Assets/.gitkeep b/packages/url_launcher/url_launcher_ios/ios/Assets/.gitkeep similarity index 100% rename from packages/url_launcher/url_launcher/ios/Assets/.gitkeep rename to packages/url_launcher/url_launcher_ios/ios/Assets/.gitkeep diff --git a/packages/url_launcher/url_launcher/ios/Classes/FLTURLLauncherPlugin.h b/packages/url_launcher/url_launcher_ios/ios/Classes/FLTURLLauncherPlugin.h similarity index 100% rename from packages/url_launcher/url_launcher/ios/Classes/FLTURLLauncherPlugin.h rename to packages/url_launcher/url_launcher_ios/ios/Classes/FLTURLLauncherPlugin.h diff --git a/packages/url_launcher/url_launcher/ios/Classes/FLTURLLauncherPlugin.m b/packages/url_launcher/url_launcher_ios/ios/Classes/FLTURLLauncherPlugin.m similarity index 100% rename from packages/url_launcher/url_launcher/ios/Classes/FLTURLLauncherPlugin.m rename to packages/url_launcher/url_launcher_ios/ios/Classes/FLTURLLauncherPlugin.m diff --git a/packages/url_launcher/url_launcher/ios/url_launcher.podspec b/packages/url_launcher/url_launcher_ios/ios/url_launcher_ios.podspec similarity index 89% rename from packages/url_launcher/url_launcher/ios/url_launcher.podspec rename to packages/url_launcher/url_launcher_ios/ios/url_launcher_ios.podspec index 6af64b66bee4..1ee2e8776b85 100644 --- a/packages/url_launcher/url_launcher/ios/url_launcher.podspec +++ b/packages/url_launcher/url_launcher_ios/ios/url_launcher_ios.podspec @@ -2,7 +2,7 @@ # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html # Pod::Spec.new do |s| - s.name = 'url_launcher' + s.name = 'url_launcher_ios' s.version = '0.0.1' s.summary = 'Flutter plugin for launching a URL.' s.description = <<-DESC @@ -11,13 +11,12 @@ A Flutter plugin for making the underlying platform (Android or iOS) launch a UR s.homepage = 'https://github.com/flutter/plugins/tree/master/packages/url_launcher' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/url_launcher' } + s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_ios' } s.documentation_url = 'https://pub.dev/packages/url_launcher' s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' s.dependency 'Flutter' - + s.platform = :ios, '9.0' s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } end - diff --git a/packages/url_launcher/url_launcher_ios/pubspec.yaml b/packages/url_launcher/url_launcher_ios/pubspec.yaml new file mode 100644 index 000000000000..dfd4f797f4ac --- /dev/null +++ b/packages/url_launcher/url_launcher_ios/pubspec.yaml @@ -0,0 +1,30 @@ +name: url_launcher_ios +description: iOS implementation of the url_launcher plugin. +repository: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_ios +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 +version: 6.0.13 + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.5.0" + +flutter: + plugin: + implements: url_launcher + platforms: + ios: + pluginClass: FLTURLLauncherPlugin + +dependencies: + flutter: + sdk: flutter + meta: ^1.3.0 + url_launcher_platform_interface: ^2.0.3 + +dev_dependencies: + flutter_test: + sdk: flutter + mockito: ^5.0.0 + pedantic: ^1.10.0 + plugin_platform_interface: ^2.0.0 + test: ^1.16.3 From 16fa1ed54e81155092f26a158ee2bfab026a9cc4 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 22 Nov 2021 16:19:42 -0500 Subject: [PATCH 008/600] Update some plugins targetSdkVersions to 31 (#4535) Precursor to https://github.com/flutter/plugins/pull/4533 On master channel, plugin unit tests must now build with SDK version 31. These plugins are transitive dependencies of other plugins' examples, which causes unit test failures in those plugins's unit test builds, so these need to be landed and publish in order for that PR to pass presubmit. --- packages/flutter_plugin_android_lifecycle/CHANGELOG.md | 4 ++-- .../flutter_plugin_android_lifecycle/android/build.gradle | 2 +- packages/flutter_plugin_android_lifecycle/pubspec.yaml | 2 +- packages/path_provider/path_provider_android/CHANGELOG.md | 4 ++++ .../path_provider/path_provider_android/android/build.gradle | 2 +- packages/path_provider/path_provider_android/pubspec.yaml | 2 +- .../shared_preferences_android/CHANGELOG.md | 4 ++++ .../shared_preferences_android/android/build.gradle | 2 +- .../example/android/app/build.gradle | 2 +- .../shared_preferences_android/pubspec.yaml | 2 +- 10 files changed, 17 insertions(+), 9 deletions(-) diff --git a/packages/flutter_plugin_android_lifecycle/CHANGELOG.md b/packages/flutter_plugin_android_lifecycle/CHANGELOG.md index 414cf8e1aeda..8b110b74acfc 100644 --- a/packages/flutter_plugin_android_lifecycle/CHANGELOG.md +++ b/packages/flutter_plugin_android_lifecycle/CHANGELOG.md @@ -1,6 +1,6 @@ -## NEXT +## 2.0.5 -* Updates example app Android compileSdkVersion to 31. +* Updates compileSdkVersion to 31. ## 2.0.4 diff --git a/packages/flutter_plugin_android_lifecycle/android/build.gradle b/packages/flutter_plugin_android_lifecycle/android/build.gradle index 5a584b4e366f..7f44c6584456 100644 --- a/packages/flutter_plugin_android_lifecycle/android/build.gradle +++ b/packages/flutter_plugin_android_lifecycle/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { minSdkVersion 16 diff --git a/packages/flutter_plugin_android_lifecycle/pubspec.yaml b/packages/flutter_plugin_android_lifecycle/pubspec.yaml index 8535de17a51c..20cae3526e82 100644 --- a/packages/flutter_plugin_android_lifecycle/pubspec.yaml +++ b/packages/flutter_plugin_android_lifecycle/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_plugin_android_lifecycle description: Flutter plugin for accessing an Android Lifecycle within other plugins. repository: https://github.com/flutter/plugins/tree/master/packages/flutter_plugin_android_lifecycle issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_plugin_android_lifecycle%22 -version: 2.0.4 +version: 2.0.5 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/path_provider/path_provider_android/CHANGELOG.md b/packages/path_provider/path_provider_android/CHANGELOG.md index ed14cb703d41..628bd442c42f 100644 --- a/packages/path_provider/path_provider_android/CHANGELOG.md +++ b/packages/path_provider/path_provider_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.9 + +* Updates Android compileSdkVersion to 31. + ## 2.0.8 * Updates example app Android compileSdkVersion to 31. diff --git a/packages/path_provider/path_provider_android/android/build.gradle b/packages/path_provider/path_provider_android/android/build.gradle index 1a22f135fe5a..c7ceec2584c6 100644 --- a/packages/path_provider/path_provider_android/android/build.gradle +++ b/packages/path_provider/path_provider_android/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { minSdkVersion 16 diff --git a/packages/path_provider/path_provider_android/pubspec.yaml b/packages/path_provider/path_provider_android/pubspec.yaml index 2275316ecadd..09b26a5efedb 100644 --- a/packages/path_provider/path_provider_android/pubspec.yaml +++ b/packages/path_provider/path_provider_android/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_android description: Android implementation of the path_provider plugin. repository: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.8 +version: 2.0.9 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md index 85baf316f0ae..16b8cc5b64f8 100644 --- a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.9 + +* Updates compileSdkVersion to 31. + ## 2.0.8 * Split from `shared_preferences` as a federated implementation. diff --git a/packages/shared_preferences/shared_preferences_android/android/build.gradle b/packages/shared_preferences/shared_preferences_android/android/build.gradle index 9284f1c36143..6eeab718059f 100644 --- a/packages/shared_preferences/shared_preferences_android/android/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/android/build.gradle @@ -30,7 +30,7 @@ allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { minSdkVersion 16 diff --git a/packages/shared_preferences/shared_preferences_android/example/android/app/build.gradle b/packages/shared_preferences/shared_preferences_android/example/android/app/build.gradle index 857ac4730a77..4cbb7307769c 100644 --- a/packages/shared_preferences/shared_preferences_android/example/android/app/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/example/android/app/build.gradle @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 30 + compileSdkVersion 31 sourceSets { main.java.srcDirs += 'src/main/kotlin' diff --git a/packages/shared_preferences/shared_preferences_android/pubspec.yaml b/packages/shared_preferences/shared_preferences_android/pubspec.yaml index 06c2ad57c7d1..3f749d0b1939 100644 --- a/packages/shared_preferences/shared_preferences_android/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_android/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_android description: Android implementation of the shared_preferences plugin repository: https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.8 +version: 2.0.9 environment: sdk: ">=2.14.0 <3.0.0" From bb65f91d3f3da4e593ebc3db712816a260b305f3 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 22 Nov 2021 19:05:37 -0500 Subject: [PATCH 009/600] Update all targetSdkVersions and enable multidex (#4533) --- packages/android_alarm_manager/CHANGELOG.md | 2 +- packages/android_alarm_manager/android/build.gradle | 2 +- .../android_alarm_manager/example/android/app/build.gradle | 2 +- packages/android_intent/CHANGELOG.md | 2 +- packages/android_intent/android/build.gradle | 2 +- packages/battery/battery/CHANGELOG.md | 2 +- packages/battery/battery/android/build.gradle | 2 +- packages/connectivity/connectivity/CHANGELOG.md | 2 +- packages/connectivity/connectivity/android/build.gradle | 2 +- packages/device_info/device_info/CHANGELOG.md | 2 +- packages/device_info/device_info/android/build.gradle | 2 +- packages/espresso/CHANGELOG.md | 2 +- packages/espresso/android/build.gradle | 2 +- packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md | 2 +- .../google_maps_flutter/android/build.gradle | 2 +- packages/google_sign_in/google_sign_in/CHANGELOG.md | 2 +- packages/google_sign_in/google_sign_in/android/build.gradle | 2 +- packages/image_picker/image_picker/CHANGELOG.md | 2 +- packages/image_picker/image_picker/android/build.gradle | 2 +- packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md | 2 +- .../in_app_purchase_android/android/build.gradle | 2 +- packages/local_auth/CHANGELOG.md | 2 +- packages/local_auth/android/build.gradle | 2 +- packages/package_info/CHANGELOG.md | 2 +- packages/package_info/android/build.gradle | 2 +- packages/quick_actions/quick_actions/CHANGELOG.md | 4 ++++ packages/quick_actions/quick_actions/android/build.gradle | 2 +- .../quick_actions/example/android/app/build.gradle | 2 +- packages/sensors/CHANGELOG.md | 2 +- packages/sensors/android/build.gradle | 2 +- packages/share/CHANGELOG.md | 2 +- packages/share/android/build.gradle | 2 +- packages/video_player/video_player/CHANGELOG.md | 2 +- packages/video_player/video_player/android/build.gradle | 2 +- .../video_player/example/android/app/build.gradle | 2 +- .../webview_flutter/example/android/app/build.gradle | 2 +- packages/webview_flutter/webview_flutter_android/CHANGELOG.md | 2 +- .../webview_flutter_android/android/build.gradle | 2 +- .../webview_flutter_android/example/android/app/build.gradle | 2 +- packages/wifi_info_flutter/wifi_info_flutter/CHANGELOG.md | 2 +- .../wifi_info_flutter/wifi_info_flutter/android/build.gradle | 2 +- 41 files changed, 44 insertions(+), 40 deletions(-) diff --git a/packages/android_alarm_manager/CHANGELOG.md b/packages/android_alarm_manager/CHANGELOG.md index 7d90afbe4de2..d57dbed9ede1 100644 --- a/packages/android_alarm_manager/CHANGELOG.md +++ b/packages/android_alarm_manager/CHANGELOG.md @@ -3,7 +3,7 @@ * Remove support for the V1 Android embedding. * Updated Android lint settings. * Removed `-Werror` in Android builds. -* Updates example app Android compileSdkVersion to 31. +* Updates compileSdkVersion to 31. ## 2.0.2 diff --git a/packages/android_alarm_manager/android/build.gradle b/packages/android_alarm_manager/android/build.gradle index 9236ffbdd086..4862c7942afa 100644 --- a/packages/android_alarm_manager/android/build.gradle +++ b/packages/android_alarm_manager/android/build.gradle @@ -27,7 +27,7 @@ project.getTasks().withType(JavaCompile){ apply plugin: 'com.android.library' android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { minSdkVersion 16 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/packages/android_alarm_manager/example/android/app/build.gradle b/packages/android_alarm_manager/example/android/app/build.gradle index 4efe095b7760..c8ff51fe7a82 100644 --- a/packages/android_alarm_manager/example/android/app/build.gradle +++ b/packages/android_alarm_manager/example/android/app/build.gradle @@ -33,7 +33,7 @@ android { defaultConfig { applicationId "io.flutter.plugins.androidalarmmanagerexample" - minSdkVersion 16 + minSdkVersion 21 targetSdkVersion 28 versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/packages/android_intent/CHANGELOG.md b/packages/android_intent/CHANGELOG.md index 7bad13fc3dbd..2c44f29ab148 100644 --- a/packages/android_intent/CHANGELOG.md +++ b/packages/android_intent/CHANGELOG.md @@ -4,7 +4,7 @@ * Updated Android lint settings. * Specify Java 8 for Android build. * Removed `-Werror` in Android builds. -* Updates example app Android compileSdkVersion to 31. +* Updates compileSdkVersion to 31. ## 2.0.2 diff --git a/packages/android_intent/android/build.gradle b/packages/android_intent/android/build.gradle index 4bc8e85c40bc..f9317a53a2ba 100644 --- a/packages/android_intent/android/build.gradle +++ b/packages/android_intent/android/build.gradle @@ -27,7 +27,7 @@ project.getTasks().withType(JavaCompile){ apply plugin: 'com.android.library' android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { minSdkVersion 16 diff --git a/packages/battery/battery/CHANGELOG.md b/packages/battery/battery/CHANGELOG.md index f55da97d9f90..1ab884f499e9 100644 --- a/packages/battery/battery/CHANGELOG.md +++ b/packages/battery/battery/CHANGELOG.md @@ -2,7 +2,7 @@ * Remove references to the Android v1 embedding. * Updated Android lint settings. -* Updates example app Android compileSdkVersion to 31. +* Updates compileSdkVersion to 31. * Recreated Android example from current template. ## 2.0.3 diff --git a/packages/battery/battery/android/build.gradle b/packages/battery/battery/android/build.gradle index 14f503813f7e..bd094768f2f4 100644 --- a/packages/battery/battery/android/build.gradle +++ b/packages/battery/battery/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { minSdkVersion 16 diff --git a/packages/connectivity/connectivity/CHANGELOG.md b/packages/connectivity/connectivity/CHANGELOG.md index ce65faf4c3fb..53814ae3e1dc 100644 --- a/packages/connectivity/connectivity/CHANGELOG.md +++ b/packages/connectivity/connectivity/CHANGELOG.md @@ -4,7 +4,7 @@ * Updated Android lint settings. * Specify Java 8 for Android build. * Removed `-Werror` in Android builds. -* Updates example app Android compileSdkVersion to 31. +* Updates compileSdkVersion to 31. ## 3.0.6 diff --git a/packages/connectivity/connectivity/android/build.gradle b/packages/connectivity/connectivity/android/build.gradle index 33c63fcb5619..b29f1575ea8d 100644 --- a/packages/connectivity/connectivity/android/build.gradle +++ b/packages/connectivity/connectivity/android/build.gradle @@ -27,7 +27,7 @@ project.getTasks().withType(JavaCompile){ apply plugin: 'com.android.library' android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { minSdkVersion 16 diff --git a/packages/device_info/device_info/CHANGELOG.md b/packages/device_info/device_info/CHANGELOG.md index a9c3b7a1dffe..825b12d8c7ad 100644 --- a/packages/device_info/device_info/CHANGELOG.md +++ b/packages/device_info/device_info/CHANGELOG.md @@ -1,6 +1,6 @@ ## NEXT -* Updates example app Android compileSdkVersion to 31. +* Updates compileSdkVersion to 31. ## 2.0.3 diff --git a/packages/device_info/device_info/android/build.gradle b/packages/device_info/device_info/android/build.gradle index ed89da419d4a..67fbf1fe5c65 100644 --- a/packages/device_info/device_info/android/build.gradle +++ b/packages/device_info/device_info/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { minSdkVersion 16 diff --git a/packages/espresso/CHANGELOG.md b/packages/espresso/CHANGELOG.md index c2afa54780f5..3f19984b9437 100644 --- a/packages/espresso/CHANGELOG.md +++ b/packages/espresso/CHANGELOG.md @@ -1,6 +1,6 @@ ## NEXT -* Updates example app Android compileSdkVersion to 31. +* Updates compileSdkVersion to 31. ## 0.1.0+4 diff --git a/packages/espresso/android/build.gradle b/packages/espresso/android/build.gradle index da0cd2ebfee8..e5a042095c2f 100644 --- a/packages/espresso/android/build.gradle +++ b/packages/espresso/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { minSdkVersion 16 diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index b5e739aa01ee..e47ecb7725c9 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,6 +1,6 @@ ## NEXT -* Updates example app Android compileSdkVersion to 31. +* Updates Android compileSdkVersion to 31. ## 2.1.1 diff --git a/packages/google_maps_flutter/google_maps_flutter/android/build.gradle b/packages/google_maps_flutter/google_maps_flutter/android/build.gradle index e3cf6ffe8818..bf283bea9ef9 100644 --- a/packages/google_maps_flutter/google_maps_flutter/android/build.gradle +++ b/packages/google_maps_flutter/google_maps_flutter/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { minSdkVersion 20 diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index 739983b2916d..7ff50a4bb24a 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,6 +1,6 @@ ## NEXT -* Updates example app Android compileSdkVersion to 31. +* Updates Android compileSdkVersion to 31. ## 5.2.1 diff --git a/packages/google_sign_in/google_sign_in/android/build.gradle b/packages/google_sign_in/google_sign_in/android/build.gradle index ea98b315f147..5db929b173a8 100644 --- a/packages/google_sign_in/google_sign_in/android/build.gradle +++ b/packages/google_sign_in/google_sign_in/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { minSdkVersion 16 diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index 688cee3eed01..ce29eb522e3d 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,6 +1,6 @@ ## NEXT -* Updates example app Android compileSdkVersion to 31. +* Updates Android compileSdkVersion to 31. * Fix iOS RunnerUITests search paths. ## 0.8.4+4 diff --git a/packages/image_picker/image_picker/android/build.gradle b/packages/image_picker/image_picker/android/build.gradle index 1e6439e6a4eb..928c7cda8b03 100755 --- a/packages/image_picker/image_picker/android/build.gradle +++ b/packages/image_picker/image_picker/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { minSdkVersion 16 diff --git a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md index 7724dd3d7f14..4198d9345889 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,6 +1,6 @@ ## NEXT -* Updates example app Android compileSdkVersion to 31. +* Updates compileSdkVersion to 31. ## 0.2.0 diff --git a/packages/in_app_purchase/in_app_purchase_android/android/build.gradle b/packages/in_app_purchase/in_app_purchase_android/android/build.gradle index 656f7c34bf7a..cbb0855509a5 100644 --- a/packages/in_app_purchase/in_app_purchase_android/android/build.gradle +++ b/packages/in_app_purchase/in_app_purchase_android/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { minSdkVersion 16 diff --git a/packages/local_auth/CHANGELOG.md b/packages/local_auth/CHANGELOG.md index a824d8a6e0ea..82358d61e644 100644 --- a/packages/local_auth/CHANGELOG.md +++ b/packages/local_auth/CHANGELOG.md @@ -1,6 +1,6 @@ ## NEXT -* Updates example app Android compileSdkVersion to 31. +* Updates Android compileSdkVersion to 31. ## 1.1.8 diff --git a/packages/local_auth/android/build.gradle b/packages/local_auth/android/build.gradle index dc282e78ced0..de8ae50641b2 100644 --- a/packages/local_auth/android/build.gradle +++ b/packages/local_auth/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { minSdkVersion 16 diff --git a/packages/package_info/CHANGELOG.md b/packages/package_info/CHANGELOG.md index 3cf675170513..ca1a943d644a 100644 --- a/packages/package_info/CHANGELOG.md +++ b/packages/package_info/CHANGELOG.md @@ -2,7 +2,7 @@ * Remove references to the Android v1 embedding. * Updated Android lint settings. -* Updates example app Android compileSdkVersion to 31. +* Updates Android compileSdkVersion to 31. ## 2.0.2 diff --git a/packages/package_info/android/build.gradle b/packages/package_info/android/build.gradle index e21d911ff490..ad620add0f93 100644 --- a/packages/package_info/android/build.gradle +++ b/packages/package_info/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { minSdkVersion 16 diff --git a/packages/quick_actions/quick_actions/CHANGELOG.md b/packages/quick_actions/quick_actions/CHANGELOG.md index c49f1149daab..497f98a9def2 100644 --- a/packages/quick_actions/quick_actions/CHANGELOG.md +++ b/packages/quick_actions/quick_actions/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates Android compileSdkVersion to 31. + ## 0.6.0+8 * Updates example app Android compileSdkVersion to 31. diff --git a/packages/quick_actions/quick_actions/android/build.gradle b/packages/quick_actions/quick_actions/android/build.gradle index ec3f84eab4cf..590bc6b4a826 100644 --- a/packages/quick_actions/quick_actions/android/build.gradle +++ b/packages/quick_actions/quick_actions/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { minSdkVersion 16 diff --git a/packages/quick_actions/quick_actions/example/android/app/build.gradle b/packages/quick_actions/quick_actions/example/android/app/build.gradle index 465b4a62a9cd..54f2e59bacf7 100644 --- a/packages/quick_actions/quick_actions/example/android/app/build.gradle +++ b/packages/quick_actions/quick_actions/example/android/app/build.gradle @@ -33,7 +33,7 @@ android { defaultConfig { applicationId "io.flutter.plugins.quickactionsexample" - minSdkVersion 16 + minSdkVersion 21 targetSdkVersion 28 versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/packages/sensors/CHANGELOG.md b/packages/sensors/CHANGELOG.md index 3dbc5d2f7df1..b5d64b48edda 100644 --- a/packages/sensors/CHANGELOG.md +++ b/packages/sensors/CHANGELOG.md @@ -2,7 +2,7 @@ * Remove references to the Android V1 embedding. * Updated Android lint settings. -* Updates example app Android compileSdkVersion to 31. +* Updates Android compileSdkVersion to 31. ## 2.0.3 diff --git a/packages/sensors/android/build.gradle b/packages/sensors/android/build.gradle index 7e1087764dee..a9a9045301c9 100644 --- a/packages/sensors/android/build.gradle +++ b/packages/sensors/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { minSdkVersion 16 diff --git a/packages/share/CHANGELOG.md b/packages/share/CHANGELOG.md index 1c93b487e27b..effc12c023c0 100644 --- a/packages/share/CHANGELOG.md +++ b/packages/share/CHANGELOG.md @@ -2,7 +2,7 @@ * Remove references to the Android V1 embedding. * Updated Android lint settings. -* Updates example app Android compileSdkVersion to 31. +* Updates Android compileSdkVersion to 31. ## 2.0.4 diff --git a/packages/share/android/build.gradle b/packages/share/android/build.gradle index b2ea363a3e11..c4fcafc70d26 100644 --- a/packages/share/android/build.gradle +++ b/packages/share/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { minSdkVersion 16 diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 7e9dfff39c69..f9031685b583 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,7 +1,7 @@ ## NEXT * Fixes integration tests. -* Updates example app Android compileSdkVersion to 31. +* Updates Android compileSdkVersion to 31. ## 2.2.7 diff --git a/packages/video_player/video_player/android/build.gradle b/packages/video_player/video_player/android/build.gradle index 5d6b737f47a5..6e6e8c792150 100644 --- a/packages/video_player/video_player/android/build.gradle +++ b/packages/video_player/video_player/android/build.gradle @@ -27,7 +27,7 @@ project.getTasks().withType(JavaCompile){ apply plugin: 'com.android.library' android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { minSdkVersion 16 diff --git a/packages/video_player/video_player/example/android/app/build.gradle b/packages/video_player/video_player/example/android/app/build.gradle index b1aaf8b45a8b..7b3c7db80c7e 100644 --- a/packages/video_player/video_player/example/android/app/build.gradle +++ b/packages/video_player/video_player/example/android/app/build.gradle @@ -37,7 +37,7 @@ android { defaultConfig { applicationId "io.flutter.plugins.videoplayerexample" - minSdkVersion 16 + minSdkVersion 21 targetSdkVersion 29 versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/packages/webview_flutter/webview_flutter/example/android/app/build.gradle b/packages/webview_flutter/webview_flutter/example/android/app/build.gradle index 0193946331cd..532b2adde9d6 100644 --- a/packages/webview_flutter/webview_flutter/example/android/app/build.gradle +++ b/packages/webview_flutter/webview_flutter/example/android/app/build.gradle @@ -34,7 +34,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "io.flutter.plugins.webviewflutterexample" - minSdkVersion 19 + minSdkVersion 21 targetSdkVersion 28 versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index b978b088d365..ae76ea8bc342 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,6 +1,6 @@ ## NEXT -* Updates example app Android compileSdkVersion to 31. +* Updates compileSdkVersion to 31. ## 2.3.0 diff --git a/packages/webview_flutter/webview_flutter_android/android/build.gradle b/packages/webview_flutter/webview_flutter_android/android/build.gradle index bb9fe9708eaa..e70d4e68edc8 100644 --- a/packages/webview_flutter/webview_flutter_android/android/build.gradle +++ b/packages/webview_flutter/webview_flutter_android/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { minSdkVersion 19 diff --git a/packages/webview_flutter/webview_flutter_android/example/android/app/build.gradle b/packages/webview_flutter/webview_flutter_android/example/android/app/build.gradle index 932aba4eb5a8..fdd8193eb2b6 100644 --- a/packages/webview_flutter/webview_flutter_android/example/android/app/build.gradle +++ b/packages/webview_flutter/webview_flutter_android/example/android/app/build.gradle @@ -34,7 +34,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "io.flutter.plugins.webviewflutterandroidexample" - minSdkVersion 19 + minSdkVersion 21 targetSdkVersion 28 versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/packages/wifi_info_flutter/wifi_info_flutter/CHANGELOG.md b/packages/wifi_info_flutter/wifi_info_flutter/CHANGELOG.md index 3248b203173b..0e41699be340 100644 --- a/packages/wifi_info_flutter/wifi_info_flutter/CHANGELOG.md +++ b/packages/wifi_info_flutter/wifi_info_flutter/CHANGELOG.md @@ -2,7 +2,7 @@ * Updated Android lint settings. * Updated package description. -* Updates example app Android compileSdkVersion to 31. +* Updates Android compileSdkVersion to 31. ## 2.0.2 diff --git a/packages/wifi_info_flutter/wifi_info_flutter/android/build.gradle b/packages/wifi_info_flutter/wifi_info_flutter/android/build.gradle index 661ee82da4d0..76ac3bc7eedc 100644 --- a/packages/wifi_info_flutter/wifi_info_flutter/android/build.gradle +++ b/packages/wifi_info_flutter/wifi_info_flutter/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { minSdkVersion 16 From a10b89e42d5a88aadec7953007a47cc96eaa60e8 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 23 Nov 2021 11:34:18 -0500 Subject: [PATCH 010/600] [flutter_plugin_tools] Check for missing version and CHANGELOG updates (#4530) The currently documented repository policy is to: - require version updates for packages changes that don't meet specific exemptions, and - require CHANGELOG changes for essentially all changes. This adds tooling that enforces that policy, with a mechanism for overriding it via PR descriptions, to avoid cases where they are accidentally omitted without reviewers catching it. In order to facilitate testing (which require mocking another `git` command), this also updates the existing `version-check` tests: - Replaces the custom git result injection/validating with the newer bind-to-process-mocks approach that is now used in the rest of the tool tests. - Fixes some tests that were only checking for `ToolExit` to also check the error output, in order to ensure that failure tests are not accidentally passing for the wrong reason (as is being done in general as tests in the tooling are updated). Fixes https://github.com/flutter/flutter/issues/93790 --- .cirrus.yml | 12 +- script/tool/CHANGELOG.md | 7 +- .../lib/src/common/git_version_finder.dart | 5 +- .../tool/lib/src/version_check_command.dart | 151 +++- .../test/common/git_version_finder_test.dart | 4 +- .../tool/test/version_check_command_test.dart | 676 ++++++++++++++---- 6 files changed, 686 insertions(+), 169 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index e5593397e270..59f686dbf5d6 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -75,16 +75,16 @@ task: CHANGE_DESC: "$TMPDIR/change-description.txt" version_check_script: # For pre-submit, pass the PR description to the script to allow for - # platform version breaking version change justifications. - # For post-submit, ignore platform version breaking version changes. - # The PR description isn't reliably part of the commit message, so using - # the same flags as for presubmit would likely result in false-positive - # post-submit failures. + # version check overrides. + # For post-submit, ignore platform version breaking version changes and + # missing version/CHANGELOG detection; the PR description isn't reliably + # part of the commit message, so using the same flags as for presubmit + # would likely result in false-positive post-submit failures. - if [[ $CIRRUS_PR == "" ]]; then - ./script/tool_runner.sh version-check --ignore-platform-interface-breaks - else - echo "$CIRRUS_CHANGE_MESSAGE" > "$CHANGE_DESC" - - ./script/tool_runner.sh version-check --change-description-file="$CHANGE_DESC" + - ./script/tool_runner.sh version-check --check-for-missing-changes --change-description-file="$CHANGE_DESC" - fi publish_check_script: ./script/tool_runner.sh publish-check - name: format diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 31efc28aa3ea..ee23428e66c1 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,9 +1,12 @@ -## NEXT +## 0.7.3 - `native-test` now builds unit tests before running them on Windows and Linux, matching the behavior of other platforms. -- Added `--log-timing` to add timing information to package headers in looping +- Adds `--log-timing` to add timing information to package headers in looping commands. +- Adds a `--check-for-missing-changes` flag to `version-check` that requires + version updates (except for recognized exemptions) and CHANGELOG changes when + modifying packages, unless the PR description explains why it's not needed. ## 0.7.2 diff --git a/script/tool/lib/src/common/git_version_finder.dart b/script/tool/lib/src/common/git_version_finder.dart index 1cdd2fcc409b..331187335f51 100644 --- a/script/tool/lib/src/common/git_version_finder.dart +++ b/script/tool/lib/src/common/git_version_finder.dart @@ -75,8 +75,9 @@ class GitVersionFinder { io.ProcessResult baseShaFromMergeBase = await baseGitDir.runCommand( ['merge-base', '--fork-point', 'FETCH_HEAD', 'HEAD'], throwOnError: false); - if (baseShaFromMergeBase.stderr != null || - baseShaFromMergeBase.stdout == null) { + final String stdout = (baseShaFromMergeBase.stdout as String? ?? '').trim(); + final String stderr = (baseShaFromMergeBase.stdout as String? ?? '').trim(); + if (stderr.isNotEmpty || stdout.isEmpty) { baseShaFromMergeBase = await baseGitDir .runCommand(['merge-base', 'FETCH_HEAD', 'HEAD']); } diff --git a/script/tool/lib/src/version_check_command.dart b/script/tool/lib/src/version_check_command.dart index dc664e53d7fd..1ec5dc4f2490 100644 --- a/script/tool/lib/src/version_check_command.dart +++ b/script/tool/lib/src/version_check_command.dart @@ -120,6 +120,14 @@ class VersionCheckCommand extends PackageLoopingCommand { '(e.g., PR description or commit message).\n\n' 'If supplied, this is used to allow overrides to some version ' 'checks.'); + argParser.addFlag(_checkForMissingChanges, + help: 'Validates that changes to packages include CHANGELOG and ' + 'version changes unless they meet an established exemption.\n\n' + 'If used with --$_changeDescriptionFile, this is should only be ' + 'used in pre-submit CI checks, to prevent the possibility of ' + 'post-submit breakage if an override justification is not ' + 'transferred into the commit message.', + hide: true); argParser.addFlag(_ignorePlatformInterfaceBreaks, help: 'Bypasses the check that platform interfaces do not contain ' 'breaking changes.\n\n' @@ -133,6 +141,7 @@ class VersionCheckCommand extends PackageLoopingCommand { static const String _againstPubFlag = 'against-pub'; static const String _changeDescriptionFile = 'change-description-file'; + static const String _checkForMissingChanges = 'check-for-missing-changes'; static const String _ignorePlatformInterfaceBreaks = 'ignore-platform-interface-breaks'; @@ -141,8 +150,26 @@ class VersionCheckCommand extends PackageLoopingCommand { static const String _breakingChangeJustificationMarker = '## Breaking change justification'; + /// The string that must be at the start of a line in [_changeDescriptionFile] + /// to allow skipping a version change for a PR that would normally require + /// one. + static const String _missingVersionChangeJustificationMarker = + 'No version change:'; + + /// The string that must be at the start of a line in [_changeDescriptionFile] + /// to allow skipping a CHANGELOG change for a PR that would normally require + /// one. + static const String _missingChangelogChangeJustificationMarker = + 'No CHANGELOG change:'; + final PubVersionFinder _pubVersionFinder; + late final GitVersionFinder _gitVersionFinder; + late final String _mergeBase; + late final List _changedFiles; + + late final String _changeDescription = _loadChangeDescription(); + @override final String name = 'version-check'; @@ -156,7 +183,11 @@ class VersionCheckCommand extends PackageLoopingCommand { bool get hasLongOutput => false; @override - Future initializeRun() async {} + Future initializeRun() async { + _gitVersionFinder = await retrieveVersionFinder(); + _mergeBase = await _gitVersionFinder.getBaseSha(); + _changedFiles = await _gitVersionFinder.getChangedFiles(); + } @override Future runForPackage(RepositoryPackage package) async { @@ -206,6 +237,17 @@ class VersionCheckCommand extends PackageLoopingCommand { errors.add('CHANGELOG.md failed validation.'); } + // If there are no other issues, make sure that there isn't a missing + // change to the version and/or CHANGELOG. + if (getBoolArg(_checkForMissingChanges) && + !versionChanged && + errors.isEmpty) { + final String? error = await _checkForMissingChangeError(package); + if (error != null) { + errors.add(error); + } + } + return errors.isEmpty ? PackageResult.success() : PackageResult.fail(errors); @@ -239,10 +281,7 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body} } /// Returns the version of [package] from git at the base comparison hash. - Future _getPreviousVersionFromGit( - RepositoryPackage package, { - required GitVersionFinder gitVersionFinder, - }) async { + Future _getPreviousVersionFromGit(RepositoryPackage package) async { final File pubspecFile = package.pubspecFile; final String relativePath = path.relative(pubspecFile.absolute.path, from: (await gitDir).path); @@ -250,7 +289,8 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body} final String gitPath = path.style == p.Style.windows ? p.posix.joinAll(path.split(relativePath)) : relativePath; - return await gitVersionFinder.getPackageVersion(gitPath); + return await _gitVersionFinder.getPackageVersion(gitPath, + gitRef: _mergeBase); } /// Returns the state of the verison of [package] relative to the comparison @@ -274,11 +314,9 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body} '$indentation${pubspec.name}: Current largest version on pub: $previousVersion'); } } else { - final GitVersionFinder gitVersionFinder = await retrieveVersionFinder(); - previousVersionSource = await gitVersionFinder.getBaseSha(); - previousVersion = await _getPreviousVersionFromGit(package, - gitVersionFinder: gitVersionFinder) ?? - Version.none; + previousVersionSource = _mergeBase; + previousVersion = + await _getPreviousVersionFromGit(package) ?? Version.none; } if (previousVersion == Version.none) { print('${indentation}Unable to find previous version ' @@ -462,9 +500,11 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog. return false; } + String _getChangeDescription() => _changeDescription; + /// Returns the contents of the file pointed to by [_changeDescriptionFile], /// or an empty string if that flag is not provided. - String _getChangeDescription() { + String _loadChangeDescription() { final String path = getStringArg(_changeDescriptionFile); if (path.isEmpty) { return ''; @@ -476,4 +516,91 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog. } return file.readAsStringSync(); } + + /// Returns an error string if the changes to this package should have + /// resulted in a version change, or shoud have resulted in a CHANGELOG change + /// but didn't. + /// + /// This should only be called if the version did not change. + Future _checkForMissingChangeError(RepositoryPackage package) async { + // Find the relative path to the current package, as it would appear at the + // beginning of a path reported by getChangedFiles() (which always uses + // Posix paths). + final Directory gitRoot = + packagesDir.fileSystem.directory((await gitDir).path); + final String relativePackagePath = + getRelativePosixPath(package.directory, from: gitRoot) + '/'; + bool hasChanges = false; + bool needsVersionChange = false; + bool hasChangelogChange = false; + for (final String path in _changedFiles) { + // Only consider files within the package. + if (!path.startsWith(relativePackagePath)) { + continue; + } + hasChanges = true; + + final List components = p.posix.split(path); + final bool isChangelog = components.last == 'CHANGELOG.md'; + if (isChangelog) { + hasChangelogChange = true; + } + + if (!needsVersionChange && + !isChangelog && + // The example's main.dart is shown on pub.dev, but for anything else + // in the example publishing has no purpose. + !(components.contains('example') && components.last != 'main.dart') && + // Changes to tests don't need to be published. + !components.contains('test') && + !components.contains('androidTest') && + !components.contains('RunnerTests') && + !components.contains('RunnerUITests') && + // Ignoring lints doesn't affect clients. + !components.contains('lint-baseline.xml')) { + needsVersionChange = true; + } + } + + if (!hasChanges) { + return null; + } + + if (needsVersionChange) { + if (_getChangeDescription().split('\n').any((String line) => + line.startsWith(_missingVersionChangeJustificationMarker))) { + logWarning('Ignoring lack of version change due to ' + '"$_missingVersionChangeJustificationMarker" in the ' + 'change description.'); + } else { + printError( + 'No version change found, but the change to this package could ' + 'not be verified to be exempt from version changes according to ' + 'repository policy. If this is a false positive, please ' + 'add a line starting with\n' + '$_missingVersionChangeJustificationMarker\n' + 'to your PR description with an explanation of why it is exempt.'); + return 'Missing version change'; + } + } + + if (!hasChangelogChange) { + if (_getChangeDescription().split('\n').any((String line) => + line.startsWith(_missingChangelogChangeJustificationMarker))) { + logWarning('Ignoring lack of CHANGELOG update due to ' + '"$_missingChangelogChangeJustificationMarker" in the ' + 'change description.'); + } else { + printError( + 'No CHANGELOG change found. If this PR needs an exemption from' + 'the standard policy of listing all changes in the CHANGELOG, ' + 'please add a line starting with\n' + '$_missingChangelogChangeJustificationMarker\n' + 'to your PR description with an explanation of why.'); + return 'Missing CHANGELOG change'; + } + } + + return null; + } } diff --git a/script/tool/test/common/git_version_finder_test.dart b/script/tool/test/common/git_version_finder_test.dart index f1f40b5e0035..fa8b1c410dd5 100644 --- a/script/tool/test/common/git_version_finder_test.dart +++ b/script/tool/test/common/git_version_finder_test.dart @@ -14,7 +14,7 @@ void main() { late List?> gitDirCommands; late String gitDiffResponse; late MockGitDir gitDir; - String? mergeBaseResponse; + String mergeBaseResponse = ''; setUp(() { gitDirCommands = ?>[]; @@ -74,7 +74,7 @@ file2/file2.cc final GitVersionFinder finder = GitVersionFinder(gitDir, null); await finder.getChangedFiles(); verify(gitDir.runCommand( - ['diff', '--name-only', mergeBaseResponse!, 'HEAD'])); + ['diff', '--name-only', mergeBaseResponse, 'HEAD'])); }); test('use correct base sha if specified', () async { diff --git a/script/tool/test/version_check_command_test.dart b/script/tool/test/version_check_command_test.dart index 30b8855b5a3f..5a5a0a108a32 100644 --- a/script/tool/test/version_check_command_test.dart +++ b/script/tool/test/version_check_command_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; import 'dart:convert'; import 'dart:io' as io; @@ -51,8 +50,6 @@ void main() { late Directory packagesDir; late CommandRunner runner; late RecordingProcessRunner processRunner; - late List> gitDirCommands; - Map gitShowResponses; late MockGitDir gitDir; // Ignored if mockHttpResponse is set. int mockHttpStatus; @@ -63,27 +60,17 @@ void main() { mockPlatform = MockPlatform(); packagesDir = createPackagesDirectory(fileSystem: fileSystem); - gitDirCommands = >[]; - gitShowResponses = {}; gitDir = MockGitDir(); when(gitDir.path).thenReturn(packagesDir.parent.path); when(gitDir.runCommand(any, throwOnError: anyNamed('throwOnError'))) .thenAnswer((Invocation invocation) { - gitDirCommands.add(invocation.positionalArguments[0] as List); - final MockProcessResult mockProcessResult = MockProcessResult(); - if (invocation.positionalArguments[0][0] == 'show') { - final String? response = - gitShowResponses[invocation.positionalArguments[0][1]]; - if (response == null) { - throw const io.ProcessException('git', ['show']); - } - when(mockProcessResult.stdout as String?) - .thenReturn(response); - } else if (invocation.positionalArguments[0][0] == 'merge-base') { - when(mockProcessResult.stdout as String?) - .thenReturn('abc123'); - } - return Future.value(mockProcessResult); + final List arguments = + invocation.positionalArguments[0]! as List; + // Route git calls through the process runner, to make mock output + // consistent with other processes. Attach the first argument to the + // command to make targeting the mock results easier. + final String gitCommand = arguments.removeAt(0); + return processRunner.run('git-$gitCommand', arguments); }); // Default to simulating the plugin never having been published. @@ -108,9 +95,9 @@ void main() { test('allows valid version', () async { createFakePlugin('plugin', packagesDir, version: '2.0.0'); - gitShowResponses = { - 'main:packages/plugin/pubspec.yaml': 'version: 1.0.0', - }; + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; final List output = await runCapturingPrint( runner, ['version-check', '--base-sha=main']); @@ -121,39 +108,49 @@ void main() { contains('1.0.0 -> 2.0.0'), ]), ); - expect(gitDirCommands.length, equals(1)); expect( - gitDirCommands, - containsAll([ - equals(['show', 'main:packages/plugin/pubspec.yaml']), + processRunner.recordedCalls, + containsAllInOrder(const [ + ProcessCall( + 'git-show', ['main:packages/plugin/pubspec.yaml'], null) ])); }); test('denies invalid version', () async { createFakePlugin('plugin', packagesDir, version: '0.2.0'); - gitShowResponses = { - 'main:packages/plugin/pubspec.yaml': 'version: 0.0.1', - }; - final Future> result = runCapturingPrint( - runner, ['version-check', '--base-sha=main']); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 0.0.1'), + ]; + Error? commandError; + final List output = await runCapturingPrint( + runner, ['version-check', '--base-sha=main'], + errorHandler: (Error e) { + commandError = e; + }); - await expectLater( - result, - throwsA(isA()), - ); - expect(gitDirCommands.length, equals(1)); + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('Incorrectly updated version.'), + ])); expect( - gitDirCommands, - containsAll([ - equals(['show', 'main:packages/plugin/pubspec.yaml']), + processRunner.recordedCalls, + containsAllInOrder(const [ + ProcessCall( + 'git-show', ['main:packages/plugin/pubspec.yaml'], null) ])); }); - test('allows valid version without explicit base-sha', () async { + test('uses merge-base without explicit base-sha', () async { createFakePlugin('plugin', packagesDir, version: '2.0.0'); - gitShowResponses = { - 'abc123:packages/plugin/pubspec.yaml': 'version: 1.0.0', - }; + processRunner.mockProcessesForExecutable['git-merge-base'] = [ + MockProcess(stdout: 'abc123'), + MockProcess(stdout: 'abc123'), + ]; + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; final List output = await runCapturingPrint(runner, ['version-check']); @@ -164,6 +161,14 @@ void main() { contains('1.0.0 -> 2.0.0'), ]), ); + expect( + processRunner.recordedCalls, + containsAllInOrder(const [ + ProcessCall('git-merge-base', + ['--fork-point', 'FETCH_HEAD', 'HEAD'], null), + ProcessCall('git-show', + ['abc123:packages/plugin/pubspec.yaml'], null), + ])); }); test('allows valid version for new package.', () async { @@ -182,11 +187,11 @@ void main() { test('allows likely reverts.', () async { createFakePlugin('plugin', packagesDir, version: '0.6.1'); - gitShowResponses = { - 'abc123:packages/plugin/pubspec.yaml': 'version: 0.6.2', - }; - final List output = - await runCapturingPrint(runner, ['version-check']); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 0.6.2'), + ]; + final List output = await runCapturingPrint( + runner, ['version-check', '--base-sha=main']); expect( output, @@ -195,43 +200,47 @@ void main() { 'This is assumed to be a revert.'), ]), ); + expect( + processRunner.recordedCalls, + containsAllInOrder(const [ + ProcessCall( + 'git-show', ['main:packages/plugin/pubspec.yaml'], null) + ])); }); test('denies lower version that could not be a simple revert', () async { createFakePlugin('plugin', packagesDir, version: '0.5.1'); - gitShowResponses = { - 'abc123:packages/plugin/pubspec.yaml': 'version: 0.6.2', - }; - final Future> result = - runCapturingPrint(runner, ['version-check']); - - await expectLater( - result, - throwsA(isA()), - ); - }); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 0.6.2'), + ]; - test('denies invalid version without explicit base-sha', () async { - createFakePlugin('plugin', packagesDir, version: '0.2.0'); - gitShowResponses = { - 'abc123:packages/plugin/pubspec.yaml': 'version: 0.0.1', - }; - final Future> result = - runCapturingPrint(runner, ['version-check']); + Error? commandError; + final List output = await runCapturingPrint( + runner, ['version-check', '--base-sha=main'], + errorHandler: (Error e) { + commandError = e; + }); - await expectLater( - result, - throwsA(isA()), - ); + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('Incorrectly updated version.'), + ])); + expect( + processRunner.recordedCalls, + containsAllInOrder(const [ + ProcessCall( + 'git-show', ['main:packages/plugin/pubspec.yaml'], null) + ])); }); test('allows minor changes to platform interfaces', () async { createFakePlugin('plugin_platform_interface', packagesDir, version: '1.1.0'); - gitShowResponses = { - 'main:packages/plugin_platform_interface/pubspec.yaml': - 'version: 1.0.0', - }; + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; final List output = await runCapturingPrint( runner, ['version-check', '--base-sha=main']); expect( @@ -241,14 +250,15 @@ void main() { contains('1.0.0 -> 1.1.0'), ]), ); - expect(gitDirCommands.length, equals(1)); - expect( - gitDirCommands, - containsAll([ - equals([ - 'show', - 'main:packages/plugin_platform_interface/pubspec.yaml' - ]), + expect( + processRunner.recordedCalls, + containsAllInOrder(const [ + ProcessCall( + 'git-show', + [ + 'main:packages/plugin_platform_interface/pubspec.yaml' + ], + null) ])); }); @@ -256,24 +266,35 @@ void main() { () async { createFakePlugin('plugin_platform_interface', packagesDir, version: '2.0.0'); - gitShowResponses = { - 'main:packages/plugin_platform_interface/pubspec.yaml': - 'version: 1.0.0', - }; - final Future> output = runCapturingPrint( - runner, ['version-check', '--base-sha=main']); - await expectLater( - output, - throwsA(isA()), - ); - expect(gitDirCommands.length, equals(1)); - expect( - gitDirCommands, - containsAll([ - equals([ - 'show', - 'main:packages/plugin_platform_interface/pubspec.yaml' - ]), + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; + Error? commandError; + final List output = await runCapturingPrint( + runner, ['version-check', '--base-sha=main'], + errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains( + ' Breaking changes to platform interfaces are not allowed ' + 'without explicit justification.\n' + ' See https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages ' + 'for more information.'), + ])); + expect( + processRunner.recordedCalls, + containsAllInOrder(const [ + ProcessCall( + 'git-show', + [ + 'main:packages/plugin_platform_interface/pubspec.yaml' + ], + null) ])); }); @@ -281,10 +302,9 @@ void main() { () async { createFakePlugin('plugin_platform_interface', packagesDir, version: '2.0.0'); - gitShowResponses = { - 'main:packages/plugin_platform_interface/pubspec.yaml': - 'version: 1.0.0', - }; + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; final File changeDescriptionFile = fileSystem.file('change_description.txt'); changeDescriptionFile.writeAsStringSync(''' @@ -310,16 +330,25 @@ This is necessary because of X, Y, and Z contains('Ran for 1 package(s) (1 with warnings)'), ]), ); + expect( + processRunner.recordedCalls, + containsAllInOrder(const [ + ProcessCall( + 'git-show', + [ + 'main:packages/plugin_platform_interface/pubspec.yaml' + ], + null) + ])); }); test('throws if a nonexistent change description file is specified', () async { createFakePlugin('plugin_platform_interface', packagesDir, version: '2.0.0'); - gitShowResponses = { - 'main:packages/plugin_platform_interface/pubspec.yaml': - 'version: 1.0.0', - }; + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; Error? commandError; final List output = await runCapturingPrint(runner, [ @@ -337,16 +366,25 @@ This is necessary because of X, Y, and Z contains('No such file: a_missing_file.txt'), ]), ); + expect( + processRunner.recordedCalls, + containsAllInOrder(const [ + ProcessCall( + 'git-show', + [ + 'main:packages/plugin_platform_interface/pubspec.yaml' + ], + null) + ])); }); test('allows breaking changes to platform interfaces with bypass flag', () async { createFakePlugin('plugin_platform_interface', packagesDir, version: '2.0.0'); - gitShowResponses = { - 'main:packages/plugin_platform_interface/pubspec.yaml': - 'version: 1.0.0', - }; + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; final List output = await runCapturingPrint(runner, [ 'version-check', '--base-sha=main', @@ -361,6 +399,16 @@ This is necessary because of X, Y, and Z contains('Ran for 1 package(s) (1 with warnings)'), ]), ); + expect( + processRunner.recordedCalls, + containsAllInOrder(const [ + ProcessCall( + 'git-show', + [ + 'main:packages/plugin_platform_interface/pubspec.yaml' + ], + null) + ])); }); test('Allow empty lines in front of the first version in CHANGELOG', @@ -392,15 +440,14 @@ This is necessary because of X, Y, and Z * Some changes. '''; createFakeCHANGELOG(pluginDirectory, changelog); - bool hasError = false; + Error? commandError; final List output = await runCapturingPrint( runner, ['version-check', '--base-sha=main', '--against-pub'], errorHandler: (Error e) { - expect(e, isA()); - hasError = true; + commandError = e; }); - expect(hasError, isTrue); + expect(commandError, isA()); expect( output, containsAllInOrder([ @@ -472,13 +519,13 @@ This is necessary because of X, Y, and Z * Some other changes. '''; createFakeCHANGELOG(pluginDirectory, changelog); - gitShowResponses = { - 'main:packages/plugin/pubspec.yaml': 'version: 1.0.0', - }; + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; final List output = await runCapturingPrint( runner, ['version-check', '--base-sha=main']); - await expectLater( + expect( output, containsAllInOrder([ contains('Running for plugin'), @@ -534,9 +581,6 @@ This is necessary because of X, Y, and Z * Some other changes. '''; createFakeCHANGELOG(pluginDirectory, changelog); - gitShowResponses = { - 'main:packages/plugin/pubspec.yaml': 'version: 1.0.0', - }; bool hasError = false; final List output = await runCapturingPrint( @@ -569,9 +613,6 @@ This is necessary because of X, Y, and Z * Some other changes. '''; createFakeCHANGELOG(pluginDirectory, changelog); - gitShowResponses = { - 'main:packages/plugin/pubspec.yaml': 'version: 1.0.0', - }; bool hasError = false; final List output = await runCapturingPrint( @@ -604,9 +645,9 @@ This is necessary because of X, Y, and Z * Some other changes. '''; createFakeCHANGELOG(pluginDirectory, changelog); - gitShowResponses = { - 'main:packages/plugin/pubspec.yaml': 'version: 1.0.0', - }; + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; Error? commandError; final List output = await runCapturingPrint(runner, [ @@ -637,9 +678,9 @@ This is necessary because of X, Y, and Z * Some changes. '''; createFakeCHANGELOG(pluginDirectory, changelog); - gitShowResponses = { - 'main:packages/plugin/pubspec.yaml': 'version: 1.0.0', - }; + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; Error? commandError; final List output = await runCapturingPrint(runner, [ @@ -658,6 +699,360 @@ This is necessary because of X, Y, and Z ); }); + group('missing change detection', () { + Future> _runWithMissingChangeDetection( + List extraArgs, + {void Function(Error error)? errorHandler}) async { + return runCapturingPrint( + runner, + [ + 'version-check', + '--base-sha=main', + '--check-for-missing-changes', + ...extraArgs, + ], + errorHandler: errorHandler); + } + + test('passes for unchanged packages', () async { + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, version: '1.0.0'); + + const String changelog = ''' +## 1.0.0 +* Some changes. +'''; + createFakeCHANGELOG(pluginDirectory, changelog); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: ''), + ]; + + final List output = + await _runWithMissingChangeDetection([]); + + expect( + output, + containsAllInOrder([ + contains('Running for plugin'), + ]), + ); + }); + + test( + 'fails if a version change is missing from a change that does not ' + 'pass the exemption check', () async { + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, version: '1.0.0'); + + const String changelog = ''' +## 1.0.0 +* Some changes. +'''; + createFakeCHANGELOG(pluginDirectory, changelog); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: ''' +packages/plugin/lib/plugin.dart +'''), + ]; + + Error? commandError; + final List output = await _runWithMissingChangeDetection( + [], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('No version change found'), + contains('plugin:\n' + ' Missing version change'), + ]), + ); + }); + + test('passes version change requirement when version changes', () async { + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, version: '1.0.1'); + + const String changelog = ''' +## 1.0.1 +* Some changes. +'''; + createFakeCHANGELOG(pluginDirectory, changelog); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: ''' +packages/plugin/lib/plugin.dart +packages/plugin/CHANGELOG.md +packages/plugin/pubspec.yaml +'''), + ]; + + final List output = + await _runWithMissingChangeDetection([]); + + expect( + output, + containsAllInOrder([ + contains('Running for plugin'), + ]), + ); + }); + + test('version change check ignores files outside the package', () async { + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, version: '1.0.0'); + + const String changelog = ''' +## 1.0.0 +* Some changes. +'''; + createFakeCHANGELOG(pluginDirectory, changelog); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: ''' +packages/plugin_a/lib/plugin.dart +tool/plugin/lib/plugin.dart +'''), + ]; + + final List output = + await _runWithMissingChangeDetection([]); + + expect( + output, + containsAllInOrder([ + contains('Running for plugin'), + ]), + ); + }); + + test('allows missing version change for exempt changes', () async { + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, version: '1.0.0'); + + const String changelog = ''' +## 1.0.0 +* Some changes. +'''; + createFakeCHANGELOG(pluginDirectory, changelog); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: ''' +packages/plugin/example/android/lint-baseline.xml +packages/plugin/example/android/src/androidTest/foo/bar/FooTest.java +packages/plugin/example/ios/RunnerTests/Foo.m +packages/plugin/example/ios/RunnerUITests/info.plist +packages/plugin/CHANGELOG.md +'''), + ]; + + final List output = + await _runWithMissingChangeDetection([]); + + expect( + output, + containsAllInOrder([ + contains('Running for plugin'), + ]), + ); + }); + + test('allows missing version change with justification', () async { + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, version: '1.0.0'); + + const String changelog = ''' +## 1.0.0 +* Some changes. +'''; + createFakeCHANGELOG(pluginDirectory, changelog); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: ''' +packages/plugin/lib/plugin.dart +packages/plugin/CHANGELOG.md +packages/plugin/pubspec.yaml +'''), + ]; + + final File changeDescriptionFile = + fileSystem.file('change_description.txt'); + changeDescriptionFile.writeAsStringSync(''' +Some general PR description + +No version change: Code change is only to implementation comments. +'''); + final List output = + await _runWithMissingChangeDetection([ + '--change-description-file=${changeDescriptionFile.path}' + ]); + + expect( + output, + containsAllInOrder([ + contains('Ignoring lack of version change due to ' + '"No version change:" in the change description.'), + ]), + ); + }); + + test('fails if a CHANGELOG change is missing', () async { + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, version: '1.0.0'); + + const String changelog = ''' +## 1.0.0 +* Some changes. +'''; + createFakeCHANGELOG(pluginDirectory, changelog); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: ''' +packages/plugin/example/lib/foo.dart +'''), + ]; + + Error? commandError; + final List output = await _runWithMissingChangeDetection( + [], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('No CHANGELOG change found'), + contains('plugin:\n' + ' Missing CHANGELOG change'), + ]), + ); + }); + + test('passes CHANGELOG check when the CHANGELOG is changed', () async { + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, version: '1.0.0'); + + const String changelog = ''' +## 1.0.0 +* Some changes. +'''; + createFakeCHANGELOG(pluginDirectory, changelog); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: ''' +packages/plugin/example/lib/foo.dart +packages/plugin/CHANGELOG.md +'''), + ]; + + final List output = + await _runWithMissingChangeDetection([]); + + expect( + output, + containsAllInOrder([ + contains('Running for plugin'), + ]), + ); + }); + + test('fails CHANGELOG check if only another package CHANGELOG chages', + () async { + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, version: '1.0.0'); + + const String changelog = ''' +## 1.0.0 +* Some changes. +'''; + createFakeCHANGELOG(pluginDirectory, changelog); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: ''' +packages/plugin/example/lib/foo.dart +packages/another_plugin/CHANGELOG.md +'''), + ]; + + Error? commandError; + final List output = await _runWithMissingChangeDetection( + [], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('No CHANGELOG change found'), + ]), + ); + }); + + test('allows missing CHANGELOG change with justification', () async { + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, version: '1.0.0'); + + const String changelog = ''' +## 1.0.0 +* Some changes. +'''; + createFakeCHANGELOG(pluginDirectory, changelog); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: ''' +packages/plugin/example/lib/foo.dart +'''), + ]; + + final File changeDescriptionFile = + fileSystem.file('change_description.txt'); + changeDescriptionFile.writeAsStringSync(''' +Some general PR description + +No CHANGELOG change: Code change is only to implementation comments. +'''); + final List output = + await _runWithMissingChangeDetection([ + '--change-description-file=${changeDescriptionFile.path}' + ]); + + expect( + output, + containsAllInOrder([ + contains('Ignoring lack of CHANGELOG update due to ' + '"No CHANGELOG change:" in the change description.'), + ]), + ); + }); + }); + test('allows valid against pub', () async { mockHttpResponse = { 'name': 'some_package', @@ -669,9 +1064,6 @@ This is necessary because of X, Y, and Z }; createFakePlugin('plugin', packagesDir, version: '2.0.0'); - gitShowResponses = { - 'main:packages/plugin/pubspec.yaml': 'version: 1.0.0', - }; final List output = await runCapturingPrint(runner, ['version-check', '--base-sha=main', '--against-pub']); @@ -693,9 +1085,6 @@ This is necessary because of X, Y, and Z }; createFakePlugin('plugin', packagesDir, version: '2.0.0'); - gitShowResponses = { - 'main:packages/plugin/pubspec.yaml': 'version: 1.0.0', - }; bool hasError = false; final List result = await runCapturingPrint( @@ -723,9 +1112,6 @@ ${indentation}Allowed versions: {1.0.0: NextVersionType.BREAKING_MAJOR, 0.1.0: N mockHttpStatus = 400; createFakePlugin('plugin', packagesDir, version: '2.0.0'); - gitShowResponses = { - 'main:packages/plugin/pubspec.yaml': 'version: 1.0.0', - }; bool hasError = false; final List result = await runCapturingPrint( runner, ['version-check', '--base-sha=main', '--against-pub'], @@ -752,9 +1138,9 @@ ${indentation}HTTP response: null mockHttpStatus = 404; createFakePlugin('plugin', packagesDir, version: '2.0.0'); - gitShowResponses = { - 'main:packages/plugin/pubspec.yaml': 'version: 1.0.0', - }; + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; final List result = await runCapturingPrint(runner, ['version-check', '--base-sha=main', '--against-pub']); From e13a46942cffe662beb996855f71913c491f1e92 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 23 Nov 2021 14:39:37 -0500 Subject: [PATCH 011/600] [webview_flutter] Adjust getTitle test (#4537) The getTitle test has been flaking on iOS, for reasons that are not clear. This is a highly speculative attempted fix, in case the issue is that the future is completing too early (e.g., if 'about:blank' is triggering a completion). --- .../webview_flutter/webview_flutter/CHANGELOG.md | 1 + .../integration_test/webview_flutter_test.dart | 13 ++++++++++--- .../webview_flutter_wkwebview/CHANGELOG.md | 4 ++++ .../integration_test/webview_flutter_test.dart | 13 ++++++++++--- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index 21240328af84..5dd2ea0cf554 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,6 +1,7 @@ ## NEXT * Updates example app Android compileSdkVersion to 31. +* Integration test fixes. ## 2.3.1 diff --git a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart index 0e7e3451d054..38561505f6ee 100644 --- a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart @@ -748,19 +748,26 @@ void main() { final Completer controllerCompleter = Completer(); + final testUrl = 'data:text/html;charset=utf-8;base64,$getTitleTestBase64'; await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: WebView( - initialUrl: 'data:text/html;charset=utf-8;base64,$getTitleTestBase64', + initialUrl: testUrl, onWebViewCreated: (WebViewController controller) { controllerCompleter.complete(controller); }, onPageStarted: (String url) { - pageStarted.complete(null); + // Ensure that this update is for the test page, in case any other + // URL (e.g., about:blank) fires earlier notifications. + if (url == testUrl) { + pageStarted.complete(null); + } }, onPageFinished: (String url) { - pageLoaded.complete(null); + if (url == testUrl) { + pageLoaded.complete(null); + } }, ), ), diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index cbf1f04a6181..3b7816188dec 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Integration test fixes. + ## 2.4.0 * Implemented new `loadFile` and `loadHtmlString` methods from the platform interface. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart index 45a72d839567..cd8c616bc474 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart @@ -733,19 +733,26 @@ void main() { final Completer controllerCompleter = Completer(); + final testUrl = 'data:text/html;charset=utf-8;base64,$getTitleTestBase64'; await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: WebView( - initialUrl: 'data:text/html;charset=utf-8;base64,$getTitleTestBase64', + initialUrl: testUrl, onWebViewCreated: (WebViewController controller) { controllerCompleter.complete(controller); }, onPageStarted: (String url) { - pageStarted.complete(null); + // Ensure that this update is for the test page, in case any other + // URL (e.g., about:blank) fires earlier notifications. + if (url == testUrl) { + pageStarted.complete(null); + } }, onPageFinished: (String url) { - pageLoaded.complete(null); + if (url == testUrl) { + pageLoaded.complete(null); + } }, ), ), From a157a0a1f7b2759af4a86193aeb6a5dffb44f332 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 23 Nov 2021 17:35:42 -0500 Subject: [PATCH 012/600] [webview_flutter] Speculative getTitle fix (#4539) * [webview_flutter] getTitle flake test fix Attempts to fix the getTitle flake by running JS before accessing the title, to force the page to be fully processed by the native code. No CHANGELOG change: Description would be the same as something that is already in the NEXT section. --- .../webview_flutter_test.dart | 20 +++++++++---------- .../webview_flutter_test.dart | 20 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart index 38561505f6ee..1630d6e61940 100644 --- a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart @@ -748,26 +748,20 @@ void main() { final Completer controllerCompleter = Completer(); - final testUrl = 'data:text/html;charset=utf-8;base64,$getTitleTestBase64'; await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: WebView( - initialUrl: testUrl, + initialUrl: 'data:text/html;charset=utf-8;base64,$getTitleTestBase64', + javascriptMode: JavascriptMode.unrestricted, onWebViewCreated: (WebViewController controller) { controllerCompleter.complete(controller); }, onPageStarted: (String url) { - // Ensure that this update is for the test page, in case any other - // URL (e.g., about:blank) fires earlier notifications. - if (url == testUrl) { - pageStarted.complete(null); - } + pageStarted.complete(null); }, onPageFinished: (String url) { - if (url == testUrl) { - pageLoaded.complete(null); - } + pageLoaded.complete(null); }, ), ), @@ -777,6 +771,12 @@ void main() { await pageStarted.future; await pageLoaded.future; + // On at least iOS, it does not appear to be guaranteed that the native + // code has the title when the page load completes. Execute some JavaScript + // before checking the title to ensure that the page has been fully parsed + // and processed. + await controller.runJavascript('1;'); + final String? title = await controller.getTitle(); expect(title, 'Some title'); }); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart index cd8c616bc474..00c7e998edc8 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart @@ -733,26 +733,20 @@ void main() { final Completer controllerCompleter = Completer(); - final testUrl = 'data:text/html;charset=utf-8;base64,$getTitleTestBase64'; await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: WebView( - initialUrl: testUrl, + initialUrl: 'data:text/html;charset=utf-8;base64,$getTitleTestBase64', + javascriptMode: JavascriptMode.unrestricted, onWebViewCreated: (WebViewController controller) { controllerCompleter.complete(controller); }, onPageStarted: (String url) { - // Ensure that this update is for the test page, in case any other - // URL (e.g., about:blank) fires earlier notifications. - if (url == testUrl) { - pageStarted.complete(null); - } + pageStarted.complete(null); }, onPageFinished: (String url) { - if (url == testUrl) { - pageLoaded.complete(null); - } + pageLoaded.complete(null); }, ), ), @@ -762,6 +756,12 @@ void main() { await pageStarted.future; await pageLoaded.future; + // On at least iOS, it does not appear to be guaranteed that the native + // code has the title when the page load completes. Execute some JavaScript + // before checking the title to ensure that the page has been fully parsed + // and processed. + await controller.runJavascript('1;'); + final String? title = await controller.getTitle(); expect(title, 'Some title'); }); From aa8095470117abd467d9f40a7f125d175c197401 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 23 Nov 2021 21:52:01 -0500 Subject: [PATCH 013/600] [flutter_plugin_tools] Check for FlutterTestRunner in FTL tests (#4531) When running via Firebase Test Lab, ensure that there is a test using `FlutterTestRunner`. This ensures that a plugin can't silently not run any of the Dart integration test on Android by having some other native integration test. Fixes https://github.com/flutter/flutter/issues/93952 --- script/tool/CHANGELOG.md | 4 + .../lib/src/firebase_test_lab_command.dart | 29 ++- .../test/firebase_test_lab_command_test.dart | 175 +++++++++++++----- 3 files changed, 156 insertions(+), 52 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index ee23428e66c1..a2a2ed824295 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +- Ensures that `firebase-test-lab` runs include an `integration_test` runner. + ## 0.7.3 - `native-test` now builds unit tests before running them on Windows and Linux, diff --git a/script/tool/lib/src/firebase_test_lab_command.dart b/script/tool/lib/src/firebase_test_lab_command.dart index 28afc638203f..4e53ee8fbace 100644 --- a/script/tool/lib/src/firebase_test_lab_command.dart +++ b/script/tool/lib/src/firebase_test_lab_command.dart @@ -127,16 +127,24 @@ class FirebaseTestLabCommand extends PackageLoopingCommand { '${example.displayName} does not support Android.'); } - if (!androidDirectory + final Directory uiTestDirectory = androidDirectory .childDirectory('app') .childDirectory('src') - .childDirectory('androidTest') - .existsSync()) { + .childDirectory('androidTest'); + if (!uiTestDirectory.existsSync()) { printError('No androidTest directory found.'); return PackageResult.fail( ['No tests ran (use --exclude if this is intentional).']); } + // Ensure that the Dart integration tests will be run, not just native UI + // tests. + if (!await _testsContainDartIntegrationTestRunner(uiTestDirectory)) { + printError('No integration_test runner found. ' + 'See the integration_test package README for setup instructions.'); + return PackageResult.fail(['No integration_test runner.']); + } + // Ensures that gradle wrapper exists final GradleProject project = GradleProject(example.directory, processRunner: processRunner, platform: platform); @@ -280,4 +288,19 @@ class FirebaseTestLabCommand extends PackageLoopingCommand { file is File && file.basename.endsWith('_test.dart')) .cast(); } + + /// Returns true if any of the test files in [uiTestDirectory] contain the + /// annotation that means that the test will reports the results of running + /// the Dart integration tests. + Future _testsContainDartIntegrationTestRunner( + Directory uiTestDirectory) async { + return uiTestDirectory + .list(recursive: true, followLinks: false) + .where((FileSystemEntity entity) => entity is File) + .cast() + .any((File file) { + return file.basename.endsWith('.java') && + file.readAsStringSync().contains('@RunWith(FlutterTestRunner.class)'); + }); + } } diff --git a/script/tool/test/firebase_test_lab_command_test.dart b/script/tool/test/firebase_test_lab_command_test.dart index 268210d00425..65f398b32ca8 100644 --- a/script/tool/test/firebase_test_lab_command_test.dart +++ b/script/tool/test/firebase_test_lab_command_test.dart @@ -8,7 +8,9 @@ import 'package:args/command_runner.dart'; import 'package:file/file.dart'; import 'package:file/memory.dart'; import 'package:flutter_plugin_tools/src/common/core.dart'; +import 'package:flutter_plugin_tools/src/common/file_utils.dart'; import 'package:flutter_plugin_tools/src/firebase_test_lab_command.dart'; +import 'package:path/path.dart' as p; import 'package:test/test.dart'; import 'mocks.dart'; @@ -38,15 +40,33 @@ void main() { runner.addCommand(command); }); + void _writeJavaTestFile(Directory pluginDir, String relativeFilePath, + {String runnerClass = 'FlutterTestRunner'}) { + childFileWithSubcomponents(pluginDir, p.posix.split(relativeFilePath)) + .writeAsStringSync(''' +@DartIntegrationTest +@RunWith($runnerClass.class) +public class MainActivityTest { + @Rule + public ActivityTestRule rule = new ActivityTestRule<>(FlutterActivity.class); +} +'''); + } + test('fails if gcloud auth fails', () async { processRunner.mockProcessesForExecutable['gcloud'] = [ MockProcess(exitCode: 1) ]; - createFakePlugin('plugin', packagesDir, extraFiles: [ + + const String javaTestFileRelativePath = + 'example/android/app/src/androidTest/MainActivityTest.java'; + final Directory pluginDir = + createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/integration_test/foo_test.dart', 'example/android/gradlew', - 'example/android/app/src/androidTest/MainActivityTest.java', + javaTestFileRelativePath, ]); + _writeJavaTestFile(pluginDir, javaTestFileRelativePath); Error? commandError; final List output = await runCapturingPrint( @@ -67,11 +87,16 @@ void main() { MockProcess(), // auth MockProcess(exitCode: 1), // config ]; - createFakePlugin('plugin', packagesDir, extraFiles: [ + + const String javaTestFileRelativePath = + 'example/android/app/src/androidTest/MainActivityTest.java'; + final Directory pluginDir = + createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/integration_test/foo_test.dart', 'example/android/gradlew', - 'example/android/app/src/androidTest/MainActivityTest.java', + javaTestFileRelativePath, ]); + _writeJavaTestFile(pluginDir, javaTestFileRelativePath); final List output = await runCapturingPrint(runner, ['firebase-test-lab']); @@ -85,18 +110,24 @@ void main() { }); test('only runs gcloud configuration once', () async { - createFakePlugin('plugin1', packagesDir, extraFiles: [ + const String javaTestFileRelativePath = + 'example/android/app/src/androidTest/MainActivityTest.java'; + final Directory plugin1Dir = + createFakePlugin('plugin1', packagesDir, extraFiles: [ 'test/plugin_test.dart', 'example/integration_test/foo_test.dart', 'example/android/gradlew', - 'example/android/app/src/androidTest/MainActivityTest.java', + javaTestFileRelativePath, ]); - createFakePlugin('plugin2', packagesDir, extraFiles: [ + _writeJavaTestFile(plugin1Dir, javaTestFileRelativePath); + final Directory plugin2Dir = + createFakePlugin('plugin2', packagesDir, extraFiles: [ 'test/plugin_test.dart', 'example/integration_test/bar_test.dart', 'example/android/gradlew', - 'example/android/app/src/androidTest/MainActivityTest.java', + javaTestFileRelativePath, ]); + _writeJavaTestFile(plugin2Dir, javaTestFileRelativePath); final List output = await runCapturingPrint(runner, [ 'firebase-test-lab', @@ -164,14 +195,18 @@ void main() { }); test('runs integration tests', () async { - createFakePlugin('plugin', packagesDir, extraFiles: [ + const String javaTestFileRelativePath = + 'example/android/app/src/androidTest/MainActivityTest.java'; + final Directory pluginDir = + createFakePlugin('plugin', packagesDir, extraFiles: [ 'test/plugin_test.dart', 'example/integration_test/bar_test.dart', 'example/integration_test/foo_test.dart', 'example/integration_test/should_not_run.dart', 'example/android/gradlew', - 'example/android/app/src/androidTest/MainActivityTest.java', + javaTestFileRelativePath, ]); + _writeJavaTestFile(pluginDir, javaTestFileRelativePath); final List output = await runCapturingPrint(runner, [ 'firebase-test-lab', @@ -237,12 +272,16 @@ void main() { }); test('fails if a test fails', () async { - createFakePlugin('plugin', packagesDir, extraFiles: [ + const String javaTestFileRelativePath = + 'example/android/app/src/androidTest/MainActivityTest.java'; + final Directory pluginDir = + createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/integration_test/bar_test.dart', 'example/integration_test/foo_test.dart', 'example/android/gradlew', - 'example/android/app/src/androidTest/MainActivityTest.java', + javaTestFileRelativePath, ]); + _writeJavaTestFile(pluginDir, javaTestFileRelativePath); processRunner.mockProcessesForExecutable['gcloud'] = [ MockProcess(), // auth @@ -258,12 +297,6 @@ void main() { 'firebase-test-lab', '--device', 'model=redfin,version=30', - '--device', - 'model=seoul,version=26', - '--test-run-id', - 'testRunId', - '--build-id', - 'buildId', ], errorHandler: (Error e) { commandError = e; @@ -295,12 +328,6 @@ void main() { 'firebase-test-lab', '--device', 'model=redfin,version=30', - '--device', - 'model=seoul,version=26', - '--test-run-id', - 'testRunId', - '--build-id', - 'buildId', ], errorHandler: (Error e) { commandError = e; @@ -321,10 +348,14 @@ void main() { }); test('fails for packages with no integration test files', () async { - createFakePlugin('plugin', packagesDir, extraFiles: [ + const String javaTestFileRelativePath = + 'example/android/app/src/androidTest/MainActivityTest.java'; + final Directory pluginDir = + createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/android/gradlew', - 'example/android/app/src/androidTest/MainActivityTest.java', + javaTestFileRelativePath, ]); + _writeJavaTestFile(pluginDir, javaTestFileRelativePath); Error? commandError; final List output = await runCapturingPrint( @@ -333,12 +364,6 @@ void main() { 'firebase-test-lab', '--device', 'model=redfin,version=30', - '--device', - 'model=seoul,version=26', - '--test-run-id', - 'testRunId', - '--build-id', - 'buildId', ], errorHandler: (Error e) { commandError = e; @@ -358,6 +383,48 @@ void main() { ); }); + test('fails for packages with no integration_test runner', () async { + const String javaTestFileRelativePath = + 'example/android/app/src/androidTest/MainActivityTest.java'; + final Directory pluginDir = + createFakePlugin('plugin', packagesDir, extraFiles: [ + 'test/plugin_test.dart', + 'example/integration_test/bar_test.dart', + 'example/integration_test/foo_test.dart', + 'example/integration_test/should_not_run.dart', + 'example/android/gradlew', + javaTestFileRelativePath, + ]); + // Use the wrong @RunWith annotation. + _writeJavaTestFile(pluginDir, javaTestFileRelativePath, + runnerClass: 'AndroidJUnit4.class'); + + Error? commandError; + final List output = await runCapturingPrint( + runner, + [ + 'firebase-test-lab', + '--device', + 'model=redfin,version=30', + ], + errorHandler: (Error e) { + commandError = e; + }, + ); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('Running for plugin'), + contains('No integration_test runner found. ' + 'See the integration_test package README for setup instructions.'), + contains('plugin:\n' + ' No integration_test runner.'), + ]), + ); + }); + test('skips packages with no android directory', () async { createFakePackage('package', packagesDir, extraFiles: [ 'example/integration_test/foo_test.dart', @@ -367,12 +434,6 @@ void main() { 'firebase-test-lab', '--device', 'model=redfin,version=30', - '--device', - 'model=seoul,version=26', - '--test-run-id', - 'testRunId', - '--build-id', - 'buildId', ]); expect( @@ -392,17 +453,19 @@ void main() { }); test('builds if gradlew is missing', () async { - createFakePlugin('plugin', packagesDir, extraFiles: [ + const String javaTestFileRelativePath = + 'example/android/app/src/androidTest/MainActivityTest.java'; + final Directory pluginDir = + createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/integration_test/foo_test.dart', - 'example/android/app/src/androidTest/MainActivityTest.java', + javaTestFileRelativePath, ]); + _writeJavaTestFile(pluginDir, javaTestFileRelativePath); final List output = await runCapturingPrint(runner, [ 'firebase-test-lab', '--device', 'model=redfin,version=30', - '--device', - 'model=seoul,version=26', '--test-run-id', 'testRunId', '--build-id', @@ -445,7 +508,7 @@ void main() { '/packages/plugin/example/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/0/ --device model=redfin,version=30 --device model=seoul,version=26' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/0/ --device model=redfin,version=30' .split(' '), '/packages/plugin/example'), ]), @@ -453,10 +516,14 @@ void main() { }); test('fails if building to generate gradlew fails', () async { - createFakePlugin('plugin', packagesDir, extraFiles: [ + const String javaTestFileRelativePath = + 'example/android/app/src/androidTest/MainActivityTest.java'; + final Directory pluginDir = + createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/integration_test/foo_test.dart', - 'example/android/app/src/androidTest/MainActivityTest.java', + javaTestFileRelativePath, ]); + _writeJavaTestFile(pluginDir, javaTestFileRelativePath); processRunner.mockProcessesForExecutable['flutter'] = [ MockProcess(exitCode: 1) // flutter build @@ -484,11 +551,14 @@ void main() { }); test('fails if assembleAndroidTest fails', () async { + const String javaTestFileRelativePath = + 'example/android/app/src/androidTest/MainActivityTest.java'; final Directory pluginDir = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/integration_test/foo_test.dart', - 'example/android/app/src/androidTest/MainActivityTest.java', + javaTestFileRelativePath, ]); + _writeJavaTestFile(pluginDir, javaTestFileRelativePath); final String gradlewPath = pluginDir .childDirectory('example') @@ -521,11 +591,14 @@ void main() { }); test('fails if assembleDebug fails', () async { + const String javaTestFileRelativePath = + 'example/android/app/src/androidTest/MainActivityTest.java'; final Directory pluginDir = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/integration_test/foo_test.dart', - 'example/android/app/src/androidTest/MainActivityTest.java', + javaTestFileRelativePath, ]); + _writeJavaTestFile(pluginDir, javaTestFileRelativePath); final String gradlewPath = pluginDir .childDirectory('example') @@ -562,11 +635,15 @@ void main() { }); test('experimental flag', () async { - createFakePlugin('plugin', packagesDir, extraFiles: [ + const String javaTestFileRelativePath = + 'example/android/app/src/androidTest/MainActivityTest.java'; + final Directory pluginDir = + createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/integration_test/foo_test.dart', 'example/android/gradlew', - 'example/android/app/src/androidTest/MainActivityTest.java', + javaTestFileRelativePath, ]); + _writeJavaTestFile(pluginDir, javaTestFileRelativePath); await runCapturingPrint(runner, [ 'firebase-test-lab', From a44ca2f238b145db106da7b2209b9341fde8f168 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Wed, 24 Nov 2021 19:38:07 +0100 Subject: [PATCH 014/600] [webview_flutter] Added contributing section to the README.md (#4543) --- .../webview_flutter_android/CHANGELOG.md | 3 ++- .../webview_flutter_android/README.md | 14 ++++++++++++++ .../webview_flutter_android/pubspec.yaml | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index ae76ea8bc342..71389dea3f17 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.3.1 +* Adds explanation on how to generate the pigeon communication layer and mockito mock objects. * Updates compileSdkVersion to 31. ## 2.3.0 diff --git a/packages/webview_flutter/webview_flutter_android/README.md b/packages/webview_flutter/webview_flutter_android/README.md index 38838562d13c..492ef913bbc7 100644 --- a/packages/webview_flutter/webview_flutter_android/README.md +++ b/packages/webview_flutter/webview_flutter_android/README.md @@ -7,6 +7,20 @@ The Android implementation of [`webview_flutter`][1]. This package is [endorsed][2], which means you can simply use `webview_flutter` normally. This package will be automatically included in your app when you do. +## Contributing + +This package uses [pigeon][3] to generate the communication layer between Flutter and the host platform (Android). The communication interface is defined in the `pigeons/android_webview.dart` file. After editing the communication interface regenerate the communication layer by running the `./generatePigeons.sh` shell script. + +Besides [pigeon][3] this package also uses [mockito][4] to generate mock objects for testing purposes. To generate the mock objects run the following command: +```bash +flutter packages pub run build_runner build --delete-conflicting-outputs +``` + +If you would like to contribute to the plugin, check out our [contribution guide][5]. + [1]: https://pub.dev/packages/webview_flutter [2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin +[3]: https://pub.dev/packages/pigeon +[4]: https://pub.dev/packages/mockito +[5]: https://github.com/flutter/plugins/blob/master/CONTRIBUTING.md diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 705951b1fda0..4bb1ebc6966a 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_android description: A Flutter plugin that provides a WebView widget on Android. repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.3.0 +version: 2.3.1 environment: sdk: ">=2.14.0 <3.0.0" From 7760622ae0953155bdcb8436d9275efbce828378 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Fri, 26 Nov 2021 09:13:03 +0100 Subject: [PATCH 015/600] [webview_flutter] Migrate webview_flutter_wkwebview to analysis_options.yaml (#4545) --- .../webview_flutter_wkwebview/CHANGELOG.md | 1 + .../analysis_options.yaml | 1 - .../webview_flutter_test.dart | 42 +++++++++-------- .../example/lib/main.dart | 47 ++++++++++--------- .../example/lib/navigation_request.dart | 2 +- .../example/lib/web_view.dart | 17 +++---- .../example/pubspec.yaml | 4 +- script/configs/custom_analysis.yaml | 3 +- 8 files changed, 62 insertions(+), 55 deletions(-) delete mode 100644 packages/webview_flutter/webview_flutter_wkwebview/analysis_options.yaml diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index 3b7816188dec..22cdb60280d7 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Migrates from `analysis_options_legacy.yaml` to `analysis_options.yaml`. * Integration test fixes. ## 2.4.0 diff --git a/packages/webview_flutter/webview_flutter_wkwebview/analysis_options.yaml b/packages/webview_flutter/webview_flutter_wkwebview/analysis_options.yaml deleted file mode 100644 index 5aeb4e7c5e21..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../../analysis_options_legacy.yaml diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart index 00c7e998edc8..8806495e629c 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart @@ -191,7 +191,7 @@ void main() { await onPageFinished.future; resizeButtonTapped = true; - await tester.tap(find.byKey(const ValueKey('resizeButton'))); + await tester.tap(find.byKey(const ValueKey('resizeButton'))); await tester.pumpAndSettle(); expect(buttonTapResizeCompleter.future, completes); }); @@ -446,10 +446,10 @@ void main() { testWidgets('Video plays inline when allowsInlineMediaPlayback is true', (WidgetTester tester) async { - Completer controllerCompleter = + final Completer controllerCompleter = Completer(); - Completer pageLoaded = Completer(); - Completer videoPlaying = Completer(); + final Completer pageLoaded = Completer(); + final Completer videoPlaying = Completer(); await tester.pumpWidget( Directionality( @@ -480,7 +480,7 @@ void main() { ), ), ); - WebViewController controller = await controllerCompleter.future; + final WebViewController controller = await controllerCompleter.future; await pageLoaded.future; // Pump once to trigger the video play. @@ -489,7 +489,7 @@ void main() { // Makes sure we get the correct event that indicates the video is actually playing. await videoPlaying.future; - String fullScreen = + final String fullScreen = await controller.runJavascriptReturningResult('isFullScreen();'); expect(fullScreen, _webviewBool(false)); }); @@ -497,10 +497,10 @@ void main() { testWidgets( 'Video plays full screen when allowsInlineMediaPlayback is false', (WidgetTester tester) async { - Completer controllerCompleter = + final Completer controllerCompleter = Completer(); - Completer pageLoaded = Completer(); - Completer videoPlaying = Completer(); + final Completer pageLoaded = Completer(); + final Completer videoPlaying = Completer(); await tester.pumpWidget( Directionality( @@ -531,7 +531,7 @@ void main() { ), ), ); - WebViewController controller = await controllerCompleter.future; + final WebViewController controller = await controllerCompleter.future; await pageLoaded.future; // Pump once to trigger the video play. @@ -540,7 +540,7 @@ void main() { // Makes sure we get the correct event that indicates the video is actually playing. await videoPlaying.future; - String fullScreen = + final String fullScreen = await controller.runJavascriptReturningResult('isFullScreen();'); expect(fullScreen, _webviewBool(true)); }); @@ -718,7 +718,7 @@ void main() { }); testWidgets('getTitle', (WidgetTester tester) async { - final String getTitleTest = ''' + const String getTitleTest = ''' Some title @@ -768,7 +768,7 @@ void main() { group('Programmatic Scroll', () { testWidgets('setAndGetScrollPosition', (WidgetTester tester) async { - final String scrollTestPage = ''' + const String scrollTestPage = ''' @@ -815,7 +815,7 @@ void main() { final WebViewController controller = await controllerCompleter.future; await pageLoaded.future; - await tester.pumpAndSettle(Duration(seconds: 3)); + await tester.pumpAndSettle(const Duration(seconds: 3)); int scrollPosX = await controller.getScrollX(); int scrollPosY = await controller.getScrollY(); @@ -845,7 +845,7 @@ void main() { }); group('NavigationDelegate', () { - final String blankPage = ""; + const String blankPage = ''; final String blankPageEncoded = 'data:text/html;charset=utf-8;base64,' + base64Encode(const Utf8Encoder().convert(blankPage)); @@ -941,7 +941,7 @@ void main() { testWidgets( 'onWebResourceError only called for main frame', (WidgetTester tester) async { - final String iframeTest = ''' + const String iframeTest = ''' @@ -1169,11 +1169,13 @@ Future _runJavascriptReturningResult( if (defaultTargetPlatform == TargetPlatform.iOS) { return await controller.runJavascriptReturningResult(js); } - return jsonDecode(await controller.runJavascriptReturningResult(js)); + return jsonDecode(await controller.runJavascriptReturningResult(js)) + as String; } class ResizableWebView extends StatefulWidget { - ResizableWebView({required this.onResize, required this.onPageFinished}); + const ResizableWebView( + {required this.onResize, required this.onPageFinished}); final JavascriptMessageHandler onResize; final VoidCallback onPageFinished; @@ -1228,14 +1230,14 @@ class ResizableWebViewState extends State { ), ), TextButton( - key: Key('resizeButton'), + key: const Key('resizeButton'), onPressed: () { setState(() { webViewWidth += 100.0; webViewHeight += 100.0; }); }, - child: Text('ResizeButton'), + child: const Text('ResizeButton'), ), ], ), diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart index 72168ecc8568..df85d42bea8f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart @@ -19,7 +19,7 @@ import 'navigation_request.dart'; import 'web_view.dart'; void main() { - runApp(MaterialApp(home: _WebViewExample())); + runApp(const MaterialApp(home: _WebViewExample())); } const String kNavigationExamplePage = ''' @@ -80,7 +80,7 @@ class _WebViewExampleState extends State<_WebViewExample> { ), // We're using a Builder here so we have a context that is below the Scaffold // to allow calling Scaffold.of(context) so we can show a snackbar. - body: Builder(builder: (context) { + body: Builder(builder: (BuildContext context) { return WebView( initialUrl: 'https://flutter.dev', onWebViewCreated: (WebViewController controller) { @@ -125,7 +125,7 @@ class _WebViewExampleState extends State<_WebViewExample> { } Set _createJavascriptChannels(BuildContext context) { - return { + return { JavascriptChannel( name: 'Snackbar', onMessageReceived: (JavascriptMessage message) { @@ -149,7 +149,7 @@ enum _MenuOptions { } class _SampleMenu extends StatelessWidget { - _SampleMenu(this.controller); + const _SampleMenu(this.controller); final Future controller; @@ -242,7 +242,7 @@ class _SampleMenu extends StatelessWidget { ); } - void _onShowUserAgent( + Future _onShowUserAgent( WebViewController controller, BuildContext context) async { // Send a message with the user agent string to the Snackbar JavaScript channel we registered // with the WebView. @@ -250,7 +250,7 @@ class _SampleMenu extends StatelessWidget { 'Snackbar.postMessage("User Agent: " + navigator.userAgent);'); } - void _onListCookies( + Future _onListCookies( WebViewController controller, BuildContext context) async { final String cookies = await controller.runJavascriptReturningResult('document.cookie'); @@ -266,7 +266,8 @@ class _SampleMenu extends StatelessWidget { )); } - void _onAddToCache(WebViewController controller, BuildContext context) async { + Future _onAddToCache( + WebViewController controller, BuildContext context) async { await controller.runJavascript( 'caches.open("test_caches_entry"); localStorage["test_localStorage"] = "dummy_entry";'); ScaffoldMessenger.of(context).showSnackBar(const SnackBar( @@ -274,20 +275,22 @@ class _SampleMenu extends StatelessWidget { )); } - void _onListCache(WebViewController controller, BuildContext context) async { + Future _onListCache( + WebViewController controller, BuildContext context) async { await controller.runJavascript('caches.keys()' '.then((cacheKeys) => JSON.stringify({"cacheKeys" : cacheKeys, "localStorage" : localStorage}))' '.then((caches) => Snackbar.postMessage(caches))'); } - void _onClearCache(WebViewController controller, BuildContext context) async { + Future _onClearCache( + WebViewController controller, BuildContext context) async { await controller.clearCache(); ScaffoldMessenger.of(context).showSnackBar(const SnackBar( - content: Text("Cache cleared."), + content: Text('Cache cleared.'), )); } - void _onClearCookies( + Future _onClearCookies( WebViewController controller, BuildContext context) async { final bool hadCookies = await WebView.platform.clearCookies(); String message = 'There were cookies. Now, they are gone!'; @@ -299,31 +302,31 @@ class _SampleMenu extends StatelessWidget { )); } - void _onNavigationDelegateExample( + Future _onNavigationDelegateExample( WebViewController controller, BuildContext context) async { final String contentBase64 = base64Encode(const Utf8Encoder().convert(kNavigationExamplePage)); await controller.loadUrl('data:text/html;base64,$contentBase64'); } - void _onLoadLocalFileExample( + Future _onLoadLocalFileExample( WebViewController controller, BuildContext context) async { - String pathToIndex = await _prepareLocalFile(); + final String pathToIndex = await _prepareLocalFile(); await controller.loadFile(pathToIndex); } - void _onLoadHtmlStringExample( + Future _onLoadHtmlStringExample( WebViewController controller, BuildContext context) async { await controller.loadHtmlString(kLocalFileExamplePage); } - void _onDoPostRequest( + Future _onDoPostRequest( WebViewController controller, BuildContext context) async { - WebViewRequest request = WebViewRequest( + final WebViewRequest request = WebViewRequest( uri: Uri.parse('https://httpbin.org/post'), method: WebViewRequestMethod.post, - headers: {'foo': 'bar', 'Content-Type': 'text/plain'}, + headers: {'foo': 'bar', 'Content-Type': 'text/plain'}, body: Uint8List.fromList('Test Body'.codeUnits), ); await controller.loadRequest(request); @@ -345,7 +348,7 @@ class _SampleMenu extends StatelessWidget { static Future _prepareLocalFile() async { final String tmpDir = (await getTemporaryDirectory()).path; - File indexFile = File('$tmpDir/www/index.html'); + final File indexFile = File('$tmpDir/www/index.html'); await Directory('$tmpDir/www').create(recursive: true); await indexFile.writeAsString(kLocalFileExamplePage); @@ -382,7 +385,7 @@ class _NavigationControls extends StatelessWidget { } else { // ignore: deprecated_member_use Scaffold.of(context).showSnackBar( - const SnackBar(content: Text("No back history item")), + const SnackBar(content: Text('No back history item')), ); return; } @@ -399,7 +402,7 @@ class _NavigationControls extends StatelessWidget { // ignore: deprecated_member_use Scaffold.of(context).showSnackBar( const SnackBar( - content: Text("No forward history item")), + content: Text('No forward history item')), ); return; } @@ -421,4 +424,4 @@ class _NavigationControls extends StatelessWidget { } /// Callback type for handling messages sent from JavaScript running in a web view. -typedef void JavascriptMessageHandler(JavascriptMessage message); +typedef JavascriptMessageHandler = void Function(JavascriptMessage message); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/navigation_request.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/navigation_request.dart index c1ff8dc5a690..2f6d7c9f8cdd 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/navigation_request.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/navigation_request.dart @@ -14,6 +14,6 @@ class NavigationRequest { @override String toString() { - return '$runtimeType(url: $url, isForMainFrame: $isForMainFrame)'; + return 'NavigationRequest(url: $url, isForMainFrame: $isForMainFrame)'; } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart index ab4b77336765..ed7f3e842051 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart @@ -15,7 +15,7 @@ import 'navigation_request.dart'; /// Optional callback invoked when a web view is first created. [controller] is /// the [WebViewController] for the created web view. -typedef void WebViewCreatedCallback(WebViewController controller); +typedef WebViewCreatedCallback = void Function(WebViewController controller); /// Decides how to handle a specific navigation request. /// @@ -23,20 +23,20 @@ typedef void WebViewCreatedCallback(WebViewController controller); /// `navigation` should be handled. /// /// See also: [WebView.navigationDelegate]. -typedef FutureOr NavigationDelegate( +typedef NavigationDelegate = FutureOr Function( NavigationRequest navigation); /// Signature for when a [WebView] has started loading a page. -typedef void PageStartedCallback(String url); +typedef PageStartedCallback = void Function(String url); /// Signature for when a [WebView] has finished loading a page. -typedef void PageFinishedCallback(String url); +typedef PageFinishedCallback = void Function(String url); /// Signature for when a [WebView] is loading a page. -typedef void PageLoadingCallback(int progress); +typedef PageLoadingCallback = void Function(int progress); /// Signature for when a [WebView] has failed to load a resource. -typedef void WebResourceErrorCallback(WebResourceError error); +typedef WebResourceErrorCallback = void Function(WebResourceError error); /// A web view widget for showing html content. /// @@ -259,7 +259,7 @@ class _WebViewState extends State { context: context, onWebViewPlatformCreated: (WebViewPlatformController? webViewPlatformController) { - WebViewController controller = WebViewController._( + final WebViewController controller = WebViewController._( widget, webViewPlatformController!, _javascriptChannelRegistry, @@ -547,7 +547,7 @@ class WebViewController { bool? hasNavigationDelegate; bool? hasProgressTracking; bool? debuggingEnabled; - WebSetting userAgent = WebSetting.absent(); + WebSetting userAgent = WebSetting.absent(); if (currentValue.javascriptMode != newValue.javascriptMode) { javascriptMode = newValue.javascriptMode; } @@ -645,6 +645,7 @@ class _PlatformCallbacksHandler implements WebViewPlatformCallbacksHandler { } } + @override void onWebResourceError(WebResourceError error) { if (_webView.onWebResourceError != null) { _webView.onWebResourceError!(error); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml index c8001c8ffde4..2888cddac552 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml @@ -21,10 +21,10 @@ dependencies: dev_dependencies: espresso: ^0.1.0+2 - flutter_test: - sdk: flutter flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter pedantic: ^1.10.0 diff --git a/script/configs/custom_analysis.yaml b/script/configs/custom_analysis.yaml index 3840d64f33f3..95cc4058e96e 100644 --- a/script/configs/custom_analysis.yaml +++ b/script/configs/custom_analysis.yaml @@ -25,7 +25,8 @@ - quick_actions - url_launcher - video_player -- webview_flutter +- webview_flutter/webview_flutter +- webview_flutter/webview_flutter_platform_interface # These plugins are deprecated in favor of the Community Plus versions, and # will be removed from the repo once the critical support window has passed, From a9f34a1d528ca34e3d37dd1e2e6d7c69081aae37 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Sun, 28 Nov 2021 15:42:05 +0100 Subject: [PATCH 016/600] [ci] Exclude files generated by Pigeon from analysis. (#4546) --- analysis_options.yaml | 1 + analysis_options_legacy.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/analysis_options.yaml b/analysis_options.yaml index 901067736edc..60d2c33601eb 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -57,6 +57,7 @@ analyzer: - '**/*.g.dart' - 'lib/src/generated/*.dart' - '**/*.mocks.dart' # Mockito @GenerateMocks + - '**/*.pigeon.dart' # Pigeon generated file linter: rules: diff --git a/analysis_options_legacy.yaml b/analysis_options_legacy.yaml index 793640e22d27..b2a343f220c8 100644 --- a/analysis_options_legacy.yaml +++ b/analysis_options_legacy.yaml @@ -5,6 +5,7 @@ analyzer: - '**/*.g.dart' - 'lib/src/generated/*.dart' - '**/*.mocks.dart' # Mockito @GenerateMocks + - '**/*.pigeon.dart' # Pigeon generated file errors: always_require_non_null_named_parameters: false # not needed with nnbd # TODO(https://github.com/flutter/flutter/issues/74381): From 1e2ffdb006e65224aab3fe13cc06b6bf2004ba8a Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Sun, 28 Nov 2021 11:01:56 -0500 Subject: [PATCH 017/600] [quick_actions] Migrate to new analysis options (#4548) Removes the use of the legacy analysis options, and fixes the resulting analysis issues. No version change: No changes that should affect behavior or package clients. Part of flutter/flutter#76229 --- packages/quick_actions/analysis_options.yaml | 1 - .../quick_actions/quick_actions/CHANGELOG.md | 1 + .../integration_test/quick_actions_test.dart | 2 +- .../quick_actions/example/lib/main.dart | 10 ++++---- .../test/quick_actions_test.dart | 19 +++++++-------- .../CHANGELOG.md | 6 ++++- .../method_channel_quick_actions.dart | 4 ++-- .../quick_actions_platform.dart | 6 ++--- .../lib/types/quick_action_handler.dart | 2 +- .../method_channel_quick_actions_test.dart | 23 ++++++++----------- ...quick_actions_platform_interface_test.dart | 16 ++++++++----- script/configs/custom_analysis.yaml | 1 - 12 files changed, 47 insertions(+), 44 deletions(-) delete mode 100644 packages/quick_actions/analysis_options.yaml diff --git a/packages/quick_actions/analysis_options.yaml b/packages/quick_actions/analysis_options.yaml deleted file mode 100644 index cda4f6e153e6..000000000000 --- a/packages/quick_actions/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../analysis_options_legacy.yaml diff --git a/packages/quick_actions/quick_actions/CHANGELOG.md b/packages/quick_actions/quick_actions/CHANGELOG.md index 497f98a9def2..f684e747bf08 100644 --- a/packages/quick_actions/quick_actions/CHANGELOG.md +++ b/packages/quick_actions/quick_actions/CHANGELOG.md @@ -1,6 +1,7 @@ ## NEXT * Updates Android compileSdkVersion to 31. +* Updates code for analyzer changes. ## 0.6.0+8 diff --git a/packages/quick_actions/quick_actions/example/integration_test/quick_actions_test.dart b/packages/quick_actions/quick_actions/example/integration_test/quick_actions_test.dart index cfe3eb0db656..bfefef3b298b 100644 --- a/packages/quick_actions/quick_actions/example/integration_test/quick_actions_test.dart +++ b/packages/quick_actions/quick_actions/example/integration_test/quick_actions_test.dart @@ -11,7 +11,7 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('Can set shortcuts', (WidgetTester tester) async { - final QuickActions quickActions = QuickActions(); + const QuickActions quickActions = QuickActions(); await quickActions.initialize(null); const ShortcutItem shortCutItem = ShortcutItem( diff --git a/packages/quick_actions/quick_actions/example/lib/main.dart b/packages/quick_actions/quick_actions/example/lib/main.dart index 8e47d16683dd..1ce6f51d71de 100644 --- a/packages/quick_actions/quick_actions/example/lib/main.dart +++ b/packages/quick_actions/quick_actions/example/lib/main.dart @@ -19,13 +19,13 @@ class MyApp extends StatelessWidget { theme: ThemeData( primarySwatch: Colors.blue, ), - home: MyHomePage(), + home: const MyHomePage(), ); } } class MyHomePage extends StatefulWidget { - MyHomePage({Key? key}) : super(key: key); + const MyHomePage({Key? key}) : super(key: key); @override _MyHomePageState createState() => _MyHomePageState(); @@ -38,7 +38,7 @@ class _MyHomePageState extends State { void initState() { super.initState(); - final QuickActions quickActions = QuickActions(); + const QuickActions quickActions = QuickActions(); quickActions.initialize((String shortcutType) { setState(() { if (shortcutType != null) { @@ -61,7 +61,7 @@ class _MyHomePageState extends State { type: 'action_two', localizedTitle: 'Action two', icon: 'ic_launcher'), - ]).then((value) { + ]).then((void _) { setState(() { if (shortcut == 'no action set') { shortcut = 'actions ready'; @@ -74,7 +74,7 @@ class _MyHomePageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('$shortcut'), + title: Text(shortcut), ), body: const Center( child: Text('On home screen, long press the app icon to ' diff --git a/packages/quick_actions/quick_actions/test/quick_actions_test.dart b/packages/quick_actions/quick_actions/test/quick_actions_test.dart index 27d3c81a809a..09fcc9799c11 100644 --- a/packages/quick_actions/quick_actions/test/quick_actions_test.dart +++ b/packages/quick_actions/quick_actions/test/quick_actions_test.dart @@ -23,7 +23,7 @@ void main() { test('initialize() PlatformInterface', () async { const QuickActions quickActions = QuickActions(); - QuickActionHandler handler = (type) {}; + final QuickActionHandler handler = (String type) {}; await quickActions.initialize(handler); verify(QuickActionsPlatform.instance.initialize(handler)).called(1); @@ -31,17 +31,18 @@ void main() { test('setShortcutItems() PlatformInterface', () { const QuickActions quickActions = QuickActions(); - QuickActionHandler handler = (type) {}; + final QuickActionHandler handler = (String type) {}; quickActions.initialize(handler); - quickActions.setShortcutItems([]); + quickActions.setShortcutItems([]); verify(QuickActionsPlatform.instance.initialize(handler)).called(1); - verify(QuickActionsPlatform.instance.setShortcutItems([])).called(1); + verify(QuickActionsPlatform.instance.setShortcutItems([])) + .called(1); }); test('clearShortcutItems() PlatformInterface', () { const QuickActions quickActions = QuickActions(); - QuickActionHandler handler = (type) {}; + final QuickActionHandler handler = (String type) {}; quickActions.initialize(handler); quickActions.clearShortcutItems(); @@ -57,15 +58,15 @@ class MockQuickActionsPlatform extends Mock implements QuickActionsPlatform { @override Future clearShortcutItems() async => - super.noSuchMethod(Invocation.method(#clearShortcutItems, [])); + super.noSuchMethod(Invocation.method(#clearShortcutItems, [])); @override Future initialize(QuickActionHandler? handler) async => - super.noSuchMethod(Invocation.method(#initialize, [handler])); + super.noSuchMethod(Invocation.method(#initialize, [handler])); @override - Future setShortcutItems(List? items) async => - super.noSuchMethod(Invocation.method(#setShortcutItems, [items])); + Future setShortcutItems(List? items) async => super + .noSuchMethod(Invocation.method(#setShortcutItems, [items])); } class MockQuickActions extends QuickActions {} diff --git a/packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md b/packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md index 4b63991e9c4a..9568f8064171 100644 --- a/packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ -# 1.0.0 +## NEXT + +* Updates code for analyzer changes. + +## 1.0.0 * Initial release of quick_actions_platform_interface diff --git a/packages/quick_actions/quick_actions_platform_interface/lib/method_channel/method_channel_quick_actions.dart b/packages/quick_actions/quick_actions_platform_interface/lib/method_channel/method_channel_quick_actions.dart index 8172fe017a4d..fa76c0f238c9 100644 --- a/packages/quick_actions/quick_actions_platform_interface/lib/method_channel/method_channel_quick_actions.dart +++ b/packages/quick_actions/quick_actions_platform_interface/lib/method_channel/method_channel_quick_actions.dart @@ -9,7 +9,7 @@ import 'package:quick_actions_platform_interface/types/types.dart'; import '../platform_interface/quick_actions_platform.dart'; -final MethodChannel _channel = +const MethodChannel _channel = MethodChannel('plugins.flutter.io/quick_actions'); /// An implementation of [QuickActionsPlatform] that uses method channels. @@ -22,7 +22,7 @@ class MethodChannelQuickActions extends QuickActionsPlatform { Future initialize(QuickActionHandler handler) async { channel.setMethodCallHandler((MethodCall call) async { assert(call.method == 'launch'); - handler(call.arguments); + handler(call.arguments as String); }); final String? action = await channel.invokeMethod('getLaunchAction'); diff --git a/packages/quick_actions/quick_actions_platform_interface/lib/platform_interface/quick_actions_platform.dart b/packages/quick_actions/quick_actions_platform_interface/lib/platform_interface/quick_actions_platform.dart index 2e06935ccb09..13e98857ef05 100644 --- a/packages/quick_actions/quick_actions_platform_interface/lib/platform_interface/quick_actions_platform.dart +++ b/packages/quick_actions/quick_actions_platform_interface/lib/platform_interface/quick_actions_platform.dart @@ -40,16 +40,16 @@ abstract class QuickActionsPlatform extends PlatformInterface { /// /// Call this once before any further interaction with the plugin. Future initialize(QuickActionHandler handler) async { - throw UnimplementedError("initialize() has not been implemented."); + throw UnimplementedError('initialize() has not been implemented.'); } /// Sets the [ShortcutItem]s to become the app's quick actions. Future setShortcutItems(List items) async { - throw UnimplementedError("setShortcutItems() has not been implemented."); + throw UnimplementedError('setShortcutItems() has not been implemented.'); } /// Removes all [ShortcutItem]s registered for the app. Future clearShortcutItems() { - throw UnimplementedError("clearShortcutItems() has not been implemented."); + throw UnimplementedError('clearShortcutItems() has not been implemented.'); } } diff --git a/packages/quick_actions/quick_actions_platform_interface/lib/types/quick_action_handler.dart b/packages/quick_actions/quick_actions_platform_interface/lib/types/quick_action_handler.dart index 27c6bb494dfd..ecc813863369 100644 --- a/packages/quick_actions/quick_actions_platform_interface/lib/types/quick_action_handler.dart +++ b/packages/quick_actions/quick_actions_platform_interface/lib/types/quick_action_handler.dart @@ -5,4 +5,4 @@ /// Handler for a quick action launch event. /// /// The argument [type] corresponds to the [ShortcutItem]'s field. -typedef void QuickActionHandler(String type); +typedef QuickActionHandler = void Function(String type); diff --git a/packages/quick_actions/quick_actions_platform_interface/test/method_channel_quick_actions_test.dart b/packages/quick_actions/quick_actions_platform_interface/test/method_channel_quick_actions_test.dart index f3e172e207fe..240f11bd8037 100644 --- a/packages/quick_actions/quick_actions_platform_interface/test/method_channel_quick_actions_test.dart +++ b/packages/quick_actions/quick_actions_platform_interface/test/method_channel_quick_actions_test.dart @@ -13,7 +13,7 @@ void main() { TestWidgetsFlutterBinding.ensureInitialized(); group('$MethodChannelQuickActions', () { - MethodChannelQuickActions quickActions = MethodChannelQuickActions(); + final MethodChannelQuickActions quickActions = MethodChannelQuickActions(); final List log = []; @@ -29,9 +29,7 @@ void main() { group('#initialize', () { test('passes getLaunchAction on launch method', () { - quickActions.initialize((type) { - 'launch'; - }); + quickActions.initialize((String type) {}); expect( log, @@ -59,19 +57,18 @@ void main() { group('#setShortCutItems', () { test('passes shortcutItem through channel', () { - quickActions.initialize((type) { - 'launch'; - }); - quickActions.setShortcutItems([ - ShortcutItem(type: 'test', localizedTitle: 'title', icon: 'icon.svg') + quickActions.initialize((String type) {}); + quickActions.setShortcutItems([ + const ShortcutItem( + type: 'test', localizedTitle: 'title', icon: 'icon.svg') ]); expect( log, [ isMethodCall('getLaunchAction', arguments: null), - isMethodCall('setShortcutItems', arguments: [ - { + isMethodCall('setShortcutItems', arguments: >[ + { 'type': 'test', 'localizedTitle': 'title', 'icon': 'icon.svg', @@ -111,9 +108,7 @@ void main() { group('#clearShortCutItems', () { test('send clearShortcutItems through channel', () { - quickActions.initialize((type) { - 'launch'; - }); + quickActions.initialize((String type) {}); quickActions.clearShortcutItems(); expect( diff --git a/packages/quick_actions/quick_actions_platform_interface/test/quick_actions_platform_interface_test.dart b/packages/quick_actions/quick_actions_platform_interface/test/quick_actions_platform_interface_test.dart index d1c8798aa65f..b9655dc56a3c 100644 --- a/packages/quick_actions/quick_actions_platform_interface/test/quick_actions_platform_interface_test.dart +++ b/packages/quick_actions/quick_actions_platform_interface/test/quick_actions_platform_interface_test.dart @@ -5,6 +5,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:quick_actions_platform_interface/method_channel/method_channel_quick_actions.dart'; import 'package:quick_actions_platform_interface/platform_interface/quick_actions_platform.dart'; +import 'package:quick_actions_platform_interface/types/types.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -31,11 +32,12 @@ void main() { 'Default implementation of initialize() should throw unimplemented error', () { // Arrange - final QuickActionsPlatform = ExtendsQuickActionsPlatform(); + final ExtendsQuickActionsPlatform quickActionsPlatform = + ExtendsQuickActionsPlatform(); // Act & Assert expect( - () => QuickActionsPlatform.initialize((type) {}), + () => quickActionsPlatform.initialize((String type) {}), throwsUnimplementedError, ); }); @@ -44,11 +46,12 @@ void main() { 'Default implementation of setShortcutItems() should throw unimplemented error', () { // Arrange - final QuickActionsPlatform = ExtendsQuickActionsPlatform(); + final ExtendsQuickActionsPlatform quickActionsPlatform = + ExtendsQuickActionsPlatform(); // Act & Assert expect( - () => QuickActionsPlatform.setShortcutItems([]), + () => quickActionsPlatform.setShortcutItems([]), throwsUnimplementedError, ); }); @@ -57,11 +60,12 @@ void main() { 'Default implementation of clearShortcutItems() should throw unimplemented error', () { // Arrange - final QuickActionsPlatform = ExtendsQuickActionsPlatform(); + final ExtendsQuickActionsPlatform quickActionsPlatform = + ExtendsQuickActionsPlatform(); // Act & Assert expect( - () => QuickActionsPlatform.clearShortcutItems(), + () => quickActionsPlatform.clearShortcutItems(), throwsUnimplementedError, ); }); diff --git a/script/configs/custom_analysis.yaml b/script/configs/custom_analysis.yaml index 95cc4058e96e..758157df3fe4 100644 --- a/script/configs/custom_analysis.yaml +++ b/script/configs/custom_analysis.yaml @@ -22,7 +22,6 @@ - in_app_purchase - ios_platform_images - local_auth -- quick_actions - url_launcher - video_player - webview_flutter/webview_flutter From 07e4027369df8cee5811c58af2cd615a4f30c405 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Sun, 28 Nov 2021 17:47:04 +0100 Subject: [PATCH 018/600] [webview_flutter] Pre-emptively ignore prefer_const_constructor warning. (#4550) --- .../example/lib/web_view.dart | 2 ++ .../test/webview_android_widget_test.dart | 22 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart index 9a535cc38d2b..6a5a3f68f0a3 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart @@ -568,6 +568,8 @@ class WebViewController { bool? hasNavigationDelegate; bool? hasProgressTracking; bool? debuggingEnabled; + // TODO(mvanbeusekom): Cleanup and convert to const constructor when platform_interface is fixed (see https://github.com/flutter/flutter/issues/94311) + // ignore: prefer_const_constructors WebSetting userAgent = WebSetting.absent(); bool? zoomEnabled; if (currentValue.javascriptMode != newValue.javascriptMode) { diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart index 0c90f2a4a19c..f34fe199cdf8 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart @@ -70,6 +70,8 @@ void main() { creationParams: creationParams ?? CreationParams( webSettings: WebSettings( + // TODO(mvanbeusekom): Cleanup and convert to const constructor when platform_interface is fixed (see https://github.com/flutter/flutter/issues/94311) + // ignore: prefer_const_constructors userAgent: WebSetting.absent(), hasNavigationDelegate: hasNavigationDelegate, hasProgressTracking: hasProgressTracking, @@ -121,6 +123,8 @@ void main() { creationParams: CreationParams( initialUrl: 'https://www.google.com', webSettings: WebSettings( + // TODO(mvanbeusekom): Cleanup and convert to const constructor when platform_interface is fixed (see https://github.com/flutter/flutter/issues/94311) + // ignore: prefer_const_constructors userAgent: WebSetting.absent(), hasNavigationDelegate: false, ), @@ -138,6 +142,8 @@ void main() { creationParams: CreationParams( userAgent: 'MyUserAgent', webSettings: WebSettings( + // TODO(mvanbeusekom): Cleanup and convert to const constructor when platform_interface is fixed (see https://github.com/flutter/flutter/issues/94311) + // ignore: prefer_const_constructors userAgent: WebSetting.absent(), hasNavigationDelegate: false, ), @@ -154,6 +160,8 @@ void main() { autoMediaPlaybackPolicy: AutoMediaPlaybackPolicy.require_user_action_for_all_media_types, webSettings: WebSettings( + // TODO(mvanbeusekom): Cleanup and convert to const constructor when platform_interface is fixed (see https://github.com/flutter/flutter/issues/94311) + // ignore: prefer_const_constructors userAgent: WebSetting.absent(), hasNavigationDelegate: false, ), @@ -169,6 +177,8 @@ void main() { creationParams: CreationParams( autoMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow, webSettings: WebSettings( + // TODO(mvanbeusekom): Cleanup and convert to const constructor when platform_interface is fixed (see https://github.com/flutter/flutter/issues/94311) + // ignore: prefer_const_constructors userAgent: WebSetting.absent(), hasNavigationDelegate: false, ), @@ -184,6 +194,8 @@ void main() { creationParams: CreationParams( javascriptChannelNames: {'a', 'b'}, webSettings: WebSettings( + // TODO(mvanbeusekom): Cleanup and convert to const constructor when platform_interface is fixed (see https://github.com/flutter/flutter/issues/94311) + // ignore: prefer_const_constructors userAgent: WebSetting.absent(), hasNavigationDelegate: false, ), @@ -202,6 +214,8 @@ void main() { tester, creationParams: CreationParams( webSettings: WebSettings( + // TODO(mvanbeusekom): Cleanup and convert to const constructor when platform_interface is fixed (see https://github.com/flutter/flutter/issues/94311) + // ignore: prefer_const_constructors userAgent: WebSetting.absent(), javascriptMode: JavascriptMode.unrestricted, hasNavigationDelegate: false, @@ -217,6 +231,8 @@ void main() { tester, creationParams: CreationParams( webSettings: WebSettings( + // TODO(mvanbeusekom): Cleanup and convert to const constructor when platform_interface is fixed (see https://github.com/flutter/flutter/issues/94311) + // ignore: prefer_const_constructors userAgent: WebSetting.absent(), hasNavigationDelegate: true, ), @@ -232,6 +248,8 @@ void main() { tester, creationParams: CreationParams( webSettings: WebSettings( + // TODO(mvanbeusekom): Cleanup and convert to const constructor when platform_interface is fixed (see https://github.com/flutter/flutter/issues/94311) + // ignore: prefer_const_constructors userAgent: WebSetting.absent(), debuggingEnabled: true, hasNavigationDelegate: false, @@ -247,6 +265,8 @@ void main() { tester, creationParams: CreationParams( webSettings: WebSettings( + // TODO(mvanbeusekom): Cleanup and convert to const constructor when platform_interface is fixed (see https://github.com/flutter/flutter/issues/94311) + // ignore: prefer_const_constructors userAgent: WebSetting.of('myUserAgent'), hasNavigationDelegate: false, ), @@ -261,6 +281,8 @@ void main() { tester, creationParams: CreationParams( webSettings: WebSettings( + // TODO(mvanbeusekom): Cleanup and convert to const constructor when platform_interface is fixed (see https://github.com/flutter/flutter/issues/94311) + // ignore: prefer_const_constructors userAgent: WebSetting.absent(), zoomEnabled: false, hasNavigationDelegate: false, From a456f6343840eba0d0b4de27e215f210bf4dbcc4 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Sun, 28 Nov 2021 19:03:22 -0500 Subject: [PATCH 019/600] [url_launcher] Publish fully federated version (#4536) Restores the app-facing package to a publishable state now that _android and _ios implementation packages are published. Part of flutter/flutter#68498 --- packages/url_launcher/url_launcher/CHANGELOG.md | 2 +- packages/url_launcher/url_launcher/pubspec.yaml | 12 +++--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index a7feb1162163..46b71f6010b5 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,4 +1,4 @@ -## NEXT +## 6.0.16 * Moves Android and iOS implementations to federated packages. diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index 48cf5d2ba437..5844031ae03e 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -3,10 +3,7 @@ description: Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes. repository: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.0.15 -# Temporarily disable publishing to allow moving Android and iOS -# implementations. -publish_to: none +version: 6.0.16 environment: sdk: ">=2.14.0 <3.0.0" @@ -32,11 +29,8 @@ dependencies: flutter: sdk: flutter meta: ^1.3.0 - # Temporary path dependencies to allow moving Android and iOS implementations. - url_launcher_android: - path: ../url_launcher_android - url_launcher_ios: - path: ../url_launcher_ios + url_launcher_android: ^6.0.13 + url_launcher_ios: ^6.0.13 url_launcher_linux: ^2.0.0 url_launcher_macos: ^2.0.0 url_launcher_platform_interface: ^2.0.3 From 8b661af14c3ef8f41c7f9c8a83178eb0dc98a4d6 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Mon, 29 Nov 2021 07:12:05 +0100 Subject: [PATCH 020/600] [webview_flutter] Migrate webview_flutter_platform_interface to analysis_options.yaml (#4549) --- .../CHANGELOG.md | 4 ++ .../analysis_options.yaml | 1 - .../webview_method_channel.dart | 51 ++++++++++--------- .../platform_interface/webview_platform.dart | 2 +- .../webview_platform_controller.dart | 47 +++++++++-------- .../lib/src/types/creation_params.dart | 2 +- .../lib/src/types/javascript_channel.dart | 4 +- .../lib/src/types/web_settings.dart | 15 ++++-- .../lib/src/types/webview_request.dart | 12 ++--- .../webview_flutter_platform_interface.dart | 2 +- .../pubspec.yaml | 2 +- .../webview_method_channel_test.dart | 16 +++--- .../javascript_channel_registry_test.dart | 2 +- .../test/src/types/webview_request_test.dart | 6 +-- script/configs/custom_analysis.yaml | 1 - 15 files changed, 90 insertions(+), 77 deletions(-) delete mode 100644 packages/webview_flutter/webview_flutter_platform_interface/analysis_options.yaml diff --git a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md index efc43cf28ed1..4e506ded26db 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.5.2 + +* Mirgrates from analysis_options_legacy.yaml to the more strict analysis_options.yaml. + ## 1.5.1 * Reverts the addition of `onUrlChanged`, which was unintentionally a breaking diff --git a/packages/webview_flutter/webview_flutter_platform_interface/analysis_options.yaml b/packages/webview_flutter/webview_flutter_platform_interface/analysis_options.yaml deleted file mode 100644 index 5aeb4e7c5e21..000000000000 --- a/packages/webview_flutter/webview_flutter_platform_interface/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../../analysis_options_legacy.yaml diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/method_channel/webview_method_channel.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/method_channel/webview_method_channel.dart index 8df9f4c62b33..043b5888a69f 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/method_channel/webview_method_channel.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/method_channel/webview_method_channel.dart @@ -35,32 +35,34 @@ class MethodChannelWebViewPlatform implements WebViewPlatformController { Future _onMethodCall(MethodCall call) async { switch (call.method) { case 'javascriptChannelMessage': - final String channel = call.arguments['channel']!; - final String message = call.arguments['message']!; + final String channel = call.arguments['channel']! as String; + final String message = call.arguments['message']! as String; _javascriptChannelRegistry.onJavascriptChannelMessage(channel, message); return true; case 'navigationRequest': return await _platformCallbacksHandler.onNavigationRequest( - url: call.arguments['url']!, - isForMainFrame: call.arguments['isForMainFrame']!, + url: call.arguments['url']! as String, + isForMainFrame: call.arguments['isForMainFrame']! as bool, ); case 'onPageFinished': - _platformCallbacksHandler.onPageFinished(call.arguments['url']!); + _platformCallbacksHandler + .onPageFinished(call.arguments['url']! as String); return null; case 'onProgress': - _platformCallbacksHandler.onProgress(call.arguments['progress']); + _platformCallbacksHandler.onProgress(call.arguments['progress'] as int); return null; case 'onPageStarted': - _platformCallbacksHandler.onPageStarted(call.arguments['url']!); + _platformCallbacksHandler + .onPageStarted(call.arguments['url']! as String); return null; case 'onWebResourceError': _platformCallbacksHandler.onWebResourceError( WebResourceError( - errorCode: call.arguments['errorCode']!, - description: call.arguments['description']!, + errorCode: call.arguments['errorCode']! as int, + description: call.arguments['description']! as String, // iOS doesn't support `failingUrl`. - failingUrl: call.arguments['failingUrl'], - domain: call.arguments['domain'], + failingUrl: call.arguments['failingUrl'] as String?, + domain: call.arguments['domain'] as String?, errorType: call.arguments['errorType'] == null ? null : WebResourceErrorType.values.firstWhere( @@ -122,23 +124,24 @@ class MethodChannelWebViewPlatform implements WebViewPlatformController { @override Future canGoBack() => - _channel.invokeMethod("canGoBack").then((result) => result!); + _channel.invokeMethod('canGoBack').then((bool? result) => result!); @override - Future canGoForward() => - _channel.invokeMethod("canGoForward").then((result) => result!); + Future canGoForward() => _channel + .invokeMethod('canGoForward') + .then((bool? result) => result!); @override - Future goBack() => _channel.invokeMethod("goBack"); + Future goBack() => _channel.invokeMethod('goBack'); @override - Future goForward() => _channel.invokeMethod("goForward"); + Future goForward() => _channel.invokeMethod('goForward'); @override - Future reload() => _channel.invokeMethod("reload"); + Future reload() => _channel.invokeMethod('reload'); @override - Future clearCache() => _channel.invokeMethod("clearCache"); + Future clearCache() => _channel.invokeMethod('clearCache'); @override Future updateSettings(WebSettings settings) async { @@ -152,7 +155,7 @@ class MethodChannelWebViewPlatform implements WebViewPlatformController { Future evaluateJavascript(String javascript) { return _channel .invokeMethod('evaluateJavascript', javascript) - .then((result) => result!); + .then((String? result) => result!); } @override @@ -164,7 +167,7 @@ class MethodChannelWebViewPlatform implements WebViewPlatformController { Future runJavascriptReturningResult(String javascript) { return _channel .invokeMethod('runJavascriptReturningResult', javascript) - .then((result) => result!); + .then((String? result) => result!); } @override @@ -180,7 +183,7 @@ class MethodChannelWebViewPlatform implements WebViewPlatformController { } @override - Future getTitle() => _channel.invokeMethod("getTitle"); + Future getTitle() => _channel.invokeMethod('getTitle'); @override Future scrollTo(int x, int y) { @@ -200,17 +203,17 @@ class MethodChannelWebViewPlatform implements WebViewPlatformController { @override Future getScrollX() => - _channel.invokeMethod("getScrollX").then((result) => result!); + _channel.invokeMethod('getScrollX').then((int? result) => result!); @override Future getScrollY() => - _channel.invokeMethod("getScrollY").then((result) => result!); + _channel.invokeMethod('getScrollY').then((int? result) => result!); /// Method channel implementation for [WebViewPlatform.clearCookies]. static Future clearCookies() { return _cookieManagerChannel .invokeMethod('clearCookies') - .then((dynamic result) => result!); + .then((dynamic result) => result! as bool); } static Map _webSettingsToMap(WebSettings? settings) { diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_platform.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_platform.dart index 4732f54d6456..ca17cb684a53 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_platform.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_platform.dart @@ -61,6 +61,6 @@ abstract class WebViewPlatform { /// Returns true if cookies were present before clearing, else false. Future clearCookies() { throw UnimplementedError( - "WebView clearCookies is not implemented on the current platform"); + 'WebView clearCookies is not implemented on the current platform'); } } diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_platform_controller.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_platform_controller.dart index cfc817450372..da73204e0099 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_platform_controller.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_platform_controller.dart @@ -21,6 +21,9 @@ abstract class WebViewPlatformController { /// Callbacks made by the WebView will be delegated to `handler`. /// /// The `handler` parameter must not be null. + // TODO(mvanbeusekom): Remove unused constructor parameter with the next + // breaking change (see issue https://github.com/flutter/flutter/issues/94292). + // ignore: avoid_unused_constructor_parameters WebViewPlatformController(WebViewPlatformCallbacksHandler handler); /// Loads the file located on the specified [absoluteFilePath]. @@ -34,7 +37,7 @@ abstract class WebViewPlatformController { String absoluteFilePath, ) { throw UnimplementedError( - "WebView loadFlutterAsset is not implemented on the current platform"); + 'WebView loadFlutterAsset is not implemented on the current platform'); } /// Loads the supplied HTML string. @@ -46,7 +49,7 @@ abstract class WebViewPlatformController { String? baseUrl, }) { throw UnimplementedError( - "WebView loadHtmlString is not implemented on the current platform"); + 'WebView loadHtmlString is not implemented on the current platform'); } /// Loads the specified URL. @@ -62,7 +65,7 @@ abstract class WebViewPlatformController { Map? headers, ) { throw UnimplementedError( - "WebView loadUrl is not implemented on the current platform"); + 'WebView loadUrl is not implemented on the current platform'); } /// Makes a specific HTTP request ands loads the response in the webview. @@ -81,7 +84,7 @@ abstract class WebViewPlatformController { WebViewRequest request, ) { throw UnimplementedError( - "WebView loadRequest is not implemented on the current platform"); + 'WebView loadRequest is not implemented on the current platform'); } /// Updates the webview settings. @@ -90,7 +93,7 @@ abstract class WebViewPlatformController { /// All null fields in `settings` are ignored. Future updateSettings(WebSettings setting) { throw UnimplementedError( - "WebView updateSettings is not implemented on the current platform"); + 'WebView updateSettings is not implemented on the current platform'); } /// Accessor to the current URL that the WebView is displaying. @@ -98,19 +101,19 @@ abstract class WebViewPlatformController { /// If no URL was ever loaded, returns `null`. Future currentUrl() { throw UnimplementedError( - "WebView currentUrl is not implemented on the current platform"); + 'WebView currentUrl is not implemented on the current platform'); } /// Checks whether there's a back history item. Future canGoBack() { throw UnimplementedError( - "WebView canGoBack is not implemented on the current platform"); + 'WebView canGoBack is not implemented on the current platform'); } /// Checks whether there's a forward history item. Future canGoForward() { throw UnimplementedError( - "WebView canGoForward is not implemented on the current platform"); + 'WebView canGoForward is not implemented on the current platform'); } /// Goes back in the history of this WebView. @@ -118,7 +121,7 @@ abstract class WebViewPlatformController { /// If there is no back history item this is a no-op. Future goBack() { throw UnimplementedError( - "WebView goBack is not implemented on the current platform"); + 'WebView goBack is not implemented on the current platform'); } /// Goes forward in the history of this WebView. @@ -126,13 +129,13 @@ abstract class WebViewPlatformController { /// If there is no forward history item this is a no-op. Future goForward() { throw UnimplementedError( - "WebView goForward is not implemented on the current platform"); + 'WebView goForward is not implemented on the current platform'); } /// Reloads the current URL. Future reload() { throw UnimplementedError( - "WebView reload is not implemented on the current platform"); + 'WebView reload is not implemented on the current platform'); } /// Clears all caches used by the [WebView]. @@ -145,7 +148,7 @@ abstract class WebViewPlatformController { /// 4. Local Storage. Future clearCache() { throw UnimplementedError( - "WebView clearCache is not implemented on the current platform"); + 'WebView clearCache is not implemented on the current platform'); } /// Evaluates a JavaScript expression in the context of the current page. @@ -154,7 +157,7 @@ abstract class WebViewPlatformController { /// evaluated expression is not supported (e.g on iOS not all non-primitive types can be evaluated). Future evaluateJavascript(String javascript) { throw UnimplementedError( - "WebView evaluateJavascript is not implemented on the current platform"); + 'WebView evaluateJavascript is not implemented on the current platform'); } /// Runs the given JavaScript in the context of the current page. @@ -162,7 +165,7 @@ abstract class WebViewPlatformController { /// The Future completes with an error if a JavaScript error occurred. Future runJavascript(String javascript) { throw UnimplementedError( - "WebView runJavascript is not implemented on the current platform"); + 'WebView runJavascript is not implemented on the current platform'); } /// Runs the given JavaScript in the context of the current page, and returns the result. @@ -172,7 +175,7 @@ abstract class WebViewPlatformController { /// certain non-primitive types on iOS, as well as `undefined` or `null` on iOS 14+. Future runJavascriptReturningResult(String javascript) { throw UnimplementedError( - "WebView runJavascriptReturningResult is not implemented on the current platform"); + 'WebView runJavascriptReturningResult is not implemented on the current platform'); } /// Adds new JavaScript channels to the set of enabled channels. @@ -188,7 +191,7 @@ abstract class WebViewPlatformController { /// See also: [CreationParams.javascriptChannelNames]. Future addJavascriptChannels(Set javascriptChannelNames) { throw UnimplementedError( - "WebView addJavascriptChannels is not implemented on the current platform"); + 'WebView addJavascriptChannels is not implemented on the current platform'); } /// Removes JavaScript channel names from the set of enabled channels. @@ -197,13 +200,13 @@ abstract class WebViewPlatformController { /// [CreationParams.javascriptChannelNames]. Future removeJavascriptChannels(Set javascriptChannelNames) { throw UnimplementedError( - "WebView removeJavascriptChannels is not implemented on the current platform"); + 'WebView removeJavascriptChannels is not implemented on the current platform'); } /// Returns the title of the currently loaded page. Future getTitle() { throw UnimplementedError( - "WebView getTitle is not implemented on the current platform"); + 'WebView getTitle is not implemented on the current platform'); } /// Set the scrolled position of this view. @@ -211,7 +214,7 @@ abstract class WebViewPlatformController { /// The parameters `x` and `y` specify the position to scroll to in WebView pixels. Future scrollTo(int x, int y) { throw UnimplementedError( - "WebView scrollTo is not implemented on the current platform"); + 'WebView scrollTo is not implemented on the current platform'); } /// Move the scrolled position of this view. @@ -219,7 +222,7 @@ abstract class WebViewPlatformController { /// The parameters `x` and `y` specify the amount of WebView pixels to scroll by. Future scrollBy(int x, int y) { throw UnimplementedError( - "WebView scrollBy is not implemented on the current platform"); + 'WebView scrollBy is not implemented on the current platform'); } /// Return the horizontal scroll position of this view. @@ -227,7 +230,7 @@ abstract class WebViewPlatformController { /// Scroll position is measured from left. Future getScrollX() { throw UnimplementedError( - "WebView getScrollX is not implemented on the current platform"); + 'WebView getScrollX is not implemented on the current platform'); } /// Return the vertical scroll position of this view. @@ -235,6 +238,6 @@ abstract class WebViewPlatformController { /// Scroll position is measured from top. Future getScrollY() { throw UnimplementedError( - "WebView getScrollY is not implemented on the current platform"); + 'WebView getScrollY is not implemented on the current platform'); } } diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/creation_params.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/creation_params.dart index f213e976ad84..e69f510e32fb 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/creation_params.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/creation_params.dart @@ -55,6 +55,6 @@ class CreationParams { @override String toString() { - return '$runtimeType(initialUrl: $initialUrl, settings: $webSettings, javascriptChannelNames: $javascriptChannelNames, UserAgent: $userAgent)'; + return 'CreationParams(initialUrl: $initialUrl, settings: $webSettings, javascriptChannelNames: $javascriptChannelNames, UserAgent: $userAgent)'; } } diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/javascript_channel.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/javascript_channel.dart index f32a41893eb5..e68cc2ef1291 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/javascript_channel.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/javascript_channel.dart @@ -5,9 +5,9 @@ import 'javascript_message.dart'; /// Callback type for handling messages sent from JavaScript running in a web view. -typedef void JavascriptMessageHandler(JavascriptMessage message); +typedef JavascriptMessageHandler = void Function(JavascriptMessage message); -final RegExp _validChannelNames = RegExp('^[a-zA-Z_][a-zA-Z0-9_]*\$'); +final RegExp _validChannelNames = RegExp(r'^[a-zA-Z_][a-zA-Z0-9_]*$'); /// A named channel for receiving messaged from JavaScript code running inside a web view. class JavascriptChannel { diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/web_settings.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/web_settings.dart index 3d94153c886e..57c0a482089c 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/web_settings.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/web_settings.dart @@ -7,20 +7,21 @@ import 'package:flutter/widgets.dart'; import 'javascript_mode.dart'; /// A single setting for configuring a WebViewPlatform which may be absent. +@immutable class WebSetting { /// Constructs an absent setting instance. /// /// The [isPresent] field for the instance will be false. /// /// Accessing [value] for an absent instance will throw. - WebSetting.absent() + const WebSetting.absent() : _value = null, isPresent = false; /// Constructs a setting of the given `value`. /// /// The [isPresent] field for the instance will be true. - WebSetting.of(T value) + const WebSetting.of(T value) : _value = value, isPresent = true; @@ -51,9 +52,13 @@ class WebSetting { @override bool operator ==(Object other) { - if (other.runtimeType != runtimeType) return false; - final WebSetting typedOther = other as WebSetting; - return typedOther.isPresent == isPresent && typedOther._value == _value; + if (other.runtimeType != runtimeType) { + return false; + } + + return other is WebSetting && + other.isPresent == isPresent && + other._value == _value; } @override diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/webview_request.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/webview_request.dart index 5e520f1baa9e..940e3a25f4ba 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/webview_request.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/webview_request.dart @@ -32,7 +32,7 @@ class WebViewRequest { WebViewRequest({ required this.uri, required this.method, - this.headers = const {}, + this.headers = const {}, this.body, }); @@ -49,10 +49,10 @@ class WebViewRequest { final Uint8List? body; /// Serializes the [WebViewRequest] to JSON. - Map toJson() => { - 'uri': this.uri.toString(), - 'method': this.method.serialize(), - 'headers': this.headers, - 'body': this.body, + Map toJson() => { + 'uri': uri.toString(), + 'method': method.serialize(), + 'headers': headers, + 'body': body, }; } diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/webview_flutter_platform_interface.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/webview_flutter_platform_interface.dart index b508989ed978..aa41c8285975 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/lib/webview_flutter_platform_interface.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/webview_flutter_platform_interface.dart @@ -2,6 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +export 'src/method_channel/webview_method_channel.dart'; export 'src/platform_interface/platform_interface.dart'; export 'src/types/types.dart'; -export 'src/method_channel/webview_method_channel.dart'; diff --git a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml index 33eb77cbefd5..318fea614b4d 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/webview_flut issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview_flutter%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 1.5.1 +version: 1.5.2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/method_channel/webview_method_channel_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/method_channel/webview_method_channel_test.dart index 396013535aa9..2db2dfad4f57 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/test/src/method_channel/webview_method_channel_test.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/method_channel/webview_method_channel_test.dart @@ -170,10 +170,10 @@ void main() { isMethodCall( 'loadRequest', arguments: { - 'request': { + 'request': { 'uri': 'https://test.url', 'method': 'get', - 'headers': {}, + 'headers': {}, 'body': null, } }, @@ -186,7 +186,7 @@ void main() { await webViewPlatform.loadRequest(WebViewRequest( uri: Uri.parse('https://test.url'), method: WebViewRequestMethod.get, - headers: {'foo': 'bar'}, + headers: {'foo': 'bar'}, body: Uint8List.fromList('hello world'.codeUnits), )); @@ -196,10 +196,10 @@ void main() { isMethodCall( 'loadRequest', arguments: { - 'request': { + 'request': { 'uri': 'https://test.url', 'method': 'get', - 'headers': {'foo': 'bar'}, + 'headers': {'foo': 'bar'}, 'body': 'hello world'.codeUnits, } }, @@ -311,7 +311,7 @@ void main() { test('updateSettings', () async { final WebSettings settings = - WebSettings(userAgent: WebSetting.of('Dart Test')); + WebSettings(userAgent: const WebSetting.of('Dart Test')); await webViewPlatform.updateSettings(settings); expect( @@ -329,7 +329,7 @@ void main() { test('updateSettings all parameters', () async { final WebSettings settings = WebSettings( - userAgent: WebSetting.of('Dart Test'), + userAgent: const WebSetting.of('Dart Test'), javascriptMode: JavascriptMode.disabled, hasNavigationDelegate: true, hasProgressTracking: true, @@ -362,7 +362,7 @@ void main() { test('updateSettings without settings', () async { final WebSettings settings = - WebSettings(userAgent: WebSetting.absent()); + WebSettings(userAgent: const WebSetting.absent()); await webViewPlatform.updateSettings(settings); expect( diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/platform_interface/javascript_channel_registry_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/platform_interface/javascript_channel_registry_test.dart index 55d0e1e13bd1..df1b53090fc8 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/test/src/platform_interface/javascript_channel_registry_test.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/platform_interface/javascript_channel_registry_test.dart @@ -3,9 +3,9 @@ // found in the LICENSE file. import 'package:flutter_test/flutter_test.dart'; +import 'package:webview_flutter_platform_interface/src/platform_interface/javascript_channel_registry.dart'; import 'package:webview_flutter_platform_interface/src/types/javascript_channel.dart'; import 'package:webview_flutter_platform_interface/src/types/types.dart'; -import 'package:webview_flutter_platform_interface/src/platform_interface/javascript_channel_registry.dart'; void main() { final Map _log = {}; diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/types/webview_request_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/types/webview_request_test.dart index 5d2b568fe5dd..6e1a4d7b4d56 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/test/src/types/webview_request_test.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/types/webview_request_test.dart @@ -23,17 +23,17 @@ void main() { serializedRequest = request.toJson(); expect(serializedRequest['uri'], 'https://flutter.dev'); expect(serializedRequest['method'], 'get'); - expect(serializedRequest['headers'], {}); + expect(serializedRequest['headers'], {}); expect(serializedRequest['body'], null); // Test serialization of headers and body request = WebViewRequest( uri: Uri.parse('https://flutter.dev'), method: WebViewRequestMethod.get, - headers: {'foo': 'bar'}, + headers: {'foo': 'bar'}, body: Uint8List.fromList('Example Body'.codeUnits), ); serializedRequest = request.toJson(); - expect(serializedRequest['headers'], {'foo': 'bar'}); + expect(serializedRequest['headers'], {'foo': 'bar'}); expect(serializedRequest['body'], 'Example Body'.codeUnits); }); } diff --git a/script/configs/custom_analysis.yaml b/script/configs/custom_analysis.yaml index 758157df3fe4..b3823dac1079 100644 --- a/script/configs/custom_analysis.yaml +++ b/script/configs/custom_analysis.yaml @@ -25,7 +25,6 @@ - url_launcher - video_player - webview_flutter/webview_flutter -- webview_flutter/webview_flutter_platform_interface # These plugins are deprecated in favor of the Community Plus versions, and # will be removed from the repo once the critical support window has passed, From f581e1396c1fd58048763e94ae9c6ff76bbf4c0e Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Mon, 29 Nov 2021 13:49:34 +0100 Subject: [PATCH 021/600] [webview_flutter] Updates webview_flutter_platform_interface to version 1.5.2 (#4554) * Updates webview_flutter_platform_interface to version 1.5.2 Updates to the latest version of the webview_flutter_platform_interface and fix one analysis warning caused by this update. This will also fix the tree. * Trigger build --- packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md | 1 + .../webview_flutter_wkwebview/example/lib/web_view.dart | 2 +- packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index 22cdb60280d7..e9c59c26c3ce 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -2,6 +2,7 @@ * Migrates from `analysis_options_legacy.yaml` to `analysis_options.yaml`. * Integration test fixes. +* Updates to webview_flutter_platform_interface version 1.5.2. ## 2.4.0 diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart index ed7f3e842051..ae540ae23c2d 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart @@ -547,7 +547,7 @@ class WebViewController { bool? hasNavigationDelegate; bool? hasProgressTracking; bool? debuggingEnabled; - WebSetting userAgent = WebSetting.absent(); + WebSetting userAgent = const WebSetting.absent(); if (currentValue.javascriptMode != newValue.javascriptMode) { javascriptMode = newValue.javascriptMode; } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 466c1a2a4fcd..a152e2ba2882 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -18,7 +18,7 @@ flutter: dependencies: flutter: sdk: flutter - webview_flutter_platform_interface: ^1.3.0 + webview_flutter_platform_interface: ^1.5.2 dev_dependencies: flutter_driver: From 6bc9a2bd62e5ac9ff36b0fddf77ece6fdddaeef6 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Mon, 29 Nov 2021 15:22:05 +0100 Subject: [PATCH 022/600] [webview_flutter] Clean up prefer_const_constructor ignore statements (#4553) --- .../webview_flutter_android/CHANGELOG.md | 4 ++ .../example/lib/web_view.dart | 4 +- .../webview_flutter_android/pubspec.yaml | 2 +- .../test/webview_android_widget_test.dart | 44 +++++-------------- 4 files changed, 17 insertions(+), 37 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 71389dea3f17..209406c4bba0 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates to webview_flutter_platform_interface version 1.5.2. + ## 2.3.1 * Adds explanation on how to generate the pigeon communication layer and mockito mock objects. diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart index 6a5a3f68f0a3..77d92c69c5fa 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart @@ -568,9 +568,7 @@ class WebViewController { bool? hasNavigationDelegate; bool? hasProgressTracking; bool? debuggingEnabled; - // TODO(mvanbeusekom): Cleanup and convert to const constructor when platform_interface is fixed (see https://github.com/flutter/flutter/issues/94311) - // ignore: prefer_const_constructors - WebSetting userAgent = WebSetting.absent(); + WebSetting userAgent = const WebSetting.absent(); bool? zoomEnabled; if (currentValue.javascriptMode != newValue.javascriptMode) { javascriptMode = newValue.javascriptMode; diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 4bb1ebc6966a..a9fa97d454bd 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -19,7 +19,7 @@ flutter: dependencies: flutter: sdk: flutter - webview_flutter_platform_interface: ^1.2.0 + webview_flutter_platform_interface: ^1.5.2 dev_dependencies: build_runner: ^2.1.4 diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart index f34fe199cdf8..d969664a42ed 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart @@ -70,9 +70,7 @@ void main() { creationParams: creationParams ?? CreationParams( webSettings: WebSettings( - // TODO(mvanbeusekom): Cleanup and convert to const constructor when platform_interface is fixed (see https://github.com/flutter/flutter/issues/94311) - // ignore: prefer_const_constructors - userAgent: WebSetting.absent(), + userAgent: const WebSetting.absent(), hasNavigationDelegate: hasNavigationDelegate, hasProgressTracking: hasProgressTracking, )), @@ -123,9 +121,7 @@ void main() { creationParams: CreationParams( initialUrl: 'https://www.google.com', webSettings: WebSettings( - // TODO(mvanbeusekom): Cleanup and convert to const constructor when platform_interface is fixed (see https://github.com/flutter/flutter/issues/94311) - // ignore: prefer_const_constructors - userAgent: WebSetting.absent(), + userAgent: const WebSetting.absent(), hasNavigationDelegate: false, ), ), @@ -142,9 +138,7 @@ void main() { creationParams: CreationParams( userAgent: 'MyUserAgent', webSettings: WebSettings( - // TODO(mvanbeusekom): Cleanup and convert to const constructor when platform_interface is fixed (see https://github.com/flutter/flutter/issues/94311) - // ignore: prefer_const_constructors - userAgent: WebSetting.absent(), + userAgent: const WebSetting.absent(), hasNavigationDelegate: false, ), ), @@ -160,9 +154,7 @@ void main() { autoMediaPlaybackPolicy: AutoMediaPlaybackPolicy.require_user_action_for_all_media_types, webSettings: WebSettings( - // TODO(mvanbeusekom): Cleanup and convert to const constructor when platform_interface is fixed (see https://github.com/flutter/flutter/issues/94311) - // ignore: prefer_const_constructors - userAgent: WebSetting.absent(), + userAgent: const WebSetting.absent(), hasNavigationDelegate: false, ), ), @@ -177,9 +169,7 @@ void main() { creationParams: CreationParams( autoMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow, webSettings: WebSettings( - // TODO(mvanbeusekom): Cleanup and convert to const constructor when platform_interface is fixed (see https://github.com/flutter/flutter/issues/94311) - // ignore: prefer_const_constructors - userAgent: WebSetting.absent(), + userAgent: const WebSetting.absent(), hasNavigationDelegate: false, ), ), @@ -194,9 +184,7 @@ void main() { creationParams: CreationParams( javascriptChannelNames: {'a', 'b'}, webSettings: WebSettings( - // TODO(mvanbeusekom): Cleanup and convert to const constructor when platform_interface is fixed (see https://github.com/flutter/flutter/issues/94311) - // ignore: prefer_const_constructors - userAgent: WebSetting.absent(), + userAgent: const WebSetting.absent(), hasNavigationDelegate: false, ), ), @@ -214,9 +202,7 @@ void main() { tester, creationParams: CreationParams( webSettings: WebSettings( - // TODO(mvanbeusekom): Cleanup and convert to const constructor when platform_interface is fixed (see https://github.com/flutter/flutter/issues/94311) - // ignore: prefer_const_constructors - userAgent: WebSetting.absent(), + userAgent: const WebSetting.absent(), javascriptMode: JavascriptMode.unrestricted, hasNavigationDelegate: false, ), @@ -231,9 +217,7 @@ void main() { tester, creationParams: CreationParams( webSettings: WebSettings( - // TODO(mvanbeusekom): Cleanup and convert to const constructor when platform_interface is fixed (see https://github.com/flutter/flutter/issues/94311) - // ignore: prefer_const_constructors - userAgent: WebSetting.absent(), + userAgent: const WebSetting.absent(), hasNavigationDelegate: true, ), ), @@ -248,9 +232,7 @@ void main() { tester, creationParams: CreationParams( webSettings: WebSettings( - // TODO(mvanbeusekom): Cleanup and convert to const constructor when platform_interface is fixed (see https://github.com/flutter/flutter/issues/94311) - // ignore: prefer_const_constructors - userAgent: WebSetting.absent(), + userAgent: const WebSetting.absent(), debuggingEnabled: true, hasNavigationDelegate: false, ), @@ -265,9 +247,7 @@ void main() { tester, creationParams: CreationParams( webSettings: WebSettings( - // TODO(mvanbeusekom): Cleanup and convert to const constructor when platform_interface is fixed (see https://github.com/flutter/flutter/issues/94311) - // ignore: prefer_const_constructors - userAgent: WebSetting.of('myUserAgent'), + userAgent: const WebSetting.of('myUserAgent'), hasNavigationDelegate: false, ), ), @@ -281,9 +261,7 @@ void main() { tester, creationParams: CreationParams( webSettings: WebSettings( - // TODO(mvanbeusekom): Cleanup and convert to const constructor when platform_interface is fixed (see https://github.com/flutter/flutter/issues/94311) - // ignore: prefer_const_constructors - userAgent: WebSetting.absent(), + userAgent: const WebSetting.absent(), zoomEnabled: false, hasNavigationDelegate: false, ), From 3990aaf18eb81262efac6a6477d9f3536e69b44b Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Mon, 29 Nov 2021 21:32:08 +0100 Subject: [PATCH 023/600] [in_app_purchase] Deprecated the `InAppPurchaseAndroidPlatformAddition.enablePendingPurchases` method. (#4527) --- .../in_app_purchase_android/CHANGELOG.md | 5 +-- .../inapppurchase/BillingClientFactory.java | 5 +-- .../BillingClientFactoryImpl.java | 9 ++---- .../inapppurchase/MethodCallHandlerImpl.java | 12 ++----- .../inapppurchase/MethodCallHandlerTest.java | 9 +----- .../in_app_purchase_test.dart | 1 - .../example/lib/main.dart | 5 --- .../billing_client_wrapper.dart | 17 +++------- ...pp_purchase_android_platform_addition.dart | 31 +++++++++---------- .../in_app_purchase_android/pubspec.yaml | 2 +- .../billing_client_wrapper_test.dart | 6 +--- ...rchase_android_platform_addition_test.dart | 2 -- ...in_app_purchase_android_platform_test.dart | 2 -- 13 files changed, 33 insertions(+), 73 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md index 4198d9345889..9bce54c2df0f 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 0.2.1 -* Updates compileSdkVersion to 31. +* Deprecated the `InAppPurchaseAndroidPlatformAddition.enablePendingPurchases()` method and `InAppPurchaseAndroidPlatformAddition.enablePendingPurchase` property. Since Google Play no longer accepts App submissions that don't support pending purchases it is no longer necessary to acknowledge this through code. +* Updates example app Android compileSdkVersion to 31. ## 0.2.0 diff --git a/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/BillingClientFactory.java b/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/BillingClientFactory.java index 7b21cbf2e6f5..81fdf27be88e 100644 --- a/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/BillingClientFactory.java +++ b/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/BillingClientFactory.java @@ -17,10 +17,7 @@ interface BillingClientFactory { * * @param context The context used to create the {@link BillingClient}. * @param channel The method channel used to create the {@link BillingClient}. - * @param enablePendingPurchases Whether to enable pending purchases. Throws an exception if it is - * false. * @return The {@link BillingClient} object that is created. */ - BillingClient createBillingClient( - @NonNull Context context, @NonNull MethodChannel channel, boolean enablePendingPurchases); + BillingClient createBillingClient(@NonNull Context context, @NonNull MethodChannel channel); } diff --git a/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/BillingClientFactoryImpl.java b/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/BillingClientFactoryImpl.java index c256d2c59551..6d2639840a74 100644 --- a/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/BillingClientFactoryImpl.java +++ b/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/BillingClientFactoryImpl.java @@ -12,12 +12,9 @@ final class BillingClientFactoryImpl implements BillingClientFactory { @Override - public BillingClient createBillingClient( - Context context, MethodChannel channel, boolean enablePendingPurchases) { - BillingClient.Builder builder = BillingClient.newBuilder(context); - if (enablePendingPurchases) { - builder.enablePendingPurchases(); - } + public BillingClient createBillingClient(Context context, MethodChannel channel) { + BillingClient.Builder builder = BillingClient.newBuilder(context).enablePendingPurchases(); + return builder.setListener(new PluginPurchaseListener(channel)).build(); } } diff --git a/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/MethodCallHandlerImpl.java b/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/MethodCallHandlerImpl.java index 5b58808b2b49..23b9cb6ecda2 100644 --- a/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/MethodCallHandlerImpl.java +++ b/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/MethodCallHandlerImpl.java @@ -110,10 +110,7 @@ public void onMethodCall(MethodCall call, MethodChannel.Result result) { isReady(result); break; case InAppPurchasePlugin.MethodNames.START_CONNECTION: - startConnection( - (int) call.argument("handle"), - (boolean) call.argument("enablePendingPurchases"), - result); + startConnection((int) call.argument("handle"), result); break; case InAppPurchasePlugin.MethodNames.END_CONNECTION: endConnection(result); @@ -319,12 +316,9 @@ public void onPurchaseHistoryResponse( }); } - private void startConnection( - final int handle, final boolean enablePendingPurchases, final MethodChannel.Result result) { + private void startConnection(final int handle, final MethodChannel.Result result) { if (billingClient == null) { - billingClient = - billingClientFactory.createBillingClient( - applicationContext, methodChannel, enablePendingPurchases); + billingClient = billingClientFactory.createBillingClient(applicationContext, methodChannel); } billingClient.startConnection( diff --git a/packages/in_app_purchase/in_app_purchase_android/android/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java b/packages/in_app_purchase/in_app_purchase_android/android/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java index 6f9256cd07bd..d676bf3436ee 100644 --- a/packages/in_app_purchase/in_app_purchase_android/android/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java +++ b/packages/in_app_purchase/in_app_purchase_android/android/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java @@ -90,10 +90,7 @@ public class MethodCallHandlerTest { @Before public void setUp() { MockitoAnnotations.openMocks(this); - factory = - (@NonNull Context context, - @NonNull MethodChannel channel, - boolean enablePendingPurchases) -> mockBillingClient; + factory = (@NonNull Context context, @NonNull MethodChannel channel) -> mockBillingClient; methodChannelHandler = new MethodCallHandlerImpl(activity, context, mockMethodChannel, factory); when(mockActivityPluginBinding.getActivity()).thenReturn(activity); } @@ -153,7 +150,6 @@ public void startConnection() { public void startConnection_multipleCalls() { Map arguments = new HashMap<>(); arguments.put("handle", 1); - arguments.put("enablePendingPurchases", true); MethodCall call = new MethodCall(START_CONNECTION, arguments); ArgumentCaptor captor = ArgumentCaptor.forClass(BillingClientStateListener.class); @@ -191,7 +187,6 @@ public void endConnection() { final int disconnectCallbackHandle = 22; Map arguments = new HashMap<>(); arguments.put("handle", disconnectCallbackHandle); - arguments.put("enablePendingPurchases", true); MethodCall connectCall = new MethodCall(START_CONNECTION, arguments); ArgumentCaptor captor = ArgumentCaptor.forClass(BillingClientStateListener.class); @@ -865,7 +860,6 @@ public void launchPriceChangeConfirmationFlow_withoutBillingClient_returnsUnavai private ArgumentCaptor mockStartConnection() { Map arguments = new HashMap<>(); arguments.put("handle", 1); - arguments.put("enablePendingPurchases", true); MethodCall call = new MethodCall(START_CONNECTION, arguments); ArgumentCaptor captor = ArgumentCaptor.forClass(BillingClientStateListener.class); @@ -880,7 +874,6 @@ private void establishConnectedBillingClient( if (arguments == null) { arguments = new HashMap<>(); arguments.put("handle", 1); - arguments.put("enablePendingPurchases", true); } if (result == null) { result = mock(Result.class); diff --git a/packages/in_app_purchase/in_app_purchase_android/example/integration_test/in_app_purchase_test.dart b/packages/in_app_purchase/in_app_purchase_android/example/integration_test/in_app_purchase_test.dart index 8b655306a2b5..bb0e1675090d 100644 --- a/packages/in_app_purchase/in_app_purchase_android/example/integration_test/in_app_purchase_test.dart +++ b/packages/in_app_purchase/in_app_purchase_android/example/integration_test/in_app_purchase_test.dart @@ -12,7 +12,6 @@ void main() { testWidgets('Can create InAppPurchaseAndroid instance', (WidgetTester tester) async { - InAppPurchaseAndroidPlatformAddition.enablePendingPurchases(); InAppPurchaseAndroidPlatform.registerPlatform(); final InAppPurchasePlatform androidPlatform = InAppPurchasePlatform.instance; diff --git a/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart index 126734187380..a53e9b3f2d02 100644 --- a/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart @@ -15,11 +15,6 @@ import 'consumable_store.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); - // For play billing library 2.0 on Android, it is mandatory to call - // [enablePendingPurchases](https://developer.android.com/reference/com/android/billingclient/api/BillingClient.Builder.html#enablependingpurchases) - // as part of initializing the app. - InAppPurchaseAndroidPlatformAddition.enablePendingPurchases(); - // When using the Android plugin directly it is mandatory to register // the plugin as default instance as part of initializing the app. InAppPurchaseAndroidPlatform.registerPlatform(); diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.dart index 0e7024a59aaa..29441fb1b921 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.dart @@ -53,8 +53,6 @@ typedef void PurchasesUpdatedListener(PurchasesResultWrapper purchasesResult); /// some minor changes to account for language differences. Callbacks have been /// converted to futures where appropriate. class BillingClient { - bool _enablePendingPurchases = false; - /// Creates a billing client. BillingClient(PurchasesUpdatedListener onPurchasesUpdated) { channel.setMethodCallHandler(callHandler); @@ -82,14 +80,12 @@ class BillingClient { /// Enable the [BillingClientWrapper] to handle pending purchases. /// - /// Play requires that you call this method when initializing your application. - /// It is to acknowledge your application has been updated to support pending purchases. - /// See [Support pending transactions](https://developer.android.com/google/play/billing/billing_library_overview#pending) - /// for more details. - /// - /// Failure to call this method before any other method in the [startConnection] will throw an exception. + /// **Deprecation warning:** it is no longer required to call + /// [enablePendingPurchases] when initializing your application. + @Deprecated( + 'The requirement to call `enablePendingPurchases()` has become obsolete since Google Play no longer accepts app submissions that don\'t support pending purchases.') void enablePendingPurchases() { - _enablePendingPurchases = true; + // No-op, until it is time to completely remove this method from the API. } /// Calls @@ -105,8 +101,6 @@ class BillingClient { Future startConnection( {required OnBillingServiceDisconnected onBillingServiceDisconnected}) async { - assert(_enablePendingPurchases, - 'enablePendingPurchases() must be called before calling startConnection'); List disconnectCallbacks = _callbacks[_kOnBillingServiceDisconnected] ??= []; disconnectCallbacks.add(onBillingServiceDisconnected); @@ -115,7 +109,6 @@ class BillingClient { "BillingClient#startConnection(BillingClientStateListener)", { 'handle': disconnectCallbacks.length - 1, - 'enablePendingPurchases': _enablePendingPurchases })) ?? {}); } diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform_addition.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform_addition.dart index 11b105aba96c..dd6fe37a1e88 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform_addition.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform_addition.dart @@ -14,30 +14,29 @@ class InAppPurchaseAndroidPlatformAddition extends InAppPurchasePlatformAddition { /// Creates a [InAppPurchaseAndroidPlatformAddition] which uses the supplied /// `BillingClient` to provide Android specific features. - InAppPurchaseAndroidPlatformAddition(this._billingClient) { - assert( - _enablePendingPurchase, - 'enablePendingPurchases() must be called when initializing the application and before you access the [InAppPurchase.instance].', - ); - - _billingClient.enablePendingPurchases(); - } + InAppPurchaseAndroidPlatformAddition(this._billingClient); /// Whether pending purchase is enabled. /// + /// **Deprecation warning:** it is no longer required to call + /// [enablePendingPurchases] when initializing your application. From now on + /// this is handled internally and the [enablePendingPurchase] property will + /// always return `true`. + /// + // ignore: deprecated_member_use_from_same_package /// See also [enablePendingPurchases] for more on pending purchases. - static bool get enablePendingPurchase => _enablePendingPurchase; - static bool _enablePendingPurchase = false; + @Deprecated( + 'The requirement to call `enablePendingPurchases()` has become obsolete since Google Play no longer accepts app submissions that don\'t support pending purchases.') + static bool get enablePendingPurchase => true; /// Enable the [InAppPurchaseConnection] to handle pending purchases. /// - /// This method is required to be called when initialize the application. - /// It is to acknowledge your application has been updated to support pending purchases. - /// See [Support pending transactions](https://developer.android.com/google/play/billing/billing_library_overview#pending) - /// for more details. - /// Failure to call this method before access [instance] will throw an exception. + /// **Deprecation warning:** it is no longer required to call + /// [enablePendingPurchases] when initializing your application. + @Deprecated( + 'The requirement to call `enablePendingPurchases()` has become obsolete since Google Play no longer accepts app submissions that don\'t support pending purchases.') static void enablePendingPurchases() { - _enablePendingPurchase = true; + // No-op, until it is time to completely remove this method from the API. } final BillingClient _billingClient; diff --git a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml index 7d5c610aca14..63bfa134377f 100644 --- a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_android description: An implementation for the Android platform of the Flutter `in_app_purchase` plugin. This uses the Android BillingClient APIs. repository: https://github.com/flutter/plugins/tree/master/packages/in_app_purchase/in_app_purchase_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.2.0 +version: 0.2.1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/billing_client_wrapper_test.dart b/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/billing_client_wrapper_test.dart index 1a2a0e4712a7..ba6e755e8a4f 100644 --- a/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/billing_client_wrapper_test.dart +++ b/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/billing_client_wrapper_test.dart @@ -22,7 +22,6 @@ void main() { setUp(() { billingClient = BillingClient((PurchasesResultWrapper _) {}); - billingClient.enablePendingPurchases(); stubPlatform.reset(); }); @@ -90,10 +89,7 @@ void main() { ); await billingClient.startConnection(onBillingServiceDisconnected: () {}); final MethodCall call = stubPlatform.previousCallMatching(methodName); - expect( - call.arguments, - equals( - {'handle': 0, 'enablePendingPurchases': true})); + expect(call.arguments, equals({'handle': 0})); }); test('handles method channel returning null', () async { diff --git a/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_addition_test.dart b/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_addition_test.dart index ecefc4d37c84..112300d54d7e 100644 --- a/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_addition_test.dart +++ b/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_addition_test.dart @@ -29,8 +29,6 @@ void main() { setUp(() { widgets.WidgetsFlutterBinding.ensureInitialized(); - InAppPurchaseAndroidPlatformAddition.enablePendingPurchases(); - const String debugMessage = 'dummy message'; final BillingResponse responseCode = BillingResponse.ok; final BillingResultWrapper expectedBillingResult = BillingResultWrapper( diff --git a/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_test.dart b/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_test.dart index 12cca69793ac..9f00bc79f52a 100644 --- a/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_test.dart +++ b/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_test.dart @@ -10,7 +10,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:in_app_purchase_android/billing_client_wrappers.dart'; import 'package:in_app_purchase_android/in_app_purchase_android.dart'; import 'package:in_app_purchase_android/src/channel.dart'; -import 'package:in_app_purchase_android/src/in_app_purchase_android_platform_addition.dart'; import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; import 'billing_client_wrappers/purchase_wrapper_test.dart'; @@ -42,7 +41,6 @@ void main() { value: buildBillingResultMap(expectedBillingResult)); stubPlatform.addResponse(name: endConnectionCall, value: null); - InAppPurchaseAndroidPlatformAddition.enablePendingPurchases(); InAppPurchaseAndroidPlatform.registerPlatform(); iapAndroidPlatform = InAppPurchasePlatform.instance as InAppPurchaseAndroidPlatform; From 2ab3044cfbb4ee2f3dcbc5741628da8f4e0eb437 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 30 Nov 2021 12:01:51 -0500 Subject: [PATCH 024/600] [url_launcher] Switch to new analysis options (#4551) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes the legacy analysis options for all `url_launcher` packages, and fixes all resulting violations. Most fixes are automatic `dart fix` changes. No version change: Most of these changes are to code that don't affect clients, or are clearly no-ops (e.g., adding a type specifier that was already strongly inferred). Only the packages where there is some potential for change—although none is actually expected—have version bumps. Part of https://github.com/flutter/flutter/issues/76229 --- packages/url_launcher/analysis_options.yaml | 1 - .../url_launcher/url_launcher/CHANGELOG.md | 4 +++ .../url_launcher/example/lib/main.dart | 10 +++---- .../url_launcher/example/pubspec.yaml | 5 ++-- .../url_launcher/url_launcher/lib/link.dart | 3 +- .../url_launcher/lib/src/link.dart | 30 ++++++++++++------- .../url_launcher/url_launcher/pubspec.yaml | 3 +- .../url_launcher/test/link_test.dart | 6 ++-- .../url_launcher/test/url_launcher_test.dart | 8 ++--- .../url_launcher_android/CHANGELOG.md | 4 +++ .../integration_test/url_launcher_test.dart | 2 +- .../example/lib/main.dart | 14 ++++----- .../url_launcher_android/example/pubspec.yaml | 5 ++-- .../url_launcher_android/pubspec.yaml | 1 - .../url_launcher_ios/CHANGELOG.md | 4 +++ .../integration_test/url_launcher_test.dart | 2 +- .../url_launcher_ios/example/lib/main.dart | 16 +++++----- .../url_launcher_ios/example/pubspec.yaml | 5 ++-- .../url_launcher_ios/pubspec.yaml | 1 - .../url_launcher_linux/CHANGELOG.md | 4 +++ .../integration_test/url_launcher_test.dart | 2 +- .../url_launcher_linux/example/lib/main.dart | 4 +-- .../url_launcher_linux/example/pubspec.yaml | 5 ++-- .../url_launcher_macos/CHANGELOG.md | 4 +++ .../integration_test/url_launcher_test.dart | 2 +- .../url_launcher_macos/example/lib/main.dart | 4 +-- .../url_launcher_macos/example/pubspec.yaml | 5 ++-- .../url_launcher_macos/pubspec.yaml | 3 -- .../CHANGELOG.md | 4 +++ .../lib/link.dart | 9 +++--- .../lib/method_channel_url_launcher.dart | 4 +-- .../pubspec.yaml | 1 - .../test/link_test.dart | 30 +++++++++---------- .../method_channel_url_launcher_test.dart | 7 ++--- .../url_launcher_web/CHANGELOG.md | 4 +++ .../integration_test/link_widget_test.dart | 19 ++++++------ .../url_launcher_web_test.dart | 7 +++-- .../url_launcher_web/example/lib/main.dart | 2 +- .../url_launcher_web/example/pubspec.yaml | 6 ++-- .../url_launcher_web/lib/src/link.dart | 13 +++++--- .../lib/src/shims/dart_ui.dart | 4 +-- .../lib/src/shims/dart_ui_fake.dart | 11 +++++-- .../third_party/platform_detect/browser.dart | 4 +-- .../lib/url_launcher_web.dart | 22 +++++++------- .../url_launcher_web/pubspec.yaml | 3 +- .../url_launcher_windows/CHANGELOG.md | 3 +- .../integration_test/url_launcher_test.dart | 2 +- .../example/lib/main.dart | 4 +-- .../url_launcher_windows/example/pubspec.yaml | 5 ++-- script/configs/custom_analysis.yaml | 1 - 50 files changed, 178 insertions(+), 144 deletions(-) delete mode 100644 packages/url_launcher/analysis_options.yaml diff --git a/packages/url_launcher/analysis_options.yaml b/packages/url_launcher/analysis_options.yaml deleted file mode 100644 index cda4f6e153e6..000000000000 --- a/packages/url_launcher/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../analysis_options_legacy.yaml diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index 46b71f6010b5..2290fd049c7d 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.0.17 + +* Updates code for new analysis options. + ## 6.0.16 * Moves Android and iOS implementations to federated packages. diff --git a/packages/url_launcher/url_launcher/example/lib/main.dart b/packages/url_launcher/url_launcher/example/lib/main.dart index 7e4d18a7cb13..a5e38ceecc84 100644 --- a/packages/url_launcher/url_launcher/example/lib/main.dart +++ b/packages/url_launcher/url_launcher/example/lib/main.dart @@ -22,13 +22,13 @@ class MyApp extends StatelessWidget { theme: ThemeData( primarySwatch: Colors.blue, ), - home: MyHomePage(title: 'URL Launcher'), + home: const MyHomePage(title: 'URL Launcher'), ); } } class MyHomePage extends StatefulWidget { - MyHomePage({Key? key, required this.title}) : super(key: key); + const MyHomePage({Key? key, required this.title}) : super(key: key); final String title; @override @@ -213,11 +213,11 @@ class _MyHomePageState extends State { uri: Uri.parse( 'https://pub.dev/documentation/url_launcher/latest/link/link-library.html'), target: LinkTarget.blank, - builder: (ctx, openLink) { + builder: (BuildContext ctx, FollowLink? openLink) { return TextButton.icon( onPressed: openLink, - label: Text('Link Widget documentation'), - icon: Icon(Icons.read_more), + label: const Text('Link Widget documentation'), + icon: const Icon(Icons.read_more), ); }, ), diff --git a/packages/url_launcher/url_launcher/example/pubspec.yaml b/packages/url_launcher/url_launcher/example/pubspec.yaml index db1d548695dc..3b2bba9833a3 100644 --- a/packages/url_launcher/url_launcher/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher/example/pubspec.yaml @@ -18,11 +18,10 @@ dependencies: path: ../ dev_dependencies: - integration_test: - sdk: flutter flutter_driver: sdk: flutter - pedantic: ^1.10.0 + integration_test: + sdk: flutter mockito: ^5.0.0 plugin_platform_interface: ^2.0.0 diff --git a/packages/url_launcher/url_launcher/lib/link.dart b/packages/url_launcher/url_launcher/lib/link.dart index 12a213b62761..00947cd4e22f 100644 --- a/packages/url_launcher/url_launcher/lib/link.dart +++ b/packages/url_launcher/url_launcher/lib/link.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -export 'src/link.dart' show Link; export 'package:url_launcher_platform_interface/link.dart' show FollowLink, LinkTarget, LinkWidgetBuilder; + +export 'src/link.dart' show Link; diff --git a/packages/url_launcher/url_launcher/lib/src/link.dart b/packages/url_launcher/url_launcher/lib/src/link.dart index 72d6e247c970..67aa24430ca7 100644 --- a/packages/url_launcher/url_launcher/lib/src/link.dart +++ b/packages/url_launcher/url_launcher/lib/src/link.dart @@ -43,27 +43,31 @@ Future Function(Object?, String) pushRouteToFrameworkFunction = /// ); /// ``` class Link extends StatelessWidget implements LinkInfo { + /// Creates a widget that renders a real link on the web, and uses WebViews in + /// native platforms to open links. + const Link({ + Key? key, + required this.uri, + this.target = LinkTarget.defaultTarget, + required this.builder, + }) : super(key: key); + /// Called at build time to construct the widget tree under the link. + @override final LinkWidgetBuilder builder; /// The destination that this link leads to. + @override final Uri? uri; /// The target indicating where to open the link. + @override final LinkTarget target; /// Whether the link is disabled or not. + @override bool get isDisabled => uri == null; - /// Creates a widget that renders a real link on the web, and uses WebViews in - /// native platforms to open links. - Link({ - Key? key, - required this.uri, - this.target = LinkTarget.defaultTarget, - required this.builder, - }) : super(key: key); - LinkDelegate get _effectiveDelegate { return UrlLauncherPlatform.instance.linkDelegate ?? DefaultLinkDelegate.create; @@ -94,8 +98,12 @@ class DefaultLinkDelegate extends StatelessWidget { final LinkInfo link; bool get _useWebView { - if (link.target == LinkTarget.self) return true; - if (link.target == LinkTarget.blank) return false; + if (link.target == LinkTarget.self) { + return true; + } + if (link.target == LinkTarget.blank) { + return false; + } return false; } diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index 5844031ae03e..4ccf48b9db22 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes. repository: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.0.16 +version: 6.0.17 environment: sdk: ">=2.14.0 <3.0.0" @@ -41,6 +41,5 @@ dev_dependencies: flutter_test: sdk: flutter mockito: ^5.0.0 - pedantic: ^1.10.0 plugin_platform_interface: ^2.0.0 test: ^1.16.3 diff --git a/packages/url_launcher/url_launcher/test/link_test.dart b/packages/url_launcher/url_launcher/test/link_test.dart index 819f6a370e30..f7a98a0bf2f0 100644 --- a/packages/url_launcher/url_launcher/test/link_test.dart +++ b/packages/url_launcher/url_launcher/test/link_test.dart @@ -3,8 +3,8 @@ // found in the LICENSE file. import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; import 'package:url_launcher/link.dart'; import 'package:url_launcher/src/link.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; @@ -118,11 +118,11 @@ void main() { )); bool frameworkCalled = false; - Future Function(Object?, String) originalPushFunction = + final Future Function(Object?, String) originalPushFunction = pushRouteToFrameworkFunction; pushRouteToFrameworkFunction = (Object? _, String __) { frameworkCalled = true; - return Future.value(ByteData(0)); + return Future.value(ByteData(0)); }; await followLink!(); diff --git a/packages/url_launcher/url_launcher/test/url_launcher_test.dart b/packages/url_launcher/url_launcher/test/url_launcher_test.dart index a038746d6bec..4e980cb37253 100644 --- a/packages/url_launcher/url_launcher/test/url_launcher_test.dart +++ b/packages/url_launcher/url_launcher/test/url_launcher_test.dart @@ -5,11 +5,11 @@ import 'dart:async'; import 'dart:ui'; -import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart' show PlatformException; +import 'package:flutter_test/flutter_test.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; -import 'package:flutter/services.dart' show PlatformException; import 'mock_url_launcher_platform.dart'; @@ -239,7 +239,7 @@ void main() { ..setResponse(true); final TestWidgetsFlutterBinding binding = - _anonymize(TestWidgetsFlutterBinding.ensureInitialized()) + _anonymize(TestWidgetsFlutterBinding.ensureInitialized())! as TestWidgetsFlutterBinding; debugDefaultTargetPlatformOverride = TargetPlatform.iOS; binding.renderView.automaticSystemUiAdjustment = true; @@ -268,7 +268,7 @@ void main() { ..setResponse(true); final TestWidgetsFlutterBinding binding = - _anonymize(TestWidgetsFlutterBinding.ensureInitialized()) + _anonymize(TestWidgetsFlutterBinding.ensureInitialized())! as TestWidgetsFlutterBinding; debugDefaultTargetPlatformOverride = TargetPlatform.android; expect(binding.renderView.automaticSystemUiAdjustment, true); diff --git a/packages/url_launcher/url_launcher_android/CHANGELOG.md b/packages/url_launcher/url_launcher_android/CHANGELOG.md index 8b336a0d9a15..1b60e86048d5 100644 --- a/packages/url_launcher/url_launcher_android/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates code for new analysis options. + ## 6.0.13 * Splits from `shared_preferences` as a federated implementation. diff --git a/packages/url_launcher/url_launcher_android/example/integration_test/url_launcher_test.dart b/packages/url_launcher/url_launcher_android/example/integration_test/url_launcher_test.dart index ac9f64c36ebc..28dc79b7af38 100644 --- a/packages/url_launcher/url_launcher_android/example/integration_test/url_launcher_test.dart +++ b/packages/url_launcher/url_launcher_android/example/integration_test/url_launcher_test.dart @@ -10,7 +10,7 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('canLaunch', (WidgetTester _) async { - UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; expect(await launcher.canLaunch('randomstring'), false); diff --git a/packages/url_launcher/url_launcher_android/example/lib/main.dart b/packages/url_launcher/url_launcher_android/example/lib/main.dart index 08992323f19c..8721c587075e 100644 --- a/packages/url_launcher/url_launcher_android/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_android/example/lib/main.dart @@ -21,13 +21,13 @@ class MyApp extends StatelessWidget { theme: ThemeData( primarySwatch: Colors.blue, ), - home: MyHomePage(title: 'URL Launcher'), + home: const MyHomePage(title: 'URL Launcher'), ); } } class MyHomePage extends StatefulWidget { - MyHomePage({Key? key, required this.title}) : super(key: key); + const MyHomePage({Key? key, required this.title}) : super(key: key); final String title; @override @@ -39,7 +39,7 @@ class _MyHomePageState extends State { String _phone = ''; Future _launchInBrowser(String url) async { - UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; if (await launcher.canLaunch(url)) { await launcher.launch( url, @@ -56,7 +56,7 @@ class _MyHomePageState extends State { } Future _launchInWebViewOrVC(String url) async { - UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; if (await launcher.canLaunch(url)) { await launcher.launch( url, @@ -73,7 +73,7 @@ class _MyHomePageState extends State { } Future _launchInWebViewWithJavaScript(String url) async { - UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; if (await launcher.canLaunch(url)) { await launcher.launch( url, @@ -90,7 +90,7 @@ class _MyHomePageState extends State { } Future _launchInWebViewWithDomStorage(String url) async { - UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; if (await launcher.canLaunch(url)) { await launcher.launch( url, @@ -115,7 +115,7 @@ class _MyHomePageState extends State { } Future _makePhoneCall(String url) async { - UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; if (await launcher.canLaunch(url)) { await launcher.launch( url, diff --git a/packages/url_launcher/url_launcher_android/example/pubspec.yaml b/packages/url_launcher/url_launcher_android/example/pubspec.yaml index db22fd31b368..9af7b2876da9 100644 --- a/packages/url_launcher/url_launcher_android/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_android/example/pubspec.yaml @@ -18,11 +18,10 @@ dependencies: path: ../ dev_dependencies: - integration_test: - sdk: flutter flutter_driver: sdk: flutter - pedantic: ^1.10.0 + integration_test: + sdk: flutter mockito: ^5.0.0 plugin_platform_interface: ^2.0.0 diff --git a/packages/url_launcher/url_launcher_android/pubspec.yaml b/packages/url_launcher/url_launcher_android/pubspec.yaml index 0abed02d923e..959e6d07c715 100644 --- a/packages/url_launcher/url_launcher_android/pubspec.yaml +++ b/packages/url_launcher/url_launcher_android/pubspec.yaml @@ -26,6 +26,5 @@ dev_dependencies: flutter_test: sdk: flutter mockito: ^5.0.0 - pedantic: ^1.10.0 plugin_platform_interface: ^2.0.0 test: ^1.16.3 diff --git a/packages/url_launcher/url_launcher_ios/CHANGELOG.md b/packages/url_launcher/url_launcher_ios/CHANGELOG.md index 931d85e0af9f..0f9190da3e0a 100644 --- a/packages/url_launcher/url_launcher_ios/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates code for new analysis options. + ## 6.0.13 * Splits from `url_launcher` as a federated implementation. diff --git a/packages/url_launcher/url_launcher_ios/example/integration_test/url_launcher_test.dart b/packages/url_launcher/url_launcher_ios/example/integration_test/url_launcher_test.dart index f7cec29f9f22..b8f19053f709 100644 --- a/packages/url_launcher/url_launcher_ios/example/integration_test/url_launcher_test.dart +++ b/packages/url_launcher/url_launcher_ios/example/integration_test/url_launcher_test.dart @@ -10,7 +10,7 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('canLaunch', (WidgetTester _) async { - UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; expect(await launcher.canLaunch('randomstring'), false); diff --git a/packages/url_launcher/url_launcher_ios/example/lib/main.dart b/packages/url_launcher/url_launcher_ios/example/lib/main.dart index a16a4dd70890..2f73622ebb41 100644 --- a/packages/url_launcher/url_launcher_ios/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_ios/example/lib/main.dart @@ -21,13 +21,13 @@ class MyApp extends StatelessWidget { theme: ThemeData( primarySwatch: Colors.blue, ), - home: MyHomePage(title: 'URL Launcher'), + home: const MyHomePage(title: 'URL Launcher'), ); } } class MyHomePage extends StatefulWidget { - MyHomePage({Key? key, required this.title}) : super(key: key); + const MyHomePage({Key? key, required this.title}) : super(key: key); final String title; @override @@ -39,7 +39,7 @@ class _MyHomePageState extends State { String _phone = ''; Future _launchInBrowser(String url) async { - UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; if (await launcher.canLaunch(url)) { await launcher.launch( url, @@ -56,7 +56,7 @@ class _MyHomePageState extends State { } Future _launchInWebViewOrVC(String url) async { - UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; if (await launcher.canLaunch(url)) { await launcher.launch( url, @@ -73,7 +73,7 @@ class _MyHomePageState extends State { } Future _launchInWebViewWithJavaScript(String url) async { - UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; if (await launcher.canLaunch(url)) { await launcher.launch( url, @@ -90,7 +90,7 @@ class _MyHomePageState extends State { } Future _launchInWebViewWithDomStorage(String url) async { - UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; if (await launcher.canLaunch(url)) { await launcher.launch( url, @@ -107,7 +107,7 @@ class _MyHomePageState extends State { } Future _launchUniversalLinkIos(String url) async { - UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; if (await launcher.canLaunch(url)) { final bool nativeAppLaunchSucceeded = await launcher.launch( url, @@ -141,7 +141,7 @@ class _MyHomePageState extends State { } Future _makePhoneCall(String url) async { - UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; if (await launcher.canLaunch(url)) { await launcher.launch( url, diff --git a/packages/url_launcher/url_launcher_ios/example/pubspec.yaml b/packages/url_launcher/url_launcher_ios/example/pubspec.yaml index 2af807dbba6b..da4c72cd13bb 100644 --- a/packages/url_launcher/url_launcher_ios/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_ios/example/pubspec.yaml @@ -18,11 +18,10 @@ dependencies: path: ../ dev_dependencies: - integration_test: - sdk: flutter flutter_driver: sdk: flutter - pedantic: ^1.10.0 + integration_test: + sdk: flutter mockito: ^5.0.0 plugin_platform_interface: ^2.0.0 diff --git a/packages/url_launcher/url_launcher_ios/pubspec.yaml b/packages/url_launcher/url_launcher_ios/pubspec.yaml index dfd4f797f4ac..1e1ee87983b2 100644 --- a/packages/url_launcher/url_launcher_ios/pubspec.yaml +++ b/packages/url_launcher/url_launcher_ios/pubspec.yaml @@ -25,6 +25,5 @@ dev_dependencies: flutter_test: sdk: flutter mockito: ^5.0.0 - pedantic: ^1.10.0 plugin_platform_interface: ^2.0.0 test: ^1.16.3 diff --git a/packages/url_launcher/url_launcher_linux/CHANGELOG.md b/packages/url_launcher/url_launcher_linux/CHANGELOG.md index 147d0f312c7e..f47d4fde4269 100644 --- a/packages/url_launcher/url_launcher_linux/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_linux/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates code for new analysis options. + ## 2.0.2 * Replaced reference to `shared_preferences` plugin with the `url_launcher` in the README. diff --git a/packages/url_launcher/url_launcher_linux/example/integration_test/url_launcher_test.dart b/packages/url_launcher/url_launcher_linux/example/integration_test/url_launcher_test.dart index ae9a9148f9d7..c9d0d8c9c096 100644 --- a/packages/url_launcher/url_launcher_linux/example/integration_test/url_launcher_test.dart +++ b/packages/url_launcher/url_launcher_linux/example/integration_test/url_launcher_test.dart @@ -10,7 +10,7 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('canLaunch', (WidgetTester _) async { - UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; expect(await launcher.canLaunch('randomstring'), false); diff --git a/packages/url_launcher/url_launcher_linux/example/lib/main.dart b/packages/url_launcher/url_launcher_linux/example/lib/main.dart index 86e06f3fafed..a9a5d22dadd5 100644 --- a/packages/url_launcher/url_launcher_linux/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_linux/example/lib/main.dart @@ -20,13 +20,13 @@ class MyApp extends StatelessWidget { theme: ThemeData( primarySwatch: Colors.blue, ), - home: MyHomePage(title: 'URL Launcher'), + home: const MyHomePage(title: 'URL Launcher'), ); } } class MyHomePage extends StatefulWidget { - MyHomePage({Key? key, required this.title}) : super(key: key); + const MyHomePage({Key? key, required this.title}) : super(key: key); final String title; @override diff --git a/packages/url_launcher/url_launcher_linux/example/pubspec.yaml b/packages/url_launcher/url_launcher_linux/example/pubspec.yaml index b0ef2e6eddbf..5e6c3fc5384f 100644 --- a/packages/url_launcher/url_launcher_linux/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_linux/example/pubspec.yaml @@ -19,11 +19,10 @@ dependencies: url_launcher_platform_interface: ^2.0.0 dev_dependencies: - integration_test: - sdk: flutter flutter_driver: sdk: flutter - pedantic: ^1.10.0 + integration_test: + sdk: flutter flutter: uses-material-design: true diff --git a/packages/url_launcher/url_launcher_macos/CHANGELOG.md b/packages/url_launcher/url_launcher_macos/CHANGELOG.md index 96d2fd49c7e7..7b7677a1ec47 100644 --- a/packages/url_launcher/url_launcher_macos/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_macos/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates code for new analysis options. + ## 2.0.2 * Replaced reference to `shared_preferences` plugin with the `url_launcher` in the README. diff --git a/packages/url_launcher/url_launcher_macos/example/integration_test/url_launcher_test.dart b/packages/url_launcher/url_launcher_macos/example/integration_test/url_launcher_test.dart index 897b22f89392..87bc3d21df07 100644 --- a/packages/url_launcher/url_launcher_macos/example/integration_test/url_launcher_test.dart +++ b/packages/url_launcher/url_launcher_macos/example/integration_test/url_launcher_test.dart @@ -10,7 +10,7 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('canLaunch', (WidgetTester _) async { - UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; expect(await launcher.canLaunch('randomstring'), false); diff --git a/packages/url_launcher/url_launcher_macos/example/lib/main.dart b/packages/url_launcher/url_launcher_macos/example/lib/main.dart index 86e06f3fafed..a9a5d22dadd5 100644 --- a/packages/url_launcher/url_launcher_macos/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_macos/example/lib/main.dart @@ -20,13 +20,13 @@ class MyApp extends StatelessWidget { theme: ThemeData( primarySwatch: Colors.blue, ), - home: MyHomePage(title: 'URL Launcher'), + home: const MyHomePage(title: 'URL Launcher'), ); } } class MyHomePage extends StatefulWidget { - MyHomePage({Key? key, required this.title}) : super(key: key); + const MyHomePage({Key? key, required this.title}) : super(key: key); final String title; @override diff --git a/packages/url_launcher/url_launcher_macos/example/pubspec.yaml b/packages/url_launcher/url_launcher_macos/example/pubspec.yaml index 6d12b75819b0..9bc3062dd08f 100644 --- a/packages/url_launcher/url_launcher_macos/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_macos/example/pubspec.yaml @@ -19,11 +19,10 @@ dependencies: url_launcher_platform_interface: ^2.0.0 dev_dependencies: - integration_test: - sdk: flutter flutter_driver: sdk: flutter - pedantic: ^1.10.0 + integration_test: + sdk: flutter flutter: uses-material-design: true diff --git a/packages/url_launcher/url_launcher_macos/pubspec.yaml b/packages/url_launcher/url_launcher_macos/pubspec.yaml index 534830000626..01f1db0439eb 100644 --- a/packages/url_launcher/url_launcher_macos/pubspec.yaml +++ b/packages/url_launcher/url_launcher_macos/pubspec.yaml @@ -19,6 +19,3 @@ flutter: dependencies: flutter: sdk: flutter - -dev_dependencies: - pedantic: ^1.8.0 diff --git a/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md b/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md index fc56473533f2..02e2d0ab10a2 100644 --- a/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates code for new analysis options. + ## 2.0.4 * Silenced warnings that may occur during build when using a very diff --git a/packages/url_launcher/url_launcher_platform_interface/lib/link.dart b/packages/url_launcher/url_launcher_platform_interface/lib/link.dart index ffff14feb8d7..da8aa1570bad 100644 --- a/packages/url_launcher/url_launcher_platform_interface/lib/link.dart +++ b/packages/url_launcher/url_launcher_platform_interface/lib/link.dart @@ -22,7 +22,7 @@ typedef LinkWidgetBuilder = Widget Function( /// Signature for a delegate function to build the [Link] widget. typedef LinkDelegate = Widget Function(LinkInfo linkWidget); -final MethodCodec _codec = const JSONMethodCodec(); +const MethodCodec _codec = JSONMethodCodec(); /// Defines where a Link URL should be open. /// @@ -42,20 +42,21 @@ class LinkTarget { /// /// iOS, on the other hand, defaults to [self] for web URLs, and [blank] for /// non-web URLs. - static const defaultTarget = LinkTarget._(debugLabel: 'defaultTarget'); + static const LinkTarget defaultTarget = + LinkTarget._(debugLabel: 'defaultTarget'); /// On the web, this opens the link in the same tab where the flutter app is /// running. /// /// On Android and iOS, this opens the link in a webview within the app. - static const self = LinkTarget._(debugLabel: 'self'); + static const LinkTarget self = LinkTarget._(debugLabel: 'self'); /// On the web, this opens the link in a new tab or window (depending on the /// browser and user configuration). /// /// On Android and iOS, this opens the link in the browser or the relevant /// app. - static const blank = LinkTarget._(debugLabel: 'blank'); + static const LinkTarget blank = LinkTarget._(debugLabel: 'blank'); } /// Encapsulates all the information necessary to build a Link widget. diff --git a/packages/url_launcher/url_launcher_platform_interface/lib/method_channel_url_launcher.dart b/packages/url_launcher/url_launcher_platform_interface/lib/method_channel_url_launcher.dart index e75e283eeca7..df738046b96b 100644 --- a/packages/url_launcher/url_launcher_platform_interface/lib/method_channel_url_launcher.dart +++ b/packages/url_launcher/url_launcher_platform_interface/lib/method_channel_url_launcher.dart @@ -21,7 +21,7 @@ class MethodChannelUrlLauncher extends UrlLauncherPlatform { return _channel.invokeMethod( 'canLaunch', {'url': url}, - ).then((value) => value ?? false); + ).then((bool? value) => value ?? false); } @override @@ -51,6 +51,6 @@ class MethodChannelUrlLauncher extends UrlLauncherPlatform { 'universalLinksOnly': universalLinksOnly, 'headers': headers, }, - ).then((value) => value ?? false); + ).then((bool? value) => value ?? false); } } diff --git a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml index 074e95b08c2c..de1e71fbad08 100644 --- a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml +++ b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml @@ -19,4 +19,3 @@ dev_dependencies: flutter_test: sdk: flutter mockito: ^5.0.0 - pedantic: ^1.10.0 diff --git a/packages/url_launcher/url_launcher_platform_interface/test/link_test.dart b/packages/url_launcher/url_launcher_platform_interface/test/link_test.dart index 75a14b2e11a6..a6b316dacec3 100644 --- a/packages/url_launcher/url_launcher_platform_interface/test/link_test.dart +++ b/packages/url_launcher/url_launcher_platform_interface/test/link_test.dart @@ -13,20 +13,20 @@ import 'package:url_launcher_platform_interface/link.dart'; void main() { testWidgets('Link with Navigator', (WidgetTester tester) async { await tester.pumpWidget(MaterialApp( - home: Placeholder(key: Key('home')), + home: const Placeholder(key: Key('home')), routes: { - '/a': (BuildContext context) => Placeholder(key: Key('a')), + '/a': (BuildContext context) => const Placeholder(key: Key('a')), }, )); - expect(find.byKey(Key('home')), findsOneWidget); - expect(find.byKey(Key('a')), findsNothing); + expect(find.byKey(const Key('home')), findsOneWidget); + expect(find.byKey(const Key('a')), findsNothing); await tester.runAsync(() => pushRouteNameToFramework(null, '/a')); // start animation await tester.pump(); // skip past animation (5s is arbitrary, just needs to be long enough) await tester.pump(const Duration(seconds: 5)); - expect(find.byKey(Key('a')), findsOneWidget); - expect(find.byKey(Key('home')), findsNothing); + expect(find.byKey(const Key('a')), findsOneWidget); + expect(find.byKey(const Key('home')), findsNothing); }); testWidgets('Link with Navigator', (WidgetTester tester) async { @@ -34,15 +34,15 @@ void main() { routeInformationParser: _RouteInformationParser(), routerDelegate: _RouteDelegate(), )); - expect(find.byKey(Key('/')), findsOneWidget); - expect(find.byKey(Key('/a')), findsNothing); + expect(find.byKey(const Key('/')), findsOneWidget); + expect(find.byKey(const Key('/a')), findsNothing); await tester.runAsync(() => pushRouteNameToFramework(null, '/a')); // start animation await tester.pump(); // skip past animation (5s is arbitrary, just needs to be long enough) await tester.pump(const Duration(seconds: 5)); - expect(find.byKey(Key('/a')), findsOneWidget); - expect(find.byKey(Key('/')), findsNothing); + expect(find.byKey(const Key('/a')), findsOneWidget); + expect(find.byKey(const Key('/')), findsNothing); }); } @@ -50,7 +50,7 @@ class _RouteInformationParser extends RouteInformationParser { @override Future parseRouteInformation( RouteInformation routeInformation) { - return SynchronousFuture(routeInformation); + return SynchronousFuture(routeInformation); } @override @@ -66,22 +66,22 @@ class _RouteDelegate extends RouterDelegate @override Future setNewRoutePath(RouteInformation configuration) { _history.add(configuration); - return SynchronousFuture(null); + return SynchronousFuture(null); } @override Future popRoute() { if (_history.isEmpty) { - return SynchronousFuture(false); + return SynchronousFuture(false); } _history.removeLast(); - return SynchronousFuture(true); + return SynchronousFuture(true); } @override Widget build(BuildContext context) { if (_history.isEmpty) { - return Placeholder(key: Key('empty')); + return const Placeholder(key: Key('empty')); } return Placeholder(key: Key('${_history.last.location}')); } diff --git a/packages/url_launcher/url_launcher_platform_interface/test/method_channel_url_launcher_test.dart b/packages/url_launcher/url_launcher_platform_interface/test/method_channel_url_launcher_test.dart index 23d9a4534f7b..e44e80bab02c 100644 --- a/packages/url_launcher/url_launcher_platform_interface/test/method_channel_url_launcher_test.dart +++ b/packages/url_launcher/url_launcher_platform_interface/test/method_channel_url_launcher_test.dart @@ -2,11 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:mockito/mockito.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - import 'package:url_launcher_platform_interface/link.dart'; import 'package:url_launcher_platform_interface/method_channel_url_launcher.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; @@ -69,7 +68,7 @@ void main() { }); test('canLaunch should return false if platform returns null', () async { - final canLaunch = await launcher.canLaunch('http://example.com/'); + final bool canLaunch = await launcher.canLaunch('http://example.com/'); expect(canLaunch, false); }); @@ -283,7 +282,7 @@ void main() { }); test('launch should return false if platform returns null', () async { - final launched = await launcher.launch( + final bool launched = await launcher.launch( 'http://example.com/', useSafariVC: true, useWebView: false, diff --git a/packages/url_launcher/url_launcher_web/CHANGELOG.md b/packages/url_launcher/url_launcher_web/CHANGELOG.md index f5338e62a775..5790e116c355 100644 --- a/packages/url_launcher/url_launcher_web/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.5 + +* Updates code for new analysis options. + ## 2.0.4 * Add `implements` to pubspec. diff --git a/packages/url_launcher/url_launcher_web/example/integration_test/link_widget_test.dart b/packages/url_launcher/url_launcher_web/example/integration_test/link_widget_test.dart index 0487aca1c2dd..3e2a025bbbb6 100644 --- a/packages/url_launcher/url_launcher_web/example/integration_test/link_widget_test.dart +++ b/packages/url_launcher/url_launcher_web/example/integration_test/link_widget_test.dart @@ -4,11 +4,12 @@ import 'dart:html' as html; import 'dart:js_util'; + import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; import 'package:url_launcher_platform_interface/link.dart'; import 'package:url_launcher_web/src/link.dart'; -import 'package:integration_test/integration_test.dart'; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); @@ -59,14 +60,14 @@ void main() { textDirection: TextDirection.ltr, child: Center( child: ConstrainedBox( - constraints: BoxConstraints.tight(Size(100.0, 100.0)), + constraints: BoxConstraints.tight(const Size(100.0, 100.0)), child: WebLinkDelegate(TestLinkInfo( uri: uri, target: LinkTarget.blank, builder: (BuildContext context, FollowLink? followLink) { return Container( key: containerKey, - child: SizedBox(width: 50.0, height: 50.0), + child: const SizedBox(width: 50.0, height: 50.0), ); }, )), @@ -128,6 +129,12 @@ html.Element _findSingleAnchor() { } class TestLinkInfo extends LinkInfo { + TestLinkInfo({ + required this.uri, + required this.target, + required this.builder, + }); + @override final LinkWidgetBuilder builder; @@ -139,10 +146,4 @@ class TestLinkInfo extends LinkInfo { @override bool get isDisabled => uri == null; - - TestLinkInfo({ - required this.uri, - required this.target, - required this.builder, - }); } diff --git a/packages/url_launcher/url_launcher_web/example/integration_test/url_launcher_web_test.dart b/packages/url_launcher/url_launcher_web/example/integration_test/url_launcher_web_test.dart index 0b53a1ffb1dd..10f5e0b40ffc 100644 --- a/packages/url_launcher/url_launcher_web/example/integration_test/url_launcher_web_test.dart +++ b/packages/url_launcher/url_launcher_web/example/integration_test/url_launcher_web_test.dart @@ -3,15 +3,16 @@ // found in the LICENSE file. import 'dart:html' as html; + import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; import 'package:mockito/annotations.dart'; -import 'package:url_launcher_web/url_launcher_web.dart'; import 'package:mockito/mockito.dart'; -import 'package:integration_test/integration_test.dart'; +import 'package:url_launcher_web/url_launcher_web.dart'; import 'url_launcher_web_test.mocks.dart'; -@GenerateMocks([html.Window, html.Navigator]) +@GenerateMocks([html.Window, html.Navigator]) void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); diff --git a/packages/url_launcher/url_launcher_web/example/lib/main.dart b/packages/url_launcher/url_launcher_web/example/lib/main.dart index e1a38dcdcd46..341913a18490 100644 --- a/packages/url_launcher/url_launcher_web/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_web/example/lib/main.dart @@ -17,7 +17,7 @@ class MyApp extends StatefulWidget { class _MyAppState extends State { @override Widget build(BuildContext context) { - return Directionality( + return const Directionality( textDirection: TextDirection.ltr, child: Text('Testing... Look at the console output for results!'), ); diff --git a/packages/url_launcher/url_launcher_web/example/pubspec.yaml b/packages/url_launcher/url_launcher_web/example/pubspec.yaml index 872bc8873b5c..bface463cfe2 100644 --- a/packages/url_launcher/url_launcher_web/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_web/example/pubspec.yaml @@ -11,12 +11,12 @@ dependencies: dev_dependencies: build_runner: ^2.1.1 - mockito: ^5.0.0 - url_launcher_web: - path: ../ flutter_driver: sdk: flutter flutter_test: sdk: flutter integration_test: sdk: flutter + mockito: ^5.0.0 + url_launcher_web: + path: ../ diff --git a/packages/url_launcher/url_launcher_web/lib/src/link.dart b/packages/url_launcher/url_launcher_web/lib/src/link.dart index 3c556b3950b0..d3fa4a84164e 100644 --- a/packages/url_launcher/url_launcher_web/lib/src/link.dart +++ b/packages/url_launcher/url_launcher_web/lib/src/link.dart @@ -85,8 +85,8 @@ class WebLinkDelegateState extends State { (BuildContext context, PlatformViewController controller) { return PlatformViewSurface( controller: controller, - gestureRecognizers: - Set>(), + gestureRecognizers: const < + Factory>{}, hitTestBehavior: PlatformViewHitTestBehavior.transparent, ); }, @@ -123,7 +123,8 @@ class LinkViewController extends PlatformViewController { return controller; } - static Map _instances = {}; + static final Map _instances = + {}; static html.Element _viewFactory(int viewId) { return _instances[viewId]!._element; @@ -131,7 +132,7 @@ class LinkViewController extends PlatformViewController { static int? _hitTestedViewId; - static late StreamSubscription _clickSubscription; + static late StreamSubscription _clickSubscription; static void _onGlobalClick(html.MouseEvent event) { final int? viewId = getViewIdFromTarget(event); @@ -271,6 +272,10 @@ class LinkViewController extends PlatformViewController { int? getViewIdFromTarget(html.Event event) { final html.Element? linkElement = getLinkElementFromTarget(event); if (linkElement != null) { + // TODO(stuartmorgan): Remove this ignore (and change to getProperty) + // once the templated version is available on stable. On master (2.8) this + // is already not necessary. + // ignore: return_of_invalid_type return getProperty(linkElement, linkViewIdProperty); } return null; diff --git a/packages/url_launcher/url_launcher_web/lib/src/shims/dart_ui.dart b/packages/url_launcher/url_launcher_web/lib/src/shims/dart_ui.dart index 5eacec5fe867..0f6cd89dd288 100644 --- a/packages/url_launcher/url_launcher_web/lib/src/shims/dart_ui.dart +++ b/packages/url_launcher/url_launcher_web/lib/src/shims/dart_ui.dart @@ -5,6 +5,6 @@ /// This file shims dart:ui in web-only scenarios, getting rid of the need to /// suppress analyzer warnings. -// TODO(flutter/flutter#55000) Remove this file once web-only dart:ui APIs -// are exposed from a dedicated place. +// TODO(ditman): Remove this file once web-only dart:ui APIs +// are exposed from a dedicated place, flutter/flutter#55000. export 'dart_ui_fake.dart' if (dart.library.html) 'dart_ui_real.dart'; diff --git a/packages/url_launcher/url_launcher_web/lib/src/shims/dart_ui_fake.dart b/packages/url_launcher/url_launcher_web/lib/src/shims/dart_ui_fake.dart index f2862af8b704..8757ca22be17 100644 --- a/packages/url_launcher/url_launcher_web/lib/src/shims/dart_ui_fake.dart +++ b/packages/url_launcher/url_launcher_web/lib/src/shims/dart_ui_fake.dart @@ -7,13 +7,18 @@ import 'dart:html' as html; // Fake interface for the logic that this package needs from (web-only) dart:ui. // This is conditionally exported so the analyzer sees these methods as available. +// ignore_for_file: avoid_classes_with_only_static_members +// ignore_for_file: camel_case_types + /// Shim for web_ui engine.PlatformViewRegistry /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L62 class platformViewRegistry { /// Shim for registerViewFactory /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L72 - static registerViewFactory( - String viewTypeId, html.Element Function(int viewId) viewFactory) {} + static bool registerViewFactory( + String viewTypeId, html.Element Function(int viewId) viewFactory) { + return false; + } } /// Shim for web_ui engine.AssetManager. @@ -21,7 +26,7 @@ class platformViewRegistry { class webOnlyAssetManager { /// Shim for getAssetUrl. /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/src/engine/assets.dart#L45 - static getAssetUrl(String asset) {} + static String getAssetUrl(String asset) => ''; } /// Signature of callbacks that have no arguments and return no data. diff --git a/packages/url_launcher/url_launcher_web/lib/src/third_party/platform_detect/browser.dart b/packages/url_launcher/url_launcher_web/lib/src/third_party/platform_detect/browser.dart index 9e83c391de0b..6935cb55df77 100644 --- a/packages/url_launcher/url_launcher_web/lib/src/third_party/platform_detect/browser.dart +++ b/packages/url_launcher/url_launcher_web/lib/src/third_party/platform_detect/browser.dart @@ -26,8 +26,8 @@ import 'dart:html' as html show Navigator; /// Determines if the `navigator` is Safari. bool navigatorIsSafari(html.Navigator navigator) { // An web view running in an iOS app does not have a 'Version/X.X.X' string in the appVersion - final vendor = navigator.vendor; - final appVersion = navigator.appVersion; + final String vendor = navigator.vendor; + final String appVersion = navigator.appVersion; return vendor != null && vendor.contains('Apple') && appVersion != null && diff --git a/packages/url_launcher/url_launcher_web/lib/url_launcher_web.dart b/packages/url_launcher/url_launcher_web/lib/url_launcher_web.dart index 9249837bd46b..571acaffed7b 100644 --- a/packages/url_launcher/url_launcher_web/lib/url_launcher_web.dart +++ b/packages/url_launcher/url_launcher_web/lib/url_launcher_web.dart @@ -4,7 +4,6 @@ import 'dart:async'; import 'dart:html' as html; -import 'src/shims/dart_ui.dart' as ui; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:meta/meta.dart'; @@ -12,9 +11,10 @@ import 'package:url_launcher_platform_interface/link.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; import 'src/link.dart'; +import 'src/shims/dart_ui.dart' as ui; import 'src/third_party/platform_detect/browser.dart'; -const _safariTargetTopSchemes = { +const Set _safariTargetTopSchemes = { 'mailto', 'tel', 'sms', @@ -28,21 +28,21 @@ bool _isSafariTargetTopScheme(String url) => /// /// This class implements the `package:url_launcher` functionality for the web. class UrlLauncherPlugin extends UrlLauncherPlatform { - html.Window _window; + /// A constructor that allows tests to override the window object used by the plugin. + UrlLauncherPlugin({@visibleForTesting html.Window? debugWindow}) + : _window = debugWindow ?? html.window { + _isSafari = navigatorIsSafari(_window.navigator); + } + + final html.Window _window; bool _isSafari = false; // The set of schemes that can be handled by the plugin - static final _supportedSchemes = { + static final Set _supportedSchemes = { 'http', 'https', }.union(_safariTargetTopSchemes); - /// A constructor that allows tests to override the window object used by the plugin. - UrlLauncherPlugin({@visibleForTesting html.Window? debugWindow}) - : _window = debugWindow ?? html.window { - _isSafari = navigatorIsSafari(_window.navigator); - } - /// Registers this class as the default instance of [UrlLauncherPlatform]. static void registerWith(Registrar registrar) { UrlLauncherPlatform.instance = UrlLauncherPlugin(); @@ -61,7 +61,7 @@ class UrlLauncherPlugin extends UrlLauncherPlatform { html.WindowBase openNewWindow(String url, {String? webOnlyWindowName}) { // We need to open mailto, tel and sms urls on the _top window context on safari browsers. // See https://github.com/flutter/flutter/issues/51461 for reference. - final target = webOnlyWindowName ?? + final String target = webOnlyWindowName ?? ((_isSafari && _isSafariTargetTopScheme(url)) ? '_top' : ''); return _window.open(url, target); } diff --git a/packages/url_launcher/url_launcher_web/pubspec.yaml b/packages/url_launcher/url_launcher_web/pubspec.yaml index 77e8068f1396..37d3193cb4b2 100644 --- a/packages/url_launcher/url_launcher_web/pubspec.yaml +++ b/packages/url_launcher/url_launcher_web/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_web description: Web platform implementation of url_launcher repository: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 2.0.4 +version: 2.0.5 environment: sdk: ">=2.12.0 <3.0.0" @@ -27,4 +27,3 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - pedantic: ^1.10.0 diff --git a/packages/url_launcher/url_launcher_windows/CHANGELOG.md b/packages/url_launcher/url_launcher_windows/CHANGELOG.md index d095a52341b5..e599b1f98092 100644 --- a/packages/url_launcher/url_launcher_windows/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_windows/CHANGELOG.md @@ -1,6 +1,7 @@ ## NEXT -* Added unit tests. +* Adds unit tests. +* Updates code for new analysis options. ## 2.0.2 diff --git a/packages/url_launcher/url_launcher_windows/example/integration_test/url_launcher_test.dart b/packages/url_launcher/url_launcher_windows/example/integration_test/url_launcher_test.dart index ae9a9148f9d7..c9d0d8c9c096 100644 --- a/packages/url_launcher/url_launcher_windows/example/integration_test/url_launcher_test.dart +++ b/packages/url_launcher/url_launcher_windows/example/integration_test/url_launcher_test.dart @@ -10,7 +10,7 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('canLaunch', (WidgetTester _) async { - UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; expect(await launcher.canLaunch('randomstring'), false); diff --git a/packages/url_launcher/url_launcher_windows/example/lib/main.dart b/packages/url_launcher/url_launcher_windows/example/lib/main.dart index 86e06f3fafed..a9a5d22dadd5 100644 --- a/packages/url_launcher/url_launcher_windows/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_windows/example/lib/main.dart @@ -20,13 +20,13 @@ class MyApp extends StatelessWidget { theme: ThemeData( primarySwatch: Colors.blue, ), - home: MyHomePage(title: 'URL Launcher'), + home: const MyHomePage(title: 'URL Launcher'), ); } } class MyHomePage extends StatefulWidget { - MyHomePage({Key? key, required this.title}) : super(key: key); + const MyHomePage({Key? key, required this.title}) : super(key: key); final String title; @override diff --git a/packages/url_launcher/url_launcher_windows/example/pubspec.yaml b/packages/url_launcher/url_launcher_windows/example/pubspec.yaml index 11be3e84f03b..08350fdaab65 100644 --- a/packages/url_launcher/url_launcher_windows/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_windows/example/pubspec.yaml @@ -19,11 +19,10 @@ dependencies: path: ../ dev_dependencies: - integration_test: - sdk: flutter flutter_driver: sdk: flutter - pedantic: ^1.10.0 + integration_test: + sdk: flutter flutter: uses-material-design: true diff --git a/script/configs/custom_analysis.yaml b/script/configs/custom_analysis.yaml index b3823dac1079..820a06a35e10 100644 --- a/script/configs/custom_analysis.yaml +++ b/script/configs/custom_analysis.yaml @@ -22,7 +22,6 @@ - in_app_purchase - ios_platform_images - local_auth -- url_launcher - video_player - webview_flutter/webview_flutter From eca90883b81184584cdd596abc01fbb64b820046 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Tue, 30 Nov 2021 18:44:07 +0100 Subject: [PATCH 025/600] [webview_flutter] Android implementation of `loadFile` and `loadHtmlString` methods (#4544) --- .../webview_flutter_android/CHANGELOG.md | 3 +- .../GeneratedAndroidWebView.java | 100 +++++++++ .../webviewflutter/WebViewHostApiImpl.java | 33 +++ .../plugins/webviewflutter/WebViewTest.java | 46 ++++ .../example/lib/main.dart | 59 +++++ .../example/lib/web_view.dart | 22 ++ .../example/pubspec.yaml | 3 + .../lib/src/android_webview.dart | 89 ++++++++ .../lib/src/android_webview.pigeon.dart | 64 ++++++ .../lib/src/android_webview_api_impls.dart | 34 +++ .../lib/webview_android_widget.dart | 18 ++ .../pigeons/android_webview.dart | 16 ++ .../webview_flutter_android/pubspec.yaml | 2 +- .../test/android_webview.pigeon.dart | 203 ++++++++++++------ .../test/android_webview_test.dart | 55 +++++ .../test/android_webview_test.mocks.dart | 42 +++- .../test/webview_android_widget_test.dart | 54 +++++ .../webview_android_widget_test.mocks.dart | 29 ++- 18 files changed, 794 insertions(+), 78 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 209406c4bba0..479364ff14a7 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.4.0 +* Adds support for Android's `WebView.loadData` and `WebView.loadDataWithBaseUrl` methods and implements the `loadFile` and `loadHtmlString` methods from the platform interface. * Updates to webview_flutter_platform_interface version 1.5.2. ## 2.3.1 diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java index a34274893117..beb2d7105f22 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java @@ -173,6 +173,16 @@ public interface WebViewHostApi { void dispose(Long instanceId); + void loadData(Long instanceId, String data, String mimeType, String encoding); + + void loadDataWithBaseUrl( + Long instanceId, + String baseUrl, + String data, + String mimeType, + String encoding, + String historyUrl); + void loadUrl(Long instanceId, String url, Map headers); String getUrl(Long instanceId); @@ -274,6 +284,96 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { channel.setMessageHandler(null); } } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.WebViewHostApi.loadData", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number instanceIdArg = (Number) args.get(0); + if (instanceIdArg == null) { + throw new NullPointerException("instanceIdArg unexpectedly null."); + } + String dataArg = (String) args.get(1); + if (dataArg == null) { + throw new NullPointerException("dataArg unexpectedly null."); + } + String mimeTypeArg = (String) args.get(2); + if (mimeTypeArg == null) { + throw new NullPointerException("mimeTypeArg unexpectedly null."); + } + String encodingArg = (String) args.get(3); + if (encodingArg == null) { + throw new NullPointerException("encodingArg unexpectedly null."); + } + api.loadData(instanceIdArg.longValue(), dataArg, mimeTypeArg, encodingArg); + wrapped.put("result", null); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number instanceIdArg = (Number) args.get(0); + if (instanceIdArg == null) { + throw new NullPointerException("instanceIdArg unexpectedly null."); + } + String baseUrlArg = (String) args.get(1); + if (baseUrlArg == null) { + throw new NullPointerException("baseUrlArg unexpectedly null."); + } + String dataArg = (String) args.get(2); + if (dataArg == null) { + throw new NullPointerException("dataArg unexpectedly null."); + } + String mimeTypeArg = (String) args.get(3); + if (mimeTypeArg == null) { + throw new NullPointerException("mimeTypeArg unexpectedly null."); + } + String encodingArg = (String) args.get(4); + if (encodingArg == null) { + throw new NullPointerException("encodingArg unexpectedly null."); + } + String historyUrlArg = (String) args.get(5); + if (historyUrlArg == null) { + throw new NullPointerException("historyUrlArg unexpectedly null."); + } + api.loadDataWithBaseUrl( + instanceIdArg.longValue(), + baseUrlArg, + dataArg, + mimeTypeArg, + encodingArg, + historyUrlArg); + wrapped.put("result", null); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } { BasicMessageChannel channel = new BasicMessageChannel<>( diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java index 86bef0fab563..b557e9889704 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 @@ -352,6 +352,30 @@ public void dispose(Long instanceId) { } } + @Override + public void loadData(Long instanceId, String data, String mimeType, String encoding) { + final WebView webView = (WebView) instanceManager.getInstance(instanceId); + webView.loadData( + data, parseNullStringIdentifier(mimeType), parseNullStringIdentifier(encoding)); + } + + @Override + public void loadDataWithBaseUrl( + Long instanceId, + String baseUrl, + String data, + String mimeType, + String encoding, + String historyUrl) { + final WebView webView = (WebView) instanceManager.getInstance(instanceId); + webView.loadDataWithBaseURL( + parseNullStringIdentifier(baseUrl), + data, + parseNullStringIdentifier(mimeType), + parseNullStringIdentifier(encoding), + parseNullStringIdentifier(historyUrl)); + } + @Override public void loadUrl(Long instanceId, String url, Map headers) { final WebView webView = (WebView) instanceManager.getInstance(instanceId); @@ -477,4 +501,13 @@ public void setWebChromeClient(Long instanceId, Long clientInstanceId) { final WebView webView = (WebView) instanceManager.getInstance(instanceId); webView.setWebChromeClient((WebChromeClient) instanceManager.getInstance(clientInstanceId)); } + + @Nullable + private static String parseNullStringIdentifier(String value) { + if (value.equals(nullStringIdentifier)) { + return null; + } + + return value; + } } 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 e506188d0c9d..dc58b9b100ff 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 @@ -156,6 +156,52 @@ public void releaseInputAwareWebViewDependents() { verify(mockJavaScriptChannel2).release(); } + @Test + public void loadData() { + testHostApiImpl.loadData( + 0L, "VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==", "text/plain", "base64"); + verify(mockWebView) + .loadData("VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==", "text/plain", "base64"); + } + + @Test + public void loadDataWithNullValues() { + testHostApiImpl.loadData( + 0L, "VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==", "", ""); + verify(mockWebView).loadData("VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==", null, null); + } + + @Test + public void loadDataWithBaseUrl() { + testHostApiImpl.loadDataWithBaseUrl( + 0L, + "https://flutter.dev", + "VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==", + "text/plain", + "base64", + "about:blank"); + verify(mockWebView) + .loadDataWithBaseURL( + "https://flutter.dev", + "VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==", + "text/plain", + "base64", + "about:blank"); + } + + @Test + public void loadDataWithBaseUrlAndNullValues() { + testHostApiImpl.loadDataWithBaseUrl( + 0L, + "", + "VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==", + "", + "", + ""); + verify(mockWebView) + .loadDataWithBaseURL(null, "VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==", null, null, null); + } + @Test public void loadUrl() { testHostApiImpl.loadUrl(0L, "https://www.google.com", new HashMap<>()); diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart index 9041f9c56f06..bc43c16d4498 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart @@ -6,8 +6,10 @@ import 'dart:async'; import 'dart:convert'; +import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:path_provider/path_provider.dart'; import 'package:webview_flutter_android/webview_surface_android.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; @@ -38,6 +40,25 @@ The navigation delegate is set to block navigation to the youtube website. '''; +const String kExamplePage = ''' + + + +Load file or HTML string example + + + +

Local demo page

+

+ This is an example page used to demonstrate how to load a local file or HTML + string using the Flutter + webview plugin. +

+ + + +'''; + class _WebViewExample extends StatefulWidget { const _WebViewExample({Key? key}) : super(key: key); @@ -135,6 +156,8 @@ enum _MenuOptions { listCache, clearCache, navigationDelegate, + loadLocalFile, + loadHtmlString, } class _SampleMenu extends StatelessWidget { @@ -172,6 +195,12 @@ class _SampleMenu extends StatelessWidget { case _MenuOptions.navigationDelegate: _onNavigationDelegateExample(controller.data!, context); break; + case _MenuOptions.loadLocalFile: + _onLoadLocalFileExample(controller.data!, context); + break; + case _MenuOptions.loadHtmlString: + _onLoadHtmlStringExample(controller.data!, context); + break; } }, itemBuilder: (BuildContext context) => >[ @@ -204,6 +233,14 @@ class _SampleMenu extends StatelessWidget { value: _MenuOptions.navigationDelegate, child: Text('Navigation Delegate example'), ), + const PopupMenuItem<_MenuOptions>( + value: _MenuOptions.loadHtmlString, + child: Text('Load HTML string'), + ), + const PopupMenuItem<_MenuOptions>( + value: _MenuOptions.loadLocalFile, + child: Text('Load local file'), + ), ], ); }, @@ -281,6 +318,18 @@ class _SampleMenu extends StatelessWidget { await controller.loadUrl('data:text/html;base64,$contentBase64'); } + Future _onLoadLocalFileExample( + WebViewController controller, BuildContext context) async { + final String pathToIndex = await _prepareLocalFile(); + + await controller.loadFile(pathToIndex); + } + + Future _onLoadHtmlStringExample( + WebViewController controller, BuildContext context) async { + await controller.loadHtmlString(kExamplePage); + } + Widget _getCookieList(String cookies) { if (cookies == null || cookies == '""') { return Container(); @@ -294,6 +343,16 @@ class _SampleMenu extends StatelessWidget { children: cookieWidgets.toList(), ); } + + static Future _prepareLocalFile() async { + final String tmpDir = (await getTemporaryDirectory()).path; + final File indexFile = File('$tmpDir/www/index.html'); + + await Directory('$tmpDir/www').create(recursive: true); + await indexFile.writeAsString(kExamplePage); + + return indexFile.path; + } } class _NavigationControls extends StatelessWidget { diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart index 77d92c69c5fa..654abbd960cd 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart @@ -363,6 +363,28 @@ class WebViewController { WebView _widget; + /// Loads the file located on the specified [absoluteFilePath]. + /// + /// The [absoluteFilePath] parameter should contain the absolute path to the + /// file as it is stored on the device. For example: + /// `/Users/username/Documents/www/index.html`. + /// + /// Throws an ArgumentError if the [absoluteFilePath] does not exist. + Future loadFile(String absoluteFilePath) { + return _webViewPlatformController.loadFile(absoluteFilePath); + } + + /// Loads the supplied HTML string. + /// + /// The [baseUrl] parameter is used when resolving relative URLs within the + /// HTML string. + Future loadHtmlString(String html, {String? baseUrl}) { + return _webViewPlatformController.loadHtmlString( + html, + baseUrl: baseUrl, + ); + } + /// Loads the specified URL. /// /// If `headers` is not null and the URL is an HTTP URL, the key value paris in `headers` will diff --git a/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml index 2faa74394054..59579df8ca50 100644 --- a/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml @@ -8,6 +8,9 @@ environment: dependencies: flutter: sdk: flutter + + path_provider: ^2.0.6 + webview_flutter_android: # When depending on this package from a real application you should use: # webview_flutter: ^x.y.z diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart index 1d0dc259ca3d..928cdb3099f1 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart @@ -66,6 +66,95 @@ class WebView { return api.setWebContentsDebuggingEnabled(enabled); } + /// Loads the given data into this WebView using a 'data' scheme URL. + /// + /// Note that JavaScript's same origin policy means that script running in a + /// page loaded using this method will be unable to access content loaded + /// using any scheme other than 'data', including 'http(s)'. To avoid this + /// restriction, use [loadDataWithBaseURL()] with an appropriate base URL. + /// + /// The [encoding] parameter specifies whether the data is base64 or URL + /// encoded. If the data is base64 encoded, the value of the encoding + /// parameter must be `'base64'`. HTML can be encoded with + /// `base64.encode(bytes)` like so: + /// ```dart + /// import 'dart:convert'; + /// + /// final unencodedHtml = ''' + /// '%28' is the code for '(' + /// '''; + /// final encodedHtml = base64.encode(utf8.encode(unencodedHtml)); + /// print(encodedHtml); + /// ``` + /// + /// The [mimeType] parameter specifies the format of the data. If WebView + /// can't handle the specified MIME type, it will download the data. If + /// `null`, defaults to 'text/html'. + Future loadData({ + required String data, + String? mimeType, + String? encoding, + }) { + return api.loadDataFromInstance( + this, + data, + mimeType ?? _nullStringIdentifier, + encoding ?? _nullStringIdentifier, + ); + } + + /// Loads the given data into this WebView. + /// + /// The [baseUrl] is used as base URL for the content. It is used both to + /// resolve relative URLs and when applying JavaScript's same origin policy. + /// + /// The [historyUrl] is used for the history entry. + /// + /// The [mimeType] parameter specifies the format of the data. If WebView + /// can't handle the specified MIME type, it will download the data. If + /// `null`, defaults to 'text/html'. + /// + /// Note that content specified in this way can access local device files (via + /// 'file' scheme URLs) only if baseUrl specifies a scheme other than 'http', + /// 'https', 'ftp', 'ftps', 'about' or 'javascript'. + /// + /// If the base URL uses the data scheme, this method is equivalent to calling + /// [loadData] and the [historyUrl] is ignored, and the data will be treated + /// as part of a data: URL, including the requirement that the content be + /// URL-encoded or base64 encoded. If the base URL uses any other scheme, then + /// the data will be loaded into the WebView as a plain string (i.e. not part + /// of a data URL) and any URL-encoded entities in the string will not be + /// decoded. + /// + /// Note that the [baseUrl] is sent in the 'Referer' HTTP header when + /// requesting subresources (images, etc.) of the page loaded using this + /// method. + /// + /// If a valid HTTP or HTTPS base URL is not specified in [baseUrl], then + /// content loaded using this method will have a `window.origin` value of + /// `"null"`. This must not be considered to be a trusted origin by the + /// application or by any JavaScript code running inside the WebView (for + /// example, event sources in DOM event handlers or web messages), because + /// malicious content can also create frames with a null origin. If you need + /// to identify the main frame's origin in a trustworthy way, you should use a + /// valid HTTP or HTTPS base URL to set the origin. + Future loadDataWithBaseUrl({ + String? baseUrl, + required String data, + String? mimeType, + String? encoding, + String? historyUrl, + }) { + return api.loadDataWithBaseUrlFromInstance( + this, + baseUrl ?? _nullStringIdentifier, + data, + mimeType ?? _nullStringIdentifier, + encoding ?? _nullStringIdentifier, + historyUrl ?? _nullStringIdentifier, + ); + } + /// Loads the given URL with additional HTTP headers, specified as a map from name to value. /// /// Note that if this map contains any of the headers that are set by default diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart index 7fb95f1dd942..9bf2de6d169b 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart @@ -129,6 +129,70 @@ class WebViewHostApi { } } + Future loadData(int arg_instanceId, String arg_data, + String arg_mimeType, String arg_encoding) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WebViewHostApi.loadData', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel.send( + [arg_instanceId, arg_data, arg_mimeType, arg_encoding]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future loadDataWithBaseUrl( + int arg_instanceId, + String arg_baseUrl, + String arg_data, + String arg_mimeType, + String arg_encoding, + String arg_historyUrl) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel.send([ + arg_instanceId, + arg_baseUrl, + arg_data, + arg_mimeType, + arg_encoding, + arg_historyUrl + ]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + Future loadUrl(int arg_instanceId, String arg_url, Map arg_headers) async { final BasicMessageChannel channel = BasicMessageChannel( diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart index 93391a51ad21..ece05b56c182 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart @@ -109,6 +109,40 @@ class WebViewHostApiImpl extends WebViewHostApi { instanceManager.removeInstance(instance); } + /// Helper method to convert the instances ids to objects. + Future loadDataFromInstance( + WebView instance, + String data, + String mimeType, + String encoding, + ) { + return loadData( + instanceManager.getInstanceId(instance)!, + data, + mimeType, + encoding, + ); + } + + /// Helper method to convert instances ids to objects. + Future loadDataWithBaseUrlFromInstance( + WebView instance, + String baseUrl, + String data, + String mimeType, + String encoding, + String historyUrl, + ) { + return loadDataWithBaseUrl( + instanceManager.getInstanceId(instance)!, + baseUrl, + data, + mimeType, + encoding, + historyUrl, + ); + } + /// Helper method to convert instances ids to objects. Future loadUrlFromInstance( WebView instance, diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart index d63d641ab7cb..c264bc1b9e6e 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart @@ -148,6 +148,24 @@ class WebViewAndroidPlatformController extends WebViewPlatformController { @visibleForTesting WebViewAndroidWebViewClient get webViewClient => _webViewClient; + @override + Future loadHtmlString(String html, {String? baseUrl}) { + return webView.loadDataWithBaseUrl( + baseUrl: baseUrl, + data: html, + mimeType: 'text/html', + ); + } + + @override + Future loadFile(String absoluteFilePath) { + final String url = absoluteFilePath.startsWith('file://') + ? absoluteFilePath + : 'file://$absoluteFilePath'; + + return webView.loadUrl(url, {}); + } + @override Future loadUrl( String url, diff --git a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart index 47bc42058ba9..963216329237 100644 --- a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart +++ b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart @@ -24,6 +24,22 @@ abstract class WebViewHostApi { void dispose(int instanceId); + void loadData( + int instanceId, + String data, + String mimeType, + String encoding, + ); + + void loadDataWithBaseUrl( + int instanceId, + String baseUrl, + String data, + String mimeType, + String encoding, + String historyUrl, + ); + void loadUrl( int instanceId, String url, diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index a9fa97d454bd..8889469920df 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_android description: A Flutter plugin that provides a WebView widget on Android. repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.3.1 +version: 2.4.0 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart index 07bbebb6d9d7..45988a0ae441 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart @@ -4,7 +4,7 @@ // Autogenerated from Pigeon (v1.0.9), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, avoid_relative_lib_imports +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, avoid_relative_lib_imports, unnecessary_parenthesis // @dart = 2.12 import 'dart:async'; import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; @@ -23,6 +23,9 @@ abstract class TestWebViewHostApi { void create(int instanceId, bool useHybridComposition); void dispose(int instanceId); + void loadData(int instanceId, String data, String mimeType, String encoding); + void loadDataWithBaseUrl(int instanceId, String baseUrl, String data, + String mimeType, String encoding, String historyUrl); void loadUrl(int instanceId, String url, Map headers); String getUrl(int instanceId); bool canGoBack(int instanceId); @@ -56,10 +59,10 @@ abstract class TestWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.create was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.create was null, expected non-null int.'); - final bool? arg_useHybridComposition = args[1] as bool?; + final bool? arg_useHybridComposition = (args[1] as bool?); assert(arg_useHybridComposition != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.create was null, expected non-null bool.'); api.create(arg_instanceId!, arg_useHybridComposition!); @@ -78,7 +81,7 @@ abstract class TestWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.dispose was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.dispose was null, expected non-null int.'); api.dispose(arg_instanceId!); @@ -86,6 +89,70 @@ abstract class TestWebViewHostApi { }); } } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WebViewHostApi.loadData', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WebViewHostApi.loadData was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WebViewHostApi.loadData was null, expected non-null int.'); + final String? arg_data = (args[1] as String?); + assert(arg_data != null, + 'Argument for dev.flutter.pigeon.WebViewHostApi.loadData was null, expected non-null String.'); + final String? arg_mimeType = (args[2] as String?); + assert(arg_mimeType != null, + 'Argument for dev.flutter.pigeon.WebViewHostApi.loadData was null, expected non-null String.'); + final String? arg_encoding = (args[3] as String?); + assert(arg_encoding != null, + 'Argument for dev.flutter.pigeon.WebViewHostApi.loadData was null, expected non-null String.'); + api.loadData( + arg_instanceId!, arg_data!, arg_mimeType!, arg_encoding!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl was null, expected non-null int.'); + final String? arg_baseUrl = (args[1] as String?); + assert(arg_baseUrl != null, + 'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl was null, expected non-null String.'); + final String? arg_data = (args[2] as String?); + assert(arg_data != null, + 'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl was null, expected non-null String.'); + final String? arg_mimeType = (args[3] as String?); + assert(arg_mimeType != null, + 'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl was null, expected non-null String.'); + final String? arg_encoding = (args[4] as String?); + assert(arg_encoding != null, + 'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl was null, expected non-null String.'); + final String? arg_historyUrl = (args[5] as String?); + assert(arg_historyUrl != null, + 'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl was null, expected non-null String.'); + api.loadDataWithBaseUrl(arg_instanceId!, arg_baseUrl!, arg_data!, + arg_mimeType!, arg_encoding!, arg_historyUrl!); + return {}; + }); + } + } { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.loadUrl', codec, @@ -97,10 +164,10 @@ abstract class TestWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.loadUrl was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.loadUrl was null, expected non-null int.'); - final String? arg_url = args[1] as String?; + final String? arg_url = (args[1] as String?); assert(arg_url != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.loadUrl was null, expected non-null String.'); final Map? arg_headers = @@ -123,7 +190,7 @@ abstract class TestWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.getUrl was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.getUrl was null, expected non-null int.'); final String output = api.getUrl(arg_instanceId!); @@ -142,7 +209,7 @@ abstract class TestWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.canGoBack was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.canGoBack was null, expected non-null int.'); final bool output = api.canGoBack(arg_instanceId!); @@ -161,7 +228,7 @@ abstract class TestWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.canGoForward was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.canGoForward was null, expected non-null int.'); final bool output = api.canGoForward(arg_instanceId!); @@ -180,7 +247,7 @@ abstract class TestWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.goBack was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.goBack was null, expected non-null int.'); api.goBack(arg_instanceId!); @@ -199,7 +266,7 @@ abstract class TestWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.goForward was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.goForward was null, expected non-null int.'); api.goForward(arg_instanceId!); @@ -218,7 +285,7 @@ abstract class TestWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.reload was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.reload was null, expected non-null int.'); api.reload(arg_instanceId!); @@ -237,10 +304,10 @@ abstract class TestWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.clearCache was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.clearCache was null, expected non-null int.'); - final bool? arg_includeDiskFiles = args[1] as bool?; + final bool? arg_includeDiskFiles = (args[1] as bool?); assert(arg_includeDiskFiles != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.clearCache was null, expected non-null bool.'); api.clearCache(arg_instanceId!, arg_includeDiskFiles!); @@ -259,10 +326,10 @@ abstract class TestWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.evaluateJavascript was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.evaluateJavascript was null, expected non-null int.'); - final String? arg_javascriptString = args[1] as String?; + final String? arg_javascriptString = (args[1] as String?); assert(arg_javascriptString != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.evaluateJavascript was null, expected non-null String.'); final String output = await api.evaluateJavascript( @@ -282,7 +349,7 @@ abstract class TestWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.getTitle was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.getTitle was null, expected non-null int.'); final String output = api.getTitle(arg_instanceId!); @@ -301,13 +368,13 @@ abstract class TestWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.scrollTo was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.scrollTo was null, expected non-null int.'); - final int? arg_x = args[1] as int?; + final int? arg_x = (args[1] as int?); assert(arg_x != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.scrollTo was null, expected non-null int.'); - final int? arg_y = args[2] as int?; + final int? arg_y = (args[2] as int?); assert(arg_y != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.scrollTo was null, expected non-null int.'); api.scrollTo(arg_instanceId!, arg_x!, arg_y!); @@ -326,13 +393,13 @@ abstract class TestWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.scrollBy was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.scrollBy was null, expected non-null int.'); - final int? arg_x = args[1] as int?; + final int? arg_x = (args[1] as int?); assert(arg_x != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.scrollBy was null, expected non-null int.'); - final int? arg_y = args[2] as int?; + final int? arg_y = (args[2] as int?); assert(arg_y != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.scrollBy was null, expected non-null int.'); api.scrollBy(arg_instanceId!, arg_x!, arg_y!); @@ -351,7 +418,7 @@ abstract class TestWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.getScrollX was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.getScrollX was null, expected non-null int.'); final int output = api.getScrollX(arg_instanceId!); @@ -370,7 +437,7 @@ abstract class TestWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.getScrollY was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.getScrollY was null, expected non-null int.'); final int output = api.getScrollY(arg_instanceId!); @@ -390,7 +457,7 @@ abstract class TestWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setWebContentsDebuggingEnabled was null.'); final List args = (message as List?)!; - final bool? arg_enabled = args[0] as bool?; + final bool? arg_enabled = (args[0] as bool?); assert(arg_enabled != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setWebContentsDebuggingEnabled was null, expected non-null bool.'); api.setWebContentsDebuggingEnabled(arg_enabled!); @@ -409,10 +476,10 @@ abstract class TestWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setWebViewClient was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setWebViewClient was null, expected non-null int.'); - final int? arg_webViewClientInstanceId = args[1] as int?; + final int? arg_webViewClientInstanceId = (args[1] as int?); assert(arg_webViewClientInstanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setWebViewClient was null, expected non-null int.'); api.setWebViewClient(arg_instanceId!, arg_webViewClientInstanceId!); @@ -431,10 +498,10 @@ abstract class TestWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.addJavaScriptChannel was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.addJavaScriptChannel was null, expected non-null int.'); - final int? arg_javaScriptChannelInstanceId = args[1] as int?; + final int? arg_javaScriptChannelInstanceId = (args[1] as int?); assert(arg_javaScriptChannelInstanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.addJavaScriptChannel was null, expected non-null int.'); api.addJavaScriptChannel( @@ -454,10 +521,10 @@ abstract class TestWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.removeJavaScriptChannel was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.removeJavaScriptChannel was null, expected non-null int.'); - final int? arg_javaScriptChannelInstanceId = args[1] as int?; + final int? arg_javaScriptChannelInstanceId = (args[1] as int?); assert(arg_javaScriptChannelInstanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.removeJavaScriptChannel was null, expected non-null int.'); api.removeJavaScriptChannel( @@ -477,10 +544,10 @@ abstract class TestWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setDownloadListener was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setDownloadListener was null, expected non-null int.'); - final int? arg_listenerInstanceId = args[1] as int?; + final int? arg_listenerInstanceId = (args[1] as int?); assert(arg_listenerInstanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setDownloadListener was null, expected non-null int.'); api.setDownloadListener(arg_instanceId!, arg_listenerInstanceId!); @@ -499,10 +566,10 @@ abstract class TestWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setWebChromeClient was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setWebChromeClient was null, expected non-null int.'); - final int? arg_clientInstanceId = args[1] as int?; + final int? arg_clientInstanceId = (args[1] as int?); assert(arg_clientInstanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setWebChromeClient was null, expected non-null int.'); api.setWebChromeClient(arg_instanceId!, arg_clientInstanceId!); @@ -546,10 +613,10 @@ abstract class TestWebSettingsHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.create was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.create was null, expected non-null int.'); - final int? arg_webViewInstanceId = args[1] as int?; + final int? arg_webViewInstanceId = (args[1] as int?); assert(arg_webViewInstanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.create was null, expected non-null int.'); api.create(arg_instanceId!, arg_webViewInstanceId!); @@ -568,7 +635,7 @@ abstract class TestWebSettingsHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.dispose was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.dispose was null, expected non-null int.'); api.dispose(arg_instanceId!); @@ -587,10 +654,10 @@ abstract class TestWebSettingsHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setDomStorageEnabled was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setDomStorageEnabled was null, expected non-null int.'); - final bool? arg_flag = args[1] as bool?; + final bool? arg_flag = (args[1] as bool?); assert(arg_flag != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setDomStorageEnabled was null, expected non-null bool.'); api.setDomStorageEnabled(arg_instanceId!, arg_flag!); @@ -610,10 +677,10 @@ abstract class TestWebSettingsHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptCanOpenWindowsAutomatically was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptCanOpenWindowsAutomatically was null, expected non-null int.'); - final bool? arg_flag = args[1] as bool?; + final bool? arg_flag = (args[1] as bool?); assert(arg_flag != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptCanOpenWindowsAutomatically was null, expected non-null bool.'); api.setJavaScriptCanOpenWindowsAutomatically( @@ -634,10 +701,10 @@ abstract class TestWebSettingsHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setSupportMultipleWindows was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setSupportMultipleWindows was null, expected non-null int.'); - final bool? arg_support = args[1] as bool?; + final bool? arg_support = (args[1] as bool?); assert(arg_support != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setSupportMultipleWindows was null, expected non-null bool.'); api.setSupportMultipleWindows(arg_instanceId!, arg_support!); @@ -656,10 +723,10 @@ abstract class TestWebSettingsHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptEnabled was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptEnabled was null, expected non-null int.'); - final bool? arg_flag = args[1] as bool?; + final bool? arg_flag = (args[1] as bool?); assert(arg_flag != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptEnabled was null, expected non-null bool.'); api.setJavaScriptEnabled(arg_instanceId!, arg_flag!); @@ -678,10 +745,10 @@ abstract class TestWebSettingsHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setUserAgentString was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setUserAgentString was null, expected non-null int.'); - final String? arg_userAgentString = args[1] as String?; + final String? arg_userAgentString = (args[1] as String?); assert(arg_userAgentString != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setUserAgentString was null, expected non-null String.'); api.setUserAgentString(arg_instanceId!, arg_userAgentString!); @@ -701,10 +768,10 @@ abstract class TestWebSettingsHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setMediaPlaybackRequiresUserGesture was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setMediaPlaybackRequiresUserGesture was null, expected non-null int.'); - final bool? arg_require = args[1] as bool?; + final bool? arg_require = (args[1] as bool?); assert(arg_require != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setMediaPlaybackRequiresUserGesture was null, expected non-null bool.'); api.setMediaPlaybackRequiresUserGesture( @@ -724,10 +791,10 @@ abstract class TestWebSettingsHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setSupportZoom was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setSupportZoom was null, expected non-null int.'); - final bool? arg_support = args[1] as bool?; + final bool? arg_support = (args[1] as bool?); assert(arg_support != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setSupportZoom was null, expected non-null bool.'); api.setSupportZoom(arg_instanceId!, arg_support!); @@ -747,10 +814,10 @@ abstract class TestWebSettingsHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setLoadWithOverviewMode was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setLoadWithOverviewMode was null, expected non-null int.'); - final bool? arg_overview = args[1] as bool?; + final bool? arg_overview = (args[1] as bool?); assert(arg_overview != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setLoadWithOverviewMode was null, expected non-null bool.'); api.setLoadWithOverviewMode(arg_instanceId!, arg_overview!); @@ -769,10 +836,10 @@ abstract class TestWebSettingsHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setUseWideViewPort was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setUseWideViewPort was null, expected non-null int.'); - final bool? arg_use = args[1] as bool?; + final bool? arg_use = (args[1] as bool?); assert(arg_use != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setUseWideViewPort was null, expected non-null bool.'); api.setUseWideViewPort(arg_instanceId!, arg_use!); @@ -791,10 +858,10 @@ abstract class TestWebSettingsHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setDisplayZoomControls was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setDisplayZoomControls was null, expected non-null int.'); - final bool? arg_enabled = args[1] as bool?; + final bool? arg_enabled = (args[1] as bool?); assert(arg_enabled != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setDisplayZoomControls was null, expected non-null bool.'); api.setDisplayZoomControls(arg_instanceId!, arg_enabled!); @@ -813,10 +880,10 @@ abstract class TestWebSettingsHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setBuiltInZoomControls was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setBuiltInZoomControls was null, expected non-null int.'); - final bool? arg_enabled = args[1] as bool?; + final bool? arg_enabled = (args[1] as bool?); assert(arg_enabled != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setBuiltInZoomControls was null, expected non-null bool.'); api.setBuiltInZoomControls(arg_instanceId!, arg_enabled!); @@ -849,10 +916,10 @@ abstract class TestJavaScriptChannelHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.JavaScriptChannelHostApi.create was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.JavaScriptChannelHostApi.create was null, expected non-null int.'); - final String? arg_channelName = args[1] as String?; + final String? arg_channelName = (args[1] as String?); assert(arg_channelName != null, 'Argument for dev.flutter.pigeon.JavaScriptChannelHostApi.create was null, expected non-null String.'); api.create(arg_instanceId!, arg_channelName!); @@ -884,10 +951,10 @@ abstract class TestWebViewClientHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebViewClientHostApi.create was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewClientHostApi.create was null, expected non-null int.'); - final bool? arg_shouldOverrideUrlLoading = args[1] as bool?; + final bool? arg_shouldOverrideUrlLoading = (args[1] as bool?); assert(arg_shouldOverrideUrlLoading != null, 'Argument for dev.flutter.pigeon.WebViewClientHostApi.create was null, expected non-null bool.'); api.create(arg_instanceId!, arg_shouldOverrideUrlLoading!); @@ -920,7 +987,7 @@ abstract class TestDownloadListenerHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.DownloadListenerHostApi.create was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.DownloadListenerHostApi.create was null, expected non-null int.'); api.create(arg_instanceId!); @@ -952,10 +1019,10 @@ abstract class TestWebChromeClientHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WebChromeClientHostApi.create was null.'); final List args = (message as List?)!; - final int? arg_instanceId = args[0] as int?; + final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebChromeClientHostApi.create was null, expected non-null int.'); - final int? arg_webViewClientInstanceId = args[1] as int?; + final int? arg_webViewClientInstanceId = (args[1] as int?); assert(arg_webViewClientInstanceId != null, 'Argument for dev.flutter.pigeon.WebChromeClientHostApi.create was null, expected non-null int.'); api.create(arg_instanceId!, arg_webViewClientInstanceId!); diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart index 29c0c3a0bb34..0e7d5fcd552e 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart @@ -58,6 +58,61 @@ void main() { verify(mockPlatformHostApi.setWebContentsDebuggingEnabled(true)); }); + test('loadData', () { + webView.loadData( + data: 'hello', + mimeType: 'text/plain', + encoding: 'base64', + ); + verify(mockPlatformHostApi.loadData( + webViewInstanceId, + 'hello', + 'text/plain', + 'base64', + )); + }); + + test('loadData with null values', () { + webView.loadData(data: 'hello', mimeType: null, encoding: null); + verify(mockPlatformHostApi.loadData( + webViewInstanceId, + 'hello', + '', + '', + )); + }); + + test('loadDataWithBaseUrl', () { + webView.loadDataWithBaseUrl( + baseUrl: 'https://base.url', + data: 'hello', + mimeType: 'text/plain', + encoding: 'base64', + historyUrl: 'https://history.url', + ); + + verify(mockPlatformHostApi.loadDataWithBaseUrl( + webViewInstanceId, + 'https://base.url', + 'hello', + 'text/plain', + 'base64', + 'https://history.url', + )); + }); + + test('loadDataWithBaseUrl with null values', () { + webView.loadDataWithBaseUrl(data: 'hello'); + verify(mockPlatformHostApi.loadDataWithBaseUrl( + webViewInstanceId, + '', + 'hello', + '', + '', + '', + )); + }); + test('loadUrl', () { webView.loadUrl('hello', {'a': 'header'}); verify(mockPlatformHostApi.loadUrl( diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart index 4378ed7abf77..90cbf2c4fefe 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart @@ -1,7 +1,3 @@ -// 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. - // Mocks generated by Mockito 5.0.16 from annotations // in webview_flutter_android/test/android_webview_test.dart. // Do not manually edit this file. @@ -226,6 +222,19 @@ class MockTestWebViewHostApi extends _i1.Mock super.noSuchMethod(Invocation.method(#dispose, [instanceId]), returnValueForMissingStub: null); @override + void loadData( + int? instanceId, String? data, String? mimeType, String? encoding) => + super.noSuchMethod( + Invocation.method(#loadData, [instanceId, data, mimeType, encoding]), + returnValueForMissingStub: null); + @override + void loadDataWithBaseUrl(int? instanceId, String? baseUrl, String? data, + String? mimeType, String? encoding, String? historyUrl) => + super.noSuchMethod( + Invocation.method(#loadDataWithBaseUrl, + [instanceId, baseUrl, data, mimeType, encoding, historyUrl]), + returnValueForMissingStub: null); + @override void loadUrl(int? instanceId, String? url, Map? headers) => super.noSuchMethod( Invocation.method(#loadUrl, [instanceId, url, headers]), @@ -359,6 +368,31 @@ class MockWebView extends _i1.Mock implements _i2.WebView { (super.noSuchMethod(Invocation.getter(#settings), returnValue: _FakeWebSettings_0()) as _i2.WebSettings); @override + _i4.Future loadData( + {String? data, String? mimeType, String? encoding}) => + (super.noSuchMethod( + Invocation.method(#loadData, [], + {#data: data, #mimeType: mimeType, #encoding: encoding}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future loadDataWithBaseUrl( + {String? baseUrl, + String? data, + String? mimeType, + String? encoding, + String? historyUrl}) => + (super.noSuchMethod( + Invocation.method(#loadDataWithBaseUrl, [], { + #baseUrl: baseUrl, + #data: data, + #mimeType: mimeType, + #encoding: encoding, + #historyUrl: historyUrl + }), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override _i4.Future loadUrl(String? url, Map? headers) => (super.noSuchMethod(Invocation.method(#loadUrl, [url, headers]), returnValue: Future.value(), diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart index d969664a42ed..460cb54bd393 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart @@ -274,6 +274,60 @@ void main() { }); group('$WebViewPlatformController', () { + testWidgets('loadFile without "file://" prefix', + (WidgetTester tester) async { + await buildWidget(tester); + + const String filePath = '/path/to/file.html'; + await testController.loadFile(filePath); + + verify(mockWebView.loadUrl( + 'file://$filePath', + {}, + )); + }); + + testWidgets('loadFile with "file://" prefix', + (WidgetTester tester) async { + await buildWidget(tester); + + await testController.loadFile('file:///path/to/file.html'); + + verify(mockWebView.loadUrl( + 'file:///path/to/file.html', + {}, + )); + }); + + testWidgets('loadHtmlString without base URL', + (WidgetTester tester) async { + await buildWidget(tester); + + const String htmlString = 'Test data.'; + await testController.loadHtmlString(htmlString); + + verify(mockWebView.loadDataWithBaseUrl( + data: htmlString, + mimeType: 'text/html', + )); + }); + + testWidgets('loadHtmlString with base URL', (WidgetTester tester) async { + await buildWidget(tester); + + const String htmlString = 'Test data.'; + await testController.loadHtmlString( + htmlString, + baseUrl: 'https://flutter.dev', + ); + + verify(mockWebView.loadDataWithBaseUrl( + baseUrl: 'https://flutter.dev', + data: htmlString, + mimeType: 'text/html', + )); + }); + testWidgets('loadUrl', (WidgetTester tester) async { await buildWidget(tester); diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart index c9d3324ecfc3..0582049acf6d 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart @@ -1,7 +1,3 @@ -// 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. - // Mocks generated by Mockito 5.0.16 from annotations // in webview_flutter_android/test/webview_android_widget_test.dart. // Do not manually edit this file. @@ -116,6 +112,31 @@ class MockWebView extends _i1.Mock implements _i2.WebView { (super.noSuchMethod(Invocation.getter(#settings), returnValue: _FakeWebSettings_0()) as _i2.WebSettings); @override + _i4.Future loadData( + {String? data, String? mimeType, String? encoding}) => + (super.noSuchMethod( + Invocation.method(#loadData, [], + {#data: data, #mimeType: mimeType, #encoding: encoding}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future loadDataWithBaseUrl( + {String? baseUrl, + String? data, + String? mimeType, + String? encoding, + String? historyUrl}) => + (super.noSuchMethod( + Invocation.method(#loadDataWithBaseUrl, [], { + #baseUrl: baseUrl, + #data: data, + #mimeType: mimeType, + #encoding: encoding, + #historyUrl: historyUrl + }), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override _i4.Future loadUrl(String? url, Map? headers) => (super.noSuchMethod(Invocation.method(#loadUrl, [url, headers]), returnValue: Future.value(), From 355d51bc0504dd59cc51c956c84dc8614175bade Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Tue, 30 Nov 2021 19:54:08 +0100 Subject: [PATCH 026/600] [webview_flutter] Migrate webview_flutter package to analysis_options.yaml (#4552) --- .../webview_flutter/CHANGELOG.md | 1 + .../webview_flutter/analysis_options.yaml | 1 - .../webview_flutter_test.dart | 58 +++++++------- .../webview_flutter/example/lib/main.dart | 29 ++++--- .../webview_flutter/example/pubspec.yaml | 4 +- .../webview_flutter/lib/src/webview.dart | 17 ++-- .../webview_flutter/pubspec.yaml | 6 +- .../test/webview_flutter_test.dart | 78 ++++++++++--------- script/configs/custom_analysis.yaml | 1 - 9 files changed, 102 insertions(+), 93 deletions(-) delete mode 100644 packages/webview_flutter/webview_flutter/analysis_options.yaml diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index 5dd2ea0cf554..63d95c04cc9c 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -2,6 +2,7 @@ * Updates example app Android compileSdkVersion to 31. * Integration test fixes. +* Updates code for new analysis options. ## 2.3.1 diff --git a/packages/webview_flutter/webview_flutter/analysis_options.yaml b/packages/webview_flutter/webview_flutter/analysis_options.yaml deleted file mode 100644 index 5aeb4e7c5e21..000000000000 --- a/packages/webview_flutter/webview_flutter/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../../analysis_options_legacy.yaml diff --git a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart index 1630d6e61940..cc74afd8a745 100644 --- a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart @@ -204,7 +204,7 @@ void main() { ); resizeButtonTapped = true; - await tester.tap(find.byKey(const ValueKey('resizeButton'))); + await tester.tap(find.byKey(const ValueKey('resizeButton'))); await tester.pumpAndSettle(); expect(buttonTapResizeCompleter.future, completes); }); @@ -460,10 +460,10 @@ void main() { testWidgets('Video plays inline when allowsInlineMediaPlayback is true', (WidgetTester tester) async { - Completer controllerCompleter = + final Completer controllerCompleter = Completer(); - Completer pageLoaded = Completer(); - Completer videoPlaying = Completer(); + final Completer pageLoaded = Completer(); + final Completer videoPlaying = Completer(); await tester.pumpWidget( Directionality( @@ -494,7 +494,7 @@ void main() { ), ), ); - WebViewController controller = await controllerCompleter.future; + final WebViewController controller = await controllerCompleter.future; await pageLoaded.future; // Pump once to trigger the video play. @@ -503,7 +503,7 @@ void main() { // Makes sure we get the correct event that indicates the video is actually playing. await videoPlaying.future; - String fullScreen = + final String fullScreen = await controller.runJavascriptReturningResult('isFullScreen();'); expect(fullScreen, _webviewBool(false)); }); @@ -512,10 +512,10 @@ void main() { testWidgets( 'Video plays full screen when allowsInlineMediaPlayback is false', (WidgetTester tester) async { - Completer controllerCompleter = + final Completer controllerCompleter = Completer(); - Completer pageLoaded = Completer(); - Completer videoPlaying = Completer(); + final Completer pageLoaded = Completer(); + final Completer videoPlaying = Completer(); await tester.pumpWidget( Directionality( @@ -546,7 +546,7 @@ void main() { ), ), ); - WebViewController controller = await controllerCompleter.future; + final WebViewController controller = await controllerCompleter.future; await pageLoaded.future; // Pump once to trigger the video play. @@ -555,7 +555,7 @@ void main() { // Makes sure we get the correct event that indicates the video is actually playing. await videoPlaying.future; - String fullScreen = + final String fullScreen = await controller.runJavascriptReturningResult('isFullScreen();'); expect(fullScreen, _webviewBool(true)); }, skip: Platform.isAndroid); @@ -733,7 +733,7 @@ void main() { }); testWidgets('getTitle', (WidgetTester tester) async { - final String getTitleTest = ''' + const String getTitleTest = ''' Some title @@ -784,7 +784,7 @@ void main() { group('Programmatic Scroll', () { // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757. testWidgets('setAndGetScrollPosition', (WidgetTester tester) async { - final String scrollTestPage = ''' + const String scrollTestPage = ''' @@ -831,7 +831,7 @@ void main() { final WebViewController controller = await controllerCompleter.future; await pageLoaded.future; - await tester.pumpAndSettle(Duration(seconds: 3)); + await tester.pumpAndSettle(const Duration(seconds: 3)); int scrollPosX = await controller.getScrollX(); int scrollPosY = await controller.getScrollY(); @@ -871,7 +871,7 @@ void main() { // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757. testWidgets('setAndGetScrollPosition', (WidgetTester tester) async { - final String scrollTestPage = ''' + const String scrollTestPage = ''' @@ -918,7 +918,7 @@ void main() { final WebViewController controller = await controllerCompleter.future; await pageLoaded.future; - await tester.pumpAndSettle(Duration(seconds: 3)); + await tester.pumpAndSettle(const Duration(seconds: 3)); // Check scrollTo() const int X_SCROLL = 123; @@ -941,7 +941,7 @@ void main() { // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757. testWidgets('inputs are scrolled into view when focused', (WidgetTester tester) async { - final String scrollTestPage = ''' + const String scrollTestPage = ''' @@ -993,7 +993,7 @@ void main() { ), ), ); - await Future.delayed(Duration(milliseconds: 20)); + await Future.delayed(const Duration(milliseconds: 20)); await tester.pump(); }); @@ -1002,7 +1002,7 @@ void main() { final String viewportRectJSON = await _runJavascriptReturningResult( controller, 'JSON.stringify(viewport.getBoundingClientRect())'); final Map viewportRectRelativeToViewport = - jsonDecode(viewportRectJSON); + jsonDecode(viewportRectJSON) as Map; // Check that the input is originally outside of the viewport. @@ -1010,7 +1010,7 @@ void main() { await _runJavascriptReturningResult( controller, 'JSON.stringify(inputEl.getBoundingClientRect())'); final Map initialInputClientRectRelativeToViewport = - jsonDecode(initialInputClientRectJSON); + jsonDecode(initialInputClientRectJSON) as Map; expect( initialInputClientRectRelativeToViewport['bottom'] <= @@ -1025,7 +1025,7 @@ void main() { await _runJavascriptReturningResult( controller, 'JSON.stringify(inputEl.getBoundingClientRect())'); final Map lastInputClientRectRelativeToViewport = - jsonDecode(lastInputClientRectJSON); + jsonDecode(lastInputClientRectJSON) as Map; expect( lastInputClientRectRelativeToViewport['top'] >= @@ -1048,7 +1048,7 @@ void main() { }); group('NavigationDelegate', () { - final String blankPage = ""; + const String blankPage = ''; final String blankPageEncoded = 'data:text/html;charset=utf-8;base64,' + base64Encode(const Utf8Encoder().convert(blankPage)); @@ -1144,7 +1144,7 @@ void main() { testWidgets( 'onWebResourceError only called for main frame', (WidgetTester tester) async { - final String iframeTest = ''' + const String iframeTest = ''' @@ -1357,7 +1357,7 @@ void main() { testWidgets( 'JavaScript does not run in parent window', (WidgetTester tester) async { - final String iframe = ''' + const String iframe = ''' - - - diff --git a/packages/connectivity/connectivity/example/web/manifest.json b/packages/connectivity/connectivity/example/web/manifest.json deleted file mode 100644 index 8c012917dab7..000000000000 --- a/packages/connectivity/connectivity/example/web/manifest.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "example", - "short_name": "example", - "start_url": ".", - "display": "standalone", - "background_color": "#0175C2", - "theme_color": "#0175C2", - "description": "A new Flutter project.", - "orientation": "portrait-primary", - "prefer_related_applications": false, - "icons": [ - { - "src": "icons/Icon-192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "icons/Icon-512.png", - "sizes": "512x512", - "type": "image/png" - } - ] -} diff --git a/packages/connectivity/connectivity/ios/Assets/.gitkeep b/packages/connectivity/connectivity/ios/Assets/.gitkeep deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/connectivity/connectivity/ios/Classes/FLTConnectivityPlugin.h b/packages/connectivity/connectivity/ios/Classes/FLTConnectivityPlugin.h deleted file mode 100644 index aec76adc0b58..000000000000 --- a/packages/connectivity/connectivity/ios/Classes/FLTConnectivityPlugin.h +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -@interface FLTConnectivityPlugin : NSObject -@end diff --git a/packages/connectivity/connectivity/ios/Classes/FLTConnectivityPlugin.m b/packages/connectivity/connectivity/ios/Classes/FLTConnectivityPlugin.m deleted file mode 100644 index ac37c2359137..000000000000 --- a/packages/connectivity/connectivity/ios/Classes/FLTConnectivityPlugin.m +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "FLTConnectivityPlugin.h" - -#import "Reachability/Reachability.h" - -#import -#import "SystemConfiguration/CaptiveNetwork.h" - -#include - -#include - -@interface FLTConnectivityPlugin () - -@end - -@implementation FLTConnectivityPlugin { - FlutterEventSink _eventSink; - Reachability* _reachabilityForInternetConnection; -} - -+ (void)registerWithRegistrar:(NSObject*)registrar { - FLTConnectivityPlugin* instance = [[FLTConnectivityPlugin alloc] init]; - - FlutterMethodChannel* channel = - [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/connectivity" - binaryMessenger:[registrar messenger]]; - [registrar addMethodCallDelegate:instance channel:channel]; - - FlutterEventChannel* streamChannel = - [FlutterEventChannel eventChannelWithName:@"plugins.flutter.io/connectivity_status" - binaryMessenger:[registrar messenger]]; - [streamChannel setStreamHandler:instance]; -} - -- (NSString*)statusFromReachability:(Reachability*)reachability { - NetworkStatus status = [reachability currentReachabilityStatus]; - switch (status) { - case NotReachable: - return @"none"; - case ReachableViaWiFi: - return @"wifi"; - case ReachableViaWWAN: - return @"mobile"; - } -} - -- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { - if ([call.method isEqualToString:@"check"]) { - // This is supposed to be quick. Another way of doing this would be to - // signup for network - // connectivity changes. However that depends on the app being in background - // and the code - // gets more involved. So for now, this will do. - result([self statusFromReachability:[Reachability reachabilityForInternetConnection]]); - } else { - result(FlutterMethodNotImplemented); - } -} - -- (void)onReachabilityDidChange:(NSNotification*)notification { - Reachability* curReach = [notification object]; - _eventSink([self statusFromReachability:curReach]); -} - -#pragma mark FlutterStreamHandler impl - -- (FlutterError*)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)eventSink { - _eventSink = eventSink; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(onReachabilityDidChange:) - name:kReachabilityChangedNotification - object:nil]; - _reachabilityForInternetConnection = [Reachability reachabilityForInternetConnection]; - [_reachabilityForInternetConnection startNotifier]; - return nil; -} - -- (FlutterError*)onCancelWithArguments:(id)arguments { - if (_reachabilityForInternetConnection) { - [_reachabilityForInternetConnection stopNotifier]; - _reachabilityForInternetConnection = nil; - } - [[NSNotificationCenter defaultCenter] removeObserver:self]; - _eventSink = nil; - return nil; -} - -@end diff --git a/packages/connectivity/connectivity/ios/connectivity.podspec b/packages/connectivity/connectivity/ios/connectivity.podspec deleted file mode 100644 index 096a76b8d3c3..000000000000 --- a/packages/connectivity/connectivity/ios/connectivity.podspec +++ /dev/null @@ -1,23 +0,0 @@ -# -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html -# -Pod::Spec.new do |s| - s.name = 'connectivity' - s.version = '0.0.1' - s.summary = 'Flutter Connectivity' - s.description = <<-DESC -This plugin allows Flutter apps to discover network connectivity and configure themselves accordingly. -Downloaded by pub (not CocoaPods). - DESC - s.homepage = 'https://github.com/flutter/plugins' - s.license = { :type => 'BSD', :file => '../LICENSE' } - s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/connectivity/connectivity' } - s.documentation_url = 'https://pub.dev/packages/connectivity' - s.source_files = 'Classes/**/*' - s.public_header_files = 'Classes/**/*.h' - s.dependency 'Flutter' - s.dependency 'Reachability' - s.platform = :ios, '8.0' - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' } -end diff --git a/packages/connectivity/connectivity/lib/connectivity.dart b/packages/connectivity/connectivity/lib/connectivity.dart deleted file mode 100644 index 1b819d7470d2..000000000000 --- a/packages/connectivity/connectivity/lib/connectivity.dart +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; - -import 'package:connectivity_platform_interface/connectivity_platform_interface.dart'; - -// Export enums from the platform_interface so plugin users can use them directly. -export 'package:connectivity_platform_interface/connectivity_platform_interface.dart' - show ConnectivityResult, LocationAuthorizationStatus; - -/// Discover network connectivity configurations: Distinguish between WI-FI and cellular, check WI-FI status and more. -class Connectivity { - /// Constructs a singleton instance of [Connectivity]. - /// - /// [Connectivity] is designed to work as a singleton. - // When a second instance is created, the first instance will not be able to listen to the - // EventChannel because it is overridden. Forcing the class to be a singleton class can prevent - // misuse of creating a second instance from a programmer. - factory Connectivity() { - if (_singleton == null) { - _singleton = Connectivity._(); - } - return _singleton!; - } - - Connectivity._(); - - static Connectivity? _singleton; - - static ConnectivityPlatform get _platform => ConnectivityPlatform.instance; - - /// Fires whenever the connectivity state changes. - Stream get onConnectivityChanged { - return _platform.onConnectivityChanged; - } - - /// Checks the connection status of the device. - /// - /// Do not use the result of this function to decide whether you can reliably - /// make a network request. It only gives you the radio status. - /// - /// Instead listen for connectivity changes via [onConnectivityChanged] stream. - Future checkConnectivity() { - return _platform.checkConnectivity(); - } -} diff --git a/packages/connectivity/connectivity/pubspec.yaml b/packages/connectivity/connectivity/pubspec.yaml deleted file mode 100644 index 16e179cfa085..000000000000 --- a/packages/connectivity/connectivity/pubspec.yaml +++ /dev/null @@ -1,40 +0,0 @@ -name: connectivity -description: Flutter plugin for discovering the state of the network (WiFi & - mobile/cellular) connectivity on Android and iOS. -repository: https://github.com/flutter/plugins/tree/master/packages/connectivity/connectivity -issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+connectivity%22 -version: 3.0.6 - -environment: - sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.12.13+hotfix.5" - -flutter: - plugin: - platforms: - android: - package: io.flutter.plugins.connectivity - pluginClass: ConnectivityPlugin - ios: - pluginClass: FLTConnectivityPlugin - macos: - default_package: connectivity_macos - web: - default_package: connectivity_for_web - -dependencies: - flutter: - sdk: flutter - meta: ^1.3.0 - connectivity_platform_interface: ^2.0.0 - connectivity_macos: ^0.2.0 - connectivity_for_web: ^0.4.0 - -dev_dependencies: - flutter_test: - sdk: flutter - flutter_driver: - sdk: flutter - plugin_platform_interface: ^2.0.0 - pedantic: ^1.10.0 - test: ^1.16.3 diff --git a/packages/connectivity/connectivity/test/connectivity_test.dart b/packages/connectivity/connectivity/test/connectivity_test.dart deleted file mode 100644 index e6c253e0d49a..000000000000 --- a/packages/connectivity/connectivity/test/connectivity_test.dart +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:connectivity/connectivity.dart'; -import 'package:connectivity_platform_interface/connectivity_platform_interface.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -import 'package:test/fake.dart'; - -const ConnectivityResult kCheckConnectivityResult = ConnectivityResult.wifi; -const LocationAuthorizationStatus kRequestLocationResult = - LocationAuthorizationStatus.authorizedAlways; -const LocationAuthorizationStatus kGetLocationResult = - LocationAuthorizationStatus.authorizedAlways; - -void main() { - group('Connectivity', () { - late Connectivity connectivity; - late MockConnectivityPlatform fakePlatform; - setUp(() async { - fakePlatform = MockConnectivityPlatform(); - ConnectivityPlatform.instance = fakePlatform; - connectivity = Connectivity(); - }); - - test('checkConnectivity', () async { - ConnectivityResult result = await connectivity.checkConnectivity(); - expect(result, kCheckConnectivityResult); - }); - }); -} - -class MockConnectivityPlatform extends Fake - with MockPlatformInterfaceMixin - implements ConnectivityPlatform { - Future checkConnectivity() async { - return kCheckConnectivityResult; - } -} diff --git a/packages/connectivity/connectivity_for_web/.gitignore b/packages/connectivity/connectivity_for_web/.gitignore deleted file mode 100644 index d7dee828a6b9..000000000000 --- a/packages/connectivity/connectivity_for_web/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -.DS_Store -.dart_tool/ - -.packages -.pub/ - -build/ -lib/generated_plugin_registrant.dart diff --git a/packages/connectivity/connectivity_for_web/.metadata b/packages/connectivity/connectivity_for_web/.metadata deleted file mode 100644 index 23eb55ba6da2..000000000000 --- a/packages/connectivity/connectivity_for_web/.metadata +++ /dev/null @@ -1,10 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: 52ee8a6c6565cd421dfa32042941eb40691f4746 - channel: master - -project_type: plugin diff --git a/packages/connectivity/connectivity_for_web/AUTHORS b/packages/connectivity/connectivity_for_web/AUTHORS deleted file mode 100644 index 493a0b4ef9c2..000000000000 --- a/packages/connectivity/connectivity_for_web/AUTHORS +++ /dev/null @@ -1,66 +0,0 @@ -# Below is a list of people and organizations that have contributed -# to the Flutter project. Names should be added to the list like so: -# -# Name/Organization - -Google Inc. -The Chromium Authors -German Saprykin -Benjamin Sauer -larsenthomasj@gmail.com -Ali Bitek -Pol Batlló -Anatoly Pulyaevskiy -Hayden Flinner -Stefano Rodriguez -Salvatore Giordano -Brian Armstrong -Paul DeMarco -Fabricio Nogueira -Simon Lightfoot -Ashton Thomas -Thomas Danner -Diego Velásquez -Hajime Nakamura -Tuyển Vũ Xuân -Miguel Ruivo -Sarthak Verma -Mike Diarmid -Invertase -Elliot Hesp -Vince Varga -Aawaz Gyawali -EUI Limited -Katarina Sheremet -Thomas Stockx -Sarbagya Dhaubanjar -Ozkan Eksi -Rishab Nayak -ko2ic -Jonathan Younger -Jose Sanchez -Debkanchan Samadder -Audrius Karosevicius -Lukasz Piliszczuk -SoundReply Solutions GmbH -Rafal Wachol -Pau Picas -Christian Weder -Alexandru Tuca -Christian Weder -Rhodes Davis Jr. -Luigi Agosti -Quentin Le Guennec -Koushik Ravikumar -Nissim Dsilva -Giancarlo Rocha -Ryo Miyake -Théo Champion -Kazuki Yamaguchi -Eitan Schwartz -Chris Rutkowski -Juan Alvarez -Aleksandr Yurkovskiy -Anton Borries -Alex Li -Rahul Raj <64.rahulraj@gmail.com> diff --git a/packages/connectivity/connectivity_for_web/CHANGELOG.md b/packages/connectivity/connectivity_for_web/CHANGELOG.md deleted file mode 100644 index 97e5032c8dd4..000000000000 --- a/packages/connectivity/connectivity_for_web/CHANGELOG.md +++ /dev/null @@ -1,41 +0,0 @@ -## 0.4.0+1 - -* Add `implements` to pubspec. - -## 0.4.0 - -* Migrate to null-safety -* Run tests using flutter driver - -## 0.3.1+4 - -* Remove unused `test` dependency. - -## 0.3.1+3 - -* Fix homepage in `pubspec.yaml`. - -## 0.3.1+2 - -* Update package:e2e to use package:integration_test - -## 0.3.1+1 - -* Update package:e2e reference to use the local version in the flutter/plugins - repository. - -## 0.3.1 - -* Use NetworkInformation API from dart:html, instead of the JS-interop version. - -## 0.3.0 - -* Rename from "experimental_connectivity_web" to "connectivity_for_web", and move to flutter/plugins master. - -## 0.2.0 - -* Add fallback on dart:html for browsers where NetworkInformationAPI is not supported. - -## 0.1.0 - -* Initial release. diff --git a/packages/connectivity/connectivity_for_web/LICENSE b/packages/connectivity/connectivity_for_web/LICENSE deleted file mode 100644 index c6823b81eb84..000000000000 --- a/packages/connectivity/connectivity_for_web/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright 2013 The Flutter Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/connectivity/connectivity_for_web/README.md b/packages/connectivity/connectivity_for_web/README.md deleted file mode 100644 index 66efc49fc840..000000000000 --- a/packages/connectivity/connectivity_for_web/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# connectivity_for_web - -A web implementation of [connectivity](https://pub.dev/connectivity/connectivity). Currently this package uses an experimental API, with a fallback to dart:html, so not all features may be available to all browsers. - -## Usage - -### Import the package - -This package is a non-endorsed implementation of `connectivity` for the web platform, so you need to modify your `pubspec.yaml` to use it: - -```yaml -... -dependencies: - ... - connectivity: ^0.4.9 - connectivity_for_web: ^0.3.0 - ... -... -``` - -## Example - -Find the example wiring in the [Google sign-in example application](https://github.com/ditman/plugins/blob/connectivity-web/packages/connectivity/connectivity/example/lib/main.dart). - -## Limitations on the web platform - -In order to retrieve information about the quality/speed of a browser's connection, the web implementation of the `connectivity` plugin uses the browser's [**NetworkInformation** Web API](https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation), which as of this writing (June 2020) is still "experimental", and not available in all browsers: - -![Data on support for the netinfo feature across the major browsers from caniuse.com](https://caniuse.bitsofco.de/image/netinfo.png) - -On desktop browsers, this API only returns a very broad set of connectivity statuses (One of `'slow-2g', '2g', '3g', or '4g'`), and may *not* provide a Stream of changes. Firefox still hasn't enabled this feature by default. - -**Fallback to `navigator.onLine`** - -For those browsers where the NetworkInformation Web API is not available, the plugin falls back to the [**NavigatorOnLine** Web API](https://developer.mozilla.org/en-US/docs/Web/API/NavigatorOnLine), which is more broadly supported: - -![Data on support for the online-status feature across the major browsers from caniuse.com](https://caniuse.bitsofco.de/image/online-status.png) - - -The NavigatorOnLine API is [provided by `dart:html`](https://api.dart.dev/stable/2.7.2/dart-html/Navigator/onLine.html), and only supports a boolean connectivity status (either online or offline), with no network speed information. In those cases the plugin will return either `wifi` (when the browser is online) or `none` (when it's not). - -Other than the approximate "downlink" speed, where available, and due to security and privacy concerns, **no Web browser will provide** any specific information about the actual network your users' device is connected to, like **the SSID on a Wi-Fi, or the MAC address of their device.** - -## Contributions and Testing - -Tests are crucial to contributions to this package. All new contributions should be reasonably tested. - -In order to run tests in this package, do: - -``` -cd test -flutter run -d chrome -``` - -All contributions to this package are welcome. Read the [Contributing to Flutter Plugins](https://github.com/flutter/plugins/blob/master/CONTRIBUTING.md) guide to get started. - -## Issues and feedback - -Please file an [issue](https://github.com/ditman/plugins/issues/new) -to send feedback or report a bug. - -**Thank you!** diff --git a/packages/connectivity/connectivity_for_web/example/README.md b/packages/connectivity/connectivity_for_web/example/README.md deleted file mode 100644 index 8a6e74b107ea..000000000000 --- a/packages/connectivity/connectivity_for_web/example/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Testing - -This package uses `package:integration_test` to run its tests in a web browser. - -See [Plugin Tests > Web Tests](https://github.com/flutter/flutter/wiki/Plugin-Tests#web-tests) -in the Flutter wiki for instructions to setup and run the tests in this package. - -Check [flutter.dev > Integration testing](https://flutter.dev/docs/testing/integration-tests) -for more info. \ No newline at end of file diff --git a/packages/connectivity/connectivity_for_web/example/integration_test/network_information_test.dart b/packages/connectivity/connectivity_for_web/example/integration_test/network_information_test.dart deleted file mode 100644 index e6faa30da4dc..000000000000 --- a/packages/connectivity/connectivity_for_web/example/integration_test/network_information_test.dart +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:connectivity_for_web/src/network_information_api_connectivity_plugin.dart'; -import 'package:connectivity_platform_interface/connectivity_platform_interface.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:integration_test/integration_test.dart'; - -import 'src/connectivity_mocks.dart'; - -void main() { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - group('checkConnectivity', () { - void testCheckConnectivity({ - String? type, - String? effectiveType, - num? downlink = 10, - int? rtt = 50, - required ConnectivityResult expected, - }) { - final connection = FakeNetworkInformation( - type: type, - effectiveType: effectiveType, - downlink: downlink, - rtt: rtt, - ); - - NetworkInformationApiConnectivityPlugin plugin = - NetworkInformationApiConnectivityPlugin.withConnection(connection); - expect(plugin.checkConnectivity(), completion(equals(expected))); - } - - testWidgets('0 downlink and rtt -> none', (WidgetTester tester) async { - testCheckConnectivity( - effectiveType: '4g', - downlink: 0, - rtt: 0, - expected: ConnectivityResult.none); - }); - testWidgets('slow-2g -> mobile', (WidgetTester tester) async { - testCheckConnectivity( - effectiveType: 'slow-2g', expected: ConnectivityResult.mobile); - }); - testWidgets('2g -> mobile', (WidgetTester tester) async { - testCheckConnectivity( - effectiveType: '2g', expected: ConnectivityResult.mobile); - }); - testWidgets('3g -> mobile', (WidgetTester tester) async { - testCheckConnectivity( - effectiveType: '3g', expected: ConnectivityResult.mobile); - }); - testWidgets('4g -> wifi', (WidgetTester tester) async { - testCheckConnectivity( - effectiveType: '4g', expected: ConnectivityResult.wifi); - }); - }); - - group('get onConnectivityChanged', () { - testWidgets('puts change events in a Stream', (WidgetTester tester) async { - final connection = FakeNetworkInformation(); - NetworkInformationApiConnectivityPlugin plugin = - NetworkInformationApiConnectivityPlugin.withConnection(connection); - - // The onConnectivityChanged stream is infinite, so we only .take(2) so the test completes. - // We need to do .toList() now, because otherwise the Stream won't be actually listened to, - // and we'll miss the calls to mockChangeValue below. - final results = plugin.onConnectivityChanged.take(2).toList(); - - // Fake a disconnect-reconnect - await connection.mockChangeValue(downlink: 0, rtt: 0); - await connection.mockChangeValue( - downlink: 10, rtt: 50, effectiveType: '4g'); - - // Expect to see the disconnect-reconnect in the resulting stream. - expect( - results, - completion([ConnectivityResult.none, ConnectivityResult.wifi]), - ); - }); - }); -} diff --git a/packages/connectivity/connectivity_for_web/example/integration_test/src/connectivity_mocks.dart b/packages/connectivity/connectivity_for_web/example/integration_test/src/connectivity_mocks.dart deleted file mode 100644 index 556b6fe6fca0..000000000000 --- a/packages/connectivity/connectivity_for_web/example/integration_test/src/connectivity_mocks.dart +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; -import 'dart:html'; -import 'dart:js_util' show getProperty; - -import 'package:flutter_test/flutter_test.dart'; - -/// A Fake implementation of the NetworkInformation API that allows -/// for external modification of its values. -/// -/// Note that the DOM API works by internally mutating and broadcasting -/// 'change' events. -class FakeNetworkInformation extends Fake implements NetworkInformation { - String? _type; - String? _effectiveType; - num? _downlink; - int? _rtt; - - @override - String? get type => _type; - - @override - String? get effectiveType => _effectiveType; - - @override - num? get downlink => _downlink; - - @override - int? get rtt => _rtt; - - FakeNetworkInformation({ - String? type, - String? effectiveType, - num? downlink, - int? rtt, - }) : this._type = type, - this._effectiveType = effectiveType, - this._downlink = downlink, - this._rtt = rtt; - - /// Changes the desired values, and triggers the change event listener. - Future mockChangeValue({ - String? type, - String? effectiveType, - num? downlink, - int? rtt, - }) async { - this._type = type; - this._effectiveType = effectiveType; - this._downlink = downlink; - this._rtt = rtt; - - // This is set by the onConnectivityChanged getter... - final Function onchange = getProperty(this, 'onchange') as Function; - onchange(Event('change')); - } -} diff --git a/packages/connectivity/connectivity_for_web/example/lib/main.dart b/packages/connectivity/connectivity_for_web/example/lib/main.dart deleted file mode 100644 index e1a38dcdcd46..000000000000 --- a/packages/connectivity/connectivity_for_web/example/lib/main.dart +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; - -void main() { - runApp(MyApp()); -} - -/// App for testing -class MyApp extends StatefulWidget { - @override - _MyAppState createState() => _MyAppState(); -} - -class _MyAppState extends State { - @override - Widget build(BuildContext context) { - return Directionality( - textDirection: TextDirection.ltr, - child: Text('Testing... Look at the console output for results!'), - ); - } -} diff --git a/packages/connectivity/connectivity_for_web/example/pubspec.yaml b/packages/connectivity/connectivity_for_web/example/pubspec.yaml deleted file mode 100644 index 3b8e209e2486..000000000000 --- a/packages/connectivity/connectivity_for_web/example/pubspec.yaml +++ /dev/null @@ -1,21 +0,0 @@ -name: connectivity_for_web_integration_tests -publish_to: none - -environment: - sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.2.0" - -dependencies: - connectivity_for_web: - path: ../ - flutter: - sdk: flutter - -dev_dependencies: - js: ^0.6.3 - flutter_test: - sdk: flutter - flutter_driver: - sdk: flutter - integration_test: - sdk: flutter diff --git a/packages/connectivity/connectivity_for_web/example/run_test.sh b/packages/connectivity/connectivity_for_web/example/run_test.sh deleted file mode 100755 index aa52974f310e..000000000000 --- a/packages/connectivity/connectivity_for_web/example/run_test.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/bash -# 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. - -if pgrep -lf chromedriver > /dev/null; then - echo "chromedriver is running." - - if [ $# -eq 0 ]; then - echo "No target specified, running all tests..." - find integration_test/ -iname *_test.dart | xargs -n1 -i -t flutter drive -d web-server --web-port=7357 --browser-name=chrome --driver=test_driver/integration_test.dart --target='{}' - else - echo "Running test target: $1..." - set -x - flutter drive -d web-server --web-port=7357 --browser-name=chrome --driver=test_driver/integration_test.dart --target=$1 - fi - - else - echo "chromedriver is not running." - echo "Please, check the README.md for instructions on how to use run_test.sh" -fi - diff --git a/packages/connectivity/connectivity_for_web/example/test_driver/integration_test.dart b/packages/connectivity/connectivity_for_web/example/test_driver/integration_test.dart deleted file mode 100644 index 4f10f2a522f3..000000000000 --- a/packages/connectivity/connectivity_for_web/example/test_driver/integration_test.dart +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:integration_test/integration_test_driver.dart'; - -Future main() => integrationDriver(); diff --git a/packages/connectivity/connectivity_for_web/example/web/index.html b/packages/connectivity/connectivity_for_web/example/web/index.html deleted file mode 100644 index 7fb138cc90fa..000000000000 --- a/packages/connectivity/connectivity_for_web/example/web/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - example - - - - - diff --git a/packages/connectivity/connectivity_for_web/lib/connectivity_for_web.dart b/packages/connectivity/connectivity_for_web/lib/connectivity_for_web.dart deleted file mode 100644 index d1c6811f5349..000000000000 --- a/packages/connectivity/connectivity_for_web/lib/connectivity_for_web.dart +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; - -import 'package:connectivity_platform_interface/connectivity_platform_interface.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_web_plugins/flutter_web_plugins.dart'; - -import 'src/network_information_api_connectivity_plugin.dart'; -import 'src/dart_html_connectivity_plugin.dart'; - -/// The web implementation of the ConnectivityPlatform of the Connectivity plugin. -class ConnectivityPlugin extends ConnectivityPlatform { - /// Factory method that initializes the connectivity plugin platform with an instance - /// of the plugin for the web. - static void registerWith(Registrar registrar) { - if (NetworkInformationApiConnectivityPlugin.isSupported()) { - ConnectivityPlatform.instance = NetworkInformationApiConnectivityPlugin(); - } else { - ConnectivityPlatform.instance = DartHtmlConnectivityPlugin(); - } - } - - // The following are completely unsupported methods on the web platform. - - // Creates an "unsupported_operation" PlatformException for a given `method` name. - Object _unsupported(String method) { - return PlatformException( - code: 'UNSUPPORTED_OPERATION', - message: '$method() is not supported on the web platform.', - ); - } - - /// Obtains the wifi name (SSID) of the connected network - @override - Future getWifiName() { - throw _unsupported('getWifiName'); - } - - /// Obtains the wifi BSSID of the connected network. - @override - Future getWifiBSSID() { - throw _unsupported('getWifiBSSID'); - } - - /// Obtains the IP address of the connected wifi network - @override - Future getWifiIP() { - throw _unsupported('getWifiIP'); - } - - /// Request to authorize the location service (Only on iOS). - @override - Future requestLocationServiceAuthorization({ - bool requestAlwaysLocationUsage = false, - }) { - throw _unsupported('requestLocationServiceAuthorization'); - } - - /// Get the current location service authorization (Only on iOS). - @override - Future getLocationServiceAuthorization() { - throw _unsupported('getLocationServiceAuthorization'); - } -} diff --git a/packages/connectivity/connectivity_for_web/lib/src/dart_html_connectivity_plugin.dart b/packages/connectivity/connectivity_for_web/lib/src/dart_html_connectivity_plugin.dart deleted file mode 100644 index 475ec0d675b7..000000000000 --- a/packages/connectivity/connectivity_for_web/lib/src/dart_html_connectivity_plugin.dart +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; -import 'dart:html' as html show window; - -import 'package:connectivity_platform_interface/connectivity_platform_interface.dart'; -import 'package:connectivity_for_web/connectivity_for_web.dart'; - -/// The web implementation of the ConnectivityPlatform of the Connectivity plugin. -class DartHtmlConnectivityPlugin extends ConnectivityPlugin { - /// Checks the connection status of the device. - @override - Future checkConnectivity() async { - return html.window.navigator.onLine ?? false - ? ConnectivityResult.wifi - : ConnectivityResult.none; - } - - StreamController? _connectivityResult; - - /// Returns a Stream of ConnectivityResults changes. - @override - Stream get onConnectivityChanged { - if (_connectivityResult == null) { - _connectivityResult = StreamController.broadcast(); - // Fallback to dart:html window.onOnline / window.onOffline - html.window.onOnline.listen((event) { - _connectivityResult!.add(ConnectivityResult.wifi); - }); - html.window.onOffline.listen((event) { - _connectivityResult!.add(ConnectivityResult.none); - }); - } - return _connectivityResult!.stream; - } -} diff --git a/packages/connectivity/connectivity_for_web/lib/src/network_information_api_connectivity_plugin.dart b/packages/connectivity/connectivity_for_web/lib/src/network_information_api_connectivity_plugin.dart deleted file mode 100644 index 6554f7a8c124..000000000000 --- a/packages/connectivity/connectivity_for_web/lib/src/network_information_api_connectivity_plugin.dart +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; -import 'dart:html' as html show window, NetworkInformation; -import 'dart:js' show allowInterop; -import 'dart:js_util' show setProperty; - -import 'package:connectivity_platform_interface/connectivity_platform_interface.dart'; -import 'package:connectivity_for_web/connectivity_for_web.dart'; -import 'package:flutter/foundation.dart'; - -import 'utils/connectivity_result.dart'; - -/// The web implementation of the ConnectivityPlatform of the Connectivity plugin. -class NetworkInformationApiConnectivityPlugin extends ConnectivityPlugin { - final html.NetworkInformation _networkInformation; - - /// A check to determine if this version of the plugin can be used. - static bool isSupported() => html.window.navigator.connection != null; - - /// The constructor of the plugin. - NetworkInformationApiConnectivityPlugin() - : this.withConnection(html.window.navigator.connection!); - - /// Creates the plugin, with an override of the NetworkInformation object. - @visibleForTesting - NetworkInformationApiConnectivityPlugin.withConnection( - html.NetworkInformation connection) - : _networkInformation = connection; - - /// Checks the connection status of the device. - @override - Future checkConnectivity() async { - return networkInformationToConnectivityResult(_networkInformation); - } - - StreamController? _connectivityResultStreamController; - late Stream _connectivityResultStream; - - /// Returns a Stream of ConnectivityResults changes. - @override - Stream get onConnectivityChanged { - if (_connectivityResultStreamController == null) { - _connectivityResultStreamController = - StreamController(); - - // Directly write the 'onchange' function on the networkInformation object. - setProperty(_networkInformation, 'onchange', allowInterop((_) { - _connectivityResultStreamController! - .add(networkInformationToConnectivityResult(_networkInformation)); - })); - // TODO: Implement the above with _networkInformation.onChange: - // _networkInformation.onChange.listen((_) { - // _connectivityResult - // .add(networkInformationToConnectivityResult(_networkInformation)); - // }); - // Once we can detect when to *cancel* a subscription to the _networkInformation - // onChange Stream upon hot restart. - // https://github.com/dart-lang/sdk/issues/42679 - _connectivityResultStream = - _connectivityResultStreamController!.stream.asBroadcastStream(); - } - return _connectivityResultStream; - } -} diff --git a/packages/connectivity/connectivity_for_web/lib/src/utils/connectivity_result.dart b/packages/connectivity/connectivity_for_web/lib/src/utils/connectivity_result.dart deleted file mode 100644 index 691bd6da3bfb..000000000000 --- a/packages/connectivity/connectivity_for_web/lib/src/utils/connectivity_result.dart +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:html' as html show NetworkInformation; -import 'package:connectivity_platform_interface/connectivity_platform_interface.dart'; - -/// Converts an incoming NetworkInformation object into the correct ConnectivityResult. -ConnectivityResult networkInformationToConnectivityResult( - html.NetworkInformation? info, -) { - if (info == null) { - return ConnectivityResult.none; - } - if (info.downlink == 0 && info.rtt == 0) { - return ConnectivityResult.none; - } - if (info.effectiveType != null) { - return _effectiveTypeToConnectivityResult(info.effectiveType!); - } - if (info.type != null) { - return _typeToConnectivityResult(info.type!); - } - return ConnectivityResult.none; -} - -ConnectivityResult _effectiveTypeToConnectivityResult(String effectiveType) { - // Possible values: - /*'2g'|'3g'|'4g'|'slow-2g'*/ - switch (effectiveType) { - case 'slow-2g': - case '2g': - case '3g': - return ConnectivityResult.mobile; - default: - return ConnectivityResult.wifi; - } -} - -ConnectivityResult _typeToConnectivityResult(String type) { - // Possible values: - /*'bluetooth'|'cellular'|'ethernet'|'mixed'|'none'|'other'|'unknown'|'wifi'|'wimax'*/ - switch (type) { - case 'none': - return ConnectivityResult.none; - case 'bluetooth': - case 'cellular': - case 'mixed': - case 'other': - case 'unknown': - return ConnectivityResult.mobile; - default: - return ConnectivityResult.wifi; - } -} diff --git a/packages/connectivity/connectivity_for_web/pubspec.yaml b/packages/connectivity/connectivity_for_web/pubspec.yaml deleted file mode 100644 index 2aaa8bd978fa..000000000000 --- a/packages/connectivity/connectivity_for_web/pubspec.yaml +++ /dev/null @@ -1,28 +0,0 @@ -name: connectivity_for_web -description: An implementation for the web platform of the Flutter `connectivity` plugin. This uses the NetworkInformation Web API, with a fallback to Navigator.onLine. -repository: https://github.com/flutter/plugins/tree/master/packages/connectivity/connectivity_for_web -issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+connectivity%22 -version: 0.4.0+1 - -environment: - sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" - -flutter: - plugin: - implements: connectivity - platforms: - web: - pluginClass: ConnectivityPlugin - fileName: connectivity_for_web.dart - -dependencies: - connectivity_platform_interface: ^2.0.0 - flutter_web_plugins: - sdk: flutter - flutter: - sdk: flutter - -dev_dependencies: - flutter_test: - sdk: flutter diff --git a/packages/connectivity/connectivity_for_web/test/README.md b/packages/connectivity/connectivity_for_web/test/README.md deleted file mode 100644 index 7c5b4ad682ba..000000000000 --- a/packages/connectivity/connectivity_for_web/test/README.md +++ /dev/null @@ -1,5 +0,0 @@ -## test - -This package uses integration tests for testing. - -See `example/README.md` for more info. diff --git a/packages/connectivity/connectivity_for_web/test/tests_exist_elsewhere_test.dart b/packages/connectivity/connectivity_for_web/test/tests_exist_elsewhere_test.dart deleted file mode 100644 index 442c50144727..000000000000 --- a/packages/connectivity/connectivity_for_web/test/tests_exist_elsewhere_test.dart +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter_test/flutter_test.dart'; - -void main() { - test('Tell the user where to find the real tests', () { - print('---'); - print('This package uses integration_test for its tests.'); - print('See `example/README.md` for more info.'); - print('---'); - }); -} diff --git a/packages/connectivity/connectivity_macos/AUTHORS b/packages/connectivity/connectivity_macos/AUTHORS deleted file mode 100644 index 493a0b4ef9c2..000000000000 --- a/packages/connectivity/connectivity_macos/AUTHORS +++ /dev/null @@ -1,66 +0,0 @@ -# Below is a list of people and organizations that have contributed -# to the Flutter project. Names should be added to the list like so: -# -# Name/Organization - -Google Inc. -The Chromium Authors -German Saprykin -Benjamin Sauer -larsenthomasj@gmail.com -Ali Bitek -Pol Batlló -Anatoly Pulyaevskiy -Hayden Flinner -Stefano Rodriguez -Salvatore Giordano -Brian Armstrong -Paul DeMarco -Fabricio Nogueira -Simon Lightfoot -Ashton Thomas -Thomas Danner -Diego Velásquez -Hajime Nakamura -Tuyển Vũ Xuân -Miguel Ruivo -Sarthak Verma -Mike Diarmid -Invertase -Elliot Hesp -Vince Varga -Aawaz Gyawali -EUI Limited -Katarina Sheremet -Thomas Stockx -Sarbagya Dhaubanjar -Ozkan Eksi -Rishab Nayak -ko2ic -Jonathan Younger -Jose Sanchez -Debkanchan Samadder -Audrius Karosevicius -Lukasz Piliszczuk -SoundReply Solutions GmbH -Rafal Wachol -Pau Picas -Christian Weder -Alexandru Tuca -Christian Weder -Rhodes Davis Jr. -Luigi Agosti -Quentin Le Guennec -Koushik Ravikumar -Nissim Dsilva -Giancarlo Rocha -Ryo Miyake -Théo Champion -Kazuki Yamaguchi -Eitan Schwartz -Chris Rutkowski -Juan Alvarez -Aleksandr Yurkovskiy -Anton Borries -Alex Li -Rahul Raj <64.rahulraj@gmail.com> diff --git a/packages/connectivity/connectivity_macos/CHANGELOG.md b/packages/connectivity/connectivity_macos/CHANGELOG.md deleted file mode 100644 index 46a4038f91ee..000000000000 --- a/packages/connectivity/connectivity_macos/CHANGELOG.md +++ /dev/null @@ -1,69 +0,0 @@ -## 0.2.1+2 - -* Add Swift language version to podspec. -* Fix `implements` package name in pubspec. - -## 0.2.1+1 - -* Ignore Reachability pointer to int cast warning. - -## 0.2.1 - -* Add `implements` to pubspec.yaml. - -## 0.2.0 - -* Remove placeholder Dart file. -* Update Dart SDK constraint for compatibility with null safety. - -## 0.1.0+8 - -* Update Flutter SDK constraint. - -## 0.1.0+7 - -* Remove unused `test` dependency. -* Update Dart SDK constraint in example. - -## 0.1.0+6 - -* Update license headers. - -## 0.1.0+5 - -* Update package:e2e reference to use the local version in the flutter/plugins - repository. -* Remove no-op android folder in the example app. - -## 0.1.0+4 - -* Remove Android folder from `connectivity_macos`. - -## 0.1.0+3 - -* Bump the minimum Flutter version to 1.12.13+hotfix.5. -* Clean up various Android workarounds no longer needed after framework v1.12. -* Complete v2 embedding support. -* Fix CocoaPods podspec lint warnings. -* Declare API stability and compatibility with `1.0.0` (more details at: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0). - -## 0.1.0+2 - -* Remove hard coded ios workspace setting of the example app. - -## 0.1.0+1 - -* Make the pedantic dev_dependency explicit. - -## 0.1.0 - -* Adds an example app to trigger CI tests. Bumped the MINOR version to -avoid compatibility issues once this packages is endorsed. - -## 0.0.2+1 - -* Add CHANGELOG. - -## 0.0.1 - -* Initial open source release. diff --git a/packages/connectivity/connectivity_macos/LICENSE b/packages/connectivity/connectivity_macos/LICENSE deleted file mode 100644 index c6823b81eb84..000000000000 --- a/packages/connectivity/connectivity_macos/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright 2013 The Flutter Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/connectivity/connectivity_macos/README.md b/packages/connectivity/connectivity_macos/README.md deleted file mode 100644 index 6974fd1fcc7e..000000000000 --- a/packages/connectivity/connectivity_macos/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# connectivity_macos - -The macos implementation of [`connectivity`]. - -## Usage - -### Import the package - - -This package has been endorsed, meaning that you only need to add `connectivity` -as a dependency in your `pubspec.yaml`. It will be automatically included in your app -when you depend on `package:connectivity`. - -This is what the above means to your `pubspec.yaml`: - -```yaml -... -dependencies: - ... - connectivity: ^0.4.8 - ... -``` - -Refer to the `connectivity` [documentation](https://github.com/flutter/plugins/tree/master/packages/connectivity/connectivity) for more details. diff --git a/packages/connectivity/connectivity_macos/example/integration_test/connectivity_test.dart b/packages/connectivity/connectivity_macos/example/integration_test/connectivity_test.dart deleted file mode 100644 index 3e2b1c008a84..000000000000 --- a/packages/connectivity/connectivity_macos/example/integration_test/connectivity_test.dart +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:io'; -import 'package:integration_test/integration_test.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:connectivity_platform_interface/connectivity_platform_interface.dart'; - -void main() { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - group('Connectivity test driver', () { - late ConnectivityPlatform _connectivity; - - setUpAll(() async { - _connectivity = ConnectivityPlatform.instance; - }); - - testWidgets('test connectivity result', (WidgetTester tester) async { - final ConnectivityResult result = await _connectivity.checkConnectivity(); - expect(result, isNotNull); - switch (result) { - case ConnectivityResult.wifi: - expect(_connectivity.getWifiName(), completes); - expect(_connectivity.getWifiBSSID(), completes); - expect((await _connectivity.getWifiIP()), isNotNull); - break; - default: - break; - } - }); - - testWidgets('test location methods, iOS only', (WidgetTester tester) async { - if (Platform.isIOS) { - expect((await _connectivity.getLocationServiceAuthorization()), - LocationAuthorizationStatus.notDetermined); - } - }); - }); -} diff --git a/packages/connectivity/connectivity_macos/example/lib/main.dart b/packages/connectivity/connectivity_macos/example/lib/main.dart deleted file mode 100644 index d0e07341fe92..000000000000 --- a/packages/connectivity/connectivity_macos/example/lib/main.dart +++ /dev/null @@ -1,172 +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. - -// ignore_for_file: public_member_api_docs - -import 'dart:async'; -import 'dart:io'; - -import 'package:connectivity_platform_interface/connectivity_platform_interface.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -// Sets a platform override for desktop to avoid exceptions. See -// https://flutter.dev/desktop#target-platform-override for more info. -void _enablePlatformOverrideForDesktop() { - if (!kIsWeb && (Platform.isWindows || Platform.isLinux)) { - debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia; - } -} - -void main() { - _enablePlatformOverrideForDesktop(); - runApp(MyApp()); -} - -class MyApp extends StatelessWidget { - // This widget is the root of your application. - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - primarySwatch: Colors.blue, - ), - home: MyHomePage(title: 'Flutter Demo Home Page'), - ); - } -} - -class MyHomePage extends StatefulWidget { - MyHomePage({Key? key, this.title}) : super(key: key); - - final String? title; - - @override - _MyHomePageState createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - String _connectionStatus = 'Unknown'; - final ConnectivityPlatform _connectivity = ConnectivityPlatform.instance; - late StreamSubscription _connectivitySubscription; - - @override - void initState() { - super.initState(); - initConnectivity(); - _connectivitySubscription = - _connectivity.onConnectivityChanged.listen(_updateConnectionStatus); - } - - @override - void dispose() { - _connectivitySubscription.cancel(); - super.dispose(); - } - - // Platform messages are asynchronous, so we initialize in an async method. - Future initConnectivity() async { - late ConnectivityResult result; - // Platform messages may fail, so we use a try/catch PlatformException. - try { - result = await _connectivity.checkConnectivity(); - } on PlatformException catch (e) { - print(e.toString()); - } - - // If the widget was removed from the tree while the asynchronous platform - // message was in flight, we want to discard the reply rather than calling - // setState to update our non-existent appearance. - if (!mounted) { - return Future.value(null); - } - - return _updateConnectionStatus(result); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('Plugin example app'), - ), - body: Center(child: Text('Connection Status: $_connectionStatus')), - ); - } - - Future _updateConnectionStatus(ConnectivityResult result) async { - switch (result) { - case ConnectivityResult.wifi: - String? wifiName, wifiBSSID, wifiIP; - - try { - if (Platform.isIOS) { - LocationAuthorizationStatus status = - await _connectivity.getLocationServiceAuthorization(); - if (status == LocationAuthorizationStatus.notDetermined) { - status = - await _connectivity.requestLocationServiceAuthorization(); - } - if (status == LocationAuthorizationStatus.authorizedAlways || - status == LocationAuthorizationStatus.authorizedWhenInUse) { - wifiName = await _connectivity.getWifiName(); - } else { - wifiName = await _connectivity.getWifiName(); - } - } else { - wifiName = await _connectivity.getWifiName(); - } - } on PlatformException catch (e) { - print(e.toString()); - wifiName = "Failed to get Wifi Name"; - } - - try { - if (Platform.isIOS) { - LocationAuthorizationStatus status = - await _connectivity.getLocationServiceAuthorization(); - if (status == LocationAuthorizationStatus.notDetermined) { - status = - await _connectivity.requestLocationServiceAuthorization(); - } - if (status == LocationAuthorizationStatus.authorizedAlways || - status == LocationAuthorizationStatus.authorizedWhenInUse) { - wifiBSSID = await _connectivity.getWifiBSSID(); - } else { - wifiBSSID = await _connectivity.getWifiBSSID(); - } - } else { - wifiBSSID = await _connectivity.getWifiBSSID(); - } - } on PlatformException catch (e) { - print(e.toString()); - wifiBSSID = "Failed to get Wifi BSSID"; - } - - try { - wifiIP = await _connectivity.getWifiIP(); - } on PlatformException catch (e) { - print(e.toString()); - wifiIP = "Failed to get Wifi IP"; - } - - setState(() { - _connectionStatus = '$result\n' - 'Wifi Name: $wifiName\n' - 'Wifi BSSID: $wifiBSSID\n' - 'Wifi IP: $wifiIP\n'; - }); - break; - case ConnectivityResult.mobile: - case ConnectivityResult.none: - setState(() => _connectionStatus = result.toString()); - break; - default: - setState(() => _connectionStatus = 'Failed to get connectivity.'); - break; - } - } -} diff --git a/packages/connectivity/connectivity_macos/example/macos/Flutter/Flutter-Debug.xcconfig b/packages/connectivity/connectivity_macos/example/macos/Flutter/Flutter-Debug.xcconfig deleted file mode 100644 index 785633d3a86b..000000000000 --- a/packages/connectivity/connectivity_macos/example/macos/Flutter/Flutter-Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" -#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/packages/connectivity/connectivity_macos/example/macos/Flutter/Flutter-Release.xcconfig b/packages/connectivity/connectivity_macos/example/macos/Flutter/Flutter-Release.xcconfig deleted file mode 100644 index 5fba960c3af2..000000000000 --- a/packages/connectivity/connectivity_macos/example/macos/Flutter/Flutter-Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" -#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/packages/connectivity/connectivity_macos/example/macos/Podfile b/packages/connectivity/connectivity_macos/example/macos/Podfile deleted file mode 100644 index e9131e75c4d2..000000000000 --- a/packages/connectivity/connectivity_macos/example/macos/Podfile +++ /dev/null @@ -1,46 +0,0 @@ -platform :osx, '10.11' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_macos_podfile_setup - -target 'Runner' do - use_frameworks! - use_modular_headers! - - flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - # Work around https://github.com/flutter/flutter/issues/82964. - if target.name == 'Reachability' - target.build_configurations.each do |config| - config.build_settings['WARNING_CFLAGS'] = '-Wno-pointer-to-int-cast' - end - end - flutter_additional_macos_build_settings(target) - end -end diff --git a/packages/connectivity/connectivity_macos/example/macos/Runner.xcodeproj/project.pbxproj b/packages/connectivity/connectivity_macos/example/macos/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index 0e2413493f6e..000000000000 --- a/packages/connectivity/connectivity_macos/example/macos/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,654 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 51; - objects = { - -/* Begin PBXAggregateTarget section */ - 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { - isa = PBXAggregateTarget; - buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; - buildPhases = ( - 33CC111E2044C6BF0003C045 /* ShellScript */, - ); - dependencies = ( - ); - name = "Flutter Assemble"; - productName = FLX; - }; -/* End PBXAggregateTarget section */ - -/* Begin PBXBuildFile section */ - 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; - 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; - 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; - 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; - 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; - 33D1A10422148B71006C7A3E /* FlutterMacOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */; }; - 33D1A10522148B93006C7A3E /* FlutterMacOS.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - D73912F022F37F9E000D13A0 /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D73912EF22F37F9E000D13A0 /* App.framework */; }; - D73912F222F3801D000D13A0 /* App.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = D73912EF22F37F9E000D13A0 /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - EA473EC5F2038B17A2FE4D78 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 748ADDF1719804343BB18004 /* Pods_Runner.framework */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 33CC10E52044A3C60003C045 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 33CC111A2044C6BA0003C045; - remoteInfo = FLX; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 33CC110E2044A8840003C045 /* Bundle Framework */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - D73912F222F3801D000D13A0 /* App.framework in Bundle Framework */, - 33D1A10522148B93006C7A3E /* FlutterMacOS.framework in Bundle Framework */, - ); - name = "Bundle Framework"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; - 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* connectivity_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = connectivity_example.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; - 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; - 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; - 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; - 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; - 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; - 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FlutterMacOS.framework; path = Flutter/ephemeral/FlutterMacOS.framework; sourceTree = SOURCE_ROOT; }; - 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; - 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; - 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; - 748ADDF1719804343BB18004 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; - 80418F0A2F74D683C63A4D0A /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; - AA19B00394637215A825CF5E /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - D73912EF22F37F9E000D13A0 /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/ephemeral/App.framework; sourceTree = SOURCE_ROOT; }; - E960ED3977AF6DF197F74FFA /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 33CC10EA2044A3C60003C045 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - D73912F022F37F9E000D13A0 /* App.framework in Frameworks */, - 33D1A10422148B71006C7A3E /* FlutterMacOS.framework in Frameworks */, - EA473EC5F2038B17A2FE4D78 /* Pods_Runner.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 33BA886A226E78AF003329D5 /* Configs */ = { - isa = PBXGroup; - children = ( - 33E5194F232828860026EE4D /* AppInfo.xcconfig */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, - ); - path = Configs; - sourceTree = ""; - }; - 33CC10E42044A3C60003C045 = { - isa = PBXGroup; - children = ( - 33FAB671232836740065AC1E /* Runner */, - 33CEB47122A05771004F2AC0 /* Flutter */, - 33CC10EE2044A3C60003C045 /* Products */, - D73912EC22F37F3D000D13A0 /* Frameworks */, - D42EAEE5849744148CC78D83 /* Pods */, - ); - sourceTree = ""; - }; - 33CC10EE2044A3C60003C045 /* Products */ = { - isa = PBXGroup; - children = ( - 33CC10ED2044A3C60003C045 /* connectivity_example.app */, - ); - name = Products; - sourceTree = ""; - }; - 33CC11242044D66E0003C045 /* Resources */ = { - isa = PBXGroup; - children = ( - 33CC10F22044A3C60003C045 /* Assets.xcassets */, - 33CC10F42044A3C60003C045 /* MainMenu.xib */, - 33CC10F72044A3C60003C045 /* Info.plist */, - ); - name = Resources; - path = ..; - sourceTree = ""; - }; - 33CEB47122A05771004F2AC0 /* Flutter */ = { - isa = PBXGroup; - children = ( - 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, - 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, - 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, - 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, - D73912EF22F37F9E000D13A0 /* App.framework */, - 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */, - ); - path = Flutter; - sourceTree = ""; - }; - 33FAB671232836740065AC1E /* Runner */ = { - isa = PBXGroup; - children = ( - 33CC10F02044A3C60003C045 /* AppDelegate.swift */, - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, - 33E51913231747F40026EE4D /* DebugProfile.entitlements */, - 33E51914231749380026EE4D /* Release.entitlements */, - 33CC11242044D66E0003C045 /* Resources */, - 33BA886A226E78AF003329D5 /* Configs */, - ); - path = Runner; - sourceTree = ""; - }; - D42EAEE5849744148CC78D83 /* Pods */ = { - isa = PBXGroup; - children = ( - 80418F0A2F74D683C63A4D0A /* Pods-Runner.debug.xcconfig */, - E960ED3977AF6DF197F74FFA /* Pods-Runner.release.xcconfig */, - AA19B00394637215A825CF5E /* Pods-Runner.profile.xcconfig */, - ); - name = Pods; - path = Pods; - sourceTree = ""; - }; - D73912EC22F37F3D000D13A0 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 748ADDF1719804343BB18004 /* Pods_Runner.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 33CC10EC2044A3C60003C045 /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - B24477CAB9D5BDFC8F3553DA /* [CP] Check Pods Manifest.lock */, - 33CC10E92044A3C60003C045 /* Sources */, - 33CC10EA2044A3C60003C045 /* Frameworks */, - 33CC10EB2044A3C60003C045 /* Resources */, - 33CC110E2044A8840003C045 /* Bundle Framework */, - 3399D490228B24CF009A79C7 /* ShellScript */, - 84A8D21305B2F01D093A8F9C /* [CP] Embed Pods Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 33CC11202044C79F0003C045 /* PBXTargetDependency */, - ); - name = Runner; - productName = Runner; - productReference = 33CC10ED2044A3C60003C045 /* connectivity_example.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 33CC10E52044A3C60003C045 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 0930; - ORGANIZATIONNAME = "Google LLC"; - TargetAttributes = { - 33CC10EC2044A3C60003C045 = { - CreatedOnToolsVersion = 9.2; - LastSwiftMigration = 1100; - ProvisioningStyle = Automatic; - SystemCapabilities = { - com.apple.Sandbox = { - enabled = 1; - }; - }; - }; - 33CC111A2044C6BA0003C045 = { - CreatedOnToolsVersion = 9.2; - ProvisioningStyle = Manual; - }; - }; - }; - buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 8.0"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 33CC10E42044A3C60003C045; - productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 33CC10EC2044A3C60003C045 /* Runner */, - 33CC111A2044C6BA0003C045 /* Flutter Assemble */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 33CC10EB2044A3C60003C045 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, - 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3399D490228B24CF009A79C7 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename\n"; - }; - 33CC111E2044C6BF0003C045 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - Flutter/ephemeral/FlutterInputs.xcfilelist, - ); - inputPaths = ( - Flutter/ephemeral/tripwire, - ); - outputFileListPaths = ( - Flutter/ephemeral/FlutterOutputs.xcfilelist, - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh\ntouch Flutter/ephemeral/tripwire\n"; - }; - 84A8D21305B2F01D093A8F9C /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - B24477CAB9D5BDFC8F3553DA /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 33CC10E92044A3C60003C045 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, - 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, - 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; - targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { - isa = PBXVariantGroup; - children = ( - 33CC10F52044A3C60003C045 /* Base */, - ); - name = MainMenu.xib; - path = Runner; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 338D0CE9231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Profile; - }; - 338D0CEA231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter/ephemeral", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - }; - name = Profile; - }; - 338D0CEB231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Profile; - }; - 33CC10F92044A3C60003C045 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - 33CC10FA2044A3C60003C045 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Release; - }; - 33CC10FC2044A3C60003C045 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter/ephemeral", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - 33CC10FD2044A3C60003C045 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter/ephemeral", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - 33CC111C2044C6BA0003C045 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 33CC111D2044C6BA0003C045 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC10F92044A3C60003C045 /* Debug */, - 33CC10FA2044A3C60003C045 /* Release */, - 338D0CE9231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC10FC2044A3C60003C045 /* Debug */, - 33CC10FD2044A3C60003C045 /* Release */, - 338D0CEA231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC111C2044C6BA0003C045 /* Debug */, - 33CC111D2044C6BA0003C045 /* Release */, - 338D0CEB231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 33CC10E52044A3C60003C045 /* Project object */; -} diff --git a/packages/connectivity/connectivity_macos/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/connectivity/connectivity_macos/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index 2a7d3e7f34ac..000000000000 --- a/packages/connectivity/connectivity_macos/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/connectivity/connectivity_macos/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/packages/connectivity/connectivity_macos/example/macos/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 21a3cc14c74e..000000000000 --- a/packages/connectivity/connectivity_macos/example/macos/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/packages/connectivity/connectivity_macos/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/connectivity/connectivity_macos/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d68..000000000000 --- a/packages/connectivity/connectivity_macos/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/packages/connectivity/connectivity_macos/example/macos/Runner/AppDelegate.swift b/packages/connectivity/connectivity_macos/example/macos/Runner/AppDelegate.swift deleted file mode 100644 index 5cec4c48f620..000000000000 --- a/packages/connectivity/connectivity_macos/example/macos/Runner/AppDelegate.swift +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import Cocoa -import FlutterMacOS - -@NSApplicationMain -class AppDelegate: FlutterAppDelegate { - override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { - return true - } -} diff --git a/packages/connectivity/connectivity_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/connectivity/connectivity_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index a2ec33f19f11..000000000000 --- a/packages/connectivity/connectivity_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "images" : [ - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_16.png", - "scale" : "1x" - }, - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "2x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "1x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_64.png", - "scale" : "2x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_128.png", - "scale" : "1x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "2x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "1x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "2x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "1x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_1024.png", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/packages/connectivity/connectivity_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/packages/connectivity/connectivity_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png deleted file mode 100644 index 3c4935a7ca84f0976aca34b7f2895d65fb94d1ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 46993 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 diff --git a/packages/connectivity/connectivity_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/packages/connectivity/connectivity_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png deleted file mode 100644 index ed4cc16421680a50164ba74381b4b35ceaa0ccfc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3276 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 N;OXk;vd$@?2>?>Ex^Vyi diff --git a/packages/connectivity/connectivity_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/packages/connectivity/connectivity_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png deleted file mode 100644 index bcbf36df2f2aaaa0a63c7dabc94e600184229d0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5933 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 f$KZHO*s&vt|9^w-6P?|#0pRK8NSwWJ?9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!ItFh?!xdN1Q+aGJ{c&& zS>O>_%)r1c48n{Iv*t(u1=&kHeO=ifbFy+6aSK)V_AxLppYn8Z42d|rc6w}vOsL55 z`t&mC&y2@JTEyg!eDiFX^k#CC!jq%>erB=yHqUP0XcDOTw6ko}L zX;EmMrq(fKk*eygEuA616;0)>@A{TK|55PV@70 z$OfzS*(VJxQev3J?yY?O=ul(v`fp}?u9z`JK3ugibK>)DyCwImZOF4d{xK%%Ks1*} zv$oa)9anR%lXIBUqYnhLmT>VOzHfNP?ZwJNZ!5$s9M08RynIvaXw>@G^T9@r9^KH1 zVy??F&uuk)bH9Y4pQY!hP58i_H6 znl-NcuCpLV6ZWU;4C zu@9exF&OZi`Bovq_m%T+WhU2kvkz@^_LpycBvqm3bMpLw8X-Or5sL>0AKE1$(k_L=_Zc=CUq#=x1-QZf)G7nHu@fmsQ1eN_N3+nTEz`4HI4Z6uVlE zJH+X&det8JU?tO?upcM4Z=cV!JV;yF>FfL5Q$M|W_2Z!P`S=}Wzp|_1^#d%e?_H`> zV@%vA$+bFVqhw9`U;TfP|5|PD{||OiYdor8P*i??|NJcb%kzT_73*7WE?Ua5hAnR2 z=7WE=PhTlJ#ZeRznjTUb;`E(wkMZrj4e|Hilz-mK>9cZHQY**5TUPw~u}k;u73KI}xAx!0m-)GVia|x^d3p~s_9gh83jA&Ra<8rM%`>U3x69t&NzbwWY}7Ar?)FK#IZ0z|d0H0EkRO w3{9;}4Xg|ebq&m|3=9_N6z8I7$jwj5OsmAL;bP(Gi$Dzwp00i_>zopr02+f8CIA2c diff --git a/packages/connectivity/connectivity_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/packages/connectivity/connectivity_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png deleted file mode 100644 index e71a726136a47ed24125c7efc79d68a4a01961b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14800 zcmZ{Lc|26@`~R6Crm_qwyCLMMh!)vm)F@HWt|+6V6lE=CaHfcnn4;2x(VilEl9-V} zsce-cGK|WaF}4{T=lt&J`Fy_L-|vs#>v^7+XU=`!*L|PszSj43o%o$Dj`9mM7C;ar z@3hrnHw59q|KcHn4EQr~{_70*BYk4yj*SqM&s>NcnFoIBdT-sm1A@YrK@dF#f+SPu z{Sb8441xx|AjtYQ1gQq5z1g(^49Fba=I8)nl7BMGpQeB(^8>dY41u79Dw6+j(A_jO z@K83?X~$;S-ud$gYZfZg5|bdvlI`TMaqs!>e}3%9HXev<6;dZZT8Yx`&;pKnN*iCJ z&x_ycWo9{*O}Gc$JHU`%s*$C%@v73hd+Mf%%9ph_Y1juXamcTAHd9tkwoua7yBu?V zgROzw>LbxAw3^;bZU~ZGnnHW?=7r9ZAK#wxT;0O<*z~_>^uV+VCU9B@)|r z*z^v>$!oH7%WZYrwf)zjGU|(8I%9PoktcsH8`z^%$48u z(O_}1U25s@Q*9{-3O!+t?w*QHo;~P99;6-KTGO{Cb#ADDYWF!eATsx{xh-!YMBiuE z%bJc7j^^B$Sa|27XRxg(XTaxWoFI}VFfV>0py8mMM;b^vH}49j;kwCA+Lw=q8lptk z?Pe`{wHI39A&xYkltf5*y%;-DF>5v`-lm0vydYtmqo0sClh5ueHCLJ+6$0y67Z zO-_LCT|JXi3tN7fB-!0_Kn#I+=tyUj87uR5*0>|SZ zy3x2;aql87`{aPZ@UbBwY0;Z-a*lYL90YApOAMKur7YgOiqA~Cne6%b&{V-t>Am2c z{eyEuKl!GsA*jF2H_gvX?bP~v46%3ax$r~B$HnZQ;UiCmRl`ROK8v>;Zs~upH9}qu1ZA3kn-AY2k2@CaH=Qh7K6`nU z3ib(Bk%H*^_omL6N4_G5NpY20UXGi}a$!}#lf<&J4~nhRwRM5cCB3Zvv#6+N1$g@W zj9?qmQ`zz-G9HTpoNl~bCOaEQqlTVYi7G0WmB5E34;f{SGcLvFpOb`+Zm)C(wjqLA z2;+nmB6~QDXbxZGWKLt38I%X$Q!;h zup9S~byxKv=$x|^YEV;l0l67jH~E8BU45ft_7xomac-48oq4PZpSNJbw<7DTM4mmz z!$)z#04cy%b8w@cOvjmb36o;gwYIOLwy+{I#3dJj#W4QdOWwJQ2#20AL49`hSFUa7 zFNAN3OD==G3_kbr1d96>l`_cI`<=thKNh5>hgg7FV>5TfC6d#u)9BNXi@p1K*;2Is zz+x;l4GbSt#*%>1iq}jGIebXYJY5;PGG0y(^{>SSuZY89aL`sDghOM&&pyP6ABJ#w zYwK~4^1eUQD)4!GL>`zrWeHV z-W!6JZbW*Ngo;Edhp_cOysYr!uhKS}vIg_UC}x z=jXxQfV@4B3`5 z!u#byBVXV5GtrSx_8bnT@iKv=Uc6n)Zpa`<9N>+!J~Loxptl5$Z`!u<3a)-+P)say z#=jc7^mJzPMI2;yMhCmN7YN78E7-^S(t8E}FklC;z|4PL{bO|JieM#p1mBjwyZMEm zkX^A1RXPGeS2YqtPMX~~t^$~oeFfWAU#jVLi%Z@l2hle^3|e(q?(uS=BVauF?VF{j z(owKLJuze;_@5p1OtRyrT`EFXf)NfMYb-)E8RVVdr<@}M>4R&~P=;B`c1L%o|8YfB z-a(LB-i8jc5!&B5cowyI2~M^YID&@Xt(D9v{|DB z959W z*vEA77fh3*w*UJ`4Y(bxsoEy6hm7_Wc5gT0^cvso%Ow>9<&@9Q>mxb6-^pv)5yc>n zQ~^!qY(lPQ1EDGkr%_*y*D8T^YbCa52^MVqYpTLhgJ;N5PfCQ{SXk|plD#Sm+g4c- zFeL2Dih35W4{_qb75U`4Rb#S0FEo%F85dOhXSX0huPOxdAid{&p6P;+9}I)XU7^=3RZu9M(g0dLyz_7$8K{`AddBLOfU&B_QNHtmsnNXq`hy~% zvJ{vtz~Yt9X|o}5vXX)9ZCHaRq8iAb zUDj8%(MpzJN39LferYKvIc!)z^5T-eW@j3h9a6d%WZ!%@2^@4+6%Z9W1GHZbOj|sb z0cU$}*~G$fYvDC|XulSC_;m}?KC2jg5pxES$Bt!hA|@EX*2+O!UEb5sn_^d>z;>;r~ zmO3BivdXboPY*}amsO&`xk|e)S*u=`o67MC(1WTB;OwG+ua4UV7T5Wvy%?U{Pa5cO zMoLG>#@chO{Oc72XPyX8f3jC7P`$j4$)0wc(b50COaDP3_Cm}aPAglUa7kRXAqmo5 z0KDD7G>Gmnpons40WJNYn+pxko92GXy@PvSErKE-Ou3)3UiRr7!L4+0%+5}sD{bf)uj^ounQ-Yn2%%JoZ%FjUv%yjS?Ks4u_88Jh%tNliYW~817IV@fqd1T zi(?;Fv-s3rQEn=9G*E-QzSl%YS|^fe*yn}Aqh!&P<5%#oB?*{wZMa5$PYa*A{VA8! zbOfS1W!W}cTo%g~iP$>WhE_x7#O4?h$jq=>{M77>bTAK_ z6uU0tl6HARboGi}=4krr6WP`9`aAt&P5ON1v(+H{T?jZuJ}B{L-=z3VX)}mZwzrqH zpf?T!k&$?{&{0_p>b`kdJbSb(p~tFcuG4zh6}hfl@ues6CfJu<-P+!>FlYMlD_3!E z9$6VE==tlxNYe(s;@8@+4c4jQ$R2g8t0QwE>Et|)5)@kJj6^yaqFYY?0LEM2C!+7+ z+FN|UxR1GCy1KA`{T_%24U+Vserchr5h`;U7TZPr@43x#MMN{@vV?KSII}R@5k`7cVK}E;c)$f~_{ZLDOoL|-01p~oafxi4F zG$?Wha&a*rTnz-nTI-bAJ*SLb!5(L!#iRdvLEyo>7D_=H78-qZrm=6{hkUR{tR{H! z`ZTOV$Oi6^qX5=_{f}V9h}WJAO%h9)kEUF#*-JyYDbOGZ>Nfs%7L}4p zopIul&&Bbn!C9o83ypC6W4F$X=_|pex$V4!Whm#48Wfm3*oAW0Gc&#&b+oq<8>aZR z2BLpouQQwyf$aHpQUK3pMRj(mS^^t#s$IC3{j*m9&l7sQt@RU{o_}N-xI_lh`rND^ zX~-8$o(;p^wf3_5-WZ^qgW`e8T@37{`J)e2KJdSSCUpX6KZu0Ga&U*+u3*PDAs1uK zpl)40+fROA@Vo#vK?^@Pq%w8DO9HdfmH+~vNinZ$5GRz?sD|k246NepqZd`>81P^P z#x#3kUS-}x4k%&~iEUrsb&-X#_;;?y9oCP4crMkC`=q58#NxQ| z*NXNA;GR4X=GiGXwab5=&M3j04fQw%2UxM`S(aE)_PlgJttBX96$$lY@Q%0xV^IbcHqzw^Uk&E=vFB;EQ@kzVIeM8lDIW_Q_ zrfy)l6s2QBApF;J2xTD_@wuNMlwDfsdfMyzRq)<>qG{M)Yt}9F1{1HaI_X7=F=7>& zYB54VaKlxu0lIgS;Ac&25Aw(tcf@K~(cvPi8(OChzhlYp6}#<_MVhU95sD&)n0FtL zmxm4w$~s(S9jmHOgyovpG!x4uLfJsMsJn^QMraKAa1Ix?{zkV!a7{f%-!u2{NqZ&) zo+^XB`eFQ4 zk-(;_>T#pTKyvW${yL|XXbcv?CE2Tp<3(PjeXhu^Jrp6^Mj}lg_)jamK{g;C+q^Da ztb!gV!q5)B7G1%lVanA2b>Xs?%hzCgJ{Hc!ldr9dnz7k^xG#4pDpr|0ZmxxiUVl}j zbD_rg3yAFQ>nnc)0>71D==715jRj4XsRb2#_lJoSOwky&c4957V-|m)@>b^Nak1!8 z@DsIOS8>Oe^T>tgB)WX3Y^I^65Uae+2M;$RxX_C)Aoo0dltvoRRIVQkpnegWj;D#G z+TwFIRUN%bZW3(K{8yN8!(1i0O!X3YN?Zo08L5D~)_tWQA8&|CvuQb8Od?p_x=GMF z-B@v9iNLYS1lUsbb`!%f5+1ev8RFPk7xyx5*G;ybRw(PW*yEZ$unu2`wpH)7b@ZXEz4Jr{?KZKYl!+3^)Q z)~^g?KlPGtT!{yQU&(Z&^rVjPu>ueeZN86AnhRwc)m|;5NvM&W3xD%n`+Hjg5$e8M zKh1Ju82L~&^ z-IQ5bYhsjqJfr38iwi~8<{oeREh|3l)*Enj4&Q$+mM$15YqwXeufK9P^(O=pj=F-1 zD+&REgwY~!W#ZPccSEi(*jiKJ5)Q|zX;hP}S2T9j_);epH9JQs{n>RG}{Nak)vIbfa zFQm?H;D+tzrBN2)6{?Mo%fzN6;6d_h0Qyn61)+XT63=!T*WQyRUoB_x0_)Ir`$FtS zak07C(mOaWN5m%bk?F9X&@mEVKN%{R6obt(9qw&p>w&p;R*l2th9$D^*`pC}NmB+v z>bk;OJ(C8p$G;jNvRsBbt=a!!tKnjJ`9*yQFgjEN1HcC<&>u9aStT3>Oq=MOQV!#WOZ6{cv$YVmlJdovPRV}<=IZUPeBVh5DC z91-?kimq3JUr;UMQ@0?h52gupvG=~(5AVdP(2(%*sL8!#K1-L$9B7MrWGdt(h&whR@vz~0oEHF8u3U1Q zdGdaIytJj4x@eF*E+^zgi{nPCA8tkjN}UoR8WhDzM3-zLqx0z?2tTdDKyENM={fp8VC@3Dt`AiK$;K#H$K2{08mrHG%jgEOLX3MCsG>afZm_0mLPS4jmYUJp~Dm! z5AUe_vEaOAT3zWdwl#cLvqwd1^lwW?gt7(92wEsOE6c#<0}{szFV4(uO70?3>=((! zQr}1{J?Wx2ZmjxYL_8OB*m&mimfojzYn~PiJ2g8R&ZRx-i^yF#sdhEWXAUIZ@J?T$ zs3PgT2<&Ki>Bob_n(@S>kUIvE+nY~ti9~6j;O9VAG#{oZ!DZCW)}i6iA!Tgsyz+hC z1VVyvbQ_nwgdZSEP=U4d#U`2*`e~d4y8uM4Bcmm%!jidaee#4WqN!ZnlBmbYpuaO! z!rU3`Kl2 z0O7PD&fQ|_b)Ub!g9^s;C2e>1i*2&?1$6yEn?~Y zI)-WIN8N(5s9;grW+J@K@I%g#?G&hzmlgV=L}ZA{f>3YCMx^P{u@c5Z;U1qmdk#)L zvX6z1!sL>+@vxO8qVn#k3YxYi?8ggV){?Rn@j$+Fd4-QkuH1@)j#3-=f82GZ!nl~{ zzZ(?kO`ANttVeHSo%xmH!NmNZECh*{s!-8S>ALoe5xOPs>|P5BbUmP@rlV8`d(c=7 zypcpLaI*FM^;GM%@q`GAb8kO`$oE|R48yn)?p(c1t>5;Wwn5r6ck&uw4}TnT80jI`IS~J%q8CpaVgIze<8IykSpVBg8~E! zW_tGqB;GO47r_er05y+Kwrcn{VLxL*1;HMv@*sd}MB6DH4zaP~u4Y;>@Nw7?F8S?c zfVIY(^ntnGgWlD|idzGz$Y+Oh(Ra=&VIf4!K2W*a)(%5%78s}8qxOknAGtDAq+HMO zM+Nu;0OgQRn36 zA@~a8`uVQ~v9?d!BxnsVaB-z-djypO44BjQAmg7&eVoaew|~)wH$SgefJ2$7_RiY+ z_7ACGoFM6Lhvho+eUG@pU&0X(Uy(*j;9pr?ET?FHTXadlfXC|MReZoU5>AG`mTM<% zc~*I@E*u0|hwVTdFA~4^b2VT7_~}~tCueNY{de3og=ASFQ`)0dhC2~Ne<}}Rc?ptA zi}+bQE%N9o*hpSUMH)9xt%Zlz&^p&5=cW}{m#f85iVX64^{!(vhClT<I)+c)RuiyrZqIw4v`z%YK&;_Fh4_+0B?qAGxMfAM`LzG_bjD>ib4;KGT4_1I>sxvL&&qp40ajgQOqIE^9=Az4w#ymo)bW-Vg{T!n=l&|nR_ zw+wcH|FxUH63)~{M;goHepmD{Fe?W9sO|eJP9L$G<{e_7FxxuXQ+)(Z^@;X8I1=%k zTK$gbHA1^4W<`q~ubQ0M_C^CA5#Z&*nGc(T?4Y_2jLu&FJDQYpCSiRny->$+nC9Jl z?avTW`ZXYT51%SrEq!}dXNM&!pM6nmL^lce=%S7{_TS)ckN8;{p*LT~LMgmlE~dpL zEBQy-jDj%cSK6N3)|CCR0LQ$N6iDM~+-1Oz|LAdkip(VZcO`gqCuJ+(Mm{m6@P%_; zBtF|MMVMP;E`5NJ{&@4j^JE5j&}(Jq{lCGL(P^#uqvbD`2)FVyfNgy|pvT!XY;02Z zZWbgGsvi6#!*$Zxwd{Xk6_M{+^yV_K@%_SAW(x)Lg|*AuG-%g2#GQYk8F?W&8|2dU z;00ppzrQnnYXnT`(S%_qF2#QNz&@Y$zcq+O8p>Gto2&4z8(^#cY?DuQwBQP4Fe?qUK_-yh4xT{8O@gb`uh` z>Q%jrgPAnANn4_)->n;w{Mei#J)F+`12&+-MLKSRzF6bL3;4O~oy~v7 zL0K-=m?>>(^qDCgvFRLBI@`04EGdTxe5}xBg#7#Wb!aUED;?5BLDEvZ@tai4*Rh8& z4V)cOr}DJ0&(FjWH%50Y+&=WtB42^eEVsmaHG)Il#j265oK&Bot(+-IIn`6InmuE# z;)qXs+X{fSb8^rYb#46X5?KCzH9X0>ppBQi(aKS--;4yA%0N|D<#8RZlOS(8n26=u zv~y;KC>`ypW=aqj`&x9 z0Zm>NKp}hPJu1+QDo(_U(Gt0SZ`IJWnp%QK`pye>Bm!w{sG>;VU^2 z4lZhV1}tCE8(?zu#j99|l3-qRBcz3bG+DlyxPGB$^6B^ssc_qYQ6lG0q~EAI?1$?( zahfn%etVvuKwB7R=>JDQluP97nLDM6*5;b0Ox#b{4nIgZA*+?IvyDN{K9WGnlA=Ju z+)6hjr}{;GxQQIDr3*lf32lRp{nHP8uiz^Fa|K+dUc@wD4Kf5RPxVkUZFCdtZH{+=c$AC)G2T-Qn@BPbr zZigIhKhKrVYy`!Mlc#HVr=CURVrhUjExhI~gZ%a=WM9BwvnN?=z!_ZQ$(sP?X;2Jy zyI$}H^^SvH2tf6+Uk$pJww@ngzPp856-l9g6WtW+%Yf>N^A}->#1W2n=WJ%sZ0<){Z&#% z^Kzl$>Km)sIxKLFjtc;}bZeoaZSpL4>`jCmAeRM-NP9sQ&-mi@p0j7Iq>1n&z@8?M z%dM7K^SgE5z)@i5w#rLE4+8%|^J`a6wYr`3BlvdD>7xW?Dd>`0HC0o{w7r_ot~h*G z2gI7Y!AUZ6YN+z$=GNzns@Tu7BxgAb3MBha30-ZG7a%rckU5}y{df`lj@^+34kr5> z988PPbWYdHye~=?>uZ4N&MN@4RBLk_?9W*b$}jqt0j%>yO9QOV(*!#cX~=wRdVL&S zhPQ{${0CGU-rfdS&b@u|IK{hV2Z=(*B2d0?&jwWfT=?Gk`4T9TfMQ)CfNgpLQa#>Q z%6A$w#QNc&qOtrHAbqY>J782@!X{9Y@N(HMSr;PP^;0DlJNxfC`oMB%Ocg zC*hnEsF|p*=CVe^dT)>BTL0yff)uo!U<+_2o3p)CE8quU1JI(=6)9$KxVdJYD*S*~ zzNeSkzFIQyqK}578+qq6X8rrRdgX z4k&R=AGex~a)MoB0pK&|yA<(*J#P&tR?ImBVD)ZTA4VH5L5DxXe<-*s`Aox%H1{-^Qa`kG_DGXD%QX-;l1#&#IVQP6>kir ztO@~ZvJDPnTvKt>fc*(j$W^)JhWk{4kWwbpFIXzuPt2V%M4H19-i5Gn*6(D`4_c1+ zYoI1@yT^~9JF~t>2eVM6p=GP3b*;daJpQOhAMNO|LKnwE2B5n8y9mf;q=)-L_FfD0 z<}YIRBO{k)6AHAn8iG>pYT+3bJ7jvP9}LSMR1nZW$5HR%PD1rFz z{4XE^Vmi-QX#?|Farz=CYS_8!%$E#G%4j2+;Avz|9QBj|YIExYk?y-1(j}0h{$$MnC_*F0U2*ExSi1ZCb_S9aV zTgyGP0Cl=m`emxM4Qih1E{`J{4oJo8K}WnH`@js^pR7Z-vTBK5F5JIFCDN}7pU^_nV>NTz@2$|Kcc5o+L&^Db_AQ);F?)X5BF*QJRCdLI-a%gW z++DZM)x=6*fNrSaUA&hf&CUqC$F*y^CJC-MAm9gd*5#^mh;-dR1?a&<3-hp3@}XN! z&8dcwo6=MQua%0KFvYbi>O{j)RrbDQo3S*y!oEJ~2=}^-v%zn~@hnmKGOvX6JLr;>DNC3)={8OM9n5Zs*(DlS*|%JTniJX2Uav7sOFT0vdIiUOC5pEtY?EF)@Fh9pCfD%N zXskZ8b^ldI{HHj{-l?iWo@IW6Nr`hAS>f8S*8FGc*gmcK^f2JS+>I&r#Gcewy=-JM zv0*w<5qBa6UQB@`esOG*4*t@7c9AkrTpM`v=eY?cO#z17H9B%Xy4m!}LhW}*iZ27w1?HrevgB1SZ1q2X$mm@FK@Qt7o z!s~Lio^IRdwzyvQ80{5iYeTV@mAo=2o5>KepRH0d{*Szlg~n%w2)S5v2|K8}pj;c{ zoDRLvYJO1@?x-=mq+LVhD{l-1-Dw4`7M?3@+ z`fu7?1#9W++6Y46N=H0+bD|CJH~q*CdEBm8D##VS7`cXy4~+x=ZC17rJeBh zI~qW^&FU`+e!{AKO3(>z5Ghh14bUT$=4B>@DVm(cj* zSLA*j!?z!=SLuVvAPh_EFKx}JE8T8;Gx)LH^H136=#Jn3Bo*@?=S`5M{WJPY&~ODs z+^V57DhJ2kD^Z|&;H}eoN~sxS8~cN5u1eW{t&y{!ouH`%p4(yDZaqw$%dlm4A0f0| z8H}XZFDs?3QuqI^PEy}T;r!5+QpfKEt&V|D)Z*xoJ?XXZ+k!sU2X!rcTF4tg8vWPM zr-JE>iu9DZK`#R5gQO{nyGDALY!l@M&eZsc*j*H~l4lD)8S?R*nrdxn?ELUR4kxK? zH(t9IM~^mfPs9WxR>J{agadQg@N6%=tUQ8Bn++TC|Hbqn*q;WydeNIS@gt|3j!P`w zxCKoeKQ*WBlF%l4-apIhERKl(hXS1vVk$U?Wifi)&lL6vF@bmFXmQEe{=$iG)Zt*l z0df@_)B-P_^K2P7h=>OIQ6f0Q-E@|M?$Z5n^oN>2_sBCpN>q(LnqUoef{tm^5^L$# z{<SL zKmH78cHX`4cBKIY8u1x*lwrgP^fJ%E&&AmHrRY7^hH*=2OA9K?!+|~Aeia=nAA`5~ z#zI=h#I>@FXaGk(n)0uqelNY;A5I9obE~OjsuW!%^NxK*52CfBPWYuw--v<1v|B>h z8R=#$TS-Pt3?d@P+xqmYpL4oB8- z>w99}%xqy9W!A^ODfLq8iA@z}10u?o#nG#MXumSaybi(S{`wIM z&nE3n2gWWMu93EvtofWzvG2{v;$ysuw^8q?3n}y=pB1vUr5gi++PjiyBH3jzKBRny zSO~O++1ZLdy7v7VzS&$yY;^Z7*j_#BI`PK`dAzJa9G1{9ahPqPi1C}ti+L)WHii*= z+RZ^+at-tlatc4|akPa&9H;%gn9aS`X_kfb>n>#NTyUVM6m4NCIfLm(28>qaYv7}t zn`M;XcONtXoa3#u3{L-ytd_&g z2mO$8CnE?460w#eSm|smlnNwFHM;A&IxSKLzVkV7nNVqZ*A`)eI{Nbg6WxsarAFuc=FFf1z|%#eTvBgUhY}N zsCT>`_YO>14i^vFX0KXbARLItzT{TeD%N~=ovGtZ6j{>PxkuYlHNTe0!u>rgw#?td z{)n=QrGvgCDE6BUem$Rh(1y!$@(Bn!k3E0|>PQ(8O==zN`?yBhAqlWyq+c%+h?p^- zE&OtLind}^_=>pbhxOgOIC0q9{cLK6p6*eg_|S+p9$W~_u4wzx@N?$QmFg2S)m~^R znni$X{U*!lHgdS@fI;|Owl=9Gwi?dr0m#>yL<8<}bLW_Kpl| zSGesADX&n?qmHC`2GyIev^hi~ka}ISZ^Y4w-yUzyPxaJB0mm%ww^>if3<;P^U+L5=s+cifT-ct*;!dOOk#SOZNv@a^J|DrS3YtSn8EEAlabX1NV3RfHwZn_41Xa z4;$taa6JJR()-FQ<#0G~WlML<l5I+IPnqDpW(PP>hRcQ+S2zU?tbG^(y z1K_?1R){jF;OKGw0WYjnm>aPxnmr5?bP?^B-|Fv`TT4ecH3O`Z3`X_r;vgFn>t1tE zGE6W2PODPKUj+@a%3lB;lS?srE5lp(tZ;uvzrPb){f~n7v_^z! z=16!Vdm!Q0q#?jy0qY%#0d^J8D9o)A;Rj!~j%u>KPs-tB08{4s1ry9VS>gW~5o^L; z7vyjmfXDGRVFa@-mis2!a$GI@9kE*pe3y_C3-$iVGUTQzZE+%>vT0=r|2%xMDBC@>WlkGU4CjoWs@D(rZ zS1NB#e69fvI^O#5r$Hj;bhHPEE4)4q5*t5Gyjzyc{)o459VkEhJ$%hJUC&67k z7gdo`Q*Jm3R&?ueqBezPTa}OI9wqcc;FRTcfVXob^z|dNIB0hMkHV26$zA%YgR$sM zTKM61S}#wJ#u+0UDE3N+U*~Tz1nnV;W<8Akz&6M7-6mIF(Pq`wJ1A%loYL( zIS;&2((xbyL7zoyaY2Sa%BBYBxo6Aa*53`~e@|RA`MP+?iI4KZ+y4EU&I zS_|(#*&j2hxpELa3r0O7ok&5!ijRiRu9i-_3cdnydZU9Mp6Y);skv%!$~`i-J7e-g zj@EoHf+gtcrKf;tY5`4iLnWSHa)9brUM$XmEzG3T0BXTG_+0}p7uGLs^(uYh0j$;~ zT1&~S%_Y5VImvf1EkD7vP-@F%hRlBe{a@T!SW(4WEQd1!O47*Crf@u-TS==48iR5x z!*`Ul4AJI^vIVaN3u5UifXBX{fJ@z>4Q2#1?jpcdLocwymBgKrZ+^Cb@QuIxl58B* zD{t-W3;M;{MGHm_@&n(6A-AsD;JO#>J3o4ru{hy;k;8?=rkp0tadEEcHNECoTI(W31`El-CI0eWQ zWD4&2ehvACkLCjG`82T`L^cNNC4Oo2IH(T4e;C75IwkJ&`|ArqSKD}TX_-E*eeiU& ziUuAC)A?d>-;@9Jcmsdca>@q1`6vzo^3etEH%1Gco&gvC{;Y-qyJ$Re`#A!5Kd((5 z6sSiKnA20uPX0**Mu&6tNgTunUR1sodoNmDst1&wz8v7AG3=^huypTi`S7+GrO$D6 z)0Ja-y5r?QQ+&jVQBjitIZ`z2Ia}iXWf#=#>nU+ zL29$)Q>f#o<#4deo!Kuo@WX{G(`eLaf%(_Nc}E`q=BXHMS(Os{!g%(|&tTDIczE_# z5y%wjCp9S?&*8bS3imJi_9_COC)-_;6D9~8Om@?U2PGQpM^7LKG7Q~(AoSRgP#tZfVDF_zr;_U*!F9qsbVQ@un9O2>T4M5tr0B~~v_@a=w^8h510a#=L z;8+9zhV}57uajb+9DbZm1G`_NqOuKN`bQ2fw9A*v*Kdb_E-SA`?2 z)OFIY-%uD`JZUZg?D4lHtNegKgWr!1m%hOpu5`R+bZ2K#&)*R-7ElKYo0$0xYxIL8 zLg%u|4oZixz}ILB-@aS4=XOe)z!VL6@?dX{LW^YCPjKtyw44)xT=H;h(fmFr>R?p%r5*}W z7_bo0drVDRq9V9QL4_!dazughK6t}tVVvBq={T0+3(1zmb>f+|;{D%J?^xnZcqio5 z%H?@L+L-CIdO=x6QrALL9&PwvjrZi5NS)1e<*%V8ntw~S2PF}zH}B5f_DHyB=I3m@ z_;^TpN|sesCU}qxQ`~jIwF>#8wGvxg9kdMT$}us8BM&W>OzZ|ry2BB)+UY*_yH+&L zl_=Jy9BNzIZs}D~Yv_H%HPjVGNV=xT3xpIW!Np1F^G#9Y8X zl)c_V1(DhYu-v%H3-m&n%M_}}c{E5Wu+6*>R24gW_A7$(U=9D|H$r;;;@o zJ)c_CmVf9l*;4SyJ}E{+4)}^C>SIJ*_bul7OJ{v&0oO>jG(5xzYP0$I%*YH|Mwu#r zubNW5VZ9^X#Phw<;?=^G?Kg&C)^x1FVsKGZ*n+{C1znj~YHSP?6PS(k5e9qGvS4X* z=1kA_27(iV65a(i+Sicmd@Vzf^2@*Wed-`aYQ~em=-h%Pu`gHfz)&@$hpr<&mNO={ zl^kI0HP0wTbbh{d(>5a#;zT2_=ppef?;D4;2^}&kZjB^yl%LBJ;|> zkLc)JEg*5rpQ;_)w?PnKynWtv!@ z>}+am{@(g$KKM+e$ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/connectivity/connectivity_macos/example/macos/Runner/Configs/AppInfo.xcconfig b/packages/connectivity/connectivity_macos/example/macos/Runner/Configs/AppInfo.xcconfig deleted file mode 100644 index db9bebac4b66..000000000000 --- a/packages/connectivity/connectivity_macos/example/macos/Runner/Configs/AppInfo.xcconfig +++ /dev/null @@ -1,14 +0,0 @@ -// Application-level settings for the Runner target. -// -// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the -// future. If not, the values below would default to using the project name when this becomes a -// 'flutter create' template. - -// The application's name. By default this is also the title of the Flutter window. -PRODUCT_NAME = connectivity_example - -// The application's bundle identifier -PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.connectivityExample - -// The copyright displayed in application information -PRODUCT_COPYRIGHT = Copyright © 2019 The Flutter Authors. All rights reserved. diff --git a/packages/connectivity/connectivity_macos/example/macos/Runner/Configs/Debug.xcconfig b/packages/connectivity/connectivity_macos/example/macos/Runner/Configs/Debug.xcconfig deleted file mode 100644 index 36b0fd9464f4..000000000000 --- a/packages/connectivity/connectivity_macos/example/macos/Runner/Configs/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "../../Flutter/Flutter-Debug.xcconfig" -#include "Warnings.xcconfig" diff --git a/packages/connectivity/connectivity_macos/example/macos/Runner/Configs/Release.xcconfig b/packages/connectivity/connectivity_macos/example/macos/Runner/Configs/Release.xcconfig deleted file mode 100644 index dff4f49561c8..000000000000 --- a/packages/connectivity/connectivity_macos/example/macos/Runner/Configs/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "../../Flutter/Flutter-Release.xcconfig" -#include "Warnings.xcconfig" diff --git a/packages/connectivity/connectivity_macos/example/macos/Runner/Configs/Warnings.xcconfig b/packages/connectivity/connectivity_macos/example/macos/Runner/Configs/Warnings.xcconfig deleted file mode 100644 index 42bcbf4780b1..000000000000 --- a/packages/connectivity/connectivity_macos/example/macos/Runner/Configs/Warnings.xcconfig +++ /dev/null @@ -1,13 +0,0 @@ -WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings -GCC_WARN_UNDECLARED_SELECTOR = YES -CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES -CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE -CLANG_WARN__DUPLICATE_METHOD_MATCH = YES -CLANG_WARN_PRAGMA_PACK = YES -CLANG_WARN_STRICT_PROTOTYPES = YES -CLANG_WARN_COMMA = YES -GCC_WARN_STRICT_SELECTOR_MATCH = YES -CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES -CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES -GCC_WARN_SHADOW = YES -CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/packages/connectivity/connectivity_macos/example/macos/Runner/DebugProfile.entitlements b/packages/connectivity/connectivity_macos/example/macos/Runner/DebugProfile.entitlements deleted file mode 100644 index dddb8a30c851..000000000000 --- a/packages/connectivity/connectivity_macos/example/macos/Runner/DebugProfile.entitlements +++ /dev/null @@ -1,12 +0,0 @@ - - - - - com.apple.security.app-sandbox - - com.apple.security.cs.allow-jit - - com.apple.security.network.server - - - diff --git a/packages/connectivity/connectivity_macos/example/macos/Runner/Info.plist b/packages/connectivity/connectivity_macos/example/macos/Runner/Info.plist deleted file mode 100644 index 4789daa6a443..000000000000 --- a/packages/connectivity/connectivity_macos/example/macos/Runner/Info.plist +++ /dev/null @@ -1,32 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIconFile - - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSMinimumSystemVersion - $(MACOSX_DEPLOYMENT_TARGET) - NSHumanReadableCopyright - $(PRODUCT_COPYRIGHT) - NSMainNibFile - MainMenu - NSPrincipalClass - NSApplication - - diff --git a/packages/connectivity/connectivity_macos/example/macos/Runner/MainFlutterWindow.swift b/packages/connectivity/connectivity_macos/example/macos/Runner/MainFlutterWindow.swift deleted file mode 100644 index 32aaeedceb1f..000000000000 --- a/packages/connectivity/connectivity_macos/example/macos/Runner/MainFlutterWindow.swift +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import Cocoa -import FlutterMacOS - -class MainFlutterWindow: NSWindow { - override func awakeFromNib() { - let flutterViewController = FlutterViewController.init() - let windowFrame = self.frame - self.contentViewController = flutterViewController - self.setFrame(windowFrame, display: true) - - RegisterGeneratedPlugins(registry: flutterViewController) - - super.awakeFromNib() - } -} diff --git a/packages/connectivity/connectivity_macos/example/macos/Runner/Release.entitlements b/packages/connectivity/connectivity_macos/example/macos/Runner/Release.entitlements deleted file mode 100644 index 852fa1a4728a..000000000000 --- a/packages/connectivity/connectivity_macos/example/macos/Runner/Release.entitlements +++ /dev/null @@ -1,8 +0,0 @@ - - - - - com.apple.security.app-sandbox - - - diff --git a/packages/connectivity/connectivity_macos/example/pubspec.yaml b/packages/connectivity/connectivity_macos/example/pubspec.yaml deleted file mode 100644 index 0af2e1587b00..000000000000 --- a/packages/connectivity/connectivity_macos/example/pubspec.yaml +++ /dev/null @@ -1,29 +0,0 @@ -name: connectivity_example -description: Demonstrates how to use the connectivity plugin. -publish_to: none - -environment: - sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.10.0" - -dependencies: - flutter: - sdk: flutter - connectivity_platform_interface: ^2.0.0 - connectivity_macos: - # When depending on this package from a real application you should use: - # connectivity_macos: ^x.y.z - # See https://dart.dev/tools/pub/dependencies#version-constraints - # The example app is bundled with the plugin so we use a path dependency on - # the parent directory to use the current plugin's version. - path: ../ - -dev_dependencies: - flutter_driver: - sdk: flutter - integration_test: - sdk: flutter - pedantic: ^1.10.0 - -flutter: - uses-material-design: true diff --git a/packages/connectivity/connectivity_macos/example/test_driver/integration_test.dart b/packages/connectivity/connectivity_macos/example/test_driver/integration_test.dart deleted file mode 100644 index 4f10f2a522f3..000000000000 --- a/packages/connectivity/connectivity_macos/example/test_driver/integration_test.dart +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:integration_test/integration_test_driver.dart'; - -Future main() => integrationDriver(); diff --git a/packages/connectivity/connectivity_macos/macos/Classes/ConnectivityPlugin.swift b/packages/connectivity/connectivity_macos/macos/Classes/ConnectivityPlugin.swift deleted file mode 100644 index 69efe80df5ac..000000000000 --- a/packages/connectivity/connectivity_macos/macos/Classes/ConnectivityPlugin.swift +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import Cocoa -import CoreWLAN -import FlutterMacOS -import Reachability -import SystemConfiguration.CaptiveNetwork - -public class ConnectivityPlugin: NSObject, FlutterPlugin, FlutterStreamHandler { - var reach: Reachability? - var eventSink: FlutterEventSink? - var cwinterface: CWInterface? - - public override init() { - cwinterface = CWWiFiClient.shared().interface() - } - - public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel( - name: "plugins.flutter.io/connectivity", - binaryMessenger: registrar.messenger) - - let streamChannel = FlutterEventChannel( - name: "plugins.flutter.io/connectivity_status", - binaryMessenger: registrar.messenger) - - let instance = ConnectivityPlugin() - streamChannel.setStreamHandler(instance) - - registrar.addMethodCallDelegate(instance, channel: channel) - } - - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - switch call.method { - case "check": - result(statusFromReachability(reachability: Reachability.forInternetConnection())) - case "wifiName": - result(cwinterface?.ssid()) - case "wifiBSSID": - result(cwinterface?.bssid()) - case "wifiIPAddress": - result(getWifiIP()) - default: - result(FlutterMethodNotImplemented) - } - } - - /// Returns a string describing connection type - /// - /// - Parameters: - /// - reachability: an instance of reachability - /// - Returns: connection type string - private func statusFromReachability(reachability: Reachability?) -> String { - // checks any non-WWAN connection - if reachability?.isReachableViaWiFi() ?? false { - return "wifi" - } - - return "none" - } - - public func onListen( - withArguments _: Any?, - eventSink events: @escaping FlutterEventSink - ) -> FlutterError? { - reach = Reachability.forInternetConnection() - eventSink = events - - NotificationCenter.default.addObserver( - self, - selector: #selector(reachabilityChanged), - name: NSNotification.Name.reachabilityChanged, - object: reach) - - reach?.startNotifier() - - return nil - } - - @objc private func reachabilityChanged(notification: NSNotification) { - let reach = notification.object - let reachability = statusFromReachability(reachability: reach as? Reachability) - eventSink?(reachability) - } - - public func onCancel(withArguments _: Any?) -> FlutterError? { - reach?.stopNotifier() - NotificationCenter.default.removeObserver(self) - eventSink = nil - return nil - } -} diff --git a/packages/connectivity/connectivity_macos/macos/Classes/IPHelper.h b/packages/connectivity/connectivity_macos/macos/Classes/IPHelper.h deleted file mode 100644 index e5370fb349c3..000000000000 --- a/packages/connectivity/connectivity_macos/macos/Classes/IPHelper.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -#import "SystemConfiguration/CaptiveNetwork.h" - -#include -#include - -NSString* getWifiIP() { - NSString* address = @"error"; - struct ifaddrs* interfaces = NULL; - struct ifaddrs* temp_addr = NULL; - int success = 0; - - // Retrieve the current interfaces - returns 0 on success. - success = getifaddrs(&interfaces); - if (success == 0) { - // Loop through linked list of interfaces. - temp_addr = interfaces; - while (temp_addr != NULL) { - if (temp_addr->ifa_addr->sa_family == AF_INET) { - // Check if interface is en0 which is the wifi connection on the iPhone. - if ([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) { - // Get NSString from C String - address = [NSString - stringWithUTF8String:inet_ntoa(((struct sockaddr_in*)temp_addr->ifa_addr)->sin_addr)]; - } - } - - temp_addr = temp_addr->ifa_next; - } - } - - // Free memory - freeifaddrs(interfaces); - - return address; -} diff --git a/packages/connectivity/connectivity_macos/macos/connectivity_macos.podspec b/packages/connectivity/connectivity_macos/macos/connectivity_macos.podspec deleted file mode 100644 index 51629084a23d..000000000000 --- a/packages/connectivity/connectivity_macos/macos/connectivity_macos.podspec +++ /dev/null @@ -1,22 +0,0 @@ -# -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html -# -Pod::Spec.new do |s| - s.name = 'connectivity_macos' - s.version = '0.0.1' - s.summary = 'Flutter plugin for checking connectivity' - s.description = <<-DESC - Desktop implementation of the connectivity plugin - DESC - s.homepage = 'https://github.com/flutter/plugins/tree/master/packages/connectivity/connectivity_macos' - s.license = { :type => 'BSD', :file => '../LICENSE' } - s.author = { 'Flutter Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/connectivity/connectivity_macos' } - s.source_files = 'Classes/**/*' - s.dependency 'FlutterMacOS' - s.dependency 'Reachability' - - s.platform = :osx - s.osx.deployment_target = '10.11' - s.swift_version = '5.0' -end diff --git a/packages/connectivity/connectivity_macos/pubspec.yaml b/packages/connectivity/connectivity_macos/pubspec.yaml deleted file mode 100644 index b98f23d34eb7..000000000000 --- a/packages/connectivity/connectivity_macos/pubspec.yaml +++ /dev/null @@ -1,30 +0,0 @@ -name: connectivity_macos -description: macOS implementation of the connectivity plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/connectivity/connectivity_macos -issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+connectivity%22 -version: 0.2.1+2 - -environment: - sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" - -flutter: - plugin: - implements: connectivity - platforms: - macos: - pluginClass: ConnectivityPlugin - -dependencies: - flutter: - sdk: flutter - # The implementation of this plugin doesn't explicitly depend on the method channel - # defined in the platform interface. - # To prevent potential breakages, this dependency is added. - # - # In the future, this plugin's platform code should be able to reference the - # interface's platform code. (Android already supports this). - connectivity_platform_interface: ^2.0.0 - -dev_dependencies: - pedantic: ^1.10.0 diff --git a/packages/connectivity/connectivity_platform_interface/AUTHORS b/packages/connectivity/connectivity_platform_interface/AUTHORS deleted file mode 100644 index 493a0b4ef9c2..000000000000 --- a/packages/connectivity/connectivity_platform_interface/AUTHORS +++ /dev/null @@ -1,66 +0,0 @@ -# Below is a list of people and organizations that have contributed -# to the Flutter project. Names should be added to the list like so: -# -# Name/Organization - -Google Inc. -The Chromium Authors -German Saprykin -Benjamin Sauer -larsenthomasj@gmail.com -Ali Bitek -Pol Batlló -Anatoly Pulyaevskiy -Hayden Flinner -Stefano Rodriguez -Salvatore Giordano -Brian Armstrong -Paul DeMarco -Fabricio Nogueira -Simon Lightfoot -Ashton Thomas -Thomas Danner -Diego Velásquez -Hajime Nakamura -Tuyển Vũ Xuân -Miguel Ruivo -Sarthak Verma -Mike Diarmid -Invertase -Elliot Hesp -Vince Varga -Aawaz Gyawali -EUI Limited -Katarina Sheremet -Thomas Stockx -Sarbagya Dhaubanjar -Ozkan Eksi -Rishab Nayak -ko2ic -Jonathan Younger -Jose Sanchez -Debkanchan Samadder -Audrius Karosevicius -Lukasz Piliszczuk -SoundReply Solutions GmbH -Rafal Wachol -Pau Picas -Christian Weder -Alexandru Tuca -Christian Weder -Rhodes Davis Jr. -Luigi Agosti -Quentin Le Guennec -Koushik Ravikumar -Nissim Dsilva -Giancarlo Rocha -Ryo Miyake -Théo Champion -Kazuki Yamaguchi -Eitan Schwartz -Chris Rutkowski -Juan Alvarez -Aleksandr Yurkovskiy -Anton Borries -Alex Li -Rahul Raj <64.rahulraj@gmail.com> diff --git a/packages/connectivity/connectivity_platform_interface/CHANGELOG.md b/packages/connectivity/connectivity_platform_interface/CHANGELOG.md deleted file mode 100644 index ee75d03ccc65..000000000000 --- a/packages/connectivity/connectivity_platform_interface/CHANGELOG.md +++ /dev/null @@ -1,44 +0,0 @@ -## 2.0.1 - -* Update platform_plugin_interface version requirement. - -## 2.0.0 - -* Migrate to null safety. - -## 1.0.7 - -* Update Flutter SDK constraint. - -## 1.0.6 - -* Update lower bound of dart dependency to 2.1.0. - -## 1.0.5 - -* Remove dart:io Platform checks from the MethodChannel implementation. This is -tripping the analysis of other versions of the plugin. - -## 1.0.4 - -* Bump the minimum Flutter version to 1.12.13+hotfix.5. - -## 1.0.3 - -* Make the pedantic dev_dependency explicit. - -## 1.0.2 - -* Bring ConnectivityResult and LocationAuthorizationStatus enums from the core package. -* Use the above Enums as return values for ConnectivityPlatformInterface methods. -* Modify the MethodChannel implementation so it returns the right types. -* Bring all utility methods, asserts and other logic that is only needed on the MethodChannel implementation from the core package. -* Bring MethodChannel unit tests from core package. - -## 1.0.1 - -* Fix README.md link. - -## 1.0.0 - -* Initial release. diff --git a/packages/connectivity/connectivity_platform_interface/LICENSE b/packages/connectivity/connectivity_platform_interface/LICENSE deleted file mode 100644 index c6823b81eb84..000000000000 --- a/packages/connectivity/connectivity_platform_interface/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright 2013 The Flutter Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/connectivity/connectivity_platform_interface/README.md b/packages/connectivity/connectivity_platform_interface/README.md deleted file mode 100644 index 76b39315637e..000000000000 --- a/packages/connectivity/connectivity_platform_interface/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# connectivity_platform_interface - -A common platform interface for the [`connectivity`][1] plugin. - -This interface allows platform-specific implementations of the `connectivity` -plugin, as well as the plugin itself, to ensure they are supporting the -same interface. - -# Usage - -To implement a new platform-specific implementation of `connectivity`, extend -[`ConnectivityPlatform`][2] with an implementation that performs the -platform-specific behavior, and when you register your plugin, set the default -`ConnectivityPlatform` by calling -`ConnectivityPlatform.instance = MyPlatformConnectivity()`. - -# Note on breaking changes - -Strongly prefer non-breaking changes (such as adding a method to the interface) -over breaking changes for this package. - -See https://flutter.dev/go/platform-interface-breaking-changes for a discussion -on why a less-clean interface is preferable to a breaking change. - -[1]: ../ -[2]: lib/connectivity_platform_interface.dart diff --git a/packages/connectivity/connectivity_platform_interface/lib/connectivity_platform_interface.dart b/packages/connectivity/connectivity_platform_interface/lib/connectivity_platform_interface.dart deleted file mode 100644 index 6667b353a4aa..000000000000 --- a/packages/connectivity/connectivity_platform_interface/lib/connectivity_platform_interface.dart +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; - -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -import 'src/enums.dart'; -import 'src/method_channel_connectivity.dart'; - -export 'src/enums.dart'; - -/// The interface that implementations of connectivity must implement. -/// -/// Platform implementations should extend this class rather than implement it as `Connectivity` -/// does not consider newly added methods to be breaking changes. Extending this class -/// (using `extends`) ensures that the subclass will get the default implementation, while -/// platform implementations that `implements` this interface will be broken by newly added -/// [ConnectivityPlatform] methods. -abstract class ConnectivityPlatform extends PlatformInterface { - /// Constructs a ConnectivityPlatform. - ConnectivityPlatform() : super(token: _token); - - static final Object _token = Object(); - - static ConnectivityPlatform _instance = MethodChannelConnectivity(); - - /// The default instance of [ConnectivityPlatform] to use. - /// - /// Defaults to [MethodChannelConnectivity]. - static ConnectivityPlatform get instance => _instance; - - /// Platform-specific plugins should set this with their own platform-specific - /// class that extends [ConnectivityPlatform] when they register themselves. - static set instance(ConnectivityPlatform instance) { - PlatformInterface.verifyToken(instance, _token); - _instance = instance; - } - - /// Checks the connection status of the device. - Future checkConnectivity() { - throw UnimplementedError('checkConnectivity() has not been implemented.'); - } - - /// Returns a Stream of ConnectivityResults changes. - Stream get onConnectivityChanged { - throw UnimplementedError( - 'get onConnectivityChanged has not been implemented.'); - } - - /// Obtains the wifi name (SSID) of the connected network - Future getWifiName() { - throw UnimplementedError('getWifiName() has not been implemented.'); - } - - /// Obtains the wifi BSSID of the connected network. - Future getWifiBSSID() { - throw UnimplementedError('getWifiBSSID() has not been implemented.'); - } - - /// Obtains the IP address of the connected wifi network - Future getWifiIP() { - throw UnimplementedError('getWifiIP() has not been implemented.'); - } - - /// Request to authorize the location service (Only on iOS). - Future requestLocationServiceAuthorization( - {bool requestAlwaysLocationUsage = false}) { - throw UnimplementedError( - 'requestLocationServiceAuthorization() has not been implemented.'); - } - - /// Get the current location service authorization (Only on iOS). - Future getLocationServiceAuthorization() { - throw UnimplementedError( - 'getLocationServiceAuthorization() has not been implemented.'); - } -} diff --git a/packages/connectivity/connectivity_platform_interface/lib/src/enums.dart b/packages/connectivity/connectivity_platform_interface/lib/src/enums.dart deleted file mode 100644 index b77f54cf60b2..000000000000 --- a/packages/connectivity/connectivity_platform_interface/lib/src/enums.dart +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/// Connection status check result. -enum ConnectivityResult { - /// WiFi: Device connected via Wi-Fi - wifi, - - /// Mobile: Device connected to cellular network - mobile, - - /// None: Device not connected to any network - none -} - -/// The status of the location service authorization. -enum LocationAuthorizationStatus { - /// The authorization of the location service is not determined. - notDetermined, - - /// This app is not authorized to use location. - restricted, - - /// User explicitly denied the location service. - denied, - - /// User authorized the app to access the location at any time. - authorizedAlways, - - /// User authorized the app to access the location when the app is visible to them. - authorizedWhenInUse, - - /// Status unknown. - unknown -} diff --git a/packages/connectivity/connectivity_platform_interface/lib/src/method_channel_connectivity.dart b/packages/connectivity/connectivity_platform_interface/lib/src/method_channel_connectivity.dart deleted file mode 100644 index bdf820ac3ba7..000000000000 --- a/packages/connectivity/connectivity_platform_interface/lib/src/method_channel_connectivity.dart +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; - -import 'package:connectivity_platform_interface/connectivity_platform_interface.dart'; -import 'package:flutter/services.dart'; -import 'package:meta/meta.dart'; - -import 'utils.dart'; - -/// An implementation of [ConnectivityPlatform] that uses method channels. -class MethodChannelConnectivity extends ConnectivityPlatform { - /// The method channel used to interact with the native platform. - @visibleForTesting - MethodChannel methodChannel = - MethodChannel('plugins.flutter.io/connectivity'); - - /// The event channel used to receive ConnectivityResult changes from the native platform. - @visibleForTesting - EventChannel eventChannel = - EventChannel('plugins.flutter.io/connectivity_status'); - - Stream? _onConnectivityChanged; - - /// Fires whenever the connectivity state changes. - Stream get onConnectivityChanged { - if (_onConnectivityChanged == null) { - _onConnectivityChanged = - eventChannel.receiveBroadcastStream().map((dynamic result) { - return result != null ? result.toString() : ''; - }).map(parseConnectivityResult); - } - return _onConnectivityChanged!; - } - - @override - Future checkConnectivity() async { - final String checkResult = - await methodChannel.invokeMethod('check') ?? ''; - return parseConnectivityResult(checkResult); - } - - @override - Future getWifiName() async { - String? wifiName = await methodChannel.invokeMethod('wifiName'); - // as Android might return , uniforming result - // our iOS implementation will return null - if (wifiName == '') { - wifiName = null; - } - return wifiName; - } - - @override - Future getWifiBSSID() { - return methodChannel.invokeMethod('wifiBSSID'); - } - - @override - Future getWifiIP() { - return methodChannel.invokeMethod('wifiIPAddress'); - } - - @override - Future requestLocationServiceAuthorization({ - bool requestAlwaysLocationUsage = false, - }) async { - final String requestLocationServiceResult = await methodChannel - .invokeMethod('requestLocationServiceAuthorization', - [requestAlwaysLocationUsage]) ?? - ''; - return parseLocationAuthorizationStatus(requestLocationServiceResult); - } - - @override - Future getLocationServiceAuthorization() async { - final String getLocationServiceResult = await methodChannel - .invokeMethod('getLocationServiceAuthorization') ?? - ''; - return parseLocationAuthorizationStatus(getLocationServiceResult); - } -} diff --git a/packages/connectivity/connectivity_platform_interface/lib/src/utils.dart b/packages/connectivity/connectivity_platform_interface/lib/src/utils.dart deleted file mode 100644 index 3b5b753f6c29..000000000000 --- a/packages/connectivity/connectivity_platform_interface/lib/src/utils.dart +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:connectivity_platform_interface/connectivity_platform_interface.dart'; - -/// Convert a String to a ConnectivityResult value. -ConnectivityResult parseConnectivityResult(String state) { - switch (state) { - case 'wifi': - return ConnectivityResult.wifi; - case 'mobile': - return ConnectivityResult.mobile; - case 'none': - default: - return ConnectivityResult.none; - } -} - -/// Convert a String to a LocationAuthorizationStatus value. -LocationAuthorizationStatus parseLocationAuthorizationStatus(String result) { - switch (result) { - case 'notDetermined': - return LocationAuthorizationStatus.notDetermined; - case 'restricted': - return LocationAuthorizationStatus.restricted; - case 'denied': - return LocationAuthorizationStatus.denied; - case 'authorizedAlways': - return LocationAuthorizationStatus.authorizedAlways; - case 'authorizedWhenInUse': - return LocationAuthorizationStatus.authorizedWhenInUse; - default: - return LocationAuthorizationStatus.unknown; - } -} diff --git a/packages/connectivity/connectivity_platform_interface/pubspec.yaml b/packages/connectivity/connectivity_platform_interface/pubspec.yaml deleted file mode 100644 index 2003fdde6eeb..000000000000 --- a/packages/connectivity/connectivity_platform_interface/pubspec.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: connectivity_platform_interface -description: A common platform interface for the connectivity plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/connectivity/connectivity_platform_interface -issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+connectivity%22 -# NOTE: We strongly prefer non-breaking changes, even at the expense of a -# less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.0.1 - -environment: - sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.12.13+hotfix.5" - -dependencies: - flutter: - sdk: flutter - meta: ^1.3.0 - plugin_platform_interface: ^2.0.0 - -dev_dependencies: - flutter_test: - sdk: flutter - pedantic: ^1.10.0 diff --git a/packages/connectivity/connectivity_platform_interface/test/method_channel_connectivity_test.dart b/packages/connectivity/connectivity_platform_interface/test/method_channel_connectivity_test.dart deleted file mode 100644 index b69feae252eb..000000000000 --- a/packages/connectivity/connectivity_platform_interface/test/method_channel_connectivity_test.dart +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:connectivity_platform_interface/connectivity_platform_interface.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:connectivity_platform_interface/src/method_channel_connectivity.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - group('$MethodChannelConnectivity', () { - final List log = []; - late MethodChannelConnectivity methodChannelConnectivity; - - setUp(() async { - methodChannelConnectivity = MethodChannelConnectivity(); - - methodChannelConnectivity.methodChannel - .setMockMethodCallHandler((MethodCall methodCall) async { - log.add(methodCall); - switch (methodCall.method) { - case 'check': - return 'wifi'; - case 'wifiName': - return '1337wifi'; - case 'wifiBSSID': - return 'c0:ff:33:c0:d3:55'; - case 'wifiIPAddress': - return '127.0.0.1'; - case 'requestLocationServiceAuthorization': - return 'authorizedAlways'; - case 'getLocationServiceAuthorization': - return 'authorizedAlways'; - default: - return null; - } - }); - log.clear(); - MethodChannel(methodChannelConnectivity.eventChannel.name) - .setMockMethodCallHandler((MethodCall methodCall) async { - switch (methodCall.method) { - case 'listen': - await _ambiguate(ServicesBinding.instance)! - .defaultBinaryMessenger - .handlePlatformMessage( - methodChannelConnectivity.eventChannel.name, - methodChannelConnectivity.eventChannel.codec - .encodeSuccessEnvelope('wifi'), - (_) {}, - ); - break; - case 'cancel': - default: - return null; - } - }); - }); - - test('onConnectivityChanged', () async { - final ConnectivityResult result = - await methodChannelConnectivity.onConnectivityChanged.first; - expect(result, ConnectivityResult.wifi); - }); - - test('getWifiName', () async { - final String? result = await methodChannelConnectivity.getWifiName(); - expect(result, '1337wifi'); - expect( - log, - [ - isMethodCall( - 'wifiName', - arguments: null, - ), - ], - ); - }); - - test('getWifiBSSID', () async { - final String? result = await methodChannelConnectivity.getWifiBSSID(); - expect(result, 'c0:ff:33:c0:d3:55'); - expect( - log, - [ - isMethodCall( - 'wifiBSSID', - arguments: null, - ), - ], - ); - }); - - test('getWifiIP', () async { - final String? result = await methodChannelConnectivity.getWifiIP(); - expect(result, '127.0.0.1'); - expect( - log, - [ - isMethodCall( - 'wifiIPAddress', - arguments: null, - ), - ], - ); - }); - - test('requestLocationServiceAuthorization', () async { - final LocationAuthorizationStatus result = - await methodChannelConnectivity.requestLocationServiceAuthorization(); - expect(result, LocationAuthorizationStatus.authorizedAlways); - expect( - log, - [ - isMethodCall( - 'requestLocationServiceAuthorization', - arguments: [false], - ), - ], - ); - }); - - test('getLocationServiceAuthorization', () async { - final LocationAuthorizationStatus result = - await methodChannelConnectivity.getLocationServiceAuthorization(); - expect(result, LocationAuthorizationStatus.authorizedAlways); - expect( - log, - [ - isMethodCall( - 'getLocationServiceAuthorization', - arguments: null, - ), - ], - ); - }); - - test('checkConnectivity', () async { - final ConnectivityResult result = - await methodChannelConnectivity.checkConnectivity(); - expect(result, ConnectivityResult.wifi); - expect( - log, - [ - isMethodCall( - 'check', - arguments: null, - ), - ], - ); - }); - }); -} - -/// This allows a value of type T or T? to be treated as a value of type T?. -/// -/// We use this so that APIs that have become non-nullable can still be used -/// with `!` and `?` on the stable branch. -// TODO(ianh): Remove this once we roll stable in late 2021. -T? _ambiguate(T? value) => value; diff --git a/packages/device_info/analysis_options.yaml b/packages/device_info/analysis_options.yaml deleted file mode 100644 index cda4f6e153e6..000000000000 --- a/packages/device_info/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../analysis_options_legacy.yaml diff --git a/packages/device_info/device_info/AUTHORS b/packages/device_info/device_info/AUTHORS deleted file mode 100644 index 493a0b4ef9c2..000000000000 --- a/packages/device_info/device_info/AUTHORS +++ /dev/null @@ -1,66 +0,0 @@ -# Below is a list of people and organizations that have contributed -# to the Flutter project. Names should be added to the list like so: -# -# Name/Organization - -Google Inc. -The Chromium Authors -German Saprykin -Benjamin Sauer -larsenthomasj@gmail.com -Ali Bitek -Pol Batlló -Anatoly Pulyaevskiy -Hayden Flinner -Stefano Rodriguez -Salvatore Giordano -Brian Armstrong -Paul DeMarco -Fabricio Nogueira -Simon Lightfoot -Ashton Thomas -Thomas Danner -Diego Velásquez -Hajime Nakamura -Tuyển Vũ Xuân -Miguel Ruivo -Sarthak Verma -Mike Diarmid -Invertase -Elliot Hesp -Vince Varga -Aawaz Gyawali -EUI Limited -Katarina Sheremet -Thomas Stockx -Sarbagya Dhaubanjar -Ozkan Eksi -Rishab Nayak -ko2ic -Jonathan Younger -Jose Sanchez -Debkanchan Samadder -Audrius Karosevicius -Lukasz Piliszczuk -SoundReply Solutions GmbH -Rafal Wachol -Pau Picas -Christian Weder -Alexandru Tuca -Christian Weder -Rhodes Davis Jr. -Luigi Agosti -Quentin Le Guennec -Koushik Ravikumar -Nissim Dsilva -Giancarlo Rocha -Ryo Miyake -Théo Champion -Kazuki Yamaguchi -Eitan Schwartz -Chris Rutkowski -Juan Alvarez -Aleksandr Yurkovskiy -Anton Borries -Alex Li -Rahul Raj <64.rahulraj@gmail.com> diff --git a/packages/device_info/device_info/CHANGELOG.md b/packages/device_info/device_info/CHANGELOG.md deleted file mode 100644 index 825b12d8c7ad..000000000000 --- a/packages/device_info/device_info/CHANGELOG.md +++ /dev/null @@ -1,186 +0,0 @@ -## NEXT - -* Updates compileSdkVersion to 31. - -## 2.0.3 - -* Remove references to the Android V1 embedding. -* Updated Android lint settings. -* Started using Background Platform Channels when available. - -## 2.0.2 - -* Update README to point to Plus Plugins version. - -## 2.0.1 - -* Migrate maven repository from jcenter to mavenCentral. - -## 2.0.0 - -* Migrate to null safety. -* Fix outdated links across a number of markdown files ([#3276](https://github.com/flutter/plugins/pull/3276)) - -## 1.0.1 - -* Update Flutter SDK constraint. - -## 1.0.0 - -* Announce 1.0.0. - -## 0.4.2+10 - -* Update Dart SDK constraint in example. - -## 0.4.2+9 - -* Update android compileSdkVersion to 29. - -## 0.4.2+8 - -* Keep handling deprecated Android v1 classes for backward compatibility. - -## 0.4.2+7 - -* Port device_info plugin to use platform interface. - -## 0.4.2+6 - -* Moved everything from device_info to device_info/device_info - -## 0.4.2+5 - -* Update package:e2e reference to use the local version in the flutter/plugins - repository. - -## 0.4.2+4 - -Update lower bound of dart dependency to 2.1.0. - -## 0.4.2+3 - -* Declare API stability and compatibility with `1.0.0` (more details at: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0). - -## 0.4.2+2 - -* Fix CocoaPods podspec lint warnings. - -## 0.4.2+1 - -* Bump the minimum Flutter version to 1.12.13+hotfix.5. -* Remove deprecated API usage warning in AndroidIntentPlugin.java. -* Migrates the Android example to V2 embedding. -* Bumps AGP to 3.6.1. - -## 0.4.2 - -* Add systemFeatures to AndroidDeviceInfo. - -## 0.4.1+5 - -* Make the pedantic dev_dependency explicit. - -## 0.4.1+4 - -* Remove the deprecated `author:` field from pubspec.yaml -* Migrate the plugin to the pubspec platforms manifest. -* Require Flutter SDK 1.10.0 or greater. - -## 0.4.1+3 - -* Fix pedantic errors. Adds some missing documentation and fixes unawaited - futures in the tests. - -## 0.4.1+2 - -* Remove AndroidX warning. - -## 0.4.1+1 - -* Include lifecycle dependency as a compileOnly one on Android to resolve - potential version conflicts with other transitive libraries. - -## 0.4.1 - -* Support the v2 Android embedding. -* Update to AndroidX. -* Migrate to using the new e2e test binding. -* Add a e2e test. - - -## 0.4.0+4 - -* Define clang module for iOS. - -## 0.4.0+3 - -* Update and migrate iOS example project. - -## 0.4.0+2 - -* Bump minimum Flutter version to 1.5.0. -* Add missing template type parameter to `invokeMethod` calls. -* Replace invokeMethod with invokeMapMethod wherever necessary. - -## 0.4.0+1 - -* Log a more detailed warning at build time about the previous AndroidX - migration. - -## 0.4.0 - -* **Breaking change**. Migrate from the deprecated original Android Support - Library to AndroidX. This shouldn't result in any functional changes, but it - requires any Android apps using this plugin to [also - migrate](https://developer.android.com/jetpack/androidx/migrate) if they're - using the original support library. - -## 0.3.0 - -* Added ability to get Android ID for Android devices - -## 0.2.1 - -* Updated Gradle tooling to match Android Studio 3.1.2. - -## 0.2.0 - -* **Breaking change**. Set SDK constraints to match the Flutter beta release. - -## 0.1.2 - -* Fixed Dart 2 type errors. - -## 0.1.1 - -* Simplified and upgraded Android project template to Android SDK 27. -* Updated package description. - -## 0.1.0 - -* **Breaking change**. Upgraded to Gradle 4.1 and Android Studio Gradle plugin - 3.0.1. Older Flutter projects need to upgrade their Gradle setup as well in - order to use this version of the plugin. Instructions can be found - [here](https://github.com/flutter/flutter/wiki/Updating-Flutter-projects-to-Gradle-4.1-and-Android-Studio-Gradle-plugin-3.0.1). - -## 0.0.5 - -* Added FLT prefix to iOS types - -## 0.0.4 - -* Fixed Java/Dart communication error with empty lists - -## 0.0.3 - -* Added support for utsname - -## 0.0.2 - -* Fixed broken type comparison -* Added "isPhysicalDevice" field, detecting emulators/simulators - -## 0.0.1 - -* Implements platform-specific device/OS properties diff --git a/packages/device_info/device_info/LICENSE b/packages/device_info/device_info/LICENSE deleted file mode 100644 index c6823b81eb84..000000000000 --- a/packages/device_info/device_info/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright 2013 The Flutter Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/device_info/device_info/README.md b/packages/device_info/device_info/README.md deleted file mode 100644 index 34cf9bb2ac2b..000000000000 --- a/packages/device_info/device_info/README.md +++ /dev/null @@ -1,46 +0,0 @@ -# device_info - ---- - -## Deprecation Notice - -This plugin has been replaced by the [Flutter Community Plus -Plugins](https://plus.fluttercommunity.dev/) version, -[`device_info_plus`](https://pub.dev/packages/device_info_plus). -No further updates are planned to this plugin, and we encourage all users to -migrate to the Plus version. - -Critical fixes (e.g., for any security incidents) will be provided through the -end of 2021, at which point this package will be marked as discontinued. - ---- - -Get current device information from within the Flutter application. - -# Usage - -Import `package:device_info/device_info.dart`, instantiate `DeviceInfoPlugin` -and use the Android and iOS getters to get platform-specific device -information. - -Example: - -```dart -import 'package:device_info/device_info.dart'; - -DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); -AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; -print('Running on ${androidInfo.model}'); // e.g. "Moto G (4)" - -IosDeviceInfo iosInfo = await deviceInfo.iosInfo; -print('Running on ${iosInfo.utsname.machine}'); // e.g. "iPod7,1" -``` - -You will find links to the API docs on the [pub page](https://pub.dev/packages/device_info). - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). - -For help on editing plugin code, view the [documentation](https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin). diff --git a/packages/device_info/device_info/android/build.gradle b/packages/device_info/device_info/android/build.gradle deleted file mode 100644 index 67fbf1fe5c65..000000000000 --- a/packages/device_info/device_info/android/build.gradle +++ /dev/null @@ -1,48 +0,0 @@ -group 'io.flutter.plugins.deviceinfo' -version '1.0-SNAPSHOT' - -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:3.6.1' - } -} - -rootProject.allprojects { - repositories { - google() - mavenCentral() - } -} - -apply plugin: 'com.android.library' - -android { - compileSdkVersion 31 - - defaultConfig { - minSdkVersion 16 - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - lintOptions { - disable 'InvalidPackage' - disable 'GradleDependency' - } - - - testOptions { - unitTests.includeAndroidResources = true - unitTests.returnDefaultValues = true - unitTests.all { - testLogging { - events "passed", "skipped", "failed", "standardOut", "standardError" - outputs.upToDateWhen {false} - showStandardStreams = true - } - } - } -} diff --git a/packages/device_info/device_info/android/settings.gradle b/packages/device_info/device_info/android/settings.gradle deleted file mode 100644 index 0e75718c9a9d..000000000000 --- a/packages/device_info/device_info/android/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'device_info' diff --git a/packages/device_info/device_info/android/src/main/AndroidManifest.xml b/packages/device_info/device_info/android/src/main/AndroidManifest.xml deleted file mode 100644 index 03e76883266f..000000000000 --- a/packages/device_info/device_info/android/src/main/AndroidManifest.xml +++ /dev/null @@ -1,3 +0,0 @@ - - diff --git a/packages/device_info/device_info/android/src/main/java/io/flutter/plugins/deviceinfo/DeviceInfoPlugin.java b/packages/device_info/device_info/android/src/main/java/io/flutter/plugins/deviceinfo/DeviceInfoPlugin.java deleted file mode 100644 index 756f5e0ce820..000000000000 --- a/packages/device_info/device_info/android/src/main/java/io/flutter/plugins/deviceinfo/DeviceInfoPlugin.java +++ /dev/null @@ -1,67 +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.deviceinfo; - -import android.content.Context; -import android.util.Log; -import io.flutter.embedding.engine.plugins.FlutterPlugin; -import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.plugin.common.MethodChannel; -import io.flutter.plugin.common.MethodCodec; -import io.flutter.plugin.common.StandardMethodCodec; -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; - -/** DeviceInfoPlugin */ -public class DeviceInfoPlugin implements FlutterPlugin { - static final String TAG = "DeviceInfoPlugin"; - MethodChannel channel; - - /** Plugin registration. */ - @SuppressWarnings("deprecation") - public static void registerWith(io.flutter.plugin.common.PluginRegistry.Registrar registrar) { - DeviceInfoPlugin plugin = new DeviceInfoPlugin(); - plugin.setupMethodChannel(registrar.messenger(), registrar.context()); - } - - @Override - public void onAttachedToEngine(FlutterPlugin.FlutterPluginBinding binding) { - setupMethodChannel(binding.getBinaryMessenger(), binding.getApplicationContext()); - } - - @Override - public void onDetachedFromEngine(FlutterPlugin.FlutterPluginBinding binding) { - tearDownChannel(); - } - - private void setupMethodChannel(BinaryMessenger messenger, Context context) { - String channelName = "plugins.flutter.io/device_info"; - // TODO(gaaclarke): Remove reflection guard when https://github.com/flutter/engine/pull/29147 - // becomes available on the stable branch. - try { - Class methodChannelClass = Class.forName("io.flutter.plugin.common.MethodChannel"); - Class taskQueueClass = Class.forName("io.flutter.plugin.common.BinaryMessenger$TaskQueue"); - Method makeBackgroundTaskQueue = messenger.getClass().getMethod("makeBackgroundTaskQueue"); - Object taskQueue = makeBackgroundTaskQueue.invoke(messenger); - Constructor constructor = - methodChannelClass.getConstructor( - BinaryMessenger.class, String.class, MethodCodec.class, taskQueueClass); - channel = - constructor.newInstance(messenger, channelName, StandardMethodCodec.INSTANCE, taskQueue); - Log.d(TAG, "Use TaskQueues."); - } catch (Exception ex) { - channel = new MethodChannel(messenger, channelName); - Log.d(TAG, "Don't use TaskQueues."); - } - final MethodCallHandlerImpl handler = - new MethodCallHandlerImpl(context.getContentResolver(), context.getPackageManager()); - channel.setMethodCallHandler(handler); - } - - private void tearDownChannel() { - channel.setMethodCallHandler(null); - channel = null; - } -} diff --git a/packages/device_info/device_info/android/src/main/java/io/flutter/plugins/deviceinfo/MethodCallHandlerImpl.java b/packages/device_info/device_info/android/src/main/java/io/flutter/plugins/deviceinfo/MethodCallHandlerImpl.java deleted file mode 100644 index 531e5db0c237..000000000000 --- a/packages/device_info/device_info/android/src/main/java/io/flutter/plugins/deviceinfo/MethodCallHandlerImpl.java +++ /dev/null @@ -1,133 +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.deviceinfo; - -import android.annotation.SuppressLint; -import android.content.ContentResolver; -import android.content.pm.FeatureInfo; -import android.content.pm.PackageManager; -import android.os.Build; -import android.provider.Settings; -import io.flutter.plugin.common.MethodCall; -import io.flutter.plugin.common.MethodChannel; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -/** - * The implementation of {@link MethodChannel.MethodCallHandler} for the plugin. Responsible for - * receiving method calls from method channel. - */ -class MethodCallHandlerImpl implements MethodChannel.MethodCallHandler { - - private final ContentResolver contentResolver; - private final PackageManager packageManager; - - /** Substitute for missing values. */ - private static final String[] EMPTY_STRING_LIST = new String[] {}; - - /** Constructs DeviceInfo. {@code contentResolver} and {@code packageManager} must not be null. */ - MethodCallHandlerImpl(ContentResolver contentResolver, PackageManager packageManager) { - this.contentResolver = contentResolver; - this.packageManager = packageManager; - } - - @Override - public void onMethodCall(MethodCall call, MethodChannel.Result result) { - if (call.method.equals("getAndroidDeviceInfo")) { - Map build = new HashMap<>(); - build.put("board", Build.BOARD); - build.put("bootloader", Build.BOOTLOADER); - build.put("brand", Build.BRAND); - build.put("device", Build.DEVICE); - build.put("display", Build.DISPLAY); - build.put("fingerprint", Build.FINGERPRINT); - build.put("hardware", Build.HARDWARE); - build.put("host", Build.HOST); - build.put("id", Build.ID); - build.put("manufacturer", Build.MANUFACTURER); - build.put("model", Build.MODEL); - build.put("product", Build.PRODUCT); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - build.put("supported32BitAbis", Arrays.asList(Build.SUPPORTED_32_BIT_ABIS)); - build.put("supported64BitAbis", Arrays.asList(Build.SUPPORTED_64_BIT_ABIS)); - build.put("supportedAbis", Arrays.asList(Build.SUPPORTED_ABIS)); - } else { - build.put("supported32BitAbis", Arrays.asList(EMPTY_STRING_LIST)); - build.put("supported64BitAbis", Arrays.asList(EMPTY_STRING_LIST)); - build.put("supportedAbis", Arrays.asList(EMPTY_STRING_LIST)); - } - build.put("tags", Build.TAGS); - build.put("type", Build.TYPE); - build.put("isPhysicalDevice", !isEmulator()); - build.put("androidId", getAndroidId()); - - build.put("systemFeatures", Arrays.asList(getSystemFeatures())); - - Map version = new HashMap<>(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - version.put("baseOS", Build.VERSION.BASE_OS); - version.put("previewSdkInt", Build.VERSION.PREVIEW_SDK_INT); - version.put("securityPatch", Build.VERSION.SECURITY_PATCH); - } - version.put("codename", Build.VERSION.CODENAME); - version.put("incremental", Build.VERSION.INCREMENTAL); - version.put("release", Build.VERSION.RELEASE); - version.put("sdkInt", Build.VERSION.SDK_INT); - build.put("version", version); - - result.success(build); - } else { - result.notImplemented(); - } - } - - private String[] getSystemFeatures() { - FeatureInfo[] featureInfos = packageManager.getSystemAvailableFeatures(); - if (featureInfos == null) { - return EMPTY_STRING_LIST; - } - String[] features = new String[featureInfos.length]; - for (int i = 0; i < featureInfos.length; i++) { - features[i] = featureInfos[i].name; - } - return features; - } - - /** - * Returns the Android hardware device ID that is unique between the device + user and app - * signing. This key will change if the app is uninstalled or its data is cleared. Device factory - * reset will also result in a value change. - * - * @return The android ID - */ - @SuppressLint("HardwareIds") - private String getAndroidId() { - return Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID); - } - - /** - * A simple emulator-detection based on the flutter tools detection logic and a couple of legacy - * detection systems - */ - private boolean isEmulator() { - return (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic")) - || Build.FINGERPRINT.startsWith("generic") - || Build.FINGERPRINT.startsWith("unknown") - || Build.HARDWARE.contains("goldfish") - || Build.HARDWARE.contains("ranchu") - || Build.MODEL.contains("google_sdk") - || Build.MODEL.contains("Emulator") - || Build.MODEL.contains("Android SDK built for x86") - || Build.MANUFACTURER.contains("Genymotion") - || Build.PRODUCT.contains("sdk_google") - || Build.PRODUCT.contains("google_sdk") - || Build.PRODUCT.contains("sdk") - || Build.PRODUCT.contains("sdk_x86") - || Build.PRODUCT.contains("vbox86p") - || Build.PRODUCT.contains("emulator") - || Build.PRODUCT.contains("simulator"); - } -} diff --git a/packages/device_info/device_info/example/README.md b/packages/device_info/device_info/example/README.md deleted file mode 100644 index ea47551011d0..000000000000 --- a/packages/device_info/device_info/example/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# device_info_example - -Demonstrates how to use the `device_info` plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/device_info/device_info/example/android/app/build.gradle b/packages/device_info/device_info/example/android/app/build.gradle deleted file mode 100644 index 9371207aba2d..000000000000 --- a/packages/device_info/device_info/example/android/app/build.gradle +++ /dev/null @@ -1,60 +0,0 @@ -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -apply plugin: 'com.android.application' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - -android { - compileSdkVersion 31 - - lintOptions { - disable 'InvalidPackage' - } - - defaultConfig { - applicationId "io.flutter.plugins.deviceinfoexample" - minSdkVersion 16 - targetSdkVersion 28 - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug - } - } -} - -flutter { - source '../..' -} - -dependencies { - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test:runner:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' -} diff --git a/packages/device_info/device_info/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/device_info/device_info/example/android/app/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 9a4163a4f5ee..000000000000 --- a/packages/device_info/device_info/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/packages/device_info/device_info/example/android/app/src/main/AndroidManifest.xml b/packages/device_info/device_info/example/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index 4268475986a3..000000000000 --- a/packages/device_info/device_info/example/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/packages/device_info/device_info/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/device_info/device_info/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index db77bb4b7b0906d62b1847e87f15cdcacf6a4f29..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ diff --git a/packages/device_info/device_info/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/device_info/device_info/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 17987b79bb8a35cc66c3c1fd44f5a5526c1b78be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ diff --git a/packages/device_info/device_info/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/device_info/device_info/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index d5f1c8d34e7a88e3f88bea192c3a370d44689c3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof diff --git a/packages/device_info/device_info/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/device_info/device_info/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 4d6372eebdb28e45604e46eeda8dd24651419bc0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` diff --git a/packages/device_info/device_info/example/android/build.gradle b/packages/device_info/device_info/example/android/build.gradle deleted file mode 100644 index 3274eb601b9b..000000000000 --- a/packages/device_info/device_info/example/android/build.gradle +++ /dev/null @@ -1,29 +0,0 @@ -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:3.6.1' - } -} - -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(':app') -} - -task clean(type: Delete) { - delete rootProject.buildDir -} diff --git a/packages/device_info/device_info/example/android/gradle.properties b/packages/device_info/device_info/example/android/gradle.properties deleted file mode 100644 index 38c8d4544ff1..000000000000 --- a/packages/device_info/device_info/example/android/gradle.properties +++ /dev/null @@ -1,4 +0,0 @@ -org.gradle.jvmargs=-Xmx1536M -android.enableR8=true -android.useAndroidX=true -android.enableJetifier=true diff --git a/packages/device_info/device_info/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/device_info/device_info/example/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index c243e25a0fff..000000000000 --- a/packages/device_info/device_info/example/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Mon Mar 09 11:05:13 GMT 2020 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip diff --git a/packages/device_info/device_info/example/android/settings.gradle b/packages/device_info/device_info/example/android/settings.gradle deleted file mode 100644 index 115da6cb4f4d..000000000000 --- a/packages/device_info/device_info/example/android/settings.gradle +++ /dev/null @@ -1,15 +0,0 @@ -include ':app' - -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() - -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withInputStream { stream -> plugins.load(stream) } -} - -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory -} diff --git a/packages/device_info/device_info/example/integration_test/device_info_test.dart b/packages/device_info/device_info/example/integration_test/device_info_test.dart deleted file mode 100644 index 953eb856d62a..000000000000 --- a/packages/device_info/device_info/example/integration_test/device_info_test.dart +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:io'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:device_info/device_info.dart'; -import 'package:integration_test/integration_test.dart'; - -void main() { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - late IosDeviceInfo iosInfo; - late AndroidDeviceInfo androidInfo; - - setUpAll(() async { - final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin(); - if (Platform.isIOS) { - iosInfo = await deviceInfoPlugin.iosInfo; - } else if (Platform.isAndroid) { - androidInfo = await deviceInfoPlugin.androidInfo; - } - }); - - testWidgets('Can get non-null device model', (WidgetTester tester) async { - if (Platform.isIOS) { - expect(iosInfo.model, isNotNull); - } else if (Platform.isAndroid) { - expect(androidInfo.model, isNotNull); - } - }); -} diff --git a/packages/device_info/device_info/example/ios/Flutter/AppFrameworkInfo.plist b/packages/device_info/device_info/example/ios/Flutter/AppFrameworkInfo.plist deleted file mode 100644 index 6c2de8086bcd..000000000000 --- a/packages/device_info/device_info/example/ios/Flutter/AppFrameworkInfo.plist +++ /dev/null @@ -1,30 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - App - CFBundleIdentifier - io.flutter.flutter.app - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - App - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - UIRequiredDeviceCapabilities - - arm64 - - MinimumOSVersion - 8.0 - - diff --git a/packages/device_info/device_info/example/ios/Flutter/Debug.xcconfig b/packages/device_info/device_info/example/ios/Flutter/Debug.xcconfig deleted file mode 100644 index e8efba114687..000000000000 --- a/packages/device_info/device_info/example/ios/Flutter/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" -#include "Generated.xcconfig" diff --git a/packages/device_info/device_info/example/ios/Flutter/Release.xcconfig b/packages/device_info/device_info/example/ios/Flutter/Release.xcconfig deleted file mode 100644 index 399e9340e6f6..000000000000 --- a/packages/device_info/device_info/example/ios/Flutter/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" -#include "Generated.xcconfig" diff --git a/packages/device_info/device_info/example/ios/Podfile b/packages/device_info/device_info/example/ios/Podfile deleted file mode 100644 index f7d6a5e68c3a..000000000000 --- a/packages/device_info/device_info/example/ios/Podfile +++ /dev/null @@ -1,38 +0,0 @@ -# Uncomment this line to define a global platform for your project -# platform :ios, '9.0' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_ios_podfile_setup - -target 'Runner' do - flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - flutter_additional_ios_build_settings(target) - end -end diff --git a/packages/device_info/device_info/example/ios/Runner.xcodeproj/project.pbxproj b/packages/device_info/device_info/example/ios/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index ca599db5b7ac..000000000000 --- a/packages/device_info/device_info/example/ios/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,460 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 4C26954642C9965233939F98 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 17470FCDF9FA37CB94B63753 /* libPods-Runner.a */; }; - 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; - 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 9705A1C41CF9048500538489 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 17470FCDF9FA37CB94B63753 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; - 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; - 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - C8043F3EE7ED1716F368CC90 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - E96AF818D5456D130681C78B /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 97C146EB1CF9000F007C117D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 4C26954642C9965233939F98 /* libPods-Runner.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 20A0DD43C00A880430740858 /* Pods */ = { - isa = PBXGroup; - children = ( - C8043F3EE7ED1716F368CC90 /* Pods-Runner.debug.xcconfig */, - E96AF818D5456D130681C78B /* Pods-Runner.release.xcconfig */, - ); - name = Pods; - sourceTree = ""; - }; - 9740EEB11CF90186004384FC /* Flutter */ = { - isa = PBXGroup; - children = ( - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 9740EEB31CF90195004384FC /* Generated.xcconfig */, - ); - name = Flutter; - sourceTree = ""; - }; - 97C146E51CF9000F007C117D = { - isa = PBXGroup; - children = ( - 9740EEB11CF90186004384FC /* Flutter */, - 97C146F01CF9000F007C117D /* Runner */, - 97C146EF1CF9000F007C117D /* Products */, - 20A0DD43C00A880430740858 /* Pods */, - EA17DAB2B097E79A4CABE344 /* Frameworks */, - ); - sourceTree = ""; - }; - 97C146EF1CF9000F007C117D /* Products */ = { - isa = PBXGroup; - children = ( - 97C146EE1CF9000F007C117D /* Runner.app */, - ); - name = Products; - sourceTree = ""; - }; - 97C146F01CF9000F007C117D /* Runner */ = { - isa = PBXGroup; - children = ( - 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, - 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, - 97C146FA1CF9000F007C117D /* Main.storyboard */, - 97C146FD1CF9000F007C117D /* Assets.xcassets */, - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, - 97C147021CF9000F007C117D /* Info.plist */, - 97C146F11CF9000F007C117D /* Supporting Files */, - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, - ); - path = Runner; - sourceTree = ""; - }; - 97C146F11CF9000F007C117D /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 97C146F21CF9000F007C117D /* main.m */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - EA17DAB2B097E79A4CABE344 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 17470FCDF9FA37CB94B63753 /* libPods-Runner.a */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 97C146ED1CF9000F007C117D /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - B8856E1B697C88C1C62EE937 /* [CP] Check Pods Manifest.lock */, - 9740EEB61CF901F6004384FC /* Run Script */, - 97C146EA1CF9000F007C117D /* Sources */, - 97C146EB1CF9000F007C117D /* Frameworks */, - 97C146EC1CF9000F007C117D /* Resources */, - 9705A1C41CF9048500538489 /* Embed Frameworks */, - 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Runner; - productName = Runner; - productReference = 97C146EE1CF9000F007C117D /* Runner.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 97C146E61CF9000F007C117D /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 1100; - ORGANIZATIONNAME = "The Flutter Authors"; - TargetAttributes = { - 97C146ED1CF9000F007C117D = { - CreatedOnToolsVersion = 7.3.1; - }; - }; - }; - buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 97C146E51CF9000F007C117D; - productRefGroup = 97C146EF1CF9000F007C117D /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 97C146ED1CF9000F007C117D /* Runner */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 97C146EC1CF9000F007C117D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Thin Binary"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; - }; - 9740EEB61CF901F6004384FC /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; - }; - B8856E1B697C88C1C62EE937 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 97C146EA1CF9000F007C117D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, - 97C146F31CF9000F007C117D /* main.m in Sources */, - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 97C146FA1CF9000F007C117D /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C146FB1CF9000F007C117D /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C147001CF9000F007C117D /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 97C147031CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 97C147041CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 97C147061CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.deviceInfoExample; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 97C147071CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.deviceInfoExample; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147031CF9000F007C117D /* Debug */, - 97C147041CF9000F007C117D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147061CF9000F007C117D /* Debug */, - 97C147071CF9000F007C117D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 97C146E61CF9000F007C117D /* Project object */; -} diff --git a/packages/device_info/device_info/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/device_info/device_info/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a6254f..000000000000 --- a/packages/device_info/device_info/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/packages/device_info/device_info/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/device_info/device_info/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index 3bb3697ef41c..000000000000 --- a/packages/device_info/device_info/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/device_info/device_info/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/device_info/device_info/example/ios/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 21a3cc14c74e..000000000000 --- a/packages/device_info/device_info/example/ios/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/packages/device_info/device_info/example/ios/Runner/AppDelegate.h b/packages/device_info/device_info/example/ios/Runner/AppDelegate.h deleted file mode 100644 index 0681d288bb70..000000000000 --- a/packages/device_info/device_info/example/ios/Runner/AppDelegate.h +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import -#import - -@interface AppDelegate : FlutterAppDelegate - -@end diff --git a/packages/device_info/device_info/example/ios/Runner/AppDelegate.m b/packages/device_info/device_info/example/ios/Runner/AppDelegate.m deleted file mode 100644 index 30b87969f44a..000000000000 --- a/packages/device_info/device_info/example/ios/Runner/AppDelegate.m +++ /dev/null @@ -1,17 +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. - -#include "AppDelegate.h" -#include "GeneratedPluginRegistrant.h" - -@implementation AppDelegate - -- (BOOL)application:(UIApplication *)application - didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - [GeneratedPluginRegistrant registerWithRegistry:self]; - // Override point for customization after application launch. - return [super application:application didFinishLaunchingWithOptions:launchOptions]; -} - -@end diff --git a/packages/device_info/device_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/device_info/device_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index d22f10b2ab63..000000000000 --- a/packages/device_info/device_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,116 +0,0 @@ -{ - "images" : [ - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@3x.png", - "scale" : "3x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/packages/device_info/device_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/device_info/device_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png deleted file mode 100644 index 28c6bf03016f6c994b70f38d1b7346e5831b531f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 564 zcmV-40?Yl0P)Px$?ny*JR5%f>l)FnDQ543{x%ZCiu33$Wg!pQFfT_}?5Q|_VSlIbLC`dpoMXL}9 zHfd9&47Mo(7D231gb+kjFxZHS4-m~7WurTH&doVX2KI5sU4v(sJ1@T9eCIKPjsqSr z)C01LsCxk=72-vXmX}CQD#BD;Cthymh&~=f$Q8nn0J<}ZrusBy4PvRNE}+1ceuj8u z0mW5k8fmgeLnTbWHGwfKA3@PdZxhn|PypR&^p?weGftrtCbjF#+zk_5BJh7;0`#Wr zgDpM_;Ax{jO##IrT`Oz;MvfwGfV$zD#c2xckpcXC6oou4ML~ezCc2EtnsQTB4tWNg z?4bkf;hG7IMfhgNI(FV5Gs4|*GyMTIY0$B=_*mso9Ityq$m^S>15>-?0(zQ<8Qy<_TjHE33(?_M8oaM zyc;NxzRVK@DL6RJnX%U^xW0Gpg(lXp(!uK1v0YgHjs^ZXSQ|m#lV7ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 diff --git a/packages/device_info/device_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/device_info/device_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png deleted file mode 100644 index f091b6b0bca859a3f474b03065bef75ba58a9e4c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1588 zcmV-42Fv-0P)C1SqPt}wig>|5Crh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)y zRAZ>eDe#*r`yDAVgB_R*LB*MAc)8(b{g{9McCXW!lq7r(btRoB9!8B-#AI6JMb~YFBEvdsV)`mEQO^&#eRKx@b&x- z5lZm*!WfD8oCLzfHGz#u7sT0^VLMI1MqGxF^v+`4YYnVYgk*=kU?HsSz{v({E3lb9 z>+xILjBN)t6`=g~IBOelGQ(O990@BfXf(DRI5I$qN$0Gkz-FSc$3a+2fX$AedL4u{ z4V+5Ong(9LiGcIKW?_352sR;LtDPmPJXI{YtT=O8=76o9;*n%_m|xo!i>7$IrZ-{l z-x3`7M}qzHsPV@$v#>H-TpjDh2UE$9g6sysUREDy_R(a)>=eHw-WAyfIN z*qb!_hW>G)Tu8nSw9yn#3wFMiLcfc4pY0ek1}8(NqkBR@t4{~oC>ryc-h_ByH(Cg5 z>ao-}771+xE3um9lWAY1FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zm zZQj(aA_HeBY&OC^jj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5Kh zX*|AU4QE#~SgPzOXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&g3j|zDgC+}2Q_v%YfDax z!?umcN^n}KYQ|a$Lr+51Nf9dkkYFSjZZjkma$0KOj+;aQ&721~t7QUKx61J3(P4P1 zstI~7-wOACnWP4=8oGOwz%vNDqD8w&Q`qcNGGrbbf&0s9L0De{4{mRS?o0MU+nR_! zrvshUau0G^DeMhM_v{5BuLjb#Hh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG z;P%vktHcXwTj~+IE(~px)3*MY77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC z(zYC$g|@+I+kY;dg_dE>scBf&bP1Nc@Hz<3R)V`=AGkc;8CXqdi=B4l2k|g;2%#m& z*jfX^%b!A8#bI!j9-0Fi0bOXl(-c^AB9|nQaE`*)Hw+o&jS9@7&Gov#HbD~#d{twV zXd^Tr^mWLfFh$@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)y zI9C9*oUga6=hxw6QasLPnee@3^Rr*M{CdaL5=R41nLs(AHk_=Y+A9$2&H(B7!_pURs&8aNw7?`&Z&xY_Ye z)~D5Bog^td-^QbUtkTirdyK^mTHAOuptDflut!#^lnKqU md>ggs(5nOWAqO?umG&QVYK#ibz}*4>0000U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~cTNYR~@Y9KepMPrrB8cABapAFa=`H+UGhkXUZV1GnwR1*lPyZ;*K(i~2gp|@bzp8}og7e*#% zEnr|^CWdVV!-4*Y_7rFvlww2Ze+>j*!Z!pQ?2l->4q#nqRu9`ELo6RMS5=br47g_X zRw}P9a7RRYQ%2Vsd0Me{_(EggTnuN6j=-?uFS6j^u69elMypu?t>op*wBx<=Wx8?( ztpe^(fwM6jJX7M-l*k3kEpWOl_Vk3@(_w4oc}4YF4|Rt=2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX?F&rExl^W|G8Z8;`5z-k}OGNZ0#WVb$WCpQu-_YsiqKP?BB# vzVHS-CTUF4Ozn5G+mq_~Qqto~ahA+K`|lyv3(-e}00000NkvXXu0mjfd`9t{ diff --git a/packages/device_info/device_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/device_info/device_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png deleted file mode 100644 index d0ef06e7edb86cdfe0d15b4b0d98334a86163658..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1716 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 diff --git a/packages/device_info/device_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/device_info/device_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png deleted file mode 100644 index c8f9ed8f5cee1c98386d13b17e89f719e83555b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1895 zcmV-t2blPYP)FQtfgmafE#=YDCq`qUBt#QpG%*H6QHY765~R=q zZ6iudfM}q!Pz#~9JgOi8QJ|DSu?1-*(kSi1K4#~5?#|rh?sS)(-JQqX*}ciXJ56_H zdw=^s_srbAdqxlvGyrgGet#6T7_|j;95sL%MtM;q86vOxKM$f#puR)Bjv9Zvz9-di zXOTSsZkM83)E9PYBXC<$6(|>lNLVBb&&6y{NByFCp%6+^ALR@NCTse_wqvNmSWI-m z!$%KlHFH2omF!>#%1l3LTZg(s7eof$7*xB)ZQ0h?ejh?Ta9fDv59+u#MokW+1t8Zb zgHv%K(u9G^Lv`lh#f3<6!JVTL3(dCpxHbnbA;kKqQyd1~^Xe0VIaYBSWm6nsr;dFj z4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3QU4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKubPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`Q zykI#*+rQ=z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRe zt3L_uNyQ*cE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=ky zx=~RKa4{iTm{_>_vSCm?$Ej=i6@=m%@VvAITnigVg{&@!7CDgs908761meDK5azA} z4?=NOH|PdvabgJ&fW2{Mo$Q0CcD8Qc84%{JPYt5EiG{MdLIAeX%T=D7NIP4%Hw}p9 zg)==!2Lbp#j{u_}hMiao9=!VSyx0gHbeCS`;q&vzeq|fs`y&^X-lso(Ls@-706qmA z7u*T5PMo_w3{se1t2`zWeO^hOvTsohG_;>J0wVqVe+n)AbQCx)yh9;w+J6?NF5Lmo zecS@ieAKL8%bVd@+-KT{yI|S}O>pYckUFs;ry9Ow$CD@ztz5K-*D$^{i(_1llhSh^ zEkL$}tsQt5>QA^;QgjgIfBDmcOgi5YDyu?t6vSnbp=1+@6D& z5MJ}B8q;bRlVoxasyhcUF1+)o`&3r0colr}QJ3hcSdLu;9;td>kf@Tcn<@9sIx&=m z;AD;SCh95=&p;$r{Xz3iWCO^MX83AGJ(yH&eTXgv|0=34#-&WAmw{)U7OU9!Wz^!7 zZ%jZFi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i h0WYBP*#0Ks^FNSabJA*5${_#%002ovPDHLkV1oKhTl@e3 diff --git a/packages/device_info/device_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/device_info/device_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png deleted file mode 100644 index a6d6b8609df07bf62e5100a53a01510388bd2b22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ diff --git a/packages/device_info/device_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/device_info/device_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png deleted file mode 100644 index a6d6b8609df07bf62e5100a53a01510388bd2b22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ diff --git a/packages/device_info/device_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/device_info/device_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png deleted file mode 100644 index 75b2d164a5a98e212cca15ea7bf2ab5de5108680..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3831 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 diff --git a/packages/device_info/device_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/device_info/device_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png deleted file mode 100644 index c4df70d39da7941ef3f6dcb7f06a192d8dcb308d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1888 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 iW8 - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/device_info/device_info/example/ios/Runner/Base.lproj/Main.storyboard b/packages/device_info/device_info/example/ios/Runner/Base.lproj/Main.storyboard deleted file mode 100644 index f3c28516fb38..000000000000 --- a/packages/device_info/device_info/example/ios/Runner/Base.lproj/Main.storyboard +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/device_info/device_info/example/ios/Runner/Info.plist b/packages/device_info/device_info/example/ios/Runner/Info.plist deleted file mode 100644 index 1ea53f8dc54b..000000000000 --- a/packages/device_info/device_info/example/ios/Runner/Info.plist +++ /dev/null @@ -1,49 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - device_info_example - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UIRequiredDeviceCapabilities - - arm64 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - - diff --git a/packages/device_info/device_info/example/ios/Runner/main.m b/packages/device_info/device_info/example/ios/Runner/main.m deleted file mode 100644 index f97b9ef5c8a1..000000000000 --- a/packages/device_info/device_info/example/ios/Runner/main.m +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import -#import -#import "AppDelegate.h" - -int main(int argc, char* argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} diff --git a/packages/device_info/device_info/example/lib/main.dart b/packages/device_info/device_info/example/lib/main.dart deleted file mode 100644 index 44e3fb4ee2f7..000000000000 --- a/packages/device_info/device_info/example/lib/main.dart +++ /dev/null @@ -1,146 +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. - -// ignore_for_file: public_member_api_docs - -import 'dart:async'; - -import 'dart:io'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:device_info/device_info.dart'; - -void main() { - runZonedGuarded(() { - runApp(MyApp()); - }, (dynamic error, dynamic stack) { - print(error); - print(stack); - }); -} - -class MyApp extends StatefulWidget { - @override - _MyAppState createState() => _MyAppState(); -} - -class _MyAppState extends State { - static final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin(); - Map _deviceData = {}; - - @override - void initState() { - super.initState(); - initPlatformState(); - } - - Future initPlatformState() async { - Map deviceData = {}; - - try { - if (Platform.isAndroid) { - deviceData = _readAndroidBuildData(await deviceInfoPlugin.androidInfo); - } else if (Platform.isIOS) { - deviceData = _readIosDeviceInfo(await deviceInfoPlugin.iosInfo); - } - } on PlatformException { - deviceData = { - 'Error:': 'Failed to get platform version.' - }; - } - - if (!mounted) return; - - setState(() { - _deviceData = deviceData; - }); - } - - Map _readAndroidBuildData(AndroidDeviceInfo build) { - return { - 'version.securityPatch': build.version.securityPatch, - 'version.sdkInt': build.version.sdkInt, - 'version.release': build.version.release, - 'version.previewSdkInt': build.version.previewSdkInt, - 'version.incremental': build.version.incremental, - 'version.codename': build.version.codename, - 'version.baseOS': build.version.baseOS, - 'board': build.board, - 'bootloader': build.bootloader, - 'brand': build.brand, - 'device': build.device, - 'display': build.display, - 'fingerprint': build.fingerprint, - 'hardware': build.hardware, - 'host': build.host, - 'id': build.id, - 'manufacturer': build.manufacturer, - 'model': build.model, - 'product': build.product, - 'supported32BitAbis': build.supported32BitAbis, - 'supported64BitAbis': build.supported64BitAbis, - 'supportedAbis': build.supportedAbis, - 'tags': build.tags, - 'type': build.type, - 'isPhysicalDevice': build.isPhysicalDevice, - 'androidId': build.androidId, - 'systemFeatures': build.systemFeatures, - }; - } - - Map _readIosDeviceInfo(IosDeviceInfo data) { - return { - 'name': data.name, - 'systemName': data.systemName, - 'systemVersion': data.systemVersion, - 'model': data.model, - 'localizedModel': data.localizedModel, - 'identifierForVendor': data.identifierForVendor, - 'isPhysicalDevice': data.isPhysicalDevice, - 'utsname.sysname:': data.utsname.sysname, - 'utsname.nodename:': data.utsname.nodename, - 'utsname.release:': data.utsname.release, - 'utsname.version:': data.utsname.version, - 'utsname.machine:': data.utsname.machine, - }; - } - - @override - Widget build(BuildContext context) { - return MaterialApp( - home: Scaffold( - appBar: AppBar( - title: Text( - Platform.isAndroid ? 'Android Device Info' : 'iOS Device Info'), - ), - body: ListView( - children: _deviceData.keys.map((String property) { - return Row( - children: [ - Container( - padding: const EdgeInsets.all(10.0), - child: Text( - property, - style: const TextStyle( - fontWeight: FontWeight.bold, - ), - ), - ), - Expanded( - child: Container( - padding: const EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 10.0), - child: Text( - '${_deviceData[property]}', - maxLines: 10, - overflow: TextOverflow.ellipsis, - ), - )), - ], - ); - }).toList(), - ), - ), - ); - } -} diff --git a/packages/device_info/device_info/example/pubspec.yaml b/packages/device_info/device_info/example/pubspec.yaml deleted file mode 100644 index c4e84f60de5e..000000000000 --- a/packages/device_info/device_info/example/pubspec.yaml +++ /dev/null @@ -1,28 +0,0 @@ -name: device_info_example -description: Demonstrates how to use the device_info plugin. -publish_to: none - -environment: - sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.12.13+hotfix.5" - -dependencies: - flutter: - sdk: flutter - device_info: - # When depending on this package from a real application you should use: - # device_info: ^x.y.z - # See https://dart.dev/tools/pub/dependencies#version-constraints - # The example app is bundled with the plugin so we use a path dependency on - # the parent directory to use the current plugin's version. - path: ../ - -dev_dependencies: - flutter_driver: - sdk: flutter - integration_test: - sdk: flutter - pedantic: ^1.10.0 - -flutter: - uses-material-design: true diff --git a/packages/device_info/device_info/example/test_driver/integration_test.dart b/packages/device_info/device_info/example/test_driver/integration_test.dart deleted file mode 100644 index 4f10f2a522f3..000000000000 --- a/packages/device_info/device_info/example/test_driver/integration_test.dart +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:integration_test/integration_test_driver.dart'; - -Future main() => integrationDriver(); diff --git a/packages/device_info/device_info/ios/Assets/.gitkeep b/packages/device_info/device_info/ios/Assets/.gitkeep deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/device_info/device_info/ios/Classes/FLTDeviceInfoPlugin.h b/packages/device_info/device_info/ios/Classes/FLTDeviceInfoPlugin.h deleted file mode 100644 index 511b5b893fcb..000000000000 --- a/packages/device_info/device_info/ios/Classes/FLTDeviceInfoPlugin.h +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -@interface FLTDeviceInfoPlugin : NSObject -@end diff --git a/packages/device_info/device_info/ios/Classes/FLTDeviceInfoPlugin.m b/packages/device_info/device_info/ios/Classes/FLTDeviceInfoPlugin.m deleted file mode 100644 index 3d4d25ad9c6e..000000000000 --- a/packages/device_info/device_info/ios/Classes/FLTDeviceInfoPlugin.m +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "FLTDeviceInfoPlugin.h" -#import - -@implementation FLTDeviceInfoPlugin -+ (void)registerWithRegistrar:(NSObject*)registrar { - FlutterMethodChannel* channel = - [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/device_info" - binaryMessenger:[registrar messenger]]; - FLTDeviceInfoPlugin* instance = [[FLTDeviceInfoPlugin alloc] init]; - [registrar addMethodCallDelegate:instance channel:channel]; -} - -- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { - if ([@"getIosDeviceInfo" isEqualToString:call.method]) { - UIDevice* device = [UIDevice currentDevice]; - struct utsname un; - uname(&un); - - result(@{ - @"name" : [device name], - @"systemName" : [device systemName], - @"systemVersion" : [device systemVersion], - @"model" : [device model], - @"localizedModel" : [device localizedModel], - @"identifierForVendor" : [[device identifierForVendor] UUIDString], - @"isPhysicalDevice" : [self isDevicePhysical], - @"utsname" : @{ - @"sysname" : @(un.sysname), - @"nodename" : @(un.nodename), - @"release" : @(un.release), - @"version" : @(un.version), - @"machine" : @(un.machine), - } - }); - } else { - result(FlutterMethodNotImplemented); - } -} - -// return value is false if code is run on a simulator -- (NSString*)isDevicePhysical { -#if TARGET_OS_SIMULATOR - NSString* isPhysicalDevice = @"false"; -#else - NSString* isPhysicalDevice = @"true"; -#endif - - return isPhysicalDevice; -} - -@end diff --git a/packages/device_info/device_info/ios/device_info.podspec b/packages/device_info/device_info/ios/device_info.podspec deleted file mode 100644 index 1d1baa4787f6..000000000000 --- a/packages/device_info/device_info/ios/device_info.podspec +++ /dev/null @@ -1,23 +0,0 @@ -# -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html -# -Pod::Spec.new do |s| - s.name = 'device_info' - s.version = '0.0.1' - s.summary = 'Flutter Device Info' - s.description = <<-DESC -Get current device information from within the Flutter application. -Downloaded by pub (not CocoaPods). - DESC - s.homepage = 'https://github.com/flutter/plugins' - s.license = { :type => 'BSD', :file => '../LICENSE' } - s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/device_info' } - s.documentation_url = 'https://pub.dev/packages/device_info' - s.source_files = 'Classes/**/*' - s.public_header_files = 'Classes/**/*.h' - s.dependency 'Flutter' - s.platform = :ios, '8.0' - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' } -end - diff --git a/packages/device_info/device_info/lib/device_info.dart b/packages/device_info/device_info/lib/device_info.dart deleted file mode 100644 index 1153ac6f7da6..000000000000 --- a/packages/device_info/device_info/lib/device_info.dart +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; -import 'package:device_info_platform_interface/device_info_platform_interface.dart'; - -export 'package:device_info_platform_interface/device_info_platform_interface.dart' - show AndroidBuildVersion, AndroidDeviceInfo, IosDeviceInfo, IosUtsname; - -/// Provides device and operating system information. -class DeviceInfoPlugin { - /// No work is done when instantiating the plugin. It's safe to call this - /// repeatedly or in performance-sensitive blocks. - DeviceInfoPlugin(); - - /// This information does not change from call to call. Cache it. - AndroidDeviceInfo? _cachedAndroidDeviceInfo; - - /// Information derived from `android.os.Build`. - /// - /// See: https://developer.android.com/reference/android/os/Build.html - Future get androidInfo async => - _cachedAndroidDeviceInfo ??= - await DeviceInfoPlatform.instance.androidInfo(); - - /// This information does not change from call to call. Cache it. - IosDeviceInfo? _cachedIosDeviceInfo; - - /// Information derived from `UIDevice`. - /// - /// See: https://developer.apple.com/documentation/uikit/uidevice - Future get iosInfo async => - _cachedIosDeviceInfo ??= await DeviceInfoPlatform.instance.iosInfo(); -} diff --git a/packages/device_info/device_info/pubspec.yaml b/packages/device_info/device_info/pubspec.yaml deleted file mode 100644 index d557e496dcab..000000000000 --- a/packages/device_info/device_info/pubspec.yaml +++ /dev/null @@ -1,29 +0,0 @@ -name: device_info -description: Flutter plugin providing detailed information about the device - (make, model, etc.), and Android or iOS version the app is running on. -repository: https://github.com/flutter/plugins/tree/master/packages/device_info/device_info -issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+device_info%22 -version: 2.0.3 - -environment: - sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.12.13+hotfix.5" - -flutter: - plugin: - platforms: - android: - package: io.flutter.plugins.deviceinfo - pluginClass: DeviceInfoPlugin - ios: - pluginClass: FLTDeviceInfoPlugin - -dependencies: - flutter: - sdk: flutter - device_info_platform_interface: ^2.0.0 -dev_dependencies: - test: ^1.16.3 - flutter_test: - sdk: flutter - pedantic: ^1.10.0 diff --git a/packages/device_info/device_info_platform_interface/AUTHORS b/packages/device_info/device_info_platform_interface/AUTHORS deleted file mode 100644 index 493a0b4ef9c2..000000000000 --- a/packages/device_info/device_info_platform_interface/AUTHORS +++ /dev/null @@ -1,66 +0,0 @@ -# Below is a list of people and organizations that have contributed -# to the Flutter project. Names should be added to the list like so: -# -# Name/Organization - -Google Inc. -The Chromium Authors -German Saprykin -Benjamin Sauer -larsenthomasj@gmail.com -Ali Bitek -Pol Batlló -Anatoly Pulyaevskiy -Hayden Flinner -Stefano Rodriguez -Salvatore Giordano -Brian Armstrong -Paul DeMarco -Fabricio Nogueira -Simon Lightfoot -Ashton Thomas -Thomas Danner -Diego Velásquez -Hajime Nakamura -Tuyển Vũ Xuân -Miguel Ruivo -Sarthak Verma -Mike Diarmid -Invertase -Elliot Hesp -Vince Varga -Aawaz Gyawali -EUI Limited -Katarina Sheremet -Thomas Stockx -Sarbagya Dhaubanjar -Ozkan Eksi -Rishab Nayak -ko2ic -Jonathan Younger -Jose Sanchez -Debkanchan Samadder -Audrius Karosevicius -Lukasz Piliszczuk -SoundReply Solutions GmbH -Rafal Wachol -Pau Picas -Christian Weder -Alexandru Tuca -Christian Weder -Rhodes Davis Jr. -Luigi Agosti -Quentin Le Guennec -Koushik Ravikumar -Nissim Dsilva -Giancarlo Rocha -Ryo Miyake -Théo Champion -Kazuki Yamaguchi -Eitan Schwartz -Chris Rutkowski -Juan Alvarez -Aleksandr Yurkovskiy -Anton Borries -Alex Li -Rahul Raj <64.rahulraj@gmail.com> diff --git a/packages/device_info/device_info_platform_interface/CHANGELOG.md b/packages/device_info/device_info_platform_interface/CHANGELOG.md deleted file mode 100644 index 438d5bccad40..000000000000 --- a/packages/device_info/device_info_platform_interface/CHANGELOG.md +++ /dev/null @@ -1,21 +0,0 @@ -## 2.0.1 - -* Update platform_plugin_interface version requirement. - -## 2.0.0 - -* Migrate to null safety. -* Make `baseOS`, `previewSdkInt`, and `securityPatch` nullable types. -* Remove default values for non-nullable types. - -## 1.0.2 - -- Update Flutter SDK constraint. - -## 1.0.1 - -- Documentation typo fixed. - -## 1.0.0 - -- Initial open-source release. diff --git a/packages/device_info/device_info_platform_interface/LICENSE b/packages/device_info/device_info_platform_interface/LICENSE deleted file mode 100644 index c6823b81eb84..000000000000 --- a/packages/device_info/device_info_platform_interface/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright 2013 The Flutter Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/device_info/device_info_platform_interface/README.md b/packages/device_info/device_info_platform_interface/README.md deleted file mode 100644 index 1391ffded5ee..000000000000 --- a/packages/device_info/device_info_platform_interface/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# device_info_platform_interface - -A common platform interface for the [`device_info`][1] plugin. - -This interface allows platform-specific implementations of the `device_info` -plugin, as well as the plugin itself, to ensure they are supporting the -same interface. - -# Usage - -To implement a new platform-specific implementation of `device_info`, extend -[`DeviceInfoPlatform`][2] with an implementation that performs the -platform-specific behavior, and when you register your plugin, set the default -`DeviceInfoPlatform` by calling -`DeviceInfoPlatform.instance = MyPlatformDeviceInfo()`. - -# Note on breaking changes - -Strongly prefer non-breaking changes (such as adding a method to the interface) -over breaking changes for this package. - -See https://flutter.dev/go/platform-interface-breaking-changes for a discussion -on why a less-clean interface is preferable to a breaking change. - -[1]: ../device_info -[2]: lib/device_info_platform_interface.dart diff --git a/packages/device_info/device_info_platform_interface/lib/device_info_platform_interface.dart b/packages/device_info/device_info_platform_interface/lib/device_info_platform_interface.dart deleted file mode 100644 index a40363b2dcb6..000000000000 --- a/packages/device_info/device_info_platform_interface/lib/device_info_platform_interface.dart +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; - -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -import 'method_channel/method_channel_device_info.dart'; -import 'model/android_device_info.dart'; -import 'model/ios_device_info.dart'; -export 'model/android_device_info.dart'; -export 'model/ios_device_info.dart'; - -/// The interface that implementations of device_info must implement. -/// -/// Platform implementations should extend this class rather than implement it as `device_info` -/// does not consider newly added methods to be breaking changes. Extending this class -/// (using `extends`) ensures that the subclass will get the default implementation, while -/// platform implementations that `implements` this interface will be broken by newly added -/// [DeviceInfoPlatform] methods. -abstract class DeviceInfoPlatform extends PlatformInterface { - /// Constructs a DeviceInfoPlatform. - DeviceInfoPlatform() : super(token: _token); - - static final Object _token = Object(); - - static DeviceInfoPlatform _instance = MethodChannelDeviceInfo(); - - /// The default instance of [DeviceInfoPlatform] to use. - /// - /// Defaults to [MethodChannelDeviceInfo]. - static DeviceInfoPlatform get instance => _instance; - - /// Platform-specific plugins should set this with their own platform-specific - /// class that extends [DeviceInfoPlatform] when they register themselves. - static set instance(DeviceInfoPlatform instance) { - PlatformInterface.verifyToken(instance, _token); - _instance = instance; - } - - /// Gets the Android device information. - Future androidInfo() { - throw UnimplementedError('androidInfo() has not been implemented.'); - } - - /// Gets the iOS device information. - Future iosInfo() { - throw UnimplementedError('iosInfo() has not been implemented.'); - } -} diff --git a/packages/device_info/device_info_platform_interface/lib/method_channel/method_channel_device_info.dart b/packages/device_info/device_info_platform_interface/lib/method_channel/method_channel_device_info.dart deleted file mode 100644 index 3c19e57f66a8..000000000000 --- a/packages/device_info/device_info_platform_interface/lib/method_channel/method_channel_device_info.dart +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; - -import 'package:flutter/services.dart'; -import 'package:meta/meta.dart'; -import 'package:device_info_platform_interface/device_info_platform_interface.dart'; - -/// An implementation of [DeviceInfoPlatform] that uses method channels. -class MethodChannelDeviceInfo extends DeviceInfoPlatform { - /// The method channel used to interact with the native platform. - @visibleForTesting - MethodChannel channel = MethodChannel('plugins.flutter.io/device_info'); - - // Method channel for Android devices - Future androidInfo() async { - return AndroidDeviceInfo.fromMap((await channel - .invokeMapMethod('getAndroidDeviceInfo')) ?? - {}); - } - - // Method channel for iOS devices - Future iosInfo() async { - return IosDeviceInfo.fromMap( - (await channel.invokeMapMethod('getIosDeviceInfo')) ?? - {}); - } -} diff --git a/packages/device_info/device_info_platform_interface/lib/model/android_device_info.dart b/packages/device_info/device_info_platform_interface/lib/model/android_device_info.dart deleted file mode 100644 index b61dc14a0420..000000000000 --- a/packages/device_info/device_info_platform_interface/lib/model/android_device_info.dart +++ /dev/null @@ -1,247 +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. - -/// Information derived from `android.os.Build`. -/// -/// See: https://developer.android.com/reference/android/os/Build.html -class AndroidDeviceInfo { - /// Android device Info class. - AndroidDeviceInfo({ - required this.version, - required this.board, - required this.bootloader, - required this.brand, - required this.device, - required this.display, - required this.fingerprint, - required this.hardware, - required this.host, - required this.id, - required this.manufacturer, - required this.model, - required this.product, - required List supported32BitAbis, - required List supported64BitAbis, - required List supportedAbis, - required this.tags, - required this.type, - required this.isPhysicalDevice, - required this.androidId, - required List systemFeatures, - }) : supported32BitAbis = List.unmodifiable(supported32BitAbis), - supported64BitAbis = List.unmodifiable(supported64BitAbis), - supportedAbis = List.unmodifiable(supportedAbis), - systemFeatures = List.unmodifiable(systemFeatures); - - /// Android operating system version values derived from `android.os.Build.VERSION`. - final AndroidBuildVersion version; - - /// The name of the underlying board, like "goldfish". - /// - /// The value is an empty String if it is not available. - final String board; - - /// The system bootloader version number. - /// - /// The value is an empty String if it is not available. - final String bootloader; - - /// The consumer-visible brand with which the product/hardware will be associated, if any. - /// - /// The value is an empty String if it is not available. - final String brand; - - /// The name of the industrial design. - /// - /// The value is an empty String if it is not available. - final String device; - - /// A build ID string meant for displaying to the user. - /// - /// The value is an empty String if it is not available. - final String display; - - /// A string that uniquely identifies this build. - /// - /// The value is an empty String if it is not available. - final String fingerprint; - - /// The name of the hardware (from the kernel command line or /proc). - /// - /// The value is an empty String if it is not available. - final String hardware; - - /// Hostname. - /// - /// The value is an empty String if it is not available. - final String host; - - /// Either a changelist number, or a label like "M4-rc20". - /// - /// The value is an empty String if it is not available. - final String id; - - /// The manufacturer of the product/hardware. - /// - /// The value is an empty String if it is not available. - final String manufacturer; - - /// The end-user-visible name for the end product. - /// - /// The value is an empty String if it is not available. - final String model; - - /// The name of the overall product. - /// - /// The value is an empty String if it is not available. - final String product; - - /// An ordered list of 32 bit ABIs supported by this device. - final List supported32BitAbis; - - /// An ordered list of 64 bit ABIs supported by this device. - final List supported64BitAbis; - - /// An ordered list of ABIs supported by this device. - final List supportedAbis; - - /// Comma-separated tags describing the build, like "unsigned,debug". - /// - /// The value is an empty String if it is not available. - final String tags; - - /// The type of build, like "user" or "eng". - /// - /// The value is an empty String if it is not available. - final String type; - - /// The value is `true` if the application is running on a physical device. - /// - /// The value is `false` when the application is running on a emulator, or the value is unavailable. - final bool isPhysicalDevice; - - /// The Android hardware device ID that is unique between the device + user and app signing. - /// - /// The value is an empty String if it is not available. - final String androidId; - - /// Describes what features are available on the current device. - /// - /// This can be used to check if the device has, for example, a front-facing - /// camera, or a touchscreen. However, in many cases this is not the best - /// API to use. For example, if you are interested in bluetooth, this API - /// can tell you if the device has a bluetooth radio, but it cannot tell you - /// if bluetooth is currently enabled, or if you have been granted the - /// necessary permissions to use it. Please *only* use this if there is no - /// other way to determine if a feature is supported. - /// - /// This data comes from Android's PackageManager.getSystemAvailableFeatures, - /// and many of the common feature strings to look for are available in - /// PackageManager's public documentation: - /// https://developer.android.com/reference/android/content/pm/PackageManager - final List systemFeatures; - - /// Deserializes from the message received from [_kChannel]. - static AndroidDeviceInfo fromMap(Map map) { - return AndroidDeviceInfo( - version: AndroidBuildVersion._fromMap(map['version'] != null - ? map['version'].cast() - : {}), - board: map['board'] ?? '', - bootloader: map['bootloader'] ?? '', - brand: map['brand'] ?? '', - device: map['device'] ?? '', - display: map['display'] ?? '', - fingerprint: map['fingerprint'] ?? '', - hardware: map['hardware'] ?? '', - host: map['host'] ?? '', - id: map['id'] ?? '', - manufacturer: map['manufacturer'] ?? '', - model: map['model'] ?? '', - product: map['product'] ?? '', - supported32BitAbis: _fromList(map['supported32BitAbis']), - supported64BitAbis: _fromList(map['supported64BitAbis']), - supportedAbis: _fromList(map['supportedAbis']), - tags: map['tags'] ?? '', - type: map['type'] ?? '', - isPhysicalDevice: map['isPhysicalDevice'] ?? false, - androidId: map['androidId'] ?? '', - systemFeatures: _fromList(map['systemFeatures']), - ); - } - - /// Deserializes message as List - static List _fromList(dynamic message) { - if (message == null) { - return []; - } - assert(message is List); - final List list = List.from(message) - ..removeWhere((value) => value == null); - return list.cast(); - } -} - -/// Version values of the current Android operating system build derived from -/// `android.os.Build.VERSION`. -/// -/// See: https://developer.android.com/reference/android/os/Build.VERSION.html -class AndroidBuildVersion { - AndroidBuildVersion._({ - this.baseOS, - this.previewSdkInt, - this.securityPatch, - required this.codename, - required this.incremental, - required this.release, - required this.sdkInt, - }); - - /// The base OS build the product is based on. - /// This is only available on Android 6.0 or above. - String? baseOS; - - /// The developer preview revision of a prerelease SDK. - /// This is only available on Android 6.0 or above. - int? previewSdkInt; - - /// The user-visible security patch level. - /// This is only available on Android 6.0 or above. - final String? securityPatch; - - /// The current development codename, or the string "REL" if this is a release build. - /// - /// The value is an empty String if it is not available. - final String codename; - - /// The internal value used by the underlying source control to represent this build. - /// - /// The value is an empty String if it is not available. - final String incremental; - - /// The user-visible version string. - /// - /// The value is an empty String if it is not available. - final String release; - - /// The user-visible SDK version of the framework. - /// - /// Possible values are defined in: https://developer.android.com/reference/android/os/Build.VERSION_CODES.html - /// - /// The value is -1 if it is unavailable. - final int sdkInt; - - /// Deserializes from the map message received from [_kChannel]. - static AndroidBuildVersion _fromMap(Map map) { - return AndroidBuildVersion._( - baseOS: map['baseOS'], - previewSdkInt: map['previewSdkInt'], - securityPatch: map['securityPatch'], - codename: map['codename'] ?? '', - incremental: map['incremental'] ?? '', - release: map['release'] ?? '', - sdkInt: map['sdkInt'] ?? -1, - ); - } -} diff --git a/packages/device_info/device_info_platform_interface/lib/model/ios_device_info.dart b/packages/device_info/device_info_platform_interface/lib/model/ios_device_info.dart deleted file mode 100644 index 17c96a3e250a..000000000000 --- a/packages/device_info/device_info_platform_interface/lib/model/ios_device_info.dart +++ /dev/null @@ -1,126 +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. - -/// Information derived from `UIDevice`. -/// -/// See: https://developer.apple.com/documentation/uikit/uidevice -class IosDeviceInfo { - /// IOS device info class. - IosDeviceInfo({ - required this.name, - required this.systemName, - required this.systemVersion, - required this.model, - required this.localizedModel, - required this.identifierForVendor, - required this.isPhysicalDevice, - required this.utsname, - }); - - /// Device name. - /// - /// The value is an empty String if it is not available. - final String name; - - /// The name of the current operating system. - /// - /// The value is an empty String if it is not available. - final String systemName; - - /// The current operating system version. - /// - /// The value is an empty String if it is not available. - final String systemVersion; - - /// Device model. - /// - /// The value is an empty String if it is not available. - final String model; - - /// Localized name of the device model. - /// - /// The value is an empty String if it is not available. - final String localizedModel; - - /// Unique UUID value identifying the current device. - /// - /// The value is an empty String if it is not available. - final String identifierForVendor; - - /// The value is `true` if the application is running on a physical device. - /// - /// The value is `false` when the application is running on a simulator, or the value is unavailable. - final bool isPhysicalDevice; - - /// Operating system information derived from `sys/utsname.h`. - /// - /// The value is an empty String if it is not available. - final IosUtsname utsname; - - /// Deserializes from the map message received from [_kChannel]. - static IosDeviceInfo fromMap(Map map) { - return IosDeviceInfo( - name: map['name'] ?? '', - systemName: map['systemName'] ?? '', - systemVersion: map['systemVersion'] ?? '', - model: map['model'] ?? '', - localizedModel: map['localizedModel'] ?? '', - identifierForVendor: map['identifierForVendor'] ?? '', - isPhysicalDevice: map['isPhysicalDevice'] != null - ? map['isPhysicalDevice'] == 'true' - : false, - utsname: IosUtsname._fromMap(map['utsname'] != null - ? map['utsname'].cast() - : {}), - ); - } -} - -/// Information derived from `utsname`. -/// See http://pubs.opengroup.org/onlinepubs/7908799/xsh/sysutsname.h.html for details. -class IosUtsname { - IosUtsname._({ - required this.sysname, - required this.nodename, - required this.release, - required this.version, - required this.machine, - }); - - /// Operating system name. - /// - /// The value is an empty String if it is not available. - final String sysname; - - /// Network node name. - /// - /// The value is an empty String if it is not available. - final String nodename; - - /// Release level. - /// - /// The value is an empty String if it is not available. - final String release; - - /// Version level. - /// - /// The value is an empty String if it is not available. - final String version; - - /// Hardware type (e.g. 'iPhone7,1' for iPhone 6 Plus). - /// - /// The value is an empty String if it is not available. - final String machine; - - /// Deserializes from the map message received from [_kChannel]. - static IosUtsname _fromMap(Map map) { - return IosUtsname._( - sysname: map['sysname'] ?? '', - nodename: map['nodename'] ?? '', - release: map['release'] ?? '', - version: map['version'] ?? '', - machine: map['machine'] ?? '', - ); - } -} diff --git a/packages/device_info/device_info_platform_interface/pubspec.yaml b/packages/device_info/device_info_platform_interface/pubspec.yaml deleted file mode 100644 index cf3e50f98422..000000000000 --- a/packages/device_info/device_info_platform_interface/pubspec.yaml +++ /dev/null @@ -1,23 +0,0 @@ -name: device_info_platform_interface -description: A common platform interface for the device_info plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/device_info/device_info_platform_interface -issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+device_info%22 -# NOTE: We strongly prefer non-breaking changes, even at the expense of a -# less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.0.1 - -environment: - sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.9.1+hotfix.4" - -dependencies: - flutter: - sdk: flutter - meta: ^1.3.0 - plugin_platform_interface: ^2.0.0 - -dev_dependencies: - flutter_test: - sdk: flutter - test: ^1.16.3 - pedantic: ^1.10.0 diff --git a/packages/device_info/device_info_platform_interface/test/method_channel_device_info_test.dart b/packages/device_info/device_info_platform_interface/test/method_channel_device_info_test.dart deleted file mode 100644 index 14ed7c0aefb4..000000000000 --- a/packages/device_info/device_info_platform_interface/test/method_channel_device_info_test.dart +++ /dev/null @@ -1,373 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:device_info_platform_interface/device_info_platform_interface.dart'; -import 'package:device_info_platform_interface/method_channel/method_channel_device_info.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - group("$MethodChannelDeviceInfo", () { - late MethodChannelDeviceInfo methodChannelDeviceInfo; - - setUp(() async { - methodChannelDeviceInfo = MethodChannelDeviceInfo(); - - methodChannelDeviceInfo.channel - .setMockMethodCallHandler((MethodCall methodCall) async { - switch (methodCall.method) { - case 'getAndroidDeviceInfo': - return ({ - "version": { - "securityPatch": "2018-09-05", - "sdkInt": 28, - "release": "9", - "previewSdkInt": 0, - "incremental": "5124027", - "codename": "REL", - "baseOS": "", - }, - "board": "goldfish_x86_64", - "bootloader": "unknown", - "brand": "google", - "device": "generic_x86_64", - "display": "PSR1.180720.075", - "fingerprint": - "google/sdk_gphone_x86_64/generic_x86_64:9/PSR1.180720.075/5124027:user/release-keys", - "hardware": "ranchu", - "host": "abfarm730", - "id": "PSR1.180720.075", - "manufacturer": "Google", - "model": "Android SDK built for x86_64", - "product": "sdk_gphone_x86_64", - "supported32BitAbis": [ - "x86", - ], - "supported64BitAbis": [ - "x86_64", - ], - "supportedAbis": [ - "x86_64", - "x86", - ], - "tags": "release-keys", - "type": "user", - "isPhysicalDevice": false, - "androidId": "f47571f3b4648f45", - "systemFeatures": [ - "android.hardware.sensor.proximity", - "android.software.adoptable_storage", - "android.hardware.sensor.accelerometer", - "android.hardware.faketouch", - "android.software.backup", - "android.hardware.touchscreen", - ], - }); - case 'getIosDeviceInfo': - return ({ - "name": "iPhone 13", - "systemName": "iOS", - "systemVersion": "13.0", - "model": "iPhone", - "localizedModel": "iPhone", - "identifierForVendor": "88F59280-55AD-402C-B922-3203B4794C06", - "isPhysicalDevice": false, - "utsname": { - "sysname": "Darwin", - "nodename": "host", - "release": "19.6.0", - "version": - "Darwin Kernel Version 19.6.0: Thu Jun 18 20:49:00 PDT 2020; root:xnu-6153.141.1~1/RELEASE_X86_64", - "machine": "x86_64", - } - }); - default: - return null; - } - }); - }); - - test("androidInfo", () async { - final AndroidDeviceInfo result = - await methodChannelDeviceInfo.androidInfo(); - - expect(result.version.securityPatch, "2018-09-05"); - expect(result.version.sdkInt, 28); - expect(result.version.release, "9"); - expect(result.version.previewSdkInt, 0); - expect(result.version.incremental, "5124027"); - expect(result.version.codename, "REL"); - expect(result.board, "goldfish_x86_64"); - expect(result.bootloader, "unknown"); - expect(result.brand, "google"); - expect(result.device, "generic_x86_64"); - expect(result.display, "PSR1.180720.075"); - expect(result.fingerprint, - "google/sdk_gphone_x86_64/generic_x86_64:9/PSR1.180720.075/5124027:user/release-keys"); - expect(result.hardware, "ranchu"); - expect(result.host, "abfarm730"); - expect(result.id, "PSR1.180720.075"); - expect(result.manufacturer, "Google"); - expect(result.model, "Android SDK built for x86_64"); - expect(result.product, "sdk_gphone_x86_64"); - expect(result.supported32BitAbis, [ - "x86", - ]); - expect(result.supported64BitAbis, [ - "x86_64", - ]); - expect(result.supportedAbis, [ - "x86_64", - "x86", - ]); - expect(result.tags, "release-keys"); - expect(result.type, "user"); - expect(result.isPhysicalDevice, false); - expect(result.androidId, "f47571f3b4648f45"); - expect(result.systemFeatures, [ - "android.hardware.sensor.proximity", - "android.software.adoptable_storage", - "android.hardware.sensor.accelerometer", - "android.hardware.faketouch", - "android.software.backup", - "android.hardware.touchscreen", - ]); - }); - - test("iosInfo", () async { - final IosDeviceInfo result = await methodChannelDeviceInfo.iosInfo(); - expect(result.name, "iPhone 13"); - expect(result.systemName, "iOS"); - expect(result.systemVersion, "13.0"); - expect(result.model, "iPhone"); - expect(result.localizedModel, "iPhone"); - expect( - result.identifierForVendor, "88F59280-55AD-402C-B922-3203B4794C06"); - expect(result.isPhysicalDevice, false); - expect(result.utsname.sysname, "Darwin"); - expect(result.utsname.nodename, "host"); - expect(result.utsname.release, "19.6.0"); - expect(result.utsname.version, - "Darwin Kernel Version 19.6.0: Thu Jun 18 20:49:00 PDT 2020; root:xnu-6153.141.1~1/RELEASE_X86_64"); - expect(result.utsname.machine, "x86_64"); - }); - }); - - group( - "$MethodChannelDeviceInfo handles null value in the map returned from method channel", - () { - late MethodChannelDeviceInfo methodChannelDeviceInfo; - - setUp(() async { - methodChannelDeviceInfo = MethodChannelDeviceInfo(); - - methodChannelDeviceInfo.channel - .setMockMethodCallHandler((MethodCall methodCall) async { - switch (methodCall.method) { - case 'getAndroidDeviceInfo': - return ({ - "version": null, - "board": null, - "bootloader": null, - "brand": null, - "device": null, - "display": null, - "fingerprint": null, - "hardware": null, - "host": null, - "id": null, - "manufacturer": null, - "model": null, - "product": null, - "supported32BitAbis": null, - "supported64BitAbis": null, - "supportedAbis": null, - "tags": null, - "type": null, - "isPhysicalDevice": null, - "androidId": null, - "systemFeatures": null, - }); - case 'getIosDeviceInfo': - return ({ - "name": null, - "systemName": null, - "systemVersion": null, - "model": null, - "localizedModel": null, - "identifierForVendor": null, - "isPhysicalDevice": null, - "utsname": null, - }); - default: - return null; - } - }); - }); - - test("androidInfo hanels null", () async { - final AndroidDeviceInfo result = - await methodChannelDeviceInfo.androidInfo(); - - expect(result.version.securityPatch, null); - expect(result.version.sdkInt, -1); - expect(result.version.release, ''); - expect(result.version.previewSdkInt, null); - expect(result.version.incremental, ''); - expect(result.version.codename, ''); - expect(result.board, ''); - expect(result.bootloader, ''); - expect(result.brand, ''); - expect(result.device, ''); - expect(result.display, ''); - expect(result.fingerprint, ''); - expect(result.hardware, ''); - expect(result.host, ''); - expect(result.id, ''); - expect(result.manufacturer, ''); - expect(result.model, ''); - expect(result.product, ''); - expect(result.supported32BitAbis, []); - expect(result.supported64BitAbis, []); - expect(result.supportedAbis, []); - expect(result.tags, ''); - expect(result.type, ''); - expect(result.isPhysicalDevice, false); - expect(result.androidId, ''); - expect(result.systemFeatures, []); - }); - - test("iosInfo handles null", () async { - final IosDeviceInfo result = await methodChannelDeviceInfo.iosInfo(); - expect(result.name, ''); - expect(result.systemName, ''); - expect(result.systemVersion, ''); - expect(result.model, ''); - expect(result.localizedModel, ''); - expect(result.identifierForVendor, ''); - expect(result.isPhysicalDevice, false); - expect(result.utsname.sysname, ''); - expect(result.utsname.nodename, ''); - expect(result.utsname.release, ''); - expect(result.utsname.version, ''); - expect(result.utsname.machine, ''); - }); - }); - - group("$MethodChannelDeviceInfo handles method channel returns null", () { - late MethodChannelDeviceInfo methodChannelDeviceInfo; - - setUp(() async { - methodChannelDeviceInfo = MethodChannelDeviceInfo(); - - methodChannelDeviceInfo.channel - .setMockMethodCallHandler((MethodCall methodCall) async { - switch (methodCall.method) { - case 'getAndroidDeviceInfo': - return null; - case 'getIosDeviceInfo': - return null; - default: - return null; - } - }); - }); - - test("androidInfo handles null", () async { - final AndroidDeviceInfo result = - await methodChannelDeviceInfo.androidInfo(); - - expect(result.version.securityPatch, null); - expect(result.version.sdkInt, -1); - expect(result.version.release, ''); - expect(result.version.previewSdkInt, null); - expect(result.version.incremental, ''); - expect(result.version.codename, ''); - expect(result.board, ''); - expect(result.bootloader, ''); - expect(result.brand, ''); - expect(result.device, ''); - expect(result.display, ''); - expect(result.fingerprint, ''); - expect(result.hardware, ''); - expect(result.host, ''); - expect(result.id, ''); - expect(result.manufacturer, ''); - expect(result.model, ''); - expect(result.product, ''); - expect(result.supported32BitAbis, []); - expect(result.supported64BitAbis, []); - expect(result.supportedAbis, []); - expect(result.tags, ''); - expect(result.type, ''); - expect(result.isPhysicalDevice, false); - expect(result.androidId, ''); - expect(result.systemFeatures, []); - }); - - test("iosInfo handles null", () async { - final IosDeviceInfo result = await methodChannelDeviceInfo.iosInfo(); - expect(result.name, ''); - expect(result.systemName, ''); - expect(result.systemVersion, ''); - expect(result.model, ''); - expect(result.localizedModel, ''); - expect(result.identifierForVendor, ''); - expect(result.isPhysicalDevice, false); - expect(result.utsname.sysname, ''); - expect(result.utsname.nodename, ''); - expect(result.utsname.release, ''); - expect(result.utsname.version, ''); - expect(result.utsname.machine, ''); - }); - }); - - group("$MethodChannelDeviceInfo android handles null values in list", () { - late MethodChannelDeviceInfo methodChannelDeviceInfo; - - setUp(() async { - methodChannelDeviceInfo = MethodChannelDeviceInfo(); - - methodChannelDeviceInfo.channel - .setMockMethodCallHandler((MethodCall methodCall) async { - switch (methodCall.method) { - case 'getAndroidDeviceInfo': - return ({ - "supported32BitAbis": ["x86"], - "supported64BitAbis": ["x86_64"], - "supportedAbis": ["x86_64", "x86"], - "systemFeatures": [ - "android.hardware.sensor.proximity", - "android.software.adoptable_storage", - "android.hardware.sensor.accelerometer", - "android.hardware.faketouch", - "android.software.backup", - "android.hardware.touchscreen", - ], - }); - default: - return null; - } - }); - }); - - test("androidInfo hanels null in list", () async { - final AndroidDeviceInfo result = - await methodChannelDeviceInfo.androidInfo(); - expect(result.supported32BitAbis, ['x86']); - expect(result.supported64BitAbis, ['x86_64']); - expect(result.supportedAbis, ['x86_64', 'x86']); - expect(result.systemFeatures, [ - "android.hardware.sensor.proximity", - "android.software.adoptable_storage", - "android.hardware.sensor.accelerometer", - "android.hardware.faketouch", - "android.software.backup", - "android.hardware.touchscreen" - ]); - }); - }); -} diff --git a/packages/image_picker/image_picker_for_web/example/pubspec.yaml b/packages/image_picker/image_picker_for_web/example/pubspec.yaml index 8dadde267e8a..306a857731e8 100644 --- a/packages/image_picker/image_picker_for_web/example/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/example/pubspec.yaml @@ -1,4 +1,4 @@ -name: connectivity_for_web_integration_tests +name: image_picker_for_web_integration_tests publish_to: none environment: diff --git a/packages/package_info/AUTHORS b/packages/package_info/AUTHORS deleted file mode 100644 index 493a0b4ef9c2..000000000000 --- a/packages/package_info/AUTHORS +++ /dev/null @@ -1,66 +0,0 @@ -# Below is a list of people and organizations that have contributed -# to the Flutter project. Names should be added to the list like so: -# -# Name/Organization - -Google Inc. -The Chromium Authors -German Saprykin -Benjamin Sauer -larsenthomasj@gmail.com -Ali Bitek -Pol Batlló -Anatoly Pulyaevskiy -Hayden Flinner -Stefano Rodriguez -Salvatore Giordano -Brian Armstrong -Paul DeMarco -Fabricio Nogueira -Simon Lightfoot -Ashton Thomas -Thomas Danner -Diego Velásquez -Hajime Nakamura -Tuyển Vũ Xuân -Miguel Ruivo -Sarthak Verma -Mike Diarmid -Invertase -Elliot Hesp -Vince Varga -Aawaz Gyawali -EUI Limited -Katarina Sheremet -Thomas Stockx -Sarbagya Dhaubanjar -Ozkan Eksi -Rishab Nayak -ko2ic -Jonathan Younger -Jose Sanchez -Debkanchan Samadder -Audrius Karosevicius -Lukasz Piliszczuk -SoundReply Solutions GmbH -Rafal Wachol -Pau Picas -Christian Weder -Alexandru Tuca -Christian Weder -Rhodes Davis Jr. -Luigi Agosti -Quentin Le Guennec -Koushik Ravikumar -Nissim Dsilva -Giancarlo Rocha -Ryo Miyake -Théo Champion -Kazuki Yamaguchi -Eitan Schwartz -Chris Rutkowski -Juan Alvarez -Aleksandr Yurkovskiy -Anton Borries -Alex Li -Rahul Raj <64.rahulraj@gmail.com> diff --git a/packages/package_info/CHANGELOG.md b/packages/package_info/CHANGELOG.md deleted file mode 100644 index ca1a943d644a..000000000000 --- a/packages/package_info/CHANGELOG.md +++ /dev/null @@ -1,188 +0,0 @@ -## NEXT - -* Remove references to the Android v1 embedding. -* Updated Android lint settings. -* Updates Android compileSdkVersion to 31. - -## 2.0.2 - -* Update README to point to Plus Plugins version. - -## 2.0.1 - -* Migrate maven repository from jcenter to mavenCentral. - -## 2.0.0 - -* Migrate to null safety. - -## 0.4.3+4 - -* Ensure `IntegrationTestPlugin` is registered in `example` app, so Firebase Test Lab tests report test results correctly. [Issue](https://github.com/flutter/flutter/issues/74944). - -## 0.4.3+3 - -* Update Flutter SDK constraint. - -## 0.4.3+2 - -* Remove unused `test` dependency. -* Update Dart SDK constraint in example. - -## 0.4.3+1 - -* Update Android compileSdkVersion to 29. - -## 0.4.3 - -* Update package:e2e -> package:integration_test - -## 0.4.2 - -* Update package:e2e reference to use the local version in the flutter/plugins - repository. - -## 0.4.1 - -* Add support for macOS. - -## 0.4.0+18 - -* Update lower bound of dart dependency to 2.1.0. - -## 0.4.0+17 - -* Bump the minimum Flutter version to 1.12.13+hotfix.5. -* Clean up various Android workarounds no longer needed after framework v1.12. -* Complete v2 embedding support. -* Fix CocoaPods podspec lint warnings. - -## 0.4.0+16 - -* Declare API stability and compatibility with `1.0.0` (more details at: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0). - -## 0.4.0+15 - -* Replace deprecated `getFlutterEngine` call on Android. - -## 0.4.0+14 - -* Make the pedantic dev_dependency explicit. - -## 0.4.0+13 - -* Remove the deprecated `author:` field from pubspec.yaml -* Migrate the plugin to the pubspec platforms manifest. -* Require Flutter SDK 1.10.0 or greater. - -## 0.4.0+12 - -* Fix pedantic lints. This involved internally refactoring how the - `PackageInfo.fromPlatform` code handled futures, but shouldn't change existing - functionality. - -## 0.4.0+11 - -* Remove AndroidX warnings. - -## 0.4.0+10 - -* Include lifecycle dependency as a compileOnly one on Android to resolve - potential version conflicts with other transitive libraries. - -## 0.4.0+9 - -* Android: Use android.arch.lifecycle instead of androidx.lifecycle:lifecycle in `build.gradle` to support apps that has not been migrated to AndroidX. - -## 0.4.0+8 - -* Support the v2 Android embedder. -* Update to AndroidX. -* Add a unit test. -* Migrate to using the new e2e test binding. - -## 0.4.0+7 - -* Update and migrate iOS example project. -* Define clang module for iOS. - -## 0.4.0+6 - -* Fix Android compiler warnings. - -## 0.4.0+5 - -* Add iOS-specific warning to README.md. - -## 0.4.0+4 - -* Add missing template type parameter to `invokeMethod` calls. -* Bump minimum Flutter version to 1.5.0. -* Replace invokeMethod with invokeMapMethod wherever necessary. - -## 0.4.0+3 - -* Add integration test. - -## 0.4.0+2 - -* Android: Using new method for BuildNumber in new android versions - -## 0.4.0+1 - -* Log a more detailed warning at build time about the previous AndroidX - migration. - -## 0.4.0 - -* **Breaking change**. Migrate from the deprecated original Android Support - Library to AndroidX. This shouldn't result in any functional changes, but it - requires any Android apps using this plugin to [also - migrate](https://developer.android.com/jetpack/androidx/migrate) if they're - using the original support library. - -## 0.3.2+1 - -* Fixed a crash on IOS when some of the package infos are not available. - -## 0.3.2 - -* Updated Gradle tooling to match Android Studio 3.1.2. - -## 0.3.1 - -* Added `appName` field to `PackageInfo` for getting the display name of the app. - -## 0.3.0 - -* **Breaking change**. Set SDK constraints to match the Flutter beta release. - -## 0.2.1 - -* Fixed Dart 2 type error. - -## 0.2.0 - -* **Breaking change**. Introduced class `PackageInfo` in place of individual functions. -* `PackageInfo` provides all package information with a single async call. - -## 0.1.1 - -* Added package name to available information. -* Simplified and upgraded Android project template to Android SDK 27. -* Updated package description. - -## 0.1.0 - -* **Breaking change**. Upgraded to Gradle 4.1 and Android Studio Gradle plugin - 3.0.1. Older Flutter projects need to upgrade their Gradle setup as well in - order to use this version of the plugin. Instructions can be found - [here](https://github.com/flutter/flutter/wiki/Updating-Flutter-projects-to-Gradle-4.1-and-Android-Studio-Gradle-plugin-3.0.1). - -## 0.0.2 - -* Add FLT prefix to iOS types - -## 0.0.1 - -* Initial release diff --git a/packages/package_info/LICENSE b/packages/package_info/LICENSE deleted file mode 100644 index c6823b81eb84..000000000000 --- a/packages/package_info/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright 2013 The Flutter Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/package_info/README.md b/packages/package_info/README.md deleted file mode 100644 index 80893880f3c2..000000000000 --- a/packages/package_info/README.md +++ /dev/null @@ -1,57 +0,0 @@ -# PackageInfo - ---- - -## Deprecation Notice - -This plugin has been replaced by the [Flutter Community Plus -Plugins](https://plus.fluttercommunity.dev/) version, -[`package_info_plus`](https://pub.dev/packages/package_info_plus). -No further updates are planned to this plugin, and we encourage all users to -migrate to the Plus version. - -Critical fixes (e.g., for any security incidents) will be provided through the -end of 2021, at which point this package will be marked as discontinued. - ---- - -This Flutter plugin provides an API for querying information about an -application package. - -# Usage - -You can use the PackageInfo to query information about the -application package. This works both on iOS and Android. - -```dart -import 'package:package_info/package_info.dart'; - -PackageInfo packageInfo = await PackageInfo.fromPlatform(); - -String appName = packageInfo.appName; -String packageName = packageInfo.packageName; -String version = packageInfo.version; -String buildNumber = packageInfo.buildNumber; -``` - -Or in async mode: - -```dart -PackageInfo.fromPlatform().then((PackageInfo packageInfo) { - String appName = packageInfo.appName; - String packageName = packageInfo.packageName; - String version = packageInfo.version; - String buildNumber = packageInfo.buildNumber; -}); -``` - -## Known Issue - -As noted on [issue 20761](https://github.com/flutter/flutter/issues/20761#issuecomment-493434578), package_info on iOS -requires the Xcode build folder to be rebuilt after changes to the version string in `pubspec.yaml`. -Clean the Xcode build folder with: -`XCode Menu -> Product -> (Holding Option Key) Clean build folder`. - -## Issues and feedback - -Please file [issues](https://github.com/flutter/flutter/issues/new) to send feedback or report a bug. Thank you! diff --git a/packages/package_info/analysis_options.yaml b/packages/package_info/analysis_options.yaml deleted file mode 100644 index cda4f6e153e6..000000000000 --- a/packages/package_info/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../analysis_options_legacy.yaml diff --git a/packages/package_info/android/build.gradle b/packages/package_info/android/build.gradle deleted file mode 100644 index ad620add0f93..000000000000 --- a/packages/package_info/android/build.gradle +++ /dev/null @@ -1,48 +0,0 @@ -group 'io.flutter.plugins.packageinfo' -version '1.0-SNAPSHOT' - -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' - } -} - -rootProject.allprojects { - repositories { - google() - mavenCentral() - } -} - -apply plugin: 'com.android.library' - -android { - compileSdkVersion 31 - - defaultConfig { - minSdkVersion 16 - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - lintOptions { - disable 'InvalidPackage' - disable 'GradleDependency' - } - - - testOptions { - unitTests.includeAndroidResources = true - unitTests.returnDefaultValues = true - unitTests.all { - testLogging { - events "passed", "skipped", "failed", "standardOut", "standardError" - outputs.upToDateWhen {false} - showStandardStreams = true - } - } - } -} diff --git a/packages/package_info/android/settings.gradle b/packages/package_info/android/settings.gradle deleted file mode 100644 index a5683f94fce7..000000000000 --- a/packages/package_info/android/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'package_info' diff --git a/packages/package_info/android/src/main/AndroidManifest.xml b/packages/package_info/android/src/main/AndroidManifest.xml deleted file mode 100644 index 133ae5faf3c7..000000000000 --- a/packages/package_info/android/src/main/AndroidManifest.xml +++ /dev/null @@ -1,3 +0,0 @@ - - diff --git a/packages/package_info/android/src/main/java/io/flutter/plugins/packageinfo/PackageInfoPlugin.java b/packages/package_info/android/src/main/java/io/flutter/plugins/packageinfo/PackageInfoPlugin.java deleted file mode 100644 index 4611f70951f9..000000000000 --- a/packages/package_info/android/src/main/java/io/flutter/plugins/packageinfo/PackageInfoPlugin.java +++ /dev/null @@ -1,79 +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.packageinfo; - -import android.content.Context; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.os.Build; -import io.flutter.embedding.engine.plugins.FlutterPlugin; -import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.plugin.common.MethodCall; -import io.flutter.plugin.common.MethodChannel; -import io.flutter.plugin.common.MethodChannel.MethodCallHandler; -import io.flutter.plugin.common.MethodChannel.Result; -import java.util.HashMap; -import java.util.Map; - -/** PackageInfoPlugin */ -public class PackageInfoPlugin implements MethodCallHandler, FlutterPlugin { - private Context applicationContext; - private MethodChannel methodChannel; - - /** Plugin registration. */ - @SuppressWarnings("deprecation") - public static void registerWith(io.flutter.plugin.common.PluginRegistry.Registrar registrar) { - final PackageInfoPlugin instance = new PackageInfoPlugin(); - instance.onAttachedToEngine(registrar.context(), registrar.messenger()); - } - - @Override - public void onAttachedToEngine(FlutterPluginBinding binding) { - onAttachedToEngine(binding.getApplicationContext(), binding.getBinaryMessenger()); - } - - private void onAttachedToEngine(Context applicationContext, BinaryMessenger messenger) { - this.applicationContext = applicationContext; - methodChannel = new MethodChannel(messenger, "plugins.flutter.io/package_info"); - methodChannel.setMethodCallHandler(this); - } - - @Override - public void onDetachedFromEngine(FlutterPluginBinding binding) { - applicationContext = null; - methodChannel.setMethodCallHandler(null); - methodChannel = null; - } - - @Override - public void onMethodCall(MethodCall call, Result result) { - try { - if (call.method.equals("getAll")) { - PackageManager pm = applicationContext.getPackageManager(); - PackageInfo info = pm.getPackageInfo(applicationContext.getPackageName(), 0); - - Map map = new HashMap<>(); - map.put("appName", info.applicationInfo.loadLabel(pm).toString()); - map.put("packageName", applicationContext.getPackageName()); - map.put("version", info.versionName); - map.put("buildNumber", String.valueOf(getLongVersionCode(info))); - - result.success(map); - } else { - result.notImplemented(); - } - } catch (PackageManager.NameNotFoundException ex) { - result.error("Name not found", ex.getMessage(), null); - } - } - - @SuppressWarnings("deprecation") - private static long getLongVersionCode(PackageInfo info) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - return info.getLongVersionCode(); - } - return info.versionCode; - } -} diff --git a/packages/package_info/darwin/Classes/FLTPackageInfoPlugin.m b/packages/package_info/darwin/Classes/FLTPackageInfoPlugin.m deleted file mode 100644 index ab686fa08676..000000000000 --- a/packages/package_info/darwin/Classes/FLTPackageInfoPlugin.m +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "FLTPackageInfoPlugin.h" - -@implementation FLTPackageInfoPlugin -+ (void)registerWithRegistrar:(NSObject*)registrar { - FlutterMethodChannel* channel = - [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/package_info" - binaryMessenger:[registrar messenger]]; - FLTPackageInfoPlugin* instance = [[FLTPackageInfoPlugin alloc] init]; - [registrar addMethodCallDelegate:instance channel:channel]; -} - -- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { - if ([call.method isEqualToString:@"getAll"]) { - result(@{ - @"appName" : [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"] - ?: [NSNull null], - @"packageName" : [[NSBundle mainBundle] bundleIdentifier] ?: [NSNull null], - @"version" : [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"] - ?: [NSNull null], - @"buildNumber" : [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"] - ?: [NSNull null], - }); - } else { - result(FlutterMethodNotImplemented); - } -} - -@end diff --git a/packages/package_info/example/README.md b/packages/package_info/example/README.md deleted file mode 100644 index 762d04ec0532..000000000000 --- a/packages/package_info/example/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# package_info_example - -Demonstrates how to use the package_info plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/package_info/example/android.iml b/packages/package_info/example/android.iml deleted file mode 100644 index 462b903e05b6..000000000000 --- a/packages/package_info/example/android.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/packages/package_info/example/android/app/build.gradle b/packages/package_info/example/android/app/build.gradle deleted file mode 100644 index 60fc74107b0f..000000000000 --- a/packages/package_info/example/android/app/build.gradle +++ /dev/null @@ -1,59 +0,0 @@ -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -apply plugin: 'com.android.application' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - -android { - compileSdkVersion 31 - - lintOptions { - disable 'InvalidPackage' - } - - defaultConfig { - applicationId "io.flutter.plugins.packageinfoexample" - minSdkVersion 16 - targetSdkVersion 28 - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug - } - } -} - -flutter { - source '../..' -} - -dependencies { - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test:runner:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' -} diff --git a/packages/package_info/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/package_info/example/android/app/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 9a4163a4f5ee..000000000000 --- a/packages/package_info/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/packages/package_info/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java b/packages/package_info/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java deleted file mode 100644 index 0f4298dca155..000000000000 --- a/packages/package_info/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package io.flutter.plugins; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -public @interface DartIntegrationTest {} diff --git a/packages/package_info/example/android/app/src/androidTest/java/io/flutter/plugins/packageinfoexample/MainActivityTest.java b/packages/package_info/example/android/app/src/androidTest/java/io/flutter/plugins/packageinfoexample/MainActivityTest.java deleted file mode 100644 index fb63f6f8c88b..000000000000 --- a/packages/package_info/example/android/app/src/androidTest/java/io/flutter/plugins/packageinfoexample/MainActivityTest.java +++ /dev/null @@ -1,19 +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.packageinfoexample; - -import androidx.test.rule.ActivityTestRule; -import dev.flutter.plugins.integration_test.FlutterTestRunner; -import io.flutter.embedding.android.FlutterActivity; -import io.flutter.plugins.DartIntegrationTest; -import org.junit.Rule; -import org.junit.runner.RunWith; - -@DartIntegrationTest -@RunWith(FlutterTestRunner.class) -public class MainActivityTest { - @Rule - public ActivityTestRule rule = new ActivityTestRule<>(FlutterActivity.class); -} diff --git a/packages/package_info/example/android/app/src/main/AndroidManifest.xml b/packages/package_info/example/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index efb42ac02c5c..000000000000 --- a/packages/package_info/example/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - diff --git a/packages/package_info/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/package_info/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index db77bb4b7b0906d62b1847e87f15cdcacf6a4f29..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ diff --git a/packages/package_info/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/package_info/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 17987b79bb8a35cc66c3c1fd44f5a5526c1b78be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ diff --git a/packages/package_info/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/package_info/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index d5f1c8d34e7a88e3f88bea192c3a370d44689c3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof diff --git a/packages/package_info/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/package_info/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 4d6372eebdb28e45604e46eeda8dd24651419bc0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` diff --git a/packages/package_info/example/android/build.gradle b/packages/package_info/example/android/build.gradle deleted file mode 100644 index 64450a26d537..000000000000 --- a/packages/package_info/example/android/build.gradle +++ /dev/null @@ -1,27 +0,0 @@ -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' - } -} - -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" - project.evaluationDependsOn(':app') -} - -task clean(type: Delete) { - delete rootProject.buildDir -} diff --git a/packages/package_info/example/android/gradle.properties b/packages/package_info/example/android/gradle.properties deleted file mode 100644 index 05413bc45d00..000000000000 --- a/packages/package_info/example/android/gradle.properties +++ /dev/null @@ -1,4 +0,0 @@ -org.gradle.jvmargs=-Xmx1536M -android.enableJetifier=true -android.enableR8=true -android.useAndroidX=true diff --git a/packages/package_info/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/package_info/example/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 019065d1d650..000000000000 --- a/packages/package_info/example/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip diff --git a/packages/package_info/example/android/settings.gradle b/packages/package_info/example/android/settings.gradle deleted file mode 100644 index 115da6cb4f4d..000000000000 --- a/packages/package_info/example/android/settings.gradle +++ /dev/null @@ -1,15 +0,0 @@ -include ':app' - -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() - -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withInputStream { stream -> plugins.load(stream) } -} - -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory -} diff --git a/packages/package_info/example/integration_test/package_info_test.dart b/packages/package_info/example/integration_test/package_info_test.dart deleted file mode 100644 index ab8f5f38b472..000000000000 --- a/packages/package_info/example/integration_test/package_info_test.dart +++ /dev/null @@ -1,64 +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. - -// @dart=2.9 - -import 'dart:io'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:integration_test/integration_test.dart'; -import 'package:package_info/package_info.dart'; -import 'package:package_info_example/main.dart'; - -void main() { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - testWidgets('fromPlatform', (WidgetTester tester) async { - final PackageInfo info = await PackageInfo.fromPlatform(); - // These tests are based on the example app. The tests should be updated if any related info changes. - if (Platform.isAndroid) { - expect(info.appName, 'package_info_example'); - expect(info.buildNumber, '1'); - expect(info.packageName, 'io.flutter.plugins.packageinfoexample'); - expect(info.version, '1.0'); - } else if (Platform.isIOS) { - expect(info.appName, 'Package Info Example'); - expect(info.buildNumber, '1'); - expect(info.packageName, 'dev.flutter.plugins.packageInfoExample'); - expect(info.version, '1.0'); - } else if (Platform.isMacOS) { - expect(info.appName, 'Package Info Example'); - expect(info.buildNumber, '1'); - expect(info.packageName, 'dev.flutter.plugins.packageInfoExample'); - expect(info.version, '1.0.0'); - } else { - throw (UnsupportedError('platform not supported')); - } - }); - - testWidgets('example', (WidgetTester tester) async { - await tester.pumpWidget(MyApp()); - await tester.pumpAndSettle(); - if (Platform.isAndroid) { - expect(find.text('package_info_example'), findsOneWidget); - expect(find.text('1'), findsOneWidget); - expect( - find.text('io.flutter.plugins.packageinfoexample'), findsOneWidget); - expect(find.text('1.0'), findsOneWidget); - } else if (Platform.isIOS) { - expect(find.text('Package Info Example'), findsOneWidget); - expect(find.text('1'), findsOneWidget); - expect( - find.text('dev.flutter.plugins.packageInfoExample'), findsOneWidget); - expect(find.text('1.0'), findsOneWidget); - } else if (Platform.isMacOS) { - expect(find.text('Package Info Example'), findsOneWidget); - expect(find.text('1'), findsOneWidget); - expect( - find.text('dev.flutter.plugins.packageInfoExample'), findsOneWidget); - expect(find.text('1.0.0'), findsOneWidget); - } else { - throw (UnsupportedError('platform not supported')); - } - }); -} diff --git a/packages/package_info/example/ios/Flutter/AppFrameworkInfo.plist b/packages/package_info/example/ios/Flutter/AppFrameworkInfo.plist deleted file mode 100644 index 6c2de8086bcd..000000000000 --- a/packages/package_info/example/ios/Flutter/AppFrameworkInfo.plist +++ /dev/null @@ -1,30 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - App - CFBundleIdentifier - io.flutter.flutter.app - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - App - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - UIRequiredDeviceCapabilities - - arm64 - - MinimumOSVersion - 8.0 - - diff --git a/packages/package_info/example/ios/Flutter/Debug.xcconfig b/packages/package_info/example/ios/Flutter/Debug.xcconfig deleted file mode 100644 index e8efba114687..000000000000 --- a/packages/package_info/example/ios/Flutter/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" -#include "Generated.xcconfig" diff --git a/packages/package_info/example/ios/Flutter/Release.xcconfig b/packages/package_info/example/ios/Flutter/Release.xcconfig deleted file mode 100644 index 399e9340e6f6..000000000000 --- a/packages/package_info/example/ios/Flutter/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" -#include "Generated.xcconfig" diff --git a/packages/package_info/example/ios/Podfile b/packages/package_info/example/ios/Podfile deleted file mode 100644 index f7d6a5e68c3a..000000000000 --- a/packages/package_info/example/ios/Podfile +++ /dev/null @@ -1,38 +0,0 @@ -# Uncomment this line to define a global platform for your project -# platform :ios, '9.0' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_ios_podfile_setup - -target 'Runner' do - flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - flutter_additional_ios_build_settings(target) - end -end diff --git a/packages/package_info/example/ios/Runner.xcodeproj/project.pbxproj b/packages/package_info/example/ios/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index f21209190faa..000000000000 --- a/packages/package_info/example/ios/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,460 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; - 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - C824856099B606661DF36830 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7D007BB586407934FC28AF83 /* libPods-Runner.a */; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 9705A1C41CF9048500538489 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 0B4CD678E84FD9C2C80D895C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; - 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; - 7D007BB586407934FC28AF83 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 8F88DBCB0DD2793F05ADE394 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; - 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 97C146EB1CF9000F007C117D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - C824856099B606661DF36830 /* libPods-Runner.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 1B8D0C5C4E228D9E0271D922 /* Pods */ = { - isa = PBXGroup; - children = ( - 0B4CD678E84FD9C2C80D895C /* Pods-Runner.debug.xcconfig */, - 8F88DBCB0DD2793F05ADE394 /* Pods-Runner.release.xcconfig */, - ); - name = Pods; - sourceTree = ""; - }; - 9740EEB11CF90186004384FC /* Flutter */ = { - isa = PBXGroup; - children = ( - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 9740EEB31CF90195004384FC /* Generated.xcconfig */, - ); - name = Flutter; - sourceTree = ""; - }; - 97C146E51CF9000F007C117D = { - isa = PBXGroup; - children = ( - 9740EEB11CF90186004384FC /* Flutter */, - 97C146F01CF9000F007C117D /* Runner */, - 97C146EF1CF9000F007C117D /* Products */, - 1B8D0C5C4E228D9E0271D922 /* Pods */, - F2F265795CE7F5960E889E92 /* Frameworks */, - ); - sourceTree = ""; - }; - 97C146EF1CF9000F007C117D /* Products */ = { - isa = PBXGroup; - children = ( - 97C146EE1CF9000F007C117D /* Runner.app */, - ); - name = Products; - sourceTree = ""; - }; - 97C146F01CF9000F007C117D /* Runner */ = { - isa = PBXGroup; - children = ( - 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, - 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, - 97C146FA1CF9000F007C117D /* Main.storyboard */, - 97C146FD1CF9000F007C117D /* Assets.xcassets */, - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, - 97C147021CF9000F007C117D /* Info.plist */, - 97C146F11CF9000F007C117D /* Supporting Files */, - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, - ); - path = Runner; - sourceTree = ""; - }; - 97C146F11CF9000F007C117D /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 97C146F21CF9000F007C117D /* main.m */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - F2F265795CE7F5960E889E92 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 7D007BB586407934FC28AF83 /* libPods-Runner.a */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 97C146ED1CF9000F007C117D /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - AA8267A332E7FFF199F5E510 /* [CP] Check Pods Manifest.lock */, - 9740EEB61CF901F6004384FC /* Run Script */, - 97C146EA1CF9000F007C117D /* Sources */, - 97C146EB1CF9000F007C117D /* Frameworks */, - 97C146EC1CF9000F007C117D /* Resources */, - 9705A1C41CF9048500538489 /* Embed Frameworks */, - 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Runner; - productName = Runner; - productReference = 97C146EE1CF9000F007C117D /* Runner.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 97C146E61CF9000F007C117D /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 1100; - ORGANIZATIONNAME = "The Flutter Authors"; - TargetAttributes = { - 97C146ED1CF9000F007C117D = { - CreatedOnToolsVersion = 7.3.1; - }; - }; - }; - buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 97C146E51CF9000F007C117D; - productRefGroup = 97C146EF1CF9000F007C117D /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 97C146ED1CF9000F007C117D /* Runner */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 97C146EC1CF9000F007C117D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Thin Binary"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; - }; - 9740EEB61CF901F6004384FC /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; - }; - AA8267A332E7FFF199F5E510 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 97C146EA1CF9000F007C117D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, - 97C146F31CF9000F007C117D /* main.m in Sources */, - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 97C146FA1CF9000F007C117D /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C146FB1CF9000F007C117D /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C147001CF9000F007C117D /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 97C147031CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 97C147041CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 97C147061CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.packageInfoExample; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 97C147071CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.packageInfoExample; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147031CF9000F007C117D /* Debug */, - 97C147041CF9000F007C117D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147061CF9000F007C117D /* Debug */, - 97C147071CF9000F007C117D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 97C146E61CF9000F007C117D /* Project object */; -} diff --git a/packages/package_info/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/package_info/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a6254f..000000000000 --- a/packages/package_info/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/packages/package_info/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/package_info/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index 3bb3697ef41c..000000000000 --- a/packages/package_info/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/package_info/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/package_info/example/ios/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 21a3cc14c74e..000000000000 --- a/packages/package_info/example/ios/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/packages/package_info/example/ios/Runner/AppDelegate.h b/packages/package_info/example/ios/Runner/AppDelegate.h deleted file mode 100644 index 0681d288bb70..000000000000 --- a/packages/package_info/example/ios/Runner/AppDelegate.h +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import -#import - -@interface AppDelegate : FlutterAppDelegate - -@end diff --git a/packages/package_info/example/ios/Runner/AppDelegate.m b/packages/package_info/example/ios/Runner/AppDelegate.m deleted file mode 100644 index 30b87969f44a..000000000000 --- a/packages/package_info/example/ios/Runner/AppDelegate.m +++ /dev/null @@ -1,17 +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. - -#include "AppDelegate.h" -#include "GeneratedPluginRegistrant.h" - -@implementation AppDelegate - -- (BOOL)application:(UIApplication *)application - didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - [GeneratedPluginRegistrant registerWithRegistry:self]; - // Override point for customization after application launch. - return [super application:application didFinishLaunchingWithOptions:launchOptions]; -} - -@end diff --git a/packages/package_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/package_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index d22f10b2ab63..000000000000 --- a/packages/package_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,116 +0,0 @@ -{ - "images" : [ - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@3x.png", - "scale" : "3x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/packages/package_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/package_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png deleted file mode 100644 index 28c6bf03016f6c994b70f38d1b7346e5831b531f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 564 zcmV-40?Yl0P)Px$?ny*JR5%f>l)FnDQ543{x%ZCiu33$Wg!pQFfT_}?5Q|_VSlIbLC`dpoMXL}9 zHfd9&47Mo(7D231gb+kjFxZHS4-m~7WurTH&doVX2KI5sU4v(sJ1@T9eCIKPjsqSr z)C01LsCxk=72-vXmX}CQD#BD;Cthymh&~=f$Q8nn0J<}ZrusBy4PvRNE}+1ceuj8u z0mW5k8fmgeLnTbWHGwfKA3@PdZxhn|PypR&^p?weGftrtCbjF#+zk_5BJh7;0`#Wr zgDpM_;Ax{jO##IrT`Oz;MvfwGfV$zD#c2xckpcXC6oou4ML~ezCc2EtnsQTB4tWNg z?4bkf;hG7IMfhgNI(FV5Gs4|*GyMTIY0$B=_*mso9Ityq$m^S>15>-?0(zQ<8Qy<_TjHE33(?_M8oaM zyc;NxzRVK@DL6RJnX%U^xW0Gpg(lXp(!uK1v0YgHjs^ZXSQ|m#lV7ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 diff --git a/packages/package_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/package_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png deleted file mode 100644 index f091b6b0bca859a3f474b03065bef75ba58a9e4c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1588 zcmV-42Fv-0P)C1SqPt}wig>|5Crh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)y zRAZ>eDe#*r`yDAVgB_R*LB*MAc)8(b{g{9McCXW!lq7r(btRoB9!8B-#AI6JMb~YFBEvdsV)`mEQO^&#eRKx@b&x- z5lZm*!WfD8oCLzfHGz#u7sT0^VLMI1MqGxF^v+`4YYnVYgk*=kU?HsSz{v({E3lb9 z>+xILjBN)t6`=g~IBOelGQ(O990@BfXf(DRI5I$qN$0Gkz-FSc$3a+2fX$AedL4u{ z4V+5Ong(9LiGcIKW?_352sR;LtDPmPJXI{YtT=O8=76o9;*n%_m|xo!i>7$IrZ-{l z-x3`7M}qzHsPV@$v#>H-TpjDh2UE$9g6sysUREDy_R(a)>=eHw-WAyfIN z*qb!_hW>G)Tu8nSw9yn#3wFMiLcfc4pY0ek1}8(NqkBR@t4{~oC>ryc-h_ByH(Cg5 z>ao-}771+xE3um9lWAY1FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zm zZQj(aA_HeBY&OC^jj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5Kh zX*|AU4QE#~SgPzOXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&g3j|zDgC+}2Q_v%YfDax z!?umcN^n}KYQ|a$Lr+51Nf9dkkYFSjZZjkma$0KOj+;aQ&721~t7QUKx61J3(P4P1 zstI~7-wOACnWP4=8oGOwz%vNDqD8w&Q`qcNGGrbbf&0s9L0De{4{mRS?o0MU+nR_! zrvshUau0G^DeMhM_v{5BuLjb#Hh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG z;P%vktHcXwTj~+IE(~px)3*MY77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC z(zYC$g|@+I+kY;dg_dE>scBf&bP1Nc@Hz<3R)V`=AGkc;8CXqdi=B4l2k|g;2%#m& z*jfX^%b!A8#bI!j9-0Fi0bOXl(-c^AB9|nQaE`*)Hw+o&jS9@7&Gov#HbD~#d{twV zXd^Tr^mWLfFh$@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)y zI9C9*oUga6=hxw6QasLPnee@3^Rr*M{CdaL5=R41nLs(AHk_=Y+A9$2&H(B7!_pURs&8aNw7?`&Z&xY_Ye z)~D5Bog^td-^QbUtkTirdyK^mTHAOuptDflut!#^lnKqU md>ggs(5nOWAqO?umG&QVYK#ibz}*4>0000U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~cTNYR~@Y9KepMPrrB8cABapAFa=`H+UGhkXUZV1GnwR1*lPyZ;*K(i~2gp|@bzp8}og7e*#% zEnr|^CWdVV!-4*Y_7rFvlww2Ze+>j*!Z!pQ?2l->4q#nqRu9`ELo6RMS5=br47g_X zRw}P9a7RRYQ%2Vsd0Me{_(EggTnuN6j=-?uFS6j^u69elMypu?t>op*wBx<=Wx8?( ztpe^(fwM6jJX7M-l*k3kEpWOl_Vk3@(_w4oc}4YF4|Rt=2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX?F&rExl^W|G8Z8;`5z-k}OGNZ0#WVb$WCpQu-_YsiqKP?BB# vzVHS-CTUF4Ozn5G+mq_~Qqto~ahA+K`|lyv3(-e}00000NkvXXu0mjfd`9t{ diff --git a/packages/package_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/package_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png deleted file mode 100644 index d0ef06e7edb86cdfe0d15b4b0d98334a86163658..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1716 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 diff --git a/packages/package_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/package_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png deleted file mode 100644 index c8f9ed8f5cee1c98386d13b17e89f719e83555b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1895 zcmV-t2blPYP)FQtfgmafE#=YDCq`qUBt#QpG%*H6QHY765~R=q zZ6iudfM}q!Pz#~9JgOi8QJ|DSu?1-*(kSi1K4#~5?#|rh?sS)(-JQqX*}ciXJ56_H zdw=^s_srbAdqxlvGyrgGet#6T7_|j;95sL%MtM;q86vOxKM$f#puR)Bjv9Zvz9-di zXOTSsZkM83)E9PYBXC<$6(|>lNLVBb&&6y{NByFCp%6+^ALR@NCTse_wqvNmSWI-m z!$%KlHFH2omF!>#%1l3LTZg(s7eof$7*xB)ZQ0h?ejh?Ta9fDv59+u#MokW+1t8Zb zgHv%K(u9G^Lv`lh#f3<6!JVTL3(dCpxHbnbA;kKqQyd1~^Xe0VIaYBSWm6nsr;dFj z4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3QU4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKubPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`Q zykI#*+rQ=z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRe zt3L_uNyQ*cE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=ky zx=~RKa4{iTm{_>_vSCm?$Ej=i6@=m%@VvAITnigVg{&@!7CDgs908761meDK5azA} z4?=NOH|PdvabgJ&fW2{Mo$Q0CcD8Qc84%{JPYt5EiG{MdLIAeX%T=D7NIP4%Hw}p9 zg)==!2Lbp#j{u_}hMiao9=!VSyx0gHbeCS`;q&vzeq|fs`y&^X-lso(Ls@-706qmA z7u*T5PMo_w3{se1t2`zWeO^hOvTsohG_;>J0wVqVe+n)AbQCx)yh9;w+J6?NF5Lmo zecS@ieAKL8%bVd@+-KT{yI|S}O>pYckUFs;ry9Ow$CD@ztz5K-*D$^{i(_1llhSh^ zEkL$}tsQt5>QA^;QgjgIfBDmcOgi5YDyu?t6vSnbp=1+@6D& z5MJ}B8q;bRlVoxasyhcUF1+)o`&3r0colr}QJ3hcSdLu;9;td>kf@Tcn<@9sIx&=m z;AD;SCh95=&p;$r{Xz3iWCO^MX83AGJ(yH&eTXgv|0=34#-&WAmw{)U7OU9!Wz^!7 zZ%jZFi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i h0WYBP*#0Ks^FNSabJA*5${_#%002ovPDHLkV1oKhTl@e3 diff --git a/packages/package_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/package_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png deleted file mode 100644 index a6d6b8609df07bf62e5100a53a01510388bd2b22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ diff --git a/packages/package_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/package_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png deleted file mode 100644 index a6d6b8609df07bf62e5100a53a01510388bd2b22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ diff --git a/packages/package_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/package_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png deleted file mode 100644 index 75b2d164a5a98e212cca15ea7bf2ab5de5108680..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3831 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 diff --git a/packages/package_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/package_info/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png deleted file mode 100644 index c4df70d39da7941ef3f6dcb7f06a192d8dcb308d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1888 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 iW8 - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/package_info/example/ios/Runner/Base.lproj/Main.storyboard b/packages/package_info/example/ios/Runner/Base.lproj/Main.storyboard deleted file mode 100644 index f3c28516fb38..000000000000 --- a/packages/package_info/example/ios/Runner/Base.lproj/Main.storyboard +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/package_info/example/ios/Runner/Info.plist b/packages/package_info/example/ios/Runner/Info.plist deleted file mode 100644 index 0ba5e95cf616..000000000000 --- a/packages/package_info/example/ios/Runner/Info.plist +++ /dev/null @@ -1,51 +0,0 @@ - - - - - CFBundleDisplayName - Package Info Example - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - package_info_example - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UIRequiredDeviceCapabilities - - arm64 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - - diff --git a/packages/package_info/example/ios/Runner/main.m b/packages/package_info/example/ios/Runner/main.m deleted file mode 100644 index f97b9ef5c8a1..000000000000 --- a/packages/package_info/example/ios/Runner/main.m +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import -#import -#import "AppDelegate.h" - -int main(int argc, char* argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} diff --git a/packages/package_info/example/lib/main.dart b/packages/package_info/example/lib/main.dart deleted file mode 100644 index 60e4a16f7817..000000000000 --- a/packages/package_info/example/lib/main.dart +++ /dev/null @@ -1,81 +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. - -// ignore_for_file: public_member_api_docs - -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:package_info/package_info.dart'; - -void main() { - runApp(MyApp()); -} - -class MyApp extends StatelessWidget { - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'PackageInfo Demo', - theme: ThemeData(primarySwatch: Colors.blue), - home: MyHomePage(title: 'PackageInfo example app'), - ); - } -} - -class MyHomePage extends StatefulWidget { - MyHomePage({Key? key, required this.title}) : super(key: key); - - final String title; - - @override - _MyHomePageState createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - PackageInfo _packageInfo = PackageInfo( - appName: 'Unknown', - packageName: 'Unknown', - version: 'Unknown', - buildNumber: 'Unknown', - ); - - @override - void initState() { - super.initState(); - _initPackageInfo(); - } - - Future _initPackageInfo() async { - final PackageInfo info = await PackageInfo.fromPlatform(); - setState(() { - _packageInfo = info; - }); - } - - Widget _infoTile(String title, String subtitle) { - return ListTile( - title: Text(title), - subtitle: Text(subtitle.isNotEmpty ? subtitle : 'Not set'), - ); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text(widget.title), - ), - body: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - _infoTile('App name', _packageInfo.appName), - _infoTile('Package name', _packageInfo.packageName), - _infoTile('App version', _packageInfo.version), - _infoTile('Build number', _packageInfo.buildNumber), - ], - ), - ); - } -} diff --git a/packages/package_info/example/macos/.gitignore b/packages/package_info/example/macos/.gitignore deleted file mode 100644 index d2fd3772308c..000000000000 --- a/packages/package_info/example/macos/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -# Flutter-related -**/Flutter/ephemeral/ -**/Pods/ - -# Xcode-related -**/xcuserdata/ diff --git a/packages/package_info/example/macos/Flutter/Flutter-Debug.xcconfig b/packages/package_info/example/macos/Flutter/Flutter-Debug.xcconfig deleted file mode 100644 index 785633d3a86b..000000000000 --- a/packages/package_info/example/macos/Flutter/Flutter-Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" -#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/packages/package_info/example/macos/Flutter/Flutter-Release.xcconfig b/packages/package_info/example/macos/Flutter/Flutter-Release.xcconfig deleted file mode 100644 index 5fba960c3af2..000000000000 --- a/packages/package_info/example/macos/Flutter/Flutter-Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" -#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/packages/package_info/example/macos/Podfile b/packages/package_info/example/macos/Podfile deleted file mode 100644 index dade8dfad0dc..000000000000 --- a/packages/package_info/example/macos/Podfile +++ /dev/null @@ -1,40 +0,0 @@ -platform :osx, '10.11' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_macos_podfile_setup - -target 'Runner' do - use_frameworks! - use_modular_headers! - - flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - flutter_additional_macos_build_settings(target) - end -end diff --git a/packages/package_info/example/macos/Runner.xcodeproj/project.pbxproj b/packages/package_info/example/macos/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index 3525d85d6678..000000000000 --- a/packages/package_info/example/macos/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,644 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 51; - objects = { - -/* Begin PBXAggregateTarget section */ - 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { - isa = PBXAggregateTarget; - buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; - buildPhases = ( - 33CC111E2044C6BF0003C045 /* ShellScript */, - ); - dependencies = ( - ); - name = "Flutter Assemble"; - productName = FLX; - }; -/* End PBXAggregateTarget section */ - -/* Begin PBXBuildFile section */ - 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; - 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; - 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; - 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; - 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; - 9A0CC0B8F23AFE5DF719BADB /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CED91D820ABAEDEBEFEBDBDA /* Pods_Runner.framework */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 33CC10E52044A3C60003C045 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 33CC111A2044C6BA0003C045; - remoteInfo = FLX; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 33CC110E2044A8840003C045 /* Bundle Framework */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Bundle Framework"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 26D1A257E90EADFCCC251DEE /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - 2DB1F806EBAC0EF2659B294F /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; - 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* Package Info Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Package Info Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; - 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; - 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; - 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; - 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; - 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; - 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; - 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; - 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; - B3868D4F5169B9990BB5D1F5 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - CED91D820ABAEDEBEFEBDBDA /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 33CC10EA2044A3C60003C045 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 9A0CC0B8F23AFE5DF719BADB /* Pods_Runner.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 33BA886A226E78AF003329D5 /* Configs */ = { - isa = PBXGroup; - children = ( - 33E5194F232828860026EE4D /* AppInfo.xcconfig */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, - ); - path = Configs; - sourceTree = ""; - }; - 33CC10E42044A3C60003C045 = { - isa = PBXGroup; - children = ( - 33FAB671232836740065AC1E /* Runner */, - 33CEB47122A05771004F2AC0 /* Flutter */, - 33CC10EE2044A3C60003C045 /* Products */, - D73912EC22F37F3D000D13A0 /* Frameworks */, - 58201D283908D33A078698CD /* Pods */, - ); - sourceTree = ""; - }; - 33CC10EE2044A3C60003C045 /* Products */ = { - isa = PBXGroup; - children = ( - 33CC10ED2044A3C60003C045 /* Package Info Example.app */, - ); - name = Products; - sourceTree = ""; - }; - 33CC11242044D66E0003C045 /* Resources */ = { - isa = PBXGroup; - children = ( - 33CC10F22044A3C60003C045 /* Assets.xcassets */, - 33CC10F42044A3C60003C045 /* MainMenu.xib */, - 33CC10F72044A3C60003C045 /* Info.plist */, - ); - name = Resources; - path = ..; - sourceTree = ""; - }; - 33CEB47122A05771004F2AC0 /* Flutter */ = { - isa = PBXGroup; - children = ( - 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, - 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, - 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, - 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, - ); - path = Flutter; - sourceTree = ""; - }; - 33FAB671232836740065AC1E /* Runner */ = { - isa = PBXGroup; - children = ( - 33CC10F02044A3C60003C045 /* AppDelegate.swift */, - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, - 33E51913231747F40026EE4D /* DebugProfile.entitlements */, - 33E51914231749380026EE4D /* Release.entitlements */, - 33CC11242044D66E0003C045 /* Resources */, - 33BA886A226E78AF003329D5 /* Configs */, - ); - path = Runner; - sourceTree = ""; - }; - 58201D283908D33A078698CD /* Pods */ = { - isa = PBXGroup; - children = ( - 26D1A257E90EADFCCC251DEE /* Pods-Runner.debug.xcconfig */, - B3868D4F5169B9990BB5D1F5 /* Pods-Runner.release.xcconfig */, - 2DB1F806EBAC0EF2659B294F /* Pods-Runner.profile.xcconfig */, - ); - path = Pods; - sourceTree = ""; - }; - D73912EC22F37F3D000D13A0 /* Frameworks */ = { - isa = PBXGroup; - children = ( - CED91D820ABAEDEBEFEBDBDA /* Pods_Runner.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 33CC10EC2044A3C60003C045 /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - A7B87C53ACB98DD8DB15DE02 /* [CP] Check Pods Manifest.lock */, - 33CC10E92044A3C60003C045 /* Sources */, - 33CC10EA2044A3C60003C045 /* Frameworks */, - 33CC10EB2044A3C60003C045 /* Resources */, - 33CC110E2044A8840003C045 /* Bundle Framework */, - 3399D490228B24CF009A79C7 /* ShellScript */, - F39B8515B2FA626EA800A9B8 /* [CP] Embed Pods Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 33CC11202044C79F0003C045 /* PBXTargetDependency */, - ); - name = Runner; - productName = Runner; - productReference = 33CC10ED2044A3C60003C045 /* Package Info Example.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 33CC10E52044A3C60003C045 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 0930; - ORGANIZATIONNAME = "The Flutter Authors"; - TargetAttributes = { - 33CC10EC2044A3C60003C045 = { - CreatedOnToolsVersion = 9.2; - LastSwiftMigration = 1100; - ProvisioningStyle = Automatic; - SystemCapabilities = { - com.apple.Sandbox = { - enabled = 1; - }; - }; - }; - 33CC111A2044C6BA0003C045 = { - CreatedOnToolsVersion = 9.2; - ProvisioningStyle = Manual; - }; - }; - }; - buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 8.0"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 33CC10E42044A3C60003C045; - productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 33CC10EC2044A3C60003C045 /* Runner */, - 33CC111A2044C6BA0003C045 /* Flutter Assemble */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 33CC10EB2044A3C60003C045 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, - 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3399D490228B24CF009A79C7 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; - }; - 33CC111E2044C6BF0003C045 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - Flutter/ephemeral/FlutterInputs.xcfilelist, - ); - inputPaths = ( - Flutter/ephemeral/tripwire, - ); - outputFileListPaths = ( - Flutter/ephemeral/FlutterOutputs.xcfilelist, - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh\ntouch Flutter/ephemeral/tripwire\n"; - }; - A7B87C53ACB98DD8DB15DE02 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - F39B8515B2FA626EA800A9B8 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/package_info/package_info.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 33CC10E92044A3C60003C045 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, - 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, - 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; - targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { - isa = PBXVariantGroup; - children = ( - 33CC10F52044A3C60003C045 /* Base */, - ); - name = MainMenu.xib; - path = Runner; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 338D0CE9231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Profile; - }; - 338D0CEA231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter/ephemeral", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - }; - name = Profile; - }; - 338D0CEB231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Profile; - }; - 33CC10F92044A3C60003C045 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - 33CC10FA2044A3C60003C045 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Release; - }; - 33CC10FC2044A3C60003C045 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter/ephemeral", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - 33CC10FD2044A3C60003C045 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter/ephemeral", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - 33CC111C2044C6BA0003C045 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 33CC111D2044C6BA0003C045 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC10F92044A3C60003C045 /* Debug */, - 33CC10FA2044A3C60003C045 /* Release */, - 338D0CE9231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC10FC2044A3C60003C045 /* Debug */, - 33CC10FD2044A3C60003C045 /* Release */, - 338D0CEA231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC111C2044C6BA0003C045 /* Debug */, - 33CC111D2044C6BA0003C045 /* Release */, - 338D0CEB231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 33CC10E52044A3C60003C045 /* Project object */; -} diff --git a/packages/package_info/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/package_info/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d68..000000000000 --- a/packages/package_info/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/packages/package_info/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/package_info/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index 9c27c87c30ba..000000000000 --- a/packages/package_info/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/package_info/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/packages/package_info/example/macos/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 21a3cc14c74e..000000000000 --- a/packages/package_info/example/macos/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/packages/package_info/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/package_info/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d68..000000000000 --- a/packages/package_info/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/packages/package_info/example/macos/Runner/AppDelegate.swift b/packages/package_info/example/macos/Runner/AppDelegate.swift deleted file mode 100644 index 5cec4c48f620..000000000000 --- a/packages/package_info/example/macos/Runner/AppDelegate.swift +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import Cocoa -import FlutterMacOS - -@NSApplicationMain -class AppDelegate: FlutterAppDelegate { - override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { - return true - } -} diff --git a/packages/package_info/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/package_info/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index a2ec33f19f11..000000000000 --- a/packages/package_info/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "images" : [ - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_16.png", - "scale" : "1x" - }, - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "2x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "1x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_64.png", - "scale" : "2x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_128.png", - "scale" : "1x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "2x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "1x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "2x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "1x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_1024.png", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/packages/package_info/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/packages/package_info/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png deleted file mode 100644 index 3c4935a7ca84f0976aca34b7f2895d65fb94d1ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 46993 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 diff --git a/packages/package_info/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/packages/package_info/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png deleted file mode 100644 index ed4cc16421680a50164ba74381b4b35ceaa0ccfc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3276 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 N;OXk;vd$@?2>?>Ex^Vyi diff --git a/packages/package_info/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/packages/package_info/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png deleted file mode 100644 index bcbf36df2f2aaaa0a63c7dabc94e600184229d0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5933 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 f$KZHO*s&vt|9^w-6P?|#0pRK8NSwWJ?9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!ItFh?!xdN1Q+aGJ{c&& zS>O>_%)r1c48n{Iv*t(u1=&kHeO=ifbFy+6aSK)V_AxLppYn8Z42d|rc6w}vOsL55 z`t&mC&y2@JTEyg!eDiFX^k#CC!jq%>erB=yHqUP0XcDOTw6ko}L zX;EmMrq(fKk*eygEuA616;0)>@A{TK|55PV@70 z$OfzS*(VJxQev3J?yY?O=ul(v`fp}?u9z`JK3ugibK>)DyCwImZOF4d{xK%%Ks1*} zv$oa)9anR%lXIBUqYnhLmT>VOzHfNP?ZwJNZ!5$s9M08RynIvaXw>@G^T9@r9^KH1 zVy??F&uuk)bH9Y4pQY!hP58i_H6 znl-NcuCpLV6ZWU;4C zu@9exF&OZi`Bovq_m%T+WhU2kvkz@^_LpycBvqm3bMpLw8X-Or5sL>0AKE1$(k_L=_Zc=CUq#=x1-QZf)G7nHu@fmsQ1eN_N3+nTEz`4HI4Z6uVlE zJH+X&det8JU?tO?upcM4Z=cV!JV;yF>FfL5Q$M|W_2Z!P`S=}Wzp|_1^#d%e?_H`> zV@%vA$+bFVqhw9`U;TfP|5|PD{||OiYdor8P*i??|NJcb%kzT_73*7WE?Ua5hAnR2 z=7WE=PhTlJ#ZeRznjTUb;`E(wkMZrj4e|Hilz-mK>9cZHQY**5TUPw~u}k;u73KI}xAx!0m-)GVia|x^d3p~s_9gh83jA&Ra<8rM%`>U3x69t&NzbwWY}7Ar?)FK#IZ0z|d0H0EkRO w3{9;}4Xg|ebq&m|3=9_N6z8I7$jwj5OsmAL;bP(Gi$Dzwp00i_>zopr02+f8CIA2c diff --git a/packages/package_info/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/packages/package_info/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png deleted file mode 100644 index e71a726136a47ed24125c7efc79d68a4a01961b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14800 zcmZ{Lc|26@`~R6Crm_qwyCLMMh!)vm)F@HWt|+6V6lE=CaHfcnn4;2x(VilEl9-V} zsce-cGK|WaF}4{T=lt&J`Fy_L-|vs#>v^7+XU=`!*L|PszSj43o%o$Dj`9mM7C;ar z@3hrnHw59q|KcHn4EQr~{_70*BYk4yj*SqM&s>NcnFoIBdT-sm1A@YrK@dF#f+SPu z{Sb8441xx|AjtYQ1gQq5z1g(^49Fba=I8)nl7BMGpQeB(^8>dY41u79Dw6+j(A_jO z@K83?X~$;S-ud$gYZfZg5|bdvlI`TMaqs!>e}3%9HXev<6;dZZT8Yx`&;pKnN*iCJ z&x_ycWo9{*O}Gc$JHU`%s*$C%@v73hd+Mf%%9ph_Y1juXamcTAHd9tkwoua7yBu?V zgROzw>LbxAw3^;bZU~ZGnnHW?=7r9ZAK#wxT;0O<*z~_>^uV+VCU9B@)|r z*z^v>$!oH7%WZYrwf)zjGU|(8I%9PoktcsH8`z^%$48u z(O_}1U25s@Q*9{-3O!+t?w*QHo;~P99;6-KTGO{Cb#ADDYWF!eATsx{xh-!YMBiuE z%bJc7j^^B$Sa|27XRxg(XTaxWoFI}VFfV>0py8mMM;b^vH}49j;kwCA+Lw=q8lptk z?Pe`{wHI39A&xYkltf5*y%;-DF>5v`-lm0vydYtmqo0sClh5ueHCLJ+6$0y67Z zO-_LCT|JXi3tN7fB-!0_Kn#I+=tyUj87uR5*0>|SZ zy3x2;aql87`{aPZ@UbBwY0;Z-a*lYL90YApOAMKur7YgOiqA~Cne6%b&{V-t>Am2c z{eyEuKl!GsA*jF2H_gvX?bP~v46%3ax$r~B$HnZQ;UiCmRl`ROK8v>;Zs~upH9}qu1ZA3kn-AY2k2@CaH=Qh7K6`nU z3ib(Bk%H*^_omL6N4_G5NpY20UXGi}a$!}#lf<&J4~nhRwRM5cCB3Zvv#6+N1$g@W zj9?qmQ`zz-G9HTpoNl~bCOaEQqlTVYi7G0WmB5E34;f{SGcLvFpOb`+Zm)C(wjqLA z2;+nmB6~QDXbxZGWKLt38I%X$Q!;h zup9S~byxKv=$x|^YEV;l0l67jH~E8BU45ft_7xomac-48oq4PZpSNJbw<7DTM4mmz z!$)z#04cy%b8w@cOvjmb36o;gwYIOLwy+{I#3dJj#W4QdOWwJQ2#20AL49`hSFUa7 zFNAN3OD==G3_kbr1d96>l`_cI`<=thKNh5>hgg7FV>5TfC6d#u)9BNXi@p1K*;2Is zz+x;l4GbSt#*%>1iq}jGIebXYJY5;PGG0y(^{>SSuZY89aL`sDghOM&&pyP6ABJ#w zYwK~4^1eUQD)4!GL>`zrWeHV z-W!6JZbW*Ngo;Edhp_cOysYr!uhKS}vIg_UC}x z=jXxQfV@4B3`5 z!u#byBVXV5GtrSx_8bnT@iKv=Uc6n)Zpa`<9N>+!J~Loxptl5$Z`!u<3a)-+P)say z#=jc7^mJzPMI2;yMhCmN7YN78E7-^S(t8E}FklC;z|4PL{bO|JieM#p1mBjwyZMEm zkX^A1RXPGeS2YqtPMX~~t^$~oeFfWAU#jVLi%Z@l2hle^3|e(q?(uS=BVauF?VF{j z(owKLJuze;_@5p1OtRyrT`EFXf)NfMYb-)E8RVVdr<@}M>4R&~P=;B`c1L%o|8YfB z-a(LB-i8jc5!&B5cowyI2~M^YID&@Xt(D9v{|DB z959W z*vEA77fh3*w*UJ`4Y(bxsoEy6hm7_Wc5gT0^cvso%Ow>9<&@9Q>mxb6-^pv)5yc>n zQ~^!qY(lPQ1EDGkr%_*y*D8T^YbCa52^MVqYpTLhgJ;N5PfCQ{SXk|plD#Sm+g4c- zFeL2Dih35W4{_qb75U`4Rb#S0FEo%F85dOhXSX0huPOxdAid{&p6P;+9}I)XU7^=3RZu9M(g0dLyz_7$8K{`AddBLOfU&B_QNHtmsnNXq`hy~% zvJ{vtz~Yt9X|o}5vXX)9ZCHaRq8iAb zUDj8%(MpzJN39LferYKvIc!)z^5T-eW@j3h9a6d%WZ!%@2^@4+6%Z9W1GHZbOj|sb z0cU$}*~G$fYvDC|XulSC_;m}?KC2jg5pxES$Bt!hA|@EX*2+O!UEb5sn_^d>z;>;r~ zmO3BivdXboPY*}amsO&`xk|e)S*u=`o67MC(1WTB;OwG+ua4UV7T5Wvy%?U{Pa5cO zMoLG>#@chO{Oc72XPyX8f3jC7P`$j4$)0wc(b50COaDP3_Cm}aPAglUa7kRXAqmo5 z0KDD7G>Gmnpons40WJNYn+pxko92GXy@PvSErKE-Ou3)3UiRr7!L4+0%+5}sD{bf)uj^ounQ-Yn2%%JoZ%FjUv%yjS?Ks4u_88Jh%tNliYW~817IV@fqd1T zi(?;Fv-s3rQEn=9G*E-QzSl%YS|^fe*yn}Aqh!&P<5%#oB?*{wZMa5$PYa*A{VA8! zbOfS1W!W}cTo%g~iP$>WhE_x7#O4?h$jq=>{M77>bTAK_ z6uU0tl6HARboGi}=4krr6WP`9`aAt&P5ON1v(+H{T?jZuJ}B{L-=z3VX)}mZwzrqH zpf?T!k&$?{&{0_p>b`kdJbSb(p~tFcuG4zh6}hfl@ues6CfJu<-P+!>FlYMlD_3!E z9$6VE==tlxNYe(s;@8@+4c4jQ$R2g8t0QwE>Et|)5)@kJj6^yaqFYY?0LEM2C!+7+ z+FN|UxR1GCy1KA`{T_%24U+Vserchr5h`;U7TZPr@43x#MMN{@vV?KSII}R@5k`7cVK}E;c)$f~_{ZLDOoL|-01p~oafxi4F zG$?Wha&a*rTnz-nTI-bAJ*SLb!5(L!#iRdvLEyo>7D_=H78-qZrm=6{hkUR{tR{H! z`ZTOV$Oi6^qX5=_{f}V9h}WJAO%h9)kEUF#*-JyYDbOGZ>Nfs%7L}4p zopIul&&Bbn!C9o83ypC6W4F$X=_|pex$V4!Whm#48Wfm3*oAW0Gc&#&b+oq<8>aZR z2BLpouQQwyf$aHpQUK3pMRj(mS^^t#s$IC3{j*m9&l7sQt@RU{o_}N-xI_lh`rND^ zX~-8$o(;p^wf3_5-WZ^qgW`e8T@37{`J)e2KJdSSCUpX6KZu0Ga&U*+u3*PDAs1uK zpl)40+fROA@Vo#vK?^@Pq%w8DO9HdfmH+~vNinZ$5GRz?sD|k246NepqZd`>81P^P z#x#3kUS-}x4k%&~iEUrsb&-X#_;;?y9oCP4crMkC`=q58#NxQ| z*NXNA;GR4X=GiGXwab5=&M3j04fQw%2UxM`S(aE)_PlgJttBX96$$lY@Q%0xV^IbcHqzw^Uk&E=vFB;EQ@kzVIeM8lDIW_Q_ zrfy)l6s2QBApF;J2xTD_@wuNMlwDfsdfMyzRq)<>qG{M)Yt}9F1{1HaI_X7=F=7>& zYB54VaKlxu0lIgS;Ac&25Aw(tcf@K~(cvPi8(OChzhlYp6}#<_MVhU95sD&)n0FtL zmxm4w$~s(S9jmHOgyovpG!x4uLfJsMsJn^QMraKAa1Ix?{zkV!a7{f%-!u2{NqZ&) zo+^XB`eFQ4 zk-(;_>T#pTKyvW${yL|XXbcv?CE2Tp<3(PjeXhu^Jrp6^Mj}lg_)jamK{g;C+q^Da ztb!gV!q5)B7G1%lVanA2b>Xs?%hzCgJ{Hc!ldr9dnz7k^xG#4pDpr|0ZmxxiUVl}j zbD_rg3yAFQ>nnc)0>71D==715jRj4XsRb2#_lJoSOwky&c4957V-|m)@>b^Nak1!8 z@DsIOS8>Oe^T>tgB)WX3Y^I^65Uae+2M;$RxX_C)Aoo0dltvoRRIVQkpnegWj;D#G z+TwFIRUN%bZW3(K{8yN8!(1i0O!X3YN?Zo08L5D~)_tWQA8&|CvuQb8Od?p_x=GMF z-B@v9iNLYS1lUsbb`!%f5+1ev8RFPk7xyx5*G;ybRw(PW*yEZ$unu2`wpH)7b@ZXEz4Jr{?KZKYl!+3^)Q z)~^g?KlPGtT!{yQU&(Z&^rVjPu>ueeZN86AnhRwc)m|;5NvM&W3xD%n`+Hjg5$e8M zKh1Ju82L~&^ z-IQ5bYhsjqJfr38iwi~8<{oeREh|3l)*Enj4&Q$+mM$15YqwXeufK9P^(O=pj=F-1 zD+&REgwY~!W#ZPccSEi(*jiKJ5)Q|zX;hP}S2T9j_);epH9JQs{n>RG}{Nak)vIbfa zFQm?H;D+tzrBN2)6{?Mo%fzN6;6d_h0Qyn61)+XT63=!T*WQyRUoB_x0_)Ir`$FtS zak07C(mOaWN5m%bk?F9X&@mEVKN%{R6obt(9qw&p>w&p;R*l2th9$D^*`pC}NmB+v z>bk;OJ(C8p$G;jNvRsBbt=a!!tKnjJ`9*yQFgjEN1HcC<&>u9aStT3>Oq=MOQV!#WOZ6{cv$YVmlJdovPRV}<=IZUPeBVh5DC z91-?kimq3JUr;UMQ@0?h52gupvG=~(5AVdP(2(%*sL8!#K1-L$9B7MrWGdt(h&whR@vz~0oEHF8u3U1Q zdGdaIytJj4x@eF*E+^zgi{nPCA8tkjN}UoR8WhDzM3-zLqx0z?2tTdDKyENM={fp8VC@3Dt`AiK$;K#H$K2{08mrHG%jgEOLX3MCsG>afZm_0mLPS4jmYUJp~Dm! z5AUe_vEaOAT3zWdwl#cLvqwd1^lwW?gt7(92wEsOE6c#<0}{szFV4(uO70?3>=((! zQr}1{J?Wx2ZmjxYL_8OB*m&mimfojzYn~PiJ2g8R&ZRx-i^yF#sdhEWXAUIZ@J?T$ zs3PgT2<&Ki>Bob_n(@S>kUIvE+nY~ti9~6j;O9VAG#{oZ!DZCW)}i6iA!Tgsyz+hC z1VVyvbQ_nwgdZSEP=U4d#U`2*`e~d4y8uM4Bcmm%!jidaee#4WqN!ZnlBmbYpuaO! z!rU3`Kl2 z0O7PD&fQ|_b)Ub!g9^s;C2e>1i*2&?1$6yEn?~Y zI)-WIN8N(5s9;grW+J@K@I%g#?G&hzmlgV=L}ZA{f>3YCMx^P{u@c5Z;U1qmdk#)L zvX6z1!sL>+@vxO8qVn#k3YxYi?8ggV){?Rn@j$+Fd4-QkuH1@)j#3-=f82GZ!nl~{ zzZ(?kO`ANttVeHSo%xmH!NmNZECh*{s!-8S>ALoe5xOPs>|P5BbUmP@rlV8`d(c=7 zypcpLaI*FM^;GM%@q`GAb8kO`$oE|R48yn)?p(c1t>5;Wwn5r6ck&uw4}TnT80jI`IS~J%q8CpaVgIze<8IykSpVBg8~E! zW_tGqB;GO47r_er05y+Kwrcn{VLxL*1;HMv@*sd}MB6DH4zaP~u4Y;>@Nw7?F8S?c zfVIY(^ntnGgWlD|idzGz$Y+Oh(Ra=&VIf4!K2W*a)(%5%78s}8qxOknAGtDAq+HMO zM+Nu;0OgQRn36 zA@~a8`uVQ~v9?d!BxnsVaB-z-djypO44BjQAmg7&eVoaew|~)wH$SgefJ2$7_RiY+ z_7ACGoFM6Lhvho+eUG@pU&0X(Uy(*j;9pr?ET?FHTXadlfXC|MReZoU5>AG`mTM<% zc~*I@E*u0|hwVTdFA~4^b2VT7_~}~tCueNY{de3og=ASFQ`)0dhC2~Ne<}}Rc?ptA zi}+bQE%N9o*hpSUMH)9xt%Zlz&^p&5=cW}{m#f85iVX64^{!(vhClT<I)+c)RuiyrZqIw4v`z%YK&;_Fh4_+0B?qAGxMfAM`LzG_bjD>ib4;KGT4_1I>sxvL&&qp40ajgQOqIE^9=Az4w#ymo)bW-Vg{T!n=l&|nR_ zw+wcH|FxUH63)~{M;goHepmD{Fe?W9sO|eJP9L$G<{e_7FxxuXQ+)(Z^@;X8I1=%k zTK$gbHA1^4W<`q~ubQ0M_C^CA5#Z&*nGc(T?4Y_2jLu&FJDQYpCSiRny->$+nC9Jl z?avTW`ZXYT51%SrEq!}dXNM&!pM6nmL^lce=%S7{_TS)ckN8;{p*LT~LMgmlE~dpL zEBQy-jDj%cSK6N3)|CCR0LQ$N6iDM~+-1Oz|LAdkip(VZcO`gqCuJ+(Mm{m6@P%_; zBtF|MMVMP;E`5NJ{&@4j^JE5j&}(Jq{lCGL(P^#uqvbD`2)FVyfNgy|pvT!XY;02Z zZWbgGsvi6#!*$Zxwd{Xk6_M{+^yV_K@%_SAW(x)Lg|*AuG-%g2#GQYk8F?W&8|2dU z;00ppzrQnnYXnT`(S%_qF2#QNz&@Y$zcq+O8p>Gto2&4z8(^#cY?DuQwBQP4Fe?qUK_-yh4xT{8O@gb`uh` z>Q%jrgPAnANn4_)->n;w{Mei#J)F+`12&+-MLKSRzF6bL3;4O~oy~v7 zL0K-=m?>>(^qDCgvFRLBI@`04EGdTxe5}xBg#7#Wb!aUED;?5BLDEvZ@tai4*Rh8& z4V)cOr}DJ0&(FjWH%50Y+&=WtB42^eEVsmaHG)Il#j265oK&Bot(+-IIn`6InmuE# z;)qXs+X{fSb8^rYb#46X5?KCzH9X0>ppBQi(aKS--;4yA%0N|D<#8RZlOS(8n26=u zv~y;KC>`ypW=aqj`&x9 z0Zm>NKp}hPJu1+QDo(_U(Gt0SZ`IJWnp%QK`pye>Bm!w{sG>;VU^2 z4lZhV1}tCE8(?zu#j99|l3-qRBcz3bG+DlyxPGB$^6B^ssc_qYQ6lG0q~EAI?1$?( zahfn%etVvuKwB7R=>JDQluP97nLDM6*5;b0Ox#b{4nIgZA*+?IvyDN{K9WGnlA=Ju z+)6hjr}{;GxQQIDr3*lf32lRp{nHP8uiz^Fa|K+dUc@wD4Kf5RPxVkUZFCdtZH{+=c$AC)G2T-Qn@BPbr zZigIhKhKrVYy`!Mlc#HVr=CURVrhUjExhI~gZ%a=WM9BwvnN?=z!_ZQ$(sP?X;2Jy zyI$}H^^SvH2tf6+Uk$pJww@ngzPp856-l9g6WtW+%Yf>N^A}->#1W2n=WJ%sZ0<){Z&#% z^Kzl$>Km)sIxKLFjtc;}bZeoaZSpL4>`jCmAeRM-NP9sQ&-mi@p0j7Iq>1n&z@8?M z%dM7K^SgE5z)@i5w#rLE4+8%|^J`a6wYr`3BlvdD>7xW?Dd>`0HC0o{w7r_ot~h*G z2gI7Y!AUZ6YN+z$=GNzns@Tu7BxgAb3MBha30-ZG7a%rckU5}y{df`lj@^+34kr5> z988PPbWYdHye~=?>uZ4N&MN@4RBLk_?9W*b$}jqt0j%>yO9QOV(*!#cX~=wRdVL&S zhPQ{${0CGU-rfdS&b@u|IK{hV2Z=(*B2d0?&jwWfT=?Gk`4T9TfMQ)CfNgpLQa#>Q z%6A$w#QNc&qOtrHAbqY>J782@!X{9Y@N(HMSr;PP^;0DlJNxfC`oMB%Ocg zC*hnEsF|p*=CVe^dT)>BTL0yff)uo!U<+_2o3p)CE8quU1JI(=6)9$KxVdJYD*S*~ zzNeSkzFIQyqK}578+qq6X8rrRdgX z4k&R=AGex~a)MoB0pK&|yA<(*J#P&tR?ImBVD)ZTA4VH5L5DxXe<-*s`Aox%H1{-^Qa`kG_DGXD%QX-;l1#&#IVQP6>kir ztO@~ZvJDPnTvKt>fc*(j$W^)JhWk{4kWwbpFIXzuPt2V%M4H19-i5Gn*6(D`4_c1+ zYoI1@yT^~9JF~t>2eVM6p=GP3b*;daJpQOhAMNO|LKnwE2B5n8y9mf;q=)-L_FfD0 z<}YIRBO{k)6AHAn8iG>pYT+3bJ7jvP9}LSMR1nZW$5HR%PD1rFz z{4XE^Vmi-QX#?|Farz=CYS_8!%$E#G%4j2+;Avz|9QBj|YIExYk?y-1(j}0h{$$MnC_*F0U2*ExSi1ZCb_S9aV zTgyGP0Cl=m`emxM4Qih1E{`J{4oJo8K}WnH`@js^pR7Z-vTBK5F5JIFCDN}7pU^_nV>NTz@2$|Kcc5o+L&^Db_AQ);F?)X5BF*QJRCdLI-a%gW z++DZM)x=6*fNrSaUA&hf&CUqC$F*y^CJC-MAm9gd*5#^mh;-dR1?a&<3-hp3@}XN! z&8dcwo6=MQua%0KFvYbi>O{j)RrbDQo3S*y!oEJ~2=}^-v%zn~@hnmKGOvX6JLr;>DNC3)={8OM9n5Zs*(DlS*|%JTniJX2Uav7sOFT0vdIiUOC5pEtY?EF)@Fh9pCfD%N zXskZ8b^ldI{HHj{-l?iWo@IW6Nr`hAS>f8S*8FGc*gmcK^f2JS+>I&r#Gcewy=-JM zv0*w<5qBa6UQB@`esOG*4*t@7c9AkrTpM`v=eY?cO#z17H9B%Xy4m!}LhW}*iZ27w1?HrevgB1SZ1q2X$mm@FK@Qt7o z!s~Lio^IRdwzyvQ80{5iYeTV@mAo=2o5>KepRH0d{*Szlg~n%w2)S5v2|K8}pj;c{ zoDRLvYJO1@?x-=mq+LVhD{l-1-Dw4`7M?3@+ z`fu7?1#9W++6Y46N=H0+bD|CJH~q*CdEBm8D##VS7`cXy4~+x=ZC17rJeBh zI~qW^&FU`+e!{AKO3(>z5Ghh14bUT$=4B>@DVm(cj* zSLA*j!?z!=SLuVvAPh_EFKx}JE8T8;Gx)LH^H136=#Jn3Bo*@?=S`5M{WJPY&~ODs z+^V57DhJ2kD^Z|&;H}eoN~sxS8~cN5u1eW{t&y{!ouH`%p4(yDZaqw$%dlm4A0f0| z8H}XZFDs?3QuqI^PEy}T;r!5+QpfKEt&V|D)Z*xoJ?XXZ+k!sU2X!rcTF4tg8vWPM zr-JE>iu9DZK`#R5gQO{nyGDALY!l@M&eZsc*j*H~l4lD)8S?R*nrdxn?ELUR4kxK? zH(t9IM~^mfPs9WxR>J{agadQg@N6%=tUQ8Bn++TC|Hbqn*q;WydeNIS@gt|3j!P`w zxCKoeKQ*WBlF%l4-apIhERKl(hXS1vVk$U?Wifi)&lL6vF@bmFXmQEe{=$iG)Zt*l z0df@_)B-P_^K2P7h=>OIQ6f0Q-E@|M?$Z5n^oN>2_sBCpN>q(LnqUoef{tm^5^L$# z{<SL zKmH78cHX`4cBKIY8u1x*lwrgP^fJ%E&&AmHrRY7^hH*=2OA9K?!+|~Aeia=nAA`5~ z#zI=h#I>@FXaGk(n)0uqelNY;A5I9obE~OjsuW!%^NxK*52CfBPWYuw--v<1v|B>h z8R=#$TS-Pt3?d@P+xqmYpL4oB8- z>w99}%xqy9W!A^ODfLq8iA@z}10u?o#nG#MXumSaybi(S{`wIM z&nE3n2gWWMu93EvtofWzvG2{v;$ysuw^8q?3n}y=pB1vUr5gi++PjiyBH3jzKBRny zSO~O++1ZLdy7v7VzS&$yY;^Z7*j_#BI`PK`dAzJa9G1{9ahPqPi1C}ti+L)WHii*= z+RZ^+at-tlatc4|akPa&9H;%gn9aS`X_kfb>n>#NTyUVM6m4NCIfLm(28>qaYv7}t zn`M;XcONtXoa3#u3{L-ytd_&g z2mO$8CnE?460w#eSm|smlnNwFHM;A&IxSKLzVkV7nNVqZ*A`)eI{Nbg6WxsarAFuc=FFf1z|%#eTvBgUhY}N zsCT>`_YO>14i^vFX0KXbARLItzT{TeD%N~=ovGtZ6j{>PxkuYlHNTe0!u>rgw#?td z{)n=QrGvgCDE6BUem$Rh(1y!$@(Bn!k3E0|>PQ(8O==zN`?yBhAqlWyq+c%+h?p^- zE&OtLind}^_=>pbhxOgOIC0q9{cLK6p6*eg_|S+p9$W~_u4wzx@N?$QmFg2S)m~^R znni$X{U*!lHgdS@fI;|Owl=9Gwi?dr0m#>yL<8<}bLW_Kpl| zSGesADX&n?qmHC`2GyIev^hi~ka}ISZ^Y4w-yUzyPxaJB0mm%ww^>if3<;P^U+L5=s+cifT-ct*;!dOOk#SOZNv@a^J|DrS3YtSn8EEAlabX1NV3RfHwZn_41Xa z4;$taa6JJR()-FQ<#0G~WlML<l5I+IPnqDpW(PP>hRcQ+S2zU?tbG^(y z1K_?1R){jF;OKGw0WYjnm>aPxnmr5?bP?^B-|Fv`TT4ecH3O`Z3`X_r;vgFn>t1tE zGE6W2PODPKUj+@a%3lB;lS?srE5lp(tZ;uvzrPb){f~n7v_^z! z=16!Vdm!Q0q#?jy0qY%#0d^J8D9o)A;Rj!~j%u>KPs-tB08{4s1ry9VS>gW~5o^L; z7vyjmfXDGRVFa@-mis2!a$GI@9kE*pe3y_C3-$iVGUTQzZE+%>vT0=r|2%xMDBC@>WlkGU4CjoWs@D(rZ zS1NB#e69fvI^O#5r$Hj;bhHPEE4)4q5*t5Gyjzyc{)o459VkEhJ$%hJUC&67k z7gdo`Q*Jm3R&?ueqBezPTa}OI9wqcc;FRTcfVXob^z|dNIB0hMkHV26$zA%YgR$sM zTKM61S}#wJ#u+0UDE3N+U*~Tz1nnV;W<8Akz&6M7-6mIF(Pq`wJ1A%loYL( zIS;&2((xbyL7zoyaY2Sa%BBYBxo6Aa*53`~e@|RA`MP+?iI4KZ+y4EU&I zS_|(#*&j2hxpELa3r0O7ok&5!ijRiRu9i-_3cdnydZU9Mp6Y);skv%!$~`i-J7e-g zj@EoHf+gtcrKf;tY5`4iLnWSHa)9brUM$XmEzG3T0BXTG_+0}p7uGLs^(uYh0j$;~ zT1&~S%_Y5VImvf1EkD7vP-@F%hRlBe{a@T!SW(4WEQd1!O47*Crf@u-TS==48iR5x z!*`Ul4AJI^vIVaN3u5UifXBX{fJ@z>4Q2#1?jpcdLocwymBgKrZ+^Cb@QuIxl58B* zD{t-W3;M;{MGHm_@&n(6A-AsD;JO#>J3o4ru{hy;k;8?=rkp0tadEEcHNECoTI(W31`El-CI0eWQ zWD4&2ehvACkLCjG`82T`L^cNNC4Oo2IH(T4e;C75IwkJ&`|ArqSKD}TX_-E*eeiU& ziUuAC)A?d>-;@9Jcmsdca>@q1`6vzo^3etEH%1Gco&gvC{;Y-qyJ$Re`#A!5Kd((5 z6sSiKnA20uPX0**Mu&6tNgTunUR1sodoNmDst1&wz8v7AG3=^huypTi`S7+GrO$D6 z)0Ja-y5r?QQ+&jVQBjitIZ`z2Ia}iXWf#=#>nU+ zL29$)Q>f#o<#4deo!Kuo@WX{G(`eLaf%(_Nc}E`q=BXHMS(Os{!g%(|&tTDIczE_# z5y%wjCp9S?&*8bS3imJi_9_COC)-_;6D9~8Om@?U2PGQpM^7LKG7Q~(AoSRgP#tZfVDF_zr;_U*!F9qsbVQ@un9O2>T4M5tr0B~~v_@a=w^8h510a#=L z;8+9zhV}57uajb+9DbZm1G`_NqOuKN`bQ2fw9A*v*Kdb_E-SA`?2 z)OFIY-%uD`JZUZg?D4lHtNegKgWr!1m%hOpu5`R+bZ2K#&)*R-7ElKYo0$0xYxIL8 zLg%u|4oZixz}ILB-@aS4=XOe)z!VL6@?dX{LW^YCPjKtyw44)xT=H;h(fmFr>R?p%r5*}W z7_bo0drVDRq9V9QL4_!dazughK6t}tVVvBq={T0+3(1zmb>f+|;{D%J?^xnZcqio5 z%H?@L+L-CIdO=x6QrALL9&PwvjrZi5NS)1e<*%V8ntw~S2PF}zH}B5f_DHyB=I3m@ z_;^TpN|sesCU}qxQ`~jIwF>#8wGvxg9kdMT$}us8BM&W>OzZ|ry2BB)+UY*_yH+&L zl_=Jy9BNzIZs}D~Yv_H%HPjVGNV=xT3xpIW!Np1F^G#9Y8X zl)c_V1(DhYu-v%H3-m&n%M_}}c{E5Wu+6*>R24gW_A7$(U=9D|H$r;;;@o zJ)c_CmVf9l*;4SyJ}E{+4)}^C>SIJ*_bul7OJ{v&0oO>jG(5xzYP0$I%*YH|Mwu#r zubNW5VZ9^X#Phw<;?=^G?Kg&C)^x1FVsKGZ*n+{C1znj~YHSP?6PS(k5e9qGvS4X* z=1kA_27(iV65a(i+Sicmd@Vzf^2@*Wed-`aYQ~em=-h%Pu`gHfz)&@$hpr<&mNO={ zl^kI0HP0wTbbh{d(>5a#;zT2_=ppef?;D4;2^}&kZjB^yl%LBJ;|> zkLc)JEg*5rpQ;_)w?PnKynWtv!@ z>}+am{@(g$KKM+e$ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/package_info/example/macos/Runner/Configs/AppInfo.xcconfig b/packages/package_info/example/macos/Runner/Configs/AppInfo.xcconfig deleted file mode 100644 index 4ecd23f86c29..000000000000 --- a/packages/package_info/example/macos/Runner/Configs/AppInfo.xcconfig +++ /dev/null @@ -1,14 +0,0 @@ -// Application-level settings for the Runner target. -// -// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the -// future. If not, the values below would default to using the project name when this becomes a -// 'flutter create' template. - -// The application's name. By default this is also the title of the Flutter window. -PRODUCT_NAME = Package Info Example - -// The application's bundle identifier -PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.packageInfoExample - -// The copyright displayed in application information -PRODUCT_COPYRIGHT = Copyright © 2020 The Flutter Authors. All rights reserved. diff --git a/packages/package_info/example/macos/Runner/Configs/Debug.xcconfig b/packages/package_info/example/macos/Runner/Configs/Debug.xcconfig deleted file mode 100644 index 36b0fd9464f4..000000000000 --- a/packages/package_info/example/macos/Runner/Configs/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "../../Flutter/Flutter-Debug.xcconfig" -#include "Warnings.xcconfig" diff --git a/packages/package_info/example/macos/Runner/Configs/Release.xcconfig b/packages/package_info/example/macos/Runner/Configs/Release.xcconfig deleted file mode 100644 index dff4f49561c8..000000000000 --- a/packages/package_info/example/macos/Runner/Configs/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "../../Flutter/Flutter-Release.xcconfig" -#include "Warnings.xcconfig" diff --git a/packages/package_info/example/macos/Runner/Configs/Warnings.xcconfig b/packages/package_info/example/macos/Runner/Configs/Warnings.xcconfig deleted file mode 100644 index 42bcbf4780b1..000000000000 --- a/packages/package_info/example/macos/Runner/Configs/Warnings.xcconfig +++ /dev/null @@ -1,13 +0,0 @@ -WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings -GCC_WARN_UNDECLARED_SELECTOR = YES -CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES -CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE -CLANG_WARN__DUPLICATE_METHOD_MATCH = YES -CLANG_WARN_PRAGMA_PACK = YES -CLANG_WARN_STRICT_PROTOTYPES = YES -CLANG_WARN_COMMA = YES -GCC_WARN_STRICT_SELECTOR_MATCH = YES -CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES -CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES -GCC_WARN_SHADOW = YES -CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/packages/package_info/example/macos/Runner/DebugProfile.entitlements b/packages/package_info/example/macos/Runner/DebugProfile.entitlements deleted file mode 100644 index dddb8a30c851..000000000000 --- a/packages/package_info/example/macos/Runner/DebugProfile.entitlements +++ /dev/null @@ -1,12 +0,0 @@ - - - - - com.apple.security.app-sandbox - - com.apple.security.cs.allow-jit - - com.apple.security.network.server - - - diff --git a/packages/package_info/example/macos/Runner/Info.plist b/packages/package_info/example/macos/Runner/Info.plist deleted file mode 100644 index a8e4f02255ba..000000000000 --- a/packages/package_info/example/macos/Runner/Info.plist +++ /dev/null @@ -1,34 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIconFile - - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSMinimumSystemVersion - $(MACOSX_DEPLOYMENT_TARGET) - NSHumanReadableCopyright - $(PRODUCT_COPYRIGHT) - NSMainNibFile - MainMenu - NSPrincipalClass - NSApplication - CFBundleDisplayName - $(PRODUCT_NAME) - - diff --git a/packages/package_info/example/macos/Runner/MainFlutterWindow.swift b/packages/package_info/example/macos/Runner/MainFlutterWindow.swift deleted file mode 100644 index 32aaeedceb1f..000000000000 --- a/packages/package_info/example/macos/Runner/MainFlutterWindow.swift +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import Cocoa -import FlutterMacOS - -class MainFlutterWindow: NSWindow { - override func awakeFromNib() { - let flutterViewController = FlutterViewController.init() - let windowFrame = self.frame - self.contentViewController = flutterViewController - self.setFrame(windowFrame, display: true) - - RegisterGeneratedPlugins(registry: flutterViewController) - - super.awakeFromNib() - } -} diff --git a/packages/package_info/example/macos/Runner/Release.entitlements b/packages/package_info/example/macos/Runner/Release.entitlements deleted file mode 100644 index 852fa1a4728a..000000000000 --- a/packages/package_info/example/macos/Runner/Release.entitlements +++ /dev/null @@ -1,8 +0,0 @@ - - - - - com.apple.security.app-sandbox - - - diff --git a/packages/package_info/example/package_info_example.iml b/packages/package_info/example/package_info_example.iml deleted file mode 100644 index 9d5dae19540c..000000000000 --- a/packages/package_info/example/package_info_example.iml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/package_info/example/package_info_example_android.iml b/packages/package_info/example/package_info_example_android.iml deleted file mode 100644 index 462b903e05b6..000000000000 --- a/packages/package_info/example/package_info_example_android.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/packages/package_info/example/pubspec.yaml b/packages/package_info/example/pubspec.yaml deleted file mode 100644 index 9c1c3cd0ad6e..000000000000 --- a/packages/package_info/example/pubspec.yaml +++ /dev/null @@ -1,28 +0,0 @@ -name: package_info_example -description: Demonstrates how to use the package_info plugin. -publish_to: none - -environment: - sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.12.13+hotfix.5" - -dependencies: - flutter: - sdk: flutter - package_info: - # When depending on this package from a real application you should use: - # package_info: ^x.y.z - # See https://dart.dev/tools/pub/dependencies#version-constraints - # The example app is bundled with the plugin so we use a path dependency on - # the parent directory to use the current plugin's version. - path: ../ - integration_test: - sdk: flutter - -dev_dependencies: - flutter_driver: - sdk: flutter - pedantic: ^1.10.0 - -flutter: - uses-material-design: true diff --git a/packages/package_info/example/test_driver/integration_test.dart b/packages/package_info/example/test_driver/integration_test.dart deleted file mode 100644 index 6a0e6fa82dbe..000000000000 --- a/packages/package_info/example/test_driver/integration_test.dart +++ /dev/null @@ -1,9 +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. - -// @dart=2.9 - -import 'package:integration_test/integration_test_driver.dart'; - -Future main() => integrationDriver(); diff --git a/packages/package_info/ios/Assets/.gitkeep b/packages/package_info/ios/Assets/.gitkeep deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/package_info/ios/Classes/FLTPackageInfoPlugin.h b/packages/package_info/ios/Classes/FLTPackageInfoPlugin.h deleted file mode 100644 index 65be5f99d569..000000000000 --- a/packages/package_info/ios/Classes/FLTPackageInfoPlugin.h +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -@interface FLTPackageInfoPlugin : NSObject -@end diff --git a/packages/package_info/ios/Classes/FLTPackageInfoPlugin.m b/packages/package_info/ios/Classes/FLTPackageInfoPlugin.m deleted file mode 120000 index a1ddabd76793..000000000000 --- a/packages/package_info/ios/Classes/FLTPackageInfoPlugin.m +++ /dev/null @@ -1 +0,0 @@ -../../darwin/Classes/FLTPackageInfoPlugin.m \ No newline at end of file diff --git a/packages/package_info/ios/package_info.podspec b/packages/package_info/ios/package_info.podspec deleted file mode 100644 index 12ccab3b855b..000000000000 --- a/packages/package_info/ios/package_info.podspec +++ /dev/null @@ -1,22 +0,0 @@ -# -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html -# -Pod::Spec.new do |s| - s.name = 'package_info' - s.version = '0.0.1' - s.summary = 'Flutter Package Info' - s.description = <<-DESC -This Flutter plugin provides an API for querying information about an application package. -Downloaded by pub (not CocoaPods). - DESC - s.homepage = 'https://github.com/flutter/plugins' - s.license = { :type => 'BSD', :file => '../LICENSE' } - s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/package_info' } - s.documentation_url = 'https://pub.dev/packages/package_info' - s.source_files = 'Classes/**/*' - s.public_header_files = 'Classes/**/*.h' - s.dependency 'Flutter' - s.platform = :ios, '8.0' - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' } -end diff --git a/packages/package_info/lib/package_info.dart b/packages/package_info/lib/package_info.dart deleted file mode 100644 index 69246813873a..000000000000 --- a/packages/package_info/lib/package_info.dart +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; - -import 'package:flutter/services.dart'; - -const MethodChannel _kChannel = - MethodChannel('plugins.flutter.io/package_info'); - -/// Application metadata. Provides application bundle information on iOS and -/// application package information on Android. -/// -/// ```dart -/// PackageInfo packageInfo = await PackageInfo.fromPlatform() -/// print("Version is: ${packageInfo.version}"); -/// ``` -class PackageInfo { - /// Constructs an instance with the given values for testing. [PackageInfo] - /// instances constructed this way won't actually reflect any real information - /// from the platform, just whatever was passed in at construction time. - /// - /// See [fromPlatform] for the right API to get a [PackageInfo] that's - /// actually populated with real data. - PackageInfo({ - required this.appName, - required this.packageName, - required this.version, - required this.buildNumber, - }); - - static PackageInfo? _fromPlatform; - - /// Retrieves package information from the platform. - /// The result is cached. - static Future fromPlatform() async { - PackageInfo? packageInfo = _fromPlatform; - if (packageInfo != null) return packageInfo; - - final Map map = - (await _kChannel.invokeMapMethod('getAll'))!; - - packageInfo = PackageInfo( - appName: map["appName"] ?? '', - packageName: map["packageName"] ?? '', - version: map["version"] ?? '', - buildNumber: map["buildNumber"] ?? '', - ); - _fromPlatform = packageInfo; - return packageInfo; - } - - /// The app name. `CFBundleDisplayName` on iOS, `application/label` on Android. - final String appName; - - /// The package name. `bundleIdentifier` on iOS, `getPackageName` on Android. - final String packageName; - - /// The package version. `CFBundleShortVersionString` on iOS, `versionName` on Android. - final String version; - - /// The build number. `CFBundleVersion` on iOS, `versionCode` on Android. - final String buildNumber; -} diff --git a/packages/package_info/macos/Assets/.gitkeep b/packages/package_info/macos/Assets/.gitkeep deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/package_info/macos/Classes/FLTPackageInfoPlugin.h b/packages/package_info/macos/Classes/FLTPackageInfoPlugin.h deleted file mode 100644 index 590e8263d951..000000000000 --- a/packages/package_info/macos/Classes/FLTPackageInfoPlugin.h +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -@interface FLTPackageInfoPlugin : NSObject -@end diff --git a/packages/package_info/macos/Classes/FLTPackageInfoPlugin.m b/packages/package_info/macos/Classes/FLTPackageInfoPlugin.m deleted file mode 120000 index a1ddabd76793..000000000000 --- a/packages/package_info/macos/Classes/FLTPackageInfoPlugin.m +++ /dev/null @@ -1 +0,0 @@ -../../darwin/Classes/FLTPackageInfoPlugin.m \ No newline at end of file diff --git a/packages/package_info/macos/package_info.podspec b/packages/package_info/macos/package_info.podspec deleted file mode 100644 index dbe5bd9a105b..000000000000 --- a/packages/package_info/macos/package_info.podspec +++ /dev/null @@ -1,20 +0,0 @@ -# -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html -# -Pod::Spec.new do |s| - s.name = 'package_info' - s.version = '0.0.1' - s.summary = 'Flutter plugin for querying information about the application package.' - s.description = <<-DESC -Flutter plugin for querying information about the application package, based on bundle data. - DESC - s.homepage = 'https://github.com/flutter/plugins/tree/master/packages/package_info' - s.license = { :type => 'BSD', :file => '../LICENSE' } - s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/package_info' } - s.source_files = 'Classes/**/*' - s.public_header_files = 'Classes/**/*.h' - s.dependency 'FlutterMacOS' - s.platform = :osx, '10.11' - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } -end diff --git a/packages/package_info/package_info_android.iml b/packages/package_info/package_info_android.iml deleted file mode 100644 index 462b903e05b6..000000000000 --- a/packages/package_info/package_info_android.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/packages/package_info/pubspec.yaml b/packages/package_info/pubspec.yaml deleted file mode 100644 index dd9de6f14808..000000000000 --- a/packages/package_info/pubspec.yaml +++ /dev/null @@ -1,34 +0,0 @@ -name: package_info -description: Flutter plugin for querying information about the application - package, such as CFBundleVersion on iOS or versionCode on Android. -repository: https://github.com/flutter/plugins/tree/master/packages/package_info -issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+package_info%22 -version: 2.0.2 - -environment: - sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.12.13+hotfix.5" - -flutter: - plugin: - platforms: - android: - package: io.flutter.plugins.packageinfo - pluginClass: PackageInfoPlugin - ios: - pluginClass: FLTPackageInfoPlugin - macos: - pluginClass: FLTPackageInfoPlugin - -dependencies: - flutter: - sdk: flutter - -dev_dependencies: - flutter_test: - sdk: flutter - flutter_driver: - sdk: flutter - integration_test: - sdk: flutter - pedantic: ^1.10.0 diff --git a/packages/package_info/test/package_info_test.dart b/packages/package_info/test/package_info_test.dart deleted file mode 100644 index 91661de72103..000000000000 --- a/packages/package_info/test/package_info_test.dart +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:package_info/package_info.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - const MethodChannel channel = - MethodChannel('plugins.flutter.io/package_info'); - late List log; - - channel.setMockMethodCallHandler((MethodCall methodCall) async { - log.add(methodCall); - switch (methodCall.method) { - case 'getAll': - return { - 'appName': 'package_info_example', - 'buildNumber': '1', - 'packageName': 'io.flutter.plugins.packageinfoexample', - 'version': '1.0', - }; - default: - assert(false); - return null; - } - }); - - setUp(() { - log = []; - }); - - test('fromPlatform', () async { - final PackageInfo info = await PackageInfo.fromPlatform(); - expect(info.appName, 'package_info_example'); - expect(info.buildNumber, '1'); - expect(info.packageName, 'io.flutter.plugins.packageinfoexample'); - expect(info.version, '1.0'); - expect( - log, - [ - isMethodCall('getAll', arguments: null), - ], - ); - }); -} diff --git a/packages/sensors/AUTHORS b/packages/sensors/AUTHORS deleted file mode 100644 index 493a0b4ef9c2..000000000000 --- a/packages/sensors/AUTHORS +++ /dev/null @@ -1,66 +0,0 @@ -# Below is a list of people and organizations that have contributed -# to the Flutter project. Names should be added to the list like so: -# -# Name/Organization - -Google Inc. -The Chromium Authors -German Saprykin -Benjamin Sauer -larsenthomasj@gmail.com -Ali Bitek -Pol Batlló -Anatoly Pulyaevskiy -Hayden Flinner -Stefano Rodriguez -Salvatore Giordano -Brian Armstrong -Paul DeMarco -Fabricio Nogueira -Simon Lightfoot -Ashton Thomas -Thomas Danner -Diego Velásquez -Hajime Nakamura -Tuyển Vũ Xuân -Miguel Ruivo -Sarthak Verma -Mike Diarmid -Invertase -Elliot Hesp -Vince Varga -Aawaz Gyawali -EUI Limited -Katarina Sheremet -Thomas Stockx -Sarbagya Dhaubanjar -Ozkan Eksi -Rishab Nayak -ko2ic -Jonathan Younger -Jose Sanchez -Debkanchan Samadder -Audrius Karosevicius -Lukasz Piliszczuk -SoundReply Solutions GmbH -Rafal Wachol -Pau Picas -Christian Weder -Alexandru Tuca -Christian Weder -Rhodes Davis Jr. -Luigi Agosti -Quentin Le Guennec -Koushik Ravikumar -Nissim Dsilva -Giancarlo Rocha -Ryo Miyake -Théo Champion -Kazuki Yamaguchi -Eitan Schwartz -Chris Rutkowski -Juan Alvarez -Aleksandr Yurkovskiy -Anton Borries -Alex Li -Rahul Raj <64.rahulraj@gmail.com> diff --git a/packages/sensors/CHANGELOG.md b/packages/sensors/CHANGELOG.md deleted file mode 100644 index b5d64b48edda..000000000000 --- a/packages/sensors/CHANGELOG.md +++ /dev/null @@ -1,178 +0,0 @@ -## NEXT - -* Remove references to the Android V1 embedding. -* Updated Android lint settings. -* Updates Android compileSdkVersion to 31. - -## 2.0.3 - -* Update README to point to Plus Plugins version. - -## 2.0.2 - -* Fix -Wstrict-prototypes analyzer warning in iOS plugin. - -## 2.0.1 - -* Migrate maven repository from jcenter to mavenCentral. - -## 2.0.0 - -* Migrate to null safety. - -## 0.4.2+8 - -* Fix outdated links across a number of markdown files ([#3276](https://github.com/flutter/plugins/pull/3276)) - -## 0.4.2+7 - -* Update Flutter SDK constraint. - -## 0.4.2+6 - -* Update android compileSdkVersion to 29. - -## 0.4.2+5 - -* Keep handling deprecated Android v1 classes for backward compatibility. - -## 0.4.2+4 - -* Update package:e2e -> package:integration_test - -## 0.4.2+3 - -* Update package:e2e reference to use the local version in the flutter/plugins - repository. - -## 0.4.2+2 - -* Post-v2 Android embedding cleanup. - -## 0.4.2+1 - -* Update lower bound of dart dependency to 2.1.0. - -## 0.4.2 - -* Remove Android dependencies fallback. -* Require Flutter SDK 1.12.13+hotfix.5 or greater. -* Fix CocoaPods podspec lint warnings. - -## 0.4.1+10 - -* Declare API stability and compatibility with `1.0.0` (more details at: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0). - -## 0.4.1+9 - -* Replace deprecated `getFlutterEngine` call on Android. - -## 0.4.1+8 - -* Make the pedantic dev_dependency explicit. - -## 0.4.1+7 - -* Fixed example userAccelerometerEvent in documentation - -## 0.4.1+6 - -* Migrate from deprecated BinaryMessages to ServicesBinding.instance.defaultBinaryMessenger. -* Require Flutter SDK 1.12.13+hotfix.5 or greater (current stable). - -## 0.4.1+5 - -* Fix example `setState()` called after `dispose()` by canceling the timer. - -## 0.4.1+4 - -* Remove the deprecated `author:` field from pubspec.yaml -* Migrate the plugin to the pubspec platforms manifest. -* Require Flutter SDK 1.10.0 or greater. - -## 0.4.1+3 - -* Improve documentation and add unit test coverage. - -## 0.4.1+2 - -* Remove AndroidX warnings. - -## 0.4.1+1 - -* Include lifecycle dependency as a compileOnly one on Android to resolve - potential version conflicts with other transitive libraries. - -## 0.4.1 - -* Support the v2 Android embedder. -* Update to AndroidX. -* Migrate to using the new e2e test binding. -* Add a e2e test. - -## 0.4.0+3 - -* Update and migrate iOS example project. -* Define clang module for iOS. - -## 0.4.0+2 - -* Suppress deprecation warning for BinaryMessages. See: https://github.com/flutter/flutter/issues/33446 - -## 0.4.0+1 - -* Log a more detailed warning at build time about the previous AndroidX - migration. - -## 0.4.0 - -* **Breaking change**. Migrate from the deprecated original Android Support - Library to AndroidX. This shouldn't result in any functional changes, but it - requires any Android apps using this plugin to [also - migrate](https://developer.android.com/jetpack/androidx/migrate) if they're - using the original support library. - -## 0.3.5 - -* Added missing test package dependency. - -## 0.3.4 - -* Make sensors Dart 2 compliant. - -## 0.3.3 - -* Updated Gradle tooling to match Android Studio 3.1.2. - -## 0.3.2 - -* Added user acceleration sensor events (i.e. accelerometer without gravity). - -## 0.3.1 - -* Fixed Dart 2 type error with iOS sensor events. - -## 0.3.0 - -* **Breaking change**. Set SDK constraints to match the Flutter beta release. - -## 0.2.1 - -* Fixed warnings from the Dart 2.0 analyzer. -* Simplified and upgraded Android project template to Android SDK 27. -* Updated package description. - -## 0.2.0 - -* **Breaking change**. Upgraded to Gradle 4.1 and Android Studio Gradle plugin - 3.0.1. Older Flutter projects need to upgrade their Gradle setup as well in - order to use this version of the plugin. Instructions can be found - [here](https://github.com/flutter/flutter/wiki/Updating-Flutter-projects-to-Gradle-4.1-and-Android-Studio-Gradle-plugin-3.0.1). - -## 0.1.1 - -* Added FLT prefix to iOS types. - -## 0.1.0 - -* Initial Open Source release. diff --git a/packages/sensors/LICENSE b/packages/sensors/LICENSE deleted file mode 100644 index c6823b81eb84..000000000000 --- a/packages/sensors/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright 2013 The Flutter Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/sensors/README.md b/packages/sensors/README.md deleted file mode 100644 index 1f46ce1c3608..000000000000 --- a/packages/sensors/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# sensors - ---- - -## Deprecation Notice - -This plugin has been replaced by the [Flutter Community Plus -Plugins](https://plus.fluttercommunity.dev/) version, -[`sensors_plus`](https://pub.dev/packages/sensors_plus). -No further updates are planned to this plugin, and we encourage all users to -migrate to the Plus version. - -Critical fixes (e.g., for any security incidents) will be provided through the -end of 2021, at which point this package will be marked as discontinued. - ---- - -A Flutter plugin to access the accelerometer and gyroscope sensors. - -## Usage - -To use this plugin, add `sensors` as a [dependency in your pubspec.yaml -file](https://flutter.dev/docs/development/platform-integration/platform-channels). - -This will expose three classes of sensor events, through three different -streams. - -- `AccelerometerEvent`s describe the velocity of the device, including the - effects of gravity. Put simply, you can use accelerometer readings to tell if - the device is moving in a particular direction. -- `UserAccelerometerEvent`s also describe the velocity of the device, but don't - include gravity. They can also be thought of as just the user's affect on the - device. -- `GyroscopeEvent`s describe the rotation of the device. - -Each of these is exposed through a `BroadcastStream`: `accelerometerEvents`, -`userAccelerometerEvents`, and `gyroscopeEvents`, respectively. - - -### Example - -``` dart -import 'package:sensors/sensors.dart'; - -accelerometerEvents.listen((AccelerometerEvent event) { - print(event); -}); -// [AccelerometerEvent (x: 0.0, y: 9.8, z: 0.0)] - -userAccelerometerEvents.listen((UserAccelerometerEvent event) { - print(event); -}); -// [UserAccelerometerEvent (x: 0.0, y: 0.0, z: 0.0)] - -gyroscopeEvents.listen((GyroscopeEvent event) { - print(event); -}); -// [GyroscopeEvent (x: 0.0, y: 0.0, z: 0.0)] - -``` - -Also see the `example` subdirectory for an example application that uses the -sensor data. diff --git a/packages/sensors/analysis_options.yaml b/packages/sensors/analysis_options.yaml deleted file mode 100644 index cda4f6e153e6..000000000000 --- a/packages/sensors/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../analysis_options_legacy.yaml diff --git a/packages/sensors/android/build.gradle b/packages/sensors/android/build.gradle deleted file mode 100644 index a9a9045301c9..000000000000 --- a/packages/sensors/android/build.gradle +++ /dev/null @@ -1,48 +0,0 @@ -group 'io.flutter.plugins.sensors' -version '1.0-SNAPSHOT' - -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' - } -} - -rootProject.allprojects { - repositories { - google() - mavenCentral() - } -} - -apply plugin: 'com.android.library' - -android { - compileSdkVersion 31 - - defaultConfig { - minSdkVersion 16 - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - lintOptions { - disable 'InvalidPackage' - disable 'GradleDependency' - } - - - testOptions { - unitTests.includeAndroidResources = true - unitTests.returnDefaultValues = true - unitTests.all { - testLogging { - events "passed", "skipped", "failed", "standardOut", "standardError" - outputs.upToDateWhen {false} - showStandardStreams = true - } - } - } -} diff --git a/packages/sensors/android/settings.gradle b/packages/sensors/android/settings.gradle deleted file mode 100644 index 48202890db16..000000000000 --- a/packages/sensors/android/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'sensors' diff --git a/packages/sensors/android/src/main/AndroidManifest.xml b/packages/sensors/android/src/main/AndroidManifest.xml deleted file mode 100644 index 44d0c9993ce9..000000000000 --- a/packages/sensors/android/src/main/AndroidManifest.xml +++ /dev/null @@ -1,3 +0,0 @@ - - diff --git a/packages/sensors/android/src/main/java/io/flutter/plugins/sensors/SensorsPlugin.java b/packages/sensors/android/src/main/java/io/flutter/plugins/sensors/SensorsPlugin.java deleted file mode 100644 index c643edce3401..000000000000 --- a/packages/sensors/android/src/main/java/io/flutter/plugins/sensors/SensorsPlugin.java +++ /dev/null @@ -1,72 +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.sensors; - -import android.content.Context; -import android.hardware.Sensor; -import android.hardware.SensorManager; -import io.flutter.embedding.engine.plugins.FlutterPlugin; -import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.plugin.common.EventChannel; - -/** SensorsPlugin */ -public class SensorsPlugin implements FlutterPlugin { - private static final String ACCELEROMETER_CHANNEL_NAME = - "plugins.flutter.io/sensors/accelerometer"; - private static final String GYROSCOPE_CHANNEL_NAME = "plugins.flutter.io/sensors/gyroscope"; - private static final String USER_ACCELEROMETER_CHANNEL_NAME = - "plugins.flutter.io/sensors/user_accel"; - - private EventChannel accelerometerChannel; - private EventChannel userAccelChannel; - private EventChannel gyroscopeChannel; - - /** Plugin registration. */ - @SuppressWarnings("deprecation") - public static void registerWith(io.flutter.plugin.common.PluginRegistry.Registrar registrar) { - SensorsPlugin plugin = new SensorsPlugin(); - plugin.setupEventChannels(registrar.context(), registrar.messenger()); - } - - @Override - public void onAttachedToEngine(FlutterPluginBinding binding) { - final Context context = binding.getApplicationContext(); - setupEventChannels(context, binding.getBinaryMessenger()); - } - - @Override - public void onDetachedFromEngine(FlutterPluginBinding binding) { - teardownEventChannels(); - } - - private void setupEventChannels(Context context, BinaryMessenger messenger) { - accelerometerChannel = new EventChannel(messenger, ACCELEROMETER_CHANNEL_NAME); - final StreamHandlerImpl accelerationStreamHandler = - new StreamHandlerImpl( - (SensorManager) context.getSystemService(context.SENSOR_SERVICE), - Sensor.TYPE_ACCELEROMETER); - accelerometerChannel.setStreamHandler(accelerationStreamHandler); - - userAccelChannel = new EventChannel(messenger, USER_ACCELEROMETER_CHANNEL_NAME); - final StreamHandlerImpl linearAccelerationStreamHandler = - new StreamHandlerImpl( - (SensorManager) context.getSystemService(context.SENSOR_SERVICE), - Sensor.TYPE_LINEAR_ACCELERATION); - userAccelChannel.setStreamHandler(linearAccelerationStreamHandler); - - gyroscopeChannel = new EventChannel(messenger, GYROSCOPE_CHANNEL_NAME); - final StreamHandlerImpl gyroScopeStreamHandler = - new StreamHandlerImpl( - (SensorManager) context.getSystemService(context.SENSOR_SERVICE), - Sensor.TYPE_GYROSCOPE); - gyroscopeChannel.setStreamHandler(gyroScopeStreamHandler); - } - - private void teardownEventChannels() { - accelerometerChannel.setStreamHandler(null); - userAccelChannel.setStreamHandler(null); - gyroscopeChannel.setStreamHandler(null); - } -} diff --git a/packages/sensors/android/src/main/java/io/flutter/plugins/sensors/StreamHandlerImpl.java b/packages/sensors/android/src/main/java/io/flutter/plugins/sensors/StreamHandlerImpl.java deleted file mode 100644 index 7e6da156386d..000000000000 --- a/packages/sensors/android/src/main/java/io/flutter/plugins/sensors/StreamHandlerImpl.java +++ /dev/null @@ -1,50 +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.sensors; - -import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorEventListener; -import android.hardware.SensorManager; -import io.flutter.plugin.common.EventChannel; - -class StreamHandlerImpl implements EventChannel.StreamHandler { - - private SensorEventListener sensorEventListener; - private final SensorManager sensorManager; - private final Sensor sensor; - - StreamHandlerImpl(SensorManager sensorManager, int sensorType) { - this.sensorManager = sensorManager; - sensor = sensorManager.getDefaultSensor(sensorType); - } - - @Override - public void onListen(Object arguments, EventChannel.EventSink events) { - sensorEventListener = createSensorEventListener(events); - sensorManager.registerListener(sensorEventListener, sensor, sensorManager.SENSOR_DELAY_NORMAL); - } - - @Override - public void onCancel(Object arguments) { - sensorManager.unregisterListener(sensorEventListener); - } - - SensorEventListener createSensorEventListener(final EventChannel.EventSink events) { - return new SensorEventListener() { - @Override - public void onAccuracyChanged(Sensor sensor, int accuracy) {} - - @Override - public void onSensorChanged(SensorEvent event) { - double[] sensorValues = new double[event.values.length]; - for (int i = 0; i < event.values.length; i++) { - sensorValues[i] = event.values[i]; - } - events.success(sensorValues); - } - }; - } -} diff --git a/packages/sensors/example/README.md b/packages/sensors/example/README.md deleted file mode 100644 index 9e7d7e0a76a9..000000000000 --- a/packages/sensors/example/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# sensors_example - -Demonstrates how to use the sensors plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/sensors/example/android.iml b/packages/sensors/example/android.iml deleted file mode 100644 index 462b903e05b6..000000000000 --- a/packages/sensors/example/android.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/packages/sensors/example/android/app/build.gradle b/packages/sensors/example/android/app/build.gradle deleted file mode 100644 index c5dd855a64b7..000000000000 --- a/packages/sensors/example/android/app/build.gradle +++ /dev/null @@ -1,60 +0,0 @@ -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -apply plugin: 'com.android.application' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - -android { - compileSdkVersion 31 - - lintOptions { - disable 'InvalidPackage' - } - - defaultConfig { - applicationId "io.flutter.plugins.sensorsexample" - minSdkVersion 16 - targetSdkVersion 28 - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug - } - } -} - -flutter { - source '../..' -} - -dependencies { - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test:runner:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' -} diff --git a/packages/sensors/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/sensors/example/android/app/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 9a4163a4f5ee..000000000000 --- a/packages/sensors/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/packages/sensors/example/android/app/src/main/AndroidManifest.xml b/packages/sensors/example/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index ea3155cb9722..000000000000 --- a/packages/sensors/example/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - diff --git a/packages/sensors/example/android/app/src/main/java/io/flutter/plugins/DartIntegrationTest.java b/packages/sensors/example/android/app/src/main/java/io/flutter/plugins/DartIntegrationTest.java deleted file mode 100644 index 0f4298dca155..000000000000 --- a/packages/sensors/example/android/app/src/main/java/io/flutter/plugins/DartIntegrationTest.java +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package io.flutter.plugins; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -public @interface DartIntegrationTest {} diff --git a/packages/sensors/example/android/app/src/main/java/io/flutter/plugins/sensorsexample/FlutterActivityTest.java b/packages/sensors/example/android/app/src/main/java/io/flutter/plugins/sensorsexample/FlutterActivityTest.java deleted file mode 100644 index 52a6b8bebaf3..000000000000 --- a/packages/sensors/example/android/app/src/main/java/io/flutter/plugins/sensorsexample/FlutterActivityTest.java +++ /dev/null @@ -1,19 +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.sensorsexample; - -import androidx.test.rule.ActivityTestRule; -import dev.flutter.plugins.integration_test.FlutterTestRunner; -import io.flutter.embedding.android.FlutterActivity; -import io.flutter.plugins.DartIntegrationTest; -import org.junit.Rule; -import org.junit.runner.RunWith; - -@DartIntegrationTest -@RunWith(FlutterTestRunner.class) -public class FlutterActivityTest { - @Rule - public ActivityTestRule rule = new ActivityTestRule<>(FlutterActivity.class); -} diff --git a/packages/sensors/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/sensors/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index db77bb4b7b0906d62b1847e87f15cdcacf6a4f29..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ diff --git a/packages/sensors/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/sensors/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 17987b79bb8a35cc66c3c1fd44f5a5526c1b78be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ diff --git a/packages/sensors/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/sensors/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index d5f1c8d34e7a88e3f88bea192c3a370d44689c3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof diff --git a/packages/sensors/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/sensors/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 4d6372eebdb28e45604e46eeda8dd24651419bc0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` diff --git a/packages/sensors/example/android/build.gradle b/packages/sensors/example/android/build.gradle deleted file mode 100644 index e101ac08df55..000000000000 --- a/packages/sensors/example/android/build.gradle +++ /dev/null @@ -1,29 +0,0 @@ -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' - } -} - -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(':app') -} - -task clean(type: Delete) { - delete rootProject.buildDir -} diff --git a/packages/sensors/example/android/gradle.properties b/packages/sensors/example/android/gradle.properties deleted file mode 100644 index 38c8d4544ff1..000000000000 --- a/packages/sensors/example/android/gradle.properties +++ /dev/null @@ -1,4 +0,0 @@ -org.gradle.jvmargs=-Xmx1536M -android.enableR8=true -android.useAndroidX=true -android.enableJetifier=true diff --git a/packages/sensors/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/sensors/example/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 019065d1d650..000000000000 --- a/packages/sensors/example/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip diff --git a/packages/sensors/example/android/settings.gradle b/packages/sensors/example/android/settings.gradle deleted file mode 100644 index 115da6cb4f4d..000000000000 --- a/packages/sensors/example/android/settings.gradle +++ /dev/null @@ -1,15 +0,0 @@ -include ':app' - -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() - -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withInputStream { stream -> plugins.load(stream) } -} - -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory -} diff --git a/packages/sensors/example/integration_test/sensors_test.dart b/packages/sensors/example/integration_test/sensors_test.dart deleted file mode 100644 index 3b8f614d2dcb..000000000000 --- a/packages/sensors/example/integration_test/sensors_test.dart +++ /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. - -// @dart = 2.9 - -import 'dart:async'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:sensors/sensors.dart'; -import 'package:integration_test/integration_test.dart'; - -void main() { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - testWidgets('Can subscript to accelerometerEvents and get non-null events', - (WidgetTester tester) async { - final Completer completer = - Completer(); - StreamSubscription subscription; - subscription = accelerometerEvents.listen((AccelerometerEvent event) { - completer.complete(event); - subscription.cancel(); - }); - expect(await completer.future, isNotNull); - }); -} diff --git a/packages/sensors/example/ios/Flutter/AppFrameworkInfo.plist b/packages/sensors/example/ios/Flutter/AppFrameworkInfo.plist deleted file mode 100644 index 6c2de8086bcd..000000000000 --- a/packages/sensors/example/ios/Flutter/AppFrameworkInfo.plist +++ /dev/null @@ -1,30 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - App - CFBundleIdentifier - io.flutter.flutter.app - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - App - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - UIRequiredDeviceCapabilities - - arm64 - - MinimumOSVersion - 8.0 - - diff --git a/packages/sensors/example/ios/Flutter/Debug.xcconfig b/packages/sensors/example/ios/Flutter/Debug.xcconfig deleted file mode 100644 index e8efba114687..000000000000 --- a/packages/sensors/example/ios/Flutter/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" -#include "Generated.xcconfig" diff --git a/packages/sensors/example/ios/Flutter/Release.xcconfig b/packages/sensors/example/ios/Flutter/Release.xcconfig deleted file mode 100644 index 399e9340e6f6..000000000000 --- a/packages/sensors/example/ios/Flutter/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" -#include "Generated.xcconfig" diff --git a/packages/sensors/example/ios/Podfile b/packages/sensors/example/ios/Podfile deleted file mode 100644 index f7d6a5e68c3a..000000000000 --- a/packages/sensors/example/ios/Podfile +++ /dev/null @@ -1,38 +0,0 @@ -# Uncomment this line to define a global platform for your project -# platform :ios, '9.0' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_ios_podfile_setup - -target 'Runner' do - flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - flutter_additional_ios_build_settings(target) - end -end diff --git a/packages/sensors/example/ios/Runner.xcodeproj/project.pbxproj b/packages/sensors/example/ios/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index 69cd37f9ab86..000000000000 --- a/packages/sensors/example/ios/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,462 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; - 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - A5B646543530B300A487D9B1 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B16C8D77F2F0873936309F38 /* libPods-Runner.a */; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 9705A1C41CF9048500538489 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 518BFCF6A33590E963FE1FA9 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - 65D7779632A59CFED1723B85 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; - 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; - 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - B16C8D77F2F0873936309F38 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 97C146EB1CF9000F007C117D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - A5B646543530B300A487D9B1 /* libPods-Runner.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 5B101E38E51195F91ACE826E /* Pods */ = { - isa = PBXGroup; - children = ( - 65D7779632A59CFED1723B85 /* Pods-Runner.debug.xcconfig */, - 518BFCF6A33590E963FE1FA9 /* Pods-Runner.release.xcconfig */, - ); - name = Pods; - sourceTree = ""; - }; - 9740EEB11CF90186004384FC /* Flutter */ = { - isa = PBXGroup; - children = ( - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 9740EEB31CF90195004384FC /* Generated.xcconfig */, - ); - name = Flutter; - sourceTree = ""; - }; - 97C146E51CF9000F007C117D = { - isa = PBXGroup; - children = ( - 9740EEB11CF90186004384FC /* Flutter */, - 97C146F01CF9000F007C117D /* Runner */, - 97C146EF1CF9000F007C117D /* Products */, - 5B101E38E51195F91ACE826E /* Pods */, - DEA20432CDDA0D695086BE46 /* Frameworks */, - ); - sourceTree = ""; - }; - 97C146EF1CF9000F007C117D /* Products */ = { - isa = PBXGroup; - children = ( - 97C146EE1CF9000F007C117D /* Runner.app */, - ); - name = Products; - sourceTree = ""; - }; - 97C146F01CF9000F007C117D /* Runner */ = { - isa = PBXGroup; - children = ( - 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, - 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, - 97C146FA1CF9000F007C117D /* Main.storyboard */, - 97C146FD1CF9000F007C117D /* Assets.xcassets */, - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, - 97C147021CF9000F007C117D /* Info.plist */, - 97C146F11CF9000F007C117D /* Supporting Files */, - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, - ); - path = Runner; - sourceTree = ""; - }; - 97C146F11CF9000F007C117D /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 97C146F21CF9000F007C117D /* main.m */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - DEA20432CDDA0D695086BE46 /* Frameworks */ = { - isa = PBXGroup; - children = ( - B16C8D77F2F0873936309F38 /* libPods-Runner.a */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 97C146ED1CF9000F007C117D /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - 7B77DB2BA78582CC43C8E79F /* [CP] Check Pods Manifest.lock */, - 9740EEB61CF901F6004384FC /* Run Script */, - 97C146EA1CF9000F007C117D /* Sources */, - 97C146EB1CF9000F007C117D /* Frameworks */, - 97C146EC1CF9000F007C117D /* Resources */, - 9705A1C41CF9048500538489 /* Embed Frameworks */, - 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Runner; - productName = Runner; - productReference = 97C146EE1CF9000F007C117D /* Runner.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 97C146E61CF9000F007C117D /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 1100; - ORGANIZATIONNAME = "The Flutter Authors"; - TargetAttributes = { - 97C146ED1CF9000F007C117D = { - CreatedOnToolsVersion = 7.3.1; - }; - }; - }; - buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 97C146E51CF9000F007C117D; - productRefGroup = 97C146EF1CF9000F007C117D /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 97C146ED1CF9000F007C117D /* Runner */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 97C146EC1CF9000F007C117D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Thin Binary"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; - }; - 7B77DB2BA78582CC43C8E79F /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 9740EEB61CF901F6004384FC /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 97C146EA1CF9000F007C117D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, - 97C146F31CF9000F007C117D /* main.m in Sources */, - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 97C146FA1CF9000F007C117D /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C146FB1CF9000F007C117D /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C147001CF9000F007C117D /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 97C147031CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 97C147041CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 97C147061CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = ""; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.sensorsExample; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 97C147071CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = ""; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.sensorsExample; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147031CF9000F007C117D /* Debug */, - 97C147041CF9000F007C117D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147061CF9000F007C117D /* Debug */, - 97C147071CF9000F007C117D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 97C146E61CF9000F007C117D /* Project object */; -} diff --git a/packages/sensors/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/sensors/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a6254f..000000000000 --- a/packages/sensors/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/packages/sensors/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/sensors/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index 3bb3697ef41c..000000000000 --- a/packages/sensors/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/sensors/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/sensors/example/ios/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 21a3cc14c74e..000000000000 --- a/packages/sensors/example/ios/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/packages/sensors/example/ios/Runner/AppDelegate.h b/packages/sensors/example/ios/Runner/AppDelegate.h deleted file mode 100644 index 0681d288bb70..000000000000 --- a/packages/sensors/example/ios/Runner/AppDelegate.h +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import -#import - -@interface AppDelegate : FlutterAppDelegate - -@end diff --git a/packages/sensors/example/ios/Runner/AppDelegate.m b/packages/sensors/example/ios/Runner/AppDelegate.m deleted file mode 100644 index 30b87969f44a..000000000000 --- a/packages/sensors/example/ios/Runner/AppDelegate.m +++ /dev/null @@ -1,17 +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. - -#include "AppDelegate.h" -#include "GeneratedPluginRegistrant.h" - -@implementation AppDelegate - -- (BOOL)application:(UIApplication *)application - didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - [GeneratedPluginRegistrant registerWithRegistry:self]; - // Override point for customization after application launch. - return [super application:application didFinishLaunchingWithOptions:launchOptions]; -} - -@end diff --git a/packages/sensors/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/sensors/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index d22f10b2ab63..000000000000 --- a/packages/sensors/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,116 +0,0 @@ -{ - "images" : [ - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@3x.png", - "scale" : "3x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/packages/sensors/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/sensors/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png deleted file mode 100644 index 28c6bf03016f6c994b70f38d1b7346e5831b531f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 564 zcmV-40?Yl0P)Px$?ny*JR5%f>l)FnDQ543{x%ZCiu33$Wg!pQFfT_}?5Q|_VSlIbLC`dpoMXL}9 zHfd9&47Mo(7D231gb+kjFxZHS4-m~7WurTH&doVX2KI5sU4v(sJ1@T9eCIKPjsqSr z)C01LsCxk=72-vXmX}CQD#BD;Cthymh&~=f$Q8nn0J<}ZrusBy4PvRNE}+1ceuj8u z0mW5k8fmgeLnTbWHGwfKA3@PdZxhn|PypR&^p?weGftrtCbjF#+zk_5BJh7;0`#Wr zgDpM_;Ax{jO##IrT`Oz;MvfwGfV$zD#c2xckpcXC6oou4ML~ezCc2EtnsQTB4tWNg z?4bkf;hG7IMfhgNI(FV5Gs4|*GyMTIY0$B=_*mso9Ityq$m^S>15>-?0(zQ<8Qy<_TjHE33(?_M8oaM zyc;NxzRVK@DL6RJnX%U^xW0Gpg(lXp(!uK1v0YgHjs^ZXSQ|m#lV7ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 diff --git a/packages/sensors/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/sensors/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png deleted file mode 100644 index f091b6b0bca859a3f474b03065bef75ba58a9e4c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1588 zcmV-42Fv-0P)C1SqPt}wig>|5Crh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)y zRAZ>eDe#*r`yDAVgB_R*LB*MAc)8(b{g{9McCXW!lq7r(btRoB9!8B-#AI6JMb~YFBEvdsV)`mEQO^&#eRKx@b&x- z5lZm*!WfD8oCLzfHGz#u7sT0^VLMI1MqGxF^v+`4YYnVYgk*=kU?HsSz{v({E3lb9 z>+xILjBN)t6`=g~IBOelGQ(O990@BfXf(DRI5I$qN$0Gkz-FSc$3a+2fX$AedL4u{ z4V+5Ong(9LiGcIKW?_352sR;LtDPmPJXI{YtT=O8=76o9;*n%_m|xo!i>7$IrZ-{l z-x3`7M}qzHsPV@$v#>H-TpjDh2UE$9g6sysUREDy_R(a)>=eHw-WAyfIN z*qb!_hW>G)Tu8nSw9yn#3wFMiLcfc4pY0ek1}8(NqkBR@t4{~oC>ryc-h_ByH(Cg5 z>ao-}771+xE3um9lWAY1FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zm zZQj(aA_HeBY&OC^jj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5Kh zX*|AU4QE#~SgPzOXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&g3j|zDgC+}2Q_v%YfDax z!?umcN^n}KYQ|a$Lr+51Nf9dkkYFSjZZjkma$0KOj+;aQ&721~t7QUKx61J3(P4P1 zstI~7-wOACnWP4=8oGOwz%vNDqD8w&Q`qcNGGrbbf&0s9L0De{4{mRS?o0MU+nR_! zrvshUau0G^DeMhM_v{5BuLjb#Hh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG z;P%vktHcXwTj~+IE(~px)3*MY77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC z(zYC$g|@+I+kY;dg_dE>scBf&bP1Nc@Hz<3R)V`=AGkc;8CXqdi=B4l2k|g;2%#m& z*jfX^%b!A8#bI!j9-0Fi0bOXl(-c^AB9|nQaE`*)Hw+o&jS9@7&Gov#HbD~#d{twV zXd^Tr^mWLfFh$@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)y zI9C9*oUga6=hxw6QasLPnee@3^Rr*M{CdaL5=R41nLs(AHk_=Y+A9$2&H(B7!_pURs&8aNw7?`&Z&xY_Ye z)~D5Bog^td-^QbUtkTirdyK^mTHAOuptDflut!#^lnKqU md>ggs(5nOWAqO?umG&QVYK#ibz}*4>0000U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~cTNYR~@Y9KepMPrrB8cABapAFa=`H+UGhkXUZV1GnwR1*lPyZ;*K(i~2gp|@bzp8}og7e*#% zEnr|^CWdVV!-4*Y_7rFvlww2Ze+>j*!Z!pQ?2l->4q#nqRu9`ELo6RMS5=br47g_X zRw}P9a7RRYQ%2Vsd0Me{_(EggTnuN6j=-?uFS6j^u69elMypu?t>op*wBx<=Wx8?( ztpe^(fwM6jJX7M-l*k3kEpWOl_Vk3@(_w4oc}4YF4|Rt=2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX?F&rExl^W|G8Z8;`5z-k}OGNZ0#WVb$WCpQu-_YsiqKP?BB# vzVHS-CTUF4Ozn5G+mq_~Qqto~ahA+K`|lyv3(-e}00000NkvXXu0mjfd`9t{ diff --git a/packages/sensors/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/sensors/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png deleted file mode 100644 index d0ef06e7edb86cdfe0d15b4b0d98334a86163658..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1716 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 diff --git a/packages/sensors/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/sensors/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png deleted file mode 100644 index c8f9ed8f5cee1c98386d13b17e89f719e83555b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1895 zcmV-t2blPYP)FQtfgmafE#=YDCq`qUBt#QpG%*H6QHY765~R=q zZ6iudfM}q!Pz#~9JgOi8QJ|DSu?1-*(kSi1K4#~5?#|rh?sS)(-JQqX*}ciXJ56_H zdw=^s_srbAdqxlvGyrgGet#6T7_|j;95sL%MtM;q86vOxKM$f#puR)Bjv9Zvz9-di zXOTSsZkM83)E9PYBXC<$6(|>lNLVBb&&6y{NByFCp%6+^ALR@NCTse_wqvNmSWI-m z!$%KlHFH2omF!>#%1l3LTZg(s7eof$7*xB)ZQ0h?ejh?Ta9fDv59+u#MokW+1t8Zb zgHv%K(u9G^Lv`lh#f3<6!JVTL3(dCpxHbnbA;kKqQyd1~^Xe0VIaYBSWm6nsr;dFj z4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3QU4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKubPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`Q zykI#*+rQ=z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRe zt3L_uNyQ*cE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=ky zx=~RKa4{iTm{_>_vSCm?$Ej=i6@=m%@VvAITnigVg{&@!7CDgs908761meDK5azA} z4?=NOH|PdvabgJ&fW2{Mo$Q0CcD8Qc84%{JPYt5EiG{MdLIAeX%T=D7NIP4%Hw}p9 zg)==!2Lbp#j{u_}hMiao9=!VSyx0gHbeCS`;q&vzeq|fs`y&^X-lso(Ls@-706qmA z7u*T5PMo_w3{se1t2`zWeO^hOvTsohG_;>J0wVqVe+n)AbQCx)yh9;w+J6?NF5Lmo zecS@ieAKL8%bVd@+-KT{yI|S}O>pYckUFs;ry9Ow$CD@ztz5K-*D$^{i(_1llhSh^ zEkL$}tsQt5>QA^;QgjgIfBDmcOgi5YDyu?t6vSnbp=1+@6D& z5MJ}B8q;bRlVoxasyhcUF1+)o`&3r0colr}QJ3hcSdLu;9;td>kf@Tcn<@9sIx&=m z;AD;SCh95=&p;$r{Xz3iWCO^MX83AGJ(yH&eTXgv|0=34#-&WAmw{)U7OU9!Wz^!7 zZ%jZFi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i h0WYBP*#0Ks^FNSabJA*5${_#%002ovPDHLkV1oKhTl@e3 diff --git a/packages/sensors/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/sensors/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png deleted file mode 100644 index a6d6b8609df07bf62e5100a53a01510388bd2b22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ diff --git a/packages/sensors/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/sensors/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png deleted file mode 100644 index a6d6b8609df07bf62e5100a53a01510388bd2b22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ diff --git a/packages/sensors/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/sensors/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png deleted file mode 100644 index 75b2d164a5a98e212cca15ea7bf2ab5de5108680..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3831 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 diff --git a/packages/sensors/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/sensors/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png deleted file mode 100644 index c4df70d39da7941ef3f6dcb7f06a192d8dcb308d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1888 zcmV-m2cP(fP)x~L`~4d)Rspd&<9kFh{hn*KP1LP0~$;u(LfAu zp%fx&qLBcRHx$G|3q(bv@+b;o0*D|jwD-Q9uQR(l*ST}s+uPgQ-MeFwZ#GS?b332? z&Tk$&_miXn3IGq)AmQ)3sisq{raD4(k*bHvpCe-TdWq^NRTEVM)i9xbgQ&ccnUVx* zEY%vS%gDcSg=!tuIK8$Th2_((_h^+7;R|G{n06&O2#6%LK`a}n?h_fL18btz<@lFG za}xS}u?#DBMB> zw^b($1Z)`9G?eP95EKi&$eOy@K%h;ryrR3la%;>|o*>CgB(s>dDcNOXg}CK9SPmD? zmr-s{0wRmxUnbDrYfRvnZ@d z6johZ2sMX{YkGSKWd}m|@V7`Degt-43=2M?+jR%8{(H$&MLLmS;-|JxnX2pnz;el1jsvqQz}pGSF<`mqEXRQ5sC4#BbwnB_4` zc5bFE-Gb#JV3tox9fp-vVEN{(tOCpRse`S+@)?%pz+zVJXSooTrNCUg`R6`hxwb{) zC@{O6MKY8tfZ5@!yy=p5Y|#+myRL=^{tc(6YgAnkg3I(Cd!r5l;|;l-MQ8B`;*SCE z{u)uP^C$lOPM z5d~UhKhRRmvv{LIa^|oavk1$QiEApSrP@~Jjbg`<*dW4TO?4qG%a%sTPUFz(QtW5( zM)lA+5)0TvH~aBaOAs|}?u2FO;yc-CZ1gNM1dAxJ?%m?YsGR`}-xk2*dxC}r5j$d* zE!#Vtbo69h>V4V`BL%_&$} z+oJAo@jQ^Tk`;%xw-4G>hhb&)B?##U+(6Fi7nno`C<|#PVA%$Y{}N-?(Gc$1%tr4Pc}}hm~yY#fTOe!@v9s-ik$dX~|ygArPhByaXn8 zpI^FUjNWMsTFKTP3X7m?UK)3m zp6rI^_zxRYrx6_QmhoWoDR`fp4R7gu6;gdO)!KexaoO2D88F9x#TM1(9Bn7g;|?|o z)~$n&Lh#hCP6_LOPD>a)NmhW})LADx2kq=X7}7wYRj-0?dXr&bHaRWCfSqvzFa=sn z-8^gSyn-RmH=BZ{AJZ~!8n5621GbUJV7Qvs%JNv&$%Q17s_X%s-41vAPfIR>;x0Wlqr5?09S>x#%Qkt>?(&XjFRY}*L6BeQ3 z<6XEBh^S7>AbwGm@XP{RkeEKj6@_o%oV?hDuUpUJ+r#JZO?!IUc;r0R?>mi)*ZpQ) z#((dn=A#i_&EQn|hd)N$#A*fjBFuiHcYvo?@y1 z5|fV=a^a~d!c-%ZbMNqkMKiSzM{Yq=7_c&1H!mXk60Uv32dV;vMg&-kQ)Q{+PFtwc zj|-uQ;b^gts??J*9VxxOro}W~Q9j4Em|zSRv)(WSO9$F$s=Ydu%Q+5DOid~lwk&we zY%W(Z@ofdwPHncEZzZgmqS|!gTj3wQq9rxQy+^eNYKr1mj&?tm@wkO*9@UtnRMG>c aR{jt9+;fr}hV%pg00001^@s67{VYS000c7NklQEG_j zup^)eW&WUIApqy$=APz8jE@awGp)!bsTjDbrJO`$x^ZR^dr;>)LW>{ zs70vpsD38v)19rI=GNk1b(0?Js9~rjsQsu*K;@SD40RB-3^gKU-MYC7G!Bw{fZsqp zih4iIi;Hr_xZ033Iu{sQxLS=}yBXgLMn40d++>aQ0#%8D1EbGZp7+ z5=mK?t31BkVYbGOxE9`i748x`YgCMwL$qMsChbSGSE1`p{nSmadR zcQ#R)(?!~dmtD0+D2!K zR9%!Xp1oOJzm(vbLvT^$IKp@+W2=-}qTzTgVtQ!#Y7Gxz}stUIm<1;oBQ^Sh2X{F4ibaOOx;5ZGSNK z0maF^@(UtV$=p6DXLgRURwF95C=|U8?osGhgOED*b z7woJ_PWXBD>V-NjQAm{~T%sjyJ{5tn2f{G%?J!KRSrrGvQ1(^`YLA5B!~eycY(e5_ z*%aa{at13SxC(=7JT7$IQF~R3sy`Nn%EMv!$-8ZEAryB*yB1k&stni)=)8-ODo41g zkJu~roIgAih94tb=YsL%iH5@^b~kU9M-=aqgXIrbtxMpFy5mekFm#edF9z7RQ6V}R zBIhbXs~pMzt0VWy1Fi$^fh+1xxLDoK09&5&MJl(q#THjPm(0=z2H2Yfm^a&E)V+a5 zbi>08u;bJsDRUKR9(INSc7XyuWv(JsD+BB*0hS)FO&l&7MdViuur@-<-EHw>kHRGY zqoT}3fDv2-m{NhBG8X}+rgOEZ;amh*DqN?jEfQdqxdj08`Sr=C-KmT)qU1 z+9Cl)a1mgXxhQiHVB}l`m;-RpmKy?0*|yl?FXvJkFxuu!fKlcmz$kN(a}i*saM3nr z0!;a~_%Xqy24IxA2rz<+08=B-Q|2PT)O4;EaxP^6qixOv7-cRh?*T?zZU`{nIM-at zTKYWr9rJ=tppQ9I#Z#mLgINVB!pO-^FOcvFw6NhV0gztuO?g ztoA*C-52Q-Z-P#xB4HAY3KQVd%dz1S4PA3vHp0aa=zAO?FCt zC_GaTyVBg2F!bBr3U@Zy2iJgIAt>1sf$JWA9kh{;L+P*HfUBX1Zy{4MgNbDfBV_ly z!y#+753arsZUt@366jIC0klaC@ckuk!qu=pAyf7&QmiBUT^L1&tOHzsK)4n|pmrVT zs2($4=?s~VejTFHbFdDOwG;_58LkIj1Fh@{glkO#F1>a==ymJS$z;gdedT1zPx4Kj ztjS`y_C}%af-RtpehdQDt3a<=W5C4$)9W@QAse;WUry$WYmr51ml9lkeunUrE`-3e zmq1SgSOPNEE-Mf+AGJ$g0M;3@w!$Ej;hMh=v=I+Lpz^n%Pg^MgwyqOkNyu2c^of)C z1~ALor3}}+RiF*K4+4{(1%1j3pif1>sv0r^mTZ?5Jd-It!tfPfiG_p$AY*Vfak%FG z4z#;wLtw&E&?}w+eKG^=#jF7HQzr8rV0mY<1YAJ_uGz~$E13p?F^fPSzXSn$8UcI$ z8er9{5w5iv0qf8%70zV71T1IBB1N}R5Kp%NO0=5wJalZt8;xYp;b{1K) zHY>2wW-`Sl{=NpR%iu3(u6l&)rc%%cSA#aV7WCowfbFR4wcc{LQZv~o1u_`}EJA3>ki`?9CKYTA!rhO)if*zRdd}Kn zEPfYbhoVE~!FI_2YbC5qAj1kq;xP6%J8+?2PAs?`V3}nyFVD#sV3+uP`pi}{$l9U^ zSz}_M9f7RgnnRhaoIJgT8us!1aB&4!*vYF07Hp&}L zCRlop0oK4DL@ISz{2_BPlezc;xj2|I z23RlDNpi9LgTG_#(w%cMaS)%N`e>~1&a3<{Xy}>?WbF>OOLuO+j&hc^YohQ$4F&ze z+hwnro1puQjnKm;vFG~o>`kCeUIlkA-2tI?WBKCFLMBY=J{hpSsQ=PDtU$=duS_hq zHpymHt^uuV1q@uc4bFb{MdG*|VoW@15Osrqt2@8ll0qO=j*uOXn{M0UJX#SUztui9FN4)K3{9!y8PC-AHHvpVTU;x|-7P+taAtyglk#rjlH2 z5Gq8ik}BPaGiM{#Woyg;*&N9R2{J0V+WGB69cEtH7F?U~Kbi6ksi*`CFXsi931q7Y zGO82?whBhN%w1iDetv%~wM*Y;E^)@Vl?VDj-f*RX>{;o_=$fU!&KAXbuadYZ46Zbg z&6jMF=49$uL^73y;;N5jaHYv)BTyfh&`qVLYn?`o6BCA_z-0niZz=qPG!vonK3MW_ zo$V96zM!+kJRs{P-5-rQVse0VBH*n6A58)4uc&gfHMa{gIhV2fGf{st>E8sKyP-$8zp~wJX^A*@DI&-;8>gANXZj zU)R+Y)PB?=)a|Kj>8NXEu^S_h^7R`~Q&7*Kn!xyvzVv&^>?^iu;S~R2e-2fJx-oUb cX)(b1KSk$MOV07*qoM6N<$f&6$jw%VRuvdN2+38CZWny1cRtlsl+0_KtW)EU14Ei(F!UtWuj4IK+3{sK@>rh zs1Z;=(DD&U6+tlyL?UnHVN^&g6QhFi2#HS+*qz;(>63G(`|jRtW|nz$Pv7qTovP!^ zP_jES{mr@O-02w%!^a?^1ZP!_KmQiz0L~jZ=W@Qt`8wzOoclQsAS<5YdH;a(4bGLE zk8s}1If(PSIgVi!XE!5kA?~z*sobvNyohr;=Q_@h2@$6Flyej3J)D-6YfheRGl`HEcPk|~huT_2-U?PfL=4BPV)f1o!%rQ!NMt_MYw-5bUSwQ9Z&zC>u zOrl~UJglJNa%f50Ok}?WB{on`Ci`p^Y!xBA?m@rcJXLxtrE0FhRF3d*ir>yzO|BD$ z3V}HpFcCh6bTzY}Nt_(W%QYd3NG)jJ4<`F<1Od) zfQblTdC&h2lCz`>y?>|9o2CdvC8qZeIZt%jN;B7Hdn2l*k4M4MFEtq`q_#5?}c$b$pf_3y{Y!cRDafZBEj-*OD|gz#PBDeu3QoueOesLzB+O zxjf2wvf6Wwz>@AiOo2mO4=TkAV+g~%_n&R;)l#!cBxjuoD$aS-`IIJv7cdX%2{WT7 zOm%5rs(wqyPE^k5SIpUZ!&Lq4<~%{*>_Hu$2|~Xa;iX*tz8~G6O3uFOS?+)tWtdi| zV2b#;zRN!m@H&jd=!$7YY6_}|=!IU@=SjvGDFtL;aCtw06U;-v^0%k0FOyESt z1Wv$={b_H&8FiRV?MrzoHWd>%v6KTRU;-v^Miiz+@q`(BoT!+<37CKhoKb)|8!+RG z6BQFU^@fRW;s8!mOf2QViKQGk0TVER6EG1`#;Nm39Do^PoT!+<37AD!%oJe86(=et zZ~|sLzU>V-qYiU6V8$0GmU7_K8|Fd0B?+9Un1BhKAz#V~Fk^`mJtlCX#{^8^M8!me z8Yg;8-~>!e<-iG;h*0B1kBKm}hItVGY6WnjVpgnTTAC$rqQ^v)4KvOtpY|sIj@WYg zyw##ZZ5AC2IKNC;^hwg9BPk0wLStlmBr;E|$5GoAo$&Ui_;S9WY62n3)i49|T%C#i017z3J=$RF|KyZWnci*@lW4 z=AKhNN6+m`Q!V3Ye68|8y@%=am>YD0nG99M)NWc20%)gwO!96j7muR}Fr&54SxKP2 zP30S~lt=a*qDlbu3+Av57=9v&vr<6g0&`!8E2fq>I|EJGKs}t|{h7+KT@)LfIV-3K zK)r_fr2?}FFyn*MYoLC>oV-J~eavL2ho4a4^r{E-8m2hi>~hA?_vIG4a*KT;2eyl1 zh_hUvUJpNCFwBvRq5BI*srSle>c6%n`#VNsyC|MGa{(P&08p=C9+WUw9Hl<1o9T4M zdD=_C0F7#o8A_bRR?sFNmU0R6tW`ElnF8p53IdHo#S9(JoZCz}fHwJ6F<&?qrpVqE zte|m%89JQD+XwaPU#%#lVs-@-OL);|MdfINd6!XwP2h(eyafTUsoRkA%&@fe?9m@jw-v(yTTiV2(*fthQH9}SqmsRPVnwwbV$1E(_lkmo&S zF-truCU914_$jpqjr(>Ha4HkM4YMT>m~NosUu&UZ>zirfHo%N6PPs9^_o$WqPA0#5 z%tG>qFCL+b*0s?sZ;Sht0nE7Kl>OVXy=gjWxxK;OJ3yGd7-pZf7JYNcZo2*1SF`u6 zHJyRRxGw9mDlOiXqVMsNe#WX`fC`vrtjSQ%KmLcl(lC>ZOQzG^%iql2w-f_K@r?OE zwCICifM#L-HJyc7Gm>Ern?+Sk3&|Khmu4(~3qa$(m6Ub^U0E5RHq49za|XklN#?kP zl;EstdW?(_4D>kwjWy2f!LM)y?F94kyU3`W!6+AyId-89v}sXJpuic^NLL7GJItl~ zsiuB98AI-(#Mnm|=A-R6&2fwJ0JVSY#Q>&3$zFh|@;#%0qeF=j5Ajq@4i0tIIW z&}sk$&fGwoJpe&u-JeGLi^r?dO`m=y(QO{@h zQqAC7$rvz&5+mo3IqE?h=a~6m>%r5Quapvzq;{y~p zJpyXOBgD9VrW7@#p6l7O?o3feml(DtSL>D^R) zZUY%T2b0-vBAFN7VB;M88!~HuOXi4KcI6aRQ&h|XQ0A?m%j2=l1f0cGP}h(oVfJ`N zz#PpmFC*ieab)zJK<4?^k=g%OjPnkANzbAbmGZHoVRk*mTfm75s_cWVa`l*f$B@xu z5E*?&@seIo#*Y~1rBm!7sF9~~u6Wrj5oICUOuz}CS)jdNIznfzCA(stJ(7$c^e5wN z?lt>eYgbA!kvAR7zYSD&*r1$b|(@;9dcZ^67R0 zXAXJKa|5Sdmj!g578Nwt6d$sXuc&MWezA0Whd`94$h{{?1IwXP4)Tx4obDK%xoFZ_Z zjjHJ_P@R_e5blG@yEjnaJb`l;s%Lb2&=8$&Ct-fV`E^4CUs)=jTk!I}2d&n!f@)bm z@ z_4Dc86+3l2*p|~;o-Sb~oXb_RuLmoifDU^&Te$*FevycC0*nE3Xws8gsWp|Rj2>SM zns)qcYj?^2sd8?N!_w~4v+f-HCF|a$TNZDoNl$I1Uq87euoNgKb6&r26TNrfkUa@o zfdiFA@p{K&mH3b8i!lcoz)V{n8Q@g(vR4ns4r6w;K z>1~ecQR0-<^J|Ndg5fvVUM9g;lbu-){#ghGw(fg>L zh)T5Ljb%lWE;V9L!;Cqk>AV1(rULYF07ZBJbGb9qbSoLAd;in9{)95YqX$J43-dY7YU*k~vrM25 zxh5_IqO0LYZW%oxQ5HOzmk4x{atE*vipUk}sh88$b2tn?!ujEHn`tQLe&vo}nMb&{ zio`xzZ&GG6&ZyN3jnaQy#iVqXE9VT(3tWY$n-)uWDQ|tc{`?fq2F`oQ{;d3aWPg4Hp-(iE{ry>MIPWL> iW8 - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/sensors/example/ios/Runner/Base.lproj/Main.storyboard b/packages/sensors/example/ios/Runner/Base.lproj/Main.storyboard deleted file mode 100644 index f3c28516fb38..000000000000 --- a/packages/sensors/example/ios/Runner/Base.lproj/Main.storyboard +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/sensors/example/ios/Runner/Info.plist b/packages/sensors/example/ios/Runner/Info.plist deleted file mode 100644 index bc49e9088995..000000000000 --- a/packages/sensors/example/ios/Runner/Info.plist +++ /dev/null @@ -1,49 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - sensors_example - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UIRequiredDeviceCapabilities - - arm64 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - - diff --git a/packages/sensors/example/ios/Runner/main.m b/packages/sensors/example/ios/Runner/main.m deleted file mode 100644 index f97b9ef5c8a1..000000000000 --- a/packages/sensors/example/ios/Runner/main.m +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import -#import -#import "AppDelegate.h" - -int main(int argc, char* argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} diff --git a/packages/sensors/example/lib/main.dart b/packages/sensors/example/lib/main.dart deleted file mode 100644 index 0946a8e8421b..000000000000 --- a/packages/sensors/example/lib/main.dart +++ /dev/null @@ -1,144 +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. - -// ignore_for_file: public_member_api_docs - -import 'dart:async'; -import 'package:flutter/material.dart'; -import 'package:sensors/sensors.dart'; - -import 'snake.dart'; - -void main() { - runApp(MyApp()); -} - -class MyApp extends StatelessWidget { - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Sensors Demo', - theme: ThemeData( - primarySwatch: Colors.blue, - ), - home: MyHomePage(title: 'Flutter Demo Home Page'), - ); - } -} - -class MyHomePage extends StatefulWidget { - MyHomePage({Key? key, required this.title}) : super(key: key); - - final String title; - - @override - _MyHomePageState createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - static const int _snakeRows = 20; - static const int _snakeColumns = 20; - static const double _snakeCellSize = 10.0; - - List? _accelerometerValues; - List? _userAccelerometerValues; - List? _gyroscopeValues; - List> _streamSubscriptions = - >[]; - - @override - Widget build(BuildContext context) { - final List? accelerometer = - _accelerometerValues?.map((double v) => v.toStringAsFixed(1)).toList(); - final List? gyroscope = - _gyroscopeValues?.map((double v) => v.toStringAsFixed(1)).toList(); - final List? userAccelerometer = _userAccelerometerValues - ?.map((double v) => v.toStringAsFixed(1)) - .toList(); - - return Scaffold( - appBar: AppBar( - title: const Text('Sensor Example'), - ), - body: Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Center( - child: DecoratedBox( - decoration: BoxDecoration( - border: Border.all(width: 1.0, color: Colors.black38), - ), - child: SizedBox( - height: _snakeRows * _snakeCellSize, - width: _snakeColumns * _snakeCellSize, - child: Snake( - rows: _snakeRows, - columns: _snakeColumns, - cellSize: _snakeCellSize, - ), - ), - ), - ), - Padding( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('Accelerometer: $accelerometer'), - ], - ), - padding: const EdgeInsets.all(16.0), - ), - Padding( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('UserAccelerometer: $userAccelerometer'), - ], - ), - padding: const EdgeInsets.all(16.0), - ), - Padding( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('Gyroscope: $gyroscope'), - ], - ), - padding: const EdgeInsets.all(16.0), - ), - ], - ), - ); - } - - @override - void dispose() { - super.dispose(); - for (StreamSubscription subscription in _streamSubscriptions) { - subscription.cancel(); - } - } - - @override - void initState() { - super.initState(); - _streamSubscriptions - .add(accelerometerEvents.listen((AccelerometerEvent event) { - setState(() { - _accelerometerValues = [event.x, event.y, event.z]; - }); - })); - _streamSubscriptions.add(gyroscopeEvents.listen((GyroscopeEvent event) { - setState(() { - _gyroscopeValues = [event.x, event.y, event.z]; - }); - })); - _streamSubscriptions - .add(userAccelerometerEvents.listen((UserAccelerometerEvent event) { - setState(() { - _userAccelerometerValues = [event.x, event.y, event.z]; - }); - })); - } -} diff --git a/packages/sensors/example/lib/snake.dart b/packages/sensors/example/lib/snake.dart deleted file mode 100644 index 47177681020f..000000000000 --- a/packages/sensors/example/lib/snake.dart +++ /dev/null @@ -1,129 +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. - -// ignore_for_file: public_member_api_docs - -import 'dart:async'; -import 'dart:math' as math; - -import 'package:flutter/material.dart'; -import 'package:sensors/sensors.dart'; - -class Snake extends StatefulWidget { - Snake({this.rows = 20, this.columns = 20, this.cellSize = 10.0}) { - assert(10 <= rows); - assert(10 <= columns); - assert(5.0 <= cellSize); - } - - final int rows; - final int columns; - final double cellSize; - - @override - State createState() => SnakeState(rows, columns, cellSize); -} - -class SnakeBoardPainter extends CustomPainter { - SnakeBoardPainter(this.state, this.cellSize); - - GameState state; - double cellSize; - - @override - void paint(Canvas canvas, Size size) { - final Paint blackLine = Paint()..color = Colors.black; - final Paint blackFilled = Paint() - ..color = Colors.black - ..style = PaintingStyle.fill; - canvas.drawRect( - Rect.fromPoints(Offset.zero, size.bottomLeft(Offset.zero)), - blackLine, - ); - for (math.Point p in state.body) { - final Offset a = Offset(cellSize * p.x, cellSize * p.y); - final Offset b = Offset(cellSize * (p.x + 1), cellSize * (p.y + 1)); - - canvas.drawRect(Rect.fromPoints(a, b), blackFilled); - } - } - - @override - bool shouldRepaint(CustomPainter oldDelegate) { - return true; - } -} - -class SnakeState extends State { - SnakeState(int rows, int columns, this.cellSize) - : state = GameState(rows, columns); - - double cellSize; - GameState state; - AccelerometerEvent? acceleration; - late StreamSubscription _streamSubscription; - late Timer _timer; - - @override - Widget build(BuildContext context) { - return CustomPaint(painter: SnakeBoardPainter(state, cellSize)); - } - - @override - void dispose() { - super.dispose(); - _streamSubscription.cancel(); - _timer.cancel(); - } - - @override - void initState() { - super.initState(); - _streamSubscription = - accelerometerEvents.listen((AccelerometerEvent event) { - setState(() { - acceleration = event; - }); - }); - - _timer = Timer.periodic(const Duration(milliseconds: 200), (_) { - setState(() { - _step(); - }); - }); - } - - void _step() { - final AccelerometerEvent? currentAcceleration = acceleration; - final math.Point? newDirection = currentAcceleration == null - ? null - : currentAcceleration.x.abs() < 1.0 && currentAcceleration.y.abs() < 1.0 - ? null - : (currentAcceleration.x.abs() < currentAcceleration.y.abs()) - ? math.Point(0, currentAcceleration.y.sign.toInt()) - : math.Point(-currentAcceleration.x.sign.toInt(), 0); - state.step(newDirection); - } -} - -class GameState { - GameState(this.rows, this.columns) - : snakeLength = math.min(rows, columns) - 5; - - int rows; - int columns; - int snakeLength; - - List> body = >[const math.Point(0, 0)]; - math.Point direction = const math.Point(1, 0); - - void step(math.Point? newDirection) { - math.Point next = body.last + direction; - next = math.Point(next.x % columns, next.y % rows); - - body.add(next); - if (body.length > snakeLength) body.removeAt(0); - direction = newDirection ?? direction; - } -} diff --git a/packages/sensors/example/pubspec.yaml b/packages/sensors/example/pubspec.yaml deleted file mode 100644 index fee7bd61f736..000000000000 --- a/packages/sensors/example/pubspec.yaml +++ /dev/null @@ -1,28 +0,0 @@ -name: sensors_example -description: Demonstrates how to use the sensors plugin. -publish_to: none - -environment: - sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" - -dependencies: - flutter: - sdk: flutter - sensors: - # When depending on this package from a real application you should use: - # sensors: ^x.y.z - # See https://dart.dev/tools/pub/dependencies#version-constraints - # The example app is bundled with the plugin so we use a path dependency on - # the parent directory to use the current plugin's version. - path: ../ - -dev_dependencies: - flutter_driver: - sdk: flutter - integration_test: - sdk: flutter - pedantic: ^1.10.0 - -flutter: - uses-material-design: true diff --git a/packages/sensors/example/sensor_example.iml b/packages/sensors/example/sensor_example.iml deleted file mode 100644 index 9d5dae19540c..000000000000 --- a/packages/sensors/example/sensor_example.iml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/sensors/example/sensor_example_android.iml b/packages/sensors/example/sensor_example_android.iml deleted file mode 100644 index 462b903e05b6..000000000000 --- a/packages/sensors/example/sensor_example_android.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/packages/sensors/example/test_driver/integration_test.dart b/packages/sensors/example/test_driver/integration_test.dart deleted file mode 100644 index 6a0e6fa82dbe..000000000000 --- a/packages/sensors/example/test_driver/integration_test.dart +++ /dev/null @@ -1,9 +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. - -// @dart=2.9 - -import 'package:integration_test/integration_test_driver.dart'; - -Future main() => integrationDriver(); diff --git a/packages/sensors/ios/Assets/.gitkeep b/packages/sensors/ios/Assets/.gitkeep deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/sensors/ios/Classes/FLTSensorsPlugin.h b/packages/sensors/ios/Classes/FLTSensorsPlugin.h deleted file mode 100644 index 8c3176b42a44..000000000000 --- a/packages/sensors/ios/Classes/FLTSensorsPlugin.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -@interface FLTSensorsPlugin : NSObject -@end - -@interface FLTUserAccelStreamHandler : NSObject -@end - -@interface FLTAccelerometerStreamHandler : NSObject -@end - -@interface FLTGyroscopeStreamHandler : NSObject -@end diff --git a/packages/sensors/ios/Classes/FLTSensorsPlugin.m b/packages/sensors/ios/Classes/FLTSensorsPlugin.m deleted file mode 100644 index 3d0ce66a2b25..000000000000 --- a/packages/sensors/ios/Classes/FLTSensorsPlugin.m +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "FLTSensorsPlugin.h" -#import - -@implementation FLTSensorsPlugin - -+ (void)registerWithRegistrar:(NSObject*)registrar { - FLTAccelerometerStreamHandler* accelerometerStreamHandler = - [[FLTAccelerometerStreamHandler alloc] init]; - FlutterEventChannel* accelerometerChannel = - [FlutterEventChannel eventChannelWithName:@"plugins.flutter.io/sensors/accelerometer" - binaryMessenger:[registrar messenger]]; - [accelerometerChannel setStreamHandler:accelerometerStreamHandler]; - - FLTUserAccelStreamHandler* userAccelerometerStreamHandler = - [[FLTUserAccelStreamHandler alloc] init]; - FlutterEventChannel* userAccelerometerChannel = - [FlutterEventChannel eventChannelWithName:@"plugins.flutter.io/sensors/user_accel" - binaryMessenger:[registrar messenger]]; - [userAccelerometerChannel setStreamHandler:userAccelerometerStreamHandler]; - - FLTGyroscopeStreamHandler* gyroscopeStreamHandler = [[FLTGyroscopeStreamHandler alloc] init]; - FlutterEventChannel* gyroscopeChannel = - [FlutterEventChannel eventChannelWithName:@"plugins.flutter.io/sensors/gyroscope" - binaryMessenger:[registrar messenger]]; - [gyroscopeChannel setStreamHandler:gyroscopeStreamHandler]; -} - -@end - -const double GRAVITY = 9.8; -CMMotionManager* _motionManager; - -void _initMotionManager(void) { - if (!_motionManager) { - _motionManager = [[CMMotionManager alloc] init]; - } -} - -static void sendTriplet(Float64 x, Float64 y, Float64 z, FlutterEventSink sink) { - NSMutableData* event = [NSMutableData dataWithCapacity:3 * sizeof(Float64)]; - [event appendBytes:&x length:sizeof(Float64)]; - [event appendBytes:&y length:sizeof(Float64)]; - [event appendBytes:&z length:sizeof(Float64)]; - sink([FlutterStandardTypedData typedDataWithFloat64:event]); -} - -@implementation FLTAccelerometerStreamHandler - -- (FlutterError*)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)eventSink { - _initMotionManager(); - [_motionManager - startAccelerometerUpdatesToQueue:[[NSOperationQueue alloc] init] - withHandler:^(CMAccelerometerData* accelerometerData, NSError* error) { - CMAcceleration acceleration = accelerometerData.acceleration; - // Multiply by gravity, and adjust sign values to - // align with Android. - sendTriplet(-acceleration.x * GRAVITY, -acceleration.y * GRAVITY, - -acceleration.z * GRAVITY, eventSink); - }]; - return nil; -} - -- (FlutterError*)onCancelWithArguments:(id)arguments { - [_motionManager stopAccelerometerUpdates]; - return nil; -} - -@end - -@implementation FLTUserAccelStreamHandler - -- (FlutterError*)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)eventSink { - _initMotionManager(); - [_motionManager - startDeviceMotionUpdatesToQueue:[[NSOperationQueue alloc] init] - withHandler:^(CMDeviceMotion* data, NSError* error) { - CMAcceleration acceleration = data.userAcceleration; - // Multiply by gravity, and adjust sign values to align with Android. - sendTriplet(-acceleration.x * GRAVITY, -acceleration.y * GRAVITY, - -acceleration.z * GRAVITY, eventSink); - }]; - return nil; -} - -- (FlutterError*)onCancelWithArguments:(id)arguments { - [_motionManager stopDeviceMotionUpdates]; - return nil; -} - -@end - -@implementation FLTGyroscopeStreamHandler - -- (FlutterError*)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)eventSink { - _initMotionManager(); - [_motionManager - startGyroUpdatesToQueue:[[NSOperationQueue alloc] init] - withHandler:^(CMGyroData* gyroData, NSError* error) { - CMRotationRate rotationRate = gyroData.rotationRate; - sendTriplet(rotationRate.x, rotationRate.y, rotationRate.z, eventSink); - }]; - return nil; -} - -- (FlutterError*)onCancelWithArguments:(id)arguments { - [_motionManager stopGyroUpdates]; - return nil; -} - -@end diff --git a/packages/sensors/ios/sensors.podspec b/packages/sensors/ios/sensors.podspec deleted file mode 100644 index 0f0a4be29b0d..000000000000 --- a/packages/sensors/ios/sensors.podspec +++ /dev/null @@ -1,23 +0,0 @@ -# -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html -# -Pod::Spec.new do |s| - s.name = 'sensors' - s.version = '0.0.1' - s.summary = 'Flutter Sensors' - s.description = <<-DESC -A Flutter plugin to access the accelerometer and gyroscope sensors. - DESC - s.homepage = 'https://github.com/flutter/plugins' - s.license = { :type => 'BSD', :file => '../LICENSE' } - s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/sensors' } - s.documentation_url = 'https://pub.dev/packages/sensors' - s.source_files = 'Classes/**/*' - s.public_header_files = 'Classes/**/*.h' - s.dependency 'Flutter' - - s.platform = :ios, '8.0' - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' } -end - diff --git a/packages/sensors/lib/sensors.dart b/packages/sensors/lib/sensors.dart deleted file mode 100644 index 8db29e017ad0..000000000000 --- a/packages/sensors/lib/sensors.dart +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; -import 'package:flutter/services.dart'; - -const EventChannel _accelerometerEventChannel = - EventChannel('plugins.flutter.io/sensors/accelerometer'); - -const EventChannel _userAccelerometerEventChannel = - EventChannel('plugins.flutter.io/sensors/user_accel'); - -const EventChannel _gyroscopeEventChannel = - EventChannel('plugins.flutter.io/sensors/gyroscope'); - -/// Discrete reading from an accelerometer. Accelerometers measure the velocity -/// of the device. Note that these readings include the effects of gravity. Put -/// simply, you can use accelerometer readings to tell if the device is moving in -/// a particular direction. -class AccelerometerEvent { - /// Contructs an instance with the given [x], [y], and [z] values. - AccelerometerEvent(this.x, this.y, this.z); - - /// Acceleration force along the x axis (including gravity) measured in m/s^2. - /// - /// When the device is held upright facing the user, positive values mean the - /// device is moving to the right and negative mean it is moving to the left. - final double x; - - /// Acceleration force along the y axis (including gravity) measured in m/s^2. - /// - /// When the device is held upright facing the user, positive values mean the - /// device is moving towards the sky and negative mean it is moving towards - /// the ground. - final double y; - - /// Acceleration force along the z axis (including gravity) measured in m/s^2. - /// - /// This uses a right-handed coordinate system. So when the device is held - /// upright and facing the user, positive values mean the device is moving - /// towards the user and negative mean it is moving away from them. - final double z; - - @override - String toString() => '[AccelerometerEvent (x: $x, y: $y, z: $z)]'; -} - -/// Discrete reading from a gyroscope. Gyroscopes measure the rate or rotation of -/// the device in 3D space. -class GyroscopeEvent { - /// Contructs an instance with the given [x], [y], and [z] values. - GyroscopeEvent(this.x, this.y, this.z); - - /// Rate of rotation around the x axis measured in rad/s. - /// - /// When the device is held upright, this can also be thought of as describing - /// "pitch". The top of the device will tilt towards or away from the - /// user as this value changes. - final double x; - - /// Rate of rotation around the y axis measured in rad/s. - /// - /// When the device is held upright, this can also be thought of as describing - /// "yaw". The lengthwise edge of the device will rotate towards or away from - /// the user as this value changes. - final double y; - - /// Rate of rotation around the z axis measured in rad/s. - /// - /// When the device is held upright, this can also be thought of as describing - /// "roll". When this changes the face of the device should remain facing - /// forward, but the orientation will change from portrait to landscape and so - /// on. - final double z; - - @override - String toString() => '[GyroscopeEvent (x: $x, y: $y, z: $z)]'; -} - -/// Like [AccelerometerEvent], this is a discrete reading from an accelerometer -/// and measures the velocity of the device. However, unlike -/// [AccelerometerEvent], this event does not include the effects of gravity. -class UserAccelerometerEvent { - /// Contructs an instance with the given [x], [y], and [z] values. - UserAccelerometerEvent(this.x, this.y, this.z); - - /// Acceleration force along the x axis (excluding gravity) measured in m/s^2. - /// - /// When the device is held upright facing the user, positive values mean the - /// device is moving to the right and negative mean it is moving to the left. - final double x; - - /// Acceleration force along the y axis (excluding gravity) measured in m/s^2. - /// - /// When the device is held upright facing the user, positive values mean the - /// device is moving towards the sky and negative mean it is moving towards - /// the ground. - final double y; - - /// Acceleration force along the z axis (excluding gravity) measured in m/s^2. - /// - /// This uses a right-handed coordinate system. So when the device is held - /// upright and facing the user, positive values mean the device is moving - /// towards the user and negative mean it is moving away from them. - final double z; - - @override - String toString() => '[UserAccelerometerEvent (x: $x, y: $y, z: $z)]'; -} - -AccelerometerEvent _listToAccelerometerEvent(List list) { - return AccelerometerEvent(list[0], list[1], list[2]); -} - -UserAccelerometerEvent _listToUserAccelerometerEvent(List list) { - return UserAccelerometerEvent(list[0], list[1], list[2]); -} - -GyroscopeEvent _listToGyroscopeEvent(List list) { - return GyroscopeEvent(list[0], list[1], list[2]); -} - -Stream? _accelerometerEvents; -Stream? _gyroscopeEvents; -Stream? _userAccelerometerEvents; - -/// A broadcast stream of events from the device accelerometer. -Stream get accelerometerEvents { - Stream? accelerometerEvents = _accelerometerEvents; - if (accelerometerEvents == null) { - accelerometerEvents = - _accelerometerEventChannel.receiveBroadcastStream().map( - (dynamic event) => - _listToAccelerometerEvent(event.cast()), - ); - _accelerometerEvents = accelerometerEvents; - } - - return accelerometerEvents; -} - -/// A broadcast stream of events from the device gyroscope. -Stream get gyroscopeEvents { - Stream? gyroscopeEvents = _gyroscopeEvents; - if (gyroscopeEvents == null) { - gyroscopeEvents = _gyroscopeEventChannel.receiveBroadcastStream().map( - (dynamic event) => _listToGyroscopeEvent(event.cast()), - ); - _gyroscopeEvents = gyroscopeEvents; - } - - return gyroscopeEvents; -} - -/// Events from the device accelerometer with gravity removed. -Stream get userAccelerometerEvents { - Stream? userAccelerometerEvents = - _userAccelerometerEvents; - if (userAccelerometerEvents == null) { - userAccelerometerEvents = - _userAccelerometerEventChannel.receiveBroadcastStream().map( - (dynamic event) => - _listToUserAccelerometerEvent(event.cast()), - ); - _userAccelerometerEvents = userAccelerometerEvents; - } - - return userAccelerometerEvents; -} diff --git a/packages/sensors/pubspec.yaml b/packages/sensors/pubspec.yaml deleted file mode 100644 index b26819b64df0..000000000000 --- a/packages/sensors/pubspec.yaml +++ /dev/null @@ -1,32 +0,0 @@ -name: sensors -description: Flutter plugin for accessing the Android and iOS accelerometer and - gyroscope sensors. -repository: https://github.com/flutter/plugins/tree/master/packages/sensors -issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+sensors%22 -version: 2.0.3 - -environment: - sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.12.13+hotfix.5" - -flutter: - plugin: - platforms: - android: - package: io.flutter.plugins.sensors - pluginClass: SensorsPlugin - ios: - pluginClass: FLTSensorsPlugin - -dependencies: - flutter: - sdk: flutter - -dev_dependencies: - test: ^1.16.0 - flutter_test: - sdk: flutter - integration_test: - sdk: flutter - mockito: ^5.0.0 - pedantic: ^1.10.0 diff --git a/packages/sensors/test/sensors_test.dart b/packages/sensors/test/sensors_test.dart deleted file mode 100644 index bce3afe6205b..000000000000 --- a/packages/sensors/test/sensors_test.dart +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:typed_data'; - -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:sensors/sensors.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - test('$accelerometerEvents are streamed', () async { - const String channelName = 'plugins.flutter.io/sensors/accelerometer'; - const List sensorData = [1.0, 2.0, 3.0]; - _initializeFakeSensorChannel(channelName, sensorData); - - final AccelerometerEvent event = await accelerometerEvents.first; - - expect(event.x, sensorData[0]); - expect(event.y, sensorData[1]); - expect(event.z, sensorData[2]); - }); - - test('$gyroscopeEvents are streamed', () async { - const String channelName = 'plugins.flutter.io/sensors/gyroscope'; - const List sensorData = [3.0, 4.0, 5.0]; - _initializeFakeSensorChannel(channelName, sensorData); - - final GyroscopeEvent event = await gyroscopeEvents.first; - - expect(event.x, sensorData[0]); - expect(event.y, sensorData[1]); - expect(event.z, sensorData[2]); - }); - - test('$userAccelerometerEvents are streamed', () async { - const String channelName = 'plugins.flutter.io/sensors/user_accel'; - const List sensorData = [6.0, 7.0, 8.0]; - _initializeFakeSensorChannel(channelName, sensorData); - - final UserAccelerometerEvent event = await userAccelerometerEvents.first; - - expect(event.x, sensorData[0]); - expect(event.y, sensorData[1]); - expect(event.z, sensorData[2]); - }); -} - -void _initializeFakeSensorChannel(String channelName, List sensorData) { - const StandardMethodCodec standardMethod = StandardMethodCodec(); - - void _emitEvent(ByteData? event) { - _ambiguate(ServicesBinding.instance)! - .defaultBinaryMessenger - .handlePlatformMessage( - channelName, - event, - (ByteData? reply) {}, - ); - } - - _ambiguate(ServicesBinding.instance)! - .defaultBinaryMessenger - .setMockMessageHandler(channelName, (ByteData? message) async { - final MethodCall methodCall = standardMethod.decodeMethodCall(message); - if (methodCall.method == 'listen') { - _emitEvent(standardMethod.encodeSuccessEnvelope(sensorData)); - _emitEvent(null); - return standardMethod.encodeSuccessEnvelope(null); - } else if (methodCall.method == 'cancel') { - return standardMethod.encodeSuccessEnvelope(null); - } else { - fail('Expected listen or cancel'); - } - }); -} - -/// This allows a value of type T or T? to be treated as a value of type T?. -/// -/// We use this so that APIs that have become non-nullable can still be used -/// with `!` and `?` on the stable branch. -// TODO(ianh): Remove this once we roll stable in late 2021. -T? _ambiguate(T? value) => value; diff --git a/packages/share/AUTHORS b/packages/share/AUTHORS deleted file mode 100644 index 493a0b4ef9c2..000000000000 --- a/packages/share/AUTHORS +++ /dev/null @@ -1,66 +0,0 @@ -# Below is a list of people and organizations that have contributed -# to the Flutter project. Names should be added to the list like so: -# -# Name/Organization - -Google Inc. -The Chromium Authors -German Saprykin -Benjamin Sauer -larsenthomasj@gmail.com -Ali Bitek -Pol Batlló -Anatoly Pulyaevskiy -Hayden Flinner -Stefano Rodriguez -Salvatore Giordano -Brian Armstrong -Paul DeMarco -Fabricio Nogueira -Simon Lightfoot -Ashton Thomas -Thomas Danner -Diego Velásquez -Hajime Nakamura -Tuyển Vũ Xuân -Miguel Ruivo -Sarthak Verma -Mike Diarmid -Invertase -Elliot Hesp -Vince Varga -Aawaz Gyawali -EUI Limited -Katarina Sheremet -Thomas Stockx -Sarbagya Dhaubanjar -Ozkan Eksi -Rishab Nayak -ko2ic -Jonathan Younger -Jose Sanchez -Debkanchan Samadder -Audrius Karosevicius -Lukasz Piliszczuk -SoundReply Solutions GmbH -Rafal Wachol -Pau Picas -Christian Weder -Alexandru Tuca -Christian Weder -Rhodes Davis Jr. -Luigi Agosti -Quentin Le Guennec -Koushik Ravikumar -Nissim Dsilva -Giancarlo Rocha -Ryo Miyake -Théo Champion -Kazuki Yamaguchi -Eitan Schwartz -Chris Rutkowski -Juan Alvarez -Aleksandr Yurkovskiy -Anton Borries -Alex Li -Rahul Raj <64.rahulraj@gmail.com> diff --git a/packages/share/CHANGELOG.md b/packages/share/CHANGELOG.md deleted file mode 100644 index effc12c023c0..000000000000 --- a/packages/share/CHANGELOG.md +++ /dev/null @@ -1,225 +0,0 @@ -## NEXT - -* Remove references to the Android V1 embedding. -* Updated Android lint settings. -* Updates Android compileSdkVersion to 31. - -## 2.0.4 - -* Update README to point to Plus Plugins version. - -## 2.0.3 - -* Do not tear down method channel onDetachedFromActivity. - -## 2.0.2 - -* Migrate maven repository from jcenter to mavenCentral. - -## 2.0.1 - -* Migrate unit tests to sound null safety. - -## 2.0.0 - -* Migrate to null safety. -* Update the example app: remove the deprecated `RaisedButton` and `FlatButton` widgets. -* Fix outdated links across a number of markdown files ([#3276](https://github.com/flutter/plugins/pull/3276)) -* Update README with the new documentation urls. - -## 0.6.5+5 - -* Update Flutter SDK constraint. - -## 0.6.5+4 - -* Fix iPad share window not showing when `origin` is null. - -## 0.6.5+3 - -* Replace deprecated `Environment.getExternalStorageDirectory()` call on Android. -* Upgrade to Android Gradle plugin 3.5.0 & target API level 29. - -## 0.6.5+2 - -* Keep handling deprecated Android v1 classes for backward compatibility. - -## 0.6.5+1 - -* Avoiding uses unchecked or unsafe Object Type Casting - -## 0.6.5 - -* Added support for sharing files - -## 0.6.4+5 - -* Update package:e2e -> package:integration_test - -## 0.6.4+4 - -* Update package:e2e reference to use the local version in the flutter/plugins - repository. - -## 0.6.4+3 - -* Post-v2 Android embedding cleanup. - -## 0.6.4+2 - -* Update lower bound of dart dependency to 2.1.0. - -## 0.6.4+1 - -* Declare API stability and compatibility with `1.0.0` (more details at: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0). - -## 0.6.4 - -* Remove Android dependencies fallback. -* Require Flutter SDK 1.12.13+hotfix.5 or greater. -* Fix CocoaPods podspec lint warnings. - -## 0.6.3+8 - -* Replace deprecated `getFlutterEngine` call on Android. - -## 0.6.3+7 - -* Updated gradle version of example. - -## 0.6.3+6 - -* Make the pedantic dev_dependency explicit. - -## 0.6.3+5 - -* Remove the deprecated `author:` field from pubspec.yaml -* Migrate the plugin to the pubspec platforms manifest. -* Require Flutter SDK 1.10.0 or greater. - -## 0.6.3+4 - -* Fix pedantic lints. This shouldn't affect existing functionality. - -## 0.6.3+3 - -* README update. - -## 0.6.3+2 - -* Remove AndroidX warnings. - -## 0.6.3+1 - -* Include lifecycle dependency as a compileOnly one on Android to resolve - potential version conflicts with other transitive libraries. - -## 0.6.3 - -* Support the v2 Android embedder. -* Update to AndroidX. -* Migrate to using the new e2e test binding. -* Add a e2e test. - -## 0.6.2+4 - -* Define clang module for iOS. - -## 0.6.2+3 - -* Fix iOS crash when setting subject to null. - -## 0.6.2+2 - -* Update and migrate iOS example project. - -## 0.6.2+1 - -* Specify explicit type for `invokeMethod`. -* Use `const` for `Rect`. -* Updated minimum Flutter SDK to 1.6.0. - -## 0.6.2 - -* Add optional subject to fill email subject in case user selects email app. - -## 0.6.1+2 - -* Update Dart code to conform to current Dart formatter. - -## 0.6.1+1 - -* Fix analyzer warnings about `const Rect` in tests. - -## 0.6.1 - -* Updated Android compileSdkVersion to 28 to match other plugins. - -## 0.6.0+1 - -* Log a more detailed warning at build time about the previous AndroidX - migration. - -## 0.6.0 - -* **Breaking change**. Migrate from the deprecated original Android Support - Library to AndroidX. This shouldn't result in any functional changes, but it - requires any Android apps using this plugin to [also - migrate](https://developer.android.com/jetpack/androidx/migrate) if they're - using the original support library. - -## 0.5.3 - -* Added missing test package dependency. -* Bumped version of mockito package dependency to pick up Dart 2 support. - -## 0.5.2 - -* Fixes iOS sharing - -## 0.5.1 - -* Updated Gradle tooling to match Android Studio 3.1.2. - -## 0.5.0 - -* **Breaking change**. Namespaced the `share` method inside a `Share` class. -* Fixed crash when sharing on iPad. -* Added functionality to specify share sheet origin on iOS. - -## 0.4.0 - -* **Breaking change**. Set SDK constraints to match the Flutter beta release. - -## 0.3.2 - -* Fixed Dart 2 type error. - -## 0.3.1 - -* Simplified and upgraded Android project template to Android SDK 27. -* Updated package description. - -## 0.3.0 - -* **Breaking change**. Upgraded to Gradle 4.1 and Android Studio Gradle plugin - 3.0.1. Older Flutter projects need to upgrade their Gradle setup as well in - order to use this version of the plugin. Instructions can be found - [here](https://github.com/flutter/flutter/wiki/Updating-Flutter-projects-to-Gradle-4.1-and-Android-Studio-Gradle-plugin-3.0.1). - -## 0.2.2 - -* Added FLT prefix to iOS types - -## 0.2.1 - -* Updated README -* Bumped buildToolsVersion to 25.0.3 - -## 0.2.0 - -* Upgrade to new plugin registration. (https://groups.google.com/forum/#!topic/flutter-dev/zba1Ynf2OKM) - -## 0.1.0 - -* Initial Open Source release. diff --git a/packages/share/LICENSE b/packages/share/LICENSE deleted file mode 100644 index c6823b81eb84..000000000000 --- a/packages/share/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright 2013 The Flutter Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/share/README.md b/packages/share/README.md deleted file mode 100644 index 7fda1198f503..000000000000 --- a/packages/share/README.md +++ /dev/null @@ -1,55 +0,0 @@ -# Share plugin - ---- - -## Deprecation Notice - -This plugin has been replaced by the [Flutter Community Plus -Plugins](https://plus.fluttercommunity.dev/) version, -[`share_plus`](https://pub.dev/packages/share_plus). -No further updates are planned to this plugin, and we encourage all users to -migrate to the Plus version. - -Critical fixes (e.g., for any security incidents) will be provided through the -end of 2021, at which point this package will be marked as discontinued. - ---- - -[![pub package](https://img.shields.io/pub/v/share.svg)](https://pub.dev/packages/share) - -A Flutter plugin to share content from your Flutter app via the platform's -share dialog. - -Wraps the ACTION_SEND Intent on Android and UIActivityViewController -on iOS. - -## Usage - -To use this plugin, add `share` as a [dependency in your pubspec.yaml file](https://flutter.dev/docs/development/packages-and-plugins/using-packages/). - -## Example - -Import the library. - -``` dart -import 'package:share/share.dart'; -``` - -Then invoke the static `share` method anywhere in your Dart code. - -``` dart -Share.share('check out my website https://example.com'); -``` - -The `share` method also takes an optional `subject` that will be used when -sharing to email. - -``` dart -Share.share('check out my website https://example.com', subject: 'Look what I made!'); -``` - -To share one or multiple files invoke the static `shareFiles` method anywhere in your Dart code. Optionally you can also pass in `text` and `subject`. -``` dart -Share.shareFiles(['${directory.path}/image.jpg'], text: 'Great picture'); -Share.shareFiles(['${directory.path}/image1.jpg', '${directory.path}/image2.jpg']); -``` diff --git a/packages/share/analysis_options.yaml b/packages/share/analysis_options.yaml deleted file mode 100644 index cda4f6e153e6..000000000000 --- a/packages/share/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../analysis_options_legacy.yaml diff --git a/packages/share/android/build.gradle b/packages/share/android/build.gradle deleted file mode 100644 index c4fcafc70d26..000000000000 --- a/packages/share/android/build.gradle +++ /dev/null @@ -1,53 +0,0 @@ -group 'io.flutter.plugins.share' -version '1.0-SNAPSHOT' - -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' - } -} - -rootProject.allprojects { - repositories { - google() - mavenCentral() - } -} - -apply plugin: 'com.android.library' - -android { - compileSdkVersion 31 - - defaultConfig { - minSdkVersion 16 - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - lintOptions { - disable 'InvalidPackage' - disable 'GradleDependency' - } - - dependencies { - implementation 'androidx.core:core:1.3.1' - implementation 'androidx.annotation:annotation:1.1.0' - } - - - testOptions { - unitTests.includeAndroidResources = true - unitTests.returnDefaultValues = true - unitTests.all { - testLogging { - events "passed", "skipped", "failed", "standardOut", "standardError" - outputs.upToDateWhen {false} - showStandardStreams = true - } - } - } -} diff --git a/packages/share/android/settings.gradle b/packages/share/android/settings.gradle deleted file mode 100644 index 64350ae697e7..000000000000 --- a/packages/share/android/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'share' diff --git a/packages/share/android/src/main/AndroidManifest.xml b/packages/share/android/src/main/AndroidManifest.xml deleted file mode 100644 index c141a5c67928..000000000000 --- a/packages/share/android/src/main/AndroidManifest.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - diff --git a/packages/share/android/src/main/java/io/flutter/plugins/share/MethodCallHandler.java b/packages/share/android/src/main/java/io/flutter/plugins/share/MethodCallHandler.java deleted file mode 100644 index 7f162e883c32..000000000000 --- a/packages/share/android/src/main/java/io/flutter/plugins/share/MethodCallHandler.java +++ /dev/null @@ -1,59 +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.share; - -import io.flutter.plugin.common.MethodCall; -import io.flutter.plugin.common.MethodChannel; -import java.io.*; -import java.util.List; -import java.util.Map; - -/** Handles the method calls for the plugin. */ -class MethodCallHandler implements MethodChannel.MethodCallHandler { - - private Share share; - - MethodCallHandler(Share share) { - this.share = share; - } - - @Override - public void onMethodCall(MethodCall call, MethodChannel.Result result) { - switch (call.method) { - case "share": - expectMapArguments(call); - // Android does not support showing the share sheet at a particular point on screen. - String text = call.argument("text"); - String subject = call.argument("subject"); - share.share(text, subject); - result.success(null); - break; - case "shareFiles": - expectMapArguments(call); - - List paths = call.argument("paths"); - List mimeTypes = call.argument("mimeTypes"); - text = call.argument("text"); - subject = call.argument("subject"); - // Android does not support showing the share sheet at a particular point on screen. - try { - share.shareFiles(paths, mimeTypes, text, subject); - result.success(null); - } catch (IOException e) { - result.error(e.getMessage(), null, null); - } - break; - default: - result.notImplemented(); - break; - } - } - - private void expectMapArguments(MethodCall call) throws IllegalArgumentException { - if (!(call.arguments instanceof Map)) { - throw new IllegalArgumentException("Map argument expected"); - } - } -} diff --git a/packages/share/android/src/main/java/io/flutter/plugins/share/Share.java b/packages/share/android/src/main/java/io/flutter/plugins/share/Share.java deleted file mode 100644 index fced7bb7f87c..000000000000 --- a/packages/share/android/src/main/java/io/flutter/plugins/share/Share.java +++ /dev/null @@ -1,232 +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.share; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.net.Uri; -import androidx.annotation.NonNull; -import androidx.core.content.FileProvider; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.List; - -/** Handles share intent. */ -class Share { - - private Context context; - private Activity activity; - - /** - * Constructs a Share object. The {@code context} and {@code activity} are used to start the share - * intent. The {@code activity} might be null when constructing the {@link Share} object and set - * to non-null when an activity is available using {@link #setActivity(Activity)}. - */ - Share(Context context, Activity activity) { - this.context = context; - this.activity = activity; - } - - /** - * Sets the activity when an activity is available. When the activity becomes unavailable, use - * this method to set it to null. - */ - void setActivity(Activity activity) { - this.activity = activity; - } - - void share(String text, String subject) { - if (text == null || text.isEmpty()) { - throw new IllegalArgumentException("Non-empty text expected"); - } - - Intent shareIntent = new Intent(); - shareIntent.setAction(Intent.ACTION_SEND); - shareIntent.putExtra(Intent.EXTRA_TEXT, text); - shareIntent.putExtra(Intent.EXTRA_SUBJECT, subject); - shareIntent.setType("text/plain"); - Intent chooserIntent = Intent.createChooser(shareIntent, null /* dialog title optional */); - startActivity(chooserIntent); - } - - void shareFiles(List paths, List mimeTypes, String text, String subject) - throws IOException { - if (paths == null || paths.isEmpty()) { - throw new IllegalArgumentException("Non-empty path expected"); - } - - clearExternalShareFolder(); - ArrayList fileUris = getUrisForPaths(paths); - - Intent shareIntent = new Intent(); - if (fileUris.isEmpty()) { - share(text, subject); - return; - } else if (fileUris.size() == 1) { - shareIntent.setAction(Intent.ACTION_SEND); - shareIntent.putExtra(Intent.EXTRA_STREAM, fileUris.get(0)); - shareIntent.setType( - !mimeTypes.isEmpty() && mimeTypes.get(0) != null ? mimeTypes.get(0) : "*/*"); - } else { - shareIntent.setAction(Intent.ACTION_SEND_MULTIPLE); - shareIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, fileUris); - shareIntent.setType(reduceMimeTypes(mimeTypes)); - } - if (text != null) shareIntent.putExtra(Intent.EXTRA_TEXT, text); - if (subject != null) shareIntent.putExtra(Intent.EXTRA_SUBJECT, subject); - shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - Intent chooserIntent = Intent.createChooser(shareIntent, null /* dialog title optional */); - - List resInfoList = - getContext() - .getPackageManager() - .queryIntentActivities(chooserIntent, PackageManager.MATCH_DEFAULT_ONLY); - for (ResolveInfo resolveInfo : resInfoList) { - String packageName = resolveInfo.activityInfo.packageName; - for (Uri fileUri : fileUris) { - getContext() - .grantUriPermission( - packageName, - fileUri, - Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); - } - } - - startActivity(chooserIntent); - } - - private void startActivity(Intent intent) { - if (activity != null) { - activity.startActivity(intent); - } else if (context != null) { - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - } else { - throw new IllegalStateException("Both context and activity are null"); - } - } - - private ArrayList getUrisForPaths(List paths) throws IOException { - ArrayList uris = new ArrayList<>(paths.size()); - for (String path : paths) { - File file = new File(path); - if (!fileIsOnExternal(file)) { - file = copyToExternalShareFolder(file); - } - - uris.add( - FileProvider.getUriForFile( - getContext(), getContext().getPackageName() + ".flutter.share_provider", file)); - } - return uris; - } - - private String reduceMimeTypes(List mimeTypes) { - if (mimeTypes.size() > 1) { - String reducedMimeType = mimeTypes.get(0); - for (int i = 1; i < mimeTypes.size(); i++) { - String mimeType = mimeTypes.get(i); - if (!reducedMimeType.equals(mimeType)) { - if (getMimeTypeBase(mimeType).equals(getMimeTypeBase(reducedMimeType))) { - reducedMimeType = getMimeTypeBase(mimeType) + "/*"; - } else { - reducedMimeType = "*/*"; - break; - } - } - } - return reducedMimeType; - } else if (mimeTypes.size() == 1) { - return mimeTypes.get(0); - } else { - return "*/*"; - } - } - - @NonNull - private String getMimeTypeBase(String mimeType) { - if (mimeType == null || !mimeType.contains("/")) { - return "*"; - } - - return mimeType.substring(0, mimeType.indexOf("/")); - } - - private boolean fileIsOnExternal(File file) { - try { - String filePath = file.getCanonicalPath(); - File externalDir = context.getExternalFilesDir(null); - return externalDir != null && filePath.startsWith(externalDir.getCanonicalPath()); - } catch (IOException e) { - return false; - } - } - - @SuppressWarnings("ResultOfMethodCallIgnored") - private void clearExternalShareFolder() { - File folder = getExternalShareFolder(); - if (folder.exists()) { - for (File file : folder.listFiles()) { - file.delete(); - } - folder.delete(); - } - } - - @SuppressWarnings("ResultOfMethodCallIgnored") - private File copyToExternalShareFolder(File file) throws IOException { - File folder = getExternalShareFolder(); - if (!folder.exists()) { - folder.mkdirs(); - } - - File newFile = new File(folder, file.getName()); - copy(file, newFile); - return newFile; - } - - @NonNull - private File getExternalShareFolder() { - return new File(getContext().getExternalCacheDir(), "share"); - } - - private Context getContext() { - if (activity != null) { - return activity; - } - if (context != null) { - return context; - } - - throw new IllegalStateException("Both context and activity are null"); - } - - private static void copy(File src, File dst) throws IOException { - InputStream in = new FileInputStream(src); - try { - OutputStream out = new FileOutputStream(dst); - try { - // Transfer bytes from in to out - byte[] buf = new byte[1024]; - int len; - while ((len = in.read(buf)) > 0) { - out.write(buf, 0, len); - } - } finally { - out.close(); - } - } finally { - in.close(); - } - } -} diff --git a/packages/share/android/src/main/java/io/flutter/plugins/share/ShareFileProvider.java b/packages/share/android/src/main/java/io/flutter/plugins/share/ShareFileProvider.java deleted file mode 100644 index fff48a6bad14..000000000000 --- a/packages/share/android/src/main/java/io/flutter/plugins/share/ShareFileProvider.java +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package io.flutter.plugins.share; - -import androidx.core.content.FileProvider; - -/** - * Providing a custom {@code FileProvider} prevents manifest {@code } name collisions. - * - *

See https://developer.android.com/guide/topics/manifest/provider-element.html for details. - */ -public class ShareFileProvider extends FileProvider {} diff --git a/packages/share/android/src/main/java/io/flutter/plugins/share/SharePlugin.java b/packages/share/android/src/main/java/io/flutter/plugins/share/SharePlugin.java deleted file mode 100644 index c596b8b71555..000000000000 --- a/packages/share/android/src/main/java/io/flutter/plugins/share/SharePlugin.java +++ /dev/null @@ -1,67 +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.share; - -import android.app.Activity; -import android.content.Context; -import io.flutter.embedding.engine.plugins.FlutterPlugin; -import io.flutter.embedding.engine.plugins.activity.ActivityAware; -import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; -import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.plugin.common.MethodChannel; - -/** Plugin method host for presenting a share sheet via Intent */ -public class SharePlugin implements FlutterPlugin, ActivityAware { - - private static final String CHANNEL = "plugins.flutter.io/share"; - private MethodCallHandler handler; - private Share share; - private MethodChannel methodChannel; - - @SuppressWarnings("deprecation") - public static void registerWith(io.flutter.plugin.common.PluginRegistry.Registrar registrar) { - SharePlugin plugin = new SharePlugin(); - plugin.setUpChannel(registrar.context(), registrar.activity(), registrar.messenger()); - } - - @Override - public void onAttachedToEngine(FlutterPluginBinding binding) { - setUpChannel(binding.getApplicationContext(), null, binding.getBinaryMessenger()); - } - - @Override - public void onDetachedFromEngine(FlutterPluginBinding binding) { - methodChannel.setMethodCallHandler(null); - methodChannel = null; - share = null; - } - - @Override - public void onAttachedToActivity(ActivityPluginBinding binding) { - share.setActivity(binding.getActivity()); - } - - @Override - public void onDetachedFromActivity() { - share.setActivity(null); - } - - @Override - public void onReattachedToActivityForConfigChanges(ActivityPluginBinding binding) { - onAttachedToActivity(binding); - } - - @Override - public void onDetachedFromActivityForConfigChanges() { - onDetachedFromActivity(); - } - - private void setUpChannel(Context context, Activity activity, BinaryMessenger messenger) { - methodChannel = new MethodChannel(messenger, CHANNEL); - share = new Share(context, activity); - handler = new MethodCallHandler(share); - methodChannel.setMethodCallHandler(handler); - } -} diff --git a/packages/share/android/src/main/res/xml/flutter_share_file_paths.xml b/packages/share/android/src/main/res/xml/flutter_share_file_paths.xml deleted file mode 100644 index e68bf916a30b..000000000000 --- a/packages/share/android/src/main/res/xml/flutter_share_file_paths.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/packages/share/example/README.md b/packages/share/example/README.md deleted file mode 100644 index 4081c8a5c9c3..000000000000 --- a/packages/share/example/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# share_example - -Demonstrates how to use the share plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/share/example/android.iml b/packages/share/example/android.iml deleted file mode 100644 index 462b903e05b6..000000000000 --- a/packages/share/example/android.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/packages/share/example/android/app/build.gradle b/packages/share/example/android/app/build.gradle deleted file mode 100644 index c0f1e05f2639..000000000000 --- a/packages/share/example/android/app/build.gradle +++ /dev/null @@ -1,60 +0,0 @@ -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -apply plugin: 'com.android.application' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - -android { - compileSdkVersion 31 - - lintOptions { - disable 'InvalidPackage' - } - - defaultConfig { - applicationId "io.flutter.plugins.shareexample" - minSdkVersion 16 - targetSdkVersion 29 - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug - } - } -} - -flutter { - source '../..' -} - -dependencies { - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test:runner:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' -} diff --git a/packages/share/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/share/example/android/app/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 9a4163a4f5ee..000000000000 --- a/packages/share/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/packages/share/example/android/app/src/main/AndroidManifest.xml b/packages/share/example/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index d1f1ce953e3a..000000000000 --- a/packages/share/example/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - diff --git a/packages/share/example/android/app/src/main/java/io/flutter/plugins/DartIntegrationTest.java b/packages/share/example/android/app/src/main/java/io/flutter/plugins/DartIntegrationTest.java deleted file mode 100644 index 0f4298dca155..000000000000 --- a/packages/share/example/android/app/src/main/java/io/flutter/plugins/DartIntegrationTest.java +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package io.flutter.plugins; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -public @interface DartIntegrationTest {} diff --git a/packages/share/example/android/app/src/main/java/io/flutter/plugins/shareexample/FlutterActivityTest.java b/packages/share/example/android/app/src/main/java/io/flutter/plugins/shareexample/FlutterActivityTest.java deleted file mode 100644 index aba658887d88..000000000000 --- a/packages/share/example/android/app/src/main/java/io/flutter/plugins/shareexample/FlutterActivityTest.java +++ /dev/null @@ -1,19 +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.shareexample; - -import androidx.test.rule.ActivityTestRule; -import dev.flutter.plugins.integration_test.FlutterTestRunner; -import io.flutter.embedding.android.FlutterActivity; -import io.flutter.plugins.DartIntegrationTest; -import org.junit.Rule; -import org.junit.runner.RunWith; - -@DartIntegrationTest -@RunWith(FlutterTestRunner.class) -public class FlutterActivityTest { - @Rule - public ActivityTestRule rule = new ActivityTestRule<>(FlutterActivity.class); -} diff --git a/packages/share/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/share/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index db77bb4b7b0906d62b1847e87f15cdcacf6a4f29..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ diff --git a/packages/share/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/share/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 17987b79bb8a35cc66c3c1fd44f5a5526c1b78be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ diff --git a/packages/share/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/share/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index d5f1c8d34e7a88e3f88bea192c3a370d44689c3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof diff --git a/packages/share/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/share/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 4d6372eebdb28e45604e46eeda8dd24651419bc0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` diff --git a/packages/share/example/android/build.gradle b/packages/share/example/android/build.gradle deleted file mode 100644 index 456d020f6e2c..000000000000 --- a/packages/share/example/android/build.gradle +++ /dev/null @@ -1,29 +0,0 @@ -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' - } -} - -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(':app') -} - -task clean(type: Delete) { - delete rootProject.buildDir -} diff --git a/packages/share/example/android/gradle.properties b/packages/share/example/android/gradle.properties deleted file mode 100644 index 38c8d4544ff1..000000000000 --- a/packages/share/example/android/gradle.properties +++ /dev/null @@ -1,4 +0,0 @@ -org.gradle.jvmargs=-Xmx1536M -android.enableR8=true -android.useAndroidX=true -android.enableJetifier=true diff --git a/packages/share/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/share/example/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index d757f3d33fcc..000000000000 --- a/packages/share/example/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip diff --git a/packages/share/example/android/settings.gradle b/packages/share/example/android/settings.gradle deleted file mode 100644 index 115da6cb4f4d..000000000000 --- a/packages/share/example/android/settings.gradle +++ /dev/null @@ -1,15 +0,0 @@ -include ':app' - -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() - -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withInputStream { stream -> plugins.load(stream) } -} - -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory -} diff --git a/packages/share/example/integration_test/share_test.dart b/packages/share/example/integration_test/share_test.dart deleted file mode 100644 index 54d553bbb5a0..000000000000 --- a/packages/share/example/integration_test/share_test.dart +++ /dev/null @@ -1,17 +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. - -// @dart = 2.9 - -import 'package:flutter_test/flutter_test.dart'; -import 'package:share/share.dart'; -import 'package:integration_test/integration_test.dart'; - -void main() { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - testWidgets('Can launch share', (WidgetTester tester) async { - expect(Share.share('message', subject: 'title'), completes); - }); -} diff --git a/packages/share/example/ios/Flutter/AppFrameworkInfo.plist b/packages/share/example/ios/Flutter/AppFrameworkInfo.plist deleted file mode 100644 index 6c2de8086bcd..000000000000 --- a/packages/share/example/ios/Flutter/AppFrameworkInfo.plist +++ /dev/null @@ -1,30 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - App - CFBundleIdentifier - io.flutter.flutter.app - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - App - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - UIRequiredDeviceCapabilities - - arm64 - - MinimumOSVersion - 8.0 - - diff --git a/packages/share/example/ios/Flutter/Debug.xcconfig b/packages/share/example/ios/Flutter/Debug.xcconfig deleted file mode 100644 index e8efba114687..000000000000 --- a/packages/share/example/ios/Flutter/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" -#include "Generated.xcconfig" diff --git a/packages/share/example/ios/Flutter/Release.xcconfig b/packages/share/example/ios/Flutter/Release.xcconfig deleted file mode 100644 index 399e9340e6f6..000000000000 --- a/packages/share/example/ios/Flutter/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" -#include "Generated.xcconfig" diff --git a/packages/share/example/ios/Podfile b/packages/share/example/ios/Podfile deleted file mode 100644 index 3924e59aa0f9..000000000000 --- a/packages/share/example/ios/Podfile +++ /dev/null @@ -1,41 +0,0 @@ -# Uncomment this line to define a global platform for your project -# platform :ios, '9.0' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_ios_podfile_setup - -target 'Runner' do - flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) - target 'RunnerTests' do - inherit! :search_paths - end -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - flutter_additional_ios_build_settings(target) - end -end diff --git a/packages/share/example/ios/Runner.xcodeproj/project.pbxproj b/packages/share/example/ios/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index d7e896212533..000000000000 --- a/packages/share/example/ios/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,698 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 28918A213BCB94C5470742D8 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 85392794417D70A970945C83 /* libPods-Runner.a */; }; - 2D9222511EC45DE6007564B0 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D9222501EC45DE6007564B0 /* GeneratedPluginRegistrant.m */; }; - 33E20B4326EFCEF400A4A191 /* RunnerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 33E20B4226EFCEF400A4A191 /* RunnerTests.m */; }; - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 683426AE2538D314009B194C /* FLTShareExampleUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 683426AD2538D314009B194C /* FLTShareExampleUITests.m */; }; - 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; - 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 33E20B4526EFCEF400A4A191 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 97C146E61CF9000F007C117D /* Project object */; - proxyType = 1; - remoteGlobalIDString = 97C146ED1CF9000F007C117D; - remoteInfo = Runner; - }; - 683426B02538D314009B194C /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 97C146E61CF9000F007C117D /* Project object */; - proxyType = 1; - remoteGlobalIDString = 97C146ED1CF9000F007C117D; - remoteInfo = Runner; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 9705A1C41CF9048500538489 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 002F2AAB9479773692FEF066 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - 1BCE6CBBA2E91FD0397A29C8 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - 2D92224F1EC45DE6007564B0 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; - 2D9222501EC45DE6007564B0 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 33E20B4026EFCEF400A4A191 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 33E20B4226EFCEF400A4A191 /* RunnerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RunnerTests.m; sourceTree = ""; }; - 33E20B4426EFCEF400A4A191 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 683426AB2538D314009B194C /* RunnerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 683426AD2538D314009B194C /* FLTShareExampleUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTShareExampleUITests.m; sourceTree = ""; }; - 683426AF2538D314009B194C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; - 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; - 85392794417D70A970945C83 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; - 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 33E20B3D26EFCEF400A4A191 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 683426A82538D314009B194C /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 97C146EB1CF9000F007C117D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 28918A213BCB94C5470742D8 /* libPods-Runner.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 16DDF472245BCC3E62219493 /* Pods */ = { - isa = PBXGroup; - children = ( - 1BCE6CBBA2E91FD0397A29C8 /* Pods-Runner.debug.xcconfig */, - 002F2AAB9479773692FEF066 /* Pods-Runner.release.xcconfig */, - ); - name = Pods; - sourceTree = ""; - }; - 33E20B4126EFCEF400A4A191 /* RunnerTests */ = { - isa = PBXGroup; - children = ( - 33E20B4226EFCEF400A4A191 /* RunnerTests.m */, - 33E20B4426EFCEF400A4A191 /* Info.plist */, - ); - path = RunnerTests; - sourceTree = ""; - }; - 683426AC2538D314009B194C /* RunnerUITests */ = { - isa = PBXGroup; - children = ( - 683426AD2538D314009B194C /* FLTShareExampleUITests.m */, - 683426AF2538D314009B194C /* Info.plist */, - ); - path = RunnerUITests; - sourceTree = ""; - }; - 8CA31EF57239BF20619316D9 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 85392794417D70A970945C83 /* libPods-Runner.a */, - ); - name = Frameworks; - sourceTree = ""; - }; - 9740EEB11CF90186004384FC /* Flutter */ = { - isa = PBXGroup; - children = ( - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 9740EEB31CF90195004384FC /* Generated.xcconfig */, - ); - name = Flutter; - sourceTree = ""; - }; - 97C146E51CF9000F007C117D = { - isa = PBXGroup; - children = ( - 9740EEB11CF90186004384FC /* Flutter */, - 97C146F01CF9000F007C117D /* Runner */, - 683426AC2538D314009B194C /* RunnerUITests */, - 33E20B4126EFCEF400A4A191 /* RunnerTests */, - 97C146EF1CF9000F007C117D /* Products */, - 16DDF472245BCC3E62219493 /* Pods */, - 8CA31EF57239BF20619316D9 /* Frameworks */, - ); - sourceTree = ""; - }; - 97C146EF1CF9000F007C117D /* Products */ = { - isa = PBXGroup; - children = ( - 97C146EE1CF9000F007C117D /* Runner.app */, - 683426AB2538D314009B194C /* RunnerUITests.xctest */, - 33E20B4026EFCEF400A4A191 /* RunnerTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - 97C146F01CF9000F007C117D /* Runner */ = { - isa = PBXGroup; - children = ( - 2D92224F1EC45DE6007564B0 /* GeneratedPluginRegistrant.h */, - 2D9222501EC45DE6007564B0 /* GeneratedPluginRegistrant.m */, - 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, - 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, - 97C146FA1CF9000F007C117D /* Main.storyboard */, - 97C146FD1CF9000F007C117D /* Assets.xcassets */, - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, - 97C147021CF9000F007C117D /* Info.plist */, - 97C146F11CF9000F007C117D /* Supporting Files */, - ); - path = Runner; - sourceTree = ""; - }; - 97C146F11CF9000F007C117D /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 97C146F21CF9000F007C117D /* main.m */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 33E20B3F26EFCEF400A4A191 /* RunnerTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 33E20B4926EFCEF400A4A191 /* Build configuration list for PBXNativeTarget "RunnerTests" */; - buildPhases = ( - 33E20B3C26EFCEF400A4A191 /* Sources */, - 33E20B3D26EFCEF400A4A191 /* Frameworks */, - 33E20B3E26EFCEF400A4A191 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 33E20B4626EFCEF400A4A191 /* PBXTargetDependency */, - ); - name = RunnerTests; - productName = RunnerTests; - productReference = 33E20B4026EFCEF400A4A191 /* RunnerTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 683426AA2538D314009B194C /* RunnerUITests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 683426B42538D314009B194C /* Build configuration list for PBXNativeTarget "RunnerUITests" */; - buildPhases = ( - 683426A72538D314009B194C /* Sources */, - 683426A82538D314009B194C /* Frameworks */, - 683426A92538D314009B194C /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 683426B12538D314009B194C /* PBXTargetDependency */, - ); - name = RunnerUITests; - productName = RunnerUITests; - productReference = 683426AB2538D314009B194C /* RunnerUITests.xctest */; - productType = "com.apple.product-type.bundle.ui-testing"; - }; - 97C146ED1CF9000F007C117D /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - 5F8AC0B5B699C537B657C107 /* [CP] Check Pods Manifest.lock */, - 9740EEB61CF901F6004384FC /* Run Script */, - 97C146EA1CF9000F007C117D /* Sources */, - 97C146EB1CF9000F007C117D /* Frameworks */, - 97C146EC1CF9000F007C117D /* Resources */, - 9705A1C41CF9048500538489 /* Embed Frameworks */, - 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Runner; - productName = Runner; - productReference = 97C146EE1CF9000F007C117D /* Runner.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 97C146E61CF9000F007C117D /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 1100; - ORGANIZATIONNAME = "The Flutter Authors"; - TargetAttributes = { - 33E20B3F26EFCEF400A4A191 = { - CreatedOnToolsVersion = 12.5; - TestTargetID = 97C146ED1CF9000F007C117D; - }; - 683426AA2538D314009B194C = { - CreatedOnToolsVersion = 11.7; - ProvisioningStyle = Automatic; - TestTargetID = 97C146ED1CF9000F007C117D; - }; - 97C146ED1CF9000F007C117D = { - CreatedOnToolsVersion = 7.3.1; - }; - }; - }; - buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 97C146E51CF9000F007C117D; - productRefGroup = 97C146EF1CF9000F007C117D /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 97C146ED1CF9000F007C117D /* Runner */, - 683426AA2538D314009B194C /* RunnerUITests */, - 33E20B3F26EFCEF400A4A191 /* RunnerTests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 33E20B3E26EFCEF400A4A191 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 683426A92538D314009B194C /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 97C146EC1CF9000F007C117D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Thin Binary"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; - }; - 5F8AC0B5B699C537B657C107 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 9740EEB61CF901F6004384FC /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 33E20B3C26EFCEF400A4A191 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33E20B4326EFCEF400A4A191 /* RunnerTests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 683426A72538D314009B194C /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 683426AE2538D314009B194C /* FLTShareExampleUITests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 97C146EA1CF9000F007C117D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, - 97C146F31CF9000F007C117D /* main.m in Sources */, - 2D9222511EC45DE6007564B0 /* GeneratedPluginRegistrant.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 33E20B4626EFCEF400A4A191 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 97C146ED1CF9000F007C117D /* Runner */; - targetProxy = 33E20B4526EFCEF400A4A191 /* PBXContainerItemProxy */; - }; - 683426B12538D314009B194C /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 97C146ED1CF9000F007C117D /* Runner */; - targetProxy = 683426B02538D314009B194C /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 97C146FA1CF9000F007C117D /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C146FB1CF9000F007C117D /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C147001CF9000F007C117D /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 33E20B4726EFCEF400A4A191 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - INFOPLIST_FILE = RunnerTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; - }; - name = Debug; - }; - 33E20B4826EFCEF400A4A191 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - INFOPLIST_FILE = RunnerTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; - }; - name = Release; - }; - 683426B22538D314009B194C /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = ""; - GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = RunnerUITests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.google.RunnerUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = Runner; - }; - name = Debug; - }; - 683426B32538D314009B194C /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = ""; - GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = RunnerUITests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.google.RunnerUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = Runner; - }; - name = Release; - }; - 97C147031CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 97C147041CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 97C147061CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.shareExample; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 97C147071CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.shareExample; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 33E20B4926EFCEF400A4A191 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33E20B4726EFCEF400A4A191 /* Debug */, - 33E20B4826EFCEF400A4A191 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 683426B42538D314009B194C /* Build configuration list for PBXNativeTarget "RunnerUITests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 683426B22538D314009B194C /* Debug */, - 683426B32538D314009B194C /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147031CF9000F007C117D /* Debug */, - 97C147041CF9000F007C117D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147061CF9000F007C117D /* Debug */, - 97C147071CF9000F007C117D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 97C146E61CF9000F007C117D /* Project object */; -} diff --git a/packages/share/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/share/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a6254f..000000000000 --- a/packages/share/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/packages/share/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/share/example/ios/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 21a3cc14c74e..000000000000 --- a/packages/share/example/ios/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/packages/share/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/share/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d68..000000000000 --- a/packages/share/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/packages/share/example/ios/Runner/AppDelegate.h b/packages/share/example/ios/Runner/AppDelegate.h deleted file mode 100644 index 0681d288bb70..000000000000 --- a/packages/share/example/ios/Runner/AppDelegate.h +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import -#import - -@interface AppDelegate : FlutterAppDelegate - -@end diff --git a/packages/share/example/ios/Runner/AppDelegate.m b/packages/share/example/ios/Runner/AppDelegate.m deleted file mode 100644 index b790a0a52635..000000000000 --- a/packages/share/example/ios/Runner/AppDelegate.m +++ /dev/null @@ -1,16 +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. - -#include "AppDelegate.h" -#include "GeneratedPluginRegistrant.h" - -@implementation AppDelegate - -- (BOOL)application:(UIApplication *)application - didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - [GeneratedPluginRegistrant registerWithRegistry:self]; - return [super application:application didFinishLaunchingWithOptions:launchOptions]; -} - -@end diff --git a/packages/share/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/share/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index d22f10b2ab63..000000000000 --- a/packages/share/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,116 +0,0 @@ -{ - "images" : [ - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@3x.png", - "scale" : "3x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/packages/share/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/share/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png deleted file mode 100644 index 28c6bf03016f6c994b70f38d1b7346e5831b531f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 564 zcmV-40?Yl0P)Px$?ny*JR5%f>l)FnDQ543{x%ZCiu33$Wg!pQFfT_}?5Q|_VSlIbLC`dpoMXL}9 zHfd9&47Mo(7D231gb+kjFxZHS4-m~7WurTH&doVX2KI5sU4v(sJ1@T9eCIKPjsqSr z)C01LsCxk=72-vXmX}CQD#BD;Cthymh&~=f$Q8nn0J<}ZrusBy4PvRNE}+1ceuj8u z0mW5k8fmgeLnTbWHGwfKA3@PdZxhn|PypR&^p?weGftrtCbjF#+zk_5BJh7;0`#Wr zgDpM_;Ax{jO##IrT`Oz;MvfwGfV$zD#c2xckpcXC6oou4ML~ezCc2EtnsQTB4tWNg z?4bkf;hG7IMfhgNI(FV5Gs4|*GyMTIY0$B=_*mso9Ityq$m^S>15>-?0(zQ<8Qy<_TjHE33(?_M8oaM zyc;NxzRVK@DL6RJnX%U^xW0Gpg(lXp(!uK1v0YgHjs^ZXSQ|m#lV7ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 diff --git a/packages/share/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/share/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png deleted file mode 100644 index f091b6b0bca859a3f474b03065bef75ba58a9e4c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1588 zcmV-42Fv-0P)C1SqPt}wig>|5Crh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)y zRAZ>eDe#*r`yDAVgB_R*LB*MAc)8(b{g{9McCXW!lq7r(btRoB9!8B-#AI6JMb~YFBEvdsV)`mEQO^&#eRKx@b&x- z5lZm*!WfD8oCLzfHGz#u7sT0^VLMI1MqGxF^v+`4YYnVYgk*=kU?HsSz{v({E3lb9 z>+xILjBN)t6`=g~IBOelGQ(O990@BfXf(DRI5I$qN$0Gkz-FSc$3a+2fX$AedL4u{ z4V+5Ong(9LiGcIKW?_352sR;LtDPmPJXI{YtT=O8=76o9;*n%_m|xo!i>7$IrZ-{l z-x3`7M}qzHsPV@$v#>H-TpjDh2UE$9g6sysUREDy_R(a)>=eHw-WAyfIN z*qb!_hW>G)Tu8nSw9yn#3wFMiLcfc4pY0ek1}8(NqkBR@t4{~oC>ryc-h_ByH(Cg5 z>ao-}771+xE3um9lWAY1FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zm zZQj(aA_HeBY&OC^jj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5Kh zX*|AU4QE#~SgPzOXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&g3j|zDgC+}2Q_v%YfDax z!?umcN^n}KYQ|a$Lr+51Nf9dkkYFSjZZjkma$0KOj+;aQ&721~t7QUKx61J3(P4P1 zstI~7-wOACnWP4=8oGOwz%vNDqD8w&Q`qcNGGrbbf&0s9L0De{4{mRS?o0MU+nR_! zrvshUau0G^DeMhM_v{5BuLjb#Hh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG z;P%vktHcXwTj~+IE(~px)3*MY77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC z(zYC$g|@+I+kY;dg_dE>scBf&bP1Nc@Hz<3R)V`=AGkc;8CXqdi=B4l2k|g;2%#m& z*jfX^%b!A8#bI!j9-0Fi0bOXl(-c^AB9|nQaE`*)Hw+o&jS9@7&Gov#HbD~#d{twV zXd^Tr^mWLfFh$@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)y zI9C9*oUga6=hxw6QasLPnee@3^Rr*M{CdaL5=R41nLs(AHk_=Y+A9$2&H(B7!_pURs&8aNw7?`&Z&xY_Ye z)~D5Bog^td-^QbUtkTirdyK^mTHAOuptDflut!#^lnKqU md>ggs(5nOWAqO?umG&QVYK#ibz}*4>0000U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~cTNYR~@Y9KepMPrrB8cABapAFa=`H+UGhkXUZV1GnwR1*lPyZ;*K(i~2gp|@bzp8}og7e*#% zEnr|^CWdVV!-4*Y_7rFvlww2Ze+>j*!Z!pQ?2l->4q#nqRu9`ELo6RMS5=br47g_X zRw}P9a7RRYQ%2Vsd0Me{_(EggTnuN6j=-?uFS6j^u69elMypu?t>op*wBx<=Wx8?( ztpe^(fwM6jJX7M-l*k3kEpWOl_Vk3@(_w4oc}4YF4|Rt=2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX?F&rExl^W|G8Z8;`5z-k}OGNZ0#WVb$WCpQu-_YsiqKP?BB# vzVHS-CTUF4Ozn5G+mq_~Qqto~ahA+K`|lyv3(-e}00000NkvXXu0mjfd`9t{ diff --git a/packages/share/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/share/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png deleted file mode 100644 index d0ef06e7edb86cdfe0d15b4b0d98334a86163658..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1716 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 diff --git a/packages/share/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/share/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png deleted file mode 100644 index c8f9ed8f5cee1c98386d13b17e89f719e83555b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1895 zcmV-t2blPYP)FQtfgmafE#=YDCq`qUBt#QpG%*H6QHY765~R=q zZ6iudfM}q!Pz#~9JgOi8QJ|DSu?1-*(kSi1K4#~5?#|rh?sS)(-JQqX*}ciXJ56_H zdw=^s_srbAdqxlvGyrgGet#6T7_|j;95sL%MtM;q86vOxKM$f#puR)Bjv9Zvz9-di zXOTSsZkM83)E9PYBXC<$6(|>lNLVBb&&6y{NByFCp%6+^ALR@NCTse_wqvNmSWI-m z!$%KlHFH2omF!>#%1l3LTZg(s7eof$7*xB)ZQ0h?ejh?Ta9fDv59+u#MokW+1t8Zb zgHv%K(u9G^Lv`lh#f3<6!JVTL3(dCpxHbnbA;kKqQyd1~^Xe0VIaYBSWm6nsr;dFj z4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3QU4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKubPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`Q zykI#*+rQ=z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRe zt3L_uNyQ*cE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=ky zx=~RKa4{iTm{_>_vSCm?$Ej=i6@=m%@VvAITnigVg{&@!7CDgs908761meDK5azA} z4?=NOH|PdvabgJ&fW2{Mo$Q0CcD8Qc84%{JPYt5EiG{MdLIAeX%T=D7NIP4%Hw}p9 zg)==!2Lbp#j{u_}hMiao9=!VSyx0gHbeCS`;q&vzeq|fs`y&^X-lso(Ls@-706qmA z7u*T5PMo_w3{se1t2`zWeO^hOvTsohG_;>J0wVqVe+n)AbQCx)yh9;w+J6?NF5Lmo zecS@ieAKL8%bVd@+-KT{yI|S}O>pYckUFs;ry9Ow$CD@ztz5K-*D$^{i(_1llhSh^ zEkL$}tsQt5>QA^;QgjgIfBDmcOgi5YDyu?t6vSnbp=1+@6D& z5MJ}B8q;bRlVoxasyhcUF1+)o`&3r0colr}QJ3hcSdLu;9;td>kf@Tcn<@9sIx&=m z;AD;SCh95=&p;$r{Xz3iWCO^MX83AGJ(yH&eTXgv|0=34#-&WAmw{)U7OU9!Wz^!7 zZ%jZFi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i h0WYBP*#0Ks^FNSabJA*5${_#%002ovPDHLkV1oKhTl@e3 diff --git a/packages/share/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/share/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png deleted file mode 100644 index a6d6b8609df07bf62e5100a53a01510388bd2b22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ diff --git a/packages/share/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/share/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png deleted file mode 100644 index a6d6b8609df07bf62e5100a53a01510388bd2b22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ diff --git a/packages/share/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/share/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png deleted file mode 100644 index 75b2d164a5a98e212cca15ea7bf2ab5de5108680..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3831 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 diff --git a/packages/share/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/share/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png deleted file mode 100644 index c4df70d39da7941ef3f6dcb7f06a192d8dcb308d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1888 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 iW8 - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/share/example/ios/Runner/Base.lproj/Main.storyboard b/packages/share/example/ios/Runner/Base.lproj/Main.storyboard deleted file mode 100644 index f3c28516fb38..000000000000 --- a/packages/share/example/ios/Runner/Base.lproj/Main.storyboard +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/share/example/ios/Runner/Info.plist b/packages/share/example/ios/Runner/Info.plist deleted file mode 100644 index 71656105a1fa..000000000000 --- a/packages/share/example/ios/Runner/Info.plist +++ /dev/null @@ -1,55 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - share_example - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UIRequiredDeviceCapabilities - - arm64 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - NSPhotoLibraryUsageDescription - This app requires access to the photo library for sharing images. - NSMicrophoneUsageDescription - This app does not require access to the microphone for sharing images. - NSCameraUsageDescription - This app requires access to the camera for sharing images. - - diff --git a/packages/share/example/ios/Runner/main.m b/packages/share/example/ios/Runner/main.m deleted file mode 100644 index f97b9ef5c8a1..000000000000 --- a/packages/share/example/ios/Runner/main.m +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import -#import -#import "AppDelegate.h" - -int main(int argc, char* argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} diff --git a/packages/share/example/ios/RunnerTests/Info.plist b/packages/share/example/ios/RunnerTests/Info.plist deleted file mode 100644 index 64d65ca49577..000000000000 --- a/packages/share/example/ios/RunnerTests/Info.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff --git a/packages/share/example/ios/RunnerTests/RunnerTests.m b/packages/share/example/ios/RunnerTests/RunnerTests.m deleted file mode 100644 index 3c4c341fd451..000000000000 --- a/packages/share/example/ios/RunnerTests/RunnerTests.m +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -@import share; -@import XCTest; - -@interface ShareTests : XCTestCase -@end - -@implementation ShareTests - -- (void)testPlugin { - FLTSharePlugin* plugin = [[FLTSharePlugin alloc] init]; - XCTAssertNotNil(plugin); -} - -@end diff --git a/packages/share/example/ios/RunnerUITests/FLTShareExampleUITests.m b/packages/share/example/ios/RunnerUITests/FLTShareExampleUITests.m deleted file mode 100644 index c099cb946b92..000000000000 --- a/packages/share/example/ios/RunnerUITests/FLTShareExampleUITests.m +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import -#import - -static const NSInteger kSecondsToWaitWhenFindingElements = 30; - -@interface FLTShareExampleUITests : XCTestCase - -@end - -@implementation FLTShareExampleUITests - -- (void)setUp { - self.continueAfterFailure = NO; -} - -- (void)testShareWithEmptyOrigin { - XCUIApplication* app = [[XCUIApplication alloc] init]; - [app launch]; - - XCUIElement* shareWithEmptyOriginButton = [app.buttons - elementMatchingPredicate:[NSPredicate - predicateWithFormat:@"label == %@", @"Share With Empty Origin"]]; - if (![shareWithEmptyOriginButton waitForExistenceWithTimeout:kSecondsToWaitWhenFindingElements]) { - os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); - XCTFail(@"Failed due to not able to find shareWithEmptyOriginButton with %@ seconds", - @(kSecondsToWaitWhenFindingElements)); - } - - XCTAssertNotNil(shareWithEmptyOriginButton); - [shareWithEmptyOriginButton tap]; - - // Find the share popup. - XCUIElement* activityListView = [app.otherElements - elementMatchingPredicate:[NSPredicate - predicateWithFormat:@"identifier == %@", @"ActivityListView"]]; - if (![activityListView waitForExistenceWithTimeout:kSecondsToWaitWhenFindingElements]) { - os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); - XCTFail(@"Failed due to not able to find activityListView with %@ seconds", - @(kSecondsToWaitWhenFindingElements)); - } - XCTAssertNotNil(activityListView); -} - -@end diff --git a/packages/share/example/ios/RunnerUITests/Info.plist b/packages/share/example/ios/RunnerUITests/Info.plist deleted file mode 100644 index 64d65ca49577..000000000000 --- a/packages/share/example/ios/RunnerUITests/Info.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff --git a/packages/share/example/lib/image_previews.dart b/packages/share/example/lib/image_previews.dart deleted file mode 100644 index 9b5b807c77c6..000000000000 --- a/packages/share/example/lib/image_previews.dart +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; - -/// Widget for displaying a preview of images -class ImagePreviews extends StatelessWidget { - /// The image paths of the displayed images - final List imagePaths; - - /// Callback when an image should be removed - final Function(int)? onDelete; - - /// Creates a widget for preview of images. [imagePaths] can not be empty - /// and all contained paths need to be non empty. - const ImagePreviews(this.imagePaths, {Key? key, this.onDelete}) - : super(key: key); - - @override - Widget build(BuildContext context) { - if (imagePaths.isEmpty) { - return Container(); - } - - List imageWidgets = []; - for (int i = 0; i < imagePaths.length; i++) { - imageWidgets.add(_ImagePreview( - imagePaths[i], - onDelete: onDelete != null ? () => onDelete!(i) : null, - )); - } - - return SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row(children: imageWidgets), - ); - } -} - -class _ImagePreview extends StatelessWidget { - final String imagePath; - final VoidCallback? onDelete; - - const _ImagePreview(this.imagePath, {Key? key, this.onDelete}) - : super(key: key); - - @override - Widget build(BuildContext context) { - File imageFile = File(imagePath); - return Padding( - padding: const EdgeInsets.all(8.0), - child: Stack( - children: [ - ConstrainedBox( - constraints: BoxConstraints( - maxWidth: 200, - maxHeight: 200, - ), - child: Image.file(imageFile), - ), - Positioned( - right: 0, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: FloatingActionButton( - backgroundColor: Colors.red, - child: Icon(Icons.delete), - onPressed: onDelete), - ), - ), - ], - ), - ); - } -} diff --git a/packages/share/example/lib/main.dart b/packages/share/example/lib/main.dart deleted file mode 100644 index f802b492df6d..000000000000 --- a/packages/share/example/lib/main.dart +++ /dev/null @@ -1,136 +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. - -// ignore_for_file: public_member_api_docs - -import 'package:flutter/material.dart'; -import 'package:image_picker/image_picker.dart'; -import 'package:share/share.dart'; - -import 'image_previews.dart'; - -void main() { - runApp(DemoApp()); -} - -class DemoApp extends StatefulWidget { - @override - DemoAppState createState() => DemoAppState(); -} - -class DemoAppState extends State { - String text = ''; - String subject = ''; - List imagePaths = []; - - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Share Plugin Demo', - home: Scaffold( - appBar: AppBar( - title: const Text('Share Plugin Demo'), - ), - body: SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(24.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TextField( - decoration: const InputDecoration( - labelText: 'Share text:', - hintText: 'Enter some text and/or link to share', - ), - maxLines: 2, - onChanged: (String value) => setState(() { - text = value; - }), - ), - TextField( - decoration: const InputDecoration( - labelText: 'Share subject:', - hintText: 'Enter subject to share (optional)', - ), - maxLines: 2, - onChanged: (String value) => setState(() { - subject = value; - }), - ), - const Padding(padding: EdgeInsets.only(top: 12.0)), - ImagePreviews(imagePaths, onDelete: _onDeleteImage), - ListTile( - leading: Icon(Icons.add), - title: Text("Add image"), - onTap: () async { - final imagePicker = ImagePicker(); - final pickedFile = await imagePicker.getImage( - source: ImageSource.gallery, - ); - if (pickedFile != null) { - setState(() { - imagePaths.add(pickedFile.path); - }); - } - }, - ), - const Padding(padding: EdgeInsets.only(top: 12.0)), - Builder( - builder: (BuildContext context) { - return ElevatedButton( - child: const Text('Share'), - onPressed: text.isEmpty && imagePaths.isEmpty - ? null - : () => _onShare(context), - ); - }, - ), - const Padding(padding: EdgeInsets.only(top: 12.0)), - Builder( - builder: (BuildContext context) { - return ElevatedButton( - child: const Text('Share With Empty Origin'), - onPressed: () => _onShareWithEmptyOrigin(context), - ); - }, - ), - ], - ), - ), - )), - ); - } - - _onDeleteImage(int position) { - setState(() { - imagePaths.removeAt(position); - }); - } - - _onShare(BuildContext context) async { - // A builder is used to retrieve the context immediately - // surrounding the ElevatedButton. - // - // The context's `findRenderObject` returns the first - // RenderObject in its descendent tree when it's not - // a RenderObjectWidget. The ElevatedButton's RenderObject - // has its position and size after it's built. - final RenderBox box = context.findRenderObject() as RenderBox; - - if (imagePaths.isNotEmpty) { - await Share.shareFiles(imagePaths, - text: text, - subject: subject, - sharePositionOrigin: box.localToGlobal(Offset.zero) & box.size); - } else { - await Share.share(text, - subject: subject, - sharePositionOrigin: box.localToGlobal(Offset.zero) & box.size); - } - } - - _onShareWithEmptyOrigin(BuildContext context) async { - await Share.share("text"); - } -} diff --git a/packages/share/example/pubspec.yaml b/packages/share/example/pubspec.yaml deleted file mode 100644 index 8a28b43d46e4..000000000000 --- a/packages/share/example/pubspec.yaml +++ /dev/null @@ -1,29 +0,0 @@ -name: share_example -description: Demonstrates how to use the share plugin. -publish_to: none - -environment: - sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.9.1+hotfix.2" - -dependencies: - flutter: - sdk: flutter - share: - # When depending on this package from a real application you should use: - # share: ^x.y.z - # See https://dart.dev/tools/pub/dependencies#version-constraints - # The example app is bundled with the plugin so we use a path dependency on - # the parent directory to use the current plugin's version. - path: ../ - image_picker: ^0.7.0 - -dev_dependencies: - flutter_driver: - sdk: flutter - integration_test: - sdk: flutter - pedantic: ^1.10.0 - -flutter: - uses-material-design: true diff --git a/packages/share/example/share_example.iml b/packages/share/example/share_example.iml deleted file mode 100644 index 9d5dae19540c..000000000000 --- a/packages/share/example/share_example.iml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/share/example/test_driver/integration_test.dart b/packages/share/example/test_driver/integration_test.dart deleted file mode 100644 index 6a0e6fa82dbe..000000000000 --- a/packages/share/example/test_driver/integration_test.dart +++ /dev/null @@ -1,9 +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. - -// @dart=2.9 - -import 'package:integration_test/integration_test_driver.dart'; - -Future main() => integrationDriver(); diff --git a/packages/share/ios/Assets/.gitkeep b/packages/share/ios/Assets/.gitkeep deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/share/ios/Classes/FLTSharePlugin.h b/packages/share/ios/Classes/FLTSharePlugin.h deleted file mode 100644 index 8f6a6a538e2a..000000000000 --- a/packages/share/ios/Classes/FLTSharePlugin.h +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -@interface FLTSharePlugin : NSObject -@end diff --git a/packages/share/ios/Classes/FLTSharePlugin.m b/packages/share/ios/Classes/FLTSharePlugin.m deleted file mode 100644 index b8a3a7ffa316..000000000000 --- a/packages/share/ios/Classes/FLTSharePlugin.m +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "FLTSharePlugin.h" - -static NSString *const PLATFORM_CHANNEL = @"plugins.flutter.io/share"; - -@interface ShareData : NSObject - -@property(readonly, nonatomic, copy) NSString *subject; -@property(readonly, nonatomic, copy) NSString *text; -@property(readonly, nonatomic, copy) NSString *path; -@property(readonly, nonatomic, copy) NSString *mimeType; - -- (instancetype)initWithSubject:(NSString *)subject text:(NSString *)text NS_DESIGNATED_INITIALIZER; -- (instancetype)initWithFile:(NSString *)path - mimeType:(NSString *)mimeType NS_DESIGNATED_INITIALIZER; - -- (instancetype)init __attribute__((unavailable("Use initWithSubject:text: instead"))); - -@end - -@implementation ShareData - -- (instancetype)init { - [super doesNotRecognizeSelector:_cmd]; - return nil; -} - -- (instancetype)initWithSubject:(NSString *)subject text:(NSString *)text { - self = [super init]; - if (self) { - _subject = [subject isKindOfClass:NSNull.class] ? @"" : subject; - _text = text; - } - return self; -} - -- (instancetype)initWithFile:(NSString *)path mimeType:(NSString *)mimeType { - self = [super init]; - if (self) { - _path = path; - _mimeType = mimeType; - } - return self; -} - -- (id)activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController { - return @""; -} - -- (id)activityViewController:(UIActivityViewController *)activityViewController - itemForActivityType:(UIActivityType)activityType { - if (!_path || !_mimeType) { - return _text; - } - - if ([_mimeType hasPrefix:@"image/"]) { - UIImage *image = [UIImage imageWithContentsOfFile:_path]; - return image; - } else { - NSURL *url = [NSURL fileURLWithPath:_path]; - return url; - } -} - -- (NSString *)activityViewController:(UIActivityViewController *)activityViewController - subjectForActivityType:(UIActivityType)activityType { - return _subject; -} - -- (UIImage *)activityViewController:(UIActivityViewController *)activityViewController - thumbnailImageForActivityType:(UIActivityType)activityType - suggestedSize:(CGSize)suggestedSize { - if (!_path || !_mimeType || ![_mimeType hasPrefix:@"image/"]) { - return nil; - } - - UIImage *image = [UIImage imageWithContentsOfFile:_path]; - return [self imageWithImage:image scaledToSize:suggestedSize]; -} - -- (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize { - UIGraphicsBeginImageContext(newSize); - [image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)]; - UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - return newImage; -} - -@end - -@implementation FLTSharePlugin - -+ (void)registerWithRegistrar:(NSObject *)registrar { - FlutterMethodChannel *shareChannel = - [FlutterMethodChannel methodChannelWithName:PLATFORM_CHANNEL - binaryMessenger:registrar.messenger]; - - [shareChannel setMethodCallHandler:^(FlutterMethodCall *call, FlutterResult result) { - NSDictionary *arguments = [call arguments]; - NSNumber *originX = arguments[@"originX"]; - NSNumber *originY = arguments[@"originY"]; - NSNumber *originWidth = arguments[@"originWidth"]; - NSNumber *originHeight = arguments[@"originHeight"]; - - CGRect originRect = CGRectZero; - if (originX && originY && originWidth && originHeight) { - originRect = CGRectMake([originX doubleValue], [originY doubleValue], - [originWidth doubleValue], [originHeight doubleValue]); - } - - if ([@"share" isEqualToString:call.method]) { - NSString *shareText = arguments[@"text"]; - NSString *shareSubject = arguments[@"subject"]; - - if (shareText.length == 0) { - result([FlutterError errorWithCode:@"error" - message:@"Non-empty text expected" - details:nil]); - return; - } - - [self shareText:shareText - subject:shareSubject - withController:[UIApplication sharedApplication].keyWindow.rootViewController - atSource:originRect]; - result(nil); - } else if ([@"shareFiles" isEqualToString:call.method]) { - NSArray *paths = arguments[@"paths"]; - NSArray *mimeTypes = arguments[@"mimeTypes"]; - NSString *subject = arguments[@"subject"]; - NSString *text = arguments[@"text"]; - - if (paths.count == 0) { - result([FlutterError errorWithCode:@"error" - message:@"Non-empty paths expected" - details:nil]); - return; - } - - for (NSString *path in paths) { - if (path.length == 0) { - result([FlutterError errorWithCode:@"error" - message:@"Each path must not be empty" - details:nil]); - return; - } - } - - [self shareFiles:paths - withMimeType:mimeTypes - withSubject:subject - withText:text - withController:[UIApplication sharedApplication].keyWindow.rootViewController - atSource:originRect]; - result(nil); - } else { - result(FlutterMethodNotImplemented); - } - }]; -} - -+ (void)share:(NSArray *)shareItems - withController:(UIViewController *)controller - atSource:(CGRect)origin { - UIActivityViewController *activityViewController = - [[UIActivityViewController alloc] initWithActivityItems:shareItems applicationActivities:nil]; - activityViewController.popoverPresentationController.sourceView = controller.view; - activityViewController.popoverPresentationController.sourceRect = origin; - - [controller presentViewController:activityViewController animated:YES completion:nil]; -} - -+ (void)shareText:(NSString *)shareText - subject:(NSString *)subject - withController:(UIViewController *)controller - atSource:(CGRect)origin { - ShareData *data = [[ShareData alloc] initWithSubject:subject text:shareText]; - [self share:@[ data ] withController:controller atSource:origin]; -} - -+ (void)shareFiles:(NSArray *)paths - withMimeType:(NSArray *)mimeTypes - withSubject:(NSString *)subject - withText:(NSString *)text - withController:(UIViewController *)controller - atSource:(CGRect)origin { - NSMutableArray *items = [[NSMutableArray alloc] init]; - - if (text || subject) { - [items addObject:[[ShareData alloc] initWithSubject:subject text:text]]; - } - - for (int i = 0; i < [paths count]; i++) { - NSString *path = paths[i]; - NSString *pathExtension = [path pathExtension]; - NSString *mimeType = mimeTypes[i]; - if ([pathExtension.lowercaseString isEqualToString:@"jpg"] || - [pathExtension.lowercaseString isEqualToString:@"jpeg"] || - [pathExtension.lowercaseString isEqualToString:@"png"] || - [mimeType.lowercaseString isEqualToString:@"image/jpg"] || - [mimeType.lowercaseString isEqualToString:@"image/jpeg"] || - [mimeType.lowercaseString isEqualToString:@"image/png"]) { - UIImage *image = [UIImage imageWithContentsOfFile:path]; - [items addObject:image]; - } else { - [items addObject:[[ShareData alloc] initWithFile:path mimeType:mimeType]]; - } - } - - [self share:items withController:controller atSource:origin]; -} - -@end diff --git a/packages/share/ios/share.podspec b/packages/share/ios/share.podspec deleted file mode 100644 index 786e1c7fb922..000000000000 --- a/packages/share/ios/share.podspec +++ /dev/null @@ -1,23 +0,0 @@ -# -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html -# -Pod::Spec.new do |s| - s.name = 'share' - s.version = '0.0.1' - s.summary = 'Flutter Share' - s.description = <<-DESC -A Flutter plugin to share content from your Flutter app via the platform's share dialog. -Downloaded by pub (not CocoaPods). - DESC - s.homepage = 'https://github.com/flutter/plugins' - s.license = { :type => 'BSD', :file => '../LICENSE' } - s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/share' } - s.documentation_url = 'https://pub.dev/packages/share' - s.source_files = 'Classes/**/*' - s.public_header_files = 'Classes/**/*.h' - s.dependency 'Flutter' - s.platform = :ios, '8.0' - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' } -end - diff --git a/packages/share/lib/share.dart b/packages/share/lib/share.dart deleted file mode 100644 index 6a3f5a317e31..000000000000 --- a/packages/share/lib/share.dart +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; -import 'dart:ui'; - -import 'package:flutter/services.dart'; -import 'package:meta/meta.dart' show visibleForTesting; -import 'package:mime/mime.dart' show lookupMimeType; - -/// Plugin for summoning a platform share sheet. -class Share { - /// [MethodChannel] used to communicate with the platform side. - @visibleForTesting - static const MethodChannel channel = - MethodChannel('plugins.flutter.io/share'); - - /// Summons the platform's share sheet to share text. - /// - /// Wraps the platform's native share dialog. Can share a text and/or a URL. - /// It uses the `ACTION_SEND` Intent on Android and `UIActivityViewController` - /// on iOS. - /// - /// The optional [subject] parameter can be used to populate a subject if the - /// user chooses to send an email. - /// - /// The optional [sharePositionOrigin] parameter can be used to specify a global - /// origin rect for the share sheet to popover from on iPads. It has no effect - /// on non-iPads. - /// - /// May throw [PlatformException] or [FormatException] - /// from [MethodChannel]. - static Future share( - String text, { - String? subject, - Rect? sharePositionOrigin, - }) { - assert(text != null); - assert(text.isNotEmpty); - final Map params = { - 'text': text, - 'subject': subject, - }; - - if (sharePositionOrigin != null) { - params['originX'] = sharePositionOrigin.left; - params['originY'] = sharePositionOrigin.top; - params['originWidth'] = sharePositionOrigin.width; - params['originHeight'] = sharePositionOrigin.height; - } - - return channel.invokeMethod('share', params); - } - - /// Summons the platform's share sheet to share multiple files. - /// - /// Wraps the platform's native share dialog. Can share a file. - /// It uses the `ACTION_SEND` Intent on Android and `UIActivityViewController` - /// on iOS. - /// - /// The optional `sharePositionOrigin` parameter can be used to specify a global - /// origin rect for the share sheet to popover from on iPads. It has no effect - /// on non-iPads. - /// - /// May throw [PlatformException] or [FormatException] - /// from [MethodChannel]. - static Future shareFiles( - List paths, { - List? mimeTypes, - String? subject, - String? text, - Rect? sharePositionOrigin, - }) { - assert(paths != null); - assert(paths.isNotEmpty); - assert(paths.every((element) => element != null && element.isNotEmpty)); - final Map params = { - 'paths': paths, - 'mimeTypes': mimeTypes ?? - paths.map((String path) => _mimeTypeForPath(path)).toList(), - }; - - if (subject != null) params['subject'] = subject; - if (text != null) params['text'] = text; - - if (sharePositionOrigin != null) { - params['originX'] = sharePositionOrigin.left; - params['originY'] = sharePositionOrigin.top; - params['originWidth'] = sharePositionOrigin.width; - params['originHeight'] = sharePositionOrigin.height; - } - - return channel.invokeMethod('shareFiles', params); - } - - static String _mimeTypeForPath(String path) { - assert(path != null); - return lookupMimeType(path) ?? 'application/octet-stream'; - } -} diff --git a/packages/share/pubspec.yaml b/packages/share/pubspec.yaml deleted file mode 100644 index 4735995fff8a..000000000000 --- a/packages/share/pubspec.yaml +++ /dev/null @@ -1,32 +0,0 @@ -name: share -description: Flutter plugin for sharing content via the platform share UI, using - the ACTION_SEND intent on Android and UIActivityViewController on iOS. -repository: https://github.com/flutter/plugins/tree/master/packages/share -issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+share%22 -version: 2.0.4 - -environment: - flutter: ">=1.12.13+hotfix.5" - sdk: ">=2.12.0 <3.0.0" - -flutter: - plugin: - platforms: - android: - package: io.flutter.plugins.share - pluginClass: SharePlugin - ios: - pluginClass: FLTSharePlugin - -dependencies: - meta: ^1.3.0 - mime: ^1.0.0 - flutter: - sdk: flutter - -dev_dependencies: - flutter_test: - sdk: flutter - integration_test: - sdk: flutter - pedantic: ^1.10.0 diff --git a/packages/share/test/share_test.dart b/packages/share/test/share_test.dart deleted file mode 100644 index d0049cef94ab..000000000000 --- a/packages/share/test/share_test.dart +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:io'; -import 'dart:ui'; - -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:share/share.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); // Required for MethodChannels - - late FakeMethodChannel fakeChannel; - - setUp(() { - fakeChannel = FakeMethodChannel(); - // Re-pipe to our fake to verify invocations. - Share.channel.setMockMethodCallHandler((MethodCall call) async { - // The explicit type can be void as the only method call has a return type of void. - await fakeChannel.invokeMethod(call.method, call.arguments); - }); - }); - - test('sharing empty fails', () { - expect( - () => Share.share(''), - throwsA(isA()), - ); - expect(fakeChannel.invocation, isNull); - }); - - test('sharing origin sets the right params', () async { - await Share.share( - 'some text to share', - subject: 'some subject to share', - sharePositionOrigin: const Rect.fromLTWH(1.0, 2.0, 3.0, 4.0), - ); - - expect( - fakeChannel.invocation, - equals({ - 'share': { - 'text': 'some text to share', - 'subject': 'some subject to share', - 'originX': 1.0, - 'originY': 2.0, - 'originWidth': 3.0, - 'originHeight': 4.0, - } - }), - ); - }); - - test('sharing empty file fails', () { - expect( - () => Share.shareFiles(['']), - throwsA(isA()), - ); - expect(fakeChannel.invocation, isNull); - }); - - test('sharing file sets correct mimeType', () async { - final String path = 'tempfile-83649a.png'; - final File file = File(path); - try { - file.createSync(); - - await Share.shareFiles([path]); - - expect( - fakeChannel.invocation, - equals({ - 'shareFiles': { - 'paths': [path], - 'mimeTypes': ['image/png'], - } - }), - ); - } finally { - file.deleteSync(); - } - }); - - test('sharing file sets passed mimeType', () async { - final String path = 'tempfile-83649a.png'; - final File file = File(path); - try { - file.createSync(); - - await Share.shareFiles([path], mimeTypes: ['*/*']); - - expect( - fakeChannel.invocation, - equals({ - 'shareFiles': { - 'paths': [file.path], - 'mimeTypes': ['*/*'], - } - }), - ); - } finally { - file.deleteSync(); - } - }); -} - -class FakeMethodChannel extends Fake implements MethodChannel { - Map? invocation; - - @override - Future invokeMethod(String method, [dynamic arguments]) async { - this.invocation = {method: arguments}; - return null; - } -} diff --git a/packages/video_player/video_player_web/example/pubspec.yaml b/packages/video_player/video_player_web/example/pubspec.yaml index c172eeaf1223..4ec938bebce2 100644 --- a/packages/video_player/video_player_web/example/pubspec.yaml +++ b/packages/video_player/video_player_web/example/pubspec.yaml @@ -1,4 +1,4 @@ -name: connectivity_for_web_integration_tests +name: video_player_for_web_integration_tests publish_to: none environment: diff --git a/packages/wifi_info_flutter/analysis_options.yaml b/packages/wifi_info_flutter/analysis_options.yaml deleted file mode 100644 index cda4f6e153e6..000000000000 --- a/packages/wifi_info_flutter/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../analysis_options_legacy.yaml diff --git a/packages/wifi_info_flutter/wifi_info_flutter/.metadata b/packages/wifi_info_flutter/wifi_info_flutter/.metadata deleted file mode 100644 index e40242b8f94f..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/.metadata +++ /dev/null @@ -1,10 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: 4513e96a3022d70aa7686906c2e9bdfbbc448334 - channel: master - -project_type: plugin diff --git a/packages/wifi_info_flutter/wifi_info_flutter/AUTHORS b/packages/wifi_info_flutter/wifi_info_flutter/AUTHORS deleted file mode 100644 index 493a0b4ef9c2..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/AUTHORS +++ /dev/null @@ -1,66 +0,0 @@ -# Below is a list of people and organizations that have contributed -# to the Flutter project. Names should be added to the list like so: -# -# Name/Organization - -Google Inc. -The Chromium Authors -German Saprykin -Benjamin Sauer -larsenthomasj@gmail.com -Ali Bitek -Pol Batlló -Anatoly Pulyaevskiy -Hayden Flinner -Stefano Rodriguez -Salvatore Giordano -Brian Armstrong -Paul DeMarco -Fabricio Nogueira -Simon Lightfoot -Ashton Thomas -Thomas Danner -Diego Velásquez -Hajime Nakamura -Tuyển Vũ Xuân -Miguel Ruivo -Sarthak Verma -Mike Diarmid -Invertase -Elliot Hesp -Vince Varga -Aawaz Gyawali -EUI Limited -Katarina Sheremet -Thomas Stockx -Sarbagya Dhaubanjar -Ozkan Eksi -Rishab Nayak -ko2ic -Jonathan Younger -Jose Sanchez -Debkanchan Samadder -Audrius Karosevicius -Lukasz Piliszczuk -SoundReply Solutions GmbH -Rafal Wachol -Pau Picas -Christian Weder -Alexandru Tuca -Christian Weder -Rhodes Davis Jr. -Luigi Agosti -Quentin Le Guennec -Koushik Ravikumar -Nissim Dsilva -Giancarlo Rocha -Ryo Miyake -Théo Champion -Kazuki Yamaguchi -Eitan Schwartz -Chris Rutkowski -Juan Alvarez -Aleksandr Yurkovskiy -Anton Borries -Alex Li -Rahul Raj <64.rahulraj@gmail.com> diff --git a/packages/wifi_info_flutter/wifi_info_flutter/CHANGELOG.md b/packages/wifi_info_flutter/wifi_info_flutter/CHANGELOG.md deleted file mode 100644 index 0e41699be340..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/CHANGELOG.md +++ /dev/null @@ -1,39 +0,0 @@ -## NEXT - -* Updated Android lint settings. -* Updated package description. -* Updates Android compileSdkVersion to 31. - -## 2.0.2 - -* Update README to point to Plus Plugins version. - -## 2.0.1 - -* Migrate maven repository from jcenter to mavenCentral. - -## 2.0.0 - -* Migrate to null safety. - -## 1.0.4 - -* Android: Add Log warning for unsatisfied requirement(s) in Android P or higher. -* Android: Update Example project. - -## 1.0.3 - -* Fix README example. - -## 1.0.2 - -* Update Flutter SDK constraint. - -## 1.0.1 - -* Fixed method channel name in android implementation. [Issue](https://github.com/flutter/flutter/issues/69073). - -## 1.0.0 - -* Initial release of the plugin. This plugin retrieves information about a device's connection to wifi. -* See [README](./README.md) for details. diff --git a/packages/wifi_info_flutter/wifi_info_flutter/LICENSE b/packages/wifi_info_flutter/wifi_info_flutter/LICENSE deleted file mode 100644 index c6823b81eb84..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright 2013 The Flutter Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/wifi_info_flutter/wifi_info_flutter/README.md b/packages/wifi_info_flutter/wifi_info_flutter/README.md deleted file mode 100644 index 3f1084eb1d2c..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/README.md +++ /dev/null @@ -1,86 +0,0 @@ -# wifi_info_flutter - ---- - -## Deprecation Notice - -This plugin has been replaced by the [Flutter Community Plus -Plugins](https://plus.fluttercommunity.dev/) version, -[`network_info_plus`](https://pub.dev/packages/network_info_plus). -No further updates are planned to this plugin, and we encourage all users to -migrate to the Plus version. - -Critical fixes (e.g., for any security incidents) will be provided through the -end of 2021, at which point this package will be marked as discontinued. - ---- - -This plugin retrieves information about a device's connection to wifi. - -> Note that on Android, this does not guarantee connection to Internet. For instance, -the app might have wifi access but it might be a VPN or a hotel WiFi with no access. - -## Usage - -### Android - -Sample usage to check current status: - -To successfully get WiFi Name or Wi-Fi BSSID starting with Android O, ensure all of the following conditions are met: - - * If your app is targeting Android 10 (API level 29) SDK or higher, your app has the ACCESS_FINE_LOCATION permission. - - * If your app is targeting SDK lower than Android 10 (API level 29), your app has the ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission. - - * Location services are enabled on the device (under Settings > Location). - -You can get wi-fi related information using: - -```dart -import 'package:wifi_info_flutter/wifi_info_flutter.dart'; - -var wifiBSSID = await WifiInfo().getWifiBSSID(); -var wifiIP = await WifiInfo().getWifiIP(); -var wifiName = await WifiInfo().getWifiName(); -``` - -### iOS 12 - -To use `.getWifiBSSID()` and `.getWifiName()` on iOS >= 12, the `Access WiFi information capability` in XCode must be enabled. Otherwise, both methods will return null. - -### iOS 13 - -The methods `.getWifiBSSID()` and `.getWifiName()` utilize the [`CNCopyCurrentNetworkInfo`](https://developer.apple.com/documentation/systemconfiguration/1614126-cncopycurrentnetworkinfo) function on iOS. - -As of iOS 13, Apple announced that these APIs will no longer return valid information. -An app linked against iOS 12 or earlier receives pseudo-values such as: - - * SSID: "Wi-Fi" or "WLAN" ("WLAN" will be returned for the China SKU). - - * BSSID: "00:00:00:00:00:00" - -An app linked against iOS 13 or later receives `null`. - -The `CNCopyCurrentNetworkInfo` will work for Apps that: - - * The app uses Core Location, and has the user’s authorization to use location information. - - * The app uses the NEHotspotConfiguration API to configure the current Wi-Fi network. - - * The app has active VPN configurations installed. - -If your app falls into the last two categories, it will work as it is. If your app doesn't fall into the last two categories, -and you still need to access the wifi information, you should request user's authorization to use location information. - -There is a helper method provided in this plugin to request the location authorization: `requestLocationServiceAuthorization`. -To request location authorization, make sure to add the following keys to your _Info.plist_ file, located in `/ios/Runner/Info.plist`: - -* `NSLocationAlwaysAndWhenInUseUsageDescription` - describe why the app needs access to the user’s location information all the time (foreground and background). This is called _Privacy - Location Always and When In Use Usage Description_ in the visual editor. -* `NSLocationWhenInUseUsageDescription` - describe why the app needs access to the user’s location information when the app is running in the foreground. This is called _Privacy - Location When In Use Usage Description_ in the visual editor. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](http://flutter.io/). - -For help on editing plugin code, view the [documentation](https://flutter.io/platform-plugins/#edit-code). diff --git a/packages/wifi_info_flutter/wifi_info_flutter/android/build.gradle b/packages/wifi_info_flutter/wifi_info_flutter/android/build.gradle deleted file mode 100644 index 76ac3bc7eedc..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/android/build.gradle +++ /dev/null @@ -1,47 +0,0 @@ -group 'io.flutter.plugins.wifi_info_flutter' -version '1.0' - -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' - } -} - -rootProject.allprojects { - repositories { - google() - mavenCentral() - } -} - -apply plugin: 'com.android.library' - -android { - compileSdkVersion 31 - - defaultConfig { - minSdkVersion 16 - } - lintOptions { - disable 'InvalidPackage' - disable 'GradleDependency' - } - - - testOptions { - unitTests.includeAndroidResources = true - unitTests.returnDefaultValues = true - unitTests.all { - testLogging { - events "passed", "skipped", "failed", "standardOut", "standardError" - outputs.upToDateWhen {false} - showStandardStreams = true - } - } - } -} diff --git a/packages/wifi_info_flutter/wifi_info_flutter/android/gradle/wrapper/gradle-wrapper.properties b/packages/wifi_info_flutter/wifi_info_flutter/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 01a286e96a21..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip diff --git a/packages/wifi_info_flutter/wifi_info_flutter/android/settings.gradle b/packages/wifi_info_flutter/wifi_info_flutter/android/settings.gradle deleted file mode 100644 index ec0e24958ea9..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/android/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'wifi_info_flutter' diff --git a/packages/wifi_info_flutter/wifi_info_flutter/android/src/main/AndroidManifest.xml b/packages/wifi_info_flutter/wifi_info_flutter/android/src/main/AndroidManifest.xml deleted file mode 100644 index 03ac924f9427..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/android/src/main/AndroidManifest.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - diff --git a/packages/wifi_info_flutter/wifi_info_flutter/android/src/main/java/io/flutter/plugins/wifi_info_flutter/WifiInfoFlutter.java b/packages/wifi_info_flutter/wifi_info_flutter/android/src/main/java/io/flutter/plugins/wifi_info_flutter/WifiInfoFlutter.java deleted file mode 100644 index bd4c8f10ce3b..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/android/src/main/java/io/flutter/plugins/wifi_info_flutter/WifiInfoFlutter.java +++ /dev/null @@ -1,157 +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.wifi_info_flutter; - -import android.Manifest; -import android.content.Context; -import android.content.pm.PackageManager; -import android.location.LocationManager; -import android.net.wifi.WifiInfo; -import android.net.wifi.WifiManager; -import android.os.Build; -import androidx.core.content.ContextCompat; -import io.flutter.Log; - -/** Reports wifi information. */ -class WifiInfoFlutter { - private WifiManager wifiManager; - private Context context; - private static final String TAG = "WifiInfoFlutter"; - - WifiInfoFlutter(WifiManager wifiManager, Context context) { - this.wifiManager = wifiManager; - this.context = context; - } - - String getWifiName() { - if (!checkPermissions()) { - return null; - } - final WifiInfo wifiInfo = getWifiInfo(); - String ssid = null; - if (wifiInfo != null) ssid = wifiInfo.getSSID(); - if (ssid != null) ssid = ssid.replaceAll("\"", ""); // Android returns "SSID" - if (ssid != null && ssid.equals("")) ssid = null; - return ssid; - } - - String getWifiBSSID() { - if (!checkPermissions()) { - return null; - } - final WifiInfo wifiInfo = getWifiInfo(); - String bssid = null; - if (wifiInfo != null) { - bssid = wifiInfo.getBSSID(); - } - return bssid; - } - - String getWifiIPAddress() { - WifiInfo wifiInfo = null; - if (wifiManager != null) wifiInfo = wifiManager.getConnectionInfo(); - - String ip = null; - int i_ip = 0; - if (wifiInfo != null) i_ip = wifiInfo.getIpAddress(); - - if (i_ip != 0) - ip = - String.format( - "%d.%d.%d.%d", - (i_ip & 0xff), (i_ip >> 8 & 0xff), (i_ip >> 16 & 0xff), (i_ip >> 24 & 0xff)); - - return ip; - } - - private WifiInfo getWifiInfo() { - return wifiManager == null ? null : wifiManager.getConnectionInfo(); - } - - private Boolean checkPermissions() { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { - return true; - } - - boolean grantedChangeWifiState = - ContextCompat.checkSelfPermission(context, Manifest.permission.CHANGE_WIFI_STATE) - == PackageManager.PERMISSION_GRANTED; - - boolean grantedAccessFine = - ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) - == PackageManager.PERMISSION_GRANTED; - - boolean grantedAccessCoarse = - ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) - == PackageManager.PERMISSION_GRANTED; - - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P - && !grantedChangeWifiState - && !grantedAccessFine - && !grantedAccessCoarse) { - Log.w( - TAG, - "Attempted to get Wi-Fi data that requires additional permission(s).\n" - + "To successfully get WiFi Name or Wi-Fi BSSID starting with Android O, please ensure your app has one of the following permissions:\n" - + "- CHANGE_WIFI_STATE\n" - + "- ACCESS_FINE_LOCATION\n" - + "- ACCESS_COARSE_LOCATION\n" - + "For more information about Wi-Fi Restrictions in Android 8.0 and above, please consult the following link:\n" - + "https://developer.android.com/guide/topics/connectivity/wifi-scan"); - return false; - } - - if (Build.VERSION.SDK_INT == Build.VERSION_CODES.P && !grantedChangeWifiState) { - Log.w( - TAG, - "Attempted to get Wi-Fi data that requires additional permission(s).\n" - + "To successfully get WiFi Name or Wi-Fi BSSID starting with Android P, please ensure your app has the CHANGE_WIFI_STATE permission.\n" - + "For more information about Wi-Fi Restrictions in Android 9.0 and above, please consult the following link:\n" - + "https://developer.android.com/guide/topics/connectivity/wifi-scan"); - return false; - } - - if (Build.VERSION.SDK_INT == Build.VERSION_CODES.P - && !grantedAccessFine - && !grantedAccessCoarse) { - Log.w( - TAG, - "Attempted to get Wi-Fi data that requires additional permission(s).\n" - + "To successfully get WiFi Name or Wi-Fi BSSID starting with Android P, additional to CHANGE_WIFI_STATE please ensure your app has one of the following permissions too:\n" - + "- ACCESS_FINE_LOCATION\n" - + "- ACCESS_COARSE_LOCATION\n" - + "For more information about Wi-Fi Restrictions in Android 9.0 and above, please consult the following link:\n" - + "https://developer.android.com/guide/topics/connectivity/wifi-scan"); - return false; - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q - && (!grantedAccessFine || !grantedChangeWifiState)) { - Log.w( - TAG, - "Attempted to get Wi-Fi data that requires additional permission(s).\n" - + "To successfully get WiFi Name or Wi-Fi BSSID starting with Android Q, please ensure your app has the CHANGE_WIFI_STATE and ACCESS_FINE_LOCATION permission.\n" - + "For more information about Wi-Fi Restrictions in Android 10.0 and above, please consult the following link:\n" - + "https://developer.android.com/guide/topics/connectivity/wifi-scan"); - return false; - } - - LocationManager locationManager = - (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); - - boolean gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && !gpsEnabled) { - Log.w( - TAG, - "Attempted to get Wi-Fi data that requires additional permission(s).\n" - + "To successfully get WiFi Name or Wi-Fi BSSID starting with Android P, please ensure Location services are enabled on the device (under Settings > Location).\n" - + "For more information about Wi-Fi Restrictions in Android 9.0 and above, please consult the following link:\n" - + "https://developer.android.com/guide/topics/connectivity/wifi-scan"); - return false; - } - return true; - } -} diff --git a/packages/wifi_info_flutter/wifi_info_flutter/android/src/main/java/io/flutter/plugins/wifi_info_flutter/WifiInfoFlutterMethodChannelHandler.java b/packages/wifi_info_flutter/wifi_info_flutter/android/src/main/java/io/flutter/plugins/wifi_info_flutter/WifiInfoFlutterMethodChannelHandler.java deleted file mode 100644 index 9ceed5968e63..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/android/src/main/java/io/flutter/plugins/wifi_info_flutter/WifiInfoFlutterMethodChannelHandler.java +++ /dev/null @@ -1,44 +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.wifi_info_flutter; - -import io.flutter.plugin.common.MethodCall; -import io.flutter.plugin.common.MethodChannel; - -/** - * The handler receives {@link MethodCall}s from the UIThread, gets the related information from - * a @{@link WifiInfoFlutter}, and then send the result back to the UIThread through the {@link - * MethodChannel.Result}. - */ -class WifiInfoFlutterMethodChannelHandler implements MethodChannel.MethodCallHandler { - private WifiInfoFlutter wifiInfoFlutter; - - /** - * Construct the WifiInfoFlutterMethodChannelHandler with a {@code wifiInfoFlutter}. The {@code - * wifiInfoFlutter} must not be null. - */ - WifiInfoFlutterMethodChannelHandler(WifiInfoFlutter wifiInfoFlutter) { - assert (wifiInfoFlutter != null); - this.wifiInfoFlutter = wifiInfoFlutter; - } - - @Override - public void onMethodCall(MethodCall call, MethodChannel.Result result) { - switch (call.method) { - case "wifiName": - result.success(wifiInfoFlutter.getWifiName()); - break; - case "wifiBSSID": - result.success(wifiInfoFlutter.getWifiBSSID()); - break; - case "wifiIPAddress": - result.success(wifiInfoFlutter.getWifiIPAddress()); - break; - default: - result.notImplemented(); - break; - } - } -} diff --git a/packages/wifi_info_flutter/wifi_info_flutter/android/src/main/java/io/flutter/plugins/wifi_info_flutter/WifiInfoFlutterPlugin.java b/packages/wifi_info_flutter/wifi_info_flutter/android/src/main/java/io/flutter/plugins/wifi_info_flutter/WifiInfoFlutterPlugin.java deleted file mode 100644 index 7757688bc9fa..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/android/src/main/java/io/flutter/plugins/wifi_info_flutter/WifiInfoFlutterPlugin.java +++ /dev/null @@ -1,46 +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.wifi_info_flutter; - -import android.content.Context; -import android.net.wifi.WifiManager; -import io.flutter.embedding.engine.plugins.FlutterPlugin; -import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.plugin.common.MethodChannel; - -/** WifiInfoFlutterPlugin */ -public class WifiInfoFlutterPlugin implements FlutterPlugin { - private MethodChannel methodChannel; - - /** Plugin registration. */ - @SuppressWarnings("deprecation") - public static void registerWith(io.flutter.plugin.common.PluginRegistry.Registrar registrar) { - WifiInfoFlutterPlugin plugin = new WifiInfoFlutterPlugin(); - plugin.setupChannels(registrar.messenger(), registrar.context()); - } - - @Override - public void onAttachedToEngine(FlutterPluginBinding binding) { - setupChannels(binding.getBinaryMessenger(), binding.getApplicationContext()); - } - - @Override - public void onDetachedFromEngine(FlutterPluginBinding binding) { - methodChannel.setMethodCallHandler(null); - methodChannel = null; - } - - private void setupChannels(BinaryMessenger messenger, Context context) { - methodChannel = new MethodChannel(messenger, "plugins.flutter.io/wifi_info_flutter"); - final WifiManager wifiManager = - (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE); - - final WifiInfoFlutter wifiInfoFlutter = new WifiInfoFlutter(wifiManager, context); - - final WifiInfoFlutterMethodChannelHandler methodChannelHandler = - new WifiInfoFlutterMethodChannelHandler(wifiInfoFlutter); - methodChannel.setMethodCallHandler(methodChannelHandler); - } -} diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/.metadata b/packages/wifi_info_flutter/wifi_info_flutter/example/.metadata deleted file mode 100644 index 8407594c70b4..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/.metadata +++ /dev/null @@ -1,10 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: 4513e96a3022d70aa7686906c2e9bdfbbc448334 - channel: master - -project_type: app diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/README.md b/packages/wifi_info_flutter/wifi_info_flutter/example/README.md deleted file mode 100644 index 30c38ad1ba92..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# wifi_info_flutter_example - -Demonstrates how to use the wifi_info_flutter plugin. - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) - -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/build.gradle b/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/build.gradle deleted file mode 100644 index 8f96b53e2663..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/build.gradle +++ /dev/null @@ -1,54 +0,0 @@ -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -apply plugin: 'com.android.application' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - -android { - compileSdkVersion 31 - - lintOptions { - disable 'InvalidPackage' - } - - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "io.flutter.plugins.wifi_info_flutter_example" - minSdkVersion 16 - targetSdkVersion 29 - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - } - - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug - } - } -} - -flutter { - source '../..' -} diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/debug/AndroidManifest.xml b/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/debug/AndroidManifest.xml deleted file mode 100644 index 357602a1d503..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/debug/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/main/AndroidManifest.xml b/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index bcecab36d14a..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/main/java/io/flutter/plugins/wifi_info_flutter_example/MainActivity.java b/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/main/java/io/flutter/plugins/wifi_info_flutter_example/MainActivity.java deleted file mode 100644 index b52123be65d4..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/main/java/io/flutter/plugins/wifi_info_flutter_example/MainActivity.java +++ /dev/null @@ -1,9 +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.wifi_info_flutter_example; - -import io.flutter.embedding.android.FlutterActivity; - -public class MainActivity extends FlutterActivity {} diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/main/res/drawable/launch_background.xml b/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/main/res/drawable/launch_background.xml deleted file mode 100644 index f74085f3f6a2..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/main/res/drawable/launch_background.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index db77bb4b7b0906d62b1847e87f15cdcacf6a4f29..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 17987b79bb8a35cc66c3c1fd44f5a5526c1b78be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index d5f1c8d34e7a88e3f88bea192c3a370d44689c3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 4d6372eebdb28e45604e46eeda8dd24651419bc0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/main/res/values-night/styles.xml b/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/main/res/values-night/styles.xml deleted file mode 100644 index 449a9f930826..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/main/res/values-night/styles.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/main/res/values/styles.xml b/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/main/res/values/styles.xml deleted file mode 100644 index d74aa35c2826..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/profile/AndroidManifest.xml b/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/profile/AndroidManifest.xml deleted file mode 100644 index 357602a1d503..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/android/app/src/profile/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/android/build.gradle b/packages/wifi_info_flutter/wifi_info_flutter/example/android/build.gradle deleted file mode 100644 index 456d020f6e2c..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/android/build.gradle +++ /dev/null @@ -1,29 +0,0 @@ -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' - } -} - -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(':app') -} - -task clean(type: Delete) { - delete rootProject.buildDir -} diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/android/gradle.properties b/packages/wifi_info_flutter/wifi_info_flutter/example/android/gradle.properties deleted file mode 100644 index a6738207fd15..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/android/gradle.properties +++ /dev/null @@ -1,4 +0,0 @@ -org.gradle.jvmargs=-Xmx1536M -android.useAndroidX=true -android.enableJetifier=true -android.enableR8=true diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/wifi_info_flutter/wifi_info_flutter/example/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 296b146b7318..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Fri Jun 23 08:50:38 CEST 2017 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/android/settings.gradle b/packages/wifi_info_flutter/wifi_info_flutter/example/android/settings.gradle deleted file mode 100644 index 44e62bcf06ae..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/android/settings.gradle +++ /dev/null @@ -1,11 +0,0 @@ -include ':app' - -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() - -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } - -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/integration_test/wifi_info_test.dart b/packages/wifi_info_flutter/wifi_info_flutter/example/integration_test/wifi_info_test.dart deleted file mode 100644 index 8190062e3ebd..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/integration_test/wifi_info_test.dart +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:io'; -import 'package:integration_test/integration_test.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:wifi_info_flutter/wifi_info_flutter.dart'; - -void main() { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - group('$WifiInfo test driver', () { - late WifiInfo _wifiInfo; - - setUpAll(() async { - _wifiInfo = WifiInfo(); - }); - - testWidgets('test location methods, iOS only', (WidgetTester tester) async { - expect( - (await _wifiInfo.getLocationServiceAuthorization()), - LocationAuthorizationStatus.notDetermined, - ); - }, skip: !Platform.isIOS); - }); -} diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Flutter/AppFrameworkInfo.plist b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Flutter/AppFrameworkInfo.plist deleted file mode 100644 index f2872cf474ee..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Flutter/AppFrameworkInfo.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - App - CFBundleIdentifier - io.flutter.flutter.app - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - App - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - MinimumOSVersion - 9.0 - - diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Flutter/Debug.xcconfig b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Flutter/Debug.xcconfig deleted file mode 100644 index e8efba114687..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Flutter/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" -#include "Generated.xcconfig" diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Flutter/Release.xcconfig b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Flutter/Release.xcconfig deleted file mode 100644 index 399e9340e6f6..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Flutter/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" -#include "Generated.xcconfig" diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Podfile b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Podfile deleted file mode 100644 index 07a4e08abf54..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Podfile +++ /dev/null @@ -1,44 +0,0 @@ -# Uncomment this line to define a global platform for your project -# platform :ios, '9.0' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_ios_podfile_setup - -target 'Runner' do - flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - # Work around https://github.com/flutter/flutter/issues/82964. - if target.name == 'Reachability' - target.build_configurations.each do |config| - config.build_settings['WARNING_CFLAGS'] = '-Wno-pointer-to-int-cast' - end - end - flutter_additional_ios_build_settings(target) - end -end diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner.xcodeproj/project.pbxproj b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index 98b6089f0d68..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,545 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 78A2B22AE5FB53474D0E7B48 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5304CE43F05781426D604828 /* libPods-Runner.a */; }; - 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; - 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 9705A1C41CF9048500538489 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 11F3C35054888B3724893A22 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 1E5A9EB282D46A445314F9FD /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - 2BADCFEAF6163E1D252C8765 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 5304CE43F05781426D604828 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; - 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; - 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 97C146EB1CF9000F007C117D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 78A2B22AE5FB53474D0E7B48 /* libPods-Runner.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 53534922C743E29B902DE7D2 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 5304CE43F05781426D604828 /* libPods-Runner.a */, - ); - name = Frameworks; - sourceTree = ""; - }; - 9740EEB11CF90186004384FC /* Flutter */ = { - isa = PBXGroup; - children = ( - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 9740EEB31CF90195004384FC /* Generated.xcconfig */, - ); - name = Flutter; - sourceTree = ""; - }; - 97C146E51CF9000F007C117D = { - isa = PBXGroup; - children = ( - 9740EEB11CF90186004384FC /* Flutter */, - 97C146F01CF9000F007C117D /* Runner */, - 97C146EF1CF9000F007C117D /* Products */, - 9938C0E7E0C974A660788A69 /* Pods */, - 53534922C743E29B902DE7D2 /* Frameworks */, - ); - sourceTree = ""; - }; - 97C146EF1CF9000F007C117D /* Products */ = { - isa = PBXGroup; - children = ( - 97C146EE1CF9000F007C117D /* Runner.app */, - ); - name = Products; - sourceTree = ""; - }; - 97C146F01CF9000F007C117D /* Runner */ = { - isa = PBXGroup; - children = ( - 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, - 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, - 97C146FA1CF9000F007C117D /* Main.storyboard */, - 97C146FD1CF9000F007C117D /* Assets.xcassets */, - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, - 97C147021CF9000F007C117D /* Info.plist */, - 97C146F11CF9000F007C117D /* Supporting Files */, - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, - ); - path = Runner; - sourceTree = ""; - }; - 97C146F11CF9000F007C117D /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 97C146F21CF9000F007C117D /* main.m */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - 9938C0E7E0C974A660788A69 /* Pods */ = { - isa = PBXGroup; - children = ( - 1E5A9EB282D46A445314F9FD /* Pods-Runner.debug.xcconfig */, - 2BADCFEAF6163E1D252C8765 /* Pods-Runner.release.xcconfig */, - 11F3C35054888B3724893A22 /* Pods-Runner.profile.xcconfig */, - ); - name = Pods; - path = Pods; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 97C146ED1CF9000F007C117D /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - 77C985AE0A130001A78D36BE /* [CP] Check Pods Manifest.lock */, - 9740EEB61CF901F6004384FC /* Run Script */, - 97C146EA1CF9000F007C117D /* Sources */, - 97C146EB1CF9000F007C117D /* Frameworks */, - 97C146EC1CF9000F007C117D /* Resources */, - 9705A1C41CF9048500538489 /* Embed Frameworks */, - 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Runner; - productName = Runner; - productReference = 97C146EE1CF9000F007C117D /* Runner.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 97C146E61CF9000F007C117D /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 1020; - ORGANIZATIONNAME = ""; - TargetAttributes = { - 97C146ED1CF9000F007C117D = { - CreatedOnToolsVersion = 7.3.1; - }; - }; - }; - buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 97C146E51CF9000F007C117D; - productRefGroup = 97C146EF1CF9000F007C117D /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 97C146ED1CF9000F007C117D /* Runner */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 97C146EC1CF9000F007C117D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Thin Binary"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; - }; - 77C985AE0A130001A78D36BE /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 9740EEB61CF901F6004384FC /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 97C146EA1CF9000F007C117D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, - 97C146F31CF9000F007C117D /* main.m in Sources */, - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 97C146FA1CF9000F007C117D /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C146FB1CF9000F007C117D /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C147001CF9000F007C117D /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 249021D3217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Profile; - }; - 249021D4217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.wifiInfoFlutterExample; - PRODUCT_NAME = "$(TARGET_NAME)"; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Profile; - }; - 97C147031CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 97C147041CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 97C147061CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.wifiInfoFlutterExample; - PRODUCT_NAME = "$(TARGET_NAME)"; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Debug; - }; - 97C147071CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.wifiInfoFlutterExample; - PRODUCT_NAME = "$(TARGET_NAME)"; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147031CF9000F007C117D /* Debug */, - 97C147041CF9000F007C117D /* Release */, - 249021D3217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147061CF9000F007C117D /* Debug */, - 97C147071CF9000F007C117D /* Release */, - 249021D4217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 97C146E61CF9000F007C117D /* Project object */; -} diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a6254f..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d68..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c5ea15..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index a28140cfdb3f..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 21a3cc14c74e..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d68..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c5ea15..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/AppDelegate.h b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/AppDelegate.h deleted file mode 100644 index 0681d288bb70..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/AppDelegate.h +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import -#import - -@interface AppDelegate : FlutterAppDelegate - -@end diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/AppDelegate.m b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/AppDelegate.m deleted file mode 100644 index 442514aaecbe..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/AppDelegate.m +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "AppDelegate.h" -#import "GeneratedPluginRegistrant.h" - -@implementation AppDelegate - -- (BOOL)application:(UIApplication *)application - didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - [GeneratedPluginRegistrant registerWithRegistry:self]; - // Override point for customization after application launch. - return [super application:application didFinishLaunchingWithOptions:launchOptions]; -} - -@end diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index d36b1fab2d9d..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "images" : [ - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@3x.png", - "scale" : "3x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" - }, - { - "size" : "1024x1024", - "idiom" : "ios-marketing", - "filename" : "Icon-App-1024x1024@1x.png", - "scale" : "1x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png deleted file mode 100644 index dc9ada4725e9b0ddb1deab583e5b5102493aa332..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10932 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 diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png deleted file mode 100644 index f091b6b0bca859a3f474b03065bef75ba58a9e4c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1588 zcmV-42Fv-0P)C1SqPt}wig>|5Crh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)y zRAZ>eDe#*r`yDAVgB_R*LB*MAc)8(b{g{9McCXW!lq7r(btRoB9!8B-#AI6JMb~YFBEvdsV)`mEQO^&#eRKx@b&x- z5lZm*!WfD8oCLzfHGz#u7sT0^VLMI1MqGxF^v+`4YYnVYgk*=kU?HsSz{v({E3lb9 z>+xILjBN)t6`=g~IBOelGQ(O990@BfXf(DRI5I$qN$0Gkz-FSc$3a+2fX$AedL4u{ z4V+5Ong(9LiGcIKW?_352sR;LtDPmPJXI{YtT=O8=76o9;*n%_m|xo!i>7$IrZ-{l z-x3`7M}qzHsPV@$v#>H-TpjDh2UE$9g6sysUREDy_R(a)>=eHw-WAyfIN z*qb!_hW>G)Tu8nSw9yn#3wFMiLcfc4pY0ek1}8(NqkBR@t4{~oC>ryc-h_ByH(Cg5 z>ao-}771+xE3um9lWAY1FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zm zZQj(aA_HeBY&OC^jj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5Kh zX*|AU4QE#~SgPzOXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&g3j|zDgC+}2Q_v%YfDax z!?umcN^n}KYQ|a$Lr+51Nf9dkkYFSjZZjkma$0KOj+;aQ&721~t7QUKx61J3(P4P1 zstI~7-wOACnWP4=8oGOwz%vNDqD8w&Q`qcNGGrbbf&0s9L0De{4{mRS?o0MU+nR_! zrvshUau0G^DeMhM_v{5BuLjb#Hh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG z;P%vktHcXwTj~+IE(~px)3*MY77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC z(zYC$g|@+I+kY;dg_dE>scBf&bP1Nc@Hz<3R)V`=AGkc;8CXqdi=B4l2k|g;2%#m& z*jfX^%b!A8#bI!j9-0Fi0bOXl(-c^AB9|nQaE`*)Hw+o&jS9@7&Gov#HbD~#d{twV zXd^Tr^mWLfFh$@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)y zI9C9*oUga6=hxw6QasLPnee@3^Rr*M{CdaL5=R41nLs(AHk_=Y+A9$2&H(B7!_pURs&8aNw7?`&Z&xY_Ye z)~D5Bog^td-^QbUtkTirdyK^mTHAOuptDflut!#^lnKqU md>ggs(5nOWAqO?umG&QVYK#ibz}*4>0000U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~cTNYR~@Y9KepMPrrB8cABapAFa=`H+UGhkXUZV1GnwR1*lPyZ;*K(i~2gp|@bzp8}og7e*#% zEnr|^CWdVV!-4*Y_7rFvlww2Ze+>j*!Z!pQ?2l->4q#nqRu9`ELo6RMS5=br47g_X zRw}P9a7RRYQ%2Vsd0Me{_(EggTnuN6j=-?uFS6j^u69elMypu?t>op*wBx<=Wx8?( ztpe^(fwM6jJX7M-l*k3kEpWOl_Vk3@(_w4oc}4YF4|Rt=2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX?F&rExl^W|G8Z8;`5z-k}OGNZ0#WVb$WCpQu-_YsiqKP?BB# vzVHS-CTUF4Ozn5G+mq_~Qqto~ahA+K`|lyv3(-e}00000NkvXXu0mjfd`9t{ diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png deleted file mode 100644 index d0ef06e7edb86cdfe0d15b4b0d98334a86163658..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1716 zcmds$`#;kQ7{|XelZftyR5~xW7?MLxS4^|Hw3&P7^y)@A9Fj{Xm1~_CIV^XZ%SLBn zA;!r`GqGHg=7>xrB{?psZQs88ZaedDoagm^KF{a*>G|dJWRSe^I$DNW008I^+;Kjt z>9p3GNR^I;v>5_`+91i(*G;u5|L+Bu6M=(afLjtkya#yZ175|z$pU~>2#^Z_pCZ7o z1c6UNcv2B3?; zX%qdxCXQpdKRz=#b*q0P%b&o)5ZrNZt7$fiETSK_VaY=mb4GK`#~0K#~9^ zcY!`#Af+4h?UMR-gMKOmpuYeN5P*RKF!(tb`)oe0j2BH1l?=>y#S5pMqkx6i{*=V9JF%>N8`ewGhRE(|WohnD59R^$_36{4>S zDFlPC5|k?;SPsDo87!B{6*7eqmMdU|QZ84>6)Kd9wNfh90=y=TFQay-0__>=<4pk& zYDjgIhL-jQ9o>z32K)BgAH+HxamL{ZL~ozu)Qqe@a`FpH=oQRA8=L-m-1dam(Ix2V z?du;LdMO+ooBelr^_y4{|44tmgH^2hSzPFd;U^!1p>6d|o)(-01z{i&Kj@)z-yfWQ)V#3Uo!_U}q3u`(fOs`_f^ueFii1xBNUB z6MecwJN$CqV&vhc+)b(p4NzGGEgwWNs z@*lUV6LaduZH)4_g!cE<2G6#+hJrWd5(|p1Z;YJ7ifVHv+n49btR}dq?HHDjl{m$T z!jLZcGkb&XS2OG~u%&R$(X+Z`CWec%QKt>NGYvd5g20)PU(dOn^7%@6kQb}C(%=vr z{?RP(z~C9DPnL{q^@pVw@|Vx~@3v!9dCaBtbh2EdtoNHm4kGxp>i#ct)7p|$QJs+U z-a3qtcPvhihub?wnJqEt>zC@)2suY?%-96cYCm$Q8R%-8$PZYsx3~QOLMDf(piXMm zB=<63yQk1AdOz#-qsEDX>>c)EES%$owHKue;?B3)8aRd}m~_)>SL3h2(9X;|+2#7X z+#2)NpD%qJvCQ0a-uzZLmz*ms+l*N}w)3LRQ*6>|Ub-fyptY(keUxw+)jfwF5K{L9 z|Cl_w=`!l_o><384d&?)$6Nh(GAm=4p_;{qVn#hI8lqewW7~wUlyBM-4Z|)cZr?Rh z=xZ&Ol>4(CU85ea(CZ^aO@2N18K>ftl8>2MqetAR53_JA>Fal`^)1Y--Am~UDa4th zKfCYpcXky$XSFDWBMIl(q=Mxj$iMBX=|j9P)^fDmF(5(5$|?Cx}DKEJa&XZP%OyE`*GvvYQ4PV&!g2|L^Q z?YG}tx;sY@GzMmsY`7r$P+F_YLz)(e}% zyakqFB<6|x9R#TdoP{R$>o7y(-`$$p0NxJ6?2B8tH)4^yF(WhqGZlM3=9Ibs$%U1w zWzcss*_c0=v_+^bfb`kBFsI`d;ElwiU%frgRB%qBjn@!0U2zZehBn|{%uNIKBA7n= zzE`nnwTP85{g;8AkYxA68>#muXa!G>xH22D1I*SiD~7C?7Za+9y7j1SHiuSkKK*^O zsZ==KO(Ua#?YUpXl{ViynyT#Hzk=}5X$e04O@fsMQjb}EMuPWFO0e&8(2N(29$@Vd zn1h8Yd>6z(*p^E{c(L0Lg=wVdupg!z@WG;E0k|4a%s7Up5C0c)55XVK*|x9RQeZ1J@1v9MX;>n34(i>=YE@Iur`0Vah(inE3VUFZNqf~tSz{1fz3Fsn_x4F>o(Yo;kpqvBe-sbwH(*Y zu$JOl0b83zu$JMvy<#oH^Wl>aWL*?aDwnS0iEAwC?DK@aT)GHRLhnz2WCvf3Ba;o=aY7 z2{Asu5MEjGOY4O#Ggz@@J;q*0`kd2n8I3BeNuMmYZf{}pg=jTdTCrIIYuW~luKecn z+E-pHY%ohj@uS0%^ z&(OxwPFPD$+#~`H?fMvi9geVLci(`K?Kj|w{rZ9JgthFHV+=6vMbK~0)Ea<&WY-NC zy-PnZft_k2tfeQ*SuC=nUj4H%SQ&Y$gbH4#2sT0cU0SdFs=*W*4hKGpuR1{)mV;Qf5pw4? zfiQgy0w3fC*w&Bj#{&=7033qFR*<*61B4f9K%CQvxEn&bsWJ{&winp;FP!KBj=(P6 z4Z_n4L7cS;ao2)ax?Tm|I1pH|uLpDSRVghkA_UtFFuZ0b2#>!8;>-_0ELjQSD-DRd z4im;599VHDZYtnWZGAB25W-e(2VrzEh|etsv2YoP#VbIZ{aFkwPrzJ#JvCvA*mXS& z`}Q^v9(W4GiSs}#s7BaN!WA2bniM$0J(#;MR>uIJ^uvgD3GS^%*ikdW6-!VFUU?JV zZc2)4cMsX@j z5HQ^e3BUzOdm}yC-xA%SY``k$rbfk z;CHqifhU*jfGM@DkYCecD9vl*qr58l6x<8URB=&%{!Cu3RO*MrKZ4VO}V6R0a zZw3Eg^0iKWM1dcTYZ0>N899=r6?+adUiBKPciJw}L$=1f4cs^bio&cr9baLF>6#BM z(F}EXe-`F=f_@`A7+Q&|QaZ??Txp_dB#lg!NH=t3$G8&06MFhwR=Iu*Im0s_b2B@| znW>X}sy~m#EW)&6E&!*0%}8UAS)wjt+A(io#wGI@Z2S+Ms1Cxl%YVE800007ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png deleted file mode 100644 index c8f9ed8f5cee1c98386d13b17e89f719e83555b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1895 zcmV-t2blPYP)FQtfgmafE#=YDCq`qUBt#QpG%*H6QHY765~R=q zZ6iudfM}q!Pz#~9JgOi8QJ|DSu?1-*(kSi1K4#~5?#|rh?sS)(-JQqX*}ciXJ56_H zdw=^s_srbAdqxlvGyrgGet#6T7_|j;95sL%MtM;q86vOxKM$f#puR)Bjv9Zvz9-di zXOTSsZkM83)E9PYBXC<$6(|>lNLVBb&&6y{NByFCp%6+^ALR@NCTse_wqvNmSWI-m z!$%KlHFH2omF!>#%1l3LTZg(s7eof$7*xB)ZQ0h?ejh?Ta9fDv59+u#MokW+1t8Zb zgHv%K(u9G^Lv`lh#f3<6!JVTL3(dCpxHbnbA;kKqQyd1~^Xe0VIaYBSWm6nsr;dFj z4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3QU4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKubPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`Q zykI#*+rQ=z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRe zt3L_uNyQ*cE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=ky zx=~RKa4{iTm{_>_vSCm?$Ej=i6@=m%@VvAITnigVg{&@!7CDgs908761meDK5azA} z4?=NOH|PdvabgJ&fW2{Mo$Q0CcD8Qc84%{JPYt5EiG{MdLIAeX%T=D7NIP4%Hw}p9 zg)==!2Lbp#j{u_}hMiao9=!VSyx0gHbeCS`;q&vzeq|fs`y&^X-lso(Ls@-706qmA z7u*T5PMo_w3{se1t2`zWeO^hOvTsohG_;>J0wVqVe+n)AbQCx)yh9;w+J6?NF5Lmo zecS@ieAKL8%bVd@+-KT{yI|S}O>pYckUFs;ry9Ow$CD@ztz5K-*D$^{i(_1llhSh^ zEkL$}tsQt5>QA^;QgjgIfBDmcOgi5YDyu?t6vSnbp=1+@6D& z5MJ}B8q;bRlVoxasyhcUF1+)o`&3r0colr}QJ3hcSdLu;9;td>kf@Tcn<@9sIx&=m z;AD;SCh95=&p;$r{Xz3iWCO^MX83AGJ(yH&eTXgv|0=34#-&WAmw{)U7OU9!Wz^!7 zZ%jZFi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i h0WYBP*#0Ks^FNSabJA*5${_#%002ovPDHLkV1oKhTl@e3 diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png deleted file mode 100644 index a6d6b8609df07bf62e5100a53a01510388bd2b22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png deleted file mode 100644 index a6d6b8609df07bf62e5100a53a01510388bd2b22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png deleted file mode 100644 index 75b2d164a5a98e212cca15ea7bf2ab5de5108680..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3831 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 diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png deleted file mode 100644 index c4df70d39da7941ef3f6dcb7f06a192d8dcb308d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1888 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 iW8Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png deleted file mode 100644 index 9da19eacad3b03bb08bbddbbf4ac48dd78b3d838..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png deleted file mode 100644 index 9da19eacad3b03bb08bbddbbf4ac48dd78b3d838..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md deleted file mode 100644 index 89c2725b70f1..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Launch Screen Assets - -You can customize the launch screen with your own desired assets by replacing the image files in this directory. - -You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index f2e259c7c939..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Base.lproj/Main.storyboard b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Base.lproj/Main.storyboard deleted file mode 100644 index f3c28516fb38..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Base.lproj/Main.storyboard +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Info.plist b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Info.plist deleted file mode 100644 index 2c7f6b8c6b85..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/Info.plist +++ /dev/null @@ -1,49 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - wifi_info_flutter_example - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleSignature - ???? - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - NSLocationAlwaysAndWhenInUseUsageDescription - This app requires accessing your location information all the time to get wi-fi information. - NSLocationWhenInUseUsageDescription - This app requires accessing your location information when the app is in foreground to get wi-fi information. - - diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/main.m b/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/main.m deleted file mode 100644 index f97b9ef5c8a1..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/ios/Runner/main.m +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import -#import -#import "AppDelegate.h" - -int main(int argc, char* argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/lib/main.dart b/packages/wifi_info_flutter/wifi_info_flutter/example/lib/main.dart deleted file mode 100644 index 8258815b0c09..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/lib/main.dart +++ /dev/null @@ -1,173 +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. - -// ignore_for_file: public_member_api_docs - -import 'dart:async'; -import 'dart:io'; - -import 'package:connectivity/connectivity.dart' - show Connectivity, ConnectivityResult; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:wifi_info_flutter/wifi_info_flutter.dart'; - -// Sets a platform override for desktop to avoid exceptions. See -// https://flutter.dev/desktop#target-platform-override for more info. -void _enablePlatformOverrideForDesktop() { - if (!kIsWeb && (Platform.isWindows || Platform.isLinux)) { - debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia; - } -} - -void main() { - _enablePlatformOverrideForDesktop(); - runApp(MyApp()); -} - -class MyApp extends StatelessWidget { - // This widget is the root of your application. - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - primarySwatch: Colors.blue, - ), - home: MyHomePage(title: 'Flutter Demo Home Page'), - ); - } -} - -class MyHomePage extends StatefulWidget { - MyHomePage({Key? key, required this.title}) : super(key: key); - - final String title; - - @override - _MyHomePageState createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - String _connectionStatus = 'Unknown'; - final Connectivity _connectivity = Connectivity(); - final WifiInfo _wifiInfo = WifiInfo(); - late StreamSubscription _connectivitySubscription; - - @override - void initState() { - super.initState(); - initConnectivity(); - _connectivitySubscription = - _connectivity.onConnectivityChanged.listen(_updateConnectionStatus); - } - - @override - void dispose() { - _connectivitySubscription.cancel(); - super.dispose(); - } - - // Platform messages are asynchronous, so we initialize in an async method. - Future initConnectivity() async { - late ConnectivityResult result; - // Platform messages may fail, so we use a try/catch PlatformException. - try { - result = await _connectivity.checkConnectivity(); - } on PlatformException catch (e) { - print(e.toString()); - } - - // If the widget was removed from the tree while the asynchronous platform - // message was in flight, we want to discard the reply rather than calling - // setState to update our non-existent appearance. - if (!mounted) { - return Future.value(null); - } - - return _updateConnectionStatus(result); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('Connectivity example app'), - ), - body: Center(child: Text('Connection Status: $_connectionStatus')), - ); - } - - Future _updateConnectionStatus(ConnectivityResult result) async { - switch (result) { - case ConnectivityResult.wifi: - String? wifiName, wifiBSSID, wifiIP; - - try { - if (!kIsWeb && Platform.isIOS) { - LocationAuthorizationStatus status = - await _wifiInfo.getLocationServiceAuthorization(); - if (status == LocationAuthorizationStatus.notDetermined) { - status = await _wifiInfo.requestLocationServiceAuthorization(); - } - if (status == LocationAuthorizationStatus.authorizedAlways || - status == LocationAuthorizationStatus.authorizedWhenInUse) { - wifiName = await _wifiInfo.getWifiName(); - } else { - wifiName = await _wifiInfo.getWifiName(); - } - } else { - wifiName = await _wifiInfo.getWifiName(); - } - } on PlatformException catch (e) { - print(e.toString()); - wifiName = "Failed to get Wifi Name"; - } - - try { - if (!kIsWeb && Platform.isIOS) { - LocationAuthorizationStatus status = - await _wifiInfo.getLocationServiceAuthorization(); - if (status == LocationAuthorizationStatus.notDetermined) { - status = await _wifiInfo.requestLocationServiceAuthorization(); - } - if (status == LocationAuthorizationStatus.authorizedAlways || - status == LocationAuthorizationStatus.authorizedWhenInUse) { - wifiBSSID = await _wifiInfo.getWifiBSSID(); - } else { - wifiBSSID = await _wifiInfo.getWifiBSSID(); - } - } else { - wifiBSSID = await _wifiInfo.getWifiBSSID(); - } - } on PlatformException catch (e) { - print(e.toString()); - wifiBSSID = "Failed to get Wifi BSSID"; - } - - try { - wifiIP = await _wifiInfo.getWifiIP(); - } on PlatformException catch (e) { - print(e.toString()); - wifiIP = "Failed to get Wifi IP"; - } - - setState(() { - _connectionStatus = '$result\n' - 'Wifi Name: $wifiName\n' - 'Wifi BSSID: $wifiBSSID\n' - 'Wifi IP: $wifiIP\n'; - }); - break; - case ConnectivityResult.mobile: - case ConnectivityResult.none: - setState(() => _connectionStatus = result.toString()); - break; - default: - setState(() => _connectionStatus = 'Failed to get connectivity.'); - break; - } - } -} diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/pubspec.yaml b/packages/wifi_info_flutter/wifi_info_flutter/example/pubspec.yaml deleted file mode 100644 index 6303907c32d6..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/pubspec.yaml +++ /dev/null @@ -1,27 +0,0 @@ -name: wifi_info_flutter_example -description: Demonstrates how to use the wifi_info_flutter plugin. -publish_to: none - -environment: - sdk: ">=2.12.0 <3.0.0" - -dependencies: - connectivity: ^3.0.0 - flutter: - sdk: flutter - wifi_info_flutter: - # When depending on this package from a real application you should use: - # wifi_info_flutter: ^x.y.z - # See https://dart.dev/tools/pub/dependencies#version-constraints - # The example app is bundled with the plugin so we use a path dependency on - # the parent directory to use the current plugin's version. - path: ../ - -dev_dependencies: - integration_test: - sdk: flutter - flutter_test: - sdk: flutter - -flutter: - uses-material-design: true diff --git a/packages/wifi_info_flutter/wifi_info_flutter/example/test_driver/integration_test.dart b/packages/wifi_info_flutter/wifi_info_flutter/example/test_driver/integration_test.dart deleted file mode 100644 index 4f10f2a522f3..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/example/test_driver/integration_test.dart +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:integration_test/integration_test_driver.dart'; - -Future main() => integrationDriver(); diff --git a/packages/wifi_info_flutter/wifi_info_flutter/ios/Assets/.gitkeep b/packages/wifi_info_flutter/wifi_info_flutter/ios/Assets/.gitkeep deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/wifi_info_flutter/wifi_info_flutter/ios/Classes/FLTWifiInfoLocationHandler.h b/packages/wifi_info_flutter/wifi_info_flutter/ios/Classes/FLTWifiInfoLocationHandler.h deleted file mode 100644 index 359562b7761c..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/ios/Classes/FLTWifiInfoLocationHandler.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@class FLTWifiInfoLocationDelegate; - -typedef void (^FLTWifiInfoLocationCompletion)(CLAuthorizationStatus); - -@interface FLTWifiInfoLocationHandler : NSObject - -+ (CLAuthorizationStatus)locationAuthorizationStatus; - -- (void)requestLocationAuthorization:(BOOL)always - completion:(_Nonnull FLTWifiInfoLocationCompletion)completionHnadler; - -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/wifi_info_flutter/wifi_info_flutter/ios/Classes/FLTWifiInfoLocationHandler.m b/packages/wifi_info_flutter/wifi_info_flutter/ios/Classes/FLTWifiInfoLocationHandler.m deleted file mode 100644 index 2fe19c5e70e9..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/ios/Classes/FLTWifiInfoLocationHandler.m +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "FLTWifiInfoLocationHandler.h" - -@interface FLTWifiInfoLocationHandler () - -@property(copy, nonatomic) FLTWifiInfoLocationCompletion completion; -@property(strong, nonatomic) CLLocationManager *locationManager; - -@end - -@implementation FLTWifiInfoLocationHandler - -+ (CLAuthorizationStatus)locationAuthorizationStatus { - return CLLocationManager.authorizationStatus; -} - -- (void)requestLocationAuthorization:(BOOL)always - completion:(FLTWifiInfoLocationCompletion)completionHandler { - CLAuthorizationStatus status = CLLocationManager.authorizationStatus; - if (status != kCLAuthorizationStatusAuthorizedWhenInUse && always) { - completionHandler(kCLAuthorizationStatusDenied); - return; - } else if (status != kCLAuthorizationStatusNotDetermined) { - completionHandler(status); - return; - } - - if (self.completion) { - // If a request is still in process, immediately return. - completionHandler(kCLAuthorizationStatusNotDetermined); - return; - } - - self.completion = completionHandler; - self.locationManager = [CLLocationManager new]; - self.locationManager.delegate = self; - if (always) { - [self.locationManager requestAlwaysAuthorization]; - } else { - [self.locationManager requestWhenInUseAuthorization]; - } -} - -- (void)locationManager:(CLLocationManager *)manager - didChangeAuthorizationStatus:(CLAuthorizationStatus)status { - if (status == kCLAuthorizationStatusNotDetermined) { - return; - } - if (self.completion) { - self.completion(status); - self.completion = nil; - } -} - -@end diff --git a/packages/wifi_info_flutter/wifi_info_flutter/ios/Classes/WifiInfoFlutterPlugin.h b/packages/wifi_info_flutter/wifi_info_flutter/ios/Classes/WifiInfoFlutterPlugin.h deleted file mode 100644 index 41f165717809..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/ios/Classes/WifiInfoFlutterPlugin.h +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -@interface WifiInfoFlutterPlugin : NSObject -@end diff --git a/packages/wifi_info_flutter/wifi_info_flutter/ios/Classes/WifiInfoFlutterPlugin.m b/packages/wifi_info_flutter/wifi_info_flutter/ios/Classes/WifiInfoFlutterPlugin.m deleted file mode 100644 index 47bd90c4429b..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/ios/Classes/WifiInfoFlutterPlugin.m +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "WifiInfoFlutterPlugin.h" - -#import -#import "FLTWifiInfoLocationHandler.h" -#import "SystemConfiguration/CaptiveNetwork.h" - -#include - -#include - -@interface WifiInfoFlutterPlugin () -@property(strong, nonatomic) FLTWifiInfoLocationHandler* locationHandler; -@end - -@implementation WifiInfoFlutterPlugin -+ (void)registerWithRegistrar:(NSObject*)registrar { - WifiInfoFlutterPlugin* instance = [[WifiInfoFlutterPlugin alloc] init]; - - FlutterMethodChannel* channel = - [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/wifi_info_flutter" - binaryMessenger:[registrar messenger]]; - [registrar addMethodCallDelegate:instance channel:channel]; -} - -- (NSString*)findNetworkInfo:(NSString*)key { - NSString* info = nil; - NSArray* interfaceNames = (__bridge_transfer id)CNCopySupportedInterfaces(); - for (NSString* interfaceName in interfaceNames) { - NSDictionary* networkInfo = - (__bridge_transfer id)CNCopyCurrentNetworkInfo((__bridge CFStringRef)interfaceName); - if (networkInfo[key]) { - info = networkInfo[key]; - } - } - return info; -} - -- (NSString*)getWifiName { - return [self findNetworkInfo:@"SSID"]; -} - -- (NSString*)getBSSID { - return [self findNetworkInfo:@"BSSID"]; -} - -- (NSString*)getWifiIP { - NSString* address = @"error"; - struct ifaddrs* interfaces = NULL; - struct ifaddrs* temp_addr = NULL; - int success = 0; - - // retrieve the current interfaces - returns 0 on success - success = getifaddrs(&interfaces); - if (success == 0) { - // Loop through linked list of interfaces - temp_addr = interfaces; - while (temp_addr != NULL) { - if (temp_addr->ifa_addr->sa_family == AF_INET) { - // Check if interface is en0 which is the wifi connection on the iPhone - if ([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) { - // Get NSString from C String - address = [NSString - stringWithUTF8String:inet_ntoa(((struct sockaddr_in*)temp_addr->ifa_addr)->sin_addr)]; - } - } - - temp_addr = temp_addr->ifa_next; - } - } - - // Free memory - freeifaddrs(interfaces); - - return address; -} - -- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { - if ([call.method isEqualToString:@"wifiName"]) { - result([self getWifiName]); - } else if ([call.method isEqualToString:@"wifiBSSID"]) { - result([self getBSSID]); - } else if ([call.method isEqualToString:@"wifiIPAddress"]) { - result([self getWifiIP]); - } else if ([call.method isEqualToString:@"getLocationServiceAuthorization"]) { - result([self convertCLAuthorizationStatusToString:[FLTWifiInfoLocationHandler - locationAuthorizationStatus]]); - } else if ([call.method isEqualToString:@"requestLocationServiceAuthorization"]) { - NSArray* arguments = call.arguments; - BOOL always = [arguments.firstObject boolValue]; - __weak typeof(self) weakSelf = self; - [self.locationHandler - requestLocationAuthorization:always - completion:^(CLAuthorizationStatus status) { - result([weakSelf convertCLAuthorizationStatusToString:status]); - }]; - } else { - result(FlutterMethodNotImplemented); - } -} - -- (NSString*)convertCLAuthorizationStatusToString:(CLAuthorizationStatus)status { - switch (status) { - case kCLAuthorizationStatusNotDetermined: { - return @"notDetermined"; - } - case kCLAuthorizationStatusRestricted: { - return @"restricted"; - } - case kCLAuthorizationStatusDenied: { - return @"denied"; - } - case kCLAuthorizationStatusAuthorizedAlways: { - return @"authorizedAlways"; - } - case kCLAuthorizationStatusAuthorizedWhenInUse: { - return @"authorizedWhenInUse"; - } - default: { - return @"unknown"; - } - } -} - -- (FLTWifiInfoLocationHandler*)locationHandler { - if (!_locationHandler) { - _locationHandler = [FLTWifiInfoLocationHandler new]; - } - return _locationHandler; -} -@end diff --git a/packages/wifi_info_flutter/wifi_info_flutter/ios/wifi_info_flutter.podspec b/packages/wifi_info_flutter/wifi_info_flutter/ios/wifi_info_flutter.podspec deleted file mode 100644 index c3b3416ad767..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/ios/wifi_info_flutter.podspec +++ /dev/null @@ -1,24 +0,0 @@ -# -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. -# Run `pod lib lint wifi_info_flutter.podspec' to validate before publishing. -# -Pod::Spec.new do |s| - s.name = 'wifi_info_flutter' - s.version = '0.0.1' - s.summary = 'A wifi information plugin for Flutter.' - s.description = <<-DESC -A Flutter plugin for retrieving wifi information from a device. - DESC - s.homepage = 'https://github.com/flutter/plugins' - s.license = { :type => 'BSD', :file => '../LICENSE' } - s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/master' } - s.documentation_url = 'https://pub.dev' - s.source_files = 'Classes/**/*' - s.public_header_files = 'Classes/**/*.h' - s.dependency 'Flutter' - s.platform = :ios, '8.0' - - # Flutter.framework does not contain a i386 slice. - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } -end diff --git a/packages/wifi_info_flutter/wifi_info_flutter/lib/wifi_info_flutter.dart b/packages/wifi_info_flutter/wifi_info_flutter/lib/wifi_info_flutter.dart deleted file mode 100644 index 1c89ee82fc3e..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/lib/wifi_info_flutter.dart +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; - -import 'package:flutter/services.dart'; -import 'package:wifi_info_flutter_platform_interface/wifi_info_flutter_platform_interface.dart'; - -// Export enums from the platform_interface so plugin users can use them directly. -export 'package:wifi_info_flutter_platform_interface/wifi_info_flutter_platform_interface.dart' - show LocationAuthorizationStatus; - -/// Checks WI-FI status and more. -class WifiInfo { - WifiInfo._(); - - /// Constructs a singleton instance of [WifiInfo]. - /// - /// [WifiInfo] is designed to work as a singleton. - factory WifiInfo() => _singleton; - - static final WifiInfo _singleton = WifiInfo._(); - - static WifiInfoFlutterPlatform get _platform => - WifiInfoFlutterPlatform.instance; - - /// Obtains the wifi name (SSID) of the connected network - /// - /// Please note that it DOESN'T WORK on emulators (returns null). - /// - /// From android 8.0 onwards the GPS must be ON (high accuracy) - /// in order to be able to obtain the SSID. - Future getWifiName() { - return _platform.getWifiName(); - } - - /// Obtains the wifi BSSID of the connected network. - /// - /// Please note that it DOESN'T WORK on emulators (returns null). - /// - /// From Android 8.0 onwards the GPS must be ON (high accuracy) - /// in order to be able to obtain the BSSID. - Future getWifiBSSID() { - return _platform.getWifiBSSID(); - } - - /// Obtains the IP address of the connected wifi network - Future getWifiIP() { - return _platform.getWifiIP(); - } - - /// Request to authorize the location service (Only on iOS). - /// - /// This method will throw a [PlatformException] on Android. - /// - /// Returns a [LocationAuthorizationStatus] after user authorized or denied the location on this request. - /// - /// If the location information needs to be accessible all the time, set `requestAlwaysLocationUsage` to true. If user has - /// already granted a [LocationAuthorizationStatus.authorizedWhenInUse] prior to requesting an "always" access, it will return [LocationAuthorizationStatus.denied]. - /// - /// If the location service authorization is not determined prior to making this call, a platform standard UI of requesting a location service will pop up. - /// This UI will only show once unless the user re-install the app to their phone which resets the location service authorization to not determined. - /// - /// This method is a helper to get the location authorization that is necessary for certain functionality of this plugin. - /// It can be replaced with other permission handling code/plugin if preferred. - /// To request location authorization, make sure to add the following keys to your _Info.plist_ file, located in `/ios/Runner/Info.plist`: - /// * `NSLocationAlwaysAndWhenInUseUsageDescription` - describe why the app needs access to the user’s location information - /// all the time (foreground and background). This is called _Privacy - Location Always and When In Use Usage Description_ in the visual editor. - /// * `NSLocationWhenInUseUsageDescription` - describe why the app needs access to the user’s location information when the app is - /// running in the foreground. This is called _Privacy - Location When In Use Usage Description_ in the visual editor. - /// - /// Starting from iOS 13, `getWifiBSSID` and `getWifiIP` will only work properly if: - /// - /// * The app uses Core Location, and has the user’s authorization to use location information. - /// * The app uses the NEHotspotConfiguration API to configure the current Wi-Fi network. - /// * The app has active VPN configurations installed. - /// - /// If the app falls into the first category, call this method before calling `getWifiBSSID` or `getWifiIP`. - /// For example, - /// ```dart - /// if (Platform.isIOS) { - /// LocationAuthorizationStatus status = await _connectivity.getLocationServiceAuthorization(); - /// if (status == LocationAuthorizationStatus.notDetermined) { - /// status = await _connectivity.requestLocationServiceAuthorization(); - /// } - /// if (status == LocationAuthorizationStatus.authorizedAlways || status == LocationAuthorizationStatus.authorizedWhenInUse) { - /// wifiBSSID = await _connectivity.getWifiName(); - /// } else { - /// print('location service is not authorized, the data might not be correct'); - /// wifiBSSID = await _connectivity.getWifiName(); - /// } - /// } else { - /// wifiBSSID = await _connectivity.getWifiName(); - /// } - /// ``` - /// - /// Ideally, a location service authorization should only be requested if the current authorization status is not determined. - /// - /// See also [getLocationServiceAuthorization] to obtain current location service status. - Future requestLocationServiceAuthorization({ - bool requestAlwaysLocationUsage = false, - }) { - return _platform.requestLocationServiceAuthorization( - requestAlwaysLocationUsage: requestAlwaysLocationUsage, - ); - } - - /// Get the current location service authorization (Only on iOS). - /// - /// This method will throw a [PlatformException] on Android. - /// - /// Returns a [LocationAuthorizationStatus]. - /// If the returned value is [LocationAuthorizationStatus.notDetermined], a subsequent [requestLocationServiceAuthorization] call - /// can request the authorization. - /// If the returned value is not [LocationAuthorizationStatus.notDetermined], a subsequent [requestLocationServiceAuthorization] - /// will not initiate another request. It will instead return the "determined" status. - /// - /// This method is a helper to get the location authorization that is necessary for certain functionality of this plugin. - /// It can be replaced with other permission handling code/plugin if preferred. - /// - /// Starting from iOS 13, `getWifiBSSID` and `getWifiIP` will only work properly if: - /// - /// * The app uses Core Location, and has the user’s authorization to use location information. - /// * The app uses the NEHotspotConfiguration API to configure the current Wi-Fi network. - /// * The app has active VPN configurations installed. - /// - /// If the app falls into the first category, call this method before calling `getWifiBSSID` or `getWifiIP`. - /// For example, - /// ```dart - /// if (Platform.isIOS) { - /// LocationAuthorizationStatus status = await _connectivity.getLocationServiceAuthorization(); - /// if (status == LocationAuthorizationStatus.authorizedAlways || status == LocationAuthorizationStatus.authorizedWhenInUse) { - /// wifiBSSID = await _connectivity.getWifiName(); - /// } else { - /// print('location service is not authorized, the data might not be correct'); - /// wifiBSSID = await _connectivity.getWifiName(); - /// } - /// } else { - /// wifiBSSID = await _connectivity.getWifiName(); - /// } - /// ``` - /// - /// See also [requestLocationServiceAuthorization] for requesting a location service authorization. - Future getLocationServiceAuthorization() { - return _platform.getLocationServiceAuthorization(); - } -} diff --git a/packages/wifi_info_flutter/wifi_info_flutter/pubspec.yaml b/packages/wifi_info_flutter/wifi_info_flutter/pubspec.yaml deleted file mode 100644 index b1e1e756c633..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/pubspec.yaml +++ /dev/null @@ -1,27 +0,0 @@ -name: wifi_info_flutter -description: A Flutter plugin to get WiFi information such as connection status and network identifiers. -repository: https://github.com/flutter/plugins/tree/master/packages/wifi_info_flutter/wifi_info_flutter -issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+wifi_info_flutter%22 -version: 2.0.2 - -environment: - sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" - -flutter: - plugin: - platforms: - android: - package: io.flutter.plugins.wifi_info_flutter - pluginClass: WifiInfoFlutterPlugin - ios: - pluginClass: WifiInfoFlutterPlugin - -dependencies: - flutter: - sdk: flutter - wifi_info_flutter_platform_interface: ^2.0.0 - -dev_dependencies: - flutter_test: - sdk: flutter diff --git a/packages/wifi_info_flutter/wifi_info_flutter/test/wifi_info_flutter_test.dart b/packages/wifi_info_flutter/wifi_info_flutter/test/wifi_info_flutter_test.dart deleted file mode 100644 index 93cf378de437..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter/test/wifi_info_flutter_test.dart +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:wifi_info_flutter/wifi_info_flutter.dart'; -import 'package:wifi_info_flutter_platform_interface/wifi_info_flutter_platform_interface.dart'; -import 'package:flutter_test/flutter_test.dart'; - -const String kWifiNameResult = '1337wifi'; -const String kWifiBSSIDResult = 'c0:ff:33:c0:d3:55'; -const String kWifiIpAddressResult = '127.0.0.1'; -const LocationAuthorizationStatus kRequestLocationResult = - LocationAuthorizationStatus.authorizedAlways; -const LocationAuthorizationStatus kGetLocationResult = - LocationAuthorizationStatus.authorizedAlways; - -void main() { - group('$WifiInfo', () { - late WifiInfo wifiInfo; - MockWifiInfoFlutterPlatform fakePlatform; - - setUp(() async { - fakePlatform = MockWifiInfoFlutterPlatform(); - WifiInfoFlutterPlatform.instance = fakePlatform; - wifiInfo = WifiInfo(); - }); - - test('getWifiName', () async { - String? result = await wifiInfo.getWifiName(); - expect(result, kWifiNameResult); - }); - - test('getWifiBSSID', () async { - String? result = await wifiInfo.getWifiBSSID(); - expect(result, kWifiBSSIDResult); - }); - - test('getWifiIP', () async { - String? result = await wifiInfo.getWifiIP(); - expect(result, kWifiIpAddressResult); - }); - - test('requestLocationServiceAuthorization', () async { - LocationAuthorizationStatus result = - await wifiInfo.requestLocationServiceAuthorization(); - expect(result, kRequestLocationResult); - }); - - test('getLocationServiceAuthorization', () async { - LocationAuthorizationStatus result = - await wifiInfo.getLocationServiceAuthorization(); - expect(result, kRequestLocationResult); - }); - }); -} - -class MockWifiInfoFlutterPlatform extends WifiInfoFlutterPlatform { - @override - Future getWifiName() async { - return kWifiNameResult; - } - - @override - Future getWifiBSSID() async { - return kWifiBSSIDResult; - } - - @override - Future getWifiIP() async { - return kWifiIpAddressResult; - } - - @override - Future requestLocationServiceAuthorization({ - bool requestAlwaysLocationUsage = false, - }) async { - return kRequestLocationResult; - } - - @override - Future getLocationServiceAuthorization() async { - return kGetLocationResult; - } -} diff --git a/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/.metadata b/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/.metadata deleted file mode 100644 index a6d65e2f3343..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/.metadata +++ /dev/null @@ -1,10 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: 4513e96a3022d70aa7686906c2e9bdfbbc448334 - channel: master - -project_type: package diff --git a/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/AUTHORS b/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/AUTHORS deleted file mode 100644 index 493a0b4ef9c2..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/AUTHORS +++ /dev/null @@ -1,66 +0,0 @@ -# Below is a list of people and organizations that have contributed -# to the Flutter project. Names should be added to the list like so: -# -# Name/Organization - -Google Inc. -The Chromium Authors -German Saprykin -Benjamin Sauer -larsenthomasj@gmail.com -Ali Bitek -Pol Batlló -Anatoly Pulyaevskiy -Hayden Flinner -Stefano Rodriguez -Salvatore Giordano -Brian Armstrong -Paul DeMarco -Fabricio Nogueira -Simon Lightfoot -Ashton Thomas -Thomas Danner -Diego Velásquez -Hajime Nakamura -Tuyển Vũ Xuân -Miguel Ruivo -Sarthak Verma -Mike Diarmid -Invertase -Elliot Hesp -Vince Varga -Aawaz Gyawali -EUI Limited -Katarina Sheremet -Thomas Stockx -Sarbagya Dhaubanjar -Ozkan Eksi -Rishab Nayak -ko2ic -Jonathan Younger -Jose Sanchez -Debkanchan Samadder -Audrius Karosevicius -Lukasz Piliszczuk -SoundReply Solutions GmbH -Rafal Wachol -Pau Picas -Christian Weder -Alexandru Tuca -Christian Weder -Rhodes Davis Jr. -Luigi Agosti -Quentin Le Guennec -Koushik Ravikumar -Nissim Dsilva -Giancarlo Rocha -Ryo Miyake -Théo Champion -Kazuki Yamaguchi -Eitan Schwartz -Chris Rutkowski -Juan Alvarez -Aleksandr Yurkovskiy -Anton Borries -Alex Li -Rahul Raj <64.rahulraj@gmail.com> diff --git a/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/CHANGELOG.md b/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/CHANGELOG.md deleted file mode 100644 index 34f8e84cd780..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/CHANGELOG.md +++ /dev/null @@ -1,16 +0,0 @@ -## 2.0.1 - -* Update platform_plugin_interface version requirement. - -## 2.0.0 - -* Migrate to null safety. - -## 1.0.1 - -* Update Flutter SDK constraint. - -## 1.0.0 - -* Initial release of package. Includes support for retrieving wifi name, wifi BSSID, wifi ip address -and requesting location service authorization. diff --git a/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/LICENSE b/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/LICENSE deleted file mode 100644 index c6823b81eb84..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright 2013 The Flutter Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/README.md b/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/README.md deleted file mode 100644 index f2039c3d5865..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# wifi_info_flutter_platform_interface - -A common platform interface for the [`wifi_info_flutter`][1] plugin. - -This interface allows platform-specific implementations of the `wifi_info_flutter` -plugin, as well as the plugin itself, to ensure they are supporting the -same interface. - -# Usage - -To implement a new platform-specific implementation of `wifi_info_flutter`, extend -[`WifiInfoFlutterPlatform`][2] with an implementation that performs the -platform-specific behavior, and when you register your plugin, set the default -`WifiInfoFlutterPlatform` by calling -`WifiInfoFlutterPlatform.instance = MyPlatformWifiInfoFlutter()`. - -# Note on breaking changes - -Strongly prefer non-breaking changes (such as adding a method to the interface) -over breaking changes for this package. - -See https://flutter.dev/go/platform-interface-breaking-changes for a discussion -on why a less-clean interface is preferable to a breaking change. - -[1]: ../ -[2]: lib/wifi_info_flutter_platform_interface.dart diff --git a/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/lib/src/enums.dart b/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/lib/src/enums.dart deleted file mode 100644 index d5f05e6121a9..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/lib/src/enums.dart +++ /dev/null @@ -1,24 +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. - -/// The status of the location service authorization. -enum LocationAuthorizationStatus { - /// The authorization of the location service is not determined. - notDetermined, - - /// This app is not authorized to use location. - restricted, - - /// User explicitly denied the location service. - denied, - - /// User authorized the app to access the location at any time. - authorizedAlways, - - /// User authorized the app to access the location when the app is visible to them. - authorizedWhenInUse, - - /// Status unknown. - unknown -} diff --git a/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/lib/src/method_channel_wifi_info_flutter.dart b/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/lib/src/method_channel_wifi_info_flutter.dart deleted file mode 100644 index 79f27e8cde44..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/lib/src/method_channel_wifi_info_flutter.dart +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; - -import '../wifi_info_flutter_platform_interface.dart'; - -/// An implementation of [WifiInfoFlutterPlatform] that uses method channels. -class MethodChannelWifiInfoFlutter extends WifiInfoFlutterPlatform { - /// The method channel used to interact with the native platform. - @visibleForTesting - MethodChannel methodChannel = - MethodChannel('plugins.flutter.io/wifi_info_flutter'); - - @override - Future getWifiName() async { - return methodChannel.invokeMethod('wifiName'); - } - - @override - Future getWifiBSSID() { - return methodChannel.invokeMethod('wifiBSSID'); - } - - @override - Future getWifiIP() { - return methodChannel.invokeMethod('wifiIPAddress'); - } - - @override - Future requestLocationServiceAuthorization({ - bool requestAlwaysLocationUsage = false, - }) { - return methodChannel.invokeMethod( - 'requestLocationServiceAuthorization', [ - requestAlwaysLocationUsage - ]).then(_parseLocationAuthorizationStatus); - } - - @override - Future getLocationServiceAuthorization() { - return methodChannel - .invokeMethod('getLocationServiceAuthorization') - .then(_parseLocationAuthorizationStatus); - } -} - -/// Convert a String to a LocationAuthorizationStatus value. -LocationAuthorizationStatus _parseLocationAuthorizationStatus(String? result) { - return LocationAuthorizationStatus.values.firstWhere( - (LocationAuthorizationStatus status) => result == describeEnum(status), - orElse: () => LocationAuthorizationStatus.unknown, - ); -} diff --git a/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/lib/wifi_info_flutter_platform_interface.dart b/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/lib/wifi_info_flutter_platform_interface.dart deleted file mode 100644 index 62330d4261a0..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/lib/wifi_info_flutter_platform_interface.dart +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; - -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -import 'src/enums.dart'; -import 'src/method_channel_wifi_info_flutter.dart'; - -export 'src/enums.dart'; - -/// The interface that implementations of wifi_info_flutter must implement. -/// -/// Platform implementations should extend this class rather than implement it -/// as `wifi_info_flutter` does not consider newly added methods to be breaking -/// changes. Extending this class (using `extends`) ensures that the subclass -/// will get the default implementation, while platform implementations that -/// `implements` this interface will be broken by newly added -/// [ConnectivityPlatform] methods. -abstract class WifiInfoFlutterPlatform extends PlatformInterface { - /// Constructs a WifiInfoFlutterPlatform. - WifiInfoFlutterPlatform() : super(token: _token); - - static final Object _token = Object(); - - static WifiInfoFlutterPlatform _instance = MethodChannelWifiInfoFlutter(); - - /// The default instance of [WifiInfoFlutterPlatform] to use. - /// - /// Defaults to [MethodChannelWifiInfoFlutter]. - static WifiInfoFlutterPlatform get instance => _instance; - - /// Set the default instance of [WifiInfoFlutterPlatform] to use. - /// - /// Platform-specific plugins should set this with their own platform-specific - /// class that extends [WifiInfoFlutterPlatform] when they register - /// themselves. - static set instance(WifiInfoFlutterPlatform instance) { - PlatformInterface.verifyToken(instance, _token); - _instance = instance; - } - - /// Obtains the wifi name (SSID) of the connected network - Future getWifiName() { - throw UnimplementedError('getWifiName() has not been implemented.'); - } - - /// Obtains the wifi BSSID of the connected network. - Future getWifiBSSID() { - throw UnimplementedError('getWifiBSSID() has not been implemented.'); - } - - /// Obtains the IP address of the connected wifi network - Future getWifiIP() { - throw UnimplementedError('getWifiIP() has not been implemented.'); - } - - /// Request to authorize the location service (Only on iOS). - Future requestLocationServiceAuthorization( - {bool requestAlwaysLocationUsage = false}) { - throw UnimplementedError( - 'requestLocationServiceAuthorization() has not been implemented.', - ); - } - - /// Get the current location service authorization (Only on iOS). - Future getLocationServiceAuthorization() { - throw UnimplementedError( - 'getLocationServiceAuthorization() has not been implemented.', - ); - } -} diff --git a/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/pubspec.yaml b/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/pubspec.yaml deleted file mode 100644 index 14ca643aa045..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/pubspec.yaml +++ /dev/null @@ -1,21 +0,0 @@ -name: wifi_info_flutter_platform_interface -description: A common platform interface for the wifi_info_flutter plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/wifi_info_flutter/wifi_info_flutter_platform_interface -issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+wifi_info_flutter%22 -version: 2.0.1 -# NOTE: We strongly prefer non-breaking changes, even at the expense of a -# less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes - -environment: - sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.17.0" - -dependencies: - plugin_platform_interface: ^2.0.0 - flutter: - sdk: flutter - -dev_dependencies: - pedantic: ^1.10.0 - flutter_test: - sdk: flutter diff --git a/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/test/method_channel_wifi_info_flutter_test.dart b/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/test/method_channel_wifi_info_flutter_test.dart deleted file mode 100644 index 875f2ab4089a..000000000000 --- a/packages/wifi_info_flutter/wifi_info_flutter_platform_interface/test/method_channel_wifi_info_flutter_test.dart +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:wifi_info_flutter_platform_interface/src/enums.dart'; -import 'package:wifi_info_flutter_platform_interface/src/method_channel_wifi_info_flutter.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - group('$MethodChannelWifiInfoFlutter', () { - final List log = []; - late MethodChannelWifiInfoFlutter methodChannelWifiInfoFlutter; - - setUp(() async { - methodChannelWifiInfoFlutter = MethodChannelWifiInfoFlutter(); - - methodChannelWifiInfoFlutter.methodChannel - .setMockMethodCallHandler((MethodCall methodCall) async { - log.add(methodCall); - switch (methodCall.method) { - case 'wifiName': - return '1337wifi'; - case 'wifiBSSID': - return 'c0:ff:33:c0:d3:55'; - case 'wifiIPAddress': - return '127.0.0.1'; - case 'requestLocationServiceAuthorization': - return 'authorizedAlways'; - case 'getLocationServiceAuthorization': - return 'authorizedAlways'; - default: - return null; - } - }); - log.clear(); - }); - - test('getWifiName', () async { - final String? result = await methodChannelWifiInfoFlutter.getWifiName(); - expect(result, '1337wifi'); - expect( - log, - [ - isMethodCall( - 'wifiName', - arguments: null, - ), - ], - ); - }); - - test('getWifiBSSID', () async { - final String? result = await methodChannelWifiInfoFlutter.getWifiBSSID(); - expect(result, 'c0:ff:33:c0:d3:55'); - expect( - log, - [ - isMethodCall( - 'wifiBSSID', - arguments: null, - ), - ], - ); - }); - - test('getWifiIP', () async { - final String? result = await methodChannelWifiInfoFlutter.getWifiIP(); - expect(result, '127.0.0.1'); - expect( - log, - [ - isMethodCall( - 'wifiIPAddress', - arguments: null, - ), - ], - ); - }); - - test('requestLocationServiceAuthorization', () async { - final LocationAuthorizationStatus result = - await methodChannelWifiInfoFlutter - .requestLocationServiceAuthorization(); - expect(result, LocationAuthorizationStatus.authorizedAlways); - expect( - log, - [ - isMethodCall( - 'requestLocationServiceAuthorization', - arguments: [false], - ), - ], - ); - }); - - test('getLocationServiceAuthorization', () async { - final LocationAuthorizationStatus result = - await methodChannelWifiInfoFlutter.getLocationServiceAuthorization(); - expect(result, LocationAuthorizationStatus.authorizedAlways); - expect( - log, - [ - isMethodCall( - 'getLocationServiceAuthorization', - arguments: null, - ), - ], - ); - }); - }); -} diff --git a/script/configs/custom_analysis.yaml b/script/configs/custom_analysis.yaml index c1c4a8d3f09c..81bb0f5db59c 100644 --- a/script/configs/custom_analysis.yaml +++ b/script/configs/custom_analysis.yaml @@ -23,16 +23,3 @@ - ios_platform_images - local_auth - video_player - -# These plugins are deprecated in favor of the Community Plus versions, and -# will be removed from the repo once the critical support window has passed, -# so are not worth updating. -- android_alarm_manager -- android_intent -- battery -- connectivity -- device_info -- package_info -- sensors -- share -- wifi_info_flutter diff --git a/script/configs/exclude_integration_android.yaml b/script/configs/exclude_integration_android.yaml index e06b062fd68f..653f3516727b 100644 --- a/script/configs/exclude_integration_android.yaml +++ b/script/configs/exclude_integration_android.yaml @@ -1,10 +1,2 @@ -# Deprecated; no plan to backfill the missing files -- android_intent -- connectivity/connectivity -- device_info/device_info -- sensors -- share -- wifi_info_flutter/wifi_info_flutter - # No integration tests to run: - espresso diff --git a/script/configs/exclude_integration_ios.yaml b/script/configs/exclude_integration_ios.yaml index a30dffbafde6..06283ae59c16 100644 --- a/script/configs/exclude_integration_ios.yaml +++ b/script/configs/exclude_integration_ios.yaml @@ -2,5 +2,3 @@ - in_app_purchase_storekit # Currently missing: https://github.com/flutter/flutter/issues/82208 - ios_platform_images -# Hangs on CI. Deprecated, so there is no plan to fix it. -- sensors diff --git a/script/configs/exclude_native_ios.yaml b/script/configs/exclude_native_ios.yaml deleted file mode 100644 index 723fcfa64715..000000000000 --- a/script/configs/exclude_native_ios.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# Deprecated; no plan to backfill the missing files -- battery -- connectivity/connectivity -- device_info/device_info -- package_info -- sensors -- wifi_info_flutter/wifi_info_flutter diff --git a/script/configs/exclude_native_macos.yaml b/script/configs/exclude_native_macos.yaml deleted file mode 100644 index 8a817a9c0178..000000000000 --- a/script/configs/exclude_native_macos.yaml +++ /dev/null @@ -1,3 +0,0 @@ -# Deprecated plugins that will not be getting unit test backfill. -- connectivity_macos -- package_info diff --git a/script/configs/exclude_native_unit_android.yaml b/script/configs/exclude_native_unit_android.yaml index 5ec80eee73a0..45197b94962f 100644 --- a/script/configs/exclude_native_unit_android.yaml +++ b/script/configs/exclude_native_unit_android.yaml @@ -1,11 +1,2 @@ -# Deprecated; no plan to backfill the missing files -- android_alarm_manager -- battery -- device_info/device_info -- package_info -- sensors -- share -- wifi_info_flutter/wifi_info_flutter - # No need for unit tests: - espresso diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 234700ab5a7d..dec218e2ba24 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -5,6 +5,9 @@ dependencies to path-based dependencies. - Adds a (hidden) `--run-on-dirty-packages` flag for use with `make-deps-path-based` in CI. +- Fix `federation-safety-check` handling of plugin deletion, and of top-level + files in unfederated plugins whose names match federated plugin heuristics + (e.g., `packages/foo/foo_android.iml`). ## 0.7.3 diff --git a/script/tool/lib/src/federation_safety_check_command.dart b/script/tool/lib/src/federation_safety_check_command.dart index 200f9c3f48cb..df9d86892e13 100644 --- a/script/tool/lib/src/federation_safety_check_command.dart +++ b/script/tool/lib/src/federation_safety_check_command.dart @@ -81,7 +81,8 @@ class FederationSafetyCheckCommand extends PackageLoopingCommand { // Count the top-level plugin as changed. _changedPlugins.add(packageName); if (relativeComponents[0] == packageName || - relativeComponents[0].startsWith('${packageName}_')) { + (relativeComponents.length > 1 && + relativeComponents[0].startsWith('${packageName}_'))) { packageName = relativeComponents.removeAt(0); } @@ -178,6 +179,10 @@ class FederationSafetyCheckCommand extends PackageLoopingCommand { String pubspecRepoRelativePosixPath) async { final File pubspecFile = childFileWithSubcomponents( packagesDir.parent, p.posix.split(pubspecRepoRelativePosixPath)); + if (!pubspecFile.existsSync()) { + // If the package was deleted, nothing will be published. + return false; + } final Pubspec pubspec = Pubspec.parse(pubspecFile.readAsStringSync()); if (pubspec.publishTo == 'none') { return false; diff --git a/script/tool/lib/src/make_deps_path_based_command.dart b/script/tool/lib/src/make_deps_path_based_command.dart index 04869639cf74..5ee42848a81b 100644 --- a/script/tool/lib/src/make_deps_path_based_command.dart +++ b/script/tool/lib/src/make_deps_path_based_command.dart @@ -196,19 +196,27 @@ dependency_overrides: Future> _getNonBreakingUpdatePackages() async { final GitVersionFinder gitVersionFinder = await retrieveVersionFinder(); final String baseSha = await gitVersionFinder.getBaseSha(); - print('Finding changed packages relative to "$baseSha"\n'); + print('Finding changed packages relative to "$baseSha"...'); final Set changedPackages = {}; - for (final String path in await gitVersionFinder.getChangedFiles()) { + for (final String changedPath in await gitVersionFinder.getChangedFiles()) { // Git output always uses Posix paths. - final List allComponents = p.posix.split(path); + final List allComponents = p.posix.split(changedPath); // Only pubspec changes are potential publishing events. if (allComponents.last != 'pubspec.yaml' || allComponents.contains('example')) { continue; } final RepositoryPackage package = - RepositoryPackage(packagesDir.fileSystem.file(path).parent); + RepositoryPackage(packagesDir.fileSystem.file(changedPath).parent); + // Ignored deleted packages, as they won't be published. + if (!package.pubspecFile.existsSync()) { + final String directoryName = p.posix.joinAll(path.split(path.relative( + package.directory.absolute.path, + from: packagesDir.path))); + print(' Skipping $directoryName; deleted.'); + continue; + } final String packageName = package.parsePubspec().name; if (!await _hasNonBreakingVersionChange(package)) { // Log packages that had pubspec changes but weren't included for ease diff --git a/script/tool/test/federation_safety_check_command_test.dart b/script/tool/test/federation_safety_check_command_test.dart index e23485fbc8b7..126aa8b41c83 100644 --- a/script/tool/test/federation_safety_check_command_test.dart +++ b/script/tool/test/federation_safety_check_command_test.dart @@ -352,4 +352,64 @@ void main() { ]), ); }); + + test('handles top-level files that match federated package heuristics', + () async { + final Directory plugin = createFakePlugin('foo', packagesDir); + + final String changedFileOutput = [ + // This should be picked up as a change to 'foo', and not crash. + plugin.childFile('foo_bar.baz'), + ].map((File file) => file.path).join('\n'); + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: changedFileOutput), + ]; + + final List output = + await runCapturingPrint(runner, ['federation-safety-check']); + + expect( + output, + containsAllInOrder([ + contains('Running for foo...'), + ]), + ); + }); + + test('handles deletion of an entire plugin', () async { + // Simulate deletion, in the form of diffs for packages that don't exist in + // the filesystem. + final String changedFileOutput = [ + packagesDir.childDirectory('foo').childFile('pubspec.yaml'), + packagesDir + .childDirectory('foo') + .childDirectory('lib') + .childFile('foo.dart'), + packagesDir + .childDirectory('foo_platform_interface') + .childFile('pubspec.yaml'), + packagesDir + .childDirectory('foo_platform_interface') + .childDirectory('lib') + .childFile('foo.dart'), + packagesDir.childDirectory('foo_web').childFile('pubspec.yaml'), + packagesDir + .childDirectory('foo_web') + .childDirectory('lib') + .childFile('foo.dart'), + ].map((File file) => file.path).join('\n'); + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: changedFileOutput), + ]; + + final List output = + await runCapturingPrint(runner, ['federation-safety-check']); + + expect( + output, + containsAllInOrder([ + contains('Ran for 0 package(s)'), + ]), + ); + }); } diff --git a/script/tool/test/make_deps_path_based_command_test.dart b/script/tool/test/make_deps_path_based_command_test.dart index 29e4f724b338..bdd2139b237c 100644 --- a/script/tool/test/make_deps_path_based_command_test.dart +++ b/script/tool/test/make_deps_path_based_command_test.dart @@ -173,6 +173,29 @@ void main() { ); }); + test('no-ops for no deleted packages', () async { + final String changedFileOutput = [ + // A change for a file that's not on disk simulates a deletion. + packagesDir.childDirectory('foo').childFile('pubspec.yaml'), + ].map((File file) => file.path).join('\n'); + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: changedFileOutput), + ]; + + final List output = await runCapturingPrint(runner, [ + 'make-deps-path-based', + '--target-dependencies-with-non-breaking-updates' + ]); + + expect( + output, + containsAllInOrder([ + contains('Skipping foo; deleted.'), + contains('No target dependencies'), + ]), + ); + }); + test('includes bugfix version changes as targets', () async { const String newVersion = '1.0.1'; final Directory package = From 5012b95815775ef14a4e0d160c77fc50111bc1d1 Mon Sep 17 00:00:00 2001 From: e-adrien Date: Mon, 6 Dec 2021 20:12:30 +0000 Subject: [PATCH 036/600] [webview_flutter] Add a backgroundColor option to the Android webview (#4569) This PR add an option to set the background color of the Android webview. Part of: https://github.com/flutter/plugins/pull/3431 Part of: https://github.com/flutter/flutter/issues/29300 --- .../webview_flutter_android/CHANGELOG.md | 11 ++-- .../GeneratedAndroidWebView.java | 33 ++++++++++ .../webviewflutter/WebViewHostApiImpl.java | 6 ++ .../BackgroundColorTest.java | 61 +++++++++++++++++++ .../android/app/src/debug/AndroidManifest.xml | 8 +++ .../DriverExtensionActivity.java | 16 +++++ .../example/lib/main.dart | 44 +++++++++++++ .../example/lib/web_view.dart | 8 +++ .../lib/src/android_webview.dart | 7 +++ .../lib/src/android_webview.pigeon.dart | 25 ++++++++ .../lib/src/android_webview_api_impls.dart | 5 ++ .../lib/webview_android_widget.dart | 5 ++ .../pigeons/android_webview.dart | 2 + .../webview_flutter_android/pubspec.yaml | 5 +- .../test/android_webview.pigeon.dart | 25 +++++++- .../test/android_webview_test.mocks.dart | 10 +++ .../webview_android_widget_test.mocks.dart | 18 ++++-- 17 files changed, 275 insertions(+), 14 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter_android/example/android/app/src/androidTest/java/io/flutter/plugins/webviewflutterexample/BackgroundColorTest.java create mode 100644 packages/webview_flutter/webview_flutter_android/example/android/app/src/main/java/io/flutter/plugins/webviewflutterexample/DriverExtensionActivity.java diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 479364ff14a7..caee5f7cb75a 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.5.0 + +* Adds an option to set the background color of the webview. + ## 2.4.0 * Adds support for Android's `WebView.loadData` and `WebView.loadDataWithBaseUrl` methods and implements the `loadFile` and `loadHtmlString` methods from the platform interface. @@ -27,13 +31,12 @@ when it is created without Hybrid Composition. ## 2.0.15 -* Added Overrides in FlutterWebView.java - +* Added Overrides in FlutterWebView.java + ## 2.0.14 -* Update example App so navigation menu loads immediatly but only becomes available when `WebViewController` is available (same behavior as example App in webview_flutter package). +* Update example App so navigation menu loads immediatly but only becomes available when `WebViewController` is available (same behavior as example App in webview_flutter package). ## 2.0.13 * Extract Android implementation from `webview_flutter`. - diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java index beb2d7105f22..a5632d351f83 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java @@ -223,6 +223,8 @@ void loadDataWithBaseUrl( void setWebChromeClient(Long instanceId, Long clientInstanceId); + void setBackgroundColor(Long instanceId, Long color); + /** The codec used by WebViewHostApi. */ static MessageCodec getCodec() { return WebViewHostApiCodec.INSTANCE; @@ -958,6 +960,37 @@ public void error(Throwable error) { channel.setMessageHandler(null); } } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.WebViewHostApi.setBackgroundColor", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number instanceIdArg = (Number) args.get(0); + if (instanceIdArg == null) { + throw new NullPointerException("instanceIdArg unexpectedly null."); + } + Number colorArg = (Number) args.get(1); + if (colorArg == null) { + throw new NullPointerException("colorArg unexpectedly null."); + } + api.setBackgroundColor(instanceIdArg.longValue(), colorArg.longValue()); + wrapped.put("result", null); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } } } 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 b557e9889704..78b06aac90b1 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 @@ -502,6 +502,12 @@ public void setWebChromeClient(Long instanceId, Long clientInstanceId) { webView.setWebChromeClient((WebChromeClient) instanceManager.getInstance(clientInstanceId)); } + @Override + public void setBackgroundColor(Long instanceId, Long color) { + final WebView webView = (WebView) instanceManager.getInstance(instanceId); + webView.setBackgroundColor(color.intValue()); + } + @Nullable private static String parseNullStringIdentifier(String value) { if (value.equals(nullStringIdentifier)) { diff --git a/packages/webview_flutter/webview_flutter_android/example/android/app/src/androidTest/java/io/flutter/plugins/webviewflutterexample/BackgroundColorTest.java b/packages/webview_flutter/webview_flutter_android/example/android/app/src/androidTest/java/io/flutter/plugins/webviewflutterexample/BackgroundColorTest.java new file mode 100644 index 000000000000..a63629e0b9e2 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_android/example/android/app/src/androidTest/java/io/flutter/plugins/webviewflutterexample/BackgroundColorTest.java @@ -0,0 +1,61 @@ +// 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.webviewflutterexample; + +import static androidx.test.espresso.flutter.EspressoFlutter.onFlutterWidget; +import static androidx.test.espresso.flutter.action.FlutterActions.click; +import static androidx.test.espresso.flutter.matcher.FlutterMatchers.withText; +import static androidx.test.espresso.flutter.matcher.FlutterMatchers.withValueKey; +import static org.junit.Assert.assertEquals; + +import android.graphics.Bitmap; +import android.graphics.Color; +import androidx.test.core.app.ActivityScenario; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; +import androidx.test.runner.screenshot.ScreenCapture; +import androidx.test.runner.screenshot.Screenshot; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class BackgroundColorTest { + @Rule + public ActivityTestRule myActivityTestRule = + new ActivityTestRule<>(DriverExtensionActivity.class, true, false); + + @Before + public void setUp() { + ActivityScenario.launch(DriverExtensionActivity.class); + } + + @Ignore("Doesn't run in Firebase Test Lab: https://github.com/flutter/flutter/issues/94748") + @Test + public void backgroundColor() { + onFlutterWidget(withValueKey("ShowPopupMenu")).perform(click()); + onFlutterWidget(withValueKey("ShowTransparentBackgroundExample")).perform(click()); + onFlutterWidget(withText("Transparent background test")); + + final ScreenCapture screenCapture = Screenshot.capture(); + final Bitmap screenBitmap = screenCapture.getBitmap(); + + final int centerLeftColor = + screenBitmap.getPixel(10, (int) Math.floor(screenBitmap.getHeight() / 2.0)); + final int centerColor = + screenBitmap.getPixel( + (int) Math.floor(screenBitmap.getWidth() / 2.0), + (int) Math.floor(screenBitmap.getHeight() / 2.0)); + + // Flutter Colors.green color : 0xFF4CAF50 + // https://github.com/flutter/flutter/blob/f4abaa0735eba4dfd8f33f73363911d63931fe03/packages/flutter/lib/src/material/colors.dart#L1208 + // The background color of the webview is : rgba(0, 0, 0, 0.5) + // The expected color is : rgba(38, 87, 40, 1) -> 0xFF265728 + assertEquals(0xFF265728, centerLeftColor); + assertEquals(Color.RED, centerColor); + } +} diff --git a/packages/webview_flutter/webview_flutter_android/example/android/app/src/debug/AndroidManifest.xml b/packages/webview_flutter/webview_flutter_android/example/android/app/src/debug/AndroidManifest.xml index 28792201bc36..110b9abe1cd8 100644 --- a/packages/webview_flutter/webview_flutter_android/example/android/app/src/debug/AndroidManifest.xml +++ b/packages/webview_flutter/webview_flutter_android/example/android/app/src/debug/AndroidManifest.xml @@ -13,5 +13,13 @@ android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize"> + + diff --git a/packages/webview_flutter/webview_flutter_android/example/android/app/src/main/java/io/flutter/plugins/webviewflutterexample/DriverExtensionActivity.java b/packages/webview_flutter/webview_flutter_android/example/android/app/src/main/java/io/flutter/plugins/webviewflutterexample/DriverExtensionActivity.java new file mode 100644 index 000000000000..59e1b04c37f2 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_android/example/android/app/src/main/java/io/flutter/plugins/webviewflutterexample/DriverExtensionActivity.java @@ -0,0 +1,16 @@ +// 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.webviewflutterexample; + +import androidx.annotation.NonNull; +import io.flutter.embedding.android.FlutterActivity; + +public class DriverExtensionActivity extends FlutterActivity { + @Override + @NonNull + public String getDartEntrypointFunctionName() { + return "appMain"; + } +} diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart index bc43c16d4498..0c04c8ca4004 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart @@ -9,6 +9,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_driver/driver_extension.dart'; import 'package:path_provider/path_provider.dart'; import 'package:webview_flutter_android/webview_surface_android.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; @@ -17,6 +18,11 @@ import 'navigation_decision.dart'; import 'navigation_request.dart'; import 'web_view.dart'; +void appMain() { + enableFlutterDriverExtension(); + main(); +} + void main() { // Configure the [WebView] to use the [SurfaceAndroidWebView] // implementation instead of the default [AndroidWebView]. @@ -59,6 +65,27 @@ const String kExamplePage = ''' '''; +const String kTransparentBackgroundPage = ''' + + + + Transparent background test + + + +
+

Transparent background test

+
+
+ + +'''; + class _WebViewExample extends StatefulWidget { const _WebViewExample({Key? key}) : super(key: key); @@ -73,6 +100,7 @@ class _WebViewExampleState extends State<_WebViewExample> { @override Widget build(BuildContext context) { return Scaffold( + backgroundColor: const Color(0xFF4CAF50), appBar: AppBar( title: const Text('Flutter WebView example'), // This drop down menu demonstrates that Flutter widgets can be shown over the web view. @@ -109,6 +137,7 @@ class _WebViewExampleState extends State<_WebViewExample> { javascriptChannels: _createJavascriptChannels(context), javascriptMode: JavascriptMode.unrestricted, userAgent: 'Custom_User_Agent', + backgroundColor: const Color(0x80000000), ); }), floatingActionButton: favoriteButton(), @@ -158,6 +187,7 @@ enum _MenuOptions { navigationDelegate, loadLocalFile, loadHtmlString, + transparentBackground, } class _SampleMenu extends StatelessWidget { @@ -172,6 +202,7 @@ class _SampleMenu extends StatelessWidget { builder: (BuildContext context, AsyncSnapshot controller) { return PopupMenuButton<_MenuOptions>( + key: const ValueKey('ShowPopupMenu'), onSelected: (_MenuOptions value) { switch (value) { case _MenuOptions.showUserAgent: @@ -201,6 +232,9 @@ class _SampleMenu extends StatelessWidget { case _MenuOptions.loadHtmlString: _onLoadHtmlStringExample(controller.data!, context); break; + case _MenuOptions.transparentBackground: + _onTransparentBackground(controller.data!, context); + break; } }, itemBuilder: (BuildContext context) => >[ @@ -241,6 +275,11 @@ class _SampleMenu extends StatelessWidget { value: _MenuOptions.loadLocalFile, child: Text('Load local file'), ), + const PopupMenuItem<_MenuOptions>( + key: ValueKey('ShowTransparentBackgroundExample'), + value: _MenuOptions.transparentBackground, + child: Text('Transparent background example'), + ), ], ); }, @@ -353,6 +392,11 @@ class _SampleMenu extends StatelessWidget { return indexFile.path; } + + Future _onTransparentBackground( + WebViewController controller, BuildContext context) async { + await controller.loadHtmlString(kTransparentBackgroundPage); + } } class _NavigationControls extends StatelessWidget { diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart index 654abbd960cd..395966bdd744 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart @@ -76,6 +76,7 @@ class WebView extends StatefulWidget { this.initialMediaPlaybackPolicy = AutoMediaPlaybackPolicy.require_user_action_for_all_media_types, this.allowsInlineMediaPlayback = false, + this.backgroundColor, }) : assert(javascriptMode != null), assert(initialMediaPlaybackPolicy != null), assert(allowsInlineMediaPlayback != null), @@ -236,6 +237,12 @@ class WebView extends StatefulWidget { /// The default policy is [AutoMediaPlaybackPolicy.require_user_action_for_all_media_types]. final AutoMediaPlaybackPolicy initialMediaPlaybackPolicy; + /// The background color of the [WebView]. + /// + /// When `null` the platform's webview default background color is used. By + /// default [backgroundColor] is `null`. + final Color? backgroundColor; + @override _WebViewState createState() => _WebViewState(); } @@ -287,6 +294,7 @@ class _WebViewState extends State { _javascriptChannelRegistry.channels.keys.toSet(), autoMediaPlaybackPolicy: widget.initialMediaPlaybackPolicy, userAgent: widget.userAgent, + backgroundColor: widget.backgroundColor, ), javascriptChannelRegistry: _javascriptChannelRegistry, ); diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart index 928cdb3099f1..ecd6f33d1e63 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:ui'; + import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart' show AndroidViewSurface; @@ -349,6 +351,11 @@ class WebView { return api.setWebChromeClientFromInstance(this, client); } + /// Sets the background color of this WebView. + Future setBackgroundColor(Color color) { + return api.setBackgroundColorFromInstance(this, color.value); + } + /// Releases all resources used by the [WebView]. /// /// Any methods called after [release] will throw an exception. diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart index 9bf2de6d169b..ae528a64bb8f 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart @@ -708,6 +708,31 @@ class WebViewHostApi { return; } } + + Future setBackgroundColor(int arg_instanceId, int arg_color) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WebViewHostApi.setBackgroundColor', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_color]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } } class _WebSettingsHostApiCodec extends StandardMessageCodec { diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart index ece05b56c182..51efdaadebdc 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart @@ -278,6 +278,11 @@ class WebViewHostApiImpl extends WebViewHostApi { instanceManager.getInstanceId(client)!, ); } + + /// Helper method to convert instances ids to objects. + Future setBackgroundColorFromInstance(WebView instance, int color) { + return setBackgroundColor(instanceManager.getInstanceId(instance)!, color); + } } /// Host api implementation for [WebSettings]. diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart index c264bc1b9e6e..0bfa04fde095 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart @@ -298,6 +298,11 @@ class WebViewAndroidPlatformController extends WebViewPlatformController { AutoMediaPlaybackPolicy.always_allow, ); + final Color? backgroundColor = creationParams.backgroundColor; + if (backgroundColor != null) { + webView.setBackgroundColor(backgroundColor); + } + addJavascriptChannels(creationParams.javascriptChannelNames); } diff --git a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart index 963216329237..0fdec2c17756 100644 --- a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart +++ b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart @@ -87,6 +87,8 @@ abstract class WebViewHostApi { void setDownloadListener(int instanceId, int listenerInstanceId); void setWebChromeClient(int instanceId, int clientInstanceId); + + void setBackgroundColor(int instanceId, int color); } @HostApi(dartHostTestHandler: 'TestWebSettingsHostApi') diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 8889469920df..75245a31e4c2 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_android description: A Flutter plugin that provides a WebView widget on Android. repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.4.0 +version: 2.5.0 environment: sdk: ">=2.14.0 <3.0.0" @@ -19,7 +19,7 @@ flutter: dependencies: flutter: sdk: flutter - webview_flutter_platform_interface: ^1.5.2 + webview_flutter_platform_interface: ^1.7.0 dev_dependencies: build_runner: ^2.1.4 @@ -30,4 +30,3 @@ dev_dependencies: mockito: ^5.0.16 pedantic: ^1.10.0 pigeon: 1.0.9 - diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart index 45988a0ae441..942e59a0b2b3 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart @@ -4,7 +4,7 @@ // Autogenerated from Pigeon (v1.0.9), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, avoid_relative_lib_imports, unnecessary_parenthesis +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import // @dart = 2.12 import 'dart:async'; import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; @@ -46,6 +46,7 @@ abstract class TestWebViewHostApi { void removeJavaScriptChannel(int instanceId, int javaScriptChannelInstanceId); void setDownloadListener(int instanceId, int listenerInstanceId); void setWebChromeClient(int instanceId, int clientInstanceId); + void setBackgroundColor(int instanceId, int color); static void setup(TestWebViewHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -577,6 +578,28 @@ abstract class TestWebViewHostApi { }); } } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WebViewHostApi.setBackgroundColor', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WebViewHostApi.setBackgroundColor was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WebViewHostApi.setBackgroundColor was null, expected non-null int.'); + final int? arg_color = (args[1] as int?); + assert(arg_color != null, + 'Argument for dev.flutter.pigeon.WebViewHostApi.setBackgroundColor was null, expected non-null int.'); + api.setBackgroundColor(arg_instanceId!, arg_color!); + return {}; + }); + } + } } } diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart index 90cbf2c4fefe..3c1cd610c136 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart @@ -3,6 +3,7 @@ // Do not manually edit this file. import 'dart:async' as _i4; +import 'dart:ui' as _i5; import 'package:mockito/mockito.dart' as _i1; import 'package:webview_flutter_android/src/android_webview.dart' as _i2; @@ -332,6 +333,10 @@ class MockTestWebViewHostApi extends _i1.Mock #setWebChromeClient, [instanceId, clientInstanceId]), returnValueForMissingStub: null); @override + void setBackgroundColor(int? instanceId, int? color) => super.noSuchMethod( + Invocation.method(#setBackgroundColor, [instanceId, color]), + returnValueForMissingStub: null); + @override String toString() => super.toString(); } @@ -485,6 +490,11 @@ class MockWebView extends _i1.Mock implements _i2.WebView { returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i4.Future); @override + _i4.Future setBackgroundColor(_i5.Color? color) => + (super.noSuchMethod(Invocation.method(#setBackgroundColor, [color]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override _i4.Future release() => (super.noSuchMethod(Invocation.method(#release, []), returnValue: Future.value(), diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart index 0582049acf6d..6ee53f9f9362 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart @@ -3,10 +3,11 @@ // Do not manually edit this file. import 'dart:async' as _i4; +import 'dart:ui' as _i5; import 'package:mockito/mockito.dart' as _i1; import 'package:webview_flutter_android/src/android_webview.dart' as _i2; -import 'package:webview_flutter_android/webview_android_widget.dart' as _i5; +import 'package:webview_flutter_android/webview_android_widget.dart' as _i6; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart' as _i3; @@ -229,6 +230,11 @@ class MockWebView extends _i1.Mock implements _i2.WebView { returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i4.Future); @override + _i4.Future setBackgroundColor(_i5.Color? color) => + (super.noSuchMethod(Invocation.method(#setBackgroundColor, [color]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override _i4.Future release() => (super.noSuchMethod(Invocation.method(#release, []), returnValue: Future.value(), @@ -241,7 +247,7 @@ class MockWebView extends _i1.Mock implements _i2.WebView { /// /// See the documentation for Mockito's code generation for more information. class MockWebViewAndroidDownloadListener extends _i1.Mock - implements _i5.WebViewAndroidDownloadListener { + implements _i6.WebViewAndroidDownloadListener { MockWebViewAndroidDownloadListener() { _i1.throwOnMissingStub(this); } @@ -267,7 +273,7 @@ class MockWebViewAndroidDownloadListener extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockWebViewAndroidJavaScriptChannel extends _i1.Mock - implements _i5.WebViewAndroidJavaScriptChannel { + implements _i6.WebViewAndroidJavaScriptChannel { MockWebViewAndroidJavaScriptChannel() { _i1.throwOnMissingStub(this); } @@ -293,7 +299,7 @@ class MockWebViewAndroidJavaScriptChannel extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockWebViewAndroidWebChromeClient extends _i1.Mock - implements _i5.WebViewAndroidWebChromeClient { + implements _i6.WebViewAndroidWebChromeClient { MockWebViewAndroidWebChromeClient() { _i1.throwOnMissingStub(this); } @@ -310,7 +316,7 @@ class MockWebViewAndroidWebChromeClient extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockWebViewAndroidWebViewClient extends _i1.Mock - implements _i5.WebViewAndroidWebViewClient { + implements _i6.WebViewAndroidWebViewClient { MockWebViewAndroidWebViewClient() { _i1.throwOnMissingStub(this); } @@ -442,7 +448,7 @@ class MockWebViewPlatformCallbacksHandler extends _i1.Mock /// A class which mocks [WebViewProxy]. /// /// See the documentation for Mockito's code generation for more information. -class MockWebViewProxy extends _i1.Mock implements _i5.WebViewProxy { +class MockWebViewProxy extends _i1.Mock implements _i6.WebViewProxy { MockWebViewProxy() { _i1.throwOnMissingStub(this); } From 7d9cc848dfd356ecc8c456b1d451d8c92a0fa507 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Mon, 6 Dec 2021 23:34:05 +0100 Subject: [PATCH 037/600] [webview_flutter] Expose loadFile and loadHtmlString through app facing package. (#4558) --- .../webview_flutter/CHANGELOG.md | 3 +- .../webview_flutter/example/lib/main.dart | 59 ++++++++++++ .../webview_flutter/example/pubspec.yaml | 1 + .../webview_flutter/lib/src/webview.dart | 29 ++++++ .../webview_flutter/pubspec.yaml | 4 +- .../test/webview_flutter_test.dart | 92 +++++++++++++++++++ .../test/webview_flutter_test.mocks.dart | 20 +++- 7 files changed, 201 insertions(+), 7 deletions(-) diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index 63d95c04cc9c..7b7e112b39fd 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.4.0 +* Adds support for the `loadFile` and `loadHtmlString` methods. * Updates example app Android compileSdkVersion to 31. * Integration test fixes. * Updates code for new analysis options. diff --git a/packages/webview_flutter/webview_flutter/example/lib/main.dart b/packages/webview_flutter/webview_flutter/example/lib/main.dart index 0a8928e34319..5c05e8f7de7d 100644 --- a/packages/webview_flutter/webview_flutter/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter/example/lib/main.dart @@ -9,6 +9,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:path_provider/path_provider.dart'; import 'package:webview_flutter/webview_flutter.dart'; void main() => runApp(MaterialApp(home: WebViewExample())); @@ -28,6 +29,25 @@ The navigation delegate is set to block navigation to the youtube website. '''; +const String kLocalExamplePage = ''' + + + +Load file or HTML string example + + + +

Local demo page

+

+ This is an example page used to demonstrate how to load a local file or HTML + string using the Flutter + webview plugin. +

+ + + +'''; + class WebViewExample extends StatefulWidget { @override _WebViewExampleState createState() => _WebViewExampleState(); @@ -133,6 +153,8 @@ enum MenuOptions { listCache, clearCache, navigationDelegate, + loadLocalFile, + loadHtmlString, } class SampleMenu extends StatelessWidget { @@ -171,6 +193,12 @@ class SampleMenu extends StatelessWidget { case MenuOptions.navigationDelegate: _onNavigationDelegateExample(controller.data!, context); break; + case MenuOptions.loadLocalFile: + _onLoadLocalFileExample(controller.data!, context); + break; + case MenuOptions.loadHtmlString: + _onLoadHtmlStringExample(controller.data!, context); + break; } }, itemBuilder: (BuildContext context) => >[ @@ -203,6 +231,14 @@ class SampleMenu extends StatelessWidget { value: MenuOptions.navigationDelegate, child: Text('Navigation Delegate example'), ), + const PopupMenuItem( + value: MenuOptions.loadHtmlString, + child: Text('Load HTML string'), + ), + const PopupMenuItem( + value: MenuOptions.loadLocalFile, + child: Text('Load local file'), + ), ], ); }, @@ -279,6 +315,18 @@ class SampleMenu extends StatelessWidget { await controller.loadUrl('data:text/html;base64,$contentBase64'); } + Future _onLoadLocalFileExample( + WebViewController controller, BuildContext context) async { + final String pathToIndex = await _prepareLocalFile(); + + await controller.loadFile(pathToIndex); + } + + Future _onLoadHtmlStringExample( + WebViewController controller, BuildContext context) async { + await controller.loadHtmlString(kLocalExamplePage); + } + Widget _getCookieList(String cookies) { if (cookies == null || cookies == '""') { return Container(); @@ -292,6 +340,17 @@ class SampleMenu extends StatelessWidget { children: cookieWidgets.toList(), ); } + + static Future _prepareLocalFile() async { + final String tmpDir = (await getTemporaryDirectory()).path; + final File indexFile = File( + {tmpDir, 'www', 'index.html'}.join(Platform.pathSeparator)); + + await indexFile.create(recursive: true); + await indexFile.writeAsString(kLocalExamplePage); + + return indexFile.path; + } } class NavigationControls extends StatelessWidget { diff --git a/packages/webview_flutter/webview_flutter/example/pubspec.yaml b/packages/webview_flutter/webview_flutter/example/pubspec.yaml index 53fdd56af301..284a7a9d5a0d 100644 --- a/packages/webview_flutter/webview_flutter/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/example/pubspec.yaml @@ -9,6 +9,7 @@ environment: dependencies: flutter: sdk: flutter + path_provider: ^2.0.6 webview_flutter: # When depending on this package from a real application you should use: # webview_flutter: ^x.y.z diff --git a/packages/webview_flutter/webview_flutter/lib/src/webview.dart b/packages/webview_flutter/webview_flutter/lib/src/webview.dart index b24020ba4dab..eb6ee4e09dc2 100644 --- a/packages/webview_flutter/webview_flutter/lib/src/webview.dart +++ b/packages/webview_flutter/webview_flutter/lib/src/webview.dart @@ -496,6 +496,35 @@ class WebViewController { WebView _widget; + /// Loads the file located at the specified [absoluteFilePath]. + /// + /// The [absoluteFilePath] parameter should contain the absolute path to the + /// file as it is stored on the device. For example: + /// `/Users/username/Documents/www/index.html`. + /// + /// Throws an ArgumentError if the [absoluteFilePath] does not exist. + Future loadFile( + String absoluteFilePath, + ) { + assert(absoluteFilePath.isNotEmpty); + return _webViewPlatformController.loadFile(absoluteFilePath); + } + + /// Loads the supplied HTML string. + /// + /// The [baseUrl] parameter is used when resolving relative URLs within the + /// HTML string. + Future loadHtmlString( + String html, { + String? baseUrl, + }) { + assert(html.isNotEmpty); + return _webViewPlatformController.loadHtmlString( + html, + baseUrl: baseUrl, + ); + } + /// Loads the specified URL. /// /// If `headers` is not null and the URL is an HTTP URL, the key value paris in `headers` will diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index 23693f3fbe6c..82f790a74478 100644 --- a/packages/webview_flutter/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter description: A Flutter plugin that provides a WebView widget on Android and iOS. repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.3.1 +version: 2.4.0 environment: sdk: ">=2.14.0 <3.0.0" @@ -19,7 +19,7 @@ flutter: dependencies: flutter: sdk: flutter - webview_flutter_android: ^2.3.1 + webview_flutter_android: ^2.4.0 webview_flutter_platform_interface: ^1.5.2 webview_flutter_wkwebview: ^2.4.0 diff --git a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart index f8a9e332c9d2..b05530272651 100644 --- a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart @@ -88,6 +88,98 @@ void main() { expect(disabledparams.webSettings!.javascriptMode, JavascriptMode.disabled); }); + testWidgets('Load file', (WidgetTester tester) async { + WebViewController? controller; + await tester.pumpWidget( + WebView( + onWebViewCreated: (WebViewController webViewController) { + controller = webViewController; + }, + ), + ); + + expect(controller, isNotNull); + + await controller!.loadFile('/test/path/index.html'); + + verify(mockWebViewPlatformController.loadFile( + '/test/path/index.html', + )); + }); + + testWidgets('Load file with empty path', (WidgetTester tester) async { + WebViewController? controller; + await tester.pumpWidget( + WebView( + onWebViewCreated: (WebViewController webViewController) { + controller = webViewController; + }, + ), + ); + + expect(controller, isNotNull); + + expect(() => controller!.loadFile(''), throwsAssertionError); + }); + + testWidgets('Load HTML string without base URL', (WidgetTester tester) async { + WebViewController? controller; + await tester.pumpWidget( + WebView( + onWebViewCreated: (WebViewController webViewController) { + controller = webViewController; + }, + ), + ); + + expect(controller, isNotNull); + + await controller!.loadHtmlString('

This is a test paragraph.

'); + + verify(mockWebViewPlatformController.loadHtmlString( + '

This is a test paragraph.

', + )); + }); + + testWidgets('Load HTML string with base URL', (WidgetTester tester) async { + WebViewController? controller; + await tester.pumpWidget( + WebView( + onWebViewCreated: (WebViewController webViewController) { + controller = webViewController; + }, + ), + ); + + expect(controller, isNotNull); + + await controller!.loadHtmlString( + '

This is a test paragraph.

', + baseUrl: 'https://flutter.dev', + ); + + verify(mockWebViewPlatformController.loadHtmlString( + '

This is a test paragraph.

', + baseUrl: 'https://flutter.dev', + )); + }); + + testWidgets('Load HTML string with empty string', + (WidgetTester tester) async { + WebViewController? controller; + await tester.pumpWidget( + WebView( + onWebViewCreated: (WebViewController webViewController) { + controller = webViewController; + }, + ), + ); + + expect(controller, isNotNull); + + expect(() => controller!.loadHtmlString(''), throwsAssertionError); + }); + testWidgets('Load url', (WidgetTester tester) async { WebViewController? controller; await tester.pumpWidget( diff --git a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.mocks.dart b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.mocks.dart index b9d43cc6f182..8857f606f06d 100644 --- a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.mocks.dart @@ -1,7 +1,3 @@ -// 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. - // Mocks generated by Mockito 5.0.16 from annotations // in webview_flutter/test/webview_flutter_test.dart. // Do not manually edit this file. @@ -81,12 +77,28 @@ class MockWebViewPlatformController extends _i1.Mock _i1.throwOnMissingStub(this); } + @override + _i9.Future loadFile(String? absoluteFilePath) => + (super.noSuchMethod(Invocation.method(#loadFile, [absoluteFilePath]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i9.Future); + @override + _i9.Future loadHtmlString(String? html, {String? baseUrl}) => + (super.noSuchMethod( + Invocation.method(#loadHtmlString, [html], {#baseUrl: baseUrl}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i9.Future); @override _i9.Future loadUrl(String? url, Map? headers) => (super.noSuchMethod(Invocation.method(#loadUrl, [url, headers]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i9.Future); @override + _i9.Future loadRequest(_i4.WebViewRequest? request) => + (super.noSuchMethod(Invocation.method(#loadRequest, [request]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i9.Future); + @override _i9.Future updateSettings(_i4.WebSettings? setting) => (super.noSuchMethod(Invocation.method(#updateSettings, [setting]), returnValue: Future.value(), From 17f95e9e7f74aa9599b37779ab632e5454d5f3f8 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Tue, 7 Dec 2021 10:09:05 +0100 Subject: [PATCH 038/600] [webview_flutter] Android implementation of `loadFlutterAsset` method. (#4581) --- .../webview_flutter_android/CHANGELOG.md | 4 + .../android/build.gradle | 4 - .../webviewflutter/FlutterAssetManager.java | 108 ++++++++++++++++++ .../FlutterAssetManagerHostApiImpl.java | 46 ++++++++ .../GeneratedAndroidWebView.java | 79 +++++++++++++ .../webviewflutter/WebViewFlutterPlugin.java | 14 ++- .../FlutterAssetManagerHostApiImplTest.java | 76 ++++++++++++ .../PluginBindingFlutterAssetManagerTest.java | 54 +++++++++ .../RegistrarFlutterAssetManagerTest.java | 55 +++++++++ .../example/assets/www/index.html | 20 ++++ .../example/assets/www/styles/style.css | 3 + .../example/lib/main.dart | 13 +++ .../example/lib/web_view.dart | 8 ++ .../example/pubspec.yaml | 2 + .../lib/src/android_webview.dart | 21 ++++ .../lib/src/android_webview.pigeon.dart | 67 +++++++++++ .../lib/webview_android_widget.dart | 38 +++++- .../pigeons/android_webview.dart | 7 ++ .../webview_flutter_android/pubspec.yaml | 4 +- .../test/android_webview.pigeon.dart | 53 +++++++++ .../test/android_webview_test.dart | 1 + .../test/android_webview_test.mocks.dart | 21 ++++ .../test/webview_android_widget_test.dart | 65 +++++++++++ .../webview_android_widget_test.mocks.dart | 22 ++++ 24 files changed, 775 insertions(+), 10 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterAssetManager.java create mode 100644 packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterAssetManagerHostApiImpl.java create mode 100644 packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/FlutterAssetManagerHostApiImplTest.java create mode 100644 packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/PluginBindingFlutterAssetManagerTest.java create mode 100644 packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/RegistrarFlutterAssetManagerTest.java create mode 100644 packages/webview_flutter/webview_flutter_android/example/assets/www/index.html create mode 100644 packages/webview_flutter/webview_flutter_android/example/assets/www/styles/style.css diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index caee5f7cb75a..e8d9e639c59d 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.6.0 + +* Adds implementation of the `loadFlutterAsset` method from the platform interface. + ## 2.5.0 * Adds an option to set the background color of the webview. diff --git a/packages/webview_flutter/webview_flutter_android/android/build.gradle b/packages/webview_flutter/webview_flutter_android/android/build.gradle index e70d4e68edc8..37954b36a834 100644 --- a/packages/webview_flutter/webview_flutter_android/android/build.gradle +++ b/packages/webview_flutter/webview_flutter_android/android/build.gradle @@ -58,8 +58,4 @@ android { } } } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } } diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterAssetManager.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterAssetManager.java new file mode 100644 index 000000000000..1d484d8639a0 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterAssetManager.java @@ -0,0 +1,108 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.webviewflutter; + +import android.content.res.AssetManager; +import androidx.annotation.NonNull; +import io.flutter.embedding.engine.plugins.FlutterPlugin; +import io.flutter.plugin.common.PluginRegistry; +import java.io.IOException; + +/** Provides access to the assets registered as part of the App bundle. */ +abstract class FlutterAssetManager { + final AssetManager assetManager; + + /** + * Constructs a new instance of the {@link FlutterAssetManager}. + * + * @param assetManager Instance of Android's {@link AssetManager} used to access assets within the + * App bundle. + */ + public FlutterAssetManager(AssetManager assetManager) { + this.assetManager = assetManager; + } + + /** + * Gets the relative file path to the Flutter asset with the given name, including the file's + * extension, e.g., "myImage.jpg". + * + *

The returned file path is relative to the Android app's standard asset's directory. + * Therefore, the returned path is appropriate to pass to Android's AssetManager, but the path is + * not appropriate to load as an absolute path. + */ + abstract String getAssetFilePathByName(String name); + + /** + * Returns a String array of all the assets at the given path. + * + * @param path A relative path within the assets, i.e., "docs/home.html". This value cannot be + * null. + * @return String[] Array of strings, one for each asset. These file names are relative to 'path'. + * This value may be null. + * @throws IOException Throws an IOException in case I/O operations were interrupted. + */ + public String[] list(@NonNull String path) throws IOException { + return assetManager.list(path); + } + + /** + * Provides access to assets using the {@link PluginRegistry.Registrar} for looking up file paths + * to Flutter assets. + * + * @deprecated The {@link RegistrarFlutterAssetManager} is for Flutter's v1 embedding. For + * instructions on migrating a plugin from Flutter's v1 Android embedding to v2, visit + * http://flutter.dev/go/android-plugin-migration + */ + @Deprecated + static class RegistrarFlutterAssetManager extends FlutterAssetManager { + final PluginRegistry.Registrar registrar; + + /** + * Constructs a new instance of the {@link RegistrarFlutterAssetManager}. + * + * @param assetManager Instance of Android's {@link AssetManager} used to access assets within + * the App bundle. + * @param registrar Instance of {@link io.flutter.plugin.common.PluginRegistry.Registrar} used + * to look up file paths to assets registered by Flutter. + */ + RegistrarFlutterAssetManager(AssetManager assetManager, PluginRegistry.Registrar registrar) { + super(assetManager); + this.registrar = registrar; + } + + @Override + public String getAssetFilePathByName(String name) { + return registrar.lookupKeyForAsset(name); + } + } + + /** + * Provides access to assets using the {@link FlutterPlugin.FlutterAssets} for looking up file + * paths to Flutter assets. + */ + static class PluginBindingFlutterAssetManager extends FlutterAssetManager { + final FlutterPlugin.FlutterAssets flutterAssets; + + /** + * Constructs a new instance of the {@link PluginBindingFlutterAssetManager}. + * + * @param assetManager Instance of Android's {@link AssetManager} used to access assets within + * the App bundle. + * @param flutterAssets Instance of {@link + * io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterAssets} used to look up file + * paths to assets registered by Flutter. + */ + PluginBindingFlutterAssetManager( + AssetManager assetManager, FlutterPlugin.FlutterAssets flutterAssets) { + super(assetManager); + this.flutterAssets = flutterAssets; + } + + @Override + public String getAssetFilePathByName(String name) { + return flutterAssets.getAssetFilePathByName(name); + } + } +} diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterAssetManagerHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterAssetManagerHostApiImpl.java new file mode 100644 index 000000000000..791912adb815 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterAssetManagerHostApiImpl.java @@ -0,0 +1,46 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.webviewflutter; + +import android.webkit.WebView; +import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.FlutterAssetManagerHostApi; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Host api implementation for {@link WebView}. + * + *

Handles creating {@link WebView}s that intercommunicate with a paired Dart object. + */ +public class FlutterAssetManagerHostApiImpl implements FlutterAssetManagerHostApi { + final FlutterAssetManager flutterAssetManager; + + /** Constructs a new instance of {@link FlutterAssetManagerHostApiImpl}. */ + public FlutterAssetManagerHostApiImpl(FlutterAssetManager flutterAssetManager) { + this.flutterAssetManager = flutterAssetManager; + } + + @Override + public List list(String path) { + try { + String[] paths = flutterAssetManager.list(path); + + if (paths == null) { + return new ArrayList<>(); + } + + return Arrays.asList(paths); + } catch (IOException ex) { + throw new RuntimeException(ex.getMessage()); + } + } + + @Override + public String getAssetFilePathByName(String name) { + return flutterAssetManager.getAssetFilePathByName(name); + } +} diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java index a5632d351f83..0123790ee799 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java @@ -16,6 +16,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; /** Generated class from Pigeon. */ @@ -1915,6 +1916,84 @@ static void setup(BinaryMessenger binaryMessenger, WebChromeClientHostApi api) { } } + private static class FlutterAssetManagerHostApiCodec extends StandardMessageCodec { + public static final FlutterAssetManagerHostApiCodec INSTANCE = + new FlutterAssetManagerHostApiCodec(); + + private FlutterAssetManagerHostApiCodec() {} + } + + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ + public interface FlutterAssetManagerHostApi { + List list(String path); + + String getAssetFilePathByName(String name); + + /** The codec used by FlutterAssetManagerHostApi. */ + static MessageCodec getCodec() { + return FlutterAssetManagerHostApiCodec.INSTANCE; + } + + /** + * Sets up an instance of `FlutterAssetManagerHostApi` to handle messages through the + * `binaryMessenger`. + */ + static void setup(BinaryMessenger binaryMessenger, FlutterAssetManagerHostApi api) { + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.FlutterAssetManagerHostApi.list", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + String pathArg = (String) args.get(0); + if (pathArg == null) { + throw new NullPointerException("pathArg unexpectedly null."); + } + List output = api.list(pathArg); + wrapped.put("result", output); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.FlutterAssetManagerHostApi.getAssetFilePathByName", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + String nameArg = (String) args.get(0); + if (nameArg == null) { + throw new NullPointerException("nameArg unexpectedly null."); + } + String output = api.getAssetFilePathByName(nameArg); + wrapped.put("result", output); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + } + } + private static class WebChromeClientFlutterApiCodec extends StandardMessageCodec { public static final WebChromeClientFlutterApiCodec INSTANCE = new WebChromeClientFlutterApiCodec(); diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java index 2b174ff35e6f..cbeda8dcf493 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java @@ -14,6 +14,7 @@ import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.platform.PlatformViewRegistry; import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.DownloadListenerHostApi; +import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.FlutterAssetManagerHostApi; import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.JavaScriptChannelHostApi; import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebChromeClientHostApi; import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebSettingsHostApi; @@ -61,7 +62,9 @@ public static void registerWith(io.flutter.plugin.common.PluginRegistry.Registra registrar.messenger(), registrar.platformViewRegistry(), registrar.activity(), - registrar.view()); + registrar.view(), + new FlutterAssetManager.RegistrarFlutterAssetManager( + registrar.context().getAssets(), registrar)); new FlutterCookieManager(registrar.messenger()); } @@ -69,7 +72,8 @@ private void setUp( BinaryMessenger binaryMessenger, PlatformViewRegistry viewRegistry, Context context, - View containerView) { + View containerView, + FlutterAssetManager flutterAssetManager) { new FlutterCookieManager(binaryMessenger); InstanceManager instanceManager = new InstanceManager(); @@ -111,6 +115,8 @@ private void setUp( binaryMessenger, new WebSettingsHostApiImpl( instanceManager, new WebSettingsHostApiImpl.WebSettingsCreator())); + FlutterAssetManagerHostApi.setup( + binaryMessenger, new FlutterAssetManagerHostApiImpl(flutterAssetManager)); } @Override @@ -120,7 +126,9 @@ public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { binding.getBinaryMessenger(), binding.getPlatformViewRegistry(), binding.getApplicationContext(), - null); + null, + new FlutterAssetManager.PluginBindingFlutterAssetManager( + binding.getApplicationContext().getAssets(), binding.getFlutterAssets())); } @Override diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/FlutterAssetManagerHostApiImplTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/FlutterAssetManagerHostApiImplTest.java new file mode 100644 index 000000000000..f530365a9334 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/FlutterAssetManagerHostApiImplTest.java @@ -0,0 +1,76 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.webviewflutter; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.util.List; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +public class FlutterAssetManagerHostApiImplTest { + @Mock FlutterAssetManager mockFlutterAssetManager; + + FlutterAssetManagerHostApiImpl testFlutterAssetManagerHostApiImpl; + + @Before + public void setUp() { + mockFlutterAssetManager = mock(FlutterAssetManager.class); + + testFlutterAssetManagerHostApiImpl = + new FlutterAssetManagerHostApiImpl(mockFlutterAssetManager); + } + + @Test + public void list() { + try { + when(mockFlutterAssetManager.list("test/path")) + .thenReturn(new String[] {"index.html", "styles.css"}); + List actualFilePaths = testFlutterAssetManagerHostApiImpl.list("test/path"); + verify(mockFlutterAssetManager).list("test/path"); + assertArrayEquals(new String[] {"index.html", "styles.css"}, actualFilePaths.toArray()); + } catch (IOException ex) { + fail(); + } + } + + @Test + public void list_returns_empty_list_when_no_results() { + try { + when(mockFlutterAssetManager.list("test/path")).thenReturn(null); + List actualFilePaths = testFlutterAssetManagerHostApiImpl.list("test/path"); + verify(mockFlutterAssetManager).list("test/path"); + assertArrayEquals(new String[] {}, actualFilePaths.toArray()); + } catch (IOException ex) { + fail(); + } + } + + @Test(expected = RuntimeException.class) + public void list_should_convert_io_exception_to_runtime_exception() { + try { + when(mockFlutterAssetManager.list("test/path")).thenThrow(new IOException()); + testFlutterAssetManagerHostApiImpl.list("test/path"); + } catch (IOException ex) { + fail(); + } + } + + @Test + public void getAssetFilePathByName() { + when(mockFlutterAssetManager.getAssetFilePathByName("index.html")) + .thenReturn("flutter_assets/index.html"); + String filePath = testFlutterAssetManagerHostApiImpl.getAssetFilePathByName("index.html"); + verify(mockFlutterAssetManager).getAssetFilePathByName("index.html"); + assertEquals("flutter_assets/index.html", filePath); + } +} diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/PluginBindingFlutterAssetManagerTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/PluginBindingFlutterAssetManagerTest.java new file mode 100644 index 000000000000..1f556b7bd486 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/PluginBindingFlutterAssetManagerTest.java @@ -0,0 +1,54 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.webviewflutter; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.res.AssetManager; +import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterAssets; +import io.flutter.plugins.webviewflutter.FlutterAssetManager.PluginBindingFlutterAssetManager; +import java.io.IOException; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +public class PluginBindingFlutterAssetManagerTest { + @Mock AssetManager mockAssetManager; + @Mock FlutterAssets mockFlutterAssets; + + PluginBindingFlutterAssetManager tesPluginBindingFlutterAssetManager; + + @Before + public void setUp() { + mockAssetManager = mock(AssetManager.class); + mockFlutterAssets = mock(FlutterAssets.class); + + tesPluginBindingFlutterAssetManager = + new PluginBindingFlutterAssetManager(mockAssetManager, mockFlutterAssets); + } + + @Test + public void list() { + try { + when(mockAssetManager.list("test/path")) + .thenReturn(new String[] {"index.html", "styles.css"}); + String[] actualFilePaths = tesPluginBindingFlutterAssetManager.list("test/path"); + verify(mockAssetManager).list("test/path"); + assertArrayEquals(new String[] {"index.html", "styles.css"}, actualFilePaths); + } catch (IOException ex) { + fail(); + } + } + + @Test + public void registrar_getAssetFilePathByName() { + tesPluginBindingFlutterAssetManager.getAssetFilePathByName("sample_movie.mp4"); + verify(mockFlutterAssets).getAssetFilePathByName("sample_movie.mp4"); + } +} diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/RegistrarFlutterAssetManagerTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/RegistrarFlutterAssetManagerTest.java new file mode 100644 index 000000000000..86b0fb5432b9 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/RegistrarFlutterAssetManagerTest.java @@ -0,0 +1,55 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.webviewflutter; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.res.AssetManager; +import io.flutter.plugin.common.PluginRegistry.Registrar; +import io.flutter.plugins.webviewflutter.FlutterAssetManager.RegistrarFlutterAssetManager; +import java.io.IOException; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +@SuppressWarnings("deprecation") +public class RegistrarFlutterAssetManagerTest { + @Mock AssetManager mockAssetManager; + @Mock Registrar mockRegistrar; + + RegistrarFlutterAssetManager testRegistrarFlutterAssetManager; + + @Before + public void setUp() { + mockAssetManager = mock(AssetManager.class); + mockRegistrar = mock(Registrar.class); + + testRegistrarFlutterAssetManager = + new RegistrarFlutterAssetManager(mockAssetManager, mockRegistrar); + } + + @Test + public void list() { + try { + when(mockAssetManager.list("test/path")) + .thenReturn(new String[] {"index.html", "styles.css"}); + String[] actualFilePaths = testRegistrarFlutterAssetManager.list("test/path"); + verify(mockAssetManager).list("test/path"); + assertArrayEquals(new String[] {"index.html", "styles.css"}, actualFilePaths); + } catch (IOException ex) { + fail(); + } + } + + @Test + public void registrar_getAssetFilePathByName() { + testRegistrarFlutterAssetManager.getAssetFilePathByName("sample_movie.mp4"); + verify(mockRegistrar).lookupKeyForAsset("sample_movie.mp4"); + } +} diff --git a/packages/webview_flutter/webview_flutter_android/example/assets/www/index.html b/packages/webview_flutter/webview_flutter_android/example/assets/www/index.html new file mode 100644 index 000000000000..9895dd3ce6cb --- /dev/null +++ b/packages/webview_flutter/webview_flutter_android/example/assets/www/index.html @@ -0,0 +1,20 @@ + + + + +Load file or HTML string example + + + + +

Local demo page

+

+ This is an example page used to demonstrate how to load a local file or HTML + string using the Flutter + webview plugin. +

+ + + \ No newline at end of file diff --git a/packages/webview_flutter/webview_flutter_android/example/assets/www/styles/style.css b/packages/webview_flutter/webview_flutter_android/example/assets/www/styles/style.css new file mode 100644 index 000000000000..c2140b8b0fd8 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_android/example/assets/www/styles/style.css @@ -0,0 +1,3 @@ +h1 { + color: blue; +} \ No newline at end of file diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart index 0c04c8ca4004..3bd283c3e712 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart @@ -185,6 +185,7 @@ enum _MenuOptions { listCache, clearCache, navigationDelegate, + loadFlutterAsset, loadLocalFile, loadHtmlString, transparentBackground, @@ -226,6 +227,9 @@ class _SampleMenu extends StatelessWidget { case _MenuOptions.navigationDelegate: _onNavigationDelegateExample(controller.data!, context); break; + case _MenuOptions.loadFlutterAsset: + _onLoadFlutterAssetExample(controller.data!, context); + break; case _MenuOptions.loadLocalFile: _onLoadLocalFileExample(controller.data!, context); break; @@ -267,6 +271,10 @@ class _SampleMenu extends StatelessWidget { value: _MenuOptions.navigationDelegate, child: Text('Navigation Delegate example'), ), + const PopupMenuItem<_MenuOptions>( + value: _MenuOptions.loadFlutterAsset, + child: Text('Load Flutter Asset'), + ), const PopupMenuItem<_MenuOptions>( value: _MenuOptions.loadHtmlString, child: Text('Load HTML string'), @@ -357,6 +365,11 @@ class _SampleMenu extends StatelessWidget { await controller.loadUrl('data:text/html;base64,$contentBase64'); } + Future _onLoadFlutterAssetExample( + WebViewController controller, BuildContext context) async { + await controller.loadFlutterAsset('assets/www/index.html'); + } + Future _onLoadLocalFileExample( WebViewController controller, BuildContext context) async { final String pathToIndex = await _prepareLocalFile(); diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart index 395966bdd744..b32deab05477 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart @@ -382,6 +382,14 @@ class WebViewController { return _webViewPlatformController.loadFile(absoluteFilePath); } + /// Loads the Flutter asset specified in the pubspec.yaml file. + /// + /// Throws an ArgumentError if [key] is not part of the specified assets + /// in the pubspec.yaml file. + Future loadFlutterAsset(String key) { + return _webViewPlatformController.loadFlutterAsset(key); + } + /// Loads the supplied HTML string. /// /// The [baseUrl] parameter is used when resolving relative URLs within the diff --git a/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml index 59579df8ca50..85990bd02139 100644 --- a/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml @@ -34,3 +34,5 @@ flutter: assets: - assets/sample_audio.ogg - assets/sample_video.mp4 + - assets/www/index.html + - assets/www/styles/style.css diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart index ecd6f33d1e63..a7561cea687c 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart @@ -7,6 +7,7 @@ import 'dart:ui'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart' show AndroidViewSurface; +import 'android_webview.pigeon.dart'; import 'android_webview_api_impls.dart'; // TODO(bparrishMines): This can be removed once pigeon supports null values: https://github.com/flutter/flutter/issues/59118 @@ -770,3 +771,23 @@ class WebResourceError { /// Describes the error. final String description; } + +/// Manages Flutter assets that are part of Android's app bundle. +class FlutterAssetManager { + /// Constructs the [FlutterAssetManager]. + const FlutterAssetManager(); + + /// Pigeon Host Api implementation for [FlutterAssetManager]. + @visibleForTesting + static FlutterAssetManagerHostApi api = FlutterAssetManagerHostApi(); + + /// Lists all assets at the given path. + /// + /// The assets are returned as a `List`. The `List` only + /// contains files which are direct childs + Future> list(String path) => api.list(path); + + /// Gets the relative file path to the Flutter asset with the given name. + Future getAssetFilePathByName(String name) => + api.getAssetFilePathByName(name); +} diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart index ae528a64bb8f..f93685611ba3 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart @@ -1616,6 +1616,73 @@ class WebChromeClientHostApi { } } +class _FlutterAssetManagerHostApiCodec extends StandardMessageCodec { + const _FlutterAssetManagerHostApiCodec(); +} + +class FlutterAssetManagerHostApi { + /// Constructor for [FlutterAssetManagerHostApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + FlutterAssetManagerHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _FlutterAssetManagerHostApiCodec(); + + Future> list(String arg_path) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.FlutterAssetManagerHostApi.list', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_path]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return (replyMap['result'] as List?)!.cast(); + } + } + + Future getAssetFilePathByName(String arg_name) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.FlutterAssetManagerHostApi.getAssetFilePathByName', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_name]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return (replyMap['result'] as String?)!; + } + } +} + class _WebChromeClientFlutterApiCodec extends StandardMessageCodec { const _WebChromeClientFlutterApiCodec(); } diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart index 0bfa04fde095..25f98742c119 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart @@ -5,7 +5,6 @@ import 'dart:async'; import 'package:flutter/widgets.dart'; - import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; import 'src/android_webview.dart' as android_webview; @@ -20,6 +19,8 @@ class WebViewAndroidWidget extends StatefulWidget { required this.javascriptChannelRegistry, required this.onBuildWidget, @visibleForTesting this.webViewProxy = const WebViewProxy(), + @visibleForTesting + this.flutterAssetManager = const android_webview.FlutterAssetManager(), }); /// Initial parameters used to setup the WebView. @@ -47,6 +48,11 @@ class WebViewAndroidWidget extends StatefulWidget { /// This should only be changed for testing purposes. final WebViewProxy webViewProxy; + /// Manages access to Flutter assets that are part of the Android App bundle. + /// + /// This should only be changed for testing purposes. + final android_webview.FlutterAssetManager flutterAssetManager; + /// Callback to build a widget once [android_webview.WebView] has been initialized. final Widget Function(WebViewAndroidPlatformController controller) onBuildWidget; @@ -67,6 +73,7 @@ class _WebViewAndroidWidgetState extends State { callbacksHandler: widget.callbacksHandler, javascriptChannelRegistry: widget.javascriptChannelRegistry, webViewProxy: widget.webViewProxy, + flutterAssetManager: widget.flutterAssetManager, ); } @@ -91,6 +98,8 @@ class WebViewAndroidPlatformController extends WebViewPlatformController { required this.callbacksHandler, required this.javascriptChannelRegistry, @visibleForTesting this.webViewProxy = const WebViewProxy(), + @visibleForTesting + this.flutterAssetManager = const android_webview.FlutterAssetManager(), }) : assert(creationParams.webSettings?.hasNavigationDelegate != null), super(callbacksHandler) { webView = webViewProxy.createWebView( @@ -134,6 +143,11 @@ class WebViewAndroidPlatformController extends WebViewPlatformController { /// This should only be changed for testing purposes. final WebViewProxy webViewProxy; + /// Manages access to Flutter assets that are part of the Android App bundle. + /// + /// This should only be changed for testing purposes. + final android_webview.FlutterAssetManager flutterAssetManager; + /// Receives callbacks when content should be downloaded instead. @visibleForTesting late final WebViewAndroidDownloadListener downloadListener = @@ -166,6 +180,28 @@ class WebViewAndroidPlatformController extends WebViewPlatformController { return webView.loadUrl(url, {}); } + @override + Future loadFlutterAsset(String key) async { + final String assetFilePath = + await flutterAssetManager.getAssetFilePathByName(key); + final List pathElements = assetFilePath.split('/'); + final String fileName = pathElements.removeLast(); + final List paths = + await flutterAssetManager.list(pathElements.join('/')); + + if (!paths.contains(fileName)) { + throw ArgumentError( + 'Asset for key "$key" not found.', + 'key', + ); + } + + return webView.loadUrl( + 'file:///android_asset/$assetFilePath', + {}, + ); + } + @override Future loadUrl( String url, diff --git a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart index 0fdec2c17756..78672ea5b671 100644 --- a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart +++ b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart @@ -193,6 +193,13 @@ abstract class WebChromeClientHostApi { void create(int instanceId, int webViewClientInstanceId); } +@HostApi(dartHostTestHandler: 'TestAssetManagerHostApi') +abstract class FlutterAssetManagerHostApi { + List list(String path); + + String getAssetFilePathByName(String name); +} + @FlutterApi() abstract class WebChromeClientFlutterApi { void dispose(int instanceId); diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 75245a31e4c2..bbe9ee174162 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_android description: A Flutter plugin that provides a WebView widget on Android. repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.5.0 +version: 2.6.0 environment: sdk: ">=2.14.0 <3.0.0" @@ -19,7 +19,7 @@ flutter: dependencies: flutter: sdk: flutter - webview_flutter_platform_interface: ^1.7.0 + webview_flutter_platform_interface: ^1.8.0 dev_dependencies: build_runner: ^2.1.4 diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart index 942e59a0b2b3..90c1474f6c67 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart @@ -1055,3 +1055,56 @@ abstract class TestWebChromeClientHostApi { } } } + +class _TestAssetManagerHostApiCodec extends StandardMessageCodec { + const _TestAssetManagerHostApiCodec(); +} + +abstract class TestAssetManagerHostApi { + static const MessageCodec codec = _TestAssetManagerHostApiCodec(); + + List list(String path); + String getAssetFilePathByName(String name); + static void setup(TestAssetManagerHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.FlutterAssetManagerHostApi.list', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.FlutterAssetManagerHostApi.list was null.'); + final List args = (message as List?)!; + final String? arg_path = (args[0] as String?); + assert(arg_path != null, + 'Argument for dev.flutter.pigeon.FlutterAssetManagerHostApi.list was null, expected non-null String.'); + final List output = api.list(arg_path!); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.FlutterAssetManagerHostApi.getAssetFilePathByName', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.FlutterAssetManagerHostApi.getAssetFilePathByName was null.'); + final List args = (message as List?)!; + final String? arg_name = (args[0] as String?); + assert(arg_name != null, + 'Argument for dev.flutter.pigeon.FlutterAssetManagerHostApi.getAssetFilePathByName was null, expected non-null String.'); + final String output = api.getAssetFilePathByName(arg_name!); + return {'result': output}; + }); + } + } + } +} diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart index 0e7d5fcd552e..b903678ecef8 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart @@ -22,6 +22,7 @@ import 'android_webview_test.mocks.dart'; TestWebSettingsHostApi, TestWebViewClientHostApi, TestWebViewHostApi, + TestAssetManagerHostApi, WebChromeClient, WebView, WebViewClient, diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart index 3c1cd610c136..a08019e112ef 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart @@ -340,6 +340,27 @@ class MockTestWebViewHostApi extends _i1.Mock String toString() => super.toString(); } +/// A class which mocks [TestAssetManagerHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestAssetManagerHostApi extends _i1.Mock + implements _i3.TestAssetManagerHostApi { + MockTestAssetManagerHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + List list(String? path) => + (super.noSuchMethod(Invocation.method(#list, [path]), + returnValue: []) as List); + @override + String getAssetFilePathByName(String? name) => + (super.noSuchMethod(Invocation.method(#getAssetFilePathByName, [name]), + returnValue: '') as String); + @override + String toString() => super.toString(); +} + /// A class which mocks [WebChromeClient]. /// /// See the documentation for Mockito's code generation for more information. diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart index 460cb54bd393..f3867b313d7d 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart @@ -16,6 +16,7 @@ import 'package:webview_flutter_platform_interface/webview_flutter_platform_inte import 'webview_android_widget_test.mocks.dart'; @GenerateMocks([ + android_webview.FlutterAssetManager, android_webview.WebSettings, android_webview.WebView, WebViewAndroidDownloadListener, @@ -30,6 +31,7 @@ void main() { TestWidgetsFlutterBinding.ensureInitialized(); group('$WebViewAndroidWidget', () { + late MockFlutterAssetManager mockFlutterAssetManager; late MockWebView mockWebView; late MockWebSettings mockWebSettings; late MockWebViewProxy mockWebViewProxy; @@ -44,6 +46,7 @@ void main() { late WebViewAndroidPlatformController testController; setUp(() { + mockFlutterAssetManager = MockFlutterAssetManager(); mockWebView = MockWebView(); mockWebSettings = MockWebSettings(); when(mockWebView.settings).thenReturn(mockWebSettings); @@ -77,6 +80,7 @@ void main() { callbacksHandler: mockCallbacksHandler, javascriptChannelRegistry: mockJavascriptChannelRegistry, webViewProxy: mockWebViewProxy, + flutterAssetManager: mockFlutterAssetManager, onBuildWidget: (WebViewAndroidPlatformController controller) { testController = controller; return Container(); @@ -299,6 +303,67 @@ void main() { )); }); + testWidgets('loadFlutterAsset', (WidgetTester tester) async { + await buildWidget(tester); + const String assetKey = 'test_assets/index.html'; + + when(mockFlutterAssetManager.getAssetFilePathByName(assetKey)) + .thenAnswer( + (_) => Future.value('flutter_assets/$assetKey')); + when(mockFlutterAssetManager.list('flutter_assets/test_assets')) + .thenAnswer( + (_) => Future>.value(['index.html'])); + + await testController.loadFlutterAsset(assetKey); + + verify(mockWebView.loadUrl( + 'file:///android_asset/flutter_assets/$assetKey', + {}, + )); + }); + + testWidgets('loadFlutterAsset with file in root', + (WidgetTester tester) async { + await buildWidget(tester); + const String assetKey = 'index.html'; + + when(mockFlutterAssetManager.getAssetFilePathByName(assetKey)) + .thenAnswer( + (_) => Future.value('flutter_assets/$assetKey')); + when(mockFlutterAssetManager.list('flutter_assets')).thenAnswer( + (_) => Future>.value(['index.html'])); + + await testController.loadFlutterAsset(assetKey); + + verify(mockWebView.loadUrl( + 'file:///android_asset/flutter_assets/$assetKey', + {}, + )); + }); + + testWidgets( + 'loadFlutterAsset throws ArgumentError when asset does not exists', + (WidgetTester tester) async { + await buildWidget(tester); + const String assetKey = 'test_assets/index.html'; + + when(mockFlutterAssetManager.getAssetFilePathByName(assetKey)) + .thenAnswer( + (_) => Future.value('flutter_assets/$assetKey')); + when(mockFlutterAssetManager.list('flutter_assets/test_assets')) + .thenAnswer((_) => Future>.value([''])); + + expect( + () => testController.loadFlutterAsset(assetKey), + throwsA( + isA() + .having((ArgumentError error) => error.name, 'name', 'key') + .having((ArgumentError error) => error.message, 'message', + 'Asset for key "$assetKey" not found.'), + ), + ); + }); + testWidgets('loadHtmlString without base URL', (WidgetTester tester) async { await buildWidget(tester); diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart index 6ee53f9f9362..f3b06ea0a0bb 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart @@ -27,6 +27,28 @@ class _FakeJavascriptChannelRegistry_1 extends _i1.Fake class _FakeWebView_2 extends _i1.Fake implements _i2.WebView {} +/// A class which mocks [FlutterAssetManager]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockFlutterAssetManager extends _i1.Mock + implements _i2.FlutterAssetManager { + MockFlutterAssetManager() { + _i1.throwOnMissingStub(this); + } + + @override + _i4.Future> list(String? path) => + (super.noSuchMethod(Invocation.method(#list, [path]), + returnValue: Future>.value([])) + as _i4.Future>); + @override + _i4.Future getAssetFilePathByName(String? name) => + (super.noSuchMethod(Invocation.method(#getAssetFilePathByName, [name]), + returnValue: Future.value('')) as _i4.Future); + @override + String toString() => super.toString(); +} + /// A class which mocks [WebSettings]. /// /// See the documentation for Mockito's code generation for more information. From 2681f505dd8cd22fef97593354b4397b56ff9308 Mon Sep 17 00:00:00 2001 From: e-adrien Date: Tue, 7 Dec 2021 12:34:04 +0000 Subject: [PATCH 039/600] [webview_flutter] Add an option to set the background color of the webview (#3431) --- .../webview_flutter/CHANGELOG.md | 4 ++ .../webview_flutter/example/lib/main.dart | 38 +++++++++++++++++++ .../webview_flutter/lib/src/webview.dart | 8 ++++ .../webview_flutter/pubspec.yaml | 8 ++-- .../test/webview_flutter_test.dart | 28 ++++++++++++++ 5 files changed, 82 insertions(+), 4 deletions(-) diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index 7b7e112b39fd..1f8a33db4cb4 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.5.0 + +* Adds an option to set the background color of the webview. + ## 2.4.0 * Adds support for the `loadFile` and `loadHtmlString` methods. diff --git a/packages/webview_flutter/webview_flutter/example/lib/main.dart b/packages/webview_flutter/webview_flutter/example/lib/main.dart index 5c05e8f7de7d..c870ae9d829d 100644 --- a/packages/webview_flutter/webview_flutter/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter/example/lib/main.dart @@ -48,6 +48,27 @@ const String kLocalExamplePage = ''' '''; +const String kTransparentBackgroundPage = ''' + + + + Transparent background test + + + +
+

Transparent background test

+
+
+ + +'''; + class WebViewExample extends StatefulWidget { @override _WebViewExampleState createState() => _WebViewExampleState(); @@ -68,6 +89,7 @@ class _WebViewExampleState extends State { @override Widget build(BuildContext context) { return Scaffold( + backgroundColor: Colors.green, appBar: AppBar( title: const Text('Flutter WebView example'), // This drop down menu demonstrates that Flutter widgets can be shown over the web view. @@ -106,6 +128,7 @@ class _WebViewExampleState extends State { print('Page finished loading: $url'); }, gestureNavigationEnabled: true, + backgroundColor: const Color(0x00000000), ); }), floatingActionButton: favoriteButton(), @@ -155,6 +178,7 @@ enum MenuOptions { navigationDelegate, loadLocalFile, loadHtmlString, + transparentBackground, } class SampleMenu extends StatelessWidget { @@ -170,6 +194,7 @@ class SampleMenu extends StatelessWidget { builder: (BuildContext context, AsyncSnapshot controller) { return PopupMenuButton( + key: const ValueKey('ShowPopupMenu'), onSelected: (MenuOptions value) { switch (value) { case MenuOptions.showUserAgent: @@ -199,6 +224,9 @@ class SampleMenu extends StatelessWidget { case MenuOptions.loadHtmlString: _onLoadHtmlStringExample(controller.data!, context); break; + case MenuOptions.transparentBackground: + _onTransparentBackground(controller.data!, context); + break; } }, itemBuilder: (BuildContext context) => >[ @@ -239,6 +267,11 @@ class SampleMenu extends StatelessWidget { value: MenuOptions.loadLocalFile, child: Text('Load local file'), ), + const PopupMenuItem( + key: ValueKey('ShowTransparentBackgroundExample'), + value: MenuOptions.transparentBackground, + child: Text('Transparent background example'), + ), ], ); }, @@ -327,6 +360,11 @@ class SampleMenu extends StatelessWidget { await controller.loadHtmlString(kLocalExamplePage); } + Future _onTransparentBackground( + WebViewController controller, BuildContext context) async { + await controller.loadHtmlString(kTransparentBackgroundPage); + } + Widget _getCookieList(String cookies) { if (cookies == null || cookies == '""') { return Container(); diff --git a/packages/webview_flutter/webview_flutter/lib/src/webview.dart b/packages/webview_flutter/webview_flutter/lib/src/webview.dart index eb6ee4e09dc2..b2cc69045e5f 100644 --- a/packages/webview_flutter/webview_flutter/lib/src/webview.dart +++ b/packages/webview_flutter/webview_flutter/lib/src/webview.dart @@ -94,6 +94,7 @@ class WebView extends StatefulWidget { this.initialMediaPlaybackPolicy = AutoMediaPlaybackPolicy.require_user_action_for_all_media_types, this.allowsInlineMediaPlayback = false, + this.backgroundColor, }) : assert(javascriptMode != null), assert(initialMediaPlaybackPolicy != null), assert(allowsInlineMediaPlayback != null), @@ -286,6 +287,12 @@ class WebView extends StatefulWidget { /// The default policy is [AutoMediaPlaybackPolicy.require_user_action_for_all_media_types]. final AutoMediaPlaybackPolicy initialMediaPlaybackPolicy; + /// The background color of the [WebView]. + /// + /// When `null` the platform's webview default background color is used. By + /// default [backgroundColor] is `null`. + final Color? backgroundColor; + @override State createState() => _WebViewState(); } @@ -357,6 +364,7 @@ CreationParams _creationParamsfromWidget(WebView widget) { javascriptChannelNames: _extractChannelNames(widget.javascriptChannels), userAgent: widget.userAgent, autoMediaPlaybackPolicy: widget.initialMediaPlaybackPolicy, + backgroundColor: widget.backgroundColor, ); } diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index 82f790a74478..6c00f65d8022 100644 --- a/packages/webview_flutter/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter description: A Flutter plugin that provides a WebView widget on Android and iOS. repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.4.0 +version: 2.5.0 environment: sdk: ">=2.14.0 <3.0.0" @@ -19,9 +19,9 @@ flutter: dependencies: flutter: sdk: flutter - webview_flutter_android: ^2.4.0 - webview_flutter_platform_interface: ^1.5.2 - webview_flutter_wkwebview: ^2.4.0 + webview_flutter_android: ^2.5.0 + webview_flutter_platform_interface: ^1.7.0 + webview_flutter_wkwebview: ^2.5.0 dev_dependencies: build_runner: ^2.1.5 diff --git a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart index b05530272651..da860f0118d4 100644 --- a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart @@ -1020,6 +1020,34 @@ void main() { }); }); + group('Background color', () { + testWidgets('Defaults to null', (WidgetTester tester) async { + await tester.pumpWidget(const WebView()); + + final CreationParams params = captureBuildArgs( + mockWebViewPlatform, + creationParams: true, + ).single as CreationParams; + + expect(params.backgroundColor, null); + }); + + testWidgets('Can be transparent', (WidgetTester tester) async { + const Color transparentColor = Color(0x00000000); + + await tester.pumpWidget(const WebView( + backgroundColor: transparentColor, + )); + + final CreationParams params = captureBuildArgs( + mockWebViewPlatform, + creationParams: true, + ).single as CreationParams; + + expect(params.backgroundColor, transparentColor); + }); + }); + group('Custom platform implementation', () { setUp(() { WebView.platform = MyWebViewPlatform(); From 3ce611e4d2980696b95e994ac3a9bffc3897f0db Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 7 Dec 2021 10:02:24 -0500 Subject: [PATCH 040/600] [flutter_plugin_tools] Improve package targeting (#4577) Improve package targeting: - `--run-on-changed-packages` now includes only changed packages in a federated plugin, not all packages. Include all packages isn't useful since (without changes that would cause them to be included anyway) package dependencies are not path-based between packages. - `--packages` now allows specifying package names of federated plugins. E.g., `--packages=path_provider_ios` will now work, instead of requiring `--packages=path_provider/path_provider_ios`. The fully qualified form still works as well (and is still needed for app-facing packages to disambiguate from the plugin group). Fixes https://github.com/flutter/flutter/issues/94618 --- script/tool/CHANGELOG.md | 5 ++ script/tool/README.md | 9 +- .../tool/lib/src/common/plugin_command.dart | 67 +++++++++++---- .../tool/test/common/plugin_command_test.dart | 85 +++++++++++++++++-- 4 files changed, 139 insertions(+), 27 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index dec218e2ba24..72539c24c5fc 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -5,6 +5,11 @@ dependencies to path-based dependencies. - Adds a (hidden) `--run-on-dirty-packages` flag for use with `make-deps-path-based` in CI. +- `--packages` now allows using a federated plugin's package as a target without + fully specifying it (if it is not the same as the plugin's name). E.g., + `--packages=path_provide_ios` now works. +- `--run-on-changed-packages` now includes only the changed packages in a + federated plugin, not all packages in that plugin. - Fix `federation-safety-check` handling of plugin deletion, and of top-level files in unfederated plugins whose names match federated plugin heuristics (e.g., `packages/foo/foo_android.iml`). diff --git a/script/tool/README.md b/script/tool/README.md index 1a87f098757b..613cb5456e0a 100644 --- a/script/tool/README.md +++ b/script/tool/README.md @@ -51,8 +51,13 @@ following shows a number of common commands being run for a specific plugin. All examples assume running from source; see above for running the published version instead. -Note that the `plugins` argument, despite the name, applies to any package. -(It will likely be renamed `packages` in the future.) +Most commands take a `--packages` argument to control which package(s) the +command is targetting. An package name can be any of: +- The name of a package (e.g., `path_provider_android`). +- The name of a federated plugin (e.g., `path_provider`), in which case all + packages that make up that plugin will be targetted. +- A combination federated_plugin_name/package_name (e.g., + `path_provider/path_provider` for the app-facing package). ### Format Code diff --git a/script/tool/lib/src/common/plugin_command.dart b/script/tool/lib/src/common/plugin_command.dart index 7166c754e129..184663568224 100644 --- a/script/tool/lib/src/common/plugin_command.dart +++ b/script/tool/lib/src/common/plugin_command.dart @@ -339,7 +339,7 @@ abstract class PluginCommand extends Command { final List changedFiles = await gitVersionFinder.getChangedFiles(); if (!_changesRequireFullTest(changedFiles)) { - packages = _getChangedPackages(changedFiles); + packages = _getChangedPackageNames(changedFiles); } } else if (getBoolArg(_runOnDirtyPackagesArg)) { final GitVersionFinder gitVersionFinder = @@ -348,7 +348,7 @@ abstract class PluginCommand extends Command { // _changesRequireFullTest is deliberately not used here, as this flag is // intended for use in CI to re-test packages changed by // 'make-deps-path-based'. - packages = _getChangedPackages( + packages = _getChangedPackageNames( await gitVersionFinder.getChangedFiles(includeUncommitted: true)); // For the same reason, empty is not treated as "all packages" as it is // for other flags. @@ -379,21 +379,23 @@ abstract class PluginCommand extends Command { await for (final FileSystemEntity subdir in entity.list(followLinks: false)) { if (_isDartPackage(subdir)) { - // If --plugin=my_plugin is passed, then match all federated - // plugins under 'my_plugin'. Also match if the exact plugin is - // passed. - final String relativePath = - path.relative(subdir.path, from: dir.path); - final String packageName = path.basename(subdir.path); - final String basenamePath = path.basename(entity.path); + // There are three ways for a federated plugin to match: + // - package name (path_provider_android) + // - fully specified name (path_provider/path_provider_android) + // - group name (path_provider), which matches all packages in + // the group + final Set possibleMatches = { + path.basename(subdir.path), // package name + path.basename(entity.path), // group name + path.relative(subdir.path, from: dir.path), // fully specified + }; if (packages.isEmpty || - packages.contains(relativePath) || - packages.contains(basenamePath)) { + packages.intersection(possibleMatches).isNotEmpty) { yield PackageEnumerationEntry( RepositoryPackage(subdir as Directory), - excluded: excludedPluginNames.contains(basenamePath) || - excludedPluginNames.contains(packageName) || - excludedPluginNames.contains(relativePath)); + excluded: excludedPluginNames + .intersection(possibleMatches) + .isNotEmpty); } } } @@ -454,17 +456,48 @@ abstract class PluginCommand extends Command { return gitVersionFinder; } - // Returns packages that have been changed given a list of changed files. + // Returns the names of packages that have been changed given a list of + // changed files. + // + // The names will either be the actual package names, or potentially + // group/name specifiers (for example, path_provider/path_provider) for + // packages in federated plugins. // // The paths must use POSIX separators (e.g., as provided by git output). - Set _getChangedPackages(List changedFiles) { + Set _getChangedPackageNames(List changedFiles) { final Set packages = {}; + + // A helper function that returns true if candidatePackageName looks like an + // implementation package of a plugin called pluginName. Used to determine + // if .../packages/parentName/candidatePackageName/... + // looks like a path in a federated plugin package (candidatePackageName) + // rather than a top-level package (parentName). + bool isFederatedPackage(String candidatePackageName, String parentName) { + return candidatePackageName == parentName || + candidatePackageName.startsWith('${parentName}_'); + } + for (final String path in changedFiles) { final List pathComponents = p.posix.split(path); final int packagesIndex = pathComponents.indexWhere((String element) => element == 'packages'); if (packagesIndex != -1) { - packages.add(pathComponents[packagesIndex + 1]); + // Find the name of the directory directly under packages. This is + // either the name of the package, or a plugin group directory for + // a federated plugin. + final String topLevelName = pathComponents[packagesIndex + 1]; + String packageName = topLevelName; + if (packagesIndex + 2 < pathComponents.length && + isFederatedPackage( + pathComponents[packagesIndex + 2], topLevelName)) { + // This looks like a federated package; use the full specifier if + // the name would be ambiguous (i.e., for the app-facing package). + packageName = pathComponents[packagesIndex + 2]; + if (packageName == topLevelName) { + packageName = '$topLevelName/$packageName'; + } + } + packages.add(packageName); } } if (packages.isEmpty) { diff --git a/script/tool/test/common/plugin_command_test.dart b/script/tool/test/common/plugin_command_test.dart index 222df544f344..28a03c61d59f 100644 --- a/script/tool/test/common/plugin_command_test.dart +++ b/script/tool/test/common/plugin_command_test.dart @@ -180,6 +180,79 @@ void main() { expect(command.plugins, unorderedEquals([])); }); + test( + 'explicitly specifying the plugin (group) name of a federated plugin ' + 'should include all plugins in the group', () async { + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: ''' +packages/plugin1/plugin1/plugin1.dart +'''), + ]; + final Directory pluginGroup = packagesDir.childDirectory('plugin1'); + final Directory appFacingPackage = + createFakePlugin('plugin1', pluginGroup); + final Directory platformInterfacePackage = + createFakePlugin('plugin1_platform_interface', pluginGroup); + final Directory implementationPackage = + createFakePlugin('plugin1_web', pluginGroup); + + await runCapturingPrint( + runner, ['sample', '--base-sha=main', '--packages=plugin1']); + + expect( + command.plugins, + unorderedEquals([ + appFacingPackage.path, + platformInterfacePackage.path, + implementationPackage.path + ])); + }); + + test( + 'specifying the app-facing package of a federated plugin using its ' + 'fully qualified name should include only that package', () async { + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: ''' +packages/plugin1/plugin1/plugin1.dart +'''), + ]; + final Directory pluginGroup = packagesDir.childDirectory('plugin1'); + final Directory appFacingPackage = + createFakePlugin('plugin1', pluginGroup); + createFakePlugin('plugin1_platform_interface', pluginGroup); + createFakePlugin('plugin1_web', pluginGroup); + + await runCapturingPrint(runner, + ['sample', '--base-sha=main', '--packages=plugin1/plugin1']); + + expect(command.plugins, unorderedEquals([appFacingPackage.path])); + }); + + test( + 'specifying a package of a federated plugin by its name should ' + 'include only that package', () async { + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: ''' +packages/plugin1/plugin1/plugin1.dart +'''), + ]; + final Directory pluginGroup = packagesDir.childDirectory('plugin1'); + + createFakePlugin('plugin1', pluginGroup); + final Directory platformInterfacePackage = + createFakePlugin('plugin1_platform_interface', pluginGroup); + createFakePlugin('plugin1_web', pluginGroup); + + await runCapturingPrint(runner, [ + 'sample', + '--base-sha=main', + '--packages=plugin1_platform_interface' + ]); + + expect(command.plugins, + unorderedEquals([platformInterfacePackage.path])); + }); + group('conflicting package selection', () { test('does not allow --packages with --run-on-changed-packages', () async { @@ -442,7 +515,7 @@ packages/plugin1/plugin1_web/plugin1_web.dart }); test( - 'changing one plugin in a federated group should include all plugins in the group', + 'changing one plugin in a federated group should only include that plugin', () async { processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: ''' @@ -451,17 +524,13 @@ packages/plugin1/plugin1/plugin1.dart ]; final Directory plugin1 = createFakePlugin('plugin1', packagesDir.childDirectory('plugin1')); - final Directory plugin2 = createFakePlugin('plugin1_platform_interface', + createFakePlugin('plugin1_platform_interface', packagesDir.childDirectory('plugin1')); - final Directory plugin3 = createFakePlugin( - 'plugin1_web', packagesDir.childDirectory('plugin1')); + createFakePlugin('plugin1_web', packagesDir.childDirectory('plugin1')); await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); - expect( - command.plugins, - unorderedEquals( - [plugin1.path, plugin2.path, plugin3.path])); + expect(command.plugins, unorderedEquals([plugin1.path])); }); test('--exclude flag works with --run-on-changed-packages', () async { From 0f0dcbf10e9cdd2a61a6538eb80378f896e42c79 Mon Sep 17 00:00:00 2001 From: BeMacized Date: Tue, 7 Dec 2021 16:07:50 +0100 Subject: [PATCH 041/600] [webview_flutter] Implement loadRequest in Android package. (#4563) Implements the `loadRequest` method added in #4450 for the Android package. Related issue: - flutter/flutter#27730 --- .../webview_flutter_android/CHANGELOG.md | 4 + .../GeneratedAndroidWebView.java | 35 ++++++ .../webviewflutter/WebViewHostApiImpl.java | 6 + .../plugins/webviewflutter/WebViewTest.java | 6 + .../example/lib/main.dart | 19 +++ .../example/lib/web_view.dart | 5 + .../lib/src/android_webview.dart | 8 ++ .../lib/src/android_webview.pigeon.dart | 27 +++++ .../lib/src/android_webview_api_impls.dart | 11 ++ .../lib/webview_android_widget.dart | 23 ++++ .../pigeons/android_webview.dart | 6 + .../webview_flutter_android/pubspec.yaml | 2 +- .../test/android_webview.pigeon.dart | 26 ++++ .../test/android_webview_test.mocks.dart | 114 ++++++++++-------- .../test/webview_android_widget_test.dart | 76 ++++++++++++ .../webview_android_widget_test.mocks.dart | 26 ++-- 16 files changed, 335 insertions(+), 59 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index e8d9e639c59d..ce73ab26cad0 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.7.0 + +* Adds support for the `loadRequest` method from the platform interface. + ## 2.6.0 * Adds implementation of the `loadFlutterAsset` method from the platform interface. diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java index 0123790ee799..0e6776ecd618 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java @@ -186,6 +186,8 @@ void loadDataWithBaseUrl( void loadUrl(Long instanceId, String url, Map headers); + void postUrl(Long instanceId, String url, byte[] data); + String getUrl(Long instanceId); Boolean canGoBack(Long instanceId); @@ -410,6 +412,39 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { channel.setMessageHandler(null); } } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.WebViewHostApi.postUrl", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number instanceIdArg = (Number) args.get(0); + if (instanceIdArg == null) { + throw new NullPointerException("instanceIdArg unexpectedly null."); + } + String urlArg = (String) args.get(1); + if (urlArg == null) { + throw new NullPointerException("urlArg unexpectedly null."); + } + byte[] dataArg = (byte[]) args.get(2); + if (dataArg == null) { + throw new NullPointerException("dataArg unexpectedly null."); + } + api.postUrl(instanceIdArg.longValue(), urlArg, dataArg); + wrapped.put("result", null); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } { BasicMessageChannel channel = new BasicMessageChannel<>( diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java index 78b06aac90b1..0f3161355dcb 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 @@ -382,6 +382,12 @@ public void loadUrl(Long instanceId, String url, Map headers) { webView.loadUrl(url, headers); } + @Override + public void postUrl(Long instanceId, String url, byte[] data) { + final WebView webView = (WebView) instanceManager.getInstance(instanceId); + webView.postUrl(url, data); + } + @Override public String getUrl(Long instanceId) { final WebView webView = (WebView) instanceManager.getInstance(instanceId); 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 dc58b9b100ff..2312b764342f 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 @@ -208,6 +208,12 @@ public void loadUrl() { verify(mockWebView).loadUrl("https://www.google.com", new HashMap<>()); } + @Test + public void postUrl() { + testHostApiImpl.postUrl(0L, "https://www.google.com", new byte[] {0x01, 0x02}); + verify(mockWebView).postUrl("https://www.google.com", new byte[] {0x01, 0x02}); + } + @Test public void getUrl() { when(mockWebView.getUrl()).thenReturn("https://www.google.com"); diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart index 3bd283c3e712..eb3162a179ff 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart @@ -7,6 +7,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'dart:typed_data'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_driver/driver_extension.dart'; @@ -189,6 +190,7 @@ enum _MenuOptions { loadLocalFile, loadHtmlString, transparentBackground, + doPostRequest, } class _SampleMenu extends StatelessWidget { @@ -239,6 +241,9 @@ class _SampleMenu extends StatelessWidget { case _MenuOptions.transparentBackground: _onTransparentBackground(controller.data!, context); break; + case _MenuOptions.doPostRequest: + _onDoPostRequest(controller.data!, context); + break; } }, itemBuilder: (BuildContext context) => >[ @@ -288,6 +293,10 @@ class _SampleMenu extends StatelessWidget { value: _MenuOptions.transparentBackground, child: Text('Transparent background example'), ), + const PopupMenuItem<_MenuOptions>( + value: _MenuOptions.doPostRequest, + child: Text('Post Request'), + ), ], ); }, @@ -382,6 +391,16 @@ class _SampleMenu extends StatelessWidget { await controller.loadHtmlString(kExamplePage); } + Future _onDoPostRequest( + WebViewController controller, BuildContext context) async { + final WebViewRequest request = WebViewRequest( + uri: Uri.parse('https://httpbin.org/post'), + method: WebViewRequestMethod.post, + body: Uint8List.fromList('Test Body'.codeUnits), + ); + await controller.loadRequest(request); + } + Widget _getCookieList(String cookies) { if (cookies == null || cookies == '""') { return Container(); diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart index b32deab05477..adaf7fbb3ee8 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart @@ -418,6 +418,11 @@ class WebViewController { return _webViewPlatformController.loadUrl(url, headers); } + /// Loads a page by making the specified request. + Future loadRequest(WebViewRequest request) async { + return _webViewPlatformController.loadRequest(request); + } + /// Accessor to the current URL that the WebView is displaying. /// /// If [WebView.initialUrl] was never specified, returns `null`. diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart index a7561cea687c..d02701643291 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:typed_data'; import 'dart:ui'; import 'package:flutter/foundation.dart'; @@ -169,6 +170,13 @@ class WebView { return api.loadUrlFromInstance(this, url, headers); } + /// Loads the URL with postData using "POST" method into this WebView. + /// + /// If url is not a network URL, it will be loaded with [loadUrl] instead, ignoring the postData param. + Future postUrl(String url, Uint8List data) { + return api.postUrlFromInstance(this, url, data); + } + /// Gets the URL for the current page. /// /// This is not always the same as the URL passed to diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart index f93685611ba3..e0a1db311b74 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart @@ -220,6 +220,33 @@ class WebViewHostApi { } } + Future postUrl( + int arg_instanceId, String arg_url, Uint8List arg_data) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WebViewHostApi.postUrl', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_url, arg_data]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + Future getUrl(int arg_instanceId) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.getUrl', codec, diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart index 51efdaadebdc..1db5ed449c56 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:typed_data'; + import 'package:flutter/services.dart'; import 'android_webview.dart'; @@ -152,6 +154,15 @@ class WebViewHostApiImpl extends WebViewHostApi { return loadUrl(instanceManager.getInstanceId(instance)!, url, headers); } + /// Helper method to convert instances ids to objects. + Future postUrlFromInstance( + WebView instance, + String url, + Uint8List data, + ) { + return postUrl(instanceManager.getInstanceId(instance)!, url, data); + } + /// Helper method to convert instances ids to objects. Future getUrlFromInstance(WebView instance) { return getUrl(instanceManager.getInstanceId(instance)!); diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart index 25f98742c119..fc295be65aa3 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:typed_data'; import 'package:flutter/widgets.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; @@ -210,6 +211,28 @@ class WebViewAndroidPlatformController extends WebViewPlatformController { return webView.loadUrl(url, headers ?? {}); } + /// When making a POST request, headers are ignored. As a workaround, make + /// the request manually and load the response data using [loadHTMLString]. + @override + Future loadRequest( + WebViewRequest request, + ) async { + if (!request.uri.hasScheme) { + throw ArgumentError('WebViewRequest#uri is required to have a scheme.'); + } + switch (request.method) { + case WebViewRequestMethod.get: + return webView.loadUrl(request.uri.toString(), request.headers); + case WebViewRequestMethod.post: + return webView.postUrl( + request.uri.toString(), request.body ?? Uint8List(0)); + default: + throw UnimplementedError( + 'This version of webview_android_widget currently has no implementation for HTTP method ${request.method.serialize()} in loadRequest.', + ); + } + } + @override Future currentUrl() => webView.getUrl(); diff --git a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart index 78672ea5b671..4907dadb41b7 100644 --- a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart +++ b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart @@ -46,6 +46,12 @@ abstract class WebViewHostApi { Map headers, ); + void postUrl( + int instanceId, + String url, + Uint8List data, + ); + String getUrl(int instanceId); bool canGoBack(int instanceId); diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index bbe9ee174162..08bcbb50602e 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_android description: A Flutter plugin that provides a WebView widget on Android. repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.6.0 +version: 2.7.0 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart index 90c1474f6c67..1e47c79d32b7 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart @@ -27,6 +27,7 @@ abstract class TestWebViewHostApi { void loadDataWithBaseUrl(int instanceId, String baseUrl, String data, String mimeType, String encoding, String historyUrl); void loadUrl(int instanceId, String url, Map headers); + void postUrl(int instanceId, String url, Uint8List data); String getUrl(int instanceId); bool canGoBack(int instanceId); bool canGoForward(int instanceId); @@ -180,6 +181,31 @@ abstract class TestWebViewHostApi { }); } } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WebViewHostApi.postUrl', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WebViewHostApi.postUrl was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WebViewHostApi.postUrl was null, expected non-null int.'); + final String? arg_url = (args[1] as String?); + assert(arg_url != null, + 'Argument for dev.flutter.pigeon.WebViewHostApi.postUrl was null, expected non-null String.'); + final Uint8List? arg_data = (args[2] as Uint8List?); + assert(arg_data != null, + 'Argument for dev.flutter.pigeon.WebViewHostApi.postUrl was null, expected non-null Uint8List.'); + api.postUrl(arg_instanceId!, arg_url!, arg_data!); + return {}; + }); + } + } { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.getUrl', codec, diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart index a08019e112ef..8bb569497b59 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart @@ -1,9 +1,14 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + // Mocks generated by Mockito 5.0.16 from annotations // in webview_flutter_android/test/android_webview_test.dart. // Do not manually edit this file. -import 'dart:async' as _i4; -import 'dart:ui' as _i5; +import 'dart:async' as _i5; +import 'dart:typed_data' as _i4; +import 'dart:ui' as _i6; import 'package:mockito/mockito.dart' as _i1; import 'package:webview_flutter_android/src/android_webview.dart' as _i2; @@ -241,6 +246,10 @@ class MockTestWebViewHostApi extends _i1.Mock Invocation.method(#loadUrl, [instanceId, url, headers]), returnValueForMissingStub: null); @override + void postUrl(int? instanceId, String? url, _i4.Uint8List? data) => + super.noSuchMethod(Invocation.method(#postUrl, [instanceId, url, data]), + returnValueForMissingStub: null); + @override String getUrl(int? instanceId) => (super.noSuchMethod(Invocation.method(#getUrl, [instanceId]), returnValue: '') as String); @@ -270,12 +279,12 @@ class MockTestWebViewHostApi extends _i1.Mock Invocation.method(#clearCache, [instanceId, includeDiskFiles]), returnValueForMissingStub: null); @override - _i4.Future evaluateJavascript( + _i5.Future evaluateJavascript( int? instanceId, String? javascriptString) => (super.noSuchMethod( Invocation.method( #evaluateJavascript, [instanceId, javascriptString]), - returnValue: Future.value('')) as _i4.Future); + returnValue: Future.value('')) as _i5.Future); @override String getTitle(int? instanceId) => (super.noSuchMethod(Invocation.method(#getTitle, [instanceId]), @@ -394,15 +403,15 @@ class MockWebView extends _i1.Mock implements _i2.WebView { (super.noSuchMethod(Invocation.getter(#settings), returnValue: _FakeWebSettings_0()) as _i2.WebSettings); @override - _i4.Future loadData( + _i5.Future loadData( {String? data, String? mimeType, String? encoding}) => (super.noSuchMethod( Invocation.method(#loadData, [], {#data: data, #mimeType: mimeType, #encoding: encoding}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future loadDataWithBaseUrl( + _i5.Future loadDataWithBaseUrl( {String? baseUrl, String? data, String? mimeType, @@ -417,109 +426,114 @@ class MockWebView extends _i1.Mock implements _i2.WebView { #historyUrl: historyUrl }), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future loadUrl(String? url, Map? headers) => + _i5.Future loadUrl(String? url, Map? headers) => (super.noSuchMethod(Invocation.method(#loadUrl, [url, headers]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future postUrl(String? url, _i4.Uint8List? data) => + (super.noSuchMethod(Invocation.method(#postUrl, [url, data]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future getUrl() => + _i5.Future getUrl() => (super.noSuchMethod(Invocation.method(#getUrl, []), - returnValue: Future.value()) as _i4.Future); + returnValue: Future.value()) as _i5.Future); @override - _i4.Future canGoBack() => + _i5.Future canGoBack() => (super.noSuchMethod(Invocation.method(#canGoBack, []), - returnValue: Future.value(false)) as _i4.Future); + returnValue: Future.value(false)) as _i5.Future); @override - _i4.Future canGoForward() => + _i5.Future canGoForward() => (super.noSuchMethod(Invocation.method(#canGoForward, []), - returnValue: Future.value(false)) as _i4.Future); + returnValue: Future.value(false)) as _i5.Future); @override - _i4.Future goBack() => + _i5.Future goBack() => (super.noSuchMethod(Invocation.method(#goBack, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future goForward() => + _i5.Future goForward() => (super.noSuchMethod(Invocation.method(#goForward, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future reload() => + _i5.Future reload() => (super.noSuchMethod(Invocation.method(#reload, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future clearCache(bool? includeDiskFiles) => + _i5.Future clearCache(bool? includeDiskFiles) => (super.noSuchMethod(Invocation.method(#clearCache, [includeDiskFiles]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future evaluateJavascript(String? javascriptString) => (super + _i5.Future evaluateJavascript(String? javascriptString) => (super .noSuchMethod(Invocation.method(#evaluateJavascript, [javascriptString]), - returnValue: Future.value()) as _i4.Future); + returnValue: Future.value()) as _i5.Future); @override - _i4.Future getTitle() => + _i5.Future getTitle() => (super.noSuchMethod(Invocation.method(#getTitle, []), - returnValue: Future.value()) as _i4.Future); + returnValue: Future.value()) as _i5.Future); @override - _i4.Future scrollTo(int? x, int? y) => + _i5.Future scrollTo(int? x, int? y) => (super.noSuchMethod(Invocation.method(#scrollTo, [x, y]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future scrollBy(int? x, int? y) => + _i5.Future scrollBy(int? x, int? y) => (super.noSuchMethod(Invocation.method(#scrollBy, [x, y]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future getScrollX() => + _i5.Future getScrollX() => (super.noSuchMethod(Invocation.method(#getScrollX, []), - returnValue: Future.value(0)) as _i4.Future); + returnValue: Future.value(0)) as _i5.Future); @override - _i4.Future getScrollY() => + _i5.Future getScrollY() => (super.noSuchMethod(Invocation.method(#getScrollY, []), - returnValue: Future.value(0)) as _i4.Future); + returnValue: Future.value(0)) as _i5.Future); @override - _i4.Future setWebViewClient(_i2.WebViewClient? webViewClient) => + _i5.Future setWebViewClient(_i2.WebViewClient? webViewClient) => (super.noSuchMethod(Invocation.method(#setWebViewClient, [webViewClient]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future addJavaScriptChannel( + _i5.Future addJavaScriptChannel( _i2.JavaScriptChannel? javaScriptChannel) => (super.noSuchMethod( Invocation.method(#addJavaScriptChannel, [javaScriptChannel]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future removeJavaScriptChannel( + _i5.Future removeJavaScriptChannel( _i2.JavaScriptChannel? javaScriptChannel) => (super.noSuchMethod( Invocation.method(#removeJavaScriptChannel, [javaScriptChannel]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setDownloadListener(_i2.DownloadListener? listener) => + _i5.Future setDownloadListener(_i2.DownloadListener? listener) => (super.noSuchMethod(Invocation.method(#setDownloadListener, [listener]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setWebChromeClient(_i2.WebChromeClient? client) => + _i5.Future setWebChromeClient(_i2.WebChromeClient? client) => (super.noSuchMethod(Invocation.method(#setWebChromeClient, [client]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setBackgroundColor(_i5.Color? color) => + _i5.Future setBackgroundColor(_i6.Color? color) => (super.noSuchMethod(Invocation.method(#setBackgroundColor, [color]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future release() => + _i5.Future release() => (super.noSuchMethod(Invocation.method(#release, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override String toString() => super.toString(); } diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart index f3867b313d7d..c203ef04a2ce 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:typed_data'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -407,6 +408,81 @@ void main() { )); }); + group('loadRequest', () { + testWidgets('Throws ArgumentError for empty scheme', + (WidgetTester tester) async { + await buildWidget(tester); + + expect( + () async => await testController.loadRequest( + WebViewRequest( + uri: Uri.parse('www.google.com'), + method: WebViewRequestMethod.get, + ), + ), + throwsA(const TypeMatcher())); + }); + + testWidgets('GET without headers', (WidgetTester tester) async { + await buildWidget(tester); + + await testController.loadRequest(WebViewRequest( + uri: Uri.parse('https://www.google.com'), + method: WebViewRequestMethod.get, + )); + + verify(mockWebView.loadUrl( + 'https://www.google.com', + {}, + )); + }); + + testWidgets('GET with headers', (WidgetTester tester) async { + await buildWidget(tester); + + await testController.loadRequest(WebViewRequest( + uri: Uri.parse('https://www.google.com'), + method: WebViewRequestMethod.get, + headers: {'a': 'header'}, + )); + + verify(mockWebView.loadUrl( + 'https://www.google.com', + {'a': 'header'}, + )); + }); + + testWidgets('POST without body', (WidgetTester tester) async { + await buildWidget(tester); + + await testController.loadRequest(WebViewRequest( + uri: Uri.parse('https://www.google.com'), + method: WebViewRequestMethod.post, + )); + + verify(mockWebView.postUrl( + 'https://www.google.com', + Uint8List(0), + )); + }); + + testWidgets('POST with body', (WidgetTester tester) async { + await buildWidget(tester); + + final Uint8List body = Uint8List.fromList('Test Body'.codeUnits); + + await testController.loadRequest(WebViewRequest( + uri: Uri.parse('https://www.google.com'), + method: WebViewRequestMethod.post, + body: body)); + + verify(mockWebView.postUrl( + 'https://www.google.com', + body, + )); + }); + }); + testWidgets('currentUrl', (WidgetTester tester) async { await buildWidget(tester); diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart index f3b06ea0a0bb..ece17ad61cb8 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart @@ -1,13 +1,18 @@ +// 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. + // Mocks generated by Mockito 5.0.16 from annotations // in webview_flutter_android/test/webview_android_widget_test.dart. // Do not manually edit this file. import 'dart:async' as _i4; -import 'dart:ui' as _i5; +import 'dart:typed_data' as _i5; +import 'dart:ui' as _i6; import 'package:mockito/mockito.dart' as _i1; import 'package:webview_flutter_android/src/android_webview.dart' as _i2; -import 'package:webview_flutter_android/webview_android_widget.dart' as _i6; +import 'package:webview_flutter_android/webview_android_widget.dart' as _i7; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart' as _i3; @@ -165,6 +170,11 @@ class MockWebView extends _i1.Mock implements _i2.WebView { returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i4.Future); @override + _i4.Future postUrl(String? url, _i5.Uint8List? data) => + (super.noSuchMethod(Invocation.method(#postUrl, [url, data]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override _i4.Future getUrl() => (super.noSuchMethod(Invocation.method(#getUrl, []), returnValue: Future.value()) as _i4.Future); @@ -252,7 +262,7 @@ class MockWebView extends _i1.Mock implements _i2.WebView { returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i4.Future); @override - _i4.Future setBackgroundColor(_i5.Color? color) => + _i4.Future setBackgroundColor(_i6.Color? color) => (super.noSuchMethod(Invocation.method(#setBackgroundColor, [color]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i4.Future); @@ -269,7 +279,7 @@ class MockWebView extends _i1.Mock implements _i2.WebView { /// /// See the documentation for Mockito's code generation for more information. class MockWebViewAndroidDownloadListener extends _i1.Mock - implements _i6.WebViewAndroidDownloadListener { + implements _i7.WebViewAndroidDownloadListener { MockWebViewAndroidDownloadListener() { _i1.throwOnMissingStub(this); } @@ -295,7 +305,7 @@ class MockWebViewAndroidDownloadListener extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockWebViewAndroidJavaScriptChannel extends _i1.Mock - implements _i6.WebViewAndroidJavaScriptChannel { + implements _i7.WebViewAndroidJavaScriptChannel { MockWebViewAndroidJavaScriptChannel() { _i1.throwOnMissingStub(this); } @@ -321,7 +331,7 @@ class MockWebViewAndroidJavaScriptChannel extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockWebViewAndroidWebChromeClient extends _i1.Mock - implements _i6.WebViewAndroidWebChromeClient { + implements _i7.WebViewAndroidWebChromeClient { MockWebViewAndroidWebChromeClient() { _i1.throwOnMissingStub(this); } @@ -338,7 +348,7 @@ class MockWebViewAndroidWebChromeClient extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockWebViewAndroidWebViewClient extends _i1.Mock - implements _i6.WebViewAndroidWebViewClient { + implements _i7.WebViewAndroidWebViewClient { MockWebViewAndroidWebViewClient() { _i1.throwOnMissingStub(this); } @@ -470,7 +480,7 @@ class MockWebViewPlatformCallbacksHandler extends _i1.Mock /// A class which mocks [WebViewProxy]. /// /// See the documentation for Mockito's code generation for more information. -class MockWebViewProxy extends _i1.Mock implements _i6.WebViewProxy { +class MockWebViewProxy extends _i1.Mock implements _i7.WebViewProxy { MockWebViewProxy() { _i1.throwOnMissingStub(this); } From c6914515c6fd04b93360fcf0dae86d5d4d1409be Mon Sep 17 00:00:00 2001 From: BeMacized Date: Tue, 7 Dec 2021 19:00:07 +0100 Subject: [PATCH 042/600] [webview_flutter] Add loadRequest functionality to app facing package. (#4573) * Add android implementations for loadRequest. * Update changelog and pubspec. * Fix comment. * Fix comment. * Add tests * Add back removed license headers * Fix analysis error * Add support for loadRequest to app facing package * Add test * Comment pending dependency in pubspec * Remove workaround for supporting custom headers when making post requests on Android. * Document android workaround in dart doc * Enforce uri scheme * Update loadRequest dartdoc * Document android workaround in readme * Processed PR feedback. * Updated dependency, version and changelog. --- .../webview_flutter/CHANGELOG.md | 4 ++ .../webview_flutter/webview_flutter/README.md | 5 +++ .../webview_flutter/example/lib/main.dart | 20 ++++++++++ .../lib/platform_interface.dart | 4 +- .../webview_flutter/lib/src/webview.dart | 20 ++++++++++ .../webview_flutter/pubspec.yaml | 6 +-- .../test/webview_flutter_test.dart | 25 +++++++++++++ .../test/webview_flutter_test.mocks.dart | 37 ++++++++++++------- 8 files changed, 103 insertions(+), 18 deletions(-) diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index 1f8a33db4cb4..a9e133d08d47 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.6.0 + +* Adds support for the `loadRequest` method. + ## 2.5.0 * Adds an option to set the background color of the webview. diff --git a/packages/webview_flutter/webview_flutter/README.md b/packages/webview_flutter/webview_flutter/README.md index de475ad9acd7..c3982eab2bad 100644 --- a/packages/webview_flutter/webview_flutter/README.md +++ b/packages/webview_flutter/webview_flutter/README.md @@ -92,3 +92,8 @@ android { To use Material Components when the user interacts with input elements in the WebView, follow the steps described in the [Enabling Material Components instructions](https://flutter.dev/docs/deployment/android#enabling-material-components). + +### Setting custom headers on POST requests + +Currently, setting custom headers when making a post request with the WebViewController's `loadRequest` method is not supported on Android. +If you require this functionality, a workaround is to make the request manually, and then load the response data using `loadHTMLString` instead. \ No newline at end of file diff --git a/packages/webview_flutter/webview_flutter/example/lib/main.dart b/packages/webview_flutter/webview_flutter/example/lib/main.dart index c870ae9d829d..02e6ef85ec61 100644 --- a/packages/webview_flutter/webview_flutter/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter/example/lib/main.dart @@ -7,6 +7,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; @@ -176,6 +177,7 @@ enum MenuOptions { listCache, clearCache, navigationDelegate, + doPostRequest, loadLocalFile, loadHtmlString, transparentBackground, @@ -218,6 +220,9 @@ class SampleMenu extends StatelessWidget { case MenuOptions.navigationDelegate: _onNavigationDelegateExample(controller.data!, context); break; + case MenuOptions.doPostRequest: + _onDoPostRequest(controller.data!, context); + break; case MenuOptions.loadLocalFile: _onLoadLocalFileExample(controller.data!, context); break; @@ -259,6 +264,10 @@ class SampleMenu extends StatelessWidget { value: MenuOptions.navigationDelegate, child: Text('Navigation Delegate example'), ), + const PopupMenuItem( + value: MenuOptions.doPostRequest, + child: Text('Post Request'), + ), const PopupMenuItem( value: MenuOptions.loadHtmlString, child: Text('Load HTML string'), @@ -348,6 +357,17 @@ class SampleMenu extends StatelessWidget { await controller.loadUrl('data:text/html;base64,$contentBase64'); } + Future _onDoPostRequest( + WebViewController controller, BuildContext context) async { + final WebViewRequest request = WebViewRequest( + uri: Uri.parse('https://httpbin.org/post'), + method: WebViewRequestMethod.post, + headers: {'foo': 'bar', 'Content-Type': 'text/plain'}, + body: Uint8List.fromList('Test Body'.codeUnits), + ); + await controller.loadRequest(request); + } + Future _onLoadLocalFileExample( WebViewController controller, BuildContext context) async { final String pathToIndex = await _prepareLocalFile(); diff --git a/packages/webview_flutter/webview_flutter/lib/platform_interface.dart b/packages/webview_flutter/webview_flutter/lib/platform_interface.dart index aa7b3a0931e8..ab1cbb1bd344 100644 --- a/packages/webview_flutter/webview_flutter/lib/platform_interface.dart +++ b/packages/webview_flutter/webview_flutter/lib/platform_interface.dart @@ -21,4 +21,6 @@ export 'package:webview_flutter_platform_interface/webview_flutter_platform_inte WebSetting, WebSettings, WebResourceError, - WebResourceErrorType; + WebResourceErrorType, + WebViewRequest, + WebViewRequestMethod; diff --git a/packages/webview_flutter/webview_flutter/lib/src/webview.dart b/packages/webview_flutter/webview_flutter/lib/src/webview.dart index b2cc69045e5f..d76f7b359482 100644 --- a/packages/webview_flutter/webview_flutter/lib/src/webview.dart +++ b/packages/webview_flutter/webview_flutter/lib/src/webview.dart @@ -550,6 +550,26 @@ class WebViewController { return _webViewPlatformController.loadUrl(url, headers); } + /// Makes a specific HTTP request and loads the response in the webview. + /// + /// [WebViewRequest.method] must be one of the supported HTTP methods + /// in [WebViewRequestMethod]. + /// + /// If [WebViewRequest.headers] is not empty, its key-value pairs will be + /// added as the headers for the request. + /// + /// If [WebViewRequest.body] is not null, it will be added as the body + /// for the request. + /// + /// Throws an ArgumentError if [WebViewRequest.uri] has empty scheme. + /// + /// Android only: + /// When making a POST request, headers are ignored. As a workaround, make + /// the request manually and load the response data using [loadHTMLString]. + Future loadRequest(WebViewRequest request) async { + return _webViewPlatformController.loadRequest(request); + } + /// Accessor to the current URL that the WebView is displaying. /// /// If [WebView.initialUrl] was never specified, returns `null`. diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index 6c00f65d8022..1bc6bab8b3ff 100644 --- a/packages/webview_flutter/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter description: A Flutter plugin that provides a WebView widget on Android and iOS. repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.5.0 +version: 2.6.0 environment: sdk: ">=2.14.0 <3.0.0" @@ -19,8 +19,8 @@ flutter: dependencies: flutter: sdk: flutter - webview_flutter_android: ^2.5.0 - webview_flutter_platform_interface: ^1.7.0 + webview_flutter_android: ^2.7.0 + webview_flutter_platform_interface: ^1.8.0 webview_flutter_wkwebview: ^2.5.0 dev_dependencies: diff --git a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart index da860f0118d4..d422402dbae7 100644 --- a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:typed_data'; + import 'package:flutter/src/foundation/basic_types.dart'; import 'package:flutter/src/gestures/recognizer.dart'; import 'package:flutter/widgets.dart'; @@ -246,6 +248,29 @@ void main() { )); }); + testWidgets('loadRequest', (WidgetTester tester) async { + WebViewController? controller; + await tester.pumpWidget( + WebView( + onWebViewCreated: (WebViewController webViewController) { + controller = webViewController; + }, + ), + ); + expect(controller, isNotNull); + + final WebViewRequest req = WebViewRequest( + uri: Uri.parse('https://flutter.dev'), + method: WebViewRequestMethod.post, + headers: {'foo': 'bar'}, + body: Uint8List.fromList('Test Body'.codeUnits), + ); + + await controller!.loadRequest(req); + + verify(mockWebViewPlatformController.loadRequest(req)); + }); + testWidgets('Clear Cache', (WidgetTester tester) async { WebViewController? controller; await tester.pumpWidget( diff --git a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.mocks.dart b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.mocks.dart index 8857f606f06d..fe3060164df2 100644 --- a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.mocks.dart @@ -1,22 +1,26 @@ +// 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. + // Mocks generated by Mockito 5.0.16 from annotations // in webview_flutter/test/webview_flutter_test.dart. // Do not manually edit this file. import 'dart:async' as _i9; -import 'package:flutter/foundation.dart' as _i7; +import 'package:flutter/foundation.dart' as _i3; import 'package:flutter/gestures.dart' as _i8; import 'package:flutter/widgets.dart' as _i2; import 'package:mockito/mockito.dart' as _i1; import 'package:webview_flutter_platform_interface/src/platform_interface/javascript_channel_registry.dart' - as _i6; + as _i7; import 'package:webview_flutter_platform_interface/src/platform_interface/webview_platform.dart' - as _i3; + as _i4; import 'package:webview_flutter_platform_interface/src/platform_interface/webview_platform_callbacks_handler.dart' - as _i5; + as _i6; import 'package:webview_flutter_platform_interface/src/platform_interface/webview_platform_controller.dart' as _i10; -import 'package:webview_flutter_platform_interface/src/types/types.dart' as _i4; +import 'package:webview_flutter_platform_interface/src/types/types.dart' as _i5; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters @@ -29,14 +33,14 @@ import 'package:webview_flutter_platform_interface/src/types/types.dart' as _i4; class _FakeWidget_0 extends _i1.Fake implements _i2.Widget { @override - String toString({_i2.DiagnosticLevel? minLevel = _i2.DiagnosticLevel.info}) => + String toString({_i3.DiagnosticLevel? minLevel = _i3.DiagnosticLevel.info}) => super.toString(); } /// A class which mocks [WebViewPlatform]. /// /// See the documentation for Mockito's code generation for more information. -class MockWebViewPlatform extends _i1.Mock implements _i3.WebViewPlatform { +class MockWebViewPlatform extends _i1.Mock implements _i4.WebViewPlatform { MockWebViewPlatform() { _i1.throwOnMissingStub(this); } @@ -44,11 +48,11 @@ class MockWebViewPlatform extends _i1.Mock implements _i3.WebViewPlatform { @override _i2.Widget build( {_i2.BuildContext? context, - _i4.CreationParams? creationParams, - _i5.WebViewPlatformCallbacksHandler? webViewPlatformCallbacksHandler, - _i6.JavascriptChannelRegistry? javascriptChannelRegistry, - _i3.WebViewPlatformCreatedCallback? onWebViewPlatformCreated, - Set<_i7.Factory<_i8.OneSequenceGestureRecognizer>>? + _i5.CreationParams? creationParams, + _i6.WebViewPlatformCallbacksHandler? webViewPlatformCallbacksHandler, + _i7.JavascriptChannelRegistry? javascriptChannelRegistry, + _i4.WebViewPlatformCreatedCallback? onWebViewPlatformCreated, + Set<_i3.Factory<_i8.OneSequenceGestureRecognizer>>? gestureRecognizers}) => (super.noSuchMethod( Invocation.method(#build, [], { @@ -83,6 +87,11 @@ class MockWebViewPlatformController extends _i1.Mock returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i9.Future); @override + _i9.Future loadFlutterAsset(String? key) => + (super.noSuchMethod(Invocation.method(#loadFlutterAsset, [key]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i9.Future); + @override _i9.Future loadHtmlString(String? html, {String? baseUrl}) => (super.noSuchMethod( Invocation.method(#loadHtmlString, [html], {#baseUrl: baseUrl}), @@ -94,12 +103,12 @@ class MockWebViewPlatformController extends _i1.Mock returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i9.Future); @override - _i9.Future loadRequest(_i4.WebViewRequest? request) => + _i9.Future loadRequest(_i5.WebViewRequest? request) => (super.noSuchMethod(Invocation.method(#loadRequest, [request]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i9.Future); @override - _i9.Future updateSettings(_i4.WebSettings? setting) => + _i9.Future updateSettings(_i5.WebSettings? setting) => (super.noSuchMethod(Invocation.method(#updateSettings, [setting]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i9.Future); From 60cd47ca648ce74c69e6cca3667a65047214387a Mon Sep 17 00:00:00 2001 From: BeMacized Date: Tue, 7 Dec 2021 19:44:05 +0100 Subject: [PATCH 043/600] [webview_flutter] Add Android implementations for new cookie manager, to allow setting cookies directly and on webview creation. (#4557) --- .../webview_flutter_android/CHANGELOG.md | 4 + .../CookieManagerHostApiImpl.java | 29 ++++ .../webviewflutter/FlutterCookieManager.java | 56 ------- .../GeneratedAndroidWebView.java | 88 +++++++++++ .../webviewflutter/WebViewFlutterPlugin.java | 14 +- .../CookieManagerHostApiImplTest.java | 83 ++++++++++ .../webviewflutter/utils/TestUtils.java | 47 ++++++ .../example/lib/main.dart | 19 ++- .../example/lib/web_view.dart | 25 +++ .../lib/src/android_webview.dart | 43 +++++ .../lib/src/android_webview.pigeon.dart | 66 ++++++++ .../lib/webview_android.dart | 8 +- .../lib/webview_android_cookie_manager.dart | 36 +++++ .../lib/webview_android_widget.dart | 9 ++ .../pigeons/android_webview.dart | 8 + .../webview_flutter_android/pubspec.yaml | 2 +- .../test/android_webview_test.dart | 17 ++ .../test/android_webview_test.mocks.dart | 147 ++++++++++-------- .../webview_android_cookie_manager_test.dart | 53 +++++++ ...iew_android_cookie_manager_test.mocks.dart | 38 +++++ 20 files changed, 660 insertions(+), 132 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/CookieManagerHostApiImpl.java delete mode 100644 packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterCookieManager.java create mode 100644 packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/CookieManagerHostApiImplTest.java create mode 100644 packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/utils/TestUtils.java create mode 100644 packages/webview_flutter/webview_flutter_android/lib/webview_android_cookie_manager.dart create mode 100644 packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.dart create mode 100644 packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.mocks.dart diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index ce73ab26cad0..7bd33879cd7d 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.8.0 + +* Implements new cookie manager for setting cookies and providing initial cookies. + ## 2.7.0 * Adds support for the `loadRequest` method from the platform interface. 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 new file mode 100644 index 000000000000..3e38ce94b3a5 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/CookieManagerHostApiImpl.java @@ -0,0 +1,29 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.webviewflutter; + +import android.os.Build; +import android.webkit.CookieManager; + +class CookieManagerHostApiImpl implements GeneratedAndroidWebView.CookieManagerHostApi { + @Override + public void clearCookies(GeneratedAndroidWebView.Result result) { + CookieManager cookieManager = CookieManager.getInstance(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + cookieManager.removeAllCookies(result::success); + } else { + final boolean hasCookies = cookieManager.hasCookies(); + if (hasCookies) { + cookieManager.removeAllCookie(); + } + result.success(hasCookies); + } + } + + @Override + public void setCookie(String url, String value) { + CookieManager.getInstance().setCookie(url, value); + } +} diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterCookieManager.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterCookieManager.java deleted file mode 100644 index df3f21daadeb..000000000000 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterCookieManager.java +++ /dev/null @@ -1,56 +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; - -import android.os.Build; -import android.os.Build.VERSION_CODES; -import android.webkit.CookieManager; -import android.webkit.ValueCallback; -import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.plugin.common.MethodCall; -import io.flutter.plugin.common.MethodChannel; -import io.flutter.plugin.common.MethodChannel.MethodCallHandler; -import io.flutter.plugin.common.MethodChannel.Result; - -class FlutterCookieManager implements MethodCallHandler { - private final MethodChannel methodChannel; - - FlutterCookieManager(BinaryMessenger messenger) { - methodChannel = new MethodChannel(messenger, "plugins.flutter.io/cookie_manager"); - methodChannel.setMethodCallHandler(this); - } - - @Override - public void onMethodCall(MethodCall methodCall, Result result) { - switch (methodCall.method) { - case "clearCookies": - clearCookies(result); - break; - default: - result.notImplemented(); - } - } - - void dispose() { - methodChannel.setMethodCallHandler(null); - } - - private static void clearCookies(final Result result) { - CookieManager cookieManager = CookieManager.getInstance(); - final boolean hasCookies = cookieManager.hasCookies(); - if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { - cookieManager.removeAllCookies( - new ValueCallback() { - @Override - public void onReceiveValue(Boolean value) { - result.success(hasCookies); - } - }); - } else { - cookieManager.removeAllCookie(); - result.success(hasCookies); - } - } -} diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java index 0e6776ecd618..8ef0b8d11f96 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java @@ -162,6 +162,94 @@ public interface Result { void error(Throwable error); } + private static class CookieManagerHostApiCodec extends StandardMessageCodec { + public static final CookieManagerHostApiCodec INSTANCE = new CookieManagerHostApiCodec(); + + private CookieManagerHostApiCodec() {} + } + + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ + public interface CookieManagerHostApi { + void clearCookies(Result result); + + void setCookie(String url, String value); + + /** The codec used by CookieManagerHostApi. */ + static MessageCodec getCodec() { + return CookieManagerHostApiCodec.INSTANCE; + } + + /** + * Sets up an instance of `CookieManagerHostApi` to handle messages through the + * `binaryMessenger`. + */ + static void setup(BinaryMessenger binaryMessenger, CookieManagerHostApi api) { + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.CookieManagerHostApi.clearCookies", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + Result resultCallback = + new Result() { + public void success(Boolean result) { + wrapped.put("result", result); + reply.reply(wrapped); + } + + public void error(Throwable error) { + wrapped.put("error", wrapError(error)); + reply.reply(wrapped); + } + }; + + api.clearCookies(resultCallback); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + reply.reply(wrapped); + } + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.CookieManagerHostApi.setCookie", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + String urlArg = (String) args.get(0); + if (urlArg == null) { + throw new NullPointerException("urlArg unexpectedly null."); + } + String valueArg = (String) args.get(1); + if (valueArg == null) { + throw new NullPointerException("valueArg unexpectedly null."); + } + api.setCookie(urlArg, valueArg); + wrapped.put("result", null); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + } + } + private static class WebViewHostApiCodec extends StandardMessageCodec { public static final WebViewHostApiCodec INSTANCE = new WebViewHostApiCodec(); diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java index cbeda8dcf493..4ef622fe47cb 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java @@ -13,6 +13,7 @@ import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.platform.PlatformViewRegistry; +import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.CookieManagerHostApi; import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.DownloadListenerHostApi; import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.FlutterAssetManagerHostApi; import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.JavaScriptChannelHostApi; @@ -30,7 +31,6 @@ */ public class WebViewFlutterPlugin implements FlutterPlugin, ActivityAware { private FlutterPluginBinding pluginBinding; - private FlutterCookieManager flutterCookieManager; private WebViewHostApiImpl webViewHostApi; private JavaScriptChannelHostApiImpl javaScriptChannelHostApi; @@ -65,7 +65,6 @@ public static void registerWith(io.flutter.plugin.common.PluginRegistry.Registra registrar.view(), new FlutterAssetManager.RegistrarFlutterAssetManager( registrar.context().getAssets(), registrar)); - new FlutterCookieManager(registrar.messenger()); } private void setUp( @@ -74,7 +73,6 @@ private void setUp( Context context, View containerView, FlutterAssetManager flutterAssetManager) { - new FlutterCookieManager(binaryMessenger); InstanceManager instanceManager = new InstanceManager(); @@ -117,6 +115,7 @@ private void setUp( instanceManager, new WebSettingsHostApiImpl.WebSettingsCreator())); FlutterAssetManagerHostApi.setup( binaryMessenger, new FlutterAssetManagerHostApiImpl(flutterAssetManager)); + CookieManagerHostApi.setup(binaryMessenger, new CookieManagerHostApiImpl()); } @Override @@ -132,14 +131,7 @@ public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { } @Override - public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { - if (flutterCookieManager == null) { - return; - } - - flutterCookieManager.dispose(); - flutterCookieManager = null; - } + public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {} @Override public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBinding) { diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/CookieManagerHostApiImplTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/CookieManagerHostApiImplTest.java new file mode 100644 index 000000000000..6daeb1be7f63 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/CookieManagerHostApiImplTest.java @@ -0,0 +1,83 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.webviewflutter; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.os.Build; +import android.webkit.CookieManager; +import android.webkit.ValueCallback; +import io.flutter.plugins.webviewflutter.utils.TestUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.MockedStatic; + +public class CookieManagerHostApiImplTest { + + private CookieManager cookieManager; + private MockedStatic staticMockCookieManager; + + @Before + public void setup() { + staticMockCookieManager = mockStatic(CookieManager.class); + cookieManager = mock(CookieManager.class); + when(CookieManager.getInstance()).thenReturn(cookieManager); + when(cookieManager.hasCookies()).thenReturn(true); + doAnswer( + answer -> { + ((ValueCallback) answer.getArgument(0)).onReceiveValue(true); + return null; + }) + .when(cookieManager) + .removeAllCookies(any()); + } + + @After + public void tearDown() { + staticMockCookieManager.close(); + } + + @Test + public void setCookieShouldCallSetCookie() { + // Setup + CookieManagerHostApiImpl impl = new CookieManagerHostApiImpl(); + // Run + impl.setCookie("flutter.dev", "foo=bar; path=/"); + // Verify + verify(cookieManager).setCookie("flutter.dev", "foo=bar; path=/"); + } + + @Test + public void clearCookiesShouldCallRemoveAllCookiesOnAndroidLAbove() { + // Setup + TestUtils.setFinalStatic(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.LOLLIPOP); + GeneratedAndroidWebView.Result result = mock(GeneratedAndroidWebView.Result.class); + CookieManagerHostApiImpl impl = new CookieManagerHostApiImpl(); + // Run + impl.clearCookies(result); + // Verify + verify(cookieManager).removeAllCookies(any()); + verify(result).success(true); + } + + @Test + public void clearCookiesShouldCallRemoveAllCookieBelowAndroidL() { + // Setup + TestUtils.setFinalStatic(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.KITKAT_WATCH); + GeneratedAndroidWebView.Result result = mock(GeneratedAndroidWebView.Result.class); + CookieManagerHostApiImpl impl = new CookieManagerHostApiImpl(); + // Run + impl.clearCookies(result); + // Verify + verify(cookieManager).removeAllCookie(); + verify(result).success(true); + } +} 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 new file mode 100644 index 000000000000..31e7d58ee13f --- /dev/null +++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/utils/TestUtils.java @@ -0,0 +1,47 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +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); + } + } + + public static void setPrivateField(T instance, String fieldName, Object newValue) { + try { + Field field = instance.getClass().getDeclaredField(fieldName); + field.setAccessible(true); + field.set(instance, newValue); + } catch (Exception e) { + Assert.fail("Unable to mock private field: " + fieldName); + } + } + + public static Object getPrivateField(T instance, String fieldName) { + try { + Field field = instance.getClass().getDeclaredField(fieldName); + field.setAccessible(true); + return field.get(instance); + } catch (Exception e) { + Assert.fail("Unable to mock private field: " + fieldName); + return null; + } + } +} diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart index eb3162a179ff..0d0cd59af796 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart @@ -191,6 +191,7 @@ enum _MenuOptions { loadHtmlString, transparentBackground, doPostRequest, + setCookie, } class _SampleMenu extends StatelessWidget { @@ -244,6 +245,9 @@ class _SampleMenu extends StatelessWidget { case _MenuOptions.doPostRequest: _onDoPostRequest(controller.data!, context); break; + case _MenuOptions.setCookie: + _onSetCookie(controller.data!, context); + break; } }, itemBuilder: (BuildContext context) => >[ @@ -297,6 +301,10 @@ class _SampleMenu extends StatelessWidget { value: _MenuOptions.doPostRequest, child: Text('Post Request'), ), + const PopupMenuItem<_MenuOptions>( + value: _MenuOptions.setCookie, + child: Text('Set Cookie'), + ), ], ); }, @@ -356,7 +364,7 @@ class _SampleMenu extends StatelessWidget { Future _onClearCookies( WebViewController controller, BuildContext context) async { - final bool hadCookies = await WebView.platform.clearCookies(); + final bool hadCookies = await WebViewCookieManager.instance.clearCookies(); String message = 'There were cookies. Now, they are gone!'; if (!hadCookies) { message = 'There are no cookies.'; @@ -367,6 +375,15 @@ class _SampleMenu extends StatelessWidget { )); } + Future _onSetCookie( + WebViewController controller, BuildContext context) async { + await WebViewCookieManager.instance.setCookie( + const WebViewCookie( + name: 'foo', value: 'bar', domain: 'httpbin.org', path: '/anything'), + ); + await controller.loadUrl('https://httpbin.org/anything'); + } + Future _onNavigationDelegateExample( WebViewController controller, BuildContext context) async { final String contentBase64 = diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart index adaf7fbb3ee8..91ea66376904 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart @@ -3,11 +3,13 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:webview_flutter_android/webview_android.dart'; +import 'package:webview_flutter_android/webview_android_cookie_manager.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; import 'navigation_decision.dart'; @@ -61,6 +63,7 @@ class WebView extends StatefulWidget { Key? key, this.onWebViewCreated, this.initialUrl, + this.initialCookies = const [], this.javascriptMode = JavascriptMode.disabled, this.javascriptChannels, this.navigationDelegate, @@ -104,6 +107,9 @@ class WebView extends StatefulWidget { /// The initial URL to load. final String? initialUrl; + /// The initial cookies to set. + final List initialCookies; + /// Whether JavaScript execution is enabled. final JavascriptMode javascriptMode; @@ -295,6 +301,7 @@ class _WebViewState extends State { autoMediaPlaybackPolicy: widget.initialMediaPlaybackPolicy, userAgent: widget.userAgent, backgroundColor: widget.backgroundColor, + cookies: widget.initialCookies, ), javascriptChannelRegistry: _javascriptChannelRegistry, ); @@ -674,3 +681,21 @@ WebSettings _webSettingsFromWidget(WebView widget) { zoomEnabled: widget.zoomEnabled, ); } + +/// App-facing cookie manager that exposes the correct platform implementation. +class WebViewCookieManager extends WebViewCookieManagerPlatform { + WebViewCookieManager._(); + + /// Returns an instance of the cookie manager for the current platform. + static WebViewCookieManagerPlatform get instance { + if (WebViewCookieManagerPlatform.instance == null) { + if (Platform.isAndroid) { + WebViewCookieManagerPlatform.instance = WebViewAndroidCookieManager(); + } else { + throw AssertionError( + 'This platform is currently unsupported for webview_flutter_android.'); + } + } + return WebViewCookieManagerPlatform.instance!; + } +} diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart index d02701643291..dfa05cd92ee6 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart @@ -375,6 +375,49 @@ class WebView { } } +/// Manages cookies globally for all webviews. +class CookieManager { + CookieManager._(); + + static CookieManager? _instance; + + /// Gets the globally set CookieManager instance. + static CookieManager get instance => _instance ??= CookieManager._(); + + /// Setter for the singleton value, for testing purposes only. + @visibleForTesting + static set instance(CookieManager value) => _instance = value; + + /// Pigeon Host Api implementation for [CookieManager]. + @visibleForTesting + static CookieManagerHostApi api = CookieManagerHostApi(); + + /// Sets a single cookie (key-value pair) for the given URL. Any existing + /// cookie with the same host, path and name will be replaced with the new + /// cookie. The cookie being set will be ignored if it is expired. To set + /// multiple cookies, your application should invoke this method multiple + /// times. + /// + /// The value parameter must follow the format of the Set-Cookie HTTP + /// response header defined by RFC6265bis. This is a key-value pair of the + /// form "key=value", optionally followed by a list of cookie attributes + /// delimited with semicolons (ex. "key=value; Max-Age=123"). Please consult + /// the RFC specification for a list of valid attributes. + /// + /// Note: if specifying a value containing the "Secure" attribute, url must + /// use the "https://" scheme. + /// + /// Params: + /// url – the URL for which the cookie is to be set + /// value – the cookie as a string, using the format of the 'Set-Cookie' HTTP response header + Future setCookie(String url, String value) => api.setCookie(url, value); + + /// Removes all cookies. + /// + /// The returned future resolves to true if any cookies were removed. + Future clearCookies() => api.clearCookies(); +} + /// Manages settings state for a [WebView]. /// /// When a WebView is first created, it obtains a set of default settings. These diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart index e0a1db311b74..810a71732e7b 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart @@ -63,6 +63,72 @@ class WebResourceErrorData { } } +class _CookieManagerHostApiCodec extends StandardMessageCodec { + const _CookieManagerHostApiCodec(); +} + +class CookieManagerHostApi { + /// Constructor for [CookieManagerHostApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + CookieManagerHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _CookieManagerHostApiCodec(); + + Future clearCookies() async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.CookieManagerHostApi.clearCookies', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send(null) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return (replyMap['result'] as bool?)!; + } + } + + Future setCookie(String arg_url, String arg_value) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.CookieManagerHostApi.setCookie', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_url, arg_value]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} + class _WebViewHostApiCodec extends StandardMessageCodec { const _WebViewHostApiCodec(); } diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_android.dart index ee474b9d43f8..1f0eb7bd7ded 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/webview_android.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/webview_android.dart @@ -65,5 +65,11 @@ class AndroidWebView implements WebViewPlatform { } @override - Future clearCookies() => MethodChannelWebViewPlatform.clearCookies(); + Future clearCookies() { + if (WebViewCookieManagerPlatform.instance == null) { + throw Exception( + 'Could not clear cookies as no implementation for WebViewCookieManagerPlatform has been registered.'); + } + return WebViewCookieManagerPlatform.instance!.clearCookies(); + } } diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android_cookie_manager.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_android_cookie_manager.dart new file mode 100644 index 000000000000..bba75ff4f613 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_android/lib/webview_android_cookie_manager.dart @@ -0,0 +1,36 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:webview_flutter_android/src/android_webview.dart' + as android_webview; +import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; + +/// Handles all cookie operations for the current platform. +class WebViewAndroidCookieManager extends WebViewCookieManagerPlatform { + @override + Future clearCookies() => + android_webview.CookieManager.instance.clearCookies(); + + @override + Future setCookie(WebViewCookie cookie) { + if (!_isValidPath(cookie.path)) { + throw ArgumentError( + 'The path property for the provided cookie was not given a legal value.'); + } + return android_webview.CookieManager.instance.setCookie( + cookie.domain, + '${Uri.encodeComponent(cookie.name)}=${Uri.encodeComponent(cookie.value)}; path=${cookie.path}', + ); + } + + bool _isValidPath(String path) { + // Permitted ranges based on RFC6265bis: https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-02#section-4.1.1 + for (final int char in path.codeUnits) { + if ((char < 0x20 || char > 0x3A) && (char < 0x3C || char > 0x7E)) { + return false; + } + } + return true; + } +} diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart index fc295be65aa3..1dec9c105741 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:typed_data'; import 'package:flutter/widgets.dart'; +import 'package:webview_flutter_android/webview_android_cookie_manager.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; import 'src/android_webview.dart' as android_webview; @@ -363,6 +364,14 @@ class WebViewAndroidPlatformController extends WebViewPlatformController { } addJavascriptChannels(creationParams.javascriptChannelNames); + + // TODO(BeMacized): Remove once platform implementations + // are able to register themselves (Flutter >=2.8), + // https://github.com/flutter/flutter/issues/94224 + WebViewCookieManagerPlatform.instance ??= WebViewAndroidCookieManager(); + + creationParams.cookies + .forEach(WebViewCookieManagerPlatform.instance!.setCookie); } Future _setHasProgressTracking(bool hasProgressTracking) async { diff --git a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart index 4907dadb41b7..36862f7cbacc 100644 --- a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart +++ b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart @@ -18,6 +18,14 @@ class WebResourceErrorData { String? description; } +@HostApi() +abstract class CookieManagerHostApi { + @async + bool clearCookies(); + + void setCookie(String url, String value); +} + @HostApi(dartHostTestHandler: 'TestWebViewHostApi') abstract class WebViewHostApi { void create(int instanceId, bool useHybridComposition); diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 08bcbb50602e..34bea570ae43 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_android description: A Flutter plugin that provides a WebView widget on Android. repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.7.0 +version: 2.8.0 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart index b903678ecef8..cc29fc755067 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart @@ -14,6 +14,7 @@ import 'android_webview.pigeon.dart'; import 'android_webview_test.mocks.dart'; @GenerateMocks([ + CookieManagerHostApi, DownloadListener, JavaScriptChannel, TestDownloadListenerHostApi, @@ -647,4 +648,20 @@ void main() { }); }); }); + + group('CookieManager', () { + test('setCookie calls setCookie on CookieManagerHostApi', () { + CookieManager.api = MockCookieManagerHostApi(); + CookieManager.instance.setCookie('foo', 'bar'); + verify(CookieManager.api.setCookie('foo', 'bar')); + }); + + test('clearCookies calls clearCookies on CookieManagerHostApi', () { + CookieManager.api = MockCookieManagerHostApi(); + when(CookieManager.api.clearCookies()) + .thenAnswer((_) => Future.value(true)); + CookieManager.instance.clearCookies(); + verify(CookieManager.api.clearCookies()); + }); + }); } diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart index 8bb569497b59..d25d2338886b 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart @@ -6,14 +6,15 @@ // in webview_flutter_android/test/android_webview_test.dart. // Do not manually edit this file. -import 'dart:async' as _i5; -import 'dart:typed_data' as _i4; -import 'dart:ui' as _i6; +import 'dart:async' as _i4; +import 'dart:typed_data' as _i6; +import 'dart:ui' as _i7; import 'package:mockito/mockito.dart' as _i1; import 'package:webview_flutter_android/src/android_webview.dart' as _i2; +import 'package:webview_flutter_android/src/android_webview.pigeon.dart' as _i3; -import 'android_webview.pigeon.dart' as _i3; +import 'android_webview.pigeon.dart' as _i5; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters @@ -26,6 +27,28 @@ import 'android_webview.pigeon.dart' as _i3; class _FakeWebSettings_0 extends _i1.Fake implements _i2.WebSettings {} +/// A class which mocks [CookieManagerHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockCookieManagerHostApi extends _i1.Mock + implements _i3.CookieManagerHostApi { + MockCookieManagerHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + _i4.Future clearCookies() => + (super.noSuchMethod(Invocation.method(#clearCookies, []), + returnValue: Future.value(false)) as _i4.Future); + @override + _i4.Future setCookie(String? arg_url, String? arg_value) => + (super.noSuchMethod(Invocation.method(#setCookie, [arg_url, arg_value]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + String toString() => super.toString(); +} + /// A class which mocks [DownloadListener]. /// /// See the documentation for Mockito's code generation for more information. @@ -69,7 +92,7 @@ class MockJavaScriptChannel extends _i1.Mock implements _i2.JavaScriptChannel { /// /// See the documentation for Mockito's code generation for more information. class MockTestDownloadListenerHostApi extends _i1.Mock - implements _i3.TestDownloadListenerHostApi { + implements _i5.TestDownloadListenerHostApi { MockTestDownloadListenerHostApi() { _i1.throwOnMissingStub(this); } @@ -86,7 +109,7 @@ class MockTestDownloadListenerHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestJavaScriptChannelHostApi extends _i1.Mock - implements _i3.TestJavaScriptChannelHostApi { + implements _i5.TestJavaScriptChannelHostApi { MockTestJavaScriptChannelHostApi() { _i1.throwOnMissingStub(this); } @@ -103,7 +126,7 @@ class MockTestJavaScriptChannelHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestWebChromeClientHostApi extends _i1.Mock - implements _i3.TestWebChromeClientHostApi { + implements _i5.TestWebChromeClientHostApi { MockTestWebChromeClientHostApi() { _i1.throwOnMissingStub(this); } @@ -121,7 +144,7 @@ class MockTestWebChromeClientHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestWebSettingsHostApi extends _i1.Mock - implements _i3.TestWebSettingsHostApi { + implements _i5.TestWebSettingsHostApi { MockTestWebSettingsHostApi() { _i1.throwOnMissingStub(this); } @@ -195,7 +218,7 @@ class MockTestWebSettingsHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestWebViewClientHostApi extends _i1.Mock - implements _i3.TestWebViewClientHostApi { + implements _i5.TestWebViewClientHostApi { MockTestWebViewClientHostApi() { _i1.throwOnMissingStub(this); } @@ -213,7 +236,7 @@ class MockTestWebViewClientHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestWebViewHostApi extends _i1.Mock - implements _i3.TestWebViewHostApi { + implements _i5.TestWebViewHostApi { MockTestWebViewHostApi() { _i1.throwOnMissingStub(this); } @@ -246,7 +269,7 @@ class MockTestWebViewHostApi extends _i1.Mock Invocation.method(#loadUrl, [instanceId, url, headers]), returnValueForMissingStub: null); @override - void postUrl(int? instanceId, String? url, _i4.Uint8List? data) => + void postUrl(int? instanceId, String? url, _i6.Uint8List? data) => super.noSuchMethod(Invocation.method(#postUrl, [instanceId, url, data]), returnValueForMissingStub: null); @override @@ -279,12 +302,12 @@ class MockTestWebViewHostApi extends _i1.Mock Invocation.method(#clearCache, [instanceId, includeDiskFiles]), returnValueForMissingStub: null); @override - _i5.Future evaluateJavascript( + _i4.Future evaluateJavascript( int? instanceId, String? javascriptString) => (super.noSuchMethod( Invocation.method( #evaluateJavascript, [instanceId, javascriptString]), - returnValue: Future.value('')) as _i5.Future); + returnValue: Future.value('')) as _i4.Future); @override String getTitle(int? instanceId) => (super.noSuchMethod(Invocation.method(#getTitle, [instanceId]), @@ -353,7 +376,7 @@ class MockTestWebViewHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestAssetManagerHostApi extends _i1.Mock - implements _i3.TestAssetManagerHostApi { + implements _i5.TestAssetManagerHostApi { MockTestAssetManagerHostApi() { _i1.throwOnMissingStub(this); } @@ -403,15 +426,15 @@ class MockWebView extends _i1.Mock implements _i2.WebView { (super.noSuchMethod(Invocation.getter(#settings), returnValue: _FakeWebSettings_0()) as _i2.WebSettings); @override - _i5.Future loadData( + _i4.Future loadData( {String? data, String? mimeType, String? encoding}) => (super.noSuchMethod( Invocation.method(#loadData, [], {#data: data, #mimeType: mimeType, #encoding: encoding}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i4.Future); @override - _i5.Future loadDataWithBaseUrl( + _i4.Future loadDataWithBaseUrl( {String? baseUrl, String? data, String? mimeType, @@ -426,114 +449,114 @@ class MockWebView extends _i1.Mock implements _i2.WebView { #historyUrl: historyUrl }), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i4.Future); @override - _i5.Future loadUrl(String? url, Map? headers) => + _i4.Future loadUrl(String? url, Map? headers) => (super.noSuchMethod(Invocation.method(#loadUrl, [url, headers]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i4.Future); @override - _i5.Future postUrl(String? url, _i4.Uint8List? data) => + _i4.Future postUrl(String? url, _i6.Uint8List? data) => (super.noSuchMethod(Invocation.method(#postUrl, [url, data]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i4.Future); @override - _i5.Future getUrl() => + _i4.Future getUrl() => (super.noSuchMethod(Invocation.method(#getUrl, []), - returnValue: Future.value()) as _i5.Future); + returnValue: Future.value()) as _i4.Future); @override - _i5.Future canGoBack() => + _i4.Future canGoBack() => (super.noSuchMethod(Invocation.method(#canGoBack, []), - returnValue: Future.value(false)) as _i5.Future); + returnValue: Future.value(false)) as _i4.Future); @override - _i5.Future canGoForward() => + _i4.Future canGoForward() => (super.noSuchMethod(Invocation.method(#canGoForward, []), - returnValue: Future.value(false)) as _i5.Future); + returnValue: Future.value(false)) as _i4.Future); @override - _i5.Future goBack() => + _i4.Future goBack() => (super.noSuchMethod(Invocation.method(#goBack, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i4.Future); @override - _i5.Future goForward() => + _i4.Future goForward() => (super.noSuchMethod(Invocation.method(#goForward, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i4.Future); @override - _i5.Future reload() => + _i4.Future reload() => (super.noSuchMethod(Invocation.method(#reload, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i4.Future); @override - _i5.Future clearCache(bool? includeDiskFiles) => + _i4.Future clearCache(bool? includeDiskFiles) => (super.noSuchMethod(Invocation.method(#clearCache, [includeDiskFiles]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i4.Future); @override - _i5.Future evaluateJavascript(String? javascriptString) => (super + _i4.Future evaluateJavascript(String? javascriptString) => (super .noSuchMethod(Invocation.method(#evaluateJavascript, [javascriptString]), - returnValue: Future.value()) as _i5.Future); + returnValue: Future.value()) as _i4.Future); @override - _i5.Future getTitle() => + _i4.Future getTitle() => (super.noSuchMethod(Invocation.method(#getTitle, []), - returnValue: Future.value()) as _i5.Future); + returnValue: Future.value()) as _i4.Future); @override - _i5.Future scrollTo(int? x, int? y) => + _i4.Future scrollTo(int? x, int? y) => (super.noSuchMethod(Invocation.method(#scrollTo, [x, y]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i4.Future); @override - _i5.Future scrollBy(int? x, int? y) => + _i4.Future scrollBy(int? x, int? y) => (super.noSuchMethod(Invocation.method(#scrollBy, [x, y]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i4.Future); @override - _i5.Future getScrollX() => + _i4.Future getScrollX() => (super.noSuchMethod(Invocation.method(#getScrollX, []), - returnValue: Future.value(0)) as _i5.Future); + returnValue: Future.value(0)) as _i4.Future); @override - _i5.Future getScrollY() => + _i4.Future getScrollY() => (super.noSuchMethod(Invocation.method(#getScrollY, []), - returnValue: Future.value(0)) as _i5.Future); + returnValue: Future.value(0)) as _i4.Future); @override - _i5.Future setWebViewClient(_i2.WebViewClient? webViewClient) => + _i4.Future setWebViewClient(_i2.WebViewClient? webViewClient) => (super.noSuchMethod(Invocation.method(#setWebViewClient, [webViewClient]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i4.Future); @override - _i5.Future addJavaScriptChannel( + _i4.Future addJavaScriptChannel( _i2.JavaScriptChannel? javaScriptChannel) => (super.noSuchMethod( Invocation.method(#addJavaScriptChannel, [javaScriptChannel]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i4.Future); @override - _i5.Future removeJavaScriptChannel( + _i4.Future removeJavaScriptChannel( _i2.JavaScriptChannel? javaScriptChannel) => (super.noSuchMethod( Invocation.method(#removeJavaScriptChannel, [javaScriptChannel]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i4.Future); @override - _i5.Future setDownloadListener(_i2.DownloadListener? listener) => + _i4.Future setDownloadListener(_i2.DownloadListener? listener) => (super.noSuchMethod(Invocation.method(#setDownloadListener, [listener]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i4.Future); @override - _i5.Future setWebChromeClient(_i2.WebChromeClient? client) => + _i4.Future setWebChromeClient(_i2.WebChromeClient? client) => (super.noSuchMethod(Invocation.method(#setWebChromeClient, [client]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i4.Future); @override - _i5.Future setBackgroundColor(_i6.Color? color) => + _i4.Future setBackgroundColor(_i7.Color? color) => (super.noSuchMethod(Invocation.method(#setBackgroundColor, [color]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i4.Future); @override - _i5.Future release() => + _i4.Future release() => (super.noSuchMethod(Invocation.method(#release, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i4.Future); @override String toString() => super.toString(); } diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.dart new file mode 100644 index 000000000000..4f274ff4499f --- /dev/null +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.dart @@ -0,0 +1,53 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:webview_flutter_android/src/android_webview.dart' + as android_webview; +import 'package:webview_flutter_android/webview_android_cookie_manager.dart'; +import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; + +import 'webview_android_cookie_manager_test.mocks.dart'; + +@GenerateMocks([android_webview.CookieManager]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + android_webview.CookieManager.instance = MockCookieManager(); + }); + + test('clearCookies should call android_webview.clearCookies', () { + when(android_webview.CookieManager.instance.clearCookies()) + .thenAnswer((_) => Future.value(true)); + WebViewAndroidCookieManager().clearCookies(); + verify(android_webview.CookieManager.instance.clearCookies()); + }); + + test('setCookie should throw ArgumentError for cookie with invalid path', () { + expect( + () => WebViewAndroidCookieManager().setCookie(const WebViewCookie( + name: 'foo', + value: 'bar', + domain: 'flutter.dev', + path: 'invalid;path', + )), + throwsA(const TypeMatcher()), + ); + }); + + test( + 'setCookie should call android_webview.csetCookie with properly formatted cookie value', + () { + WebViewAndroidCookieManager().setCookie(const WebViewCookie( + name: 'foo&', + value: 'bar@', + domain: 'flutter.dev', + )); + verify(android_webview.CookieManager.instance + .setCookie('flutter.dev', 'foo%26=bar%40; path=/')); + }); +} diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.mocks.dart new file mode 100644 index 000000000000..131977bd3dfd --- /dev/null +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.mocks.dart @@ -0,0 +1,38 @@ +// Mocks generated by Mockito 5.0.16 from annotations +// in webview_flutter_android/test/webview_android_cookie_manager_test.dart. +// Do not manually edit this file. + +import 'dart:async' as _i3; + +import 'package:mockito/mockito.dart' as _i1; +import 'package:webview_flutter_android/src/android_webview.dart' as _i2; + +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types + +/// A class which mocks [CookieManager]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockCookieManager extends _i1.Mock implements _i2.CookieManager { + MockCookieManager() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.Future setCookie(String? url, String? value) => + (super.noSuchMethod(Invocation.method(#setCookie, [url, value]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i3.Future); + @override + _i3.Future clearCookies() => + (super.noSuchMethod(Invocation.method(#clearCookies, []), + returnValue: Future.value(false)) as _i3.Future); + @override + String toString() => super.toString(); +} From ab99ed5d590ecf25ea606a6fc32aba5569b09e69 Mon Sep 17 00:00:00 2001 From: BeMacized Date: Tue, 7 Dec 2021 20:24:05 +0100 Subject: [PATCH 044/600] [webview_flutter] Add iOS implementations for new cookie manager, to allow setting cookies directly and on webview creation. (#4556) --- .../webview_flutter_wkwebview/CHANGELOG.md | 4 + .../webview_flutter_test.dart | 11 +- .../ios/Runner.xcodeproj/project.pbxproj | 73 +++++----- .../ios/RunnerTests/FLTCookieManagerTests.m | 127 ++++++++++++++++++ .../FLTWKNavigationDelegateTests.m | 1 + .../example/ios/RunnerTests/FLTWebViewTests.m | 26 +++- .../example/lib/main.dart | 24 +++- .../example/lib/web_view.dart | 24 ++++ .../ios/Classes/FLTCookieManager.h | 6 + .../ios/Classes/FLTCookieManager.m | 62 ++++++++- .../ios/Classes/FLTCookieManager_Test.h | 20 +++ .../ios/Classes/FLTWebViewFlutterPlugin.m | 5 +- .../ios/Classes/FlutterWebView.h | 4 +- .../ios/Classes/FlutterWebView.m | 9 +- .../ios/Classes/FlutterWebView.modulemap | 1 + .../lib/src/webview_cupertino.dart | 8 +- .../lib/src/wkwebview_cookie_manager.dart | 30 +++++ .../lib/webview_flutter_wkwebview.dart | 1 + .../webview_flutter_wkwebview/pubspec.yaml | 4 +- .../src/wkwebview_cookie_manager_test.dart | 65 +++++++++ 20 files changed, 448 insertions(+), 57 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTCookieManagerTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager_Test.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/wkwebview_cookie_manager.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/src/wkwebview_cookie_manager_test.dart diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index 22f45f27c4d7..c1d27121f9ee 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.6.0 + +* Implements new cookie manager for setting cookies and providing initial cookies. + ## 2.5.0 * Adds an option to set the background color of the webview. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart index 8806495e629c..1e4adb9f7de0 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart @@ -1161,16 +1161,7 @@ String _webviewBool(bool value) { /// Returns the value used for the HTTP User-Agent: request header in subsequent HTTP requests. Future _getUserAgent(WebViewController controller) async { - return _runJavascriptReturningResult(controller, 'navigator.userAgent;'); -} - -Future _runJavascriptReturningResult( - WebViewController controller, String js) async { - if (defaultTargetPlatform == TargetPlatform.iOS) { - return await controller.runJavascriptReturningResult(js); - } - return jsonDecode(await controller.runJavascriptReturningResult(js)) - as String; + return await controller.runJavascriptReturningResult('navigator.userAgent;'); } class ResizableWebView extends StatefulWidget { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj index e292b1bd52fa..b681c4704ddd 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 46; objects = { /* Begin PBXBuildFile section */ @@ -16,8 +16,9 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - AE8C124DC8CA68E4D9B30EAB /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 528CB85D53C983D2C5DAFDC5 /* libPods-RunnerTests.a */; }; + D7587C3652F6906210B3AE88 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 17781D9462A1AEA7C99F8E45 /* libPods-RunnerTests.a */; }; DAF0E91266956134538CC667 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 572FFC2B2BA326B420B22679 /* libPods-Runner.a */; }; + E43693B527512C0F00382F85 /* FLTCookieManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E43693B427512C0F00382F85 /* FLTCookieManagerTests.m */; }; F7151F77266057800028CB91 /* FLTWebViewUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = F7151F76266057800028CB91 /* FLTWebViewUITests.m */; }; /* End PBXBuildFile section */ @@ -54,10 +55,11 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 17781D9462A1AEA7C99F8E45 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 2286ACB87EA8CA27E739AD6C /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 39B2BDAA45DC06EAB8A6C4E7 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 528CB85D53C983D2C5DAFDC5 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 572FFC2B2BA326B420B22679 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 5C776D27D0DDA247ED5EA72B /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; 686B4BF82548DBC7000AEA36 /* FLTWKNavigationDelegateTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTWKNavigationDelegateTests.m; sourceTree = ""; }; 68BDCAE923C3F7CB00D9C032 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 68BDCAED23C3F7CB00D9C032 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -74,7 +76,7 @@ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; B89AA31A64040E4A2F1E0CAF /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - C370F140C3A19241FD8C5E64 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + E43693B427512C0F00382F85 /* FLTCookieManagerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTCookieManagerTests.m; sourceTree = ""; }; F7151F74266057800028CB91 /* RunnerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; F7151F76266057800028CB91 /* FLTWebViewUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTWebViewUITests.m; sourceTree = ""; }; F7151F78266057800028CB91 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -86,7 +88,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - AE8C124DC8CA68E4D9B30EAB /* libPods-RunnerTests.a in Frameworks */, + D7587C3652F6906210B3AE88 /* libPods-RunnerTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -112,7 +114,7 @@ isa = PBXGroup; children = ( 572FFC2B2BA326B420B22679 /* libPods-Runner.a */, - 528CB85D53C983D2C5DAFDC5 /* libPods-RunnerTests.a */, + 17781D9462A1AEA7C99F8E45 /* libPods-RunnerTests.a */, ); name = Frameworks; sourceTree = ""; @@ -123,6 +125,7 @@ 686B4BF82548DBC7000AEA36 /* FLTWKNavigationDelegateTests.m */, 68BDCAF523C3F97800D9C032 /* FLTWebViewTests.m */, 68BDCAED23C3F7CB00D9C032 /* Info.plist */, + E43693B427512C0F00382F85 /* FLTCookieManagerTests.m */, ); path = RunnerTests; sourceTree = ""; @@ -190,8 +193,8 @@ children = ( F7A1921261392D1CBDAEC2E8 /* Pods-Runner.debug.xcconfig */, B89AA31A64040E4A2F1E0CAF /* Pods-Runner.release.xcconfig */, - C370F140C3A19241FD8C5E64 /* Pods-RunnerTests.debug.xcconfig */, - 5C776D27D0DDA247ED5EA72B /* Pods-RunnerTests.release.xcconfig */, + 39B2BDAA45DC06EAB8A6C4E7 /* Pods-RunnerTests.debug.xcconfig */, + 2286ACB87EA8CA27E739AD6C /* Pods-RunnerTests.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -212,7 +215,7 @@ isa = PBXNativeTarget; buildConfigurationList = 68BDCAF223C3F7CB00D9C032 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( - 0067CEC0658A36CBFF8074E7 /* [CP] Check Pods Manifest.lock */, + AA38EF430495C2FB50F0F114 /* [CP] Check Pods Manifest.lock */, 68BDCAE523C3F7CB00D9C032 /* Sources */, 68BDCAE623C3F7CB00D9C032 /* Frameworks */, 68BDCAE723C3F7CB00D9C032 /* Resources */, @@ -277,13 +280,16 @@ ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { 68BDCAE823C3F7CB00D9C032 = { + DevelopmentTeam = 7624MWN53C; ProvisioningStyle = Automatic; }; 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; + DevelopmentTeam = 7624MWN53C; }; F7151F73266057800028CB91 = { CreatedOnToolsVersion = 12.5; + DevelopmentTeam = 7624MWN53C; ProvisioningStyle = Automatic; TestTargetID = 97C146ED1CF9000F007C117D; }; @@ -338,7 +344,21 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 0067CEC0658A36CBFF8074E7 /* [CP] Check Pods Manifest.lock */ = { + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed\n/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin\n"; + }; + 6F536C27DD48B395A30EBB65 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -353,28 +373,28 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Thin Binary"; + name = "Run Script"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed\n/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin\n"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n"; }; - 6F536C27DD48B395A30EBB65 /* [CP] Check Pods Manifest.lock */ = { + AA38EF430495C2FB50F0F114 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -389,27 +409,13 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 9740EEB61CF901F6004384FC /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n"; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -419,6 +425,7 @@ files = ( 334734012669319100DCC49E /* FLTWebViewTests.m in Sources */, 334734022669319400DCC49E /* FLTWKNavigationDelegateTests.m in Sources */, + E43693B527512C0F00382F85 /* FLTCookieManagerTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -477,7 +484,7 @@ /* Begin XCBuildConfiguration section */ 68BDCAF023C3F7CB00D9C032 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = C370F140C3A19241FD8C5E64 /* Pods-RunnerTests.debug.xcconfig */; + baseConfigurationReference = 39B2BDAA45DC06EAB8A6C4E7 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -491,7 +498,7 @@ }; 68BDCAF123C3F7CB00D9C032 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 5C776D27D0DDA247ED5EA72B /* Pods-RunnerTests.release.xcconfig */; + baseConfigurationReference = 2286ACB87EA8CA27E739AD6C /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTCookieManagerTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTCookieManagerTests.m new file mode 100644 index 000000000000..837c0d892b64 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTCookieManagerTests.m @@ -0,0 +1,127 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import Flutter; +@import XCTest; +@import webview_flutter_wkwebview; +@import webview_flutter_wkwebview.Test; + +// OCMock library doesn't generate a valid modulemap. +#import + +@interface FLTCookieManagerTests : XCTestCase + +@end + +@implementation FLTCookieManagerTests + +- (void)setUp { + [super setUp]; +} + +- (void)testSetCookieForResultSetsCookieAndReturnsResultOnIOS11 { + if (@available(iOS 11.0, *)) { + // Setup + XCTestExpectation *resultExpectation = [self + expectationWithDescription:@"Should return success result when setting cookie completes."]; + [FLTCookieManager.instance setHttpCookieStore:OCMClassMock(WKHTTPCookieStore.class)]; + NSDictionary *arguments = @{ + @"name" : @"foo", + @"value" : @"bar", + @"domain" : @"flutter.dev", + @"path" : @"/", + }; + NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:@{ + NSHTTPCookieName : arguments[@"name"], + NSHTTPCookieValue : arguments[@"value"], + NSHTTPCookieDomain : arguments[@"domain"], + NSHTTPCookiePath : arguments[@"path"], + }]; + [OCMStub([FLTCookieManager.instance.httpCookieStore setCookie:[OCMArg isEqual:cookie] + completionHandler:[OCMArg any]]) + andDo:^(NSInvocation *invocation) { + void (^setCookieCompletionHandler)(void); + [invocation getArgument:&setCookieCompletionHandler atIndex:3]; + setCookieCompletionHandler(); + }]; + // Run + [[FLTCookieManager instance] + setCookieForResult:^(id _Nullable result) { + XCTAssertNil(result); + [resultExpectation fulfill]; + } + arguments:arguments]; + // Verify + [self waitForExpectationsWithTimeout:30.0 handler:nil]; + } +} + +- (void)testSetCookieForDataSetsCookieOnIOS11 { + if (@available(iOS 11.0, *)) { + // Setup + WKHTTPCookieStore *mockHttpCookieStore = OCMClassMock(WKHTTPCookieStore.class); + [FLTCookieManager.instance setHttpCookieStore:mockHttpCookieStore]; + NSDictionary *cookieData = @{ + @"name" : @"foo", + @"value" : @"bar", + @"domain" : @"flutter.dev", + @"path" : @"/", + }; + // Run + [[FLTCookieManager instance] setCookieForData:cookieData]; + // Verify + NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:@{ + NSHTTPCookieName : cookieData[@"name"], + NSHTTPCookieValue : cookieData[@"value"], + NSHTTPCookieDomain : cookieData[@"domain"], + NSHTTPCookiePath : cookieData[@"path"], + }]; + OCMVerify([mockHttpCookieStore setCookie:[OCMArg isEqual:cookie] + completionHandler:[OCMArg any]]); + } +} + +- (void)testSetCookiesForDataSetsCookiesOnIOS11 { + if (@available(iOS 11.0, *)) { + // Setup + WKHTTPCookieStore *mockHttpCookieStore = OCMClassMock(WKHTTPCookieStore.class); + [FLTCookieManager.instance setHttpCookieStore:mockHttpCookieStore]; + NSArray *cookieDatas = @[ + @{ + @"name" : @"foo1", + @"value" : @"bar1", + @"domain" : @"flutter.dev", + @"path" : @"/", + }, + @{ + @"name" : @"foo2", + @"value" : @"bar2", + @"domain" : @"flutter2.dev", + @"path" : @"/2", + } + ]; + // Run + [[FLTCookieManager instance] setCookiesForData:cookieDatas]; + // Verify + NSHTTPCookie *cookie1 = [NSHTTPCookie cookieWithProperties:@{ + NSHTTPCookieName : cookieDatas[0][@"name"], + NSHTTPCookieValue : cookieDatas[0][@"value"], + NSHTTPCookieDomain : cookieDatas[0][@"domain"], + NSHTTPCookiePath : cookieDatas[0][@"path"], + }]; + + OCMVerify([mockHttpCookieStore setCookie:[OCMArg isEqual:cookie1] + completionHandler:[OCMArg any]]); + NSHTTPCookie *cookie2 = [NSHTTPCookie cookieWithProperties:@{ + NSHTTPCookieName : cookieDatas[1][@"name"], + NSHTTPCookieValue : cookieDatas[1][@"value"], + NSHTTPCookieDomain : cookieDatas[1][@"domain"], + NSHTTPCookiePath : cookieDatas[1][@"path"], + }]; + OCMVerify([mockHttpCookieStore setCookie:[OCMArg isEqual:cookie2] + completionHandler:[OCMArg any]]); + } +} + +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWKNavigationDelegateTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWKNavigationDelegateTests.m index a819a9b53d60..d39a9f203d70 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWKNavigationDelegateTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWKNavigationDelegateTests.m @@ -5,6 +5,7 @@ @import Flutter; @import XCTest; @import webview_flutter_wkwebview; +@import webview_flutter_wkwebview.Test; // OCMock library doesn't generate a valid modulemap. #import diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWebViewTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWebViewTests.m index a3c314a79d2a..6c7c6bffbc3f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWebViewTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWebViewTests.m @@ -16,6 +16,8 @@ @interface FLTWebViewTests : XCTestCase @property(strong, nonatomic) NSObject *mockBinaryMessenger; +@property(strong, nonatomic) FLTCookieManager *mockCookieManager; + @end @implementation FLTWebViewTests @@ -23,6 +25,7 @@ @implementation FLTWebViewTests - (void)setUp { [super setUp]; self.mockBinaryMessenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); + self.mockCookieManager = OCMClassMock(FLTCookieManager.class); } - (void)testCanInitFLTWebViewController { @@ -35,8 +38,8 @@ - (void)testCanInitFLTWebViewController { } - (void)testCanInitFLTWebViewFactory { - FLTWebViewFactory *factory = - [[FLTWebViewFactory alloc] initWithMessenger:self.mockBinaryMessenger]; + FLTWebViewFactory *factory = [[FLTWebViewFactory alloc] initWithMessenger:self.mockBinaryMessenger + cookieManager:self.mockCookieManager]; XCTAssertNotNil(factory); } @@ -648,7 +651,7 @@ - (void)testOnLoadUrlLoadsRequestWithSuccessResult { [self waitForExpectationsWithTimeout:30.0 handler:nil]; } -- (void)testOnLoadRequestReturnsErroResultForInvalidRequest { +- (void)testOnLoadRequestReturnsErrorResultForInvalidRequest { // Setup FLTWebViewController *controller = [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) @@ -697,4 +700,21 @@ - (void)testOnLoadRequestLoadsRequestWithSuccessResult { [self waitForExpectationsWithTimeout:30.0 handler:nil]; } +- (void)testCreateWithFrameShouldSetCookiesOnIOS11 { + if (@available(iOS 11, *)) { + // Setup + FLTWebViewFactory *factory = + [[FLTWebViewFactory alloc] initWithMessenger:self.mockBinaryMessenger + cookieManager:self.mockCookieManager]; + NSArray *cookies = + @[ @{@"name" : @"foo", @"value" : @"bar", @"domain" : @"flutter.dev", @"path" : @"/"} ]; + // Run + [factory createWithFrame:CGRectMake(0, 0, 300, 400) + viewIdentifier:1 + arguments:@{@"cookies" : cookies}]; + // Verify + OCMVerify([_mockCookieManager setCookiesForData:cookies]); + } +} + @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart index 41297bfd94fe..ce7548a87cab 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart @@ -88,6 +88,11 @@ class _WebViewExampleState extends State<_WebViewExample> { final Completer _controller = Completer(); + @override + void initState() { + super.initState(); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -104,7 +109,7 @@ class _WebViewExampleState extends State<_WebViewExample> { // to allow calling Scaffold.of(context) so we can show a snackbar. body: Builder(builder: (BuildContext context) { return WebView( - initialUrl: 'https://flutter.dev', + initialUrl: 'https://flutter.dev/', onWebViewCreated: (WebViewController controller) { _controller.complete(controller); }, @@ -169,6 +174,7 @@ enum _MenuOptions { loadLocalFile, loadHtmlString, doPostRequest, + setCookie, transparentBackground, } @@ -217,6 +223,9 @@ class _SampleMenu extends StatelessWidget { case _MenuOptions.doPostRequest: _onDoPostRequest(controller.data!, context); break; + case _MenuOptions.setCookie: + _onSetCookie(controller.data!, context); + break; case _MenuOptions.transparentBackground: _onTransparentBackground(controller.data!, context); break; @@ -264,6 +273,10 @@ class _SampleMenu extends StatelessWidget { value: _MenuOptions.doPostRequest, child: Text('Post Request'), ), + const PopupMenuItem<_MenuOptions>( + value: _MenuOptions.setCookie, + child: Text('Set Cookie'), + ), const PopupMenuItem<_MenuOptions>( key: ValueKey('ShowTransparentBackgroundExample'), value: _MenuOptions.transparentBackground, @@ -365,6 +378,15 @@ class _SampleMenu extends StatelessWidget { await controller.loadRequest(request); } + Future _onSetCookie( + WebViewController controller, BuildContext context) async { + await WebViewCookieManager.instance.setCookie( + const WebViewCookie( + name: 'foo', value: 'bar', domain: 'httpbin.org', path: '/anything'), + ); + await controller.loadUrl('https://httpbin.org/anything'); + } + Widget _getCookieList(String cookies) { if (cookies == null || cookies == '""') { return Container(); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart index 69fa2bdb79e3..1efd0315072b 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; @@ -54,6 +55,7 @@ class WebView extends StatefulWidget { Key? key, this.onWebViewCreated, this.initialUrl, + this.initialCookies = const [], this.javascriptMode = JavascriptMode.disabled, this.javascriptChannels, this.navigationDelegate, @@ -95,6 +97,9 @@ class WebView extends StatefulWidget { /// The initial URL to load. final String? initialUrl; + /// The initial cookies to set. + final List initialCookies; + /// Whether JavaScript execution is enabled. final JavascriptMode javascriptMode; @@ -285,6 +290,7 @@ class _WebViewState extends State { _javascriptChannelRegistry.channels.keys.toSet(), autoMediaPlaybackPolicy: widget.initialMediaPlaybackPolicy, userAgent: widget.userAgent, + cookies: widget.initialCookies, backgroundColor: widget.backgroundColor, ), javascriptChannelRegistry: _javascriptChannelRegistry, @@ -660,3 +666,21 @@ class _PlatformCallbacksHandler implements WebViewPlatformCallbacksHandler { } } } + +/// App-facing cookie manager that exposes the correct platform implementation. +class WebViewCookieManager extends WebViewCookieManagerPlatform { + WebViewCookieManager._(); + + /// Returns an instance of the cookie manager for the current platform. + static WebViewCookieManagerPlatform get instance { + if (WebViewCookieManagerPlatform.instance == null) { + if (Platform.isIOS) { + WebViewCookieManagerPlatform.instance = WKWebViewCookieManager(); + } else { + throw AssertionError( + 'This platform is currently unsupported for webview_flutter_wkwebview.'); + } + } + return WebViewCookieManagerPlatform.instance!; + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager.h index 8fe331875250..dc5b8f589ade 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager.h @@ -9,6 +9,12 @@ NS_ASSUME_NONNULL_BEGIN @interface FLTCookieManager : NSObject ++ (FLTCookieManager*)instance; + +- (void)setCookiesForData:(NSArray*)cookies; + +- (void)setCookieForData:(NSDictionary*)cookie; + @end NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager.m index f4783ffb4123..39976d11153e 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager.m @@ -3,22 +3,32 @@ // found in the LICENSE file. #import "FLTCookieManager.h" +#import "FLTCookieManager_Test.h" @implementation FLTCookieManager { + WKHTTPCookieStore *_httpCookieStore API_AVAILABLE(macos(10.13), ios(11.0)); } -+ (void)registerWithRegistrar:(NSObject *)registrar { - FLTCookieManager *instance = [[FLTCookieManager alloc] init]; ++ (FLTCookieManager *)instance { + static FLTCookieManager *instance = nil; + if (instance == nil) { + instance = [[FLTCookieManager alloc] init]; + } + return instance; +} ++ (void)registerWithRegistrar:(NSObject *)registrar { FlutterMethodChannel *channel = [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/cookie_manager" binaryMessenger:[registrar messenger]]; - [registrar addMethodCallDelegate:instance channel:channel]; + [registrar addMethodCallDelegate:[self instance] channel:channel]; } - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { if ([[call method] isEqualToString:@"clearCookies"]) { [self clearCookies:result]; + } else if ([[call method] isEqualToString:@"setCookie"]) { + [self setCookieForResult:result arguments:[call arguments]]; } else { result(FlutterMethodNotImplemented); } @@ -46,4 +56,50 @@ - (void)clearCookies:(FlutterResult)result { } } +- (void)setCookiesForData:(NSArray *)cookies { + for (id cookie in cookies) { + [self setCookieForData:cookie]; + } +} + +- (void)setCookieForData:(NSDictionary *)cookieData { + if (@available(iOS 11.0, *)) { + if (!_httpCookieStore) { + _httpCookieStore = [[WKWebsiteDataStore defaultDataStore] httpCookieStore]; + } + NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:@{ + NSHTTPCookieName : cookieData[@"name"], + NSHTTPCookieValue : cookieData[@"value"], + NSHTTPCookieDomain : cookieData[@"domain"], + NSHTTPCookiePath : cookieData[@"path"], + }]; + [_httpCookieStore setCookie:cookie + completionHandler:^{ + }]; + } else { + NSLog(@"Setting cookies is not supported for Flutter WebViews prior to iOS 11."); + } +} + +- (void)setCookieForResult:(FlutterResult)result arguments:(NSDictionary *)arguments { + if (@available(iOS 11.0, *)) { + if (!_httpCookieStore) { + _httpCookieStore = [[WKWebsiteDataStore defaultDataStore] httpCookieStore]; + } + NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:@{ + NSHTTPCookieName : arguments[@"name"], + NSHTTPCookieValue : arguments[@"value"], + NSHTTPCookieDomain : arguments[@"domain"], + NSHTTPCookiePath : arguments[@"path"], + }]; + [_httpCookieStore setCookie:cookie + completionHandler:^{ + result(nil); + }]; + } else { + NSLog(@"Setting cookies is not supported for Flutter WebViews prior to iOS 11."); + result(nil); + } +} + @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager_Test.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager_Test.h new file mode 100644 index 000000000000..fecec4932570 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager_Test.h @@ -0,0 +1,20 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This header is available in the Test module. Import via "@import webview_flutter_wkwebview.Test;" + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface FLTCookieManager () + +@property(nonatomic, strong) + WKHTTPCookieStore *httpCookieStore API_AVAILABLE(macos(10.13), ios(11.0)); + +- (void)setCookieForResult:(FlutterResult)result arguments:(NSDictionary *)arguments; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWebViewFlutterPlugin.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWebViewFlutterPlugin.m index 9f01416acc6a..0d04d6c3a9ee 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWebViewFlutterPlugin.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWebViewFlutterPlugin.m @@ -9,10 +9,11 @@ @implementation FLTWebViewFlutterPlugin + (void)registerWithRegistrar:(NSObject*)registrar { + [FLTCookieManager registerWithRegistrar:registrar]; FLTWebViewFactory* webviewFactory = - [[FLTWebViewFactory alloc] initWithMessenger:registrar.messenger]; + [[FLTWebViewFactory alloc] initWithMessenger:registrar.messenger + cookieManager:[FLTCookieManager instance]]; [registrar registerViewFactory:webviewFactory withId:@"plugins.flutter.io/webview"]; - [FLTCookieManager registerWithRegistrar:registrar]; } @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.h index 6d8e463c7b13..145a772c5015 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.h @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#import #import #import @@ -31,7 +32,8 @@ NS_ASSUME_NONNULL_BEGIN @end @interface FLTWebViewFactory : NSObject -- (instancetype)initWithMessenger:(NSObject*)messenger; +- (instancetype)initWithMessenger:(NSObject*)messenger + cookieManager:(FLTCookieManager*)cookieManager; @end NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m index 25a93a67addc..ea45a8b9b534 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m @@ -10,12 +10,15 @@ @implementation FLTWebViewFactory { NSObject* _messenger; + FLTCookieManager* _cookieManager; } -- (instancetype)initWithMessenger:(NSObject*)messenger { +- (instancetype)initWithMessenger:(NSObject*)messenger + cookieManager:(FLTCookieManager*)cookieManager { self = [super init]; if (self) { _messenger = messenger; + _cookieManager = cookieManager; } return self; } @@ -27,6 +30,10 @@ - (instancetype)initWithMessenger:(NSObject*)messenger { - (NSObject*)createWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args { + if (@available(iOS 11.0, *)) { + [_cookieManager setCookiesForData:args[@"cookies"]]; + } + FLTWebViewController* webviewController = [[FLTWebViewController alloc] initWithFrame:frame viewIdentifier:viewId arguments:args diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.modulemap b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.modulemap index fa5143060381..096507557688 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.modulemap +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.modulemap @@ -6,5 +6,6 @@ framework module webview_flutter_wkwebview { explicit module Test { header "FlutterWebView_Test.h" + header "FLTCookieManager_Test.h" } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webview_cupertino.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webview_cupertino.dart index 05b79d0a72e4..cd00d9a7d14b 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webview_cupertino.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webview_cupertino.dart @@ -45,5 +45,11 @@ class CupertinoWebView implements WebViewPlatform { } @override - Future clearCookies() => MethodChannelWebViewPlatform.clearCookies(); + Future clearCookies() { + if (WebViewCookieManagerPlatform.instance == null) { + throw Exception( + 'Could not clear cookies as no implementation for WebViewCookieManagerPlatform has been registered.'); + } + return WebViewCookieManagerPlatform.instance!.clearCookies(); + } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/wkwebview_cookie_manager.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/wkwebview_cookie_manager.dart new file mode 100644 index 000000000000..d460ce02ab68 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/wkwebview_cookie_manager.dart @@ -0,0 +1,30 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; + +/// Handles all cookie operations for the current platform. +class WKWebViewCookieManager extends WebViewCookieManagerPlatform { + @override + Future clearCookies() => MethodChannelWebViewPlatform.clearCookies(); + + @override + Future setCookie(WebViewCookie cookie) { + if (!_isValidPath(cookie.path)) { + throw ArgumentError( + 'The path property for the provided cookie was not given a legal value.'); + } + return MethodChannelWebViewPlatform.setCookie(cookie); + } + + bool _isValidPath(String path) { + // Permitted ranges based on RFC6265bis: https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-02#section-4.1.1 + for (final int char in path.codeUnits) { + if ((char < 0x20 || char > 0x3A) && (char < 0x3C || char > 0x7E)) { + return false; + } + } + return true; + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/webview_flutter_wkwebview.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/webview_flutter_wkwebview.dart index bbec415dccd0..f647ab38a41b 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/webview_flutter_wkwebview.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/webview_flutter_wkwebview.dart @@ -3,3 +3,4 @@ // found in the LICENSE file. export 'src/webview_cupertino.dart'; +export 'src/wkwebview_cookie_manager.dart'; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 702473382793..fc274b453995 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_wkwebview description: A Flutter plugin that provides a WebView widget based on Apple's WKWebView control. repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter_wkwebview issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.5.0 +version: 2.6.0 environment: sdk: ">=2.14.0 <3.0.0" @@ -18,7 +18,7 @@ flutter: dependencies: flutter: sdk: flutter - webview_flutter_platform_interface: ^1.7.0 + webview_flutter_platform_interface: ^1.8.0 dev_dependencies: flutter_driver: diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/wkwebview_cookie_manager_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/wkwebview_cookie_manager_test.dart new file mode 100644 index 000000000000..54b1921583b9 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/wkwebview_cookie_manager_test.dart @@ -0,0 +1,65 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; +import 'package:webview_flutter_wkwebview/src/wkwebview_cookie_manager.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + const MethodChannel cookieChannel = + MethodChannel('plugins.flutter.io/cookie_manager'); + final List log = []; + + cookieChannel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + + if (methodCall.method == 'clearCookies') { + return true; + } + + // Return null explicitly instead of relying on the implicit null + // returned by the method channel if no return statement is specified. + return null; + }); + + tearDown(() { + log.clear(); + }); + + test('clearCookies should call `clearCookies` on the method channel', + () async { + await WKWebViewCookieManager().clearCookies(); + expect( + log, + [ + isMethodCall( + 'clearCookies', + arguments: null, + ), + ], + ); + }); + + test('setCookie should call `setCookie` on the method channel', () async { + await WKWebViewCookieManager().setCookie( + const WebViewCookie(name: 'foo', value: 'bar', domain: 'flutter.dev'), + ); + expect( + log, + [ + isMethodCall( + 'setCookie', + arguments: { + 'name': 'foo', + 'value': 'bar', + 'domain': 'flutter.dev', + 'path': '/', + }, + ), + ], + ); + }); +} From 131036d0282dee7b0ee3c2bb4279035ac77145fe Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Tue, 7 Dec 2021 21:39:05 +0100 Subject: [PATCH 045/600] [webview_flutter] WKWebView implementation of loadFlutterAsset method. (#4582) --- .../webview_flutter_wkwebview/CHANGELOG.md | 4 + .../example/assets/www/index.html | 20 +++ .../example/assets/www/styles/style.css | 3 + .../example/ios/RunnerTests/FLTWebViewTests.m | 117 +++++++++++++++++- .../example/lib/main.dart | 13 ++ .../example/lib/web_view.dart | 8 ++ .../example/pubspec.yaml | 2 + .../ios/Classes/FlutterWebView.m | 30 +++++ .../webview_flutter_wkwebview/pubspec.yaml | 2 +- 9 files changed, 196 insertions(+), 3 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/assets/www/index.html create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/assets/www/styles/style.css diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index c1d27121f9ee..0aaf4bcf57aa 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.7.0 + +* Adds implementation of the `loadFlutterAsset` method from the platform interface. + ## 2.6.0 * Implements new cookie manager for setting cookies and providing initial cookies. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/assets/www/index.html b/packages/webview_flutter/webview_flutter_wkwebview/example/assets/www/index.html new file mode 100644 index 000000000000..9895dd3ce6cb --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/assets/www/index.html @@ -0,0 +1,20 @@ + + + + +Load file or HTML string example + + + + +

Local demo page

+

+ This is an example page used to demonstrate how to load a local file or HTML + string using the Flutter + webview plugin. +

+ + + \ No newline at end of file diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/assets/www/styles/style.css b/packages/webview_flutter/webview_flutter_wkwebview/example/assets/www/styles/style.css new file mode 100644 index 000000000000..c2140b8b0fd8 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/assets/www/styles/style.css @@ -0,0 +1,3 @@ +h1 { + color: blue; +} \ No newline at end of file diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWebViewTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWebViewTests.m index 6c7c6bffbc3f..976b9c46579a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWebViewTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWebViewTests.m @@ -164,7 +164,120 @@ - (void)testLoadFileFailsWithInvalidPath { OCMReject([mockWebView loadFileURL:[OCMArg any] allowingReadAccessToURL:[OCMArg any]]); } -- (void)testLoadFileSucceedsWithBaseUrl { +- (void)testLoadFlutterAssetSucceeds { + NSBundle *mockBundle = OCMPartialMock([NSBundle mainBundle]); + NSString *filePath = [FlutterDartProject lookupKeyForAsset:@"assets/file.html"]; + NSURL *url = [NSURL URLWithString:[@"file:///" stringByAppendingString:filePath]]; + [OCMStub([mockBundle URLForResource:[filePath stringByDeletingPathExtension] + withExtension:@"html"]) andReturn:(url)]; + + XCTestExpectation *resultExpectation = + [self expectationWithDescription:@"Should return successful result over the method channel."]; + FLTWebViewController *controller = + [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) + viewIdentifier:1 + arguments:nil + binaryMessenger:self.mockBinaryMessenger]; + FLTWKWebView *mockWebView = OCMClassMock(FLTWKWebView.class); + controller.webView = mockWebView; + [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFlutterAsset" + arguments:@"assets/file.html"] + result:^(id _Nullable result) { + XCTAssertNil(result); + [resultExpectation fulfill]; + }]; + + [self waitForExpectations:@[ resultExpectation ] timeout:1.0]; + OCMVerify([mockWebView loadFileURL:url + allowingReadAccessToURL:[url URLByDeletingLastPathComponent]]); +} + +- (void)testLoadFlutterAssetFailsWithInvalidKey { + NSArray *resultExpectations = @[ + [self expectationWithDescription:@"Should return failed result when argument is nil."], + [self expectationWithDescription: + @"Should return failed result when argument is not of type NSString*."], + [self expectationWithDescription: + @"Should return failed result when argument is an empty string."], + ]; + + FLTWebViewController *controller = + [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) + viewIdentifier:1 + arguments:nil + binaryMessenger:self.mockBinaryMessenger]; + FLTWKWebView *mockWebView = OCMClassMock(FLTWKWebView.class); + controller.webView = mockWebView; + [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFlutterAsset" + arguments:nil] + result:^(id _Nullable result) { + FlutterError *expected = + [FlutterError errorWithCode:@"loadFlutterAsset_invalidKey" + message:@"Supplied asset key is not valid." + details:@"Argument is nil."]; + [FLTWebViewTests assertFlutterError:result withExpected:expected]; + [resultExpectations[0] fulfill]; + }]; + [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFlutterAsset" + arguments:@(10)] + result:^(id _Nullable result) { + FlutterError *expected = + [FlutterError errorWithCode:@"loadFlutterAsset_invalidKey" + message:@"Supplied asset key is not valid." + details:@"Argument is not of type NSString."]; + [FLTWebViewTests assertFlutterError:result withExpected:expected]; + [resultExpectations[1] fulfill]; + }]; + [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFlutterAsset" + arguments:@""] + result:^(id _Nullable result) { + FlutterError *expected = + [FlutterError errorWithCode:@"loadFlutterAsset_invalidKey" + message:@"Supplied asset key is not valid." + details:@"Argument contains an empty string."]; + [FLTWebViewTests assertFlutterError:result withExpected:expected]; + [resultExpectations[2] fulfill]; + }]; + + [self waitForExpectations:resultExpectations timeout:1.0]; + OCMReject([mockWebView loadFileURL:[OCMArg any] allowingReadAccessToURL:[OCMArg any]]); +} + +- (void)testLoadFlutterAssetFailsWithParsingError { + NSBundle *mockBundle = OCMPartialMock([NSBundle mainBundle]); + NSString *filePath = [FlutterDartProject lookupKeyForAsset:@"assets/file.html"]; + [OCMStub([mockBundle URLForResource:[filePath stringByDeletingPathExtension] + withExtension:@"html"]) andReturn:(nil)]; + + XCTestExpectation *resultExpectation = + [self expectationWithDescription:@"Should return failed result over the method channel."]; + FLTWebViewController *controller = + [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) + viewIdentifier:1 + arguments:nil + binaryMessenger:self.mockBinaryMessenger]; + FLTWKWebView *mockWebView = OCMClassMock(FLTWKWebView.class); + controller.webView = mockWebView; + [controller + onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFlutterAsset" + arguments:@"assets/file.html"] + result:^(id _Nullable result) { + FlutterError *expected = [FlutterError + errorWithCode:@"loadFlutterAsset_invalidKey" + message:@"Failed parsing file path for supplied key." + details:[NSString + stringWithFormat: + @"Failed to convert path '%@' into NSURL for key '%@'.", + filePath, @"assets/file.html"]]; + [FLTWebViewTests assertFlutterError:result withExpected:expected]; + [resultExpectation fulfill]; + }]; + + [self waitForExpectations:@[ resultExpectation ] timeout:1.0]; + OCMReject([mockWebView loadFileURL:[OCMArg any] allowingReadAccessToURL:[OCMArg any]]); +} + +- (void)testLoadHtmlStringSucceedsWithBaseUrl { NSURL *baseUrl = [NSURL URLWithString:@"https://flutter.dev"]; XCTestExpectation *resultExpectation = [self expectationWithDescription:@"Should return successful result over the method channel."]; @@ -189,7 +302,7 @@ - (void)testLoadFileSucceedsWithBaseUrl { OCMVerify([mockWebView loadHTMLString:@"some HTML string" baseURL:baseUrl]); } -- (void)testLoadFileSucceedsWithoutBaseUrl { +- (void)testLoadHtmlStringSucceedsWithoutBaseUrl { XCTestExpectation *resultExpectation = [self expectationWithDescription:@"Should return successful result over the method channel."]; FLTWebViewController *controller = diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart index ce7548a87cab..d4e0ec4aba0c 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart @@ -171,6 +171,7 @@ enum _MenuOptions { listCache, clearCache, navigationDelegate, + loadFlutterAsset, loadLocalFile, loadHtmlString, doPostRequest, @@ -214,6 +215,9 @@ class _SampleMenu extends StatelessWidget { case _MenuOptions.navigationDelegate: _onNavigationDelegateExample(controller.data!, context); break; + case _MenuOptions.loadFlutterAsset: + _onLoadFlutterAssetExample(controller.data!, context); + break; case _MenuOptions.loadLocalFile: _onLoadLocalFileExample(controller.data!, context); break; @@ -261,6 +265,10 @@ class _SampleMenu extends StatelessWidget { value: _MenuOptions.navigationDelegate, child: Text('Navigation Delegate example'), ), + const PopupMenuItem<_MenuOptions>( + value: _MenuOptions.loadFlutterAsset, + child: Text('Load Flutter Asset'), + ), const PopupMenuItem<_MenuOptions>( value: _MenuOptions.loadHtmlString, child: Text('Load HTML string'), @@ -355,6 +363,11 @@ class _SampleMenu extends StatelessWidget { await controller.loadUrl('data:text/html;base64,$contentBase64'); } + Future _onLoadFlutterAssetExample( + WebViewController controller, BuildContext context) async { + await controller.loadFlutterAsset('assets/www/index.html'); + } + Future _onLoadLocalFileExample( WebViewController controller, BuildContext context) async { final String pathToIndex = await _prepareLocalFile(); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart index 1efd0315072b..4d479f943d62 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart @@ -319,6 +319,14 @@ class WebViewController { WebView _widget; + /// Loads the Flutter asset specified in the pubspec.yaml file. + /// + /// Throws an ArgumentError if [key] is not part of the specified assets + /// in the pubspec.yaml file. + Future loadFlutterAsset(String key) { + return _webViewPlatformController.loadFlutterAsset(key); + } + /// Loads the file located on the specified [absoluteFilePath]. /// /// The [absoluteFilePath] parameter should contain the absolute path to the diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml index 2070b1161ba0..b8c2464eb051 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml @@ -34,3 +34,5 @@ flutter: assets: - assets/sample_audio.ogg - assets/sample_video.mp4 + - assets/www/index.html + - assets/www/styles/style.css diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m index ea45a8b9b534..3f3b9d917783 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m @@ -163,6 +163,8 @@ - (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { [self onUpdateSettings:call result:result]; } else if ([[call method] isEqualToString:@"loadFile"]) { [self onLoadFile:call result:result]; + } else if ([[call method] isEqualToString:@"loadFlutterAsset"]) { + [self onLoadFlutterAsset:call result:result]; } else if ([[call method] isEqualToString:@"loadHtmlString"]) { [self onLoadHtmlString:call result:result]; } else if ([[call method] isEqualToString:@"loadUrl"]) { @@ -244,6 +246,34 @@ - (void)onLoadFile:(FlutterMethodCall*)call result:(FlutterResult)result { result(nil); } +- (void)onLoadFlutterAsset:(FlutterMethodCall*)call result:(FlutterResult)result { + NSString* error = nil; + if (![FLTWebViewController isValidStringArgument:[call arguments] withErrorMessage:&error]) { + result([FlutterError errorWithCode:@"loadFlutterAsset_invalidKey" + message:@"Supplied asset key is not valid." + details:error]); + return; + } + + NSString* assetKey = [call arguments]; + NSString* assetFilePath = [FlutterDartProject lookupKeyForAsset:assetKey]; + NSURL* url = [[NSBundle mainBundle] URLForResource:[assetFilePath stringByDeletingPathExtension] + withExtension:assetFilePath.pathExtension]; + + if (!url) { + result([FlutterError + errorWithCode:@"loadFlutterAsset_invalidKey" + message:@"Failed parsing file path for supplied key." + details:[NSString + stringWithFormat:@"Failed to convert path '%@' into NSURL for key '%@'.", + assetFilePath, assetKey]]); + return; + } + + [_webView loadFileURL:url allowingReadAccessToURL:[url URLByDeletingLastPathComponent]]; + result(nil); +} + - (void)onLoadHtmlString:(FlutterMethodCall*)call result:(FlutterResult)result { NSDictionary* arguments = [call arguments]; if (![arguments isKindOfClass:NSDictionary.class]) { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index fc274b453995..08e98b11d4bf 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_wkwebview description: A Flutter plugin that provides a WebView widget based on Apple's WKWebView control. repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter_wkwebview issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.6.0 +version: 2.7.0 environment: sdk: ">=2.14.0 <3.0.0" From 4fdf85cb9f1a6d52525a611fdb4f0b722bc106cd Mon Sep 17 00:00:00 2001 From: BeMacized Date: Tue, 7 Dec 2021 21:53:00 +0100 Subject: [PATCH 046/600] [webview_flutter] Add setCookie to CookieManager and support initial cookies in creation params. (#4561) This PR adds a function for setting cookies to the CookieManager, as well support for setting initial cookies through the `CreationParams` object. Fixes flutter/flutter#27597 --- .../webview_flutter/CHANGELOG.md | 5 +++ .../webview_flutter/example/lib/main.dart | 17 ++++++++ .../lib/platform_interface.dart | 1 + .../webview_flutter/lib/src/webview.dart | 32 +++++++++++++-- .../webview_flutter/pubspec.yaml | 6 +-- .../test/webview_flutter_test.dart | 41 +++++++++++++++++-- 6 files changed, 92 insertions(+), 10 deletions(-) diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index a9e133d08d47..1df7e53d8e92 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.7.0 + +* Adds `setCookie` to CookieManager. +* CreationParams now supports setting `initialCookies`. + ## 2.6.0 * Adds support for the `loadRequest` method. diff --git a/packages/webview_flutter/webview_flutter/example/lib/main.dart b/packages/webview_flutter/webview_flutter/example/lib/main.dart index 02e6ef85ec61..65786de1f8b9 100644 --- a/packages/webview_flutter/webview_flutter/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter/example/lib/main.dart @@ -181,6 +181,7 @@ enum MenuOptions { loadLocalFile, loadHtmlString, transparentBackground, + setCookie, } class SampleMenu extends StatelessWidget { @@ -232,6 +233,9 @@ class SampleMenu extends StatelessWidget { case MenuOptions.transparentBackground: _onTransparentBackground(controller.data!, context); break; + case MenuOptions.setCookie: + _onSetCookie(controller.data!, context); + break; } }, itemBuilder: (BuildContext context) => >[ @@ -281,6 +285,10 @@ class SampleMenu extends StatelessWidget { value: MenuOptions.transparentBackground, child: Text('Transparent background example'), ), + const PopupMenuItem( + value: MenuOptions.setCookie, + child: Text('Set cookie'), + ), ], ); }, @@ -357,6 +365,15 @@ class SampleMenu extends StatelessWidget { await controller.loadUrl('data:text/html;base64,$contentBase64'); } + Future _onSetCookie( + WebViewController controller, BuildContext context) async { + await CookieManager().setCookie( + const WebViewCookie( + name: 'foo', value: 'bar', domain: 'httpbin.org', path: '/anything'), + ); + await controller.loadUrl('https://httpbin.org/anything'); + } + Future _onDoPostRequest( WebViewController controller, BuildContext context) async { final WebViewRequest request = WebViewRequest( diff --git a/packages/webview_flutter/webview_flutter/lib/platform_interface.dart b/packages/webview_flutter/webview_flutter/lib/platform_interface.dart index ab1cbb1bd344..48f74346fe61 100644 --- a/packages/webview_flutter/webview_flutter/lib/platform_interface.dart +++ b/packages/webview_flutter/webview_flutter/lib/platform_interface.dart @@ -22,5 +22,6 @@ export 'package:webview_flutter_platform_interface/webview_flutter_platform_inte WebSettings, WebResourceError, WebResourceErrorType, + WebViewCookie, WebViewRequest, WebViewRequestMethod; diff --git a/packages/webview_flutter/webview_flutter/lib/src/webview.dart b/packages/webview_flutter/webview_flutter/lib/src/webview.dart index d76f7b359482..8fe4f4147cd9 100644 --- a/packages/webview_flutter/webview_flutter/lib/src/webview.dart +++ b/packages/webview_flutter/webview_flutter/lib/src/webview.dart @@ -3,12 +3,15 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; import 'package:webview_flutter_android/webview_android.dart'; +import 'package:webview_flutter_android/webview_android_cookie_manager.dart'; +import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart'; import '../platform_interface.dart'; @@ -79,6 +82,7 @@ class WebView extends StatefulWidget { Key? key, this.onWebViewCreated, this.initialUrl, + this.initialCookies = const [], this.javascriptMode = JavascriptMode.disabled, this.javascriptChannels, this.navigationDelegate, @@ -150,6 +154,9 @@ class WebView extends StatefulWidget { /// The initial URL to load. final String? initialUrl; + /// The initial cookies to set. + final List initialCookies; + /// Whether JavaScript execution is enabled. final JavascriptMode javascriptMode; @@ -365,6 +372,7 @@ CreationParams _creationParamsfromWidget(WebView widget) { userAgent: widget.userAgent, autoMediaPlaybackPolicy: widget.initialMediaPlaybackPolicy, backgroundColor: widget.backgroundColor, + cookies: widget.initialCookies, ); } @@ -779,16 +787,32 @@ class CookieManager { return _instance ??= CookieManager._(); } - CookieManager._(); + CookieManager._() { + if (WebViewCookieManagerPlatform.instance == null) { + if (Platform.isAndroid) { + WebViewCookieManagerPlatform.instance = WebViewAndroidCookieManager(); + } else if (Platform.isIOS) { + WebViewCookieManagerPlatform.instance = WKWebViewCookieManager(); + } else { + throw AssertionError( + 'This platform is currently unsupported by webview_flutter.'); + } + } + } static CookieManager? _instance; /// Clears all cookies for all [WebView] instances. /// - /// This is a no op on iOS version smaller than 9. - /// /// Returns true if cookies were present before clearing, else false. - Future clearCookies() => WebView.platform.clearCookies(); + Future clearCookies() => + WebViewCookieManagerPlatform.instance!.clearCookies(); + + /// Sets a cookie for all [WebView] instances. + /// + /// This is a no op on iOS versions below 11. + Future setCookie(WebViewCookie cookie) => + WebViewCookieManagerPlatform.instance!.setCookie(cookie); } // Throws an ArgumentError if `url` is not a valid URL string. diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index 1bc6bab8b3ff..cd6c618025fe 100644 --- a/packages/webview_flutter/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter description: A Flutter plugin that provides a WebView widget on Android and iOS. repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.6.0 +version: 2.7.0 environment: sdk: ">=2.14.0 <3.0.0" @@ -19,9 +19,9 @@ flutter: dependencies: flutter: sdk: flutter - webview_flutter_android: ^2.7.0 + webview_flutter_android: ^2.8.0 webview_flutter_platform_interface: ^1.8.0 - webview_flutter_wkwebview: ^2.5.0 + webview_flutter_wkwebview: ^2.6.0 dev_dependencies: build_runner: ^2.1.5 diff --git a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart index d422402dbae7..40baef7e0ab5 100644 --- a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart @@ -23,10 +23,12 @@ void main() { late MockWebViewPlatform mockWebViewPlatform; late MockWebViewPlatformController mockWebViewPlatformController; + late MockWebViewCookieManagerPlatform mockWebViewCookieManagerPlatform; setUp(() { mockWebViewPlatformController = MockWebViewPlatformController(); mockWebViewPlatform = MockWebViewPlatform(); + mockWebViewCookieManagerPlatform = MockWebViewCookieManagerPlatform(); when(mockWebViewPlatform.build( context: anyNamed('context'), creationParams: anyNamed('creationParams'), @@ -46,6 +48,11 @@ void main() { }); WebView.platform = mockWebViewPlatform; + WebViewCookieManagerPlatform.instance = mockWebViewCookieManagerPlatform; + }); + + tearDown(() { + mockWebViewCookieManagerPlatform.reset(); }); testWidgets('Create WebView', (WidgetTester tester) async { @@ -499,9 +506,6 @@ void main() { }); testWidgets('Cookies can be cleared once', (WidgetTester tester) async { - when(mockWebViewPlatform.clearCookies()) - .thenAnswer((_) => Future.value(true)); - await tester.pumpWidget( const WebView( initialUrl: 'https://flutter.io', @@ -512,6 +516,21 @@ void main() { expect(hasCookies, true); }); + testWidgets('Cookies can be set', (WidgetTester tester) async { + const WebViewCookie cookie = + WebViewCookie(name: 'foo', value: 'bar', domain: 'flutter.dev'); + + await tester.pumpWidget( + const WebView( + initialUrl: 'https://flutter.io', + ), + ); + final CookieManager cookieManager = CookieManager(); + await cookieManager.setCookie(cookie); + expect(mockWebViewCookieManagerPlatform.setCookieCalls, + [cookie]); + }); + testWidgets('Initial JavaScript channels', (WidgetTester tester) async { await tester.pumpWidget( WebView( @@ -1308,3 +1327,19 @@ class MatchesCreationParams extends Matcher { .matches(creationParams.javascriptChannelNames, matchState); } } + +class MockWebViewCookieManagerPlatform extends WebViewCookieManagerPlatform { + List setCookieCalls = []; + + @override + Future clearCookies() async => true; + + @override + Future setCookie(WebViewCookie cookie) async { + setCookieCalls.add(cookie); + } + + void reset() { + setCookieCalls = []; + } +} From 7d44b40ee4f8b3e737ba5e913fb0a441c1b0f01c Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Tue, 7 Dec 2021 23:24:06 +0100 Subject: [PATCH 047/600] [webview_flutter] Implements the loadFlutterAsset in the app facing package. (#4593) --- .../webview_flutter/CHANGELOG.md | 4 +++ .../example/assets/www/index.html | 20 +++++++++++ .../example/assets/www/styles/style.css | 3 ++ .../webview_flutter/example/lib/main.dart | 13 +++++++ .../webview_flutter/example/pubspec.yaml | 2 ++ .../webview_flutter/lib/src/webview.dart | 9 +++++ .../webview_flutter/pubspec.yaml | 4 +-- .../test/webview_flutter_test.dart | 34 +++++++++++++++++++ 8 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter/example/assets/www/index.html create mode 100644 packages/webview_flutter/webview_flutter/example/assets/www/styles/style.css diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index 1df7e53d8e92..4906003a2e02 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.8.0 + +* Adds support for the `loadFlutterAsset` method. + ## 2.7.0 * Adds `setCookie` to CookieManager. diff --git a/packages/webview_flutter/webview_flutter/example/assets/www/index.html b/packages/webview_flutter/webview_flutter/example/assets/www/index.html new file mode 100644 index 000000000000..9895dd3ce6cb --- /dev/null +++ b/packages/webview_flutter/webview_flutter/example/assets/www/index.html @@ -0,0 +1,20 @@ + + + + +Load file or HTML string example + + + + +

Local demo page

+

+ This is an example page used to demonstrate how to load a local file or HTML + string using the Flutter + webview plugin. +

+ + + \ No newline at end of file diff --git a/packages/webview_flutter/webview_flutter/example/assets/www/styles/style.css b/packages/webview_flutter/webview_flutter/example/assets/www/styles/style.css new file mode 100644 index 000000000000..c2140b8b0fd8 --- /dev/null +++ b/packages/webview_flutter/webview_flutter/example/assets/www/styles/style.css @@ -0,0 +1,3 @@ +h1 { + color: blue; +} \ No newline at end of file diff --git a/packages/webview_flutter/webview_flutter/example/lib/main.dart b/packages/webview_flutter/webview_flutter/example/lib/main.dart index 65786de1f8b9..88c240a7316c 100644 --- a/packages/webview_flutter/webview_flutter/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter/example/lib/main.dart @@ -179,6 +179,7 @@ enum MenuOptions { navigationDelegate, doPostRequest, loadLocalFile, + loadFlutterAsset, loadHtmlString, transparentBackground, setCookie, @@ -227,6 +228,9 @@ class SampleMenu extends StatelessWidget { case MenuOptions.loadLocalFile: _onLoadLocalFileExample(controller.data!, context); break; + case MenuOptions.loadFlutterAsset: + _onLoadFlutterAssetExample(controller.data!, context); + break; case MenuOptions.loadHtmlString: _onLoadHtmlStringExample(controller.data!, context); break; @@ -280,6 +284,10 @@ class SampleMenu extends StatelessWidget { value: MenuOptions.loadLocalFile, child: Text('Load local file'), ), + const PopupMenuItem( + value: MenuOptions.loadFlutterAsset, + child: Text('Load Flutter Asset'), + ), const PopupMenuItem( key: ValueKey('ShowTransparentBackgroundExample'), value: MenuOptions.transparentBackground, @@ -392,6 +400,11 @@ class SampleMenu extends StatelessWidget { await controller.loadFile(pathToIndex); } + Future _onLoadFlutterAssetExample( + WebViewController controller, BuildContext context) async { + await controller.loadFlutterAsset('assets/www/index.html'); + } + Future _onLoadHtmlStringExample( WebViewController controller, BuildContext context) async { await controller.loadHtmlString(kLocalExamplePage); diff --git a/packages/webview_flutter/webview_flutter/example/pubspec.yaml b/packages/webview_flutter/webview_flutter/example/pubspec.yaml index 284a7a9d5a0d..ae3b57e07a89 100644 --- a/packages/webview_flutter/webview_flutter/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/example/pubspec.yaml @@ -33,3 +33,5 @@ flutter: assets: - assets/sample_audio.ogg - assets/sample_video.mp4 + - assets/www/index.html + - assets/www/styles/style.css diff --git a/packages/webview_flutter/webview_flutter/lib/src/webview.dart b/packages/webview_flutter/webview_flutter/lib/src/webview.dart index 8fe4f4147cd9..7b907a5f8bb2 100644 --- a/packages/webview_flutter/webview_flutter/lib/src/webview.dart +++ b/packages/webview_flutter/webview_flutter/lib/src/webview.dart @@ -526,6 +526,15 @@ class WebViewController { return _webViewPlatformController.loadFile(absoluteFilePath); } + /// Loads the Flutter asset specified in the pubspec.yaml file. + /// + /// Throws an ArgumentError if [key] is not part of the specified assets + /// in the pubspec.yaml file. + Future loadFlutterAsset(String key) { + assert(key.isNotEmpty); + return _webViewPlatformController.loadFlutterAsset(key); + } + /// Loads the supplied HTML string. /// /// The [baseUrl] parameter is used when resolving relative URLs within the diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index cd6c618025fe..4ac5e1d8add3 100644 --- a/packages/webview_flutter/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter description: A Flutter plugin that provides a WebView widget on Android and iOS. repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.7.0 +version: 2.8.0 environment: sdk: ">=2.14.0 <3.0.0" @@ -21,7 +21,7 @@ dependencies: sdk: flutter webview_flutter_android: ^2.8.0 webview_flutter_platform_interface: ^1.8.0 - webview_flutter_wkwebview: ^2.6.0 + webview_flutter_wkwebview: ^2.7.0 dev_dependencies: build_runner: ^2.1.5 diff --git a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart index 40baef7e0ab5..ad25cadf1dc4 100644 --- a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart @@ -131,6 +131,40 @@ void main() { expect(() => controller!.loadFile(''), throwsAssertionError); }); + testWidgets('Load Flutter asset', (WidgetTester tester) async { + WebViewController? controller; + await tester.pumpWidget( + WebView( + onWebViewCreated: (WebViewController webViewController) { + controller = webViewController; + }, + ), + ); + + expect(controller, isNotNull); + + await controller!.loadFlutterAsset('assets/index.html'); + + verify(mockWebViewPlatformController.loadFlutterAsset( + 'assets/index.html', + )); + }); + + testWidgets('Load Flutter asset with empty key', (WidgetTester tester) async { + WebViewController? controller; + await tester.pumpWidget( + WebView( + onWebViewCreated: (WebViewController webViewController) { + controller = webViewController; + }, + ), + ); + + expect(controller, isNotNull); + + expect(() => controller!.loadFlutterAsset(''), throwsAssertionError); + }); + testWidgets('Load HTML string without base URL', (WidgetTester tester) async { WebViewController? controller; await tester.pumpWidget( From 84964325d304c2a153e1dd6dc5e96be31ee93d3c Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 7 Dec 2021 19:39:12 -0500 Subject: [PATCH 048/600] [webview_flutter] Proof of concept web implementation (#4594) Initial proof of concept implementation of flutter_webview for the web. --- .../webview_flutter_web/AUTHORS | 8 + .../webview_flutter_web/CHANGELOG.md | 4 + .../webview_flutter_web/LICENSE | 26 + .../webview_flutter_web/README.md | 20 + .../webview_flutter_web/example/.metadata | 8 + .../webview_flutter_web/example/README.md | 8 + .../webview_flutter_test.dart | 72 + .../webview_flutter_web/example/lib/main.dart | 93 ++ .../example/lib/web_view.dart | 385 +++++ .../webview_flutter_web/example/pubspec.yaml | 32 + .../example/test_driver/integration_test.dart | 7 + .../example/web/favicon.png | Bin 0 -> 917 bytes .../example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../example/web/icons/Icon-maskable-192.png | Bin 0 -> 5594 bytes .../example/web/icons/Icon-maskable-512.png | Bin 0 -> 20998 bytes .../example/web/index.html | 104 ++ .../example/web/manifest.json | 35 + .../lib/shims/dart_ui.dart | 10 + .../lib/shims/dart_ui_fake.dart | 33 + .../lib/shims/dart_ui_real.dart | 5 + .../lib/webview_flutter_web.dart | 278 ++++ .../webview_flutter_web/pubspec.yaml | 32 + .../test/webview_flutter_web_test.dart | 130 ++ .../test/webview_flutter_web_test.mocks.dart | 1285 +++++++++++++++++ 25 files changed, 2575 insertions(+) create mode 100644 packages/webview_flutter/webview_flutter_web/AUTHORS create mode 100644 packages/webview_flutter/webview_flutter_web/CHANGELOG.md create mode 100644 packages/webview_flutter/webview_flutter_web/LICENSE create mode 100644 packages/webview_flutter/webview_flutter_web/README.md create mode 100644 packages/webview_flutter/webview_flutter_web/example/.metadata create mode 100644 packages/webview_flutter/webview_flutter_web/example/README.md create mode 100644 packages/webview_flutter/webview_flutter_web/example/integration_test/webview_flutter_test.dart create mode 100644 packages/webview_flutter/webview_flutter_web/example/lib/main.dart create mode 100644 packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart create mode 100644 packages/webview_flutter/webview_flutter_web/example/pubspec.yaml create mode 100644 packages/webview_flutter/webview_flutter_web/example/test_driver/integration_test.dart create mode 100644 packages/webview_flutter/webview_flutter_web/example/web/favicon.png create mode 100644 packages/webview_flutter/webview_flutter_web/example/web/icons/Icon-192.png create mode 100644 packages/webview_flutter/webview_flutter_web/example/web/icons/Icon-512.png create mode 100644 packages/webview_flutter/webview_flutter_web/example/web/icons/Icon-maskable-192.png create mode 100644 packages/webview_flutter/webview_flutter_web/example/web/icons/Icon-maskable-512.png create mode 100644 packages/webview_flutter/webview_flutter_web/example/web/index.html create mode 100644 packages/webview_flutter/webview_flutter_web/example/web/manifest.json create mode 100644 packages/webview_flutter/webview_flutter_web/lib/shims/dart_ui.dart create mode 100644 packages/webview_flutter/webview_flutter_web/lib/shims/dart_ui_fake.dart create mode 100644 packages/webview_flutter/webview_flutter_web/lib/shims/dart_ui_real.dart create mode 100644 packages/webview_flutter/webview_flutter_web/lib/webview_flutter_web.dart create mode 100644 packages/webview_flutter/webview_flutter_web/pubspec.yaml create mode 100644 packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.dart create mode 100644 packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.mocks.dart diff --git a/packages/webview_flutter/webview_flutter_web/AUTHORS b/packages/webview_flutter/webview_flutter_web/AUTHORS new file mode 100644 index 000000000000..05432a7fbf9a --- /dev/null +++ b/packages/webview_flutter/webview_flutter_web/AUTHORS @@ -0,0 +1,8 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. +Bodhi Mulders + diff --git a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md new file mode 100644 index 000000000000..11cfedaaf7e4 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md @@ -0,0 +1,4 @@ +## 0.1.0 + +* First web implementation for webview_flutter + diff --git a/packages/webview_flutter/webview_flutter_web/LICENSE b/packages/webview_flutter/webview_flutter_web/LICENSE new file mode 100644 index 000000000000..77130909e474 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_web/LICENSE @@ -0,0 +1,26 @@ +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/packages/webview_flutter/webview_flutter_web/README.md b/packages/webview_flutter/webview_flutter_web/README.md new file mode 100644 index 000000000000..9e7e1c6499fe --- /dev/null +++ b/packages/webview_flutter/webview_flutter_web/README.md @@ -0,0 +1,20 @@ +# webview\_flutter\_web + +This is an implementation of the [`webview_flutter`](https://pub.dev/packages/webview_flutter) plugin for web. + +It is currently severely limited and doesn't implement most of the available functionality. +The following functionality is currently available: + +- `loadUrl` (Without headers) +- `requestUrl` +- `loadHTMLString` (Without `baseUrl`) +- Setting the `initialUrl` through `CreationParams`. + +Nothing else is currently supported. + +## Usage + +### Depend on the package + +This package is not an endorsed implementation of the `webview_flutter` plugin yet, so you'll need to +[add it explicitly](https://pub.dev/packages/webview_flutter_web/install). \ No newline at end of file diff --git a/packages/webview_flutter/webview_flutter_web/example/.metadata b/packages/webview_flutter/webview_flutter_web/example/.metadata new file mode 100644 index 000000000000..da83b1ada1bd --- /dev/null +++ b/packages/webview_flutter/webview_flutter_web/example/.metadata @@ -0,0 +1,8 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 1e5cb2d87f8542f9fbbd0f22d528823274be0acb + channel: master diff --git a/packages/webview_flutter/webview_flutter_web/example/README.md b/packages/webview_flutter/webview_flutter_web/example/README.md new file mode 100644 index 000000000000..850ee74397a9 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_web/example/README.md @@ -0,0 +1,8 @@ +# webview_flutter_example + +Demonstrates how to use the webview_flutter plugin. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](https://flutter.dev/). diff --git a/packages/webview_flutter/webview_flutter_web/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_web/example/integration_test/webview_flutter_test.dart new file mode 100644 index 000000000000..232ecdd302b7 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_web/example/integration_test/webview_flutter_test.dart @@ -0,0 +1,72 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:html' as html; + +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:webview_flutter_web_example/web_view.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + // URLs to navigate to in tests. These need to be URLs that we are confident will + // always be accessible, and won't do redirection. (E.g., just + // 'https://www.google.com/' will sometimes redirect traffic that looks + // like it's coming from a bot, which is true of these tests). + const String primaryUrl = 'https://flutter.dev/'; + const String secondaryUrl = 'https://www.google.com/robots.txt'; + + testWidgets('initialUrl', (WidgetTester tester) async { + final Completer controllerCompleter = + Completer(); + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: WebView( + key: GlobalKey(), + initialUrl: primaryUrl, + onWebViewCreated: (WebViewController controller) { + controllerCompleter.complete(controller); + }, + ), + ), + ); + await controllerCompleter.future; + + // Assert an iframe has been rendered to the DOM with the correct src attribute. + final html.IFrameElement? element = + html.document.querySelector('iframe') as html.IFrameElement?; + expect(element, isNotNull); + expect(element!.src, primaryUrl); + }); + + testWidgets('loadUrl', (WidgetTester tester) async { + final Completer controllerCompleter = + Completer(); + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: WebView( + key: GlobalKey(), + initialUrl: primaryUrl, + onWebViewCreated: (WebViewController controller) { + controllerCompleter.complete(controller); + }, + ), + ), + ); + final WebViewController controller = await controllerCompleter.future; + await controller.loadUrl(secondaryUrl); + + // Assert an iframe has been rendered to the DOM with the correct src attribute. + final html.IFrameElement? element = + html.document.querySelector('iframe') as html.IFrameElement?; + expect(element, isNotNull); + expect(element!.src, secondaryUrl); + }); +} diff --git a/packages/webview_flutter/webview_flutter_web/example/lib/main.dart b/packages/webview_flutter/webview_flutter_web/example/lib/main.dart new file mode 100644 index 000000000000..9cf412d74e50 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_web/example/lib/main.dart @@ -0,0 +1,93 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; +import 'dart:typed_data'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; + +import 'web_view.dart'; + +void main() { + runApp(const MaterialApp(home: _WebViewExample())); +} + +class _WebViewExample extends StatefulWidget { + const _WebViewExample({Key? key}) : super(key: key); + + @override + _WebViewExampleState createState() => _WebViewExampleState(); +} + +class _WebViewExampleState extends State<_WebViewExample> { + final Completer _controller = + Completer(); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Flutter WebView example'), + actions: [ + _SampleMenu(_controller.future), + ], + ), + body: WebView( + initialUrl: 'https://flutter.dev', + onWebViewCreated: (WebViewController controller) { + _controller.complete(controller); + }, + ), + ); + } +} + +enum _MenuOptions { + doPostRequest, +} + +class _SampleMenu extends StatelessWidget { + const _SampleMenu(this.controller); + + final Future controller; + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: controller, + builder: + (BuildContext context, AsyncSnapshot controller) { + return PopupMenuButton<_MenuOptions>( + onSelected: (_MenuOptions value) { + switch (value) { + case _MenuOptions.doPostRequest: + _onDoPostRequest(controller.data!, context); + break; + } + }, + itemBuilder: (BuildContext context) => >[ + const PopupMenuItem<_MenuOptions>( + value: _MenuOptions.doPostRequest, + child: Text('Post Request'), + ), + ], + ); + }, + ); + } + + Future _onDoPostRequest( + WebViewController controller, BuildContext context) async { + final WebViewRequest request = WebViewRequest( + uri: Uri.parse('https://httpbin.org/post'), + method: WebViewRequestMethod.post, + headers: {'foo': 'bar', 'Content-Type': 'text/plain'}, + body: Uint8List.fromList('Test Body'.codeUnits), + ); + await controller.loadRequest(request); + } +} diff --git a/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart new file mode 100644 index 000000000000..787b016d2b77 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart @@ -0,0 +1,385 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; +import 'package:webview_flutter_web/webview_flutter_web.dart'; + +/// Optional callback invoked when a web view is first created. [controller] is +/// the [WebViewController] for the created web view. +typedef WebViewCreatedCallback = void Function(WebViewController controller); + +/// A web view widget for showing html content. +/// +/// The [WebView] widget wraps around the [WebWebViewPlatform]. +/// +/// The [WebView] widget is controlled using the [WebViewController] which is +/// provided through the `onWebViewCreated` callback. +/// +/// In this example project it's main purpose is to facilitate integration +/// testing of the `webview_flutter_web` package. +class WebView extends StatefulWidget { + /// Creates a new web view. + /// + /// The web view can be controlled using a `WebViewController` that is passed to the + /// `onWebViewCreated` callback once the web view is created. + const WebView({ + Key? key, + this.onWebViewCreated, + this.initialUrl, + }) : super(key: key); + + /// The WebView platform that's used by this WebView. + /// + /// The default value is [WebWebViewPlatform]. + /// This property can be set to use a custom platform implementation for WebViews. + /// Setting `platform` doesn't affect [WebView]s that were already created. + static WebViewPlatform platform = WebWebViewPlatform(); + + /// If not null invoked once the web view is created. + final WebViewCreatedCallback? onWebViewCreated; + + /// The initial URL to load. + final String? initialUrl; + + @override + _WebViewState createState() => _WebViewState(); +} + +class _WebViewState extends State { + final Completer _controller = + Completer(); + late final _PlatformCallbacksHandler _platformCallbacksHandler; + + @override + void initState() { + super.initState(); + _platformCallbacksHandler = _PlatformCallbacksHandler(); + } + + @override + void didUpdateWidget(WebView oldWidget) { + super.didUpdateWidget(oldWidget); + _controller.future.then((WebViewController controller) { + controller.updateWidget(widget); + }); + } + + @override + Widget build(BuildContext context) { + return WebView.platform.build( + context: context, + onWebViewPlatformCreated: + (WebViewPlatformController? webViewPlatformController) { + final WebViewController controller = WebViewController( + widget, + webViewPlatformController!, + ); + _controller.complete(controller); + + if (widget.onWebViewCreated != null) { + widget.onWebViewCreated!(controller); + } + }, + webViewPlatformCallbacksHandler: _platformCallbacksHandler, + creationParams: CreationParams( + initialUrl: widget.initialUrl, + webSettings: _webSettingsFromWidget(widget), + ), + javascriptChannelRegistry: + JavascriptChannelRegistry({}), + ); + } +} + +class _PlatformCallbacksHandler implements WebViewPlatformCallbacksHandler { + _PlatformCallbacksHandler(); + + @override + FutureOr onNavigationRequest( + {required String url, required bool isForMainFrame}) { + throw UnimplementedError(); + } + + @override + void onPageFinished(String url) {} + + @override + void onPageStarted(String url) {} + + @override + void onProgress(int progress) {} + + @override + void onWebResourceError(WebResourceError error) {} +} + +/// Controls a [WebView]. +/// +/// A [WebViewController] instance can be obtained by setting the [WebView.onWebViewCreated] +/// callback for a [WebView] widget. +class WebViewController { + /// Creates a [WebViewController] which can be used to control the provided + /// [WebView] widget. + WebViewController( + this._widget, + this._webViewPlatformController, + ) : assert(_webViewPlatformController != null) { + _settings = _webSettingsFromWidget(_widget); + } + + final WebViewPlatformController _webViewPlatformController; + + late WebSettings _settings; + + WebView _widget; + + /// Loads the specified URL. + /// + /// If `headers` is not null and the URL is an HTTP URL, the key value paris in `headers` will + /// be added as key value pairs of HTTP headers for the request. + /// + /// `url` must not be null. + /// + /// Throws an ArgumentError if `url` is not a valid URL string. + Future loadUrl( + String url, { + Map? headers, + }) async { + assert(url != null); + _validateUrlString(url); + return _webViewPlatformController.loadUrl(url, headers); + } + + /// Loads a page by making the specified request. + Future loadRequest(WebViewRequest request) async { + return _webViewPlatformController.loadRequest(request); + } + + /// Accessor to the current URL that the WebView is displaying. + /// + /// If [WebView.initialUrl] was never specified, returns `null`. + /// Note that this operation is asynchronous, and it is possible that the + /// current URL changes again by the time this function returns (in other + /// words, by the time this future completes, the WebView may be displaying a + /// different URL). + Future currentUrl() { + return _webViewPlatformController.currentUrl(); + } + + /// Checks whether there's a back history item. + /// + /// Note that this operation is asynchronous, and it is possible that the "canGoBack" state has + /// changed by the time the future completed. + Future canGoBack() { + return _webViewPlatformController.canGoBack(); + } + + /// Checks whether there's a forward history item. + /// + /// Note that this operation is asynchronous, and it is possible that the "canGoForward" state has + /// changed by the time the future completed. + Future canGoForward() { + return _webViewPlatformController.canGoForward(); + } + + /// Goes back in the history of this WebView. + /// + /// If there is no back history item this is a no-op. + Future goBack() { + return _webViewPlatformController.goBack(); + } + + /// Goes forward in the history of this WebView. + /// + /// If there is no forward history item this is a no-op. + Future goForward() { + return _webViewPlatformController.goForward(); + } + + /// Reloads the current URL. + Future reload() { + return _webViewPlatformController.reload(); + } + + /// Clears all caches used by the [WebView]. + /// + /// The following caches are cleared: + /// 1. Browser HTTP Cache. + /// 2. [Cache API](https://developers.google.com/web/fundamentals/instant-and-offline/web-storage/cache-api) caches. + /// These are not yet supported in iOS WkWebView. Service workers tend to use this cache. + /// 3. Application cache. + /// 4. Local Storage. + /// + /// Note: Calling this method also triggers a reload. + Future clearCache() async { + await _webViewPlatformController.clearCache(); + return reload(); + } + + /// Update the widget managed by the [WebViewController]. + Future updateWidget(WebView widget) async { + _widget = widget; + await _updateSettings(_webSettingsFromWidget(widget)); + } + + Future _updateSettings(WebSettings newSettings) { + final WebSettings update = + _clearUnchangedWebSettings(_settings, newSettings); + _settings = newSettings; + return _webViewPlatformController.updateSettings(update); + } + + @visibleForTesting + // ignore: public_member_api_docs + Future evaluateJavascript(String javascriptString) { + if (_settings.javascriptMode == JavascriptMode.disabled) { + return Future.error(FlutterError( + 'JavaScript mode must be enabled/unrestricted when calling evaluateJavascript.')); + } + return _webViewPlatformController.evaluateJavascript(javascriptString); + } + + /// Runs the given JavaScript in the context of the current page. + /// If you are looking for the result, use [runJavascriptReturningResult] instead. + /// The Future completes with an error if a JavaScript error occurred. + /// + /// When running JavaScript in a [WebView], it is best practice to wait for + // the [WebView.onPageFinished] callback. This guarantees all the JavaScript + // embedded in the main frame HTML has been loaded. + Future runJavascript(String javaScriptString) { + if (_settings.javascriptMode == JavascriptMode.disabled) { + return Future.error(FlutterError( + 'Javascript mode must be enabled/unrestricted when calling runJavascript.')); + } + return _webViewPlatformController.runJavascript(javaScriptString); + } + + /// Runs the given JavaScript in the context of the current page, and returns the result. + /// + /// Returns the evaluation result as a JSON formatted string. + /// The Future completes with an error if a JavaScript error occurred. + /// + /// When evaluating JavaScript in a [WebView], it is best practice to wait for + /// the [WebView.onPageFinished] callback. This guarantees all the JavaScript + /// embedded in the main frame HTML has been loaded. + Future runJavascriptReturningResult(String javaScriptString) { + if (_settings.javascriptMode == JavascriptMode.disabled) { + return Future.error(FlutterError( + 'Javascript mode must be enabled/unrestricted when calling runJavascriptReturningResult.')); + } + return _webViewPlatformController + .runJavascriptReturningResult(javaScriptString); + } + + /// Returns the title of the currently loaded page. + Future getTitle() { + return _webViewPlatformController.getTitle(); + } + + /// Sets the WebView's content scroll position. + /// + /// The parameters `x` and `y` specify the scroll position in WebView pixels. + Future scrollTo(int x, int y) { + return _webViewPlatformController.scrollTo(x, y); + } + + /// Move the scrolled position of this view. + /// + /// The parameters `x` and `y` specify the amount of WebView pixels to scroll by horizontally and vertically respectively. + Future scrollBy(int x, int y) { + return _webViewPlatformController.scrollBy(x, y); + } + + /// Return the horizontal scroll position, in WebView pixels, of this view. + /// + /// Scroll position is measured from left. + Future getScrollX() { + return _webViewPlatformController.getScrollX(); + } + + /// Return the vertical scroll position, in WebView pixels, of this view. + /// + /// Scroll position is measured from top. + Future getScrollY() { + return _webViewPlatformController.getScrollY(); + } + + // This method assumes that no fields in `currentValue` are null. + WebSettings _clearUnchangedWebSettings( + WebSettings currentValue, WebSettings newValue) { + assert(currentValue.javascriptMode != null); + assert(currentValue.hasNavigationDelegate != null); + assert(currentValue.hasProgressTracking != null); + assert(currentValue.debuggingEnabled != null); + assert(currentValue.userAgent != null); + assert(newValue.javascriptMode != null); + assert(newValue.hasNavigationDelegate != null); + assert(newValue.debuggingEnabled != null); + assert(newValue.userAgent != null); + assert(newValue.zoomEnabled != null); + + JavascriptMode? javascriptMode; + bool? hasNavigationDelegate; + bool? hasProgressTracking; + bool? debuggingEnabled; + WebSetting userAgent = const WebSetting.absent(); + bool? zoomEnabled; + if (currentValue.javascriptMode != newValue.javascriptMode) { + javascriptMode = newValue.javascriptMode; + } + if (currentValue.hasNavigationDelegate != newValue.hasNavigationDelegate) { + hasNavigationDelegate = newValue.hasNavigationDelegate; + } + if (currentValue.hasProgressTracking != newValue.hasProgressTracking) { + hasProgressTracking = newValue.hasProgressTracking; + } + if (currentValue.debuggingEnabled != newValue.debuggingEnabled) { + debuggingEnabled = newValue.debuggingEnabled; + } + if (currentValue.userAgent != newValue.userAgent) { + userAgent = newValue.userAgent; + } + if (currentValue.zoomEnabled != newValue.zoomEnabled) { + zoomEnabled = newValue.zoomEnabled; + } + + return WebSettings( + javascriptMode: javascriptMode, + hasNavigationDelegate: hasNavigationDelegate, + hasProgressTracking: hasProgressTracking, + debuggingEnabled: debuggingEnabled, + userAgent: userAgent, + zoomEnabled: zoomEnabled, + ); + } + + // Throws an ArgumentError if `url` is not a valid URL string. + void _validateUrlString(String url) { + try { + final Uri uri = Uri.parse(url); + if (uri.scheme.isEmpty) { + throw ArgumentError('Missing scheme in URL string: "$url"'); + } + } on FormatException catch (e) { + throw ArgumentError(e); + } + } +} + +WebSettings _webSettingsFromWidget(WebView widget) { + return WebSettings( + javascriptMode: JavascriptMode.unrestricted, + hasNavigationDelegate: false, + hasProgressTracking: false, + debuggingEnabled: false, + gestureNavigationEnabled: false, + allowsInlineMediaPlayback: true, + userAgent: const WebSetting.of(''), + zoomEnabled: false, + ); +} diff --git a/packages/webview_flutter/webview_flutter_web/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_web/example/pubspec.yaml new file mode 100644 index 000000000000..a98df799e92c --- /dev/null +++ b/packages/webview_flutter/webview_flutter_web/example/pubspec.yaml @@ -0,0 +1,32 @@ +name: webview_flutter_web_example +description: Demonstrates how to use the webview_flutter_web plugin. +publish_to: none + +environment: + sdk: ">=2.14.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter + flutter_web_plugins: + sdk: flutter + webview_flutter_web: + # When depending on this package from a real application you should use: + # webview_flutter_web: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + +dev_dependencies: + espresso: ^0.1.0+2 + flutter_driver: + sdk: flutter + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + pedantic: ^1.10.0 + +flutter: + uses-material-design: true diff --git a/packages/webview_flutter/webview_flutter_web/example/test_driver/integration_test.dart b/packages/webview_flutter/webview_flutter_web/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4f10f2a522f3 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_web/example/test_driver/integration_test.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); diff --git a/packages/webview_flutter/webview_flutter_web/example/web/favicon.png b/packages/webview_flutter/webview_flutter_web/example/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/packages/webview_flutter/webview_flutter_web/example/web/icons/Icon-192.png b/packages/webview_flutter/webview_flutter_web/example/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 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 literal 0 HcmV?d00001 diff --git a/packages/webview_flutter/webview_flutter_web/example/web/icons/Icon-512.png b/packages/webview_flutter/webview_flutter_web/example/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 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 literal 0 HcmV?d00001 diff --git a/packages/webview_flutter/webview_flutter_web/example/web/icons/Icon-maskable-192.png b/packages/webview_flutter/webview_flutter_web/example/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000000000000000000000000000000000000..eb9b4d76e525556d5d89141648c724331630325d GIT binary patch literal 5594 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 X>c!)3PgKfL&lX^$Z>Cpu&6)6jvi^Z! literal 0 HcmV?d00001 diff --git a/packages/webview_flutter/webview_flutter_web/example/web/icons/Icon-maskable-512.png b/packages/webview_flutter/webview_flutter_web/example/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000000000000000000000000000000000000..d69c56691fbdb0b7efa65097c7cc1edac12a6d3e GIT binary patch literal 20998 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 MPgg&ebxsLQ025IeI{*Lx literal 0 HcmV?d00001 diff --git a/packages/webview_flutter/webview_flutter_web/example/web/index.html b/packages/webview_flutter/webview_flutter_web/example/web/index.html new file mode 100644 index 000000000000..8b8b5bf92f89 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_web/example/web/index.html @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + webview_flutter_web Example + + + + + + + diff --git a/packages/webview_flutter/webview_flutter_web/example/web/manifest.json b/packages/webview_flutter/webview_flutter_web/example/web/manifest.json new file mode 100644 index 000000000000..1124a93355ec --- /dev/null +++ b/packages/webview_flutter/webview_flutter_web/example/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "webview_flutter_web Example", + "short_name": "example", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/packages/webview_flutter/webview_flutter_web/lib/shims/dart_ui.dart b/packages/webview_flutter/webview_flutter_web/lib/shims/dart_ui.dart new file mode 100644 index 000000000000..1724dd60eab4 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_web/lib/shims/dart_ui.dart @@ -0,0 +1,10 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/// This file shims dart:ui in web-only scenarios, getting rid of the need to +/// suppress analyzer warnings. + +// TODO(BeMacized): Remove this file once web-only dart:ui APIs, +// are exposed from a dedicated place. flutter/flutter#55000 +export 'dart_ui_fake.dart' if (dart.library.html) 'dart_ui_real.dart'; diff --git a/packages/webview_flutter/webview_flutter_web/lib/shims/dart_ui_fake.dart b/packages/webview_flutter/webview_flutter_web/lib/shims/dart_ui_fake.dart new file mode 100644 index 000000000000..8757ca22be17 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_web/lib/shims/dart_ui_fake.dart @@ -0,0 +1,33 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; + +// Fake interface for the logic that this package needs from (web-only) dart:ui. +// This is conditionally exported so the analyzer sees these methods as available. + +// ignore_for_file: avoid_classes_with_only_static_members +// ignore_for_file: camel_case_types + +/// Shim for web_ui engine.PlatformViewRegistry +/// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L62 +class platformViewRegistry { + /// Shim for registerViewFactory + /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L72 + static bool registerViewFactory( + String viewTypeId, html.Element Function(int viewId) viewFactory) { + return false; + } +} + +/// Shim for web_ui engine.AssetManager. +/// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/src/engine/assets.dart#L12 +class webOnlyAssetManager { + /// Shim for getAssetUrl. + /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/src/engine/assets.dart#L45 + static String getAssetUrl(String asset) => ''; +} + +/// Signature of callbacks that have no arguments and return no data. +typedef VoidCallback = void Function(); diff --git a/packages/webview_flutter/webview_flutter_web/lib/shims/dart_ui_real.dart b/packages/webview_flutter/webview_flutter_web/lib/shims/dart_ui_real.dart new file mode 100644 index 000000000000..276b768c76c5 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_web/lib/shims/dart_ui_real.dart @@ -0,0 +1,5 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +export 'dart:ui'; diff --git a/packages/webview_flutter/webview_flutter_web/lib/webview_flutter_web.dart b/packages/webview_flutter/webview_flutter_web/lib/webview_flutter_web.dart new file mode 100644 index 000000000000..aa427eb6874a --- /dev/null +++ b/packages/webview_flutter/webview_flutter_web/lib/webview_flutter_web.dart @@ -0,0 +1,278 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:html'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_web_plugins/flutter_web_plugins.dart'; +import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; +import 'shims/dart_ui.dart' as ui; + +/// Builds an iframe based WebView. +/// +/// This is used as the default implementation for [WebView.platform] on web. +class WebWebViewPlatform implements WebViewPlatform { + /// Constructs a new instance of [WebWebViewPlatform]. + WebWebViewPlatform() { + ui.platformViewRegistry.registerViewFactory( + 'webview-iframe', + (int viewId) => IFrameElement() + ..id = 'webview-$viewId' + ..width = '100%' + ..height = '100%' + ..style.border = 'none'); + } + + @override + Widget build({ + required BuildContext context, + required CreationParams creationParams, + required WebViewPlatformCallbacksHandler webViewPlatformCallbacksHandler, + required JavascriptChannelRegistry? javascriptChannelRegistry, + WebViewPlatformCreatedCallback? onWebViewPlatformCreated, + Set>? gestureRecognizers, + }) { + return HtmlElementView( + viewType: 'webview-iframe', + onPlatformViewCreated: (int viewId) { + if (onWebViewPlatformCreated == null) { + return; + } + final IFrameElement element = + document.getElementById('webview-$viewId')! as IFrameElement; + if (creationParams.initialUrl != null) { + element.src = creationParams.initialUrl; + } + onWebViewPlatformCreated(WebWebViewPlatformController( + element, + )); + }, + ); + } + + @override + Future clearCookies() async => false; + + /// Gets called when the plugin is registered. + static void registerWith(Registrar registrar) {} +} + +/// Implementation of [WebViewPlatformController] for web. +class WebWebViewPlatformController implements WebViewPlatformController { + /// Constructs a [WebWebViewPlatformController]. + WebWebViewPlatformController(this._element); + + final IFrameElement _element; + HttpRequestFactory _httpRequestFactory = HttpRequestFactory(); + + /// Setter for setting the HttpRequestFactory, for testing purposes. + @visibleForTesting + set httpRequestFactory(HttpRequestFactory factory) { + _httpRequestFactory = factory; + } + + @override + Future addJavascriptChannels(Set javascriptChannelNames) { + throw UnimplementedError(); + } + + @override + Future canGoBack() { + throw UnimplementedError(); + } + + @override + Future canGoForward() { + throw UnimplementedError(); + } + + @override + Future clearCache() { + throw UnimplementedError(); + } + + @override + Future currentUrl() { + throw UnimplementedError(); + } + + @override + Future evaluateJavascript(String javascript) { + throw UnimplementedError(); + } + + @override + Future getScrollX() { + throw UnimplementedError(); + } + + @override + Future getScrollY() { + throw UnimplementedError(); + } + + @override + Future getTitle() { + throw UnimplementedError(); + } + + @override + Future goBack() { + throw UnimplementedError(); + } + + @override + Future goForward() { + throw UnimplementedError(); + } + + @override + Future loadUrl(String url, Map? headers) async { + _element.src = url; + } + + @override + Future reload() { + throw UnimplementedError(); + } + + @override + Future removeJavascriptChannels(Set javascriptChannelNames) { + throw UnimplementedError(); + } + + @override + Future runJavascript(String javascript) { + throw UnimplementedError(); + } + + @override + Future runJavascriptReturningResult(String javascript) { + throw UnimplementedError(); + } + + @override + Future scrollBy(int x, int y) { + throw UnimplementedError(); + } + + @override + Future scrollTo(int x, int y) { + throw UnimplementedError(); + } + + @override + Future updateSettings(WebSettings setting) { + throw UnimplementedError(); + } + + @override + Future loadFile(String absoluteFilePath) { + throw UnimplementedError(); + } + + @override + Future loadHtmlString( + String html, { + String? baseUrl, + }) async { + _element.src = 'data:text/html,' + Uri.encodeFull(html); + } + + @override + Future loadRequest(WebViewRequest request) async { + if (!request.uri.hasScheme) { + throw ArgumentError('WebViewRequest#uri is required to have a scheme.'); + } + final HttpRequest httpReq = await _httpRequestFactory.request( + request.uri.toString(), + method: request.method.serialize(), + requestHeaders: request.headers, + sendData: request.body); + final String contentType = + httpReq.getResponseHeader('content-type') ?? 'text/html'; + _element.src = + 'data:$contentType,' + Uri.encodeFull(httpReq.responseText ?? ''); + } + + @override + Future loadFlutterAsset(String key) { + throw UnimplementedError(); + } +} + +/// Factory class for creating [HttpRequest] instances. +class HttpRequestFactory { + /// Creates and sends a URL request for the specified [url]. + /// + /// By default `request` will perform an HTTP GET request, but a different + /// method (`POST`, `PUT`, `DELETE`, etc) can be used by specifying the + /// [method] parameter. (See also [HttpRequest.postFormData] for `POST` + /// requests only. + /// + /// The Future is completed when the response is available. + /// + /// If specified, `sendData` will send data in the form of a [ByteBuffer], + /// [Blob], [Document], [String], or [FormData] along with the HttpRequest. + /// + /// If specified, [responseType] sets the desired response format for the + /// request. By default it is [String], but can also be 'arraybuffer', 'blob', + /// 'document', 'json', or 'text'. See also [HttpRequest.responseType] + /// for more information. + /// + /// The [withCredentials] parameter specified that credentials such as a cookie + /// (already) set in the header or + /// [authorization headers](http://tools.ietf.org/html/rfc1945#section-10.2) + /// should be specified for the request. Details to keep in mind when using + /// credentials: + /// + /// /// Using credentials is only useful for cross-origin requests. + /// /// The `Access-Control-Allow-Origin` header of `url` cannot contain a wildcard (///). + /// /// The `Access-Control-Allow-Credentials` header of `url` must be set to true. + /// /// If `Access-Control-Expose-Headers` has not been set to true, only a subset of all the response headers will be returned when calling [getAllResponseHeaders]. + /// + /// The following is equivalent to the [getString] sample above: + /// + /// var name = Uri.encodeQueryComponent('John'); + /// var id = Uri.encodeQueryComponent('42'); + /// HttpRequest.request('users.json?name=$name&id=$id') + /// .then((HttpRequest resp) { + /// // Do something with the response. + /// }); + /// + /// Here's an example of submitting an entire form with [FormData]. + /// + /// var myForm = querySelector('form#myForm'); + /// var data = new FormData(myForm); + /// HttpRequest.request('/submit', method: 'POST', sendData: data) + /// .then((HttpRequest resp) { + /// // Do something with the response. + /// }); + /// + /// Note that requests for file:// URIs are only supported by Chrome extensions + /// with appropriate permissions in their manifest. Requests to file:// URIs + /// will also never fail- the Future will always complete successfully, even + /// when the file cannot be found. + /// + /// See also: [authorization headers](http://en.wikipedia.org/wiki/Basic_access_authentication). + Future request(String url, + {String? method, + bool? withCredentials, + String? responseType, + String? mimeType, + Map? requestHeaders, + dynamic sendData, + void onProgress(ProgressEvent e)?}) { + return HttpRequest.request(url, + method: method, + withCredentials: withCredentials, + responseType: responseType, + mimeType: mimeType, + requestHeaders: requestHeaders, + sendData: sendData, + onProgress: onProgress); + } +} diff --git a/packages/webview_flutter/webview_flutter_web/pubspec.yaml b/packages/webview_flutter/webview_flutter_web/pubspec.yaml new file mode 100644 index 000000000000..3b24476283ec --- /dev/null +++ b/packages/webview_flutter/webview_flutter_web/pubspec.yaml @@ -0,0 +1,32 @@ +name: webview_flutter_web +description: A Flutter plugin that provides a WebView widget on web. +repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter_web +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 +version: 0.1.0 + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.5.0" + +flutter: + plugin: + implements: webview_flutter + platforms: + web: + pluginClass: WebWebViewPlatform + fileName: webview_flutter_web.dart + +dependencies: + flutter: + sdk: flutter + flutter_web_plugins: + sdk: flutter + webview_flutter_platform_interface: ^1.8.0 + +dev_dependencies: + build_runner: ^2.1.5 + flutter_driver: + sdk: flutter + flutter_test: + sdk: flutter + mockito: ^5.0.0 diff --git a/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.dart b/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.dart new file mode 100644 index 000000000000..90e2ea465782 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.dart @@ -0,0 +1,130 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html'; +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; +import 'package:webview_flutter_web/webview_flutter_web.dart'; +import './webview_flutter_web_test.mocks.dart'; + +@GenerateMocks([ + IFrameElement, + BuildContext, + CreationParams, + WebViewPlatformCallbacksHandler, + HttpRequestFactory, + HttpRequest, +]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('WebWebViewPlatform', () { + test('build returns a HtmlElementView', () { + // Setup + final WebWebViewPlatform platform = WebWebViewPlatform(); + // Run + final Widget widget = platform.build( + context: MockBuildContext(), + creationParams: CreationParams(), + webViewPlatformCallbacksHandler: MockWebViewPlatformCallbacksHandler(), + javascriptChannelRegistry: null, + ); + // Verify + expect(widget, isA()); + }); + }); + + group('WebWebViewPlatformController', () { + test('loadUrl sets url on iframe src attribute', () { + // Setup + final MockIFrameElement mockElement = MockIFrameElement(); + final WebWebViewPlatformController controller = + WebWebViewPlatformController( + mockElement, + ); + // Run + controller.loadUrl('test url', null); + // Verify + verify(mockElement.src = 'test url'); + }); + + test('loadHtmlString loads html into iframe', () { + // Setup + final MockIFrameElement mockElement = MockIFrameElement(); + final WebWebViewPlatformController controller = + WebWebViewPlatformController( + mockElement, + ); + // Run + controller.loadHtmlString('test html'); + // Verify + verify(mockElement.src = 'data:text/html,' + Uri.encodeFull('test html')); + }); + + group('loadRequest', () { + test('loadRequest throws ArgumentError on missing scheme', () { + // Setup + final MockIFrameElement mockElement = MockIFrameElement(); + final WebWebViewPlatformController controller = + WebWebViewPlatformController( + mockElement, + ); + // Run & Verify + expect( + () async => await controller.loadRequest( + WebViewRequest( + uri: Uri.parse('flutter.dev'), + method: WebViewRequestMethod.get, + ), + ), + throwsA(const TypeMatcher())); + }); + + test('loadRequest makes request and loads response into iframe', + () async { + // Setup + final MockIFrameElement mockElement = MockIFrameElement(); + final WebWebViewPlatformController controller = + WebWebViewPlatformController( + mockElement, + ); + final MockHttpRequest mockHttpRequest = MockHttpRequest(); + when(mockHttpRequest.getResponseHeader('content-type')) + .thenReturn('text/plain'); + when(mockHttpRequest.responseText).thenReturn('test data'); + final MockHttpRequestFactory mockHttpRequestFactory = + MockHttpRequestFactory(); + when(mockHttpRequestFactory.request( + any, + method: anyNamed('method'), + requestHeaders: anyNamed('requestHeaders'), + sendData: anyNamed('sendData'), + )).thenAnswer((_) => Future.value(mockHttpRequest)); + controller.httpRequestFactory = mockHttpRequestFactory; + // Run + await controller.loadRequest( + WebViewRequest( + uri: Uri.parse('https://flutter.dev'), + method: WebViewRequestMethod.post, + body: Uint8List.fromList('test body'.codeUnits), + headers: {'Foo': 'Bar'}), + ); + // Verify + verify(mockHttpRequestFactory.request( + 'https://flutter.dev', + method: 'post', + requestHeaders: {'Foo': 'Bar'}, + sendData: Uint8List.fromList('test body'.codeUnits), + )); + verify( + mockElement.src = 'data:text/plain,' + Uri.encodeFull('test data')); + }); + }); + }); +} diff --git a/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.mocks.dart b/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.mocks.dart new file mode 100644 index 000000000000..2507cd292142 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.mocks.dart @@ -0,0 +1,1285 @@ +// 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. + +// Mocks generated by Mockito 5.0.16 from annotations +// in webview_flutter_web/test/webview_flutter_web_test.dart. +// Do not manually edit this file. + +import 'dart:async' as _i6; +import 'dart:html' as _i2; +import 'dart:math' as _i3; + +import 'package:flutter/foundation.dart' as _i5; +import 'package:flutter/widgets.dart' as _i4; +import 'package:mockito/mockito.dart' as _i1; +import 'package:webview_flutter_platform_interface/src/types/auto_media_playback_policy.dart' + as _i8; +import 'package:webview_flutter_platform_interface/src/types/types.dart' as _i7; +import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart' + as _i9; +import 'package:webview_flutter_web/webview_flutter_web.dart' as _i10; + +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types + +class _FakeCssClassSet_0 extends _i1.Fake implements _i2.CssClassSet {} + +class _FakeRectangle_1 extends _i1.Fake + implements _i3.Rectangle {} + +class _FakeCssRect_2 extends _i1.Fake implements _i2.CssRect {} + +class _FakePoint_3 extends _i1.Fake implements _i3.Point {} + +class _FakeElementEvents_4 extends _i1.Fake implements _i2.ElementEvents {} + +class _FakeCssStyleDeclaration_5 extends _i1.Fake + implements _i2.CssStyleDeclaration {} + +class _FakeElementStream_6 extends _i1.Fake + implements _i2.ElementStream {} + +class _FakeElementList_7 extends _i1.Fake + implements _i2.ElementList {} + +class _FakeScrollState_8 extends _i1.Fake implements _i2.ScrollState {} + +class _FakeAnimation_9 extends _i1.Fake implements _i2.Animation {} + +class _FakeElement_10 extends _i1.Fake implements _i2.Element {} + +class _FakeShadowRoot_11 extends _i1.Fake implements _i2.ShadowRoot {} + +class _FakeDocumentFragment_12 extends _i1.Fake + implements _i2.DocumentFragment {} + +class _FakeNode_13 extends _i1.Fake implements _i2.Node {} + +class _FakeWidget_14 extends _i1.Fake implements _i4.Widget { + @override + String toString({_i5.DiagnosticLevel? minLevel = _i5.DiagnosticLevel.info}) => + super.toString(); +} + +class _FakeInheritedWidget_15 extends _i1.Fake implements _i4.InheritedWidget { + @override + String toString({_i5.DiagnosticLevel? minLevel = _i5.DiagnosticLevel.info}) => + super.toString(); +} + +class _FakeDiagnosticsNode_16 extends _i1.Fake implements _i5.DiagnosticsNode { + @override + String toString( + {_i5.TextTreeConfiguration? parentConfiguration, + _i5.DiagnosticLevel? minLevel = _i5.DiagnosticLevel.info}) => + super.toString(); +} + +class _FakeHttpRequest_17 extends _i1.Fake implements _i2.HttpRequest {} + +class _FakeHttpRequestUpload_18 extends _i1.Fake + implements _i2.HttpRequestUpload {} + +class _FakeEvents_19 extends _i1.Fake implements _i2.Events {} + +/// A class which mocks [IFrameElement]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockIFrameElement extends _i1.Mock implements _i2.IFrameElement { + MockIFrameElement() { + _i1.throwOnMissingStub(this); + } + + @override + set allow(String? value) => + super.noSuchMethod(Invocation.setter(#allow, value), + returnValueForMissingStub: null); + @override + set allowFullscreen(bool? value) => + super.noSuchMethod(Invocation.setter(#allowFullscreen, value), + returnValueForMissingStub: null); + @override + set allowPaymentRequest(bool? value) => + super.noSuchMethod(Invocation.setter(#allowPaymentRequest, value), + returnValueForMissingStub: null); + @override + set csp(String? value) => super.noSuchMethod(Invocation.setter(#csp, value), + returnValueForMissingStub: null); + @override + set height(String? value) => + super.noSuchMethod(Invocation.setter(#height, value), + returnValueForMissingStub: null); + @override + set name(String? value) => super.noSuchMethod(Invocation.setter(#name, value), + returnValueForMissingStub: null); + @override + set referrerPolicy(String? value) => + super.noSuchMethod(Invocation.setter(#referrerPolicy, value), + returnValueForMissingStub: null); + @override + set src(String? value) => super.noSuchMethod(Invocation.setter(#src, value), + returnValueForMissingStub: null); + @override + set srcdoc(String? value) => + super.noSuchMethod(Invocation.setter(#srcdoc, value), + returnValueForMissingStub: null); + @override + set width(String? value) => + super.noSuchMethod(Invocation.setter(#width, value), + returnValueForMissingStub: null); + @override + set nonce(String? value) => + super.noSuchMethod(Invocation.setter(#nonce, value), + returnValueForMissingStub: null); + @override + Map get attributes => + (super.noSuchMethod(Invocation.getter(#attributes), + returnValue: {}) as Map); + @override + set attributes(Map? value) => + super.noSuchMethod(Invocation.setter(#attributes, value), + returnValueForMissingStub: null); + @override + List<_i2.Element> get children => + (super.noSuchMethod(Invocation.getter(#children), + returnValue: <_i2.Element>[]) as List<_i2.Element>); + @override + set children(List<_i2.Element>? value) => + super.noSuchMethod(Invocation.setter(#children, value), + returnValueForMissingStub: null); + @override + _i2.CssClassSet get classes => + (super.noSuchMethod(Invocation.getter(#classes), + returnValue: _FakeCssClassSet_0()) as _i2.CssClassSet); + @override + set classes(Iterable? value) => + super.noSuchMethod(Invocation.setter(#classes, value), + returnValueForMissingStub: null); + @override + Map get dataset => + (super.noSuchMethod(Invocation.getter(#dataset), + returnValue: {}) as Map); + @override + set dataset(Map? value) => + super.noSuchMethod(Invocation.setter(#dataset, value), + returnValueForMissingStub: null); + @override + _i3.Rectangle get client => + (super.noSuchMethod(Invocation.getter(#client), + returnValue: _FakeRectangle_1()) as _i3.Rectangle); + @override + _i3.Rectangle get offset => + (super.noSuchMethod(Invocation.getter(#offset), + returnValue: _FakeRectangle_1()) as _i3.Rectangle); + @override + String get localName => + (super.noSuchMethod(Invocation.getter(#localName), returnValue: '') + as String); + @override + _i2.CssRect get contentEdge => + (super.noSuchMethod(Invocation.getter(#contentEdge), + returnValue: _FakeCssRect_2()) as _i2.CssRect); + @override + _i2.CssRect get paddingEdge => + (super.noSuchMethod(Invocation.getter(#paddingEdge), + returnValue: _FakeCssRect_2()) as _i2.CssRect); + @override + _i2.CssRect get borderEdge => + (super.noSuchMethod(Invocation.getter(#borderEdge), + returnValue: _FakeCssRect_2()) as _i2.CssRect); + @override + _i2.CssRect get marginEdge => + (super.noSuchMethod(Invocation.getter(#marginEdge), + returnValue: _FakeCssRect_2()) as _i2.CssRect); + @override + _i3.Point get documentOffset => + (super.noSuchMethod(Invocation.getter(#documentOffset), + returnValue: _FakePoint_3()) as _i3.Point); + @override + set innerHtml(String? html) => + super.noSuchMethod(Invocation.setter(#innerHtml, html), + returnValueForMissingStub: null); + @override + String get innerText => + (super.noSuchMethod(Invocation.getter(#innerText), returnValue: '') + as String); + @override + set innerText(String? value) => + super.noSuchMethod(Invocation.setter(#innerText, value), + returnValueForMissingStub: null); + @override + _i2.ElementEvents get on => (super.noSuchMethod(Invocation.getter(#on), + returnValue: _FakeElementEvents_4()) as _i2.ElementEvents); + @override + int get offsetHeight => + (super.noSuchMethod(Invocation.getter(#offsetHeight), returnValue: 0) + as int); + @override + int get offsetLeft => + (super.noSuchMethod(Invocation.getter(#offsetLeft), returnValue: 0) + as int); + @override + int get offsetTop => + (super.noSuchMethod(Invocation.getter(#offsetTop), returnValue: 0) + as int); + @override + int get offsetWidth => + (super.noSuchMethod(Invocation.getter(#offsetWidth), returnValue: 0) + as int); + @override + int get scrollHeight => + (super.noSuchMethod(Invocation.getter(#scrollHeight), returnValue: 0) + as int); + @override + int get scrollLeft => + (super.noSuchMethod(Invocation.getter(#scrollLeft), returnValue: 0) + as int); + @override + set scrollLeft(int? value) => + super.noSuchMethod(Invocation.setter(#scrollLeft, value), + returnValueForMissingStub: null); + @override + int get scrollTop => + (super.noSuchMethod(Invocation.getter(#scrollTop), returnValue: 0) + as int); + @override + set scrollTop(int? value) => + super.noSuchMethod(Invocation.setter(#scrollTop, value), + returnValueForMissingStub: null); + @override + int get scrollWidth => + (super.noSuchMethod(Invocation.getter(#scrollWidth), returnValue: 0) + as int); + @override + String get contentEditable => + (super.noSuchMethod(Invocation.getter(#contentEditable), returnValue: '') + as String); + @override + set contentEditable(String? value) => + super.noSuchMethod(Invocation.setter(#contentEditable, value), + returnValueForMissingStub: null); + @override + set dir(String? value) => super.noSuchMethod(Invocation.setter(#dir, value), + returnValueForMissingStub: null); + @override + bool get draggable => + (super.noSuchMethod(Invocation.getter(#draggable), returnValue: false) + as bool); + @override + set draggable(bool? value) => + super.noSuchMethod(Invocation.setter(#draggable, value), + returnValueForMissingStub: null); + @override + bool get hidden => + (super.noSuchMethod(Invocation.getter(#hidden), returnValue: false) + as bool); + @override + set hidden(bool? value) => + super.noSuchMethod(Invocation.setter(#hidden, value), + returnValueForMissingStub: null); + @override + set inert(bool? value) => super.noSuchMethod(Invocation.setter(#inert, value), + returnValueForMissingStub: null); + @override + set inputMode(String? value) => + super.noSuchMethod(Invocation.setter(#inputMode, value), + returnValueForMissingStub: null); + @override + set lang(String? value) => super.noSuchMethod(Invocation.setter(#lang, value), + returnValueForMissingStub: null); + @override + set spellcheck(bool? value) => + super.noSuchMethod(Invocation.setter(#spellcheck, value), + returnValueForMissingStub: null); + @override + _i2.CssStyleDeclaration get style => (super.noSuchMethod( + Invocation.getter(#style), + returnValue: _FakeCssStyleDeclaration_5()) as _i2.CssStyleDeclaration); + @override + set tabIndex(int? value) => + super.noSuchMethod(Invocation.setter(#tabIndex, value), + returnValueForMissingStub: null); + @override + set title(String? value) => + super.noSuchMethod(Invocation.setter(#title, value), + returnValueForMissingStub: null); + @override + set translate(bool? value) => + super.noSuchMethod(Invocation.setter(#translate, value), + returnValueForMissingStub: null); + @override + String get className => + (super.noSuchMethod(Invocation.getter(#className), returnValue: '') + as String); + @override + set className(String? value) => + super.noSuchMethod(Invocation.setter(#className, value), + returnValueForMissingStub: null); + @override + int get clientHeight => + (super.noSuchMethod(Invocation.getter(#clientHeight), returnValue: 0) + as int); + @override + int get clientWidth => + (super.noSuchMethod(Invocation.getter(#clientWidth), returnValue: 0) + as int); + @override + String get id => + (super.noSuchMethod(Invocation.getter(#id), returnValue: '') as String); + @override + set id(String? value) => super.noSuchMethod(Invocation.setter(#id, value), + returnValueForMissingStub: null); + @override + set slot(String? value) => super.noSuchMethod(Invocation.setter(#slot, value), + returnValueForMissingStub: null); + @override + String get tagName => + (super.noSuchMethod(Invocation.getter(#tagName), returnValue: '') + as String); + @override + _i2.ElementStream<_i2.Event> get onAbort => + (super.noSuchMethod(Invocation.getter(#onAbort), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onBeforeCopy => + (super.noSuchMethod(Invocation.getter(#onBeforeCopy), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onBeforeCut => + (super.noSuchMethod(Invocation.getter(#onBeforeCut), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onBeforePaste => + (super.noSuchMethod(Invocation.getter(#onBeforePaste), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onBlur => + (super.noSuchMethod(Invocation.getter(#onBlur), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onCanPlay => + (super.noSuchMethod(Invocation.getter(#onCanPlay), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onCanPlayThrough => + (super.noSuchMethod(Invocation.getter(#onCanPlayThrough), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onChange => + (super.noSuchMethod(Invocation.getter(#onChange), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.MouseEvent> get onClick => + (super.noSuchMethod(Invocation.getter(#onClick), + returnValue: _FakeElementStream_6<_i2.MouseEvent>()) + as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.MouseEvent> get onContextMenu => + (super.noSuchMethod(Invocation.getter(#onContextMenu), + returnValue: _FakeElementStream_6<_i2.MouseEvent>()) + as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.ClipboardEvent> get onCopy => + (super.noSuchMethod(Invocation.getter(#onCopy), + returnValue: _FakeElementStream_6<_i2.ClipboardEvent>()) + as _i2.ElementStream<_i2.ClipboardEvent>); + @override + _i2.ElementStream<_i2.ClipboardEvent> get onCut => + (super.noSuchMethod(Invocation.getter(#onCut), + returnValue: _FakeElementStream_6<_i2.ClipboardEvent>()) + as _i2.ElementStream<_i2.ClipboardEvent>); + @override + _i2.ElementStream<_i2.Event> get onDoubleClick => + (super.noSuchMethod(Invocation.getter(#onDoubleClick), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.MouseEvent> get onDrag => + (super.noSuchMethod(Invocation.getter(#onDrag), + returnValue: _FakeElementStream_6<_i2.MouseEvent>()) + as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.MouseEvent> get onDragEnd => + (super.noSuchMethod(Invocation.getter(#onDragEnd), + returnValue: _FakeElementStream_6<_i2.MouseEvent>()) + as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.MouseEvent> get onDragEnter => + (super.noSuchMethod(Invocation.getter(#onDragEnter), + returnValue: _FakeElementStream_6<_i2.MouseEvent>()) + as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.MouseEvent> get onDragLeave => + (super.noSuchMethod(Invocation.getter(#onDragLeave), + returnValue: _FakeElementStream_6<_i2.MouseEvent>()) + as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.MouseEvent> get onDragOver => + (super.noSuchMethod(Invocation.getter(#onDragOver), + returnValue: _FakeElementStream_6<_i2.MouseEvent>()) + as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.MouseEvent> get onDragStart => + (super.noSuchMethod(Invocation.getter(#onDragStart), + returnValue: _FakeElementStream_6<_i2.MouseEvent>()) + as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.MouseEvent> get onDrop => + (super.noSuchMethod(Invocation.getter(#onDrop), + returnValue: _FakeElementStream_6<_i2.MouseEvent>()) + as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.Event> get onDurationChange => + (super.noSuchMethod(Invocation.getter(#onDurationChange), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onEmptied => + (super.noSuchMethod(Invocation.getter(#onEmptied), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onEnded => + (super.noSuchMethod(Invocation.getter(#onEnded), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onError => + (super.noSuchMethod(Invocation.getter(#onError), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onFocus => + (super.noSuchMethod(Invocation.getter(#onFocus), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onInput => + (super.noSuchMethod(Invocation.getter(#onInput), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onInvalid => + (super.noSuchMethod(Invocation.getter(#onInvalid), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.KeyboardEvent> get onKeyDown => + (super.noSuchMethod(Invocation.getter(#onKeyDown), + returnValue: _FakeElementStream_6<_i2.KeyboardEvent>()) + as _i2.ElementStream<_i2.KeyboardEvent>); + @override + _i2.ElementStream<_i2.KeyboardEvent> get onKeyPress => + (super.noSuchMethod(Invocation.getter(#onKeyPress), + returnValue: _FakeElementStream_6<_i2.KeyboardEvent>()) + as _i2.ElementStream<_i2.KeyboardEvent>); + @override + _i2.ElementStream<_i2.KeyboardEvent> get onKeyUp => + (super.noSuchMethod(Invocation.getter(#onKeyUp), + returnValue: _FakeElementStream_6<_i2.KeyboardEvent>()) + as _i2.ElementStream<_i2.KeyboardEvent>); + @override + _i2.ElementStream<_i2.Event> get onLoad => + (super.noSuchMethod(Invocation.getter(#onLoad), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onLoadedData => + (super.noSuchMethod(Invocation.getter(#onLoadedData), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onLoadedMetadata => + (super.noSuchMethod(Invocation.getter(#onLoadedMetadata), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.MouseEvent> get onMouseDown => + (super.noSuchMethod(Invocation.getter(#onMouseDown), + returnValue: _FakeElementStream_6<_i2.MouseEvent>()) + as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.MouseEvent> get onMouseEnter => + (super.noSuchMethod(Invocation.getter(#onMouseEnter), + returnValue: _FakeElementStream_6<_i2.MouseEvent>()) + as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.MouseEvent> get onMouseLeave => + (super.noSuchMethod(Invocation.getter(#onMouseLeave), + returnValue: _FakeElementStream_6<_i2.MouseEvent>()) + as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.MouseEvent> get onMouseMove => + (super.noSuchMethod(Invocation.getter(#onMouseMove), + returnValue: _FakeElementStream_6<_i2.MouseEvent>()) + as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.MouseEvent> get onMouseOut => + (super.noSuchMethod(Invocation.getter(#onMouseOut), + returnValue: _FakeElementStream_6<_i2.MouseEvent>()) + as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.MouseEvent> get onMouseOver => + (super.noSuchMethod(Invocation.getter(#onMouseOver), + returnValue: _FakeElementStream_6<_i2.MouseEvent>()) + as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.MouseEvent> get onMouseUp => + (super.noSuchMethod(Invocation.getter(#onMouseUp), + returnValue: _FakeElementStream_6<_i2.MouseEvent>()) + as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.WheelEvent> get onMouseWheel => + (super.noSuchMethod(Invocation.getter(#onMouseWheel), + returnValue: _FakeElementStream_6<_i2.WheelEvent>()) + as _i2.ElementStream<_i2.WheelEvent>); + @override + _i2.ElementStream<_i2.ClipboardEvent> get onPaste => + (super.noSuchMethod(Invocation.getter(#onPaste), + returnValue: _FakeElementStream_6<_i2.ClipboardEvent>()) + as _i2.ElementStream<_i2.ClipboardEvent>); + @override + _i2.ElementStream<_i2.Event> get onPause => + (super.noSuchMethod(Invocation.getter(#onPause), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onPlay => + (super.noSuchMethod(Invocation.getter(#onPlay), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onPlaying => + (super.noSuchMethod(Invocation.getter(#onPlaying), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onRateChange => + (super.noSuchMethod(Invocation.getter(#onRateChange), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onReset => + (super.noSuchMethod(Invocation.getter(#onReset), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onResize => + (super.noSuchMethod(Invocation.getter(#onResize), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onScroll => + (super.noSuchMethod(Invocation.getter(#onScroll), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onSearch => + (super.noSuchMethod(Invocation.getter(#onSearch), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onSeeked => + (super.noSuchMethod(Invocation.getter(#onSeeked), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onSeeking => + (super.noSuchMethod(Invocation.getter(#onSeeking), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onSelect => + (super.noSuchMethod(Invocation.getter(#onSelect), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onSelectStart => + (super.noSuchMethod(Invocation.getter(#onSelectStart), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onStalled => + (super.noSuchMethod(Invocation.getter(#onStalled), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onSubmit => + (super.noSuchMethod(Invocation.getter(#onSubmit), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onSuspend => + (super.noSuchMethod(Invocation.getter(#onSuspend), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onTimeUpdate => + (super.noSuchMethod(Invocation.getter(#onTimeUpdate), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.TouchEvent> get onTouchCancel => + (super.noSuchMethod(Invocation.getter(#onTouchCancel), + returnValue: _FakeElementStream_6<_i2.TouchEvent>()) + as _i2.ElementStream<_i2.TouchEvent>); + @override + _i2.ElementStream<_i2.TouchEvent> get onTouchEnd => + (super.noSuchMethod(Invocation.getter(#onTouchEnd), + returnValue: _FakeElementStream_6<_i2.TouchEvent>()) + as _i2.ElementStream<_i2.TouchEvent>); + @override + _i2.ElementStream<_i2.TouchEvent> get onTouchEnter => + (super.noSuchMethod(Invocation.getter(#onTouchEnter), + returnValue: _FakeElementStream_6<_i2.TouchEvent>()) + as _i2.ElementStream<_i2.TouchEvent>); + @override + _i2.ElementStream<_i2.TouchEvent> get onTouchLeave => + (super.noSuchMethod(Invocation.getter(#onTouchLeave), + returnValue: _FakeElementStream_6<_i2.TouchEvent>()) + as _i2.ElementStream<_i2.TouchEvent>); + @override + _i2.ElementStream<_i2.TouchEvent> get onTouchMove => + (super.noSuchMethod(Invocation.getter(#onTouchMove), + returnValue: _FakeElementStream_6<_i2.TouchEvent>()) + as _i2.ElementStream<_i2.TouchEvent>); + @override + _i2.ElementStream<_i2.TouchEvent> get onTouchStart => + (super.noSuchMethod(Invocation.getter(#onTouchStart), + returnValue: _FakeElementStream_6<_i2.TouchEvent>()) + as _i2.ElementStream<_i2.TouchEvent>); + @override + _i2.ElementStream<_i2.TransitionEvent> get onTransitionEnd => + (super.noSuchMethod(Invocation.getter(#onTransitionEnd), + returnValue: _FakeElementStream_6<_i2.TransitionEvent>()) + as _i2.ElementStream<_i2.TransitionEvent>); + @override + _i2.ElementStream<_i2.Event> get onVolumeChange => + (super.noSuchMethod(Invocation.getter(#onVolumeChange), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onWaiting => + (super.noSuchMethod(Invocation.getter(#onWaiting), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onFullscreenChange => + (super.noSuchMethod(Invocation.getter(#onFullscreenChange), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onFullscreenError => + (super.noSuchMethod(Invocation.getter(#onFullscreenError), + returnValue: _FakeElementStream_6<_i2.Event>()) + as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.WheelEvent> get onWheel => + (super.noSuchMethod(Invocation.getter(#onWheel), + returnValue: _FakeElementStream_6<_i2.WheelEvent>()) + as _i2.ElementStream<_i2.WheelEvent>); + @override + List<_i2.Node> get nodes => + (super.noSuchMethod(Invocation.getter(#nodes), returnValue: <_i2.Node>[]) + as List<_i2.Node>); + @override + set nodes(Iterable<_i2.Node>? value) => + super.noSuchMethod(Invocation.setter(#nodes, value), + returnValueForMissingStub: null); + @override + List<_i2.Node> get childNodes => + (super.noSuchMethod(Invocation.getter(#childNodes), + returnValue: <_i2.Node>[]) as List<_i2.Node>); + @override + int get nodeType => + (super.noSuchMethod(Invocation.getter(#nodeType), returnValue: 0) as int); + @override + set text(String? value) => super.noSuchMethod(Invocation.setter(#text, value), + returnValueForMissingStub: null); + @override + String? getAttribute(String? name) => + (super.noSuchMethod(Invocation.method(#getAttribute, [name])) as String?); + @override + String? getAttributeNS(String? namespaceURI, String? name) => + (super.noSuchMethod( + Invocation.method(#getAttributeNS, [namespaceURI, name])) as String?); + @override + bool hasAttribute(String? name) => + (super.noSuchMethod(Invocation.method(#hasAttribute, [name]), + returnValue: false) as bool); + @override + bool hasAttributeNS(String? namespaceURI, String? name) => (super + .noSuchMethod(Invocation.method(#hasAttributeNS, [namespaceURI, name]), + returnValue: false) as bool); + @override + void removeAttribute(String? name) => + super.noSuchMethod(Invocation.method(#removeAttribute, [name]), + returnValueForMissingStub: null); + @override + void removeAttributeNS(String? namespaceURI, String? name) => super + .noSuchMethod(Invocation.method(#removeAttributeNS, [namespaceURI, name]), + returnValueForMissingStub: null); + @override + void setAttribute(String? name, Object? value) => + super.noSuchMethod(Invocation.method(#setAttribute, [name, value]), + returnValueForMissingStub: null); + @override + void setAttributeNS(String? namespaceURI, String? name, Object? value) => + super.noSuchMethod( + Invocation.method(#setAttributeNS, [namespaceURI, name, value]), + returnValueForMissingStub: null); + @override + _i2.ElementList querySelectorAll( + String? selectors) => + (super.noSuchMethod(Invocation.method(#querySelectorAll, [selectors]), + returnValue: _FakeElementList_7()) as _i2.ElementList); + @override + _i6.Future<_i2.ScrollState> setApplyScroll(String? nativeScrollBehavior) => + (super.noSuchMethod( + Invocation.method(#setApplyScroll, [nativeScrollBehavior]), + returnValue: Future<_i2.ScrollState>.value(_FakeScrollState_8())) + as _i6.Future<_i2.ScrollState>); + @override + _i6.Future<_i2.ScrollState> setDistributeScroll( + String? nativeScrollBehavior) => + (super.noSuchMethod( + Invocation.method(#setDistributeScroll, [nativeScrollBehavior]), + returnValue: Future<_i2.ScrollState>.value(_FakeScrollState_8())) + as _i6.Future<_i2.ScrollState>); + @override + Map getNamespacedAttributes(String? namespace) => (super + .noSuchMethod(Invocation.method(#getNamespacedAttributes, [namespace]), + returnValue: {}) as Map); + @override + _i2.CssStyleDeclaration getComputedStyle([String? pseudoElement]) => + (super.noSuchMethod(Invocation.method(#getComputedStyle, [pseudoElement]), + returnValue: _FakeCssStyleDeclaration_5()) + as _i2.CssStyleDeclaration); + @override + void appendText(String? text) => + super.noSuchMethod(Invocation.method(#appendText, [text]), + returnValueForMissingStub: null); + @override + void appendHtml(String? text, + {_i2.NodeValidator? validator, + _i2.NodeTreeSanitizer? treeSanitizer}) => + super.noSuchMethod( + Invocation.method(#appendHtml, [text], + {#validator: validator, #treeSanitizer: treeSanitizer}), + returnValueForMissingStub: null); + @override + void attached() => super.noSuchMethod(Invocation.method(#attached, []), + returnValueForMissingStub: null); + @override + void detached() => super.noSuchMethod(Invocation.method(#detached, []), + returnValueForMissingStub: null); + @override + void enteredView() => super.noSuchMethod(Invocation.method(#enteredView, []), + returnValueForMissingStub: null); + @override + List<_i3.Rectangle> getClientRects() => + (super.noSuchMethod(Invocation.method(#getClientRects, []), + returnValue: <_i3.Rectangle>[]) as List<_i3.Rectangle>); + @override + void leftView() => super.noSuchMethod(Invocation.method(#leftView, []), + returnValueForMissingStub: null); + @override + _i2.Animation animate(Iterable>? frames, + [dynamic timing]) => + (super.noSuchMethod(Invocation.method(#animate, [frames, timing]), + returnValue: _FakeAnimation_9()) as _i2.Animation); + @override + void attributeChanged(String? name, String? oldValue, String? newValue) => + super.noSuchMethod( + Invocation.method(#attributeChanged, [name, oldValue, newValue]), + returnValueForMissingStub: null); + @override + String toString() => super.toString(); + @override + void scrollIntoView([_i2.ScrollAlignment? alignment]) => + super.noSuchMethod(Invocation.method(#scrollIntoView, [alignment]), + returnValueForMissingStub: null); + @override + void insertAdjacentText(String? where, String? text) => + super.noSuchMethod(Invocation.method(#insertAdjacentText, [where, text]), + returnValueForMissingStub: null); + @override + void insertAdjacentHtml(String? where, String? html, + {_i2.NodeValidator? validator, + _i2.NodeTreeSanitizer? treeSanitizer}) => + super.noSuchMethod( + Invocation.method(#insertAdjacentHtml, [where, html], + {#validator: validator, #treeSanitizer: treeSanitizer}), + returnValueForMissingStub: null); + @override + _i2.Element insertAdjacentElement(String? where, _i2.Element? element) => + (super.noSuchMethod( + Invocation.method(#insertAdjacentElement, [where, element]), + returnValue: _FakeElement_10()) as _i2.Element); + @override + bool matches(String? selectors) => + (super.noSuchMethod(Invocation.method(#matches, [selectors]), + returnValue: false) as bool); + @override + bool matchesWithAncestors(String? selectors) => + (super.noSuchMethod(Invocation.method(#matchesWithAncestors, [selectors]), + returnValue: false) as bool); + @override + _i2.ShadowRoot createShadowRoot() => + (super.noSuchMethod(Invocation.method(#createShadowRoot, []), + returnValue: _FakeShadowRoot_11()) as _i2.ShadowRoot); + @override + _i3.Point offsetTo(_i2.Element? parent) => + (super.noSuchMethod(Invocation.method(#offsetTo, [parent]), + returnValue: _FakePoint_3()) as _i3.Point); + @override + _i2.DocumentFragment createFragment(String? html, + {_i2.NodeValidator? validator, + _i2.NodeTreeSanitizer? treeSanitizer}) => + (super.noSuchMethod( + Invocation.method(#createFragment, [html], + {#validator: validator, #treeSanitizer: treeSanitizer}), + returnValue: _FakeDocumentFragment_12()) as _i2.DocumentFragment); + @override + void setInnerHtml(String? html, + {_i2.NodeValidator? validator, + _i2.NodeTreeSanitizer? treeSanitizer}) => + super.noSuchMethod( + Invocation.method(#setInnerHtml, [html], + {#validator: validator, #treeSanitizer: treeSanitizer}), + returnValueForMissingStub: null); + @override + void blur() => super.noSuchMethod(Invocation.method(#blur, []), + returnValueForMissingStub: null); + @override + void click() => super.noSuchMethod(Invocation.method(#click, []), + returnValueForMissingStub: null); + @override + void focus() => super.noSuchMethod(Invocation.method(#focus, []), + returnValueForMissingStub: null); + @override + _i2.ShadowRoot attachShadow(Map? shadowRootInitDict) => + (super.noSuchMethod( + Invocation.method(#attachShadow, [shadowRootInitDict]), + returnValue: _FakeShadowRoot_11()) as _i2.ShadowRoot); + @override + _i2.Element? closest(String? selectors) => + (super.noSuchMethod(Invocation.method(#closest, [selectors])) + as _i2.Element?); + @override + List<_i2.Animation> getAnimations() => + (super.noSuchMethod(Invocation.method(#getAnimations, []), + returnValue: <_i2.Animation>[]) as List<_i2.Animation>); + @override + List getAttributeNames() => + (super.noSuchMethod(Invocation.method(#getAttributeNames, []), + returnValue: []) as List); + @override + _i3.Rectangle getBoundingClientRect() => + (super.noSuchMethod(Invocation.method(#getBoundingClientRect, []), + returnValue: _FakeRectangle_1()) as _i3.Rectangle); + @override + List<_i2.Node> getDestinationInsertionPoints() => + (super.noSuchMethod(Invocation.method(#getDestinationInsertionPoints, []), + returnValue: <_i2.Node>[]) as List<_i2.Node>); + @override + List<_i2.Node> getElementsByClassName(String? classNames) => (super + .noSuchMethod(Invocation.method(#getElementsByClassName, [classNames]), + returnValue: <_i2.Node>[]) as List<_i2.Node>); + @override + bool hasPointerCapture(int? pointerId) => + (super.noSuchMethod(Invocation.method(#hasPointerCapture, [pointerId]), + returnValue: false) as bool); + @override + void releasePointerCapture(int? pointerId) => + super.noSuchMethod(Invocation.method(#releasePointerCapture, [pointerId]), + returnValueForMissingStub: null); + @override + void requestPointerLock() => + super.noSuchMethod(Invocation.method(#requestPointerLock, []), + returnValueForMissingStub: null); + @override + void scroll([dynamic options_OR_x, num? y]) => + super.noSuchMethod(Invocation.method(#scroll, [options_OR_x, y]), + returnValueForMissingStub: null); + @override + void scrollBy([dynamic options_OR_x, num? y]) => + super.noSuchMethod(Invocation.method(#scrollBy, [options_OR_x, y]), + returnValueForMissingStub: null); + @override + void scrollTo([dynamic options_OR_x, num? y]) => + super.noSuchMethod(Invocation.method(#scrollTo, [options_OR_x, y]), + returnValueForMissingStub: null); + @override + void setPointerCapture(int? pointerId) => + super.noSuchMethod(Invocation.method(#setPointerCapture, [pointerId]), + returnValueForMissingStub: null); + @override + void requestFullscreen() => + super.noSuchMethod(Invocation.method(#requestFullscreen, []), + returnValueForMissingStub: null); + @override + void after(Object? nodes) => + super.noSuchMethod(Invocation.method(#after, [nodes]), + returnValueForMissingStub: null); + @override + void before(Object? nodes) => + super.noSuchMethod(Invocation.method(#before, [nodes]), + returnValueForMissingStub: null); + @override + _i2.Element? querySelector(String? selectors) => + (super.noSuchMethod(Invocation.method(#querySelector, [selectors])) + as _i2.Element?); + @override + void remove() => super.noSuchMethod(Invocation.method(#remove, []), + returnValueForMissingStub: null); + @override + _i2.Node replaceWith(_i2.Node? otherNode) => + (super.noSuchMethod(Invocation.method(#replaceWith, [otherNode]), + returnValue: _FakeNode_13()) as _i2.Node); + @override + void insertAllBefore(Iterable<_i2.Node>? newNodes, _i2.Node? refChild) => + super.noSuchMethod( + Invocation.method(#insertAllBefore, [newNodes, refChild]), + returnValueForMissingStub: null); + @override + _i2.Node append(_i2.Node? node) => + (super.noSuchMethod(Invocation.method(#append, [node]), + returnValue: _FakeNode_13()) as _i2.Node); + @override + _i2.Node clone(bool? deep) => + (super.noSuchMethod(Invocation.method(#clone, [deep]), + returnValue: _FakeNode_13()) as _i2.Node); + @override + bool contains(_i2.Node? other) => + (super.noSuchMethod(Invocation.method(#contains, [other]), + returnValue: false) as bool); + @override + _i2.Node getRootNode([Map? options]) => + (super.noSuchMethod(Invocation.method(#getRootNode, [options]), + returnValue: _FakeNode_13()) as _i2.Node); + @override + bool hasChildNodes() => + (super.noSuchMethod(Invocation.method(#hasChildNodes, []), + returnValue: false) as bool); + @override + _i2.Node insertBefore(_i2.Node? node, _i2.Node? child) => + (super.noSuchMethod(Invocation.method(#insertBefore, [node, child]), + returnValue: _FakeNode_13()) as _i2.Node); + @override + void addEventListener(String? type, _i2.EventListener? listener, + [bool? useCapture]) => + super.noSuchMethod( + Invocation.method(#addEventListener, [type, listener, useCapture]), + returnValueForMissingStub: null); + @override + void removeEventListener(String? type, _i2.EventListener? listener, + [bool? useCapture]) => + super.noSuchMethod( + Invocation.method(#removeEventListener, [type, listener, useCapture]), + returnValueForMissingStub: null); + @override + bool dispatchEvent(_i2.Event? event) => + (super.noSuchMethod(Invocation.method(#dispatchEvent, [event]), + returnValue: false) as bool); +} + +/// A class which mocks [BuildContext]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockBuildContext extends _i1.Mock implements _i4.BuildContext { + MockBuildContext() { + _i1.throwOnMissingStub(this); + } + + @override + _i4.Widget get widget => (super.noSuchMethod(Invocation.getter(#widget), + returnValue: _FakeWidget_14()) as _i4.Widget); + @override + bool get debugDoingBuild => (super + .noSuchMethod(Invocation.getter(#debugDoingBuild), returnValue: false) + as bool); + @override + _i4.InheritedWidget dependOnInheritedElement(_i4.InheritedElement? ancestor, + {Object? aspect}) => + (super.noSuchMethod( + Invocation.method( + #dependOnInheritedElement, [ancestor], {#aspect: aspect}), + returnValue: _FakeInheritedWidget_15()) as _i4.InheritedWidget); + @override + void visitAncestorElements(bool Function(_i4.Element)? visitor) => + super.noSuchMethod(Invocation.method(#visitAncestorElements, [visitor]), + returnValueForMissingStub: null); + @override + void visitChildElements(_i4.ElementVisitor? visitor) => + super.noSuchMethod(Invocation.method(#visitChildElements, [visitor]), + returnValueForMissingStub: null); + @override + _i5.DiagnosticsNode describeElement(String? name, + {_i5.DiagnosticsTreeStyle? style = + _i5.DiagnosticsTreeStyle.errorProperty}) => + (super.noSuchMethod( + Invocation.method(#describeElement, [name], {#style: style}), + returnValue: _FakeDiagnosticsNode_16()) as _i5.DiagnosticsNode); + @override + _i5.DiagnosticsNode describeWidget(String? name, + {_i5.DiagnosticsTreeStyle? style = + _i5.DiagnosticsTreeStyle.errorProperty}) => + (super.noSuchMethod( + Invocation.method(#describeWidget, [name], {#style: style}), + returnValue: _FakeDiagnosticsNode_16()) as _i5.DiagnosticsNode); + @override + List<_i5.DiagnosticsNode> describeMissingAncestor( + {Type? expectedAncestorType}) => + (super.noSuchMethod( + Invocation.method(#describeMissingAncestor, [], + {#expectedAncestorType: expectedAncestorType}), + returnValue: <_i5.DiagnosticsNode>[]) as List<_i5.DiagnosticsNode>); + @override + _i5.DiagnosticsNode describeOwnershipChain(String? name) => + (super.noSuchMethod(Invocation.method(#describeOwnershipChain, [name]), + returnValue: _FakeDiagnosticsNode_16()) as _i5.DiagnosticsNode); + @override + String toString() => super.toString(); +} + +/// A class which mocks [CreationParams]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockCreationParams extends _i1.Mock implements _i7.CreationParams { + MockCreationParams() { + _i1.throwOnMissingStub(this); + } + + @override + Set get javascriptChannelNames => + (super.noSuchMethod(Invocation.getter(#javascriptChannelNames), + returnValue: {}) as Set); + @override + _i8.AutoMediaPlaybackPolicy get autoMediaPlaybackPolicy => + (super.noSuchMethod(Invocation.getter(#autoMediaPlaybackPolicy), + returnValue: _i8.AutoMediaPlaybackPolicy + .require_user_action_for_all_media_types) + as _i8.AutoMediaPlaybackPolicy); + @override + List<_i7.WebViewCookie> get cookies => + (super.noSuchMethod(Invocation.getter(#cookies), + returnValue: <_i7.WebViewCookie>[]) as List<_i7.WebViewCookie>); + @override + String toString() => super.toString(); +} + +/// A class which mocks [WebViewPlatformCallbacksHandler]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockWebViewPlatformCallbacksHandler extends _i1.Mock + implements _i9.WebViewPlatformCallbacksHandler { + MockWebViewPlatformCallbacksHandler() { + _i1.throwOnMissingStub(this); + } + + @override + _i6.FutureOr onNavigationRequest({String? url, bool? isForMainFrame}) => + (super.noSuchMethod( + Invocation.method(#onNavigationRequest, [], + {#url: url, #isForMainFrame: isForMainFrame}), + returnValue: Future.value(false)) as _i6.FutureOr); + @override + void onPageStarted(String? url) => + super.noSuchMethod(Invocation.method(#onPageStarted, [url]), + returnValueForMissingStub: null); + @override + void onPageFinished(String? url) => + super.noSuchMethod(Invocation.method(#onPageFinished, [url]), + returnValueForMissingStub: null); + @override + void onProgress(int? progress) => + super.noSuchMethod(Invocation.method(#onProgress, [progress]), + returnValueForMissingStub: null); + @override + void onWebResourceError(_i7.WebResourceError? error) => + super.noSuchMethod(Invocation.method(#onWebResourceError, [error]), + returnValueForMissingStub: null); + @override + String toString() => super.toString(); +} + +/// A class which mocks [HttpRequestFactory]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockHttpRequestFactory extends _i1.Mock + implements _i10.HttpRequestFactory { + MockHttpRequestFactory() { + _i1.throwOnMissingStub(this); + } + + @override + _i6.Future<_i2.HttpRequest> request(String? url, + {String? method, + bool? withCredentials, + String? responseType, + String? mimeType, + Map? requestHeaders, + dynamic sendData, + void Function(_i2.ProgressEvent)? onProgress}) => + (super.noSuchMethod( + Invocation.method(#request, [ + url + ], { + #method: method, + #withCredentials: withCredentials, + #responseType: responseType, + #mimeType: mimeType, + #requestHeaders: requestHeaders, + #sendData: sendData, + #onProgress: onProgress + }), + returnValue: Future<_i2.HttpRequest>.value(_FakeHttpRequest_17())) + as _i6.Future<_i2.HttpRequest>); + @override + String toString() => super.toString(); +} + +/// A class which mocks [HttpRequest]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockHttpRequest extends _i1.Mock implements _i2.HttpRequest { + MockHttpRequest() { + _i1.throwOnMissingStub(this); + } + + @override + Map get responseHeaders => + (super.noSuchMethod(Invocation.getter(#responseHeaders), + returnValue: {}) as Map); + @override + int get readyState => + (super.noSuchMethod(Invocation.getter(#readyState), returnValue: 0) + as int); + @override + String get responseType => + (super.noSuchMethod(Invocation.getter(#responseType), returnValue: '') + as String); + @override + set responseType(String? value) => + super.noSuchMethod(Invocation.setter(#responseType, value), + returnValueForMissingStub: null); + @override + set timeout(int? value) => + super.noSuchMethod(Invocation.setter(#timeout, value), + returnValueForMissingStub: null); + @override + _i2.HttpRequestUpload get upload => + (super.noSuchMethod(Invocation.getter(#upload), + returnValue: _FakeHttpRequestUpload_18()) as _i2.HttpRequestUpload); + @override + set withCredentials(bool? value) => + super.noSuchMethod(Invocation.setter(#withCredentials, value), + returnValueForMissingStub: null); + @override + _i6.Stream<_i2.Event> get onReadyStateChange => + (super.noSuchMethod(Invocation.getter(#onReadyStateChange), + returnValue: Stream<_i2.Event>.empty()) as _i6.Stream<_i2.Event>); + @override + _i6.Stream<_i2.ProgressEvent> get onAbort => + (super.noSuchMethod(Invocation.getter(#onAbort), + returnValue: Stream<_i2.ProgressEvent>.empty()) + as _i6.Stream<_i2.ProgressEvent>); + @override + _i6.Stream<_i2.ProgressEvent> get onError => + (super.noSuchMethod(Invocation.getter(#onError), + returnValue: Stream<_i2.ProgressEvent>.empty()) + as _i6.Stream<_i2.ProgressEvent>); + @override + _i6.Stream<_i2.ProgressEvent> get onLoad => + (super.noSuchMethod(Invocation.getter(#onLoad), + returnValue: Stream<_i2.ProgressEvent>.empty()) + as _i6.Stream<_i2.ProgressEvent>); + @override + _i6.Stream<_i2.ProgressEvent> get onLoadEnd => + (super.noSuchMethod(Invocation.getter(#onLoadEnd), + returnValue: Stream<_i2.ProgressEvent>.empty()) + as _i6.Stream<_i2.ProgressEvent>); + @override + _i6.Stream<_i2.ProgressEvent> get onLoadStart => + (super.noSuchMethod(Invocation.getter(#onLoadStart), + returnValue: Stream<_i2.ProgressEvent>.empty()) + as _i6.Stream<_i2.ProgressEvent>); + @override + _i6.Stream<_i2.ProgressEvent> get onProgress => + (super.noSuchMethod(Invocation.getter(#onProgress), + returnValue: Stream<_i2.ProgressEvent>.empty()) + as _i6.Stream<_i2.ProgressEvent>); + @override + _i6.Stream<_i2.ProgressEvent> get onTimeout => + (super.noSuchMethod(Invocation.getter(#onTimeout), + returnValue: Stream<_i2.ProgressEvent>.empty()) + as _i6.Stream<_i2.ProgressEvent>); + @override + _i2.Events get on => + (super.noSuchMethod(Invocation.getter(#on), returnValue: _FakeEvents_19()) + as _i2.Events); + @override + void open(String? method, String? url, + {bool? async, String? user, String? password}) => + super.noSuchMethod( + Invocation.method(#open, [method, url], + {#async: async, #user: user, #password: password}), + returnValueForMissingStub: null); + @override + void abort() => super.noSuchMethod(Invocation.method(#abort, []), + returnValueForMissingStub: null); + @override + String getAllResponseHeaders() => + (super.noSuchMethod(Invocation.method(#getAllResponseHeaders, []), + returnValue: '') as String); + @override + String? getResponseHeader(String? name) => + (super.noSuchMethod(Invocation.method(#getResponseHeader, [name])) + as String?); + @override + void overrideMimeType(String? mime) => + super.noSuchMethod(Invocation.method(#overrideMimeType, [mime]), + returnValueForMissingStub: null); + @override + void send([dynamic body_OR_data]) => + super.noSuchMethod(Invocation.method(#send, [body_OR_data]), + returnValueForMissingStub: null); + @override + void setRequestHeader(String? name, String? value) => + super.noSuchMethod(Invocation.method(#setRequestHeader, [name, value]), + returnValueForMissingStub: null); + @override + void addEventListener(String? type, _i2.EventListener? listener, + [bool? useCapture]) => + super.noSuchMethod( + Invocation.method(#addEventListener, [type, listener, useCapture]), + returnValueForMissingStub: null); + @override + void removeEventListener(String? type, _i2.EventListener? listener, + [bool? useCapture]) => + super.noSuchMethod( + Invocation.method(#removeEventListener, [type, listener, useCapture]), + returnValueForMissingStub: null); + @override + bool dispatchEvent(_i2.Event? event) => + (super.noSuchMethod(Invocation.method(#dispatchEvent, [event]), + returnValue: false) as bool); + @override + String toString() => super.toString(); +} From d7f7b89adf34f92eee8f72de263d5ce8ce752068 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 7 Dec 2021 19:48:51 -0500 Subject: [PATCH 049/600] [webview_flutter] Default Android to hybrid composition (#4576) Switches the default implemention on Android from virtual display to hybrid composition. This is a breaking change. Part of https://github.com/flutter/flutter/issues/93335 --- .../webview_flutter/CHANGELOG.md | 6 + .../webview_flutter/webview_flutter/README.md | 45 ++--- .../webview_flutter_test.dart | 177 ++---------------- .../webview_flutter/lib/src/webview.dart | 6 +- .../webview_flutter/pubspec.yaml | 2 +- 5 files changed, 43 insertions(+), 193 deletions(-) diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index 4906003a2e02..aa117c9d72a8 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,3 +1,9 @@ +## 3.0.0 + +* **BREAKING CHANGE**: On Android, hybrid composition (SurfaceAndroidWebView) + is now the default. The previous default, virtual display, can be specified + with `WebView.platform = AndroidWebView()` + ## 2.8.0 * Adds support for the `loadFlutterAsset` method. diff --git a/packages/webview_flutter/webview_flutter/README.md b/packages/webview_flutter/webview_flutter/README.md index c3982eab2bad..8387eb685b7d 100644 --- a/packages/webview_flutter/webview_flutter/README.md +++ b/packages/webview_flutter/webview_flutter/README.md @@ -15,68 +15,63 @@ You can now include a WebView widget in your widget tree. See the widget's Dartdoc for more details on how to use the widget. ## Android Platform Views -The WebView is relying on +This plugin uses [Platform Views](https://flutter.dev/docs/development/platform-integration/platform-views) to embed -the Android’s webview within the Flutter app. It supports two modes: *Virtual displays* (the current default) and *Hybrid composition*. +the Android’s webview within the Flutter app. It supports two modes: +*hybrid composition* (the current default) and *virtual display*. Here are some points to consider when choosing between the two: -* *Hybrid composition* mode has a built-in keyboard support while *Virtual displays* mode has multiple -[keyboard issues](https://github.com/flutter/flutter/issues?q=is%3Aopen+label%3Avd-only+label%3A%22p%3A+webview-keyboard%22) -* *Hybrid composition* mode requires Android SDK 19+ while *Virtual displays* mode requires Android SDK 20+ -* *Hybrid composition* mode has [performance limitations](https://flutter.dev/docs/development/platform-integration/platform-views#performance) when working on Android versions prior to Android 10 while *Virtual displays* is performant on all supported Android versions +* *Hybrid composition* has built-in keyboard support while *virtual display* has multiple +[keyboard issues](https://github.com/flutter/flutter/issues?q=is%3Aopen+label%3Avd-only+label%3A%22p%3A+webview-keyboard%22). +* *Hybrid composition* requires Android SDK 19+ while *virtual display* requires Android SDK 20+. +* *Hybrid composition* and *virtual display* have different + [performance tradeoffs](https://flutter.dev/docs/development/platform-integration/platform-views#performance). -| | Hybrid composition | Virtual displays | -| --------------------------- | ------------------- | ---------------- | -| **Full keyboard supoport** | yes | no | -| **Android SDK support** | 19+ | 20+ | -| **Full performance** | Android 10+ | always | -| **The default** | no | yes | -### Using Virtual displays +### Using Hybrid Composition -The mode is currently enabled by default. You should however make sure to set the correct `minSdkVersion` in `android/app/build.gradle` (if it was previously lower than 20): +The mode is currently enabled by default. You should however make sure to set the correct `minSdkVersion` in `android/app/build.gradle` if it was previously lower than 19: ```groovy android { defaultConfig { - minSdkVersion 20 + minSdkVersion 19 } } ``` +### Using Virtual displays -### Using Hybrid Composition - -1. Set the correct `minSdkVersion` in `android/app/build.gradle` (if it was previously lower than 19): +1. Set the correct `minSdkVersion` in `android/app/build.gradle` (if it was previously lower than 20): ```groovy android { defaultConfig { - minSdkVersion 19 + minSdkVersion 20 } } ``` -2. Set `WebView.platform = SurfaceAndroidWebView();` in `initState()`. +2. Set `WebView.platform = AndroidWebView();` in `initState()`. For example: - + ```dart import 'dart:io'; - + import 'package:webview_flutter/webview_flutter.dart'; class WebViewExample extends StatefulWidget { @override WebViewExampleState createState() => WebViewExampleState(); } - + class WebViewExampleState extends State { @override void initState() { super.initState(); - // Enable hybrid composition. - if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView(); + // Enable virtual display. + if (Platform.isAndroid) WebView.platform = AndroidWebView(); } @override diff --git a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart index cc74afd8a745..4c2d1fb0f4ff 100644 --- a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart @@ -860,192 +860,41 @@ void main() { }, skip: Platform.isAndroid && _skipDueToIssue86757); }); - group('SurfaceAndroidWebView', () { + // Minimial end-to-end testing of the legacy Android implementation. + group('AndroidWebView (virtual display)', () { setUpAll(() { - WebView.platform = SurfaceAndroidWebView(); + WebView.platform = AndroidWebView(); }); tearDownAll(() { WebView.platform = null; }); - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757. - testWidgets('setAndGetScrollPosition', (WidgetTester tester) async { - const String scrollTestPage = ''' - - - - - - -
- - - '''; - - final String scrollTestPageBase64 = - base64Encode(const Utf8Encoder().convert(scrollTestPage)); - - final Completer pageLoaded = Completer(); + testWidgets('initialUrl', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); - + final Completer loadCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: WebView( - initialUrl: - 'data:text/html;charset=utf-8;base64,$scrollTestPageBase64', + key: GlobalKey(), + initialUrl: primaryUrl, onWebViewCreated: (WebViewController controller) { controllerCompleter.complete(controller); }, onPageFinished: (String url) { - pageLoaded.complete(null); + loadCompleter.complete(); }, ), ), ); - - final WebViewController controller = await controllerCompleter.future; - await pageLoaded.future; - - await tester.pumpAndSettle(const Duration(seconds: 3)); - - // Check scrollTo() - const int X_SCROLL = 123; - const int Y_SCROLL = 321; - - await controller.scrollTo(X_SCROLL, Y_SCROLL); - int scrollPosX = await controller.getScrollX(); - int scrollPosY = await controller.getScrollY(); - expect(X_SCROLL, scrollPosX); - expect(Y_SCROLL, scrollPosY); - - // Check scrollBy() (on top of scrollTo()) - await controller.scrollBy(X_SCROLL, Y_SCROLL); - scrollPosX = await controller.getScrollX(); - scrollPosY = await controller.getScrollY(); - expect(X_SCROLL * 2, scrollPosX); - expect(Y_SCROLL * 2, scrollPosY); - }, skip: !Platform.isAndroid || _skipDueToIssue86757); - - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757. - testWidgets('inputs are scrolled into view when focused', - (WidgetTester tester) async { - const String scrollTestPage = ''' - - - - - - -
- - - - '''; - - final String scrollTestPageBase64 = - base64Encode(const Utf8Encoder().convert(scrollTestPage)); - - final Completer pageLoaded = Completer(); - final Completer controllerCompleter = - Completer(); - - await tester.runAsync(() async { - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: SizedBox( - width: 200, - height: 200, - child: WebView( - initialUrl: - 'data:text/html;charset=utf-8;base64,$scrollTestPageBase64', - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - onPageFinished: (String url) { - pageLoaded.complete(null); - }, - javascriptMode: JavascriptMode.unrestricted, - ), - ), - ), - ); - await Future.delayed(const Duration(milliseconds: 20)); - await tester.pump(); - }); - final WebViewController controller = await controllerCompleter.future; - await pageLoaded.future; - final String viewportRectJSON = await _runJavascriptReturningResult( - controller, 'JSON.stringify(viewport.getBoundingClientRect())'); - final Map viewportRectRelativeToViewport = - jsonDecode(viewportRectJSON) as Map; - - // Check that the input is originally outside of the viewport. - - final String initialInputClientRectJSON = - await _runJavascriptReturningResult( - controller, 'JSON.stringify(inputEl.getBoundingClientRect())'); - final Map initialInputClientRectRelativeToViewport = - jsonDecode(initialInputClientRectJSON) as Map; - - expect( - initialInputClientRectRelativeToViewport['bottom'] <= - viewportRectRelativeToViewport['bottom'], - isFalse); - - await controller.runJavascript('inputEl.focus()'); - - // Check that focusing the input brought it into view. - - final String lastInputClientRectJSON = - await _runJavascriptReturningResult( - controller, 'JSON.stringify(inputEl.getBoundingClientRect())'); - final Map lastInputClientRectRelativeToViewport = - jsonDecode(lastInputClientRectJSON) as Map; - - expect( - lastInputClientRectRelativeToViewport['top'] >= - viewportRectRelativeToViewport['top'], - isTrue); - expect( - lastInputClientRectRelativeToViewport['bottom'] <= - viewportRectRelativeToViewport['bottom'], - isTrue); - - expect( - lastInputClientRectRelativeToViewport['left'] >= - viewportRectRelativeToViewport['left'], - isTrue); - expect( - lastInputClientRectRelativeToViewport['right'] <= - viewportRectRelativeToViewport['right'], - isTrue); - }, skip: !Platform.isAndroid || _skipDueToIssue86757); - }); + await loadCompleter.future; + final String? currentUrl = await controller.currentUrl(); + expect(currentUrl, primaryUrl); + }); + }, skip: !Platform.isAndroid || _skipDueToIssue86757); group('NavigationDelegate', () { const String blankPage = ''; diff --git a/packages/webview_flutter/webview_flutter/lib/src/webview.dart b/packages/webview_flutter/webview_flutter/lib/src/webview.dart index 7b907a5f8bb2..a17702750d62 100644 --- a/packages/webview_flutter/webview_flutter/lib/src/webview.dart +++ b/packages/webview_flutter/webview_flutter/lib/src/webview.dart @@ -9,8 +9,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; -import 'package:webview_flutter_android/webview_android.dart'; import 'package:webview_flutter_android/webview_android_cookie_manager.dart'; +import 'package:webview_flutter_android/webview_surface_android.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart'; @@ -119,12 +119,12 @@ class WebView extends StatefulWidget { /// The WebView platform that's used by this WebView. /// - /// The default value is [AndroidWebView] on Android and [CupertinoWebView] on iOS. + /// The default value is [SurfaceAndroidWebView] on Android and [CupertinoWebView] on iOS. static WebViewPlatform get platform { if (_platform == null) { switch (defaultTargetPlatform) { case TargetPlatform.android: - _platform = AndroidWebView(); + _platform = SurfaceAndroidWebView(); break; case TargetPlatform.iOS: _platform = CupertinoWebView(); diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index 4ac5e1d8add3..4af446248307 100644 --- a/packages/webview_flutter/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter description: A Flutter plugin that provides a WebView widget on Android and iOS. repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.8.0 +version: 3.0.0 environment: sdk: ">=2.14.0 <3.0.0" From 3e29e9173980253ab31c210487dc6221fd6b03e2 Mon Sep 17 00:00:00 2001 From: keyonghan <54558023+keyonghan@users.noreply.github.com> Date: Tue, 7 Dec 2021 20:50:23 -0800 Subject: [PATCH 050/600] renew cirrus key (#4592) --- .cirrus.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index 64bbfdf1b9cc..95bd6092674b 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -1,4 +1,4 @@ -gcp_credentials: ENCRYPTED[!48cff44dd32e9cc412d4d381c7fe68d373ca04cf2639f8192d21cb1a9ab5e21129651423a1cf88f3fd7fe2125c1cabd9!] +gcp_credentials: ENCRYPTED[!cc769765170bebc37e0556e2da5915ca64ee37f4ec8c966ec147e2f59578b476c99e457eafce4e2f8b1a4e305f7096b8!] # Don't run on release tags since it creates O(n^2) tasks where n is the # number of plugins From 1f0d2786c2b8980756c436500bf723ee4d5d08ef Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 8 Dec 2021 15:16:00 -0500 Subject: [PATCH 051/600] [path_provider] Remove platform registration (#4540) The plugin requires Flutter 2.5, which supports auto-registration of Dart implementation code in platform packages, and the packages already do auto-registration. This registration is unused, so can be removed. --- .../path_provider/path_provider/CHANGELOG.md | 3 +- .../path_provider/lib/path_provider.dart | 29 ++----------------- .../path_provider/path_provider/pubspec.yaml | 6 ++-- 3 files changed, 8 insertions(+), 30 deletions(-) diff --git a/packages/path_provider/path_provider/CHANGELOG.md b/packages/path_provider/path_provider/CHANGELOG.md index 237815de6ce4..bc349f7b86fa 100644 --- a/packages/path_provider/path_provider/CHANGELOG.md +++ b/packages/path_provider/path_provider/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 2.0.8 * Updates example app Android compileSdkVersion to 31. +* Removes obsolete manual registration of Windows and Linux implementations. ## 2.0.7 diff --git a/packages/path_provider/path_provider/lib/path_provider.dart b/packages/path_provider/path_provider/lib/path_provider.dart index e690b7f92960..e89d29dc0036 100644 --- a/packages/path_provider/path_provider/lib/path_provider.dart +++ b/packages/path_provider/path_provider/lib/path_provider.dart @@ -2,14 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:io' show Directory, Platform; +import 'dart:io' show Directory; -import 'package:flutter/foundation.dart' show kIsWeb, visibleForTesting; -import 'package:path_provider_linux/path_provider_linux.dart'; +import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; -// ignore: implementation_imports -import 'package:path_provider_platform_interface/src/method_channel_path_provider.dart'; -import 'package:path_provider_windows/path_provider_windows.dart'; export 'package:path_provider_platform_interface/path_provider_platform_interface.dart' show StorageDirectory; @@ -18,8 +14,6 @@ export 'package:path_provider_platform_interface/path_provider_platform_interfac @Deprecated('This is no longer necessary, and is now a no-op') set disablePathProviderPlatformOverride(bool override) {} -bool _manualDartRegistrationNeeded = true; - /// An exception thrown when a directory that should always be available on /// the current platform cannot be obtained. class MissingPlatformDirectoryException implements Exception { @@ -41,24 +35,7 @@ class MissingPlatformDirectoryException implements Exception { } } -PathProviderPlatform get _platform { - // TODO(egarciad): Remove once auto registration lands on Flutter stable. - // https://github.com/flutter/flutter/issues/81421. - if (_manualDartRegistrationNeeded) { - // Only do the initial registration if it hasn't already been overridden - // with a non-default instance. - if (!kIsWeb && PathProviderPlatform.instance is MethodChannelPathProvider) { - if (Platform.isLinux) { - PathProviderPlatform.instance = PathProviderLinux(); - } else if (Platform.isWindows) { - PathProviderPlatform.instance = PathProviderWindows(); - } - } - _manualDartRegistrationNeeded = false; - } - - return PathProviderPlatform.instance; -} +PathProviderPlatform get _platform => PathProviderPlatform.instance; /// Path to the temporary directory on the device that is not backed up and is /// suitable for storing caches of downloaded files. diff --git a/packages/path_provider/path_provider/pubspec.yaml b/packages/path_provider/path_provider/pubspec.yaml index a31e804fa357..d8bca0853cc7 100644 --- a/packages/path_provider/path_provider/pubspec.yaml +++ b/packages/path_provider/path_provider/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider description: Flutter plugin for getting commonly used locations on host platform file systems, such as the temp and app data directories. repository: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.7 +version: 2.0.8 environment: sdk: ">=2.14.0 <3.0.0" @@ -27,10 +27,10 @@ dependencies: sdk: flutter path_provider_android: ^2.0.6 path_provider_ios: ^2.0.6 - path_provider_linux: ^2.0.0 + path_provider_linux: ^2.0.1 path_provider_macos: ^2.0.0 path_provider_platform_interface: ^2.0.0 - path_provider_windows: ^2.0.0 + path_provider_windows: ^2.0.2 dev_dependencies: flutter_driver: From d5e200badd422a70a074f9d8e2a03490729f1fbe Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 9 Dec 2021 08:23:03 -0500 Subject: [PATCH 052/600] [shared_preferences] Remove platform registration (#4596) The plugin requires Flutter 2.5, which supports auto-registration of Dart implementation code in platform packages, and the packages already do auto-registration. This registration is unused, so can be removed. Fixes https://github.com/flutter/flutter/issues/81421 --- .../shared_preferences/CHANGELOG.md | 5 +++- .../lib/shared_preferences.dart | 28 ++----------------- .../shared_preferences/pubspec.yaml | 6 ++-- 3 files changed, 9 insertions(+), 30 deletions(-) diff --git a/packages/shared_preferences/shared_preferences/CHANGELOG.md b/packages/shared_preferences/shared_preferences/CHANGELOG.md index 818266f92597..cc29dd98fc89 100644 --- a/packages/shared_preferences/shared_preferences/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences/CHANGELOG.md @@ -1,8 +1,11 @@ +## 2.0.10 + +* Removes obsolete manual registration of Windows and Linux implementations. + ## 2.0.9 * Fixes newly enabled analyzer options. * Updates example app Android compileSdkVersion to 31. - * Moved Android and iOS implementations to federated packages. ## 2.0.8 diff --git a/packages/shared_preferences/shared_preferences/lib/shared_preferences.dart b/packages/shared_preferences/shared_preferences/lib/shared_preferences.dart index b0a11fc8b13a..8e9f08729355 100644 --- a/packages/shared_preferences/shared_preferences/lib/shared_preferences.dart +++ b/packages/shared_preferences/shared_preferences/lib/shared_preferences.dart @@ -3,14 +3,9 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:io' show Platform; -import 'package:flutter/foundation.dart' show kIsWeb; import 'package:meta/meta.dart'; -import 'package:shared_preferences_linux/shared_preferences_linux.dart'; -import 'package:shared_preferences_platform_interface/method_channel_shared_preferences.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; -import 'package:shared_preferences_windows/shared_preferences_windows.dart'; /// Wraps NSUserDefaults (on iOS) and SharedPreferences (on Android), providing /// a persistent store for simple data. @@ -21,28 +16,9 @@ class SharedPreferences { static const String _prefix = 'flutter.'; static Completer? _completer; - static bool _manualDartRegistrationNeeded = true; - - static SharedPreferencesStorePlatform get _store { - // TODO(egarciad): Remove once auto registration lands on Flutter stable. - // https://github.com/flutter/flutter/issues/81421. - if (_manualDartRegistrationNeeded) { - // Only do the initial registration if it hasn't already been overridden - // with a non-default instance. - if (!kIsWeb && - SharedPreferencesStorePlatform.instance - is MethodChannelSharedPreferencesStore) { - if (Platform.isLinux) { - SharedPreferencesStorePlatform.instance = SharedPreferencesLinux(); - } else if (Platform.isWindows) { - SharedPreferencesStorePlatform.instance = SharedPreferencesWindows(); - } - } - _manualDartRegistrationNeeded = false; - } - return SharedPreferencesStorePlatform.instance; - } + static SharedPreferencesStorePlatform get _store => + SharedPreferencesStorePlatform.instance; /// Loads and parses the [SharedPreferences] for this app from disk. /// diff --git a/packages/shared_preferences/shared_preferences/pubspec.yaml b/packages/shared_preferences/shared_preferences/pubspec.yaml index 732a62adc5dc..156e2e230565 100644 --- a/packages/shared_preferences/shared_preferences/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for reading and writing simple key-value pairs. Wraps NSUserDefaults on iOS and SharedPreferences on Android. repository: https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.9 +version: 2.0.10 environment: sdk: ">=2.14.0 <3.0.0" @@ -31,11 +31,11 @@ dependencies: meta: ^1.3.0 shared_preferences_android: ^2.0.8 shared_preferences_ios: ^2.0.8 - shared_preferences_linux: ^2.0.0 + shared_preferences_linux: ^2.0.1 shared_preferences_macos: ^2.0.0 shared_preferences_platform_interface: ^2.0.0 shared_preferences_web: ^2.0.0 - shared_preferences_windows: ^2.0.0 + shared_preferences_windows: ^2.0.1 dev_dependencies: flutter_driver: From 2f72b8fba3814595a84bb4d01d538e9d75ef8cd2 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 9 Dec 2021 11:00:02 -0500 Subject: [PATCH 053/600] Match repo state to ignored files list (#4602) There are a number of file types that were added to the root `.gitignore` list at some point without all of the existing checked-in copies of those files being removed, which is now a `pub publish` error. This - removes all of the files that are on the .gitignore list but are checked in, and - adds generated_plugin_registrant.h to the ignore list, since all of the other generated registrant files are there; it doesn't make sense to remove the .cc files but not the .h files. --- .gitignore | 5 ++-- packages/camera/camera/camera_android.iml | 12 -------- packages/camera/camera/example/android.iml | 12 -------- .../camera/camera/example/camera_example.iml | 15 ---------- .../camera/example/camera_example_android.iml | 12 -------- .../google_maps_flutter/example/android.iml | 12 -------- .../example/google_maps_flutter_example.iml | 16 ---------- .../google_maps_flutter_example_android.iml | 26 ----------------- .../google_mobile_maps.iml | 19 ------------ .../google_mobile_maps_android.iml | 29 ------------------- .../google_sign_in/example/android.iml | 12 -------- .../google_sign_in/example/example.iml | 15 ---------- .../example/google_sign_in_example.iml | 15 ---------- .../image_picker/example/android.iml | 12 -------- .../example/image_picker_example.iml | 16 ---------- packages/local_auth/example/android.iml | 12 -------- .../local_auth/example/local_auth_example.iml | 15 ---------- .../example/local_auth_example_android.iml | 12 -------- packages/local_auth/local_auth_android.iml | 12 -------- .../flutter/generated_plugin_registrant.cc | 11 ------- .../flutter/generated_plugin_registrant.h | 15 ---------- .../Flutter/GeneratedPluginRegistrant.swift | 12 -------- .../flutter/generated_plugin_registrant.cc | 11 ------- .../flutter/generated_plugin_registrant.h | 15 ---------- .../flutter/generated_plugin_registrant.cc | 11 ------- .../flutter/generated_plugin_registrant.h | 15 ---------- .../flutter/generated_plugin_registrant.cc | 11 ------- .../flutter/generated_plugin_registrant.h | 15 ---------- .../flutter/generated_plugin_registrant.cc | 11 ------- .../flutter/generated_plugin_registrant.h | 15 ---------- .../Flutter/GeneratedPluginRegistrant.swift | 12 -------- .../flutter/generated_plugin_registrant.cc | 11 ------- .../flutter/generated_plugin_registrant.h | 15 ---------- .../flutter/generated_plugin_registrant.cc | 11 ------- .../flutter/generated_plugin_registrant.h | 15 ---------- .../Flutter/GeneratedPluginRegistrant.swift | 12 -------- .../flutter/generated_plugin_registrant.cc | 11 ------- .../flutter/generated_plugin_registrant.h | 15 ---------- .../flutter/generated_plugin_registrant.cc | 15 ---------- .../flutter/generated_plugin_registrant.h | 15 ---------- .../Flutter/GeneratedPluginRegistrant.swift | 12 -------- .../example/url_launcher_example.iml | 16 ---------- .../flutter/generated_plugin_registrant.cc | 14 --------- .../flutter/generated_plugin_registrant.h | 15 ---------- .../flutter/generated_plugin_registrant.cc | 15 ---------- .../flutter/generated_plugin_registrant.h | 15 ---------- .../Flutter/GeneratedPluginRegistrant.swift | 12 -------- .../flutter/generated_plugin_registrant.cc | 14 --------- .../flutter/generated_plugin_registrant.h | 15 ---------- .../video_player/example/android.iml | 12 -------- .../example/video_player_example.iml | 15 ---------- .../example/video_player_example_android.iml | 12 -------- .../video_player/video_player_android.iml | 12 -------- 53 files changed, 3 insertions(+), 729 deletions(-) delete mode 100644 packages/camera/camera/camera_android.iml delete mode 100644 packages/camera/camera/example/android.iml delete mode 100644 packages/camera/camera/example/camera_example.iml delete mode 100644 packages/camera/camera/example/camera_example_android.iml delete mode 100644 packages/google_maps_flutter/google_maps_flutter/example/android.iml delete mode 100644 packages/google_maps_flutter/google_maps_flutter/example/google_maps_flutter_example.iml delete mode 100644 packages/google_maps_flutter/google_maps_flutter/example/google_maps_flutter_example_android.iml delete mode 100644 packages/google_maps_flutter/google_maps_flutter/google_mobile_maps.iml delete mode 100644 packages/google_maps_flutter/google_maps_flutter/google_mobile_maps_android.iml delete mode 100644 packages/google_sign_in/google_sign_in/example/android.iml delete mode 100644 packages/google_sign_in/google_sign_in/example/example.iml delete mode 100644 packages/google_sign_in/google_sign_in/example/google_sign_in_example.iml delete mode 100755 packages/image_picker/image_picker/example/android.iml delete mode 100755 packages/image_picker/image_picker/example/image_picker_example.iml delete mode 100644 packages/local_auth/example/android.iml delete mode 100644 packages/local_auth/example/local_auth_example.iml delete mode 100644 packages/local_auth/example/local_auth_example_android.iml delete mode 100644 packages/local_auth/local_auth_android.iml delete mode 100644 packages/path_provider/path_provider/example/linux/flutter/generated_plugin_registrant.cc delete mode 100644 packages/path_provider/path_provider/example/linux/flutter/generated_plugin_registrant.h delete mode 100644 packages/path_provider/path_provider/example/macos/Flutter/GeneratedPluginRegistrant.swift delete mode 100644 packages/path_provider/path_provider/example/windows/flutter/generated_plugin_registrant.cc delete mode 100644 packages/path_provider/path_provider/example/windows/flutter/generated_plugin_registrant.h delete mode 100644 packages/path_provider/path_provider_linux/example/linux/flutter/generated_plugin_registrant.cc delete mode 100644 packages/path_provider/path_provider_linux/example/linux/flutter/generated_plugin_registrant.h delete mode 100644 packages/path_provider/path_provider_windows/example/windows/flutter/generated_plugin_registrant.cc delete mode 100644 packages/path_provider/path_provider_windows/example/windows/flutter/generated_plugin_registrant.h delete mode 100644 packages/shared_preferences/shared_preferences/example/linux/flutter/generated_plugin_registrant.cc delete mode 100644 packages/shared_preferences/shared_preferences/example/linux/flutter/generated_plugin_registrant.h delete mode 100644 packages/shared_preferences/shared_preferences/example/macos/Flutter/GeneratedPluginRegistrant.swift delete mode 100644 packages/shared_preferences/shared_preferences/example/windows/flutter/generated_plugin_registrant.cc delete mode 100644 packages/shared_preferences/shared_preferences/example/windows/flutter/generated_plugin_registrant.h delete mode 100644 packages/shared_preferences/shared_preferences_linux/example/linux/flutter/generated_plugin_registrant.cc delete mode 100644 packages/shared_preferences/shared_preferences_linux/example/linux/flutter/generated_plugin_registrant.h delete mode 100644 packages/shared_preferences/shared_preferences_macos/example/macos/Flutter/GeneratedPluginRegistrant.swift delete mode 100644 packages/shared_preferences/shared_preferences_windows/example/windows/flutter/generated_plugin_registrant.cc delete mode 100644 packages/shared_preferences/shared_preferences_windows/example/windows/flutter/generated_plugin_registrant.h delete mode 100644 packages/url_launcher/url_launcher/example/linux/flutter/generated_plugin_registrant.cc delete mode 100644 packages/url_launcher/url_launcher/example/linux/flutter/generated_plugin_registrant.h delete mode 100644 packages/url_launcher/url_launcher/example/macos/Flutter/GeneratedPluginRegistrant.swift delete mode 100644 packages/url_launcher/url_launcher/example/url_launcher_example.iml delete mode 100644 packages/url_launcher/url_launcher/example/windows/flutter/generated_plugin_registrant.cc delete mode 100644 packages/url_launcher/url_launcher/example/windows/flutter/generated_plugin_registrant.h delete mode 100644 packages/url_launcher/url_launcher_linux/example/linux/flutter/generated_plugin_registrant.cc delete mode 100644 packages/url_launcher/url_launcher_linux/example/linux/flutter/generated_plugin_registrant.h delete mode 100644 packages/url_launcher/url_launcher_macos/example/macos/Flutter/GeneratedPluginRegistrant.swift delete mode 100644 packages/url_launcher/url_launcher_windows/example/windows/flutter/generated_plugin_registrant.cc delete mode 100644 packages/url_launcher/url_launcher_windows/example/windows/flutter/generated_plugin_registrant.h delete mode 100644 packages/video_player/video_player/example/android.iml delete mode 100644 packages/video_player/video_player/example/video_player_example.iml delete mode 100644 packages/video_player/video_player/example/video_player_example_android.iml delete mode 100644 packages/video_player/video_player/video_player_android.iml diff --git a/.gitignore b/.gitignore index f4fa0b9b7795..8eaeaff8de55 100644 --- a/.gitignore +++ b/.gitignore @@ -34,11 +34,12 @@ gradle-wrapper.jar .flutter-plugins-dependencies *.iml +generated_plugin_registrant.cc +generated_plugin_registrant.h generated_plugin_registrant.dart +GeneratedPluginRegistrant.java GeneratedPluginRegistrant.h GeneratedPluginRegistrant.m -generated_plugin_registrant.cc -GeneratedPluginRegistrant.java GeneratedPluginRegistrant.swift build/ .flutter-plugins diff --git a/packages/camera/camera/camera_android.iml b/packages/camera/camera/camera_android.iml deleted file mode 100644 index 462b903e05b6..000000000000 --- a/packages/camera/camera/camera_android.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/packages/camera/camera/example/android.iml b/packages/camera/camera/example/android.iml deleted file mode 100644 index 462b903e05b6..000000000000 --- a/packages/camera/camera/example/android.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/packages/camera/camera/example/camera_example.iml b/packages/camera/camera/example/camera_example.iml deleted file mode 100644 index dafb001137cd..000000000000 --- a/packages/camera/camera/example/camera_example.iml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/camera/camera/example/camera_example_android.iml b/packages/camera/camera/example/camera_example_android.iml deleted file mode 100644 index 462b903e05b6..000000000000 --- a/packages/camera/camera/example/camera_example_android.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/packages/google_maps_flutter/google_maps_flutter/example/android.iml b/packages/google_maps_flutter/google_maps_flutter/example/android.iml deleted file mode 100644 index 462b903e05b6..000000000000 --- a/packages/google_maps_flutter/google_maps_flutter/example/android.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/packages/google_maps_flutter/google_maps_flutter/example/google_maps_flutter_example.iml b/packages/google_maps_flutter/google_maps_flutter/example/google_maps_flutter_example.iml deleted file mode 100644 index 8070e6469054..000000000000 --- a/packages/google_maps_flutter/google_maps_flutter/example/google_maps_flutter_example.iml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/google_maps_flutter/google_maps_flutter/example/google_maps_flutter_example_android.iml b/packages/google_maps_flutter/google_maps_flutter/example/google_maps_flutter_example_android.iml deleted file mode 100644 index 0ca70ed93eaf..000000000000 --- a/packages/google_maps_flutter/google_maps_flutter/example/google_maps_flutter_example_android.iml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/packages/google_maps_flutter/google_maps_flutter/google_mobile_maps.iml b/packages/google_maps_flutter/google_maps_flutter/google_mobile_maps.iml deleted file mode 100644 index 0fbaf2c3a822..000000000000 --- a/packages/google_maps_flutter/google_maps_flutter/google_mobile_maps.iml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/google_maps_flutter/google_maps_flutter/google_mobile_maps_android.iml b/packages/google_maps_flutter/google_maps_flutter/google_mobile_maps_android.iml deleted file mode 100644 index 0ebb6c9fe763..000000000000 --- a/packages/google_maps_flutter/google_maps_flutter/google_mobile_maps_android.iml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/google_sign_in/google_sign_in/example/android.iml b/packages/google_sign_in/google_sign_in/example/android.iml deleted file mode 100644 index 462b903e05b6..000000000000 --- a/packages/google_sign_in/google_sign_in/example/android.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/packages/google_sign_in/google_sign_in/example/example.iml b/packages/google_sign_in/google_sign_in/example/example.iml deleted file mode 100644 index c4447024fe3c..000000000000 --- a/packages/google_sign_in/google_sign_in/example/example.iml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/google_sign_in/google_sign_in/example/google_sign_in_example.iml b/packages/google_sign_in/google_sign_in/example/google_sign_in_example.iml deleted file mode 100644 index 9d5dae19540c..000000000000 --- a/packages/google_sign_in/google_sign_in/example/google_sign_in_example.iml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/image_picker/image_picker/example/android.iml b/packages/image_picker/image_picker/example/android.iml deleted file mode 100755 index 462b903e05b6..000000000000 --- a/packages/image_picker/image_picker/example/android.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/packages/image_picker/image_picker/example/image_picker_example.iml b/packages/image_picker/image_picker/example/image_picker_example.iml deleted file mode 100755 index 1ae40a0f7f54..000000000000 --- a/packages/image_picker/image_picker/example/image_picker_example.iml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/local_auth/example/android.iml b/packages/local_auth/example/android.iml deleted file mode 100644 index 462b903e05b6..000000000000 --- a/packages/local_auth/example/android.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/packages/local_auth/example/local_auth_example.iml b/packages/local_auth/example/local_auth_example.iml deleted file mode 100644 index 9d5dae19540c..000000000000 --- a/packages/local_auth/example/local_auth_example.iml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/local_auth/example/local_auth_example_android.iml b/packages/local_auth/example/local_auth_example_android.iml deleted file mode 100644 index 462b903e05b6..000000000000 --- a/packages/local_auth/example/local_auth_example_android.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/packages/local_auth/local_auth_android.iml b/packages/local_auth/local_auth_android.iml deleted file mode 100644 index 462b903e05b6..000000000000 --- a/packages/local_auth/local_auth_android.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/packages/path_provider/path_provider/example/linux/flutter/generated_plugin_registrant.cc b/packages/path_provider/path_provider/example/linux/flutter/generated_plugin_registrant.cc deleted file mode 100644 index e71a16d23d05..000000000000 --- a/packages/path_provider/path_provider/example/linux/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,11 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - - -void fl_register_plugins(FlPluginRegistry* registry) { -} diff --git a/packages/path_provider/path_provider/example/linux/flutter/generated_plugin_registrant.h b/packages/path_provider/path_provider/example/linux/flutter/generated_plugin_registrant.h deleted file mode 100644 index e0f0a47bc08f..000000000000 --- a/packages/path_provider/path_provider/example/linux/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void fl_register_plugins(FlPluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/packages/path_provider/path_provider/example/macos/Flutter/GeneratedPluginRegistrant.swift b/packages/path_provider/path_provider/example/macos/Flutter/GeneratedPluginRegistrant.swift deleted file mode 100644 index 0d56f519c923..000000000000 --- a/packages/path_provider/path_provider/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// Generated file. Do not edit. -// - -import FlutterMacOS -import Foundation - -import path_provider_macos - -func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) -} diff --git a/packages/path_provider/path_provider/example/windows/flutter/generated_plugin_registrant.cc b/packages/path_provider/path_provider/example/windows/flutter/generated_plugin_registrant.cc deleted file mode 100644 index 8b6d4680af38..000000000000 --- a/packages/path_provider/path_provider/example/windows/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,11 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - - -void RegisterPlugins(flutter::PluginRegistry* registry) { -} diff --git a/packages/path_provider/path_provider/example/windows/flutter/generated_plugin_registrant.h b/packages/path_provider/path_provider/example/windows/flutter/generated_plugin_registrant.h deleted file mode 100644 index dc139d85a931..000000000000 --- a/packages/path_provider/path_provider/example/windows/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void RegisterPlugins(flutter::PluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/packages/path_provider/path_provider_linux/example/linux/flutter/generated_plugin_registrant.cc b/packages/path_provider/path_provider_linux/example/linux/flutter/generated_plugin_registrant.cc deleted file mode 100644 index e71a16d23d05..000000000000 --- a/packages/path_provider/path_provider_linux/example/linux/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,11 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - - -void fl_register_plugins(FlPluginRegistry* registry) { -} diff --git a/packages/path_provider/path_provider_linux/example/linux/flutter/generated_plugin_registrant.h b/packages/path_provider/path_provider_linux/example/linux/flutter/generated_plugin_registrant.h deleted file mode 100644 index e0f0a47bc08f..000000000000 --- a/packages/path_provider/path_provider_linux/example/linux/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void fl_register_plugins(FlPluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/packages/path_provider/path_provider_windows/example/windows/flutter/generated_plugin_registrant.cc b/packages/path_provider/path_provider_windows/example/windows/flutter/generated_plugin_registrant.cc deleted file mode 100644 index 8b6d4680af38..000000000000 --- a/packages/path_provider/path_provider_windows/example/windows/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,11 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - - -void RegisterPlugins(flutter::PluginRegistry* registry) { -} diff --git a/packages/path_provider/path_provider_windows/example/windows/flutter/generated_plugin_registrant.h b/packages/path_provider/path_provider_windows/example/windows/flutter/generated_plugin_registrant.h deleted file mode 100644 index dc139d85a931..000000000000 --- a/packages/path_provider/path_provider_windows/example/windows/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void RegisterPlugins(flutter::PluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/packages/shared_preferences/shared_preferences/example/linux/flutter/generated_plugin_registrant.cc b/packages/shared_preferences/shared_preferences/example/linux/flutter/generated_plugin_registrant.cc deleted file mode 100644 index e71a16d23d05..000000000000 --- a/packages/shared_preferences/shared_preferences/example/linux/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,11 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - - -void fl_register_plugins(FlPluginRegistry* registry) { -} diff --git a/packages/shared_preferences/shared_preferences/example/linux/flutter/generated_plugin_registrant.h b/packages/shared_preferences/shared_preferences/example/linux/flutter/generated_plugin_registrant.h deleted file mode 100644 index e0f0a47bc08f..000000000000 --- a/packages/shared_preferences/shared_preferences/example/linux/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void fl_register_plugins(FlPluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/packages/shared_preferences/shared_preferences/example/macos/Flutter/GeneratedPluginRegistrant.swift b/packages/shared_preferences/shared_preferences/example/macos/Flutter/GeneratedPluginRegistrant.swift deleted file mode 100644 index 287b6a9d2769..000000000000 --- a/packages/shared_preferences/shared_preferences/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// Generated file. Do not edit. -// - -import FlutterMacOS -import Foundation - -import shared_preferences_macos - -func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) -} diff --git a/packages/shared_preferences/shared_preferences/example/windows/flutter/generated_plugin_registrant.cc b/packages/shared_preferences/shared_preferences/example/windows/flutter/generated_plugin_registrant.cc deleted file mode 100644 index 8b6d4680af38..000000000000 --- a/packages/shared_preferences/shared_preferences/example/windows/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,11 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - - -void RegisterPlugins(flutter::PluginRegistry* registry) { -} diff --git a/packages/shared_preferences/shared_preferences/example/windows/flutter/generated_plugin_registrant.h b/packages/shared_preferences/shared_preferences/example/windows/flutter/generated_plugin_registrant.h deleted file mode 100644 index dc139d85a931..000000000000 --- a/packages/shared_preferences/shared_preferences/example/windows/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void RegisterPlugins(flutter::PluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/packages/shared_preferences/shared_preferences_linux/example/linux/flutter/generated_plugin_registrant.cc b/packages/shared_preferences/shared_preferences_linux/example/linux/flutter/generated_plugin_registrant.cc deleted file mode 100644 index e71a16d23d05..000000000000 --- a/packages/shared_preferences/shared_preferences_linux/example/linux/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,11 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - - -void fl_register_plugins(FlPluginRegistry* registry) { -} diff --git a/packages/shared_preferences/shared_preferences_linux/example/linux/flutter/generated_plugin_registrant.h b/packages/shared_preferences/shared_preferences_linux/example/linux/flutter/generated_plugin_registrant.h deleted file mode 100644 index e0f0a47bc08f..000000000000 --- a/packages/shared_preferences/shared_preferences_linux/example/linux/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void fl_register_plugins(FlPluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/packages/shared_preferences/shared_preferences_macos/example/macos/Flutter/GeneratedPluginRegistrant.swift b/packages/shared_preferences/shared_preferences_macos/example/macos/Flutter/GeneratedPluginRegistrant.swift deleted file mode 100644 index 287b6a9d2769..000000000000 --- a/packages/shared_preferences/shared_preferences_macos/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// Generated file. Do not edit. -// - -import FlutterMacOS -import Foundation - -import shared_preferences_macos - -func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) -} diff --git a/packages/shared_preferences/shared_preferences_windows/example/windows/flutter/generated_plugin_registrant.cc b/packages/shared_preferences/shared_preferences_windows/example/windows/flutter/generated_plugin_registrant.cc deleted file mode 100644 index 8b6d4680af38..000000000000 --- a/packages/shared_preferences/shared_preferences_windows/example/windows/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,11 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - - -void RegisterPlugins(flutter::PluginRegistry* registry) { -} diff --git a/packages/shared_preferences/shared_preferences_windows/example/windows/flutter/generated_plugin_registrant.h b/packages/shared_preferences/shared_preferences_windows/example/windows/flutter/generated_plugin_registrant.h deleted file mode 100644 index dc139d85a931..000000000000 --- a/packages/shared_preferences/shared_preferences_windows/example/windows/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void RegisterPlugins(flutter::PluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/packages/url_launcher/url_launcher/example/linux/flutter/generated_plugin_registrant.cc b/packages/url_launcher/url_launcher/example/linux/flutter/generated_plugin_registrant.cc deleted file mode 100644 index f6f23bfe970f..000000000000 --- a/packages/url_launcher/url_launcher/example/linux/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - -#include - -void fl_register_plugins(FlPluginRegistry* registry) { - g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); - url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); -} diff --git a/packages/url_launcher/url_launcher/example/linux/flutter/generated_plugin_registrant.h b/packages/url_launcher/url_launcher/example/linux/flutter/generated_plugin_registrant.h deleted file mode 100644 index e0f0a47bc08f..000000000000 --- a/packages/url_launcher/url_launcher/example/linux/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void fl_register_plugins(FlPluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/packages/url_launcher/url_launcher/example/macos/Flutter/GeneratedPluginRegistrant.swift b/packages/url_launcher/url_launcher/example/macos/Flutter/GeneratedPluginRegistrant.swift deleted file mode 100644 index 8236f5728c63..000000000000 --- a/packages/url_launcher/url_launcher/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// Generated file. Do not edit. -// - -import FlutterMacOS -import Foundation - -import url_launcher_macos - -func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) -} diff --git a/packages/url_launcher/url_launcher/example/url_launcher_example.iml b/packages/url_launcher/url_launcher/example/url_launcher_example.iml deleted file mode 100644 index f2b096d12510..000000000000 --- a/packages/url_launcher/url_launcher/example/url_launcher_example.iml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/url_launcher/url_launcher/example/windows/flutter/generated_plugin_registrant.cc b/packages/url_launcher/url_launcher/example/windows/flutter/generated_plugin_registrant.cc deleted file mode 100644 index 4f7884874da7..000000000000 --- a/packages/url_launcher/url_launcher/example/windows/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,14 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - -#include - -void RegisterPlugins(flutter::PluginRegistry* registry) { - UrlLauncherWindowsRegisterWithRegistrar( - registry->GetRegistrarForPlugin("UrlLauncherWindows")); -} diff --git a/packages/url_launcher/url_launcher/example/windows/flutter/generated_plugin_registrant.h b/packages/url_launcher/url_launcher/example/windows/flutter/generated_plugin_registrant.h deleted file mode 100644 index dc139d85a931..000000000000 --- a/packages/url_launcher/url_launcher/example/windows/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void RegisterPlugins(flutter::PluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/packages/url_launcher/url_launcher_linux/example/linux/flutter/generated_plugin_registrant.cc b/packages/url_launcher/url_launcher_linux/example/linux/flutter/generated_plugin_registrant.cc deleted file mode 100644 index f6f23bfe970f..000000000000 --- a/packages/url_launcher/url_launcher_linux/example/linux/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - -#include - -void fl_register_plugins(FlPluginRegistry* registry) { - g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); - url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); -} diff --git a/packages/url_launcher/url_launcher_linux/example/linux/flutter/generated_plugin_registrant.h b/packages/url_launcher/url_launcher_linux/example/linux/flutter/generated_plugin_registrant.h deleted file mode 100644 index e0f0a47bc08f..000000000000 --- a/packages/url_launcher/url_launcher_linux/example/linux/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void fl_register_plugins(FlPluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/packages/url_launcher/url_launcher_macos/example/macos/Flutter/GeneratedPluginRegistrant.swift b/packages/url_launcher/url_launcher_macos/example/macos/Flutter/GeneratedPluginRegistrant.swift deleted file mode 100644 index 8236f5728c63..000000000000 --- a/packages/url_launcher/url_launcher_macos/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// Generated file. Do not edit. -// - -import FlutterMacOS -import Foundation - -import url_launcher_macos - -func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) -} diff --git a/packages/url_launcher/url_launcher_windows/example/windows/flutter/generated_plugin_registrant.cc b/packages/url_launcher/url_launcher_windows/example/windows/flutter/generated_plugin_registrant.cc deleted file mode 100644 index 4f7884874da7..000000000000 --- a/packages/url_launcher/url_launcher_windows/example/windows/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,14 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - -#include - -void RegisterPlugins(flutter::PluginRegistry* registry) { - UrlLauncherWindowsRegisterWithRegistrar( - registry->GetRegistrarForPlugin("UrlLauncherWindows")); -} diff --git a/packages/url_launcher/url_launcher_windows/example/windows/flutter/generated_plugin_registrant.h b/packages/url_launcher/url_launcher_windows/example/windows/flutter/generated_plugin_registrant.h deleted file mode 100644 index dc139d85a931..000000000000 --- a/packages/url_launcher/url_launcher_windows/example/windows/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void RegisterPlugins(flutter::PluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/packages/video_player/video_player/example/android.iml b/packages/video_player/video_player/example/android.iml deleted file mode 100644 index 462b903e05b6..000000000000 --- a/packages/video_player/video_player/example/android.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/packages/video_player/video_player/example/video_player_example.iml b/packages/video_player/video_player/example/video_player_example.iml deleted file mode 100644 index dafb001137cd..000000000000 --- a/packages/video_player/video_player/example/video_player_example.iml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/video_player/video_player/example/video_player_example_android.iml b/packages/video_player/video_player/example/video_player_example_android.iml deleted file mode 100644 index 462b903e05b6..000000000000 --- a/packages/video_player/video_player/example/video_player_example_android.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/packages/video_player/video_player/video_player_android.iml b/packages/video_player/video_player/video_player_android.iml deleted file mode 100644 index 462b903e05b6..000000000000 --- a/packages/video_player/video_player/video_player_android.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - From 33968c0e9e223ca64dbd6e07dc4addf81cb7269f Mon Sep 17 00:00:00 2001 From: BeMacized Date: Thu, 9 Dec 2021 20:44:22 +0100 Subject: [PATCH 054/600] [webview_flutter] Change import of FLTCookieManager.h to relative import (#4600) --- .../webview_flutter/webview_flutter_wkwebview/CHANGELOG.md | 4 ++++ .../webview_flutter_wkwebview/ios/Classes/FlutterWebView.h | 3 ++- .../webview_flutter/webview_flutter_wkwebview/pubspec.yaml | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index 0aaf4bcf57aa..db8338df5565 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.7.1 + +* Fixes header import for cookie manager to be relative only. + ## 2.7.0 * Adds implementation of the `loadFlutterAsset` method from the platform interface. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.h index 145a772c5015..0dae94290a0d 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.h @@ -2,10 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import #import #import +#import "FLTCookieManager.h" + NS_ASSUME_NONNULL_BEGIN /** diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 08e98b11d4bf..d39f42b0ecdf 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_wkwebview description: A Flutter plugin that provides a WebView widget based on Apple's WKWebView control. repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter_wkwebview issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.7.0 +version: 2.7.1 environment: sdk: ">=2.14.0 <3.0.0" From 64c222d368bab925fe855c201a6b5f49f82bb7eb Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 9 Dec 2021 17:02:24 -0500 Subject: [PATCH 055/600] Move custom analysis options into sub-packages (#4603) When the legacy analysis options were bulk-added to the repository to allow transitioning incrementally to the Flutter analysis options, they were added to top-level plugin groups. This makes it harder to migrate incrementally, and also means that when we add a new sub-package (e.g, a web or desktop implementation) it's automatically using the legacy options. This moves all the legacy option files into each individual package, and updates the allow list for the tool accordingly, so that migration can be done package by package. --- packages/camera/analysis_options.yaml | 1 - packages/camera/camera/analysis_options.yaml | 1 + .../analysis_options.yaml | 1 + .../camera/camera_web/analysis_options.yaml | 1 + packages/camera/camera_web/pubspec.yaml | 2 +- .../google_maps_flutter/analysis_options.yaml | 1 - .../google_maps_flutter/analysis_options.yaml | 1 + .../analysis_options.yaml | 1 + .../analysis_options.yaml | 1 + .../google_maps_flutter_web/pubspec.yaml | 2 +- packages/google_sign_in/analysis_options.yaml | 1 - .../google_sign_in/analysis_options.yaml | 1 + .../analysis_options.yaml | 1 + .../google_sign_in_web/analysis_options.yaml | 1 + .../google_sign_in_web/pubspec.yaml | 2 +- packages/image_picker/analysis_options.yaml | 1 - .../image_picker/analysis_options.yaml | 1 + .../analysis_options.yaml | 1 + .../image_picker_for_web/pubspec.yaml | 2 +- .../analysis_options.yaml | 1 + packages/video_player/analysis_options.yaml | 1 - .../video_player/analysis_options.yaml | 1 + .../analysis_options.yaml | 1 + .../video_player_web/analysis_options.yaml | 1 + .../video_player_web/pubspec.yaml | 2 +- script/configs/custom_analysis.yaml | 28 ++++++++++++------- 26 files changed, 38 insertions(+), 20 deletions(-) delete mode 100644 packages/camera/analysis_options.yaml create mode 100644 packages/camera/camera/analysis_options.yaml create mode 100644 packages/camera/camera_platform_interface/analysis_options.yaml create mode 100644 packages/camera/camera_web/analysis_options.yaml delete mode 100644 packages/google_maps_flutter/analysis_options.yaml create mode 100644 packages/google_maps_flutter/google_maps_flutter/analysis_options.yaml create mode 100644 packages/google_maps_flutter/google_maps_flutter_platform_interface/analysis_options.yaml create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/analysis_options.yaml delete mode 100644 packages/google_sign_in/analysis_options.yaml create mode 100644 packages/google_sign_in/google_sign_in/analysis_options.yaml create mode 100644 packages/google_sign_in/google_sign_in_platform_interface/analysis_options.yaml create mode 100644 packages/google_sign_in/google_sign_in_web/analysis_options.yaml delete mode 100644 packages/image_picker/analysis_options.yaml create mode 100644 packages/image_picker/image_picker/analysis_options.yaml create mode 100644 packages/image_picker/image_picker_for_web/analysis_options.yaml create mode 100644 packages/image_picker/image_picker_platform_interface/analysis_options.yaml delete mode 100644 packages/video_player/analysis_options.yaml create mode 100644 packages/video_player/video_player/analysis_options.yaml create mode 100644 packages/video_player/video_player_platform_interface/analysis_options.yaml create mode 100644 packages/video_player/video_player_web/analysis_options.yaml diff --git a/packages/camera/analysis_options.yaml b/packages/camera/analysis_options.yaml deleted file mode 100644 index cda4f6e153e6..000000000000 --- a/packages/camera/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../analysis_options_legacy.yaml diff --git a/packages/camera/camera/analysis_options.yaml b/packages/camera/camera/analysis_options.yaml new file mode 100644 index 000000000000..5aeb4e7c5e21 --- /dev/null +++ b/packages/camera/camera/analysis_options.yaml @@ -0,0 +1 @@ +include: ../../../analysis_options_legacy.yaml diff --git a/packages/camera/camera_platform_interface/analysis_options.yaml b/packages/camera/camera_platform_interface/analysis_options.yaml new file mode 100644 index 000000000000..5aeb4e7c5e21 --- /dev/null +++ b/packages/camera/camera_platform_interface/analysis_options.yaml @@ -0,0 +1 @@ +include: ../../../analysis_options_legacy.yaml diff --git a/packages/camera/camera_web/analysis_options.yaml b/packages/camera/camera_web/analysis_options.yaml new file mode 100644 index 000000000000..5aeb4e7c5e21 --- /dev/null +++ b/packages/camera/camera_web/analysis_options.yaml @@ -0,0 +1 @@ +include: ../../../analysis_options_legacy.yaml diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index f37500ad6e22..ce09f7ed9d64 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -22,9 +22,9 @@ dependencies: sdk: flutter flutter_web_plugins: sdk: flutter + pedantic: ^1.11.1 stream_transform: ^2.0.0 dev_dependencies: flutter_test: sdk: flutter - pedantic: ^1.11.1 diff --git a/packages/google_maps_flutter/analysis_options.yaml b/packages/google_maps_flutter/analysis_options.yaml deleted file mode 100644 index cda4f6e153e6..000000000000 --- a/packages/google_maps_flutter/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../analysis_options_legacy.yaml diff --git a/packages/google_maps_flutter/google_maps_flutter/analysis_options.yaml b/packages/google_maps_flutter/google_maps_flutter/analysis_options.yaml new file mode 100644 index 000000000000..5aeb4e7c5e21 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter/analysis_options.yaml @@ -0,0 +1 @@ +include: ../../../analysis_options_legacy.yaml diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/analysis_options.yaml b/packages/google_maps_flutter/google_maps_flutter_platform_interface/analysis_options.yaml new file mode 100644 index 000000000000..5aeb4e7c5e21 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/analysis_options.yaml @@ -0,0 +1 @@ +include: ../../../analysis_options_legacy.yaml diff --git a/packages/google_maps_flutter/google_maps_flutter_web/analysis_options.yaml b/packages/google_maps_flutter/google_maps_flutter_web/analysis_options.yaml new file mode 100644 index 000000000000..5aeb4e7c5e21 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/analysis_options.yaml @@ -0,0 +1 @@ +include: ../../../analysis_options_legacy.yaml diff --git a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml index 1f5fe4d96ccc..080726ee6d85 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -24,13 +24,13 @@ dependencies: google_maps_flutter_platform_interface: ^2.1.2 google_maps: ^5.2.0 meta: ^1.3.0 + pedantic: ^1.10.0 sanitize_html: ^2.0.0 stream_transform: ^2.0.0 dev_dependencies: flutter_test: sdk: flutter - pedantic: ^1.10.0 # The example deliberately includes limited-use secrets. false_secrets: diff --git a/packages/google_sign_in/analysis_options.yaml b/packages/google_sign_in/analysis_options.yaml deleted file mode 100644 index cda4f6e153e6..000000000000 --- a/packages/google_sign_in/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../analysis_options_legacy.yaml diff --git a/packages/google_sign_in/google_sign_in/analysis_options.yaml b/packages/google_sign_in/google_sign_in/analysis_options.yaml new file mode 100644 index 000000000000..5aeb4e7c5e21 --- /dev/null +++ b/packages/google_sign_in/google_sign_in/analysis_options.yaml @@ -0,0 +1 @@ +include: ../../../analysis_options_legacy.yaml diff --git a/packages/google_sign_in/google_sign_in_platform_interface/analysis_options.yaml b/packages/google_sign_in/google_sign_in_platform_interface/analysis_options.yaml new file mode 100644 index 000000000000..5aeb4e7c5e21 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_platform_interface/analysis_options.yaml @@ -0,0 +1 @@ +include: ../../../analysis_options_legacy.yaml diff --git a/packages/google_sign_in/google_sign_in_web/analysis_options.yaml b/packages/google_sign_in/google_sign_in_web/analysis_options.yaml new file mode 100644 index 000000000000..5aeb4e7c5e21 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_web/analysis_options.yaml @@ -0,0 +1 @@ +include: ../../../analysis_options_legacy.yaml diff --git a/packages/google_sign_in/google_sign_in_web/pubspec.yaml b/packages/google_sign_in/google_sign_in_web/pubspec.yaml index 723dbe9ce56f..7388ea138883 100644 --- a/packages/google_sign_in/google_sign_in_web/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_web/pubspec.yaml @@ -25,8 +25,8 @@ dependencies: google_sign_in_platform_interface: ^2.0.0 js: ^0.6.3 meta: ^1.3.0 + pedantic: ^1.10.0 dev_dependencies: flutter_test: sdk: flutter - pedantic: ^1.10.0 diff --git a/packages/image_picker/analysis_options.yaml b/packages/image_picker/analysis_options.yaml deleted file mode 100644 index cda4f6e153e6..000000000000 --- a/packages/image_picker/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../analysis_options_legacy.yaml diff --git a/packages/image_picker/image_picker/analysis_options.yaml b/packages/image_picker/image_picker/analysis_options.yaml new file mode 100644 index 000000000000..5aeb4e7c5e21 --- /dev/null +++ b/packages/image_picker/image_picker/analysis_options.yaml @@ -0,0 +1 @@ +include: ../../../analysis_options_legacy.yaml diff --git a/packages/image_picker/image_picker_for_web/analysis_options.yaml b/packages/image_picker/image_picker_for_web/analysis_options.yaml new file mode 100644 index 000000000000..5aeb4e7c5e21 --- /dev/null +++ b/packages/image_picker/image_picker_for_web/analysis_options.yaml @@ -0,0 +1 @@ +include: ../../../analysis_options_legacy.yaml diff --git a/packages/image_picker/image_picker_for_web/pubspec.yaml b/packages/image_picker/image_picker_for_web/pubspec.yaml index d0d97011c9ec..a104dce66fe9 100644 --- a/packages/image_picker/image_picker_for_web/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/pubspec.yaml @@ -23,8 +23,8 @@ dependencies: sdk: flutter image_picker_platform_interface: ^2.2.0 meta: ^1.3.0 + pedantic: ^1.10.0 dev_dependencies: flutter_test: sdk: flutter - pedantic: ^1.10.0 diff --git a/packages/image_picker/image_picker_platform_interface/analysis_options.yaml b/packages/image_picker/image_picker_platform_interface/analysis_options.yaml new file mode 100644 index 000000000000..5aeb4e7c5e21 --- /dev/null +++ b/packages/image_picker/image_picker_platform_interface/analysis_options.yaml @@ -0,0 +1 @@ +include: ../../../analysis_options_legacy.yaml diff --git a/packages/video_player/analysis_options.yaml b/packages/video_player/analysis_options.yaml deleted file mode 100644 index cda4f6e153e6..000000000000 --- a/packages/video_player/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../analysis_options_legacy.yaml diff --git a/packages/video_player/video_player/analysis_options.yaml b/packages/video_player/video_player/analysis_options.yaml new file mode 100644 index 000000000000..5aeb4e7c5e21 --- /dev/null +++ b/packages/video_player/video_player/analysis_options.yaml @@ -0,0 +1 @@ +include: ../../../analysis_options_legacy.yaml diff --git a/packages/video_player/video_player_platform_interface/analysis_options.yaml b/packages/video_player/video_player_platform_interface/analysis_options.yaml new file mode 100644 index 000000000000..5aeb4e7c5e21 --- /dev/null +++ b/packages/video_player/video_player_platform_interface/analysis_options.yaml @@ -0,0 +1 @@ +include: ../../../analysis_options_legacy.yaml diff --git a/packages/video_player/video_player_web/analysis_options.yaml b/packages/video_player/video_player_web/analysis_options.yaml new file mode 100644 index 000000000000..5aeb4e7c5e21 --- /dev/null +++ b/packages/video_player/video_player_web/analysis_options.yaml @@ -0,0 +1 @@ +include: ../../../analysis_options_legacy.yaml diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml index b401673c628d..ac0754b1a5d0 100644 --- a/packages/video_player/video_player_web/pubspec.yaml +++ b/packages/video_player/video_player_web/pubspec.yaml @@ -22,9 +22,9 @@ dependencies: flutter_web_plugins: sdk: flutter meta: ^1.3.0 + pedantic: ^1.10.0 video_player_platform_interface: ^4.2.0 dev_dependencies: flutter_test: sdk: flutter - pedantic: ^1.10.0 diff --git a/script/configs/custom_analysis.yaml b/script/configs/custom_analysis.yaml index 81bb0f5db59c..e991e5e1446c 100644 --- a/script/configs/custom_analysis.yaml +++ b/script/configs/custom_analysis.yaml @@ -4,10 +4,6 @@ # analysis_options.yaml based on flutter/flutter, rather than the original # rules based on pedantic (now at analysis_options_legacy.yaml). # -# DO NOT add new entries to the list, unless it is to push the legacy rules -# from a top-level package into more specific packages in order to incrementally -# migrate a federated plugin. -# # DO NOT move or delete this file without updating # https://github.com/dart-lang/sdk/blob/master/tools/bots/flutter/analyze_flutter_plugins.sh # which references this file from source, but out-of-repo. @@ -15,11 +11,23 @@ # TODO(ecosystem): Remove everything from this list. See: # https://github.com/flutter/flutter/issues/76229 -- camera -- google_maps_flutter -- google_sign_in -- image_picker -- in_app_purchase +- camera/camera +- camera/camera_platform_interface +- camera/camera_web +- google_maps_flutter/google_maps_flutter +- google_maps_flutter/google_maps_flutter_platform_interface +- google_maps_flutter/google_maps_flutter_web +- google_sign_in/google_sign_in +- google_sign_in/google_sign_in_platform_interface +- google_sign_in/google_sign_in_web +- image_picker/image_picker +- image_picker/image_picker_for_web +- image_picker/image_picker_platform_interface +- in_app_purchase/in_app_purchase +- in_app_purchase/in_app_purchase_android +- in_app_purchase/in_app_purchase_storekit - ios_platform_images - local_auth -- video_player +- video_player/video_player +- video_player/video_player_platform_interface +- video_player/video_player_web From 537cc437c0bf4d502654113bba7d941a9266f4a8 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Fri, 10 Dec 2021 11:03:04 +1300 Subject: [PATCH 056/600] [path_provider] Use the application ID in the application support path (#2845) (#3077) Use the existing executable named directory if it exists, to allow backwards compatibility to work. --- .../path_provider_linux/CHANGELOG.md | 5 + .../lib/path_provider_linux.dart | 59 +----------- .../lib/src/get_application_id.dart | 9 ++ .../lib/src/get_application_id_real.dart | 42 +++++++++ .../lib/src/get_application_id_stub.dart | 6 ++ .../lib/src/path_provider_linux.dart | 92 +++++++++++++++++++ .../path_provider_linux/pubspec.yaml | 3 +- .../test/path_provider_linux_test.dart | 17 +++- 8 files changed, 172 insertions(+), 61 deletions(-) create mode 100644 packages/path_provider/path_provider_linux/lib/src/get_application_id.dart create mode 100644 packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart create mode 100644 packages/path_provider/path_provider_linux/lib/src/get_application_id_stub.dart create mode 100644 packages/path_provider/path_provider_linux/lib/src/path_provider_linux.dart diff --git a/packages/path_provider/path_provider_linux/CHANGELOG.md b/packages/path_provider/path_provider_linux/CHANGELOG.md index 209fddbd61bd..f227570bdd1d 100644 --- a/packages/path_provider/path_provider_linux/CHANGELOG.md +++ b/packages/path_provider/path_provider_linux/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.1.3 + +* Change getApplicationSupportPath from using executable name to application ID (if provided). + * If the executable name based directory exists, continue to use that so existing applications continue with the same behaviour. + ## 2.1.2 * Fixes link in README. diff --git a/packages/path_provider/path_provider_linux/lib/path_provider_linux.dart b/packages/path_provider/path_provider_linux/lib/path_provider_linux.dart index ab18db69ddfb..e32af1bf5f13 100644 --- a/packages/path_provider/path_provider_linux/lib/path_provider_linux.dart +++ b/packages/path_provider/path_provider_linux/lib/path_provider_linux.dart @@ -2,61 +2,4 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:io'; - -import 'package:flutter/foundation.dart'; -import 'package:path/path.dart' as path; -import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; -import 'package:xdg_directories/xdg_directories.dart' as xdg; - -/// The linux implementation of [PathProviderPlatform] -/// -/// This class implements the `package:path_provider` functionality for linux -class PathProviderLinux extends PathProviderPlatform { - /// Constructs an instance of [PathProviderLinux] - PathProviderLinux() : _environment = Platform.environment; - - /// Constructs an instance of [PathProviderLinux] with the given [environment] - @visibleForTesting - PathProviderLinux.private({ - required Map environment, - }) : _environment = environment; - - final Map _environment; - - /// Registers this class as the default instance of [PathProviderPlatform] - static void registerWith() { - PathProviderPlatform.instance = PathProviderLinux(); - } - - @override - Future getTemporaryPath() { - final String environmentTmpDir = _environment['TMPDIR'] ?? ''; - return Future.value( - environmentTmpDir.isEmpty ? '/tmp' : environmentTmpDir, - ); - } - - @override - Future getApplicationSupportPath() async { - final String processName = path.basenameWithoutExtension( - await File('/proc/self/exe').resolveSymbolicLinks()); - final Directory directory = - Directory(path.join(xdg.dataHome.path, processName)); - // Creating the directory if it doesn't exist, because mobile implementations assume the directory exists - if (!directory.existsSync()) { - await directory.create(recursive: true); - } - return directory.path; - } - - @override - Future getApplicationDocumentsPath() { - return Future.value(xdg.getUserDirectory('DOCUMENTS')?.path); - } - - @override - Future getDownloadsPath() { - return Future.value(xdg.getUserDirectory('DOWNLOAD')?.path); - } -} +export 'src/path_provider_linux.dart'; diff --git a/packages/path_provider/path_provider_linux/lib/src/get_application_id.dart b/packages/path_provider/path_provider_linux/lib/src/get_application_id.dart new file mode 100644 index 000000000000..e169c025eef1 --- /dev/null +++ b/packages/path_provider/path_provider_linux/lib/src/get_application_id.dart @@ -0,0 +1,9 @@ +// 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. + +// getApplicationId() is implemented using FFI; export a stub for platforms +// that don't support FFI (e.g., web) to avoid having transitive dependencies +// break web compilation. +export 'get_application_id_stub.dart' + if (dart.library.ffi) 'get_application_id_real.dart'; diff --git a/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart b/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart new file mode 100644 index 000000000000..f6d25bbe783e --- /dev/null +++ b/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart @@ -0,0 +1,42 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +// GApplication* g_application_get_default(); +typedef _GApplicationGetDefaultC = IntPtr Function(); +typedef _GApplicationGetDefaultDart = int Function(); + +// const gchar* g_application_get_application_id(GApplication* application); +typedef _GApplicationGetApplicationIdC = Pointer Function(IntPtr); +typedef _GApplicationGetApplicationIdDart = Pointer Function(int); + +/// Gets the application ID for this app. +String? getApplicationId() { + DynamicLibrary gio; + try { + gio = DynamicLibrary.open('libgio-2.0.so'); + } on ArgumentError { + return null; + } + final _GApplicationGetDefaultDart gApplicationGetDefault = + gio.lookupFunction<_GApplicationGetDefaultC, _GApplicationGetDefaultDart>( + 'g_application_get_default'); + final int app = gApplicationGetDefault(); + if (app == 0) { + return null; + } + + final _GApplicationGetApplicationIdDart gApplicationGetApplicationId = + gio.lookupFunction<_GApplicationGetApplicationIdC, + _GApplicationGetApplicationIdDart>( + 'g_application_get_application_id'); + final Pointer appId = gApplicationGetApplicationId(app); + if (appId == null) { + return null; + } + + return appId.toDartString(); +} diff --git a/packages/path_provider/path_provider_linux/lib/src/get_application_id_stub.dart b/packages/path_provider/path_provider_linux/lib/src/get_application_id_stub.dart new file mode 100644 index 000000000000..909997693626 --- /dev/null +++ b/packages/path_provider/path_provider_linux/lib/src/get_application_id_stub.dart @@ -0,0 +1,6 @@ +// 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. + +/// Gets the application ID for this app. +String? getApplicationId() => null; diff --git a/packages/path_provider/path_provider_linux/lib/src/path_provider_linux.dart b/packages/path_provider/path_provider_linux/lib/src/path_provider_linux.dart new file mode 100644 index 000000000000..1544dcea2984 --- /dev/null +++ b/packages/path_provider/path_provider_linux/lib/src/path_provider_linux.dart @@ -0,0 +1,92 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:path/path.dart' as path; +import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; +import 'package:xdg_directories/xdg_directories.dart' as xdg; + +import 'get_application_id.dart'; + +/// The linux implementation of [PathProviderPlatform] +/// +/// This class implements the `package:path_provider` functionality for Linux. +class PathProviderLinux extends PathProviderPlatform { + /// Constructs an instance of [PathProviderLinux] + PathProviderLinux() : _environment = Platform.environment; + + /// Constructs an instance of [PathProviderLinux] with the given [environment] + @visibleForTesting + PathProviderLinux.private( + {Map environment = const {}, + String? executableName, + String? applicationId}) + : _environment = environment, + _executableName = executableName, + _applicationId = applicationId; + + final Map _environment; + String? _executableName; + String? _applicationId; + + /// Registers this class as the default instance of [PathProviderPlatform] + static void registerWith() { + PathProviderPlatform.instance = PathProviderLinux(); + } + + @override + Future getTemporaryPath() { + final String environmentTmpDir = _environment['TMPDIR'] ?? ''; + return Future.value( + environmentTmpDir.isEmpty ? '/tmp' : environmentTmpDir, + ); + } + + @override + Future getApplicationSupportPath() async { + final Directory directory = + Directory(path.join(xdg.dataHome.path, await _getId())); + if (directory.existsSync()) { + return directory.path; + } + + // This plugin originally used the executable name as a directory. + // Use that if it exists for backwards compatibility. + final Directory legacyDirectory = + Directory(path.join(xdg.dataHome.path, await _getExecutableName())); + if (legacyDirectory.existsSync()) { + return legacyDirectory.path; + } + + // Create the directory, because mobile implementations assume the directory exists. + await directory.create(recursive: true); + return directory.path; + } + + @override + Future getApplicationDocumentsPath() { + return Future.value(xdg.getUserDirectory('DOCUMENTS')?.path); + } + + @override + Future getDownloadsPath() { + return Future.value(xdg.getUserDirectory('DOWNLOAD')?.path); + } + + // Gets the name of this executable. + Future _getExecutableName() async { + _executableName ??= path.basenameWithoutExtension( + await File('/proc/self/exe').resolveSymbolicLinks()); + return _executableName!; + } + + // Gets the unique ID for this application. + Future _getId() async { + _applicationId ??= getApplicationId(); + // If no application ID then fall back to using the executable name. + return _applicationId ?? await _getExecutableName(); + } +} diff --git a/packages/path_provider/path_provider_linux/pubspec.yaml b/packages/path_provider/path_provider_linux/pubspec.yaml index 67638cdef332..25d1af9936c8 100644 --- a/packages/path_provider/path_provider_linux/pubspec.yaml +++ b/packages/path_provider/path_provider_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_linux description: Linux implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.1.2 +version: 2.1.3 environment: sdk: ">=2.12.0 <3.0.0" @@ -16,6 +16,7 @@ flutter: dartPluginClass: PathProviderLinux dependencies: + ffi: ^1.1.2 flutter: sdk: flutter path: ^1.8.0 diff --git a/packages/path_provider/path_provider_linux/test/path_provider_linux_test.dart b/packages/path_provider/path_provider_linux/test/path_provider_linux_test.dart index 6dd35000a8ea..1f567c00513d 100644 --- a/packages/path_provider/path_provider_linux/test/path_provider_linux_test.dart +++ b/packages/path_provider/path_provider_linux/test/path_provider_linux_test.dart @@ -4,6 +4,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:path_provider_linux/path_provider_linux.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; +import 'package:xdg_directories/xdg_directories.dart' as xdg; void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -35,8 +36,20 @@ void main() { }); test('getApplicationSupportPath', () async { - final PathProviderPlatform plugin = PathProviderPlatform.instance; - expect(await plugin.getApplicationSupportPath(), startsWith('/')); + final PathProviderPlatform plugin = PathProviderLinux.private( + executableName: 'path_provider_linux_test_binary', + applicationId: 'com.example.Test'); + // Note this will fail if ${xdg.dataHome.path}/path_provider_linux_test_binary exists on the local filesystem. + expect(await plugin.getApplicationSupportPath(), + '${xdg.dataHome.path}/com.example.Test'); + }); + + test('getApplicationSupportPath uses executable name if no application Id', + () async { + final PathProviderPlatform plugin = PathProviderLinux.private( + executableName: 'path_provider_linux_test_binary'); + expect(await plugin.getApplicationSupportPath(), + '${xdg.dataHome.path}/path_provider_linux_test_binary'); }); test('getApplicationDocumentsPath', () async { From e74df9bbef32563ec0326896382a0cd5bba43cad Mon Sep 17 00:00:00 2001 From: Alan Ou <50572272+HiIamAlanOu@users.noreply.github.com> Date: Fri, 10 Dec 2021 19:16:42 +0800 Subject: [PATCH 057/600] [in_app_purchase] Fix upgrading subscription by deferred proration mode on android. (#4560) This PR is for updating the bug of upgrading subscription by deferred mode on android, the purchaseStream of PurchaseStatus will return error. Fixes [flutter/flutter#94439] --- .../in_app_purchase_android/CHANGELOG.md | 4 ++ .../src/in_app_purchase_android_platform.dart | 10 +++-- .../in_app_purchase_android/pubspec.yaml | 2 +- ...in_app_purchase_android_platform_test.dart | 45 +++++++++++++++++++ 4 files changed, 57 insertions(+), 4 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md index 9bce54c2df0f..5c34de267737 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.2 + +* Fixes the `purchaseStream` incorrectly reporting `PurchaseStatus.error` when user upgrades subscription by deferred proration mode. + ## 0.2.1 * Deprecated the `InAppPurchaseAndroidPlatformAddition.enablePendingPurchases()` method and `InAppPurchaseAndroidPlatformAddition.enablePendingPurchase` property. Since Google Play no longer accepts App submissions that don't support pending purchases it is no longer necessary to acknowledge this through code. diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform.dart index 7deec6e6ab69..7c8ca529c0f5 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform.dart @@ -270,13 +270,17 @@ class InAppPurchaseAndroidPlatform extends InAppPurchasePlatform { if (purchases.isNotEmpty) { return Future.wait(purchases); } else { + PurchaseStatus status = PurchaseStatus.error; + if (resultWrapper.responseCode == BillingResponse.userCanceled) { + status = PurchaseStatus.canceled; + } else if (resultWrapper.responseCode == BillingResponse.ok) { + status = PurchaseStatus.purchased; + } return [ PurchaseDetails( purchaseID: '', productID: '', - status: resultWrapper.responseCode == BillingResponse.userCanceled - ? PurchaseStatus.canceled - : PurchaseStatus.error, + status: status, transactionDate: null, verificationData: PurchaseVerificationData( localVerificationData: '', diff --git a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml index 63bfa134377f..4d8864357b57 100644 --- a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_android description: An implementation for the Android platform of the Flutter `in_app_purchase` plugin. This uses the Android BillingClient APIs. repository: https://github.com/flutter/plugins/tree/master/packages/in_app_purchase/in_app_purchase_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.2.1 +version: 0.2.2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_test.dart b/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_test.dart index 9f00bc79f52a..9c167e5ea3be 100644 --- a/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_test.dart +++ b/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_test.dart @@ -682,6 +682,51 @@ void main() { GooglePlayPurchaseDetails result = await completer.future; expect(result.status, PurchaseStatus.canceled); }); + + test( + 'should get purchased purchase status when upgrading subscription by deferred proration mode', + () async { + final SkuDetailsWrapper skuDetails = dummySkuDetails; + final String accountId = "hashedAccountId"; + const String debugMessage = 'dummy message'; + final BillingResponse sentCode = BillingResponse.ok; + final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + responseCode: sentCode, debugMessage: debugMessage); + stubPlatform.addResponse( + name: launchMethodName, + value: buildBillingResultMap(expectedBillingResult), + additionalStepBeforeReturn: (_) { + // Mock java update purchase callback. + MethodCall call = MethodCall(kOnPurchasesUpdated, { + 'billingResult': buildBillingResultMap(expectedBillingResult), + 'responseCode': BillingResponseConverter().toJson(sentCode), + 'purchasesList': [] + }); + iapAndroidPlatform.billingClient.callHandler(call); + }); + + Completer completer = Completer(); + PurchaseDetails purchaseDetails; + Stream purchaseStream = iapAndroidPlatform.purchaseStream; + late StreamSubscription subscription; + subscription = purchaseStream.listen((_) { + purchaseDetails = _.first; + completer.complete(purchaseDetails); + subscription.cancel(); + }, onDone: () {}); + final GooglePlayPurchaseParam purchaseParam = GooglePlayPurchaseParam( + productDetails: GooglePlayProductDetails.fromSkuDetails(skuDetails), + applicationUserName: accountId, + changeSubscriptionParam: ChangeSubscriptionParam( + oldPurchaseDetails: GooglePlayPurchaseDetails.fromPurchase( + dummyUnacknowledgedPurchase), + prorationMode: ProrationMode.deferred, + )); + await iapAndroidPlatform.buyNonConsumable(purchaseParam: purchaseParam); + + PurchaseDetails result = await completer.future; + expect(result.status, PurchaseStatus.purchased); + }); }); group('complete purchase', () { From f9f6597ad844f5d5c1d76ecbf8576da5e7f8ba5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Uek=C3=B6tter?= Date: Fri, 10 Dec 2021 12:17:17 +0100 Subject: [PATCH 058/600] [shared_preferences] Fix example in readme for mocking values (#4411) This fixes the example for mocking values in the readme of shared_preferences. After the NNBD migration mock values can only be of type Object. --- packages/shared_preferences/shared_preferences/CHANGELOG.md | 4 ++++ packages/shared_preferences/shared_preferences/README.md | 3 ++- packages/shared_preferences/shared_preferences/pubspec.yaml | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/shared_preferences/shared_preferences/CHANGELOG.md b/packages/shared_preferences/shared_preferences/CHANGELOG.md index cc29dd98fc89..d8c4331133aa 100644 --- a/packages/shared_preferences/shared_preferences/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.11 + +* Corrects example for mocking in readme. + ## 2.0.10 * Removes obsolete manual registration of Windows and Linux implementations. diff --git a/packages/shared_preferences/shared_preferences/README.md b/packages/shared_preferences/shared_preferences/README.md index e51ddea1c890..46d022f4647c 100644 --- a/packages/shared_preferences/shared_preferences/README.md +++ b/packages/shared_preferences/shared_preferences/README.md @@ -42,5 +42,6 @@ _incrementCounter() async { You can populate `SharedPreferences` with initial values in your tests by running this code: ```dart -SharedPreferences.setMockInitialValues (Map values); +Map values = {'counter': 1}; +SharedPreferences.setMockInitialValues(values); ``` diff --git a/packages/shared_preferences/shared_preferences/pubspec.yaml b/packages/shared_preferences/shared_preferences/pubspec.yaml index 156e2e230565..a16cd2009738 100644 --- a/packages/shared_preferences/shared_preferences/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for reading and writing simple key-value pairs. Wraps NSUserDefaults on iOS and SharedPreferences on Android. repository: https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.10 +version: 2.0.11 environment: sdk: ">=2.14.0 <3.0.0" From 684c34f5692808ce2fbf9a3d89290d3d7a4f7e33 Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Fri, 10 Dec 2021 03:18:07 -0800 Subject: [PATCH 059/600] Add missing return for nullably typed function (#4598) --- script/tool/lib/src/pubspec_check_command.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/script/tool/lib/src/pubspec_check_command.dart b/script/tool/lib/src/pubspec_check_command.dart index 04c3848a7dec..4f2b208524d9 100644 --- a/script/tool/lib/src/pubspec_check_command.dart +++ b/script/tool/lib/src/pubspec_check_command.dart @@ -211,6 +211,7 @@ class PubspecCheckCommand extends PackageLoopingCommand { return '"description" is too long. pub.dev recommends package ' 'descriptions of 60-180 characters.'; } + return null; } bool _checkIssueLink(Pubspec pubspec) { From b78018a365046629be142a429712c405896ff5a8 Mon Sep 17 00:00:00 2001 From: Daewon Kim Date: Fri, 10 Dec 2021 06:20:58 -0500 Subject: [PATCH 060/600] [in_app-purchase] Updated the ReadMe to remove the deprecated `InAppPurchaseAndroidPlatformAddition.enablePendingPurchases ` method (#4597) This will resolve flutter/flutter#93837 & this work is just the follow-up from #4527 PR. We need to update the documentation in the README.md of the in_app_purchase package to remove the instruction of initialization with the deprecated `InAppPurchaseAndroidPlatformAddition.enablePendingPurchases` method. Fixes https://github.com/flutter/flutter/issues/94977 --- .../in_app_purchase/CHANGELOG.md | 4 ++++ .../in_app_purchase/in_app_purchase/README.md | 22 ------------------- .../in_app_purchase/pubspec.yaml | 2 +- 3 files changed, 5 insertions(+), 23 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md index 9e82ebdfe7a8..232dacf0b71e 100644 --- a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.1 + +* Removes the instructions on initializing the plugin since this functionality is deprecated. + ## 2.0.0 * **BREAKING CHANGES**: diff --git a/packages/in_app_purchase/in_app_purchase/README.md b/packages/in_app_purchase/in_app_purchase/README.md index f3ed1e0570f7..b67f9e5b52ac 100644 --- a/packages/in_app_purchase/in_app_purchase/README.md +++ b/packages/in_app_purchase/in_app_purchase/README.md @@ -54,7 +54,6 @@ See also the codelab for [in-app purchases in Flutter](https://codelabs.develope This section has examples of code for the following tasks: -* [Initializing the plugin](#initializing-the-plugin) * [Listening to purchase updates](#listening-to-purchase-updates) * [Connecting to the underlying store](#connecting-to-the-underlying-store) * [Loading products for sale](#loading-products-for-sale) @@ -65,27 +64,6 @@ This section has examples of code for the following tasks: * [Accessing platform specific product or purchase properties](#accessing-platform-specific-product-or-purchase-properties) * [Presenting a code redemption sheet (iOS 14)](#presenting-a-code-redemption-sheet-ios-14) -### Initializing the plugin - -The following initialization code is required for Google Play: - -```dart -// Import `in_app_purchase_android.dart` to be able to access the -// `InAppPurchaseAndroidPlatformAddition` class. -import 'package:in_app_purchase_android/in_app_purchase_android.dart'; -import 'package:flutter/foundation.dart'; - -void main() { - // Inform the plugin that this app supports pending purchases on Android. - // An error will occur on Android if you access the plugin `instance` - // without this call. - if (defaultTargetPlatform == TargetPlatform.android) { - InAppPurchaseAndroidPlatformAddition.enablePendingPurchases(); - } - runApp(MyApp()); -} -``` - **Note:** It is not necessary to depend on `com.android.billingclient:billing` in your own app's `android/app/build.gradle` file. If you choose to do so know that conflicts might occur. ### Listening to purchase updates diff --git a/packages/in_app_purchase/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/pubspec.yaml index 72fac1d29f55..c7bb11f38f8f 100644 --- a/packages/in_app_purchase/in_app_purchase/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase description: A Flutter plugin for in-app purchases. Exposes APIs for making in-app purchases through the App Store and Google Play. repository: https://github.com/flutter/plugins/tree/master/packages/in_app_purchase/in_app_purchase issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 2.0.0 +version: 2.0.1 environment: sdk: ">=2.12.0 <3.0.0" From e7dbb5f4a59fc13514183b278be50dd5b6b24eb7 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 10 Dec 2021 14:33:10 -0500 Subject: [PATCH 061/600] [path_provider] Fix handling of null application ID (#4606) The lookup of application ID was handling `null` (Dart null), but not `nullptr` (Dart representation of a C null pointer), so was throwing an exception in applications without an application ID. This includes the `shared_preferences_linux` example application, so this fixes tree breakage. --- .../path_provider_linux/CHANGELOG.md | 5 ++ .../lib/src/get_application_id_real.dart | 68 ++++++++++++++----- .../path_provider_linux/pubspec.yaml | 3 +- .../test/get_application_id_test.dart | 62 +++++++++++++++++ 4 files changed, 121 insertions(+), 17 deletions(-) create mode 100644 packages/path_provider/path_provider_linux/test/get_application_id_test.dart diff --git a/packages/path_provider/path_provider_linux/CHANGELOG.md b/packages/path_provider/path_provider_linux/CHANGELOG.md index f227570bdd1d..cfea5702dbe1 100644 --- a/packages/path_provider/path_provider_linux/CHANGELOG.md +++ b/packages/path_provider/path_provider_linux/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.1.4 + +* Fixes `getApplicationSupportPath` handling of applications where the + application ID is not set. + ## 2.1.3 * Change getApplicationSupportPath from using executable name to application ID (if provided). diff --git a/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart b/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart index f6d25bbe783e..d2e60fb6ff82 100644 --- a/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart +++ b/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart @@ -4,6 +4,7 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; +import 'package:meta/meta.dart'; // GApplication* g_application_get_default(); typedef _GApplicationGetDefaultC = IntPtr Function(); @@ -13,30 +14,65 @@ typedef _GApplicationGetDefaultDart = int Function(); typedef _GApplicationGetApplicationIdC = Pointer Function(IntPtr); typedef _GApplicationGetApplicationIdDart = Pointer Function(int); +/// Interface for interacting with libgio. +@visibleForTesting +class GioUtils { + /// Creates a default instance that uses the real libgio. + GioUtils() { + try { + _gio = DynamicLibrary.open('libgio-2.0.so'); + } on ArgumentError { + _gio = null; + } + } + + DynamicLibrary? _gio; + + /// True if libgio was opened successfully. + bool get libraryIsPresent => _gio != null; + + /// Wraps `g_application_get_default`. + int gApplicationGetDefault() { + if (_gio == null) { + return 0; + } + final _GApplicationGetDefaultDart getDefault = _gio! + .lookupFunction<_GApplicationGetDefaultC, _GApplicationGetDefaultDart>( + 'g_application_get_default'); + return getDefault(); + } + + /// Wraps g_application_get_application_id. + Pointer gApplicationGetApplicationId(int app) { + if (_gio == null) { + return nullptr; + } + final _GApplicationGetApplicationIdDart gApplicationGetApplicationId = _gio! + .lookupFunction<_GApplicationGetApplicationIdC, + _GApplicationGetApplicationIdDart>( + 'g_application_get_application_id'); + return gApplicationGetApplicationId(app); + } +} + +/// Allows overriding the default GioUtils instance with a fake for testing. +@visibleForTesting +GioUtils? gioUtilsOverride; + /// Gets the application ID for this app. String? getApplicationId() { - DynamicLibrary gio; - try { - gio = DynamicLibrary.open('libgio-2.0.so'); - } on ArgumentError { + final GioUtils gio = gioUtilsOverride ?? GioUtils(); + if (!gio.libraryIsPresent) { return null; } - final _GApplicationGetDefaultDart gApplicationGetDefault = - gio.lookupFunction<_GApplicationGetDefaultC, _GApplicationGetDefaultDart>( - 'g_application_get_default'); - final int app = gApplicationGetDefault(); + + final int app = gio.gApplicationGetDefault(); if (app == 0) { return null; } - - final _GApplicationGetApplicationIdDart gApplicationGetApplicationId = - gio.lookupFunction<_GApplicationGetApplicationIdC, - _GApplicationGetApplicationIdDart>( - 'g_application_get_application_id'); - final Pointer appId = gApplicationGetApplicationId(app); - if (appId == null) { + final Pointer appId = gio.gApplicationGetApplicationId(app); + if (appId == null || appId == nullptr) { return null; } - return appId.toDartString(); } diff --git a/packages/path_provider/path_provider_linux/pubspec.yaml b/packages/path_provider/path_provider_linux/pubspec.yaml index 25d1af9936c8..ec1d8d8b3148 100644 --- a/packages/path_provider/path_provider_linux/pubspec.yaml +++ b/packages/path_provider/path_provider_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_linux description: Linux implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.1.3 +version: 2.1.4 environment: sdk: ">=2.12.0 <3.0.0" @@ -19,6 +19,7 @@ dependencies: ffi: ^1.1.2 flutter: sdk: flutter + meta: ^1.3.0 path: ^1.8.0 path_provider_platform_interface: ^2.0.0 xdg_directories: ^0.2.0 diff --git a/packages/path_provider/path_provider_linux/test/get_application_id_test.dart b/packages/path_provider/path_provider_linux/test/get_application_id_test.dart new file mode 100644 index 000000000000..d9eb5163b5fe --- /dev/null +++ b/packages/path_provider/path_provider_linux/test/get_application_id_test.dart @@ -0,0 +1,62 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +import 'dart:ffi'; + +import 'package:ffi/ffi.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:path_provider_linux/src/get_application_id_real.dart'; + +class _FakeGioUtils implements GioUtils { + int? application; + Pointer? applicationId; + + @override + bool libraryIsPresent = false; + + @override + int gApplicationGetDefault() => application!; + + @override + Pointer gApplicationGetApplicationId(int app) => applicationId!; +} + +void main() { + late _FakeGioUtils fakeGio; + + setUp(() { + fakeGio = _FakeGioUtils(); + gioUtilsOverride = fakeGio; + }); + + tearDown(() { + gioUtilsOverride = null; + }); + + test('returns null if libgio is not available', () { + expect(getApplicationId(), null); + }); + + test('returns null if g_paplication_get_default returns 0', () { + fakeGio.libraryIsPresent = true; + fakeGio.application = 0; + expect(getApplicationId(), null); + }); + + test('returns null if g_application_get_application_id returns nullptr', () { + fakeGio.libraryIsPresent = true; + fakeGio.application = 1; + fakeGio.applicationId = nullptr; + expect(getApplicationId(), null); + }); + + test('returns value if g_application_get_application_id returns a value', () { + fakeGio.libraryIsPresent = true; + fakeGio.application = 1; + const String id = 'foo'; + final Pointer idPtr = id.toNativeUtf8(); + fakeGio.applicationId = idPtr; + expect(getApplicationId(), id); + calloc.free(idPtr); + }); +} From a5b664db7c7b053328202a20830dcdcc4010353c Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Fri, 10 Dec 2021 16:09:20 -0800 Subject: [PATCH 062/600] [tools] fix typo in tools (#4607) --- script/tool/lib/src/lint_android_command.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/tool/lib/src/lint_android_command.dart b/script/tool/lib/src/lint_android_command.dart index a7b5c4f2e8bf..49b2181a4615 100644 --- a/script/tool/lib/src/lint_android_command.dart +++ b/script/tool/lib/src/lint_android_command.dart @@ -12,9 +12,9 @@ import 'common/package_looping_command.dart'; import 'common/process_runner.dart'; import 'common/repository_package.dart'; -/// Lint the CocoaPod podspecs and run unit tests. +/// Run 'gradlew lint'. /// -/// See https://guides.cocoapods.org/terminal/commands.html#pod_lib_lint. +/// See https://developer.android.com/studio/write/lint. class LintAndroidCommand extends PackageLoopingCommand { /// Creates an instance of the linter command. LintAndroidCommand( From 1a56bb2daea752887320d8a3d5eb93af52afc6a4 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 13 Dec 2021 16:09:40 -0500 Subject: [PATCH 063/600] [local_auth] Switch to new analysis options (#4604) Removes the legacy analysis options and fixes all violations. Most fixes were automated `dart fix --apply` changes. Part of https://github.com/flutter/flutter/issues/76229 --- packages/local_auth/CHANGELOG.md | 3 +- packages/local_auth/analysis_options.yaml | 1 - packages/local_auth/example/lib/main.dart | 105 ++++++++++-------- packages/local_auth/example/pubspec.yaml | 4 +- packages/local_auth/lib/local_auth.dart | 8 +- packages/local_auth/pubspec.yaml | 2 +- packages/local_auth/test/local_auth_test.dart | 4 +- script/configs/custom_analysis.yaml | 1 - 8 files changed, 68 insertions(+), 60 deletions(-) delete mode 100644 packages/local_auth/analysis_options.yaml diff --git a/packages/local_auth/CHANGELOG.md b/packages/local_auth/CHANGELOG.md index 82358d61e644..7fe3a98ed08f 100644 --- a/packages/local_auth/CHANGELOG.md +++ b/packages/local_auth/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 1.1.9 +* Updates code for analysis option changes. * Updates Android compileSdkVersion to 31. ## 1.1.8 diff --git a/packages/local_auth/analysis_options.yaml b/packages/local_auth/analysis_options.yaml deleted file mode 100644 index cda4f6e153e6..000000000000 --- a/packages/local_auth/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../analysis_options_legacy.yaml diff --git a/packages/local_auth/example/lib/main.dart b/packages/local_auth/example/lib/main.dart index b6b6f3278423..a9604b3411b7 100644 --- a/packages/local_auth/example/lib/main.dart +++ b/packages/local_auth/example/lib/main.dart @@ -31,7 +31,7 @@ class _MyAppState extends State { void initState() { super.initState(); auth.isDeviceSupported().then( - (isSupported) => setState(() => _supportState = isSupported + (bool isSupported) => setState(() => _supportState = isSupported ? _SupportState.supported : _SupportState.unsupported), ); @@ -45,7 +45,9 @@ class _MyAppState extends State { canCheckBiometrics = false; print(e); } - if (!mounted) return; + if (!mounted) { + return; + } setState(() { _canCheckBiometrics = canCheckBiometrics; @@ -60,7 +62,9 @@ class _MyAppState extends State { availableBiometrics = []; print(e); } - if (!mounted) return; + if (!mounted) { + return; + } setState(() { _availableBiometrics = availableBiometrics; @@ -85,11 +89,13 @@ class _MyAppState extends State { print(e); setState(() { _isAuthenticating = false; - _authorized = "Error - ${e.message}"; + _authorized = 'Error - ${e.message}'; }); return; } - if (!mounted) return; + if (!mounted) { + return; + } setState( () => _authorized = authenticated ? 'Authorized' : 'Not Authorized'); @@ -116,11 +122,13 @@ class _MyAppState extends State { print(e); setState(() { _isAuthenticating = false; - _authorized = "Error - ${e.message}"; + _authorized = 'Error - ${e.message}'; }); return; } - if (!mounted) return; + if (!mounted) { + return; + } final String message = authenticated ? 'Authorized' : 'Not Authorized'; setState(() { @@ -128,7 +136,7 @@ class _MyAppState extends State { }); } - void _cancelAuthentication() async { + Future _cancelAuthentication() async { await auth.stopAuthentication(); setState(() => _isAuthenticating = false); } @@ -142,67 +150,68 @@ class _MyAppState extends State { ), body: ListView( padding: const EdgeInsets.only(top: 30), - children: [ + children: [ Column( mainAxisAlignment: MainAxisAlignment.center, - children: [ + children: [ if (_supportState == _SupportState.unknown) - CircularProgressIndicator() + const CircularProgressIndicator() else if (_supportState == _SupportState.supported) - Text("This device is supported") + const Text('This device is supported') else - Text("This device is not supported"), - Divider(height: 100), + const Text('This device is not supported'), + const Divider(height: 100), Text('Can check biometrics: $_canCheckBiometrics\n'), ElevatedButton( child: const Text('Check biometrics'), onPressed: _checkBiometrics, ), - Divider(height: 100), + const Divider(height: 100), Text('Available biometrics: $_availableBiometrics\n'), ElevatedButton( child: const Text('Get available biometrics'), onPressed: _getAvailableBiometrics, ), - Divider(height: 100), + const Divider(height: 100), Text('Current State: $_authorized\n'), - (_isAuthenticating) - ? ElevatedButton( - onPressed: _cancelAuthentication, + if (_isAuthenticating) + ElevatedButton( + onPressed: _cancelAuthentication, + child: Row( + mainAxisSize: MainAxisSize.min, + children: const [ + Text('Cancel Authentication'), + Icon(Icons.cancel), + ], + ), + ) + else + Column( + children: [ + ElevatedButton( + child: Row( + mainAxisSize: MainAxisSize.min, + children: const [ + Text('Authenticate'), + Icon(Icons.perm_device_information), + ], + ), + onPressed: _authenticate, + ), + ElevatedButton( child: Row( mainAxisSize: MainAxisSize.min, - children: [ - Text("Cancel Authentication"), - Icon(Icons.cancel), + children: [ + Text(_isAuthenticating + ? 'Cancel' + : 'Authenticate: biometrics only'), + const Icon(Icons.fingerprint), ], ), - ) - : Column( - children: [ - ElevatedButton( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text('Authenticate'), - Icon(Icons.perm_device_information), - ], - ), - onPressed: _authenticate, - ), - ElevatedButton( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text(_isAuthenticating - ? 'Cancel' - : 'Authenticate: biometrics only'), - Icon(Icons.fingerprint), - ], - ), - onPressed: _authenticateWithBiometrics, - ), - ], + onPressed: _authenticateWithBiometrics, ), + ], + ), ], ), ], diff --git a/packages/local_auth/example/pubspec.yaml b/packages/local_auth/example/pubspec.yaml index 3aa8fd848057..ad0de2002a4c 100644 --- a/packages/local_auth/example/pubspec.yaml +++ b/packages/local_auth/example/pubspec.yaml @@ -18,10 +18,10 @@ dependencies: path: ../ dev_dependencies: - integration_test: - sdk: flutter flutter_driver: sdk: flutter + integration_test: + sdk: flutter pedantic: ^1.10.0 flutter: diff --git a/packages/local_auth/lib/local_auth.dart b/packages/local_auth/lib/local_auth.dart index 0b75a83d4029..af02d57a7359 100644 --- a/packages/local_auth/lib/local_auth.dart +++ b/packages/local_auth/lib/local_auth.dart @@ -32,7 +32,7 @@ void setMockPathProviderPlatform(Platform platform) { class LocalAuthentication { /// The `authenticateWithBiometrics` method has been deprecated. /// Use `authenticate` with `biometricOnly: true` instead - @Deprecated("Use `authenticate` with `biometricOnly: true` instead") + @Deprecated('Use `authenticate` with `biometricOnly: true` instead') Future authenticateWithBiometrics({ required String localizedReason, bool useErrorDialogs = true, @@ -160,9 +160,9 @@ class LocalAuthentication { final List result = (await _channel.invokeListMethod( 'getAvailableBiometrics', )) ?? - []; + []; final List biometrics = []; - result.forEach((String value) { + for (final String value in result) { switch (value) { case 'face': biometrics.add(BiometricType.face); @@ -176,7 +176,7 @@ class LocalAuthentication { case 'undefined': break; } - }); + } return biometrics; } } diff --git a/packages/local_auth/pubspec.yaml b/packages/local_auth/pubspec.yaml index 4f5ef26d9fb1..9303b0c8cc74 100644 --- a/packages/local_auth/pubspec.yaml +++ b/packages/local_auth/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Android and iOS devices to allow local authentication via fingerprint, touch ID, face ID, passcode, pin, or pattern. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.1.8 +version: 1.1.9 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/test/local_auth_test.dart b/packages/local_auth/test/local_auth_test.dart index b24de8bd3c11..758b9cec0e97 100644 --- a/packages/local_auth/test/local_auth_test.dart +++ b/packages/local_auth/test/local_auth_test.dart @@ -30,7 +30,7 @@ void main() { log.clear(); }); - group("With device auth fail over", () { + group('With device auth fail over', () { test('authenticate with no args on Android.', () async { setMockPathProviderPlatform(FakePlatform(operatingSystem: 'android')); await localAuthentication.authenticate( @@ -108,7 +108,7 @@ void main() { }); }); - group("With biometrics only", () { + group('With biometrics only', () { test('authenticate with no args on Android.', () async { setMockPathProviderPlatform(FakePlatform(operatingSystem: 'android')); await localAuthentication.authenticate( diff --git a/script/configs/custom_analysis.yaml b/script/configs/custom_analysis.yaml index e991e5e1446c..71ce1417196f 100644 --- a/script/configs/custom_analysis.yaml +++ b/script/configs/custom_analysis.yaml @@ -27,7 +27,6 @@ - in_app_purchase/in_app_purchase_android - in_app_purchase/in_app_purchase_storekit - ios_platform_images -- local_auth - video_player/video_player - video_player/video_player_platform_interface - video_player/video_player_web From bfa34380ea05631aa21997f4908f74eda65f651e Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 13 Dec 2021 17:20:35 -0500 Subject: [PATCH 064/600] [video_player] Fix a flaky test (#4611) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'reports buffering status' test was written in an unnecessary complicated way that used pairs of completers and booleans that tracked the same thing, and more importantly had a bug (hidden by the fact that this package is still on legacy analysis options) where the `await` statements weren't actually doing anything, because they were not actually `await`ing futures. This fixes the lack of awaits—which should fix the flake—and removes the unnecessary booleans. Fixes https://github.com/flutter/flutter/issues/94775 --- packages/video_player/video_player/CHANGELOG.md | 1 + .../integration_test/video_player_test.dart | 17 +++++------------ 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index f9031685b583..9d4e36d72017 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -2,6 +2,7 @@ * Fixes integration tests. * Updates Android compileSdkVersion to 31. +* Fixes a flaky integration test. ## 2.2.7 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 866b5bce0a8d..63a38290a613 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 @@ -48,17 +48,13 @@ void main() { await networkController.setVolume(0); final Completer started = Completer(); final Completer ended = Completer(); - bool startedBuffering = false; - bool endedBuffering = false; networkController.addListener(() { - if (networkController.value.isBuffering && !startedBuffering) { - startedBuffering = true; + if (!started.isCompleted && networkController.value.isBuffering) { started.complete(); } - if (startedBuffering && + if (started.isCompleted && !networkController.value.isBuffering && - !endedBuffering) { - endedBuffering = true; + !ended.isCompleted) { ended.complete(); } }); @@ -72,11 +68,8 @@ void main() { expect(networkController.value.position, (Duration position) => position > const Duration(seconds: 0)); - await started; - expect(startedBuffering, true); - - await ended; - expect(endedBuffering, true); + await expectLater(started.future, completes); + await expectLater(ended.future, completes); }, skip: !(kIsWeb || defaultTargetPlatform == TargetPlatform.android), ); From 453089d1fa9b45e17352807c6e73adc059a027c2 Mon Sep 17 00:00:00 2001 From: keyonghan <54558023+keyonghan@users.noreply.github.com> Date: Tue, 14 Dec 2021 16:56:10 -0800 Subject: [PATCH 065/600] update firebase test key (#4615) --- .cirrus.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index 95bd6092674b..5dff2d1b035f 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -195,7 +195,7 @@ task: CHANNEL: "master" CHANNEL: "stable" MAPS_API_KEY: ENCRYPTED[596a9f6bca436694625ac50851dc5da6b4d34cba8025f7db5bc9465142e8cd44e15f69e3507787753accebfc4910d550] - GCLOUD_FIREBASE_TESTLAB_KEY: ENCRYPTED[!c9446a7b11d5520c2ebce3c64ccc82fe6d146272cb06a4a4590e22c389f33153f951347a25422522df1a81fe2f085e9a!] + GCLOUD_FIREBASE_TESTLAB_KEY: ENCRYPTED[!cc769765170bebc37e0556e2da5915ca64ee37f4ec8c966ec147e2f59578b476c99e457eafce4e2f8b1a4e305f7096b8!] build_script: # Unsetting CIRRUS_CHANGE_MESSAGE and CIRRUS_COMMIT_MESSAGE as they # might include non-ASCII characters which makes Gradle crash. From 910055b2b53259efef8c76a50f5ae2efa217c270 Mon Sep 17 00:00:00 2001 From: keyonghan <54558023+keyonghan@users.noreply.github.com> Date: Tue, 14 Dec 2021 19:26:33 -0800 Subject: [PATCH 066/600] update firebase key (#4616) --- .cirrus.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index 5dff2d1b035f..bcd0e49327a3 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -195,7 +195,7 @@ task: CHANNEL: "master" CHANNEL: "stable" MAPS_API_KEY: ENCRYPTED[596a9f6bca436694625ac50851dc5da6b4d34cba8025f7db5bc9465142e8cd44e15f69e3507787753accebfc4910d550] - GCLOUD_FIREBASE_TESTLAB_KEY: ENCRYPTED[!cc769765170bebc37e0556e2da5915ca64ee37f4ec8c966ec147e2f59578b476c99e457eafce4e2f8b1a4e305f7096b8!] + GCLOUD_FIREBASE_TESTLAB_KEY: ENCRYPTED[de02374f8d2d14d50792c6b521af2dfb86cbb522efed104f905002e4332546104d387d2bb8710956b729b4bd6533bba0] build_script: # Unsetting CIRRUS_CHANGE_MESSAGE and CIRRUS_COMMIT_MESSAGE as they # might include non-ASCII characters which makes Gradle crash. From 92539fc24765acb755bcd942ea598c1c4aa01b56 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 15 Dec 2021 11:22:52 -0500 Subject: [PATCH 067/600] [flutter_plugin_tools] Auto-retry failed FTL tests (#4610) Currently the flake situation for Firebase Test Lab tests is very bad, and the task running those tests are some of the slowest tasks in the CI. Re-running failed tests is slow, manual work that is signficantly affecting productivity. There are plans to actually address the flake in the short-to-medium term, but in the immediate term this will improve the CI situation, as well as reducing the drag on engineering time that could be spent on the root causes. Part of https://github.com/flutter/flutter/issues/95063 --- script/tool/CHANGELOG.md | 2 + .../lib/src/firebase_test_lab_command.dart | 74 +++++++++++++------ .../test/firebase_test_lab_command_test.dart | 41 +++++++++- 3 files changed, 93 insertions(+), 24 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 72539c24c5fc..12ccf17d4d0b 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -13,6 +13,8 @@ - Fix `federation-safety-check` handling of plugin deletion, and of top-level files in unfederated plugins whose names match federated plugin heuristics (e.g., `packages/foo/foo_android.iml`). +- Add an auto-retry for failed Firebase Test Lab tests as a short-term patch + for flake issues. ## 0.7.3 diff --git a/script/tool/lib/src/firebase_test_lab_command.dart b/script/tool/lib/src/firebase_test_lab_command.dart index 4e53ee8fbace..e824d8ad1a90 100644 --- a/script/tool/lib/src/firebase_test_lab_command.dart +++ b/script/tool/lib/src/firebase_test_lab_command.dart @@ -176,30 +176,22 @@ class FirebaseTestLabCommand extends PackageLoopingCommand { final String testRunId = getStringArg('test-run-id'); final String resultsDir = 'plugins_android_test/${package.displayName}/$buildId/$testRunId/${resultsCounter++}/'; - final List args = [ - 'firebase', - 'test', - 'android', - 'run', - '--type', - 'instrumentation', - '--app', - 'build/app/outputs/apk/debug/app-debug.apk', - '--test', - 'build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk', - '--timeout', - '7m', - '--results-bucket=${getStringArg('results-bucket')}', - '--results-dir=$resultsDir', - ]; - for (final String device in getStringListArg('device')) { - args.addAll(['--device', device]); - } - final int exitCode = await processRunner.runAndStream('gcloud', args, - workingDir: example.directory); - if (exitCode != 0) { - printError('Test failure for $testName'); + // Automatically retry failures; there is significant flake with these + // tests whose cause isn't yet understood, and having to re-run the + // entire shard for a flake in any one test is extremely slow. This should + // be removed once the root cause of the flake is understood. + // See https://github.com/flutter/flutter/issues/95063 + const int maxRetries = 2; + bool passing = false; + for (int i = 1; i <= maxRetries && !passing; ++i) { + if (i > 1) { + logWarning('$testName failed on attempt ${i - 1}. Retrying...'); + } + passing = await _runFirebaseTest(example, test, resultsDir: resultsDir); + } + if (!passing) { + printError('Test failure for $testName after $maxRetries attempts'); errors.add('$testName failed tests'); } } @@ -238,6 +230,42 @@ class FirebaseTestLabCommand extends PackageLoopingCommand { return true; } + /// Runs [test] from [example] as a Firebase Test Lab test, returning true if + /// the test passed. + /// + /// [resultsDir] should be a unique-to-the-test-run directory to store the + /// results on the server. + Future _runFirebaseTest( + RepositoryPackage example, + File test, { + required String resultsDir, + }) async { + final List args = [ + 'firebase', + 'test', + 'android', + 'run', + '--type', + 'instrumentation', + '--app', + 'build/app/outputs/apk/debug/app-debug.apk', + '--test', + 'build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk', + '--timeout', + '7m', + '--results-bucket=${getStringArg('results-bucket')}', + '--results-dir=$resultsDir', + for (final String device in getStringListArg('device')) ...[ + '--device', + device + ], + ]; + final int exitCode = await processRunner.runAndStream('gcloud', args, + workingDir: example.directory); + + return exitCode == 0; + } + /// Builds [target] using Gradle in the given [project]. Assumes Gradle is /// already configured. /// diff --git a/script/tool/test/firebase_test_lab_command_test.dart b/script/tool/test/firebase_test_lab_command_test.dart index 65f398b32ca8..1dfd8ba66b58 100644 --- a/script/tool/test/firebase_test_lab_command_test.dart +++ b/script/tool/test/firebase_test_lab_command_test.dart @@ -271,7 +271,7 @@ public class MainActivityTest { ); }); - test('fails if a test fails', () async { + test('fails if a test fails twice', () async { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; final Directory pluginDir = @@ -287,6 +287,7 @@ public class MainActivityTest { MockProcess(), // auth MockProcess(), // config MockProcess(exitCode: 1), // integration test #1 + MockProcess(exitCode: 1), // integration test #1 retry MockProcess(), // integration test #2 ]; @@ -315,6 +316,44 @@ public class MainActivityTest { ); }); + test('passes with warning if a test fails once, then passes on retry', + () async { + const String javaTestFileRelativePath = + 'example/android/app/src/androidTest/MainActivityTest.java'; + final Directory pluginDir = + createFakePlugin('plugin', packagesDir, extraFiles: [ + 'example/integration_test/bar_test.dart', + 'example/integration_test/foo_test.dart', + 'example/android/gradlew', + javaTestFileRelativePath, + ]); + _writeJavaTestFile(pluginDir, javaTestFileRelativePath); + + processRunner.mockProcessesForExecutable['gcloud'] = [ + MockProcess(), // auth + MockProcess(), // config + MockProcess(exitCode: 1), // integration test #1 + MockProcess(), // integration test #1 retry + MockProcess(), // integration test #2 + ]; + + final List output = await runCapturingPrint(runner, [ + 'firebase-test-lab', + '--device', + 'model=redfin,version=30', + ]); + + expect( + output, + containsAllInOrder([ + contains('Testing example/integration_test/bar_test.dart...'), + contains('bar_test.dart failed on attempt 1. Retrying...'), + contains('Testing example/integration_test/foo_test.dart...'), + contains('Ran for 1 package(s) (1 with warnings)'), + ]), + ); + }); + test('fails for packages with no androidTest directory', () async { createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/integration_test/foo_test.dart', From ee97b6534dd575c0df517d7bcffc89a6db43b3ed Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 16 Dec 2021 16:29:20 -0500 Subject: [PATCH 068/600] [video_player] Eliminate platform channel from mock platform (#4588) --- .../video_player/video_player/CHANGELOG.md | 5 +- .../video_player/lib/video_player.dart | 16 +- .../video_player/video_player/pubspec.yaml | 8 +- .../video_player_initialization_test.dart | 2 + .../video_player/test/video_player_test.dart | 215 ++++++------------ 5 files changed, 89 insertions(+), 157 deletions(-) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 9d4e36d72017..6f2a2b7250d0 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,5 +1,8 @@ -## NEXT +## 2.2.8 +* Changes the way the `VideoPlayerPlatform` instance is cached in the + controller, so that it's no longer impossible to change after the first use. +* Updates unit tests to be self-contained. * Fixes integration tests. * Updates Android compileSdkVersion to 31. * Fixes a flaky integration test. diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index ff727f1432c8..523a1adc5425 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -17,10 +17,18 @@ export 'package:video_player_platform_interface/video_player_platform_interface. import 'src/closed_caption_file.dart'; export 'src/closed_caption_file.dart'; -final VideoPlayerPlatform _videoPlayerPlatform = VideoPlayerPlatform.instance - // This will clear all open videos on the platform when a full restart is - // performed. - ..init(); +VideoPlayerPlatform? _lastVideoPlayerPlatform; + +VideoPlayerPlatform get _videoPlayerPlatform { + VideoPlayerPlatform currentInstance = VideoPlayerPlatform.instance; + if (_lastVideoPlayerPlatform != currentInstance) { + // This will clear all open videos on the platform when a full restart is + // performed. + currentInstance.init(); + _lastVideoPlayerPlatform = currentInstance; + } + return currentInstance; +} /// The duration, current position, buffering state, error state and settings /// of a [VideoPlayerController]. diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index f0d5951b1403..f8c091786526 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for displaying inline video with other Flutter widgets on Android, iOS, and web. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.2.7 +version: 2.2.8 environment: sdk: ">=2.14.0 <3.0.0" @@ -25,12 +25,6 @@ dependencies: sdk: flutter meta: ^1.3.0 video_player_platform_interface: ^4.2.0 - # The design on https://flutter.dev/go/federated-plugins was to leave - # this constraint as "any". We cannot do it right now as it fails pub publish - # validation, so we set a ^ constraint. The exact value doesn't matter since - # the constraints on the interface pins it. - # TODO(amirh): Revisit this (either update this part in the design or the pub tool). - # https://github.com/flutter/flutter/issues/46264 video_player_web: ^2.0.0 html: ^0.15.0 diff --git a/packages/video_player/video_player/test/video_player_initialization_test.dart b/packages/video_player/video_player/test/video_player_initialization_test.dart index 13bfd7be7889..1870934a931e 100644 --- a/packages/video_player/video_player/test/video_player_initialization_test.dart +++ b/packages/video_player/video_player/test/video_player_initialization_test.dart @@ -4,6 +4,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:video_player/video_player.dart'; +import 'package:video_player_platform_interface/video_player_platform_interface.dart'; import 'video_player_test.dart' show FakeVideoPlayerPlatform; @@ -13,6 +14,7 @@ void main() { test('plugin initialized', () async { TestWidgetsFlutterBinding.ensureInitialized(); FakeVideoPlayerPlatform fakeVideoPlayerPlatform = FakeVideoPlayerPlatform(); + VideoPlayerPlatform.instance = fakeVideoPlayerPlatform; final VideoPlayerController controller = VideoPlayerController.network( 'https://127.0.0.1', diff --git a/packages/video_player/video_player/test/video_player_test.dart b/packages/video_player/video_player/test/video_player_test.dart index 08fd9dc604f1..959f98f25e28 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -11,8 +11,6 @@ import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:video_player/video_player.dart'; -import 'package:video_player_platform_interface/messages.dart'; -import 'package:video_player_platform_interface/test.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; class FakeController extends ValueNotifier @@ -187,6 +185,7 @@ void main() { setUp(() { fakeVideoPlayerPlatform = FakeVideoPlayerPlatform(); + VideoPlayerPlatform.instance = fakeVideoPlayerPlatform; }); group('initialize', () { @@ -196,10 +195,8 @@ void main() { ); await controller.initialize(); - expect( - fakeVideoPlayerPlatform.dataSourceDescriptions[0].asset, 'a.avi'); - expect(fakeVideoPlayerPlatform.dataSourceDescriptions[0].packageName, - null); + expect(fakeVideoPlayerPlatform.dataSources[0].asset, 'a.avi'); + expect(fakeVideoPlayerPlatform.dataSources[0].package, null); }); test('network', () async { @@ -209,15 +206,15 @@ void main() { await controller.initialize(); expect( - fakeVideoPlayerPlatform.dataSourceDescriptions[0].uri, + fakeVideoPlayerPlatform.dataSources[0].uri, 'https://127.0.0.1', ); expect( - fakeVideoPlayerPlatform.dataSourceDescriptions[0].formatHint, + fakeVideoPlayerPlatform.dataSources[0].formatHint, null, ); expect( - fakeVideoPlayerPlatform.dataSourceDescriptions[0].httpHeaders, + fakeVideoPlayerPlatform.dataSources[0].httpHeaders, {}, ); }); @@ -230,16 +227,16 @@ void main() { await controller.initialize(); expect( - fakeVideoPlayerPlatform.dataSourceDescriptions[0].uri, + fakeVideoPlayerPlatform.dataSources[0].uri, 'https://127.0.0.1', ); expect( - fakeVideoPlayerPlatform.dataSourceDescriptions[0].formatHint, - 'dash', + fakeVideoPlayerPlatform.dataSources[0].formatHint, + VideoFormat.dash, ); expect( - fakeVideoPlayerPlatform.dataSourceDescriptions[0].httpHeaders, - {}, + fakeVideoPlayerPlatform.dataSources[0].httpHeaders, + {}, ); }); @@ -251,15 +248,15 @@ void main() { await controller.initialize(); expect( - fakeVideoPlayerPlatform.dataSourceDescriptions[0].uri, + fakeVideoPlayerPlatform.dataSources[0].uri, 'https://127.0.0.1', ); expect( - fakeVideoPlayerPlatform.dataSourceDescriptions[0].formatHint, + fakeVideoPlayerPlatform.dataSources[0].formatHint, null, ); expect( - fakeVideoPlayerPlatform.dataSourceDescriptions[0].httpHeaders, + fakeVideoPlayerPlatform.dataSources[0].httpHeaders, {'Authorization': 'Bearer token'}, ); }); @@ -268,15 +265,12 @@ void main() { final VideoPlayerController controller = VideoPlayerController.network( 'http://testing.com/invalid_url', ); - try { - late dynamic error; - fakeVideoPlayerPlatform.forceInitError = true; - await controller.initialize().catchError((dynamic e) => error = e); - final PlatformException platformEx = error; - expect(platformEx.code, equals('VideoError')); - } finally { - fakeVideoPlayerPlatform.forceInitError = false; - } + + late dynamic error; + fakeVideoPlayerPlatform.forceInitError = true; + await controller.initialize().catchError((dynamic e) => error = e); + final PlatformException platformEx = error; + expect(platformEx.code, equals('VideoError')); }); test('file', () async { @@ -284,8 +278,7 @@ void main() { VideoPlayerController.file(File('a.avi')); await controller.initialize(); - expect(fakeVideoPlayerPlatform.dataSourceDescriptions[0].uri, - 'file://a.avi'); + expect(fakeVideoPlayerPlatform.dataSources[0].uri, 'file://a.avi'); }); }); @@ -294,8 +287,7 @@ void main() { VideoPlayerController.contentUri(Uri.parse('content://video')); await controller.initialize(); - expect(fakeVideoPlayerPlatform.dataSourceDescriptions[0].uri, - 'content://video'); + expect(fakeVideoPlayerPlatform.dataSources[0].uri, 'content://video'); }); test('dispose', () async { @@ -571,11 +563,11 @@ void main() { expect(controller.value.isPlaying, isFalse); await controller.play(); expect(controller.value.isPlaying, isTrue); - final FakeVideoEventStream fakeVideoEventStream = + final StreamController fakeVideoEventStream = fakeVideoPlayerPlatform.streams[controller.textureId]!; - fakeVideoEventStream.eventsChannel - .sendEvent({'event': 'completed'}); + fakeVideoEventStream + .add(VideoEvent(eventType: VideoEventType.completed)); await tester.pumpAndSettle(); expect(controller.value.isPlaying, isFalse); @@ -589,30 +581,30 @@ void main() { await controller.initialize(); expect(controller.value.isBuffering, false); expect(controller.value.buffered, isEmpty); - final FakeVideoEventStream fakeVideoEventStream = + final StreamController fakeVideoEventStream = fakeVideoPlayerPlatform.streams[controller.textureId]!; - fakeVideoEventStream.eventsChannel - .sendEvent({'event': 'bufferingStart'}); + fakeVideoEventStream + .add(VideoEvent(eventType: VideoEventType.bufferingStart)); await tester.pumpAndSettle(); expect(controller.value.isBuffering, isTrue); const Duration bufferStart = Duration(seconds: 0); const Duration bufferEnd = Duration(milliseconds: 500); - fakeVideoEventStream.eventsChannel.sendEvent({ - 'event': 'bufferingUpdate', - 'values': >[ - [bufferStart.inMilliseconds, bufferEnd.inMilliseconds] - ], - }); + fakeVideoEventStream + ..add(VideoEvent( + eventType: VideoEventType.bufferingUpdate, + buffered: [ + DurationRange(bufferStart, bufferEnd), + ])); await tester.pumpAndSettle(); expect(controller.value.isBuffering, isTrue); expect(controller.value.buffered.length, 1); expect(controller.value.buffered[0].toString(), DurationRange(bufferStart, bufferEnd).toString()); - fakeVideoEventStream.eventsChannel - .sendEvent({'event': 'bufferingEnd'}); + fakeVideoEventStream + .add(VideoEvent(eventType: VideoEventType.bufferingEnd)); await tester.pumpAndSettle(); expect(controller.value.isBuffering, isFalse); }); @@ -807,155 +799,88 @@ void main() { }); } -class FakeVideoPlayerPlatform extends TestHostVideoPlayerApi { - FakeVideoPlayerPlatform() { - TestHostVideoPlayerApi.setup(this); - } - +class FakeVideoPlayerPlatform extends VideoPlayerPlatform { Completer initialized = Completer(); List calls = []; - List dataSourceDescriptions = []; - final Map streams = {}; + List dataSources = []; + final Map> streams = + >{}; bool forceInitError = false; int nextTextureId = 0; final Map _positions = {}; @override - TextureMessage create(CreateMessage arg) { + Future create(DataSource dataSource) async { calls.add('create'); - streams[nextTextureId] = FakeVideoEventStream( - nextTextureId, 100, 100, const Duration(seconds: 1), forceInitError); - TextureMessage result = TextureMessage(); - result.textureId = nextTextureId++; - dataSourceDescriptions.add(arg); - return result; + StreamController stream = StreamController(); + streams[nextTextureId] = stream; + if (forceInitError) { + stream.addError(PlatformException( + code: 'VideoError', message: 'Video player had error XYZ')); + } else { + stream.add(VideoEvent( + eventType: VideoEventType.initialized, + size: Size(100, 100), + duration: Duration(seconds: 1))); + } + dataSources.add(dataSource); + return nextTextureId++; } @override - void dispose(TextureMessage arg) { + Future dispose(int textureId) async { calls.add('dispose'); } @override - void initialize() { + Future init() async { calls.add('init'); initialized.complete(true); } + Stream videoEventsFor(int textureId) { + return streams[textureId]!.stream; + } + @override - void pause(TextureMessage arg) { + Future pause(int textureId) async { calls.add('pause'); } @override - void play(TextureMessage arg) { + Future play(int textureId) async { calls.add('play'); } @override - PositionMessage position(TextureMessage arg) { + Future getPosition(int textureId) async { calls.add('position'); - final Duration position = - _positions[arg.textureId] ?? const Duration(seconds: 0); - return PositionMessage()..position = position.inMilliseconds; + return _positions[textureId] ?? const Duration(seconds: 0); } @override - void seekTo(PositionMessage arg) { + Future seekTo(int textureId, Duration position) async { calls.add('seekTo'); - _positions[arg.textureId!] = Duration(milliseconds: arg.position!); + _positions[textureId] = position; } @override - void setLooping(LoopingMessage arg) { + Future setLooping(int textureId, bool looping) async { calls.add('setLooping'); } @override - void setVolume(VolumeMessage arg) { + Future setVolume(int textureId, double volume) async { calls.add('setVolume'); } @override - void setPlaybackSpeed(PlaybackSpeedMessage arg) { + Future setPlaybackSpeed(int textureId, double speed) async { calls.add('setPlaybackSpeed'); } @override - void setMixWithOthers(MixWithOthersMessage arg) { + Future setMixWithOthers(bool mixWithOthers) async { calls.add('setMixWithOthers'); } } - -class FakeVideoEventStream { - FakeVideoEventStream(this.textureId, this.width, this.height, this.duration, - this.initWithError) { - eventsChannel = FakeEventsChannel( - 'flutter.io/videoPlayer/videoEvents$textureId', onListen); - } - - int textureId; - int width; - int height; - Duration duration; - bool initWithError; - late FakeEventsChannel eventsChannel; - - void onListen() { - if (!initWithError) { - eventsChannel.sendEvent({ - 'event': 'initialized', - 'duration': duration.inMilliseconds, - 'width': width, - 'height': height, - }); - } else { - eventsChannel.sendError('VideoError', 'Video player had error XYZ'); - } - } -} - -class FakeEventsChannel { - FakeEventsChannel(String name, this.onListen) { - eventsMethodChannel = MethodChannel(name); - eventsMethodChannel.setMockMethodCallHandler(onMethodCall); - } - - late MethodChannel eventsMethodChannel; - VoidCallback onListen; - - Future onMethodCall(MethodCall call) { - switch (call.method) { - case 'listen': - onListen(); - break; - } - return Future.sync(() {}); - } - - void sendEvent(dynamic event) { - _sendMessage(const StandardMethodCodec().encodeSuccessEnvelope(event)); - } - - void sendError(String code, [String? message, dynamic details]) { - _sendMessage(const StandardMethodCodec().encodeErrorEnvelope( - code: code, - message: message, - details: details, - )); - } - - void _sendMessage(ByteData data) { - _ambiguate(ServicesBinding.instance)! - .defaultBinaryMessenger - .handlePlatformMessage( - eventsMethodChannel.name, data, (ByteData? data) {}); - } -} - -/// This allows a value of type T or T? to be treated as a value of type T?. -/// -/// We use this so that APIs that have become non-nullable can still be used -/// with `!` and `?` on the stable branch. -// TODO(ianh): Remove this once we roll stable in late 2021. -T? _ambiguate(T? value) => value; From 73cb8a7c7fa74686cb3fb60cb8688b90d1a73e62 Mon Sep 17 00:00:00 2001 From: Maurice Parrish Date: Fri, 17 Dec 2021 10:54:02 -0500 Subject: [PATCH 069/600] Prevent setting user agent string to default value on every rebuild (#4618) --- .../webview_flutter_android/CHANGELOG.md | 5 ++++ .../lib/webview_android_widget.dart | 7 ++--- .../webview_flutter_android/pubspec.yaml | 2 +- .../test/webview_android_widget_test.dart | 26 +++++++++++++++++++ 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 7bd33879cd7d..1337bab304b8 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.8.1 + +* Fixes bug where the default user agent string was being set for every rebuild. See + https://github.com/flutter/flutter/issues/94847. + ## 2.8.0 * Implements new cookie manager for setting cookies and providing initial cookies. diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart index 1dec9c105741..de55b5207a84 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart @@ -418,11 +418,12 @@ class WebViewAndroidPlatformController extends WebViewPlatformController { } Future _setUserAgent(WebSetting userAgent) { - if (userAgent.isPresent && userAgent.value != null) { - return webView.settings.setUserAgentString(userAgent.value!); + if (userAgent.isPresent) { + // If the string is empty, the system default value will be used. + return webView.settings.setUserAgentString(userAgent.value ?? ''); } - return webView.settings.setUserAgentString(''); + return Future.value(); } Future _setZoomEnabled(bool zoomEnabled) { diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 34bea570ae43..0b78c72ecc4c 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_android description: A Flutter plugin that provides a WebView widget on Android. repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.8.0 +version: 2.8.1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart index c203ef04a2ce..2b25022f9087 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart @@ -483,6 +483,32 @@ void main() { }); }); + testWidgets('no update to userAgentString when there is no change', + (WidgetTester tester) async { + await buildWidget(tester); + + reset(mockWebSettings); + + await testController.updateSettings(WebSettings( + userAgent: const WebSetting.absent(), + )); + + verifyNever(mockWebSettings.setUserAgentString(any)); + }); + + testWidgets('update null userAgentString with empty string', + (WidgetTester tester) async { + await buildWidget(tester); + + reset(mockWebSettings); + + await testController.updateSettings(WebSettings( + userAgent: const WebSetting.of(null), + )); + + verify(mockWebSettings.setUserAgentString('')); + }); + testWidgets('currentUrl', (WidgetTester tester) async { await buildWidget(tester); From d383fada852f4924e37d6ee9673950088413bac3 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Fri, 17 Dec 2021 17:59:21 +0100 Subject: [PATCH 070/600] [webview_flutter] Enable setAllowFileAccess on Android setting when loading files (#4601) --- .../webview_flutter_android/CHANGELOG.md | 4 +++ .../GeneratedAndroidWebView.java | 33 +++++++++++++++++++ .../WebSettingsHostApiImpl.java | 6 ++++ .../lib/src/android_webview.dart | 10 ++++++ .../lib/src/android_webview.pigeon.dart | 25 ++++++++++++++ .../lib/src/android_webview_api_impls.dart | 11 +++++++ .../lib/webview_android_widget.dart | 1 + .../pigeons/android_webview.dart | 2 ++ .../webview_flutter_android/pubspec.yaml | 2 +- .../test/android_webview.pigeon.dart | 23 +++++++++++++ .../test/android_webview_test.dart | 8 +++++ .../test/android_webview_test.mocks.dart | 8 ++--- .../test/webview_android_widget_test.dart | 9 +++++ .../webview_android_widget_test.mocks.dart | 9 ++--- 14 files changed, 142 insertions(+), 9 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 1337bab304b8..818a13439c95 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.8.2 + +* Adds the `WebSettings.setAllowFileAccess()` method and ensure that file access is allowed when the `WebViewAndroidWidget.loadFile()` method is executed. + ## 2.8.1 * Fixes bug where the default user agent string was being set for every rebuild. See diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java index 8ef0b8d11f96..15b78b718115 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java @@ -1152,6 +1152,8 @@ public interface WebSettingsHostApi { void setBuiltInZoomControls(Long instanceId, Boolean enabled); + void setAllowFileAccess(Long instanceId, Boolean enabled); + /** The codec used by WebSettingsHostApi. */ static MessageCodec getCodec() { return WebSettingsHostApiCodec.INSTANCE; @@ -1556,6 +1558,37 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { channel.setMessageHandler(null); } } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.WebSettingsHostApi.setAllowFileAccess", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number instanceIdArg = (Number) args.get(0); + if (instanceIdArg == null) { + throw new NullPointerException("instanceIdArg unexpectedly null."); + } + Boolean enabledArg = (Boolean) args.get(1); + if (enabledArg == null) { + throw new NullPointerException("enabledArg unexpectedly null."); + } + api.setAllowFileAccess(instanceIdArg.longValue(), enabledArg); + wrapped.put("result", null); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } } } diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebSettingsHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebSettingsHostApiImpl.java index 239ef473b546..b168e206214f 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebSettingsHostApiImpl.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebSettingsHostApiImpl.java @@ -118,4 +118,10 @@ public void setBuiltInZoomControls(Long instanceId, Boolean enabled) { final WebSettings webSettings = (WebSettings) instanceManager.getInstance(instanceId); webSettings.setBuiltInZoomControls(enabled); } + + @Override + public void setAllowFileAccess(Long instanceId, Boolean enabled) { + final WebSettings webSettings = (WebSettings) instanceManager.getInstance(instanceId); + webSettings.setAllowFileAccess(enabled); + } } diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart index dfa05cd92ee6..10989321a9bb 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart @@ -546,6 +546,16 @@ class WebSettings { Future setBuiltInZoomControls(bool enabled) { return api.setBuiltInZoomControlsFromInstance(this, enabled); } + + /// Enables or disables file access within WebView. + /// + /// This enables or disables file system access only. Assets and resources are + /// still accessible using file:///android_asset and file:///android_res. The + /// default value is true for apps targeting Build.VERSION_CODES.Q and below, + /// and false when targeting Build.VERSION_CODES.R and above. + Future setAllowFileAccess(bool enabled) { + return api.setAllowFileAccessFromInstance(this, enabled); + } } /// Exposes a channel to receive calls from javaScript. diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart index 810a71732e7b..20391c43d966 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart @@ -1179,6 +1179,31 @@ class WebSettingsHostApi { return; } } + + Future setAllowFileAccess(int arg_instanceId, bool arg_enabled) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WebSettingsHostApi.setAllowFileAccess', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_enabled]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } } class _JavaScriptChannelHostApiCodec extends StandardMessageCodec { diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart index 1db5ed449c56..ead60f6a2b35 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart @@ -437,6 +437,17 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { enabled, ); } + + /// Helper method to convert instances ids to objects. + Future setAllowFileAccessFromInstance( + WebSettings instance, + bool enabled, + ) { + return setAllowFileAccess( + instanceManager.getInstanceId(instance)!, + enabled, + ); + } } /// Host api implementation for [JavaScriptChannel]. diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart index de55b5207a84..bf85ac97687e 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart @@ -179,6 +179,7 @@ class WebViewAndroidPlatformController extends WebViewPlatformController { ? absoluteFilePath : 'file://$absoluteFilePath'; + webView.settings.setAllowFileAccess(true); return webView.loadUrl(url, {}); } diff --git a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart index 36862f7cbacc..b29835266717 100644 --- a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart +++ b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart @@ -132,6 +132,8 @@ abstract class WebSettingsHostApi { void setDisplayZoomControls(int instanceId, bool enabled); void setBuiltInZoomControls(int instanceId, bool enabled); + + void setAllowFileAccess(int instanceId, bool enabled); } @HostApi(dartHostTestHandler: 'TestJavaScriptChannelHostApi') diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 0b78c72ecc4c..8905d7fb66e2 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_android description: A Flutter plugin that provides a WebView widget on Android. repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.8.1 +version: 2.8.2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart index 1e47c79d32b7..720fe408d96c 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart @@ -649,6 +649,7 @@ abstract class TestWebSettingsHostApi { void setUseWideViewPort(int instanceId, bool use); void setDisplayZoomControls(int instanceId, bool enabled); void setBuiltInZoomControls(int instanceId, bool enabled); + void setAllowFileAccess(int instanceId, bool enabled); static void setup(TestWebSettingsHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -940,6 +941,28 @@ abstract class TestWebSettingsHostApi { }); } } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WebSettingsHostApi.setAllowFileAccess', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setAllowFileAccess was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setAllowFileAccess was null, expected non-null int.'); + final bool? arg_enabled = (args[1] as bool?); + assert(arg_enabled != null, + 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setAllowFileAccess was null, expected non-null bool.'); + api.setAllowFileAccess(arg_instanceId!, arg_enabled!); + return {}; + }); + } + } } } diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart index cc29fc755067..8688a1977d83 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart @@ -430,6 +430,14 @@ void main() { true, )); }); + + test('setAllowFileAccess', () { + webSettings.setAllowFileAccess(true); + verify(mockPlatformHostApi.setAllowFileAccess( + webSettingsInstanceId, + true, + )); + }); }); group('$JavaScriptChannel', () { diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart index d25d2338886b..2134de54a415 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart @@ -1,7 +1,3 @@ -// 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. - // Mocks generated by Mockito 5.0.16 from annotations // in webview_flutter_android/test/android_webview_test.dart. // Do not manually edit this file. @@ -211,6 +207,10 @@ class MockTestWebSettingsHostApi extends _i1.Mock Invocation.method(#setBuiltInZoomControls, [instanceId, enabled]), returnValueForMissingStub: null); @override + void setAllowFileAccess(int? instanceId, bool? enabled) => super.noSuchMethod( + Invocation.method(#setAllowFileAccess, [instanceId, enabled]), + returnValueForMissingStub: null); + @override String toString() => super.toString(); } diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart index 2b25022f9087..fed1c1113e55 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart @@ -304,6 +304,15 @@ void main() { )); }); + testWidgets('loadFile should setAllowFileAccess to true', + (WidgetTester tester) async { + await buildWidget(tester); + + await testController.loadFile('file:///path/to/file.html'); + + verify(mockWebSettings.setAllowFileAccess(true)); + }); + testWidgets('loadFlutterAsset', (WidgetTester tester) async { await buildWidget(tester); const String assetKey = 'test_assets/index.html'; diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart index ece17ad61cb8..12e993bafa31 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart @@ -1,7 +1,3 @@ -// 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. - // Mocks generated by Mockito 5.0.16 from annotations // in webview_flutter_android/test/webview_android_widget_test.dart. // Do not manually edit this file. @@ -120,6 +116,11 @@ class MockWebSettings extends _i1.Mock implements _i2.WebSettings { returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i4.Future); @override + _i4.Future setAllowFileAccess(bool? enabled) => + (super.noSuchMethod(Invocation.method(#setAllowFileAccess, [enabled]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override String toString() => super.toString(); } From 29b637301dfbafe7f84c0c6166f2b5c4dd186697 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Sun, 19 Dec 2021 14:35:48 -0500 Subject: [PATCH 071/600] [ci] Move Android build-all CI to heavy workload (#4624) This task's memory usage is very close to the 4GB limit of the light-workload machines, and sometimes goes over and causes flaky failures. --- .cirrus.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index bcd0e49327a3..50b6cca0362a 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -128,14 +128,6 @@ task: # Restore the tree to a clean state, to avoid accidental issues if # other script steps are added to this task. - git checkout . - ### Android tasks ### - - name: android-build_all_plugins - env: - BUILD_ALL_ARGS: "apk" - matrix: - CHANNEL: "master" - CHANNEL: "stable" - << : *BUILD_ALL_PLUGINS_APP_TEMPLATE ### Web tasks ### - name: web-build_all_plugins env: @@ -238,6 +230,13 @@ task: path: "**/reports/lint-results-debug.xml" type: text/xml format: android-lint + - name: android-build_all_plugins + env: + BUILD_ALL_ARGS: "apk" + matrix: + CHANNEL: "master" + CHANNEL: "stable" + << : *BUILD_ALL_PLUGINS_APP_TEMPLATE ### Web tasks ### - name: web-platform_tests env: From 6531740363e539e1751a97c56ad5c1a87c985be9 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Sun, 19 Dec 2021 14:41:00 -0500 Subject: [PATCH 072/600] [video_player] Remove test code from platform interface (#4589) This is a breaking change to video_player_platform_interface to remove Pigeon mocks from the public interface. These should never have been there, as tests in packages outside of the platform interface should be mocking the platform interface rather than the method channel, but the app-facing tests weren't rewritten when they should have been so they ended up published to support the legacy tests. This creates an unwanted non-dev dependency on `flutter_test`, in addition to promoting an anti-pattern. While making the breaking change, this also adopts `PlatformInterface`, removing `isMock` in favor of the standard mixin pattern provided by the base class. Part of https://github.com/flutter/flutter/issues/83562 --- .../CHANGELOG.md | 8 ++++ .../lib/video_player_platform_interface.dart | 44 +++++-------------- .../pubspec.yaml | 8 ++-- .../method_channel_video_player_test.dart | 3 +- .../{lib => test}/test.dart | 3 +- 5 files changed, 25 insertions(+), 41 deletions(-) rename packages/video_player/video_player_platform_interface/{lib => test}/test.dart (99%) diff --git a/packages/video_player/video_player_platform_interface/CHANGELOG.md b/packages/video_player/video_player_platform_interface/CHANGELOG.md index b3da9c8924ef..e0e6a11065ee 100644 --- a/packages/video_player/video_player_platform_interface/CHANGELOG.md +++ b/packages/video_player/video_player_platform_interface/CHANGELOG.md @@ -1,3 +1,11 @@ +## 5.0.0 + +* **BREAKING CHANGES**: + * Updates to extending `PlatformInterface`. Removes `isMock`, in favor of the + now-standard `MockPlatformInterfaceMixin`. + * Removes test.dart from the public interface. Tests in other packages should + mock `VideoPlatformInterface` rather than the method channel. + ## 4.2.0 * Add `contentUri` to `DataSourceType`. diff --git a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart index 21ad972d8e06..66b6d709e9fe 100644 --- a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart +++ b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart @@ -2,12 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; -import 'dart:ui'; - import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; -import 'package:meta/meta.dart' show visibleForTesting; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'method_channel_video_player.dart'; @@ -18,37 +15,24 @@ import 'method_channel_video_player.dart'; /// (using `extends`) ensures that the subclass will get the default implementation, while /// platform implementations that `implements` this interface will be broken by newly added /// [VideoPlayerPlatform] methods. -abstract class VideoPlayerPlatform { - /// Only mock implementations should set this to true. - /// - /// Mockito mocks are implementing this class with `implements` which is forbidden for anything - /// other than mocks (see class docs). This property provides a backdoor for mockito mocks to - /// skip the verification that the class isn't implemented with `implements`. - @visibleForTesting - bool get isMock => false; +abstract class VideoPlayerPlatform extends PlatformInterface { + /// Constructs a VideoPlayerPlatform. + VideoPlayerPlatform() : super(token: _token); + + static final Object _token = Object(); static VideoPlayerPlatform _instance = MethodChannelVideoPlayer(); /// The default instance of [VideoPlayerPlatform] to use. /// - /// Platform-specific plugins should override this with their own - /// platform-specific class that extends [VideoPlayerPlatform] when they - /// register themselves. - /// /// Defaults to [MethodChannelVideoPlayer]. static VideoPlayerPlatform get instance => _instance; - // TODO(amirh): Extract common platform interface logic. - // https://github.com/flutter/flutter/issues/43368 + /// Platform-specific plugins should override this with their own + /// platform-specific class that extends [VideoPlayerPlatform] when they + /// register themselves. static set instance(VideoPlayerPlatform instance) { - if (!instance.isMock) { - try { - instance._verifyProvidesDefaultImplementations(); - } on NoSuchMethodError catch (_) { - throw AssertionError( - 'Platform interfaces must not be implemented with `implements`'); - } - } + PlatformInterface.verifyToken(instance, _token); _instance = instance; } @@ -119,14 +103,6 @@ abstract class VideoPlayerPlatform { Future setMixWithOthers(bool mixWithOthers) { throw UnimplementedError('setMixWithOthers() has not been implemented.'); } - - // This method makes sure that VideoPlayer isn't implemented with `implements`. - // - // See class doc for more details on why implementing this class is forbidden. - // - // This private method is called by the instance setter, which fails if the class is - // implemented with `implements`. - void _verifyProvidesDefaultImplementations() {} } /// Description of the data source used to create an instance of diff --git a/packages/video_player/video_player_platform_interface/pubspec.yaml b/packages/video_player/video_player_platform_interface/pubspec.yaml index 35b30793a20f..b8404772bffa 100644 --- a/packages/video_player/video_player_platform_interface/pubspec.yaml +++ b/packages/video_player/video_player_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/video_player issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 4.2.0 +version: 5.0.0 environment: sdk: ">=2.12.0 <3.0.0" @@ -13,9 +13,9 @@ environment: dependencies: flutter: sdk: flutter - flutter_test: - sdk: flutter - meta: ^1.3.0 + plugin_platform_interface: ^2.0.0 dev_dependencies: + flutter_test: + sdk: flutter pedantic: ^1.10.0 diff --git a/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart b/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart index f5439b844045..4d1c9b78fc34 100644 --- a/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart +++ b/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart @@ -8,9 +8,10 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:video_player_platform_interface/messages.dart'; import 'package:video_player_platform_interface/method_channel_video_player.dart'; -import 'package:video_player_platform_interface/test.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; +import 'test.dart'; + class _ApiLogger implements TestHostVideoPlayerApi { final List log = []; TextureMessage? textureMessage; diff --git a/packages/video_player/video_player_platform_interface/lib/test.dart b/packages/video_player/video_player_platform_interface/test/test.dart similarity index 99% rename from packages/video_player/video_player_platform_interface/lib/test.dart rename to packages/video_player/video_player_platform_interface/test/test.dart index b4fd81f44f41..a12ae45e59db 100644 --- a/packages/video_player/video_player_platform_interface/lib/test.dart +++ b/packages/video_player/video_player_platform_interface/test/test.dart @@ -10,8 +10,7 @@ import 'dart:async'; import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; - -import 'messages.dart'; +import 'package:video_player_platform_interface/messages.dart'; abstract class TestHostVideoPlayerApi { void initialize(); From 39722125e26fdbb2aff69d7b9eb260623150eeff Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Mon, 20 Dec 2021 11:30:17 -0800 Subject: [PATCH 073/600] [camera]fix a crash related to calling engine api in the background thread (#4608) --- packages/camera/camera/CHANGELOG.md | 6 +- .../ios/Runner.xcodeproj/project.pbxproj | 14 +++- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- .../RunnerTests/ThreadSafeEventChannelTests.m | 44 +++++++++++ .../ThreadSafeMethodChannelTests.m | 46 ++++++++++++ .../ThreadSafeTextureRegistryTests.m | 74 +++++++++++++++++++ .../camera/camera/ios/Classes/CameraPlugin.m | 27 ++++--- .../ios/Classes/FLTThreadSafeEventChannel.h | 27 +++++++ .../ios/Classes/FLTThreadSafeEventChannel.m | 29 ++++++++ .../ios/Classes/FLTThreadSafeMethodChannel.h | 27 +++++++ .../ios/Classes/FLTThreadSafeMethodChannel.m | 29 ++++++++ .../Classes/FLTThreadSafeTextureRegistry.h | 51 +++++++++++++ .../Classes/FLTThreadSafeTextureRegistry.m | 58 +++++++++++++++ .../camera/ios/Classes/camera-umbrella.h | 3 + 14 files changed, 425 insertions(+), 12 deletions(-) create mode 100644 packages/camera/camera/example/ios/RunnerTests/ThreadSafeEventChannelTests.m create mode 100644 packages/camera/camera/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m create mode 100644 packages/camera/camera/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m create mode 100644 packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.h create mode 100644 packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.m create mode 100644 packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.h create mode 100644 packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.m create mode 100644 packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.h create mode 100644 packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.m diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 1a6eceb957b1..f5b783d5114b 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,4 +1,8 @@ -## 0.9.4+5 +## NEXT + +* Fixed a crash in iOS when using image stream due to threading issue. + +## 0.9.4+5 * Fixes bug where calling a method after the camera was closed resulted in a Java `IllegalStateException` exception. * Fixes integration tests. diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj index feb789f2ecba..ad0ece9adbd2 100644 --- a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj @@ -20,6 +20,9 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */; }; + E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */; }; + E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */; }; E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */; }; F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */ = {isa = PBXBuildFile; fileRef = F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */; }; /* End PBXBuildFile section */ @@ -74,6 +77,9 @@ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9C5CC6CAD53AD388B2694F3A /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; A24F9E418BA48BCC7409B117 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeMethodChannelTests.m; sourceTree = ""; }; + E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeTextureRegistryTests.m; sourceTree = ""; }; + E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeEventChannelTests.m; sourceTree = ""; }; E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPreviewPauseTests.m; sourceTree = ""; }; F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockFLTThreadSafeFlutterResult.h; sourceTree = ""; }; F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockFLTThreadSafeFlutterResult.m; sourceTree = ""; }; @@ -107,6 +113,9 @@ 03BB766C2665316900CE5A93 /* Info.plist */, 033B94BD269C40A200B4DF97 /* CameraMethodChannelTests.m */, 03F6F8B126CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m */, + E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */, + E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */, + E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */, E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */, F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */, F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */, @@ -239,7 +248,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1100; + LastUpgradeCheck = 1300; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { 03BB76672665316900CE5A93 = { @@ -378,6 +387,9 @@ E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */, F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */, 334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */, + E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */, + E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */, + E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/camera/camera/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 1447e08231be..f4b3c1099001 100644 --- a/packages/camera/camera/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/camera/camera/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ + +@interface ThreadSafeEventChannelTests : XCTestCase +@end + +@implementation ThreadSafeEventChannelTests { + FLTThreadSafeEventChannel *_channel; + XCTestExpectation *_mainThreadExpectation; +} + +- (void)setUp { + [super setUp]; + id mockEventChannel = OCMClassMock([FlutterEventChannel class]); + + _mainThreadExpectation = + [[XCTestExpectation alloc] initWithDescription:@"invokeMethod must be called in main thread"]; + _channel = [[FLTThreadSafeEventChannel alloc] initWithEventChannel:mockEventChannel]; + + OCMStub([mockEventChannel setStreamHandler:[OCMArg any]]).andDo(^(NSInvocation *invocation) { + if (NSThread.isMainThread) { + [self->_mainThreadExpectation fulfill]; + } + }); +} + +- (void)testSetStreamHandler_shouldStayOnMainThreadIfCalledFromMainThread { + [_channel setStreamHandler:nil]; + [self waitForExpectations:@[ _mainThreadExpectation ] timeout:1]; +} + +- (void)testSetStreamHandler_shouldDispatchToMainThreadIfCalledFromBackgroundThread { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [self->_channel setStreamHandler:nil]; + }); + [self waitForExpectations:@[ _mainThreadExpectation ] timeout:1]; +} + +@end diff --git a/packages/camera/camera/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m b/packages/camera/camera/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m new file mode 100644 index 000000000000..a7cabbd92e37 --- /dev/null +++ b/packages/camera/camera/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m @@ -0,0 +1,46 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import camera; +@import XCTest; +#import + +@interface ThreadSafeMethodChannelTests : XCTestCase +@end + +@implementation ThreadSafeMethodChannelTests { + FLTThreadSafeMethodChannel *_channel; + XCTestExpectation *_mainThreadExpectation; +} + +- (void)setUp { + [super setUp]; + id mockMethodChannel = OCMClassMock([FlutterMethodChannel class]); + + _mainThreadExpectation = + [[XCTestExpectation alloc] initWithDescription:@"invokeMethod must be called in main thread"]; + _channel = [[FLTThreadSafeMethodChannel alloc] initWithMethodChannel:mockMethodChannel]; + + OCMStub([mockMethodChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]) + .andDo(^(NSInvocation *invocation) { + if (NSThread.isMainThread) { + [self->_mainThreadExpectation fulfill]; + } + }); +} + +- (void)testInvokeMethod_shouldStayOnMainThreadIfCalledFromMainThread { + [_channel invokeMethod:@"foo" arguments:nil]; + + [self waitForExpectations:@[ _mainThreadExpectation ] timeout:1]; +} + +- (void)testInvokeMethod__shouldDispatchToMainThreadIfCalledFromBackgroundThread { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [self->_channel invokeMethod:@"foo" arguments:nil]; + }); + [self waitForExpectations:@[ _mainThreadExpectation ] timeout:1]; +} + +@end diff --git a/packages/camera/camera/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m b/packages/camera/camera/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m new file mode 100644 index 000000000000..73a9fb8e3c3d --- /dev/null +++ b/packages/camera/camera/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m @@ -0,0 +1,74 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import camera; +@import XCTest; +#import + +@interface ThreadSafeTextureRegistryTests : XCTestCase +@end + +@implementation ThreadSafeTextureRegistryTests { + FLTThreadSafeTextureRegistry *_registry; + XCTestExpectation *_registerTextureExpectation; + XCTestExpectation *_unregisterTextureExpectation; + XCTestExpectation *_textureFrameAvailableExpectation; +} + +- (void)setUp { + [super setUp]; + id mockTextureRegistry = OCMProtocolMock(@protocol(FlutterTextureRegistry)); + _registry = [[FLTThreadSafeTextureRegistry alloc] initWithTextureRegistry:mockTextureRegistry]; + + _registerTextureExpectation = [[XCTestExpectation alloc] + initWithDescription:@"registerTexture must be called in main thread"]; + _unregisterTextureExpectation = [[XCTestExpectation alloc] + initWithDescription:@"unregisterTexture must be called in main thread"]; + _textureFrameAvailableExpectation = [[XCTestExpectation alloc] + initWithDescription:@"textureFrameAvailable must be called in main thread"]; + + OCMStub([mockTextureRegistry registerTexture:[OCMArg any]]).andDo(^(NSInvocation *invocation) { + if (NSThread.isMainThread) { + [self->_registerTextureExpectation fulfill]; + } + }); + + OCMStub([mockTextureRegistry unregisterTexture:0]).andDo(^(NSInvocation *invocation) { + if (NSThread.isMainThread) { + [self->_unregisterTextureExpectation fulfill]; + } + }); + + OCMStub([mockTextureRegistry textureFrameAvailable:0]).andDo(^(NSInvocation *invocation) { + if (NSThread.isMainThread) { + [self->_textureFrameAvailableExpectation fulfill]; + } + }); +} + +- (void)testShouldStayOnMainThreadIfCalledFromMainThread { + NSObject *anyTexture = OCMProtocolMock(@protocol(FlutterTexture)); + [_registry registerTextureSync:anyTexture]; + [_registry textureFrameAvailable:0]; + [_registry unregisterTexture:0]; + [self waitForExpectations:@[ + _registerTextureExpectation, _unregisterTextureExpectation, _textureFrameAvailableExpectation + ] + timeout:1]; +} + +- (void)testShouldDispatchToMainThreadIfCalledFromBackgroundThread { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSObject *anyTexture = OCMProtocolMock(@protocol(FlutterTexture)); + [self->_registry registerTextureSync:anyTexture]; + [self->_registry textureFrameAvailable:0]; + [self->_registry unregisterTexture:0]; + }); + [self waitForExpectations:@[ + _registerTextureExpectation, _unregisterTextureExpectation, _textureFrameAvailableExpectation + ] + timeout:1]; +} + +@end diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.m b/packages/camera/camera/ios/Classes/CameraPlugin.m index 2c12081da807..448727963ab3 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin.m +++ b/packages/camera/camera/ios/Classes/CameraPlugin.m @@ -10,7 +10,10 @@ #import #import #import +#import "FLTThreadSafeEventChannel.h" #import "FLTThreadSafeFlutterResult.h" +#import "FLTThreadSafeMethodChannel.h" +#import "FLTThreadSafeTextureRegistry.h" @interface FLTSavePhotoDelegate : NSObject @property(readonly, nonatomic) NSString *path; @@ -305,7 +308,7 @@ @interface FLTCam : NSObject *)messen FlutterEventChannel *eventChannel = [FlutterEventChannel eventChannelWithName:@"plugins.flutter.io/camera/imageStream" binaryMessenger:messenger]; + FLTThreadSafeEventChannel *threadSafeEventChannel = + [[FLTThreadSafeEventChannel alloc] initWithEventChannel:eventChannel]; _imageStreamHandler = [[FLTImageStreamHandler alloc] init]; - [eventChannel setStreamHandler:_imageStreamHandler]; + [threadSafeEventChannel setStreamHandler:_imageStreamHandler]; _isStreamingImages = YES; } else { @@ -1285,10 +1290,10 @@ - (void)setUpCaptureSessionForAudio { @end @interface CameraPlugin () -@property(readonly, nonatomic) NSObject *registry; +@property(readonly, nonatomic) FLTThreadSafeTextureRegistry *registry; @property(readonly, nonatomic) NSObject *messenger; @property(readonly, nonatomic) FLTCam *camera; -@property(readonly, nonatomic) FlutterMethodChannel *deviceEventMethodChannel; +@property(readonly, nonatomic) FLTThreadSafeMethodChannel *deviceEventMethodChannel; @end @implementation CameraPlugin { @@ -1308,7 +1313,7 @@ - (instancetype)initWithRegistry:(NSObject *)registry messenger:(NSObject *)messenger { self = [super init]; NSAssert(self, @"super init cannot be nil"); - _registry = registry; + _registry = [[FLTThreadSafeTextureRegistry alloc] initWithTextureRegistry:registry]; _messenger = messenger; [self initDeviceEventMethodChannel]; [self startOrientationListener]; @@ -1316,9 +1321,11 @@ - (instancetype)initWithRegistry:(NSObject *)registry } - (void)initDeviceEventMethodChannel { - _deviceEventMethodChannel = + FlutterMethodChannel *methodChannel = [FlutterMethodChannel methodChannelWithName:@"flutter.io/cameraPlugin/device" binaryMessenger:_messenger]; + _deviceEventMethodChannel = + [[FLTThreadSafeMethodChannel alloc] initWithMethodChannel:methodChannel]; } - (void)startOrientationListener { @@ -1417,7 +1424,7 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call if (_camera) { [_camera close]; } - int64_t textureId = [self.registry registerTexture:cam]; + int64_t textureId = [self.registry registerTextureSync:cam]; _camera = cam; [result sendSuccessWithData:@{ @"cameraId" : @(textureId), @@ -1446,8 +1453,10 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call methodChannelWithName:[NSString stringWithFormat:@"flutter.io/cameraPlugin/camera%lu", (unsigned long)cameraId] binaryMessenger:_messenger]; - _camera.methodChannel = methodChannel; - [methodChannel + FLTThreadSafeMethodChannel *threadSafeMethodChannel = + [[FLTThreadSafeMethodChannel alloc] initWithMethodChannel:methodChannel]; + _camera.methodChannel = threadSafeMethodChannel; + [threadSafeMethodChannel invokeMethod:@"initialized" arguments:@{ @"previewWidth" : @(_camera.previewSize.width), diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.h b/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.h new file mode 100644 index 000000000000..dd84a6733d64 --- /dev/null +++ b/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.h @@ -0,0 +1,27 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * Wrapper for FlutterEventChannel that always sends events on the main thread + */ +@interface FLTThreadSafeEventChannel : NSObject + +/** + * Creates a FLTThreadSafeEventChannel by wrapping a FlutterEventChannel object. + * @param channel The FlutterEventChannel object to be wrapped. + */ +- (instancetype)initWithEventChannel:(FlutterEventChannel *)channel; + +/* + * Registers a handler for stream setup requests from the Flutter side on main thread. + */ +- (void)setStreamHandler:(nullable NSObject *)handler; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.m b/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.m new file mode 100644 index 000000000000..418d83c37849 --- /dev/null +++ b/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.m @@ -0,0 +1,29 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FLTThreadSafeEventChannel.h" + +@implementation FLTThreadSafeEventChannel { + FlutterEventChannel *_channel; +} + +- (instancetype)initWithEventChannel:(FlutterEventChannel *)channel { + self = [super init]; + if (self) { + _channel = channel; + } + return self; +} + +- (void)setStreamHandler:(NSObject *)handler { + if (!NSThread.isMainThread) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self->_channel setStreamHandler:handler]; + }); + } else { + [_channel setStreamHandler:handler]; + } +} + +@end diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.h b/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.h new file mode 100644 index 000000000000..db99d0f7f66a --- /dev/null +++ b/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.h @@ -0,0 +1,27 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * Wrapper for FlutterMethodChannel that always invokes messages on the main thread + */ +@interface FLTThreadSafeMethodChannel : NSObject + +/** + * Creates a FLTThreadSafeMethodChannel by wrapping a FlutterMethodChannel object. + * @param channel The FlutterMethodChannel object to be wrapped. + */ +- (instancetype)initWithMethodChannel:(FlutterMethodChannel *)channel; + +/** + * Invokes the specified flutter method with the specified arguments on main thread. + */ +- (void)invokeMethod:(NSString *)method arguments:(nullable id)arguments; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.m b/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.m new file mode 100644 index 000000000000..e62fd5bb3af8 --- /dev/null +++ b/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.m @@ -0,0 +1,29 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FLTThreadSafeMethodChannel.h" + +@implementation FLTThreadSafeMethodChannel { + FlutterMethodChannel *_channel; +} + +- (instancetype)initWithMethodChannel:(FlutterMethodChannel *)channel { + self = [super init]; + if (self) { + _channel = channel; + } + return self; +} + +- (void)invokeMethod:(NSString *)method arguments:(id)arguments { + if (!NSThread.isMainThread) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self->_channel invokeMethod:method arguments:arguments]; + }); + } else { + [_channel invokeMethod:method arguments:arguments]; + } +} + +@end diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.h b/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.h new file mode 100644 index 000000000000..34ee4400bcc1 --- /dev/null +++ b/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.h @@ -0,0 +1,51 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * Wrapper for FlutterTextureRegistry that always sends events on the main thread + */ +@interface FLTThreadSafeTextureRegistry : NSObject + +/** + * Creates a FLTThreadSafeTextureRegistry by wrapping an object conforming to + * FlutterTextureRegistry. + * @param registry The FlutterTextureRegistry object to be wrapped. + */ +- (instancetype)initWithTextureRegistry:(NSObject *)registry; + +/** + * Registers a `FlutterTexture` for usage in Flutter and returns an id that can be used to reference + * that texture when calling into Flutter with channels. Textures must be registered on the + * main thread. On success returns the pointer to the registered texture, else returns 0. + * + * Runs on main thread. + */ +- (int64_t)registerTextureSync:(NSObject *)texture; + +/** + * Notifies Flutter that the content of the previously registered texture has been updated. + * + * This will trigger a call to `-[FlutterTexture copyPixelBuffer]` on the raster thread. + * + * Runs on main thread. + */ +- (void)textureFrameAvailable:(int64_t)textureId; + +/** + * Unregisters a `FlutterTexture` that has previously regeistered with `registerTexture:`. Textures + * must be unregistered on the main thread. + * + * Runs on main thread. + * + * @param textureId The result that was previously returned from `registerTexture:`. + */ +- (void)unregisterTexture:(int64_t)textureId; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.m b/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.m new file mode 100644 index 000000000000..103c61cce6fd --- /dev/null +++ b/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.m @@ -0,0 +1,58 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FLTThreadSafeTextureRegistry.h" + +@implementation FLTThreadSafeTextureRegistry { + NSObject *_registry; +} + +- (instancetype)initWithTextureRegistry:(NSObject *)registry { + self = [super init]; + if (self) { + _registry = registry; + } + return self; +} + +- (int64_t)registerTextureSync:(NSObject *)texture { + if (!NSThread.isMainThread) { + __block int64_t textureId; + // We cannot use async API (with completion block) because completion block does not work for + // separate functions (e.g. `dispose` and `create` are separately registered functions). It's + // hard to tell if the developers had made implicit assumption of the synchronous nature of the + // original API when implementing this plugin. Use dispatch_sync to keep + // FlutterTextureRegistry's sychronous API, so that we don't introduce new potential race + // conditions. We do not break priority inversion here since it's the background thread waiting + // for main thread. + dispatch_sync(dispatch_get_main_queue(), ^{ + textureId = [self->_registry registerTexture:texture]; + }); + return textureId; + } else { + return [_registry registerTexture:texture]; + } +} + +- (void)textureFrameAvailable:(int64_t)textureId { + if (!NSThread.isMainThread) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self->_registry textureFrameAvailable:textureId]; + }); + } else { + [_registry textureFrameAvailable:textureId]; + } +} + +- (void)unregisterTexture:(int64_t)textureId { + if (!NSThread.isMainThread) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self->_registry unregisterTexture:textureId]; + }); + } else { + [_registry unregisterTexture:textureId]; + } +} + +@end diff --git a/packages/camera/camera/ios/Classes/camera-umbrella.h b/packages/camera/camera/ios/Classes/camera-umbrella.h index b0fd493b24df..c55276e31147 100644 --- a/packages/camera/camera/ios/Classes/camera-umbrella.h +++ b/packages/camera/camera/ios/Classes/camera-umbrella.h @@ -4,7 +4,10 @@ #import #import +#import #import +#import +#import FOUNDATION_EXPORT double cameraVersionNumber; FOUNDATION_EXPORT const unsigned char cameraVersionString[]; From c9c234a53e563a0312f96ca7a77a8d015bfaab5f Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 20 Dec 2021 14:41:55 -0500 Subject: [PATCH 074/600] [video_player] Add 5.0 interface support (#4627) Allows the other video_player packages to use either 4.x or 5.x of `video_player_platform_interface`, since the only breaking change doesn't affect them (as it just removes test code that they no longer use). This allows clients of `video_player` to no longer have a transitive dependency on test packages, but doesn't create any version lock with any unendorsed implementations that might exist. Fixes https://github.com/flutter/flutter/issues/83562 --- .../video_player/video_player/CHANGELOG.md | 5 ++ .../video_player/video_player/CONTRIBUTING.md | 49 ------------------- .../video_player/video_player/pubspec.yaml | 4 +- .../video_player_web/CHANGELOG.md | 7 ++- .../video_player_web/pubspec.yaml | 4 +- 5 files changed, 15 insertions(+), 54 deletions(-) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 6f2a2b7250d0..b87ac4bbeded 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.2.9 + +* Adds compatibility with `video_player_platform_interface` 5.0, which does not + include non-dev test dependencies. + ## 2.2.8 * Changes the way the `VideoPlayerPlatform` instance is cached in the diff --git a/packages/video_player/video_player/CONTRIBUTING.md b/packages/video_player/video_player/CONTRIBUTING.md index 15c48038f6fc..387551bda2f6 100644 --- a/packages/video_player/video_player/CONTRIBUTING.md +++ b/packages/video_player/video_player/CONTRIBUTING.md @@ -31,52 +31,3 @@ pigeon, not your version or the version on master. In either case, the configuration will be obtained automatically from the `pigeons/messages.dart` file (see `configurePigeon` at the bottom of that file). - -While contributing, you may also want to set the following dependency -overrides: - -```yaml -dependency_overrides: - video_player_platform_interface: - path: - ../video_player_platform_interface - video_player_web: - path: - ../video_player_web -``` - -## Publishing plugin updates that span multiple plugin packages - -If your change affects both the interface package and the -implementation packages, then you will need to publish a version of -the plugin in between landing the interface changes and the -implementation changes, since the implementations depend on the -interface via pub. - -To do this, follow these steps: - -1. Create a PR that has all the changes, and update the -`pubspec.yaml`s to have path-based dependency overrides as described -in the "Updating pigeon-generated files" section above. - -2. Upload that PR and get it reviewed and into a state where the only -test failure is the one complaining that you can't publish a package -that has dependency overrides. - -3. Create a PR that's a subset of the one in the previous step that -only includes the interface changes, with no dependency overrides, and -submit that. - -4. Once you have had that reviewed and landed, publish the interface -parts of the plugin to pub. - -5. Now, update the original full PR to not use dependency overrides -but to instead refer to the new version of the plugin, and sync it to -master (so that the interface changes are gone from the PR). Submit -that PR. - -6. Once you have had _that_ PR reviewed and landed, publish the -implementation parts of the plugin to pub. - -You may need to publish each implementation package independently of -the main package also, depending on exactly what your change entails. diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index f8c091786526..3ae9c0dfd301 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for displaying inline video with other Flutter widgets on Android, iOS, and web. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.2.8 +version: 2.2.9 environment: sdk: ">=2.14.0 <3.0.0" @@ -24,7 +24,7 @@ dependencies: flutter: sdk: flutter meta: ^1.3.0 - video_player_platform_interface: ^4.2.0 + video_player_platform_interface: ">=4.2.0 <6.0.0" video_player_web: ^2.0.0 html: ^0.15.0 diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md index 4eb7c9d610b5..13cbf2e24dae 100644 --- a/packages/video_player/video_player_web/CHANGELOG.md +++ b/packages/video_player/video_player_web/CHANGELOG.md @@ -1,7 +1,12 @@ +## 2.0.5 + +* Adds compatibility with `video_player_platform_interface` 5.0, which does not + include non-dev test dependencies. + ## 2.0.4 * Adopt `video_player_platform_interface` 4.2 and opt out of `contentUri` data source. - + ## 2.0.3 * Add `implements` to pubspec. diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml index ac0754b1a5d0..ec2377ea1fb1 100644 --- a/packages/video_player/video_player_web/pubspec.yaml +++ b/packages/video_player/video_player_web/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_web description: Web platform implementation of video_player. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.0.4 +version: 2.0.5 environment: sdk: ">=2.12.0 <3.0.0" @@ -23,7 +23,7 @@ dependencies: sdk: flutter meta: ^1.3.0 pedantic: ^1.10.0 - video_player_platform_interface: ^4.2.0 + video_player_platform_interface: ">=4.2.0 <6.0.0" dev_dependencies: flutter_test: From 19468e0950f5ec237ca9319c20055ff08ec963c3 Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Mon, 20 Dec 2021 14:44:17 -0800 Subject: [PATCH 075/600] Revert "[camera]Fix crash due to calling engine APIs from background thread" (#4630) --- packages/camera/camera/CHANGELOG.md | 6 +- .../ios/Runner.xcodeproj/project.pbxproj | 14 +--- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- .../RunnerTests/ThreadSafeEventChannelTests.m | 44 ----------- .../ThreadSafeMethodChannelTests.m | 46 ------------ .../ThreadSafeTextureRegistryTests.m | 74 ------------------- .../camera/camera/ios/Classes/CameraPlugin.m | 27 +++---- .../ios/Classes/FLTThreadSafeEventChannel.h | 27 ------- .../ios/Classes/FLTThreadSafeEventChannel.m | 29 -------- .../ios/Classes/FLTThreadSafeMethodChannel.h | 27 ------- .../ios/Classes/FLTThreadSafeMethodChannel.m | 29 -------- .../Classes/FLTThreadSafeTextureRegistry.h | 51 ------------- .../Classes/FLTThreadSafeTextureRegistry.m | 58 --------------- .../camera/ios/Classes/camera-umbrella.h | 3 - 14 files changed, 12 insertions(+), 425 deletions(-) delete mode 100644 packages/camera/camera/example/ios/RunnerTests/ThreadSafeEventChannelTests.m delete mode 100644 packages/camera/camera/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m delete mode 100644 packages/camera/camera/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m delete mode 100644 packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.h delete mode 100644 packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.m delete mode 100644 packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.h delete mode 100644 packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.m delete mode 100644 packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.h delete mode 100644 packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.m diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index f5b783d5114b..1a6eceb957b1 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,8 +1,4 @@ -## NEXT - -* Fixed a crash in iOS when using image stream due to threading issue. - -## 0.9.4+5 +## 0.9.4+5 * Fixes bug where calling a method after the camera was closed resulted in a Java `IllegalStateException` exception. * Fixes integration tests. diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj index ad0ece9adbd2..feb789f2ecba 100644 --- a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj @@ -20,9 +20,6 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */; }; - E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */; }; - E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */; }; E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */; }; F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */ = {isa = PBXBuildFile; fileRef = F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */; }; /* End PBXBuildFile section */ @@ -77,9 +74,6 @@ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9C5CC6CAD53AD388B2694F3A /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; A24F9E418BA48BCC7409B117 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; - E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeMethodChannelTests.m; sourceTree = ""; }; - E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeTextureRegistryTests.m; sourceTree = ""; }; - E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeEventChannelTests.m; sourceTree = ""; }; E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPreviewPauseTests.m; sourceTree = ""; }; F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockFLTThreadSafeFlutterResult.h; sourceTree = ""; }; F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockFLTThreadSafeFlutterResult.m; sourceTree = ""; }; @@ -113,9 +107,6 @@ 03BB766C2665316900CE5A93 /* Info.plist */, 033B94BD269C40A200B4DF97 /* CameraMethodChannelTests.m */, 03F6F8B126CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m */, - E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */, - E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */, - E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */, E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */, F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */, F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */, @@ -248,7 +239,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1100; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { 03BB76672665316900CE5A93 = { @@ -387,9 +378,6 @@ E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */, F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */, 334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */, - E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */, - E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */, - E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/camera/camera/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index f4b3c1099001..1447e08231be 100644 --- a/packages/camera/camera/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/camera/camera/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ - -@interface ThreadSafeEventChannelTests : XCTestCase -@end - -@implementation ThreadSafeEventChannelTests { - FLTThreadSafeEventChannel *_channel; - XCTestExpectation *_mainThreadExpectation; -} - -- (void)setUp { - [super setUp]; - id mockEventChannel = OCMClassMock([FlutterEventChannel class]); - - _mainThreadExpectation = - [[XCTestExpectation alloc] initWithDescription:@"invokeMethod must be called in main thread"]; - _channel = [[FLTThreadSafeEventChannel alloc] initWithEventChannel:mockEventChannel]; - - OCMStub([mockEventChannel setStreamHandler:[OCMArg any]]).andDo(^(NSInvocation *invocation) { - if (NSThread.isMainThread) { - [self->_mainThreadExpectation fulfill]; - } - }); -} - -- (void)testSetStreamHandler_shouldStayOnMainThreadIfCalledFromMainThread { - [_channel setStreamHandler:nil]; - [self waitForExpectations:@[ _mainThreadExpectation ] timeout:1]; -} - -- (void)testSetStreamHandler_shouldDispatchToMainThreadIfCalledFromBackgroundThread { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [self->_channel setStreamHandler:nil]; - }); - [self waitForExpectations:@[ _mainThreadExpectation ] timeout:1]; -} - -@end diff --git a/packages/camera/camera/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m b/packages/camera/camera/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m deleted file mode 100644 index a7cabbd92e37..000000000000 --- a/packages/camera/camera/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -@import camera; -@import XCTest; -#import - -@interface ThreadSafeMethodChannelTests : XCTestCase -@end - -@implementation ThreadSafeMethodChannelTests { - FLTThreadSafeMethodChannel *_channel; - XCTestExpectation *_mainThreadExpectation; -} - -- (void)setUp { - [super setUp]; - id mockMethodChannel = OCMClassMock([FlutterMethodChannel class]); - - _mainThreadExpectation = - [[XCTestExpectation alloc] initWithDescription:@"invokeMethod must be called in main thread"]; - _channel = [[FLTThreadSafeMethodChannel alloc] initWithMethodChannel:mockMethodChannel]; - - OCMStub([mockMethodChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]) - .andDo(^(NSInvocation *invocation) { - if (NSThread.isMainThread) { - [self->_mainThreadExpectation fulfill]; - } - }); -} - -- (void)testInvokeMethod_shouldStayOnMainThreadIfCalledFromMainThread { - [_channel invokeMethod:@"foo" arguments:nil]; - - [self waitForExpectations:@[ _mainThreadExpectation ] timeout:1]; -} - -- (void)testInvokeMethod__shouldDispatchToMainThreadIfCalledFromBackgroundThread { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [self->_channel invokeMethod:@"foo" arguments:nil]; - }); - [self waitForExpectations:@[ _mainThreadExpectation ] timeout:1]; -} - -@end diff --git a/packages/camera/camera/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m b/packages/camera/camera/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m deleted file mode 100644 index 73a9fb8e3c3d..000000000000 --- a/packages/camera/camera/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -@import camera; -@import XCTest; -#import - -@interface ThreadSafeTextureRegistryTests : XCTestCase -@end - -@implementation ThreadSafeTextureRegistryTests { - FLTThreadSafeTextureRegistry *_registry; - XCTestExpectation *_registerTextureExpectation; - XCTestExpectation *_unregisterTextureExpectation; - XCTestExpectation *_textureFrameAvailableExpectation; -} - -- (void)setUp { - [super setUp]; - id mockTextureRegistry = OCMProtocolMock(@protocol(FlutterTextureRegistry)); - _registry = [[FLTThreadSafeTextureRegistry alloc] initWithTextureRegistry:mockTextureRegistry]; - - _registerTextureExpectation = [[XCTestExpectation alloc] - initWithDescription:@"registerTexture must be called in main thread"]; - _unregisterTextureExpectation = [[XCTestExpectation alloc] - initWithDescription:@"unregisterTexture must be called in main thread"]; - _textureFrameAvailableExpectation = [[XCTestExpectation alloc] - initWithDescription:@"textureFrameAvailable must be called in main thread"]; - - OCMStub([mockTextureRegistry registerTexture:[OCMArg any]]).andDo(^(NSInvocation *invocation) { - if (NSThread.isMainThread) { - [self->_registerTextureExpectation fulfill]; - } - }); - - OCMStub([mockTextureRegistry unregisterTexture:0]).andDo(^(NSInvocation *invocation) { - if (NSThread.isMainThread) { - [self->_unregisterTextureExpectation fulfill]; - } - }); - - OCMStub([mockTextureRegistry textureFrameAvailable:0]).andDo(^(NSInvocation *invocation) { - if (NSThread.isMainThread) { - [self->_textureFrameAvailableExpectation fulfill]; - } - }); -} - -- (void)testShouldStayOnMainThreadIfCalledFromMainThread { - NSObject *anyTexture = OCMProtocolMock(@protocol(FlutterTexture)); - [_registry registerTextureSync:anyTexture]; - [_registry textureFrameAvailable:0]; - [_registry unregisterTexture:0]; - [self waitForExpectations:@[ - _registerTextureExpectation, _unregisterTextureExpectation, _textureFrameAvailableExpectation - ] - timeout:1]; -} - -- (void)testShouldDispatchToMainThreadIfCalledFromBackgroundThread { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - NSObject *anyTexture = OCMProtocolMock(@protocol(FlutterTexture)); - [self->_registry registerTextureSync:anyTexture]; - [self->_registry textureFrameAvailable:0]; - [self->_registry unregisterTexture:0]; - }); - [self waitForExpectations:@[ - _registerTextureExpectation, _unregisterTextureExpectation, _textureFrameAvailableExpectation - ] - timeout:1]; -} - -@end diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.m b/packages/camera/camera/ios/Classes/CameraPlugin.m index 448727963ab3..2c12081da807 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin.m +++ b/packages/camera/camera/ios/Classes/CameraPlugin.m @@ -10,10 +10,7 @@ #import #import #import -#import "FLTThreadSafeEventChannel.h" #import "FLTThreadSafeFlutterResult.h" -#import "FLTThreadSafeMethodChannel.h" -#import "FLTThreadSafeTextureRegistry.h" @interface FLTSavePhotoDelegate : NSObject @property(readonly, nonatomic) NSString *path; @@ -308,7 +305,7 @@ @interface FLTCam : NSObject *)messen FlutterEventChannel *eventChannel = [FlutterEventChannel eventChannelWithName:@"plugins.flutter.io/camera/imageStream" binaryMessenger:messenger]; - FLTThreadSafeEventChannel *threadSafeEventChannel = - [[FLTThreadSafeEventChannel alloc] initWithEventChannel:eventChannel]; _imageStreamHandler = [[FLTImageStreamHandler alloc] init]; - [threadSafeEventChannel setStreamHandler:_imageStreamHandler]; + [eventChannel setStreamHandler:_imageStreamHandler]; _isStreamingImages = YES; } else { @@ -1290,10 +1285,10 @@ - (void)setUpCaptureSessionForAudio { @end @interface CameraPlugin () -@property(readonly, nonatomic) FLTThreadSafeTextureRegistry *registry; +@property(readonly, nonatomic) NSObject *registry; @property(readonly, nonatomic) NSObject *messenger; @property(readonly, nonatomic) FLTCam *camera; -@property(readonly, nonatomic) FLTThreadSafeMethodChannel *deviceEventMethodChannel; +@property(readonly, nonatomic) FlutterMethodChannel *deviceEventMethodChannel; @end @implementation CameraPlugin { @@ -1313,7 +1308,7 @@ - (instancetype)initWithRegistry:(NSObject *)registry messenger:(NSObject *)messenger { self = [super init]; NSAssert(self, @"super init cannot be nil"); - _registry = [[FLTThreadSafeTextureRegistry alloc] initWithTextureRegistry:registry]; + _registry = registry; _messenger = messenger; [self initDeviceEventMethodChannel]; [self startOrientationListener]; @@ -1321,11 +1316,9 @@ - (instancetype)initWithRegistry:(NSObject *)registry } - (void)initDeviceEventMethodChannel { - FlutterMethodChannel *methodChannel = + _deviceEventMethodChannel = [FlutterMethodChannel methodChannelWithName:@"flutter.io/cameraPlugin/device" binaryMessenger:_messenger]; - _deviceEventMethodChannel = - [[FLTThreadSafeMethodChannel alloc] initWithMethodChannel:methodChannel]; } - (void)startOrientationListener { @@ -1424,7 +1417,7 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call if (_camera) { [_camera close]; } - int64_t textureId = [self.registry registerTextureSync:cam]; + int64_t textureId = [self.registry registerTexture:cam]; _camera = cam; [result sendSuccessWithData:@{ @"cameraId" : @(textureId), @@ -1453,10 +1446,8 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call methodChannelWithName:[NSString stringWithFormat:@"flutter.io/cameraPlugin/camera%lu", (unsigned long)cameraId] binaryMessenger:_messenger]; - FLTThreadSafeMethodChannel *threadSafeMethodChannel = - [[FLTThreadSafeMethodChannel alloc] initWithMethodChannel:methodChannel]; - _camera.methodChannel = threadSafeMethodChannel; - [threadSafeMethodChannel + _camera.methodChannel = methodChannel; + [methodChannel invokeMethod:@"initialized" arguments:@{ @"previewWidth" : @(_camera.previewSize.width), diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.h b/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.h deleted file mode 100644 index dd84a6733d64..000000000000 --- a/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -NS_ASSUME_NONNULL_BEGIN - -/** - * Wrapper for FlutterEventChannel that always sends events on the main thread - */ -@interface FLTThreadSafeEventChannel : NSObject - -/** - * Creates a FLTThreadSafeEventChannel by wrapping a FlutterEventChannel object. - * @param channel The FlutterEventChannel object to be wrapped. - */ -- (instancetype)initWithEventChannel:(FlutterEventChannel *)channel; - -/* - * Registers a handler for stream setup requests from the Flutter side on main thread. - */ -- (void)setStreamHandler:(nullable NSObject *)handler; - -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.m b/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.m deleted file mode 100644 index 418d83c37849..000000000000 --- a/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.m +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "FLTThreadSafeEventChannel.h" - -@implementation FLTThreadSafeEventChannel { - FlutterEventChannel *_channel; -} - -- (instancetype)initWithEventChannel:(FlutterEventChannel *)channel { - self = [super init]; - if (self) { - _channel = channel; - } - return self; -} - -- (void)setStreamHandler:(NSObject *)handler { - if (!NSThread.isMainThread) { - dispatch_async(dispatch_get_main_queue(), ^{ - [self->_channel setStreamHandler:handler]; - }); - } else { - [_channel setStreamHandler:handler]; - } -} - -@end diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.h b/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.h deleted file mode 100644 index db99d0f7f66a..000000000000 --- a/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -NS_ASSUME_NONNULL_BEGIN - -/** - * Wrapper for FlutterMethodChannel that always invokes messages on the main thread - */ -@interface FLTThreadSafeMethodChannel : NSObject - -/** - * Creates a FLTThreadSafeMethodChannel by wrapping a FlutterMethodChannel object. - * @param channel The FlutterMethodChannel object to be wrapped. - */ -- (instancetype)initWithMethodChannel:(FlutterMethodChannel *)channel; - -/** - * Invokes the specified flutter method with the specified arguments on main thread. - */ -- (void)invokeMethod:(NSString *)method arguments:(nullable id)arguments; - -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.m b/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.m deleted file mode 100644 index e62fd5bb3af8..000000000000 --- a/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.m +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "FLTThreadSafeMethodChannel.h" - -@implementation FLTThreadSafeMethodChannel { - FlutterMethodChannel *_channel; -} - -- (instancetype)initWithMethodChannel:(FlutterMethodChannel *)channel { - self = [super init]; - if (self) { - _channel = channel; - } - return self; -} - -- (void)invokeMethod:(NSString *)method arguments:(id)arguments { - if (!NSThread.isMainThread) { - dispatch_async(dispatch_get_main_queue(), ^{ - [self->_channel invokeMethod:method arguments:arguments]; - }); - } else { - [_channel invokeMethod:method arguments:arguments]; - } -} - -@end diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.h b/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.h deleted file mode 100644 index 34ee4400bcc1..000000000000 --- a/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -NS_ASSUME_NONNULL_BEGIN - -/** - * Wrapper for FlutterTextureRegistry that always sends events on the main thread - */ -@interface FLTThreadSafeTextureRegistry : NSObject - -/** - * Creates a FLTThreadSafeTextureRegistry by wrapping an object conforming to - * FlutterTextureRegistry. - * @param registry The FlutterTextureRegistry object to be wrapped. - */ -- (instancetype)initWithTextureRegistry:(NSObject *)registry; - -/** - * Registers a `FlutterTexture` for usage in Flutter and returns an id that can be used to reference - * that texture when calling into Flutter with channels. Textures must be registered on the - * main thread. On success returns the pointer to the registered texture, else returns 0. - * - * Runs on main thread. - */ -- (int64_t)registerTextureSync:(NSObject *)texture; - -/** - * Notifies Flutter that the content of the previously registered texture has been updated. - * - * This will trigger a call to `-[FlutterTexture copyPixelBuffer]` on the raster thread. - * - * Runs on main thread. - */ -- (void)textureFrameAvailable:(int64_t)textureId; - -/** - * Unregisters a `FlutterTexture` that has previously regeistered with `registerTexture:`. Textures - * must be unregistered on the main thread. - * - * Runs on main thread. - * - * @param textureId The result that was previously returned from `registerTexture:`. - */ -- (void)unregisterTexture:(int64_t)textureId; - -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.m b/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.m deleted file mode 100644 index 103c61cce6fd..000000000000 --- a/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.m +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "FLTThreadSafeTextureRegistry.h" - -@implementation FLTThreadSafeTextureRegistry { - NSObject *_registry; -} - -- (instancetype)initWithTextureRegistry:(NSObject *)registry { - self = [super init]; - if (self) { - _registry = registry; - } - return self; -} - -- (int64_t)registerTextureSync:(NSObject *)texture { - if (!NSThread.isMainThread) { - __block int64_t textureId; - // We cannot use async API (with completion block) because completion block does not work for - // separate functions (e.g. `dispose` and `create` are separately registered functions). It's - // hard to tell if the developers had made implicit assumption of the synchronous nature of the - // original API when implementing this plugin. Use dispatch_sync to keep - // FlutterTextureRegistry's sychronous API, so that we don't introduce new potential race - // conditions. We do not break priority inversion here since it's the background thread waiting - // for main thread. - dispatch_sync(dispatch_get_main_queue(), ^{ - textureId = [self->_registry registerTexture:texture]; - }); - return textureId; - } else { - return [_registry registerTexture:texture]; - } -} - -- (void)textureFrameAvailable:(int64_t)textureId { - if (!NSThread.isMainThread) { - dispatch_async(dispatch_get_main_queue(), ^{ - [self->_registry textureFrameAvailable:textureId]; - }); - } else { - [_registry textureFrameAvailable:textureId]; - } -} - -- (void)unregisterTexture:(int64_t)textureId { - if (!NSThread.isMainThread) { - dispatch_async(dispatch_get_main_queue(), ^{ - [self->_registry unregisterTexture:textureId]; - }); - } else { - [_registry unregisterTexture:textureId]; - } -} - -@end diff --git a/packages/camera/camera/ios/Classes/camera-umbrella.h b/packages/camera/camera/ios/Classes/camera-umbrella.h index c55276e31147..b0fd493b24df 100644 --- a/packages/camera/camera/ios/Classes/camera-umbrella.h +++ b/packages/camera/camera/ios/Classes/camera-umbrella.h @@ -4,10 +4,7 @@ #import #import -#import #import -#import -#import FOUNDATION_EXPORT double cameraVersionNumber; FOUNDATION_EXPORT const unsigned char cameraVersionString[]; From 09c61ea393787bfeb4ea947aa5ce49675aef254c Mon Sep 17 00:00:00 2001 From: Kevin Gray Date: Wed, 22 Dec 2021 10:38:20 -0500 Subject: [PATCH 076/600] [video_player] Update texture on seekTo (#2758) --- .../video_player/video_player/CHANGELOG.md | 4 +++ .../video_player/example/ios/Podfile | 1 + .../ios/RunnerTests/VideoPlayerTests.m | 25 ++++++++++++++++++- .../ios/Classes/FLTVideoPlayerPlugin.h | 1 + .../ios/Classes/FLTVideoPlayerPlugin.m | 1 + .../video_player/video_player/pubspec.yaml | 2 +- 6 files changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index b87ac4bbeded..435484ba6e33 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.2.10 + +* iOS: Updates texture on `seekTo`. + ## 2.2.9 * Adds compatibility with `video_player_platform_interface` 5.0, which does not diff --git a/packages/video_player/video_player/example/ios/Podfile b/packages/video_player/video_player/example/ios/Podfile index 3924e59aa0f9..fe37427f8a74 100644 --- a/packages/video_player/video_player/example/ios/Podfile +++ b/packages/video_player/video_player/example/ios/Podfile @@ -31,6 +31,7 @@ target 'Runner' do flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) target 'RunnerTests' do inherit! :search_paths + pod 'OCMock', '3.5' end end diff --git a/packages/video_player/video_player/example/ios/RunnerTests/VideoPlayerTests.m b/packages/video_player/video_player/example/ios/RunnerTests/VideoPlayerTests.m index 890866f34952..deea83350bce 100644 --- a/packages/video_player/video_player/example/ios/RunnerTests/VideoPlayerTests.m +++ b/packages/video_player/video_player/example/ios/RunnerTests/VideoPlayerTests.m @@ -5,14 +5,37 @@ @import video_player; @import XCTest; +#import + @interface VideoPlayerTests : XCTestCase @end @implementation VideoPlayerTests - (void)testPlugin { - FLTVideoPlayerPlugin* plugin = [[FLTVideoPlayerPlugin alloc] init]; + FLTVideoPlayerPlugin *plugin = [[FLTVideoPlayerPlugin alloc] init]; XCTAssertNotNil(plugin); } +- (void)testSeekToInvokesTextureFrameAvailableOnTextureRegistry { + NSObject *mockTextureRegistry = + OCMProtocolMock(@protocol(FlutterTextureRegistry)); + NSObject *registry = + (NSObject *)[[UIApplication sharedApplication] delegate]; + NSObject *registrar = + [registry registrarForPlugin:@"TEST_FLTVideoPlayerPlugin"]; + NSObject *partialRegistrar = OCMPartialMock(registrar); + OCMStub([partialRegistrar textures]).andReturn(mockTextureRegistry); + [FLTVideoPlayerPlugin registerWithRegistrar:partialRegistrar]; + FLTVideoPlayerPlugin *videoPlayerPlugin = + (FLTVideoPlayerPlugin *)[[FLTVideoPlayerPlugin alloc] + initWithRegistrar:partialRegistrar]; + FLTPositionMessage *message = [[FLTPositionMessage alloc] init]; + message.textureId = @101; + message.position = @0; + FlutterError *error; + [videoPlayerPlugin seekTo:message error:&error]; + OCMVerify([mockTextureRegistry textureFrameAvailable:message.textureId.intValue]); +} + @end diff --git a/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.h b/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.h index 6c9d91468d6b..2514aee71f10 100644 --- a/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.h +++ b/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.h @@ -5,4 +5,5 @@ #import @interface FLTVideoPlayerPlugin : NSObject +- (instancetype)initWithRegistrar:(NSObject*)registrar; @end diff --git a/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m b/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m index 78626eb4f3c1..b581cd8b7b5e 100644 --- a/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m +++ b/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m @@ -616,6 +616,7 @@ - (FLTPositionMessage*)position:(FLTTextureMessage*)input error:(FlutterError**) - (void)seekTo:(FLTPositionMessage*)input error:(FlutterError**)error { FLTVideoPlayer* player = _players[input.textureId]; [player seekTo:[input.position intValue]]; + [_registry textureFrameAvailable:input.textureId.intValue]; } - (void)pause:(FLTTextureMessage*)input error:(FlutterError**)error { diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 3ae9c0dfd301..fcb6b844249a 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for displaying inline video with other Flutter widgets on Android, iOS, and web. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.2.9 +version: 2.2.10 environment: sdk: ">=2.14.0 <3.0.0" From 0619c3a9b81be1e20480c1ba82eddd41b49ea2c2 Mon Sep 17 00:00:00 2001 From: guille-ideotec <96171330+guille-ideotec@users.noreply.github.com> Date: Wed, 22 Dec 2021 18:01:18 +0100 Subject: [PATCH 077/600] [path_provider] Switch Android to an internal method channel (#4617) --- .../path_provider_android/CHANGELOG.md | 4 + .../pathprovider/PathProviderPlugin.java | 2 +- .../lib/path_provider_android.dart | 67 +++++++++ .../path_provider_android/pubspec.yaml | 5 +- .../test/path_provider_android_test.dart | 136 ++++++++++++++++++ 5 files changed, 211 insertions(+), 3 deletions(-) create mode 100644 packages/path_provider/path_provider_android/lib/path_provider_android.dart create mode 100644 packages/path_provider/path_provider_android/test/path_provider_android_test.dart diff --git a/packages/path_provider/path_provider_android/CHANGELOG.md b/packages/path_provider/path_provider_android/CHANGELOG.md index 628bd442c42f..f9d3543a92d4 100644 --- a/packages/path_provider/path_provider_android/CHANGELOG.md +++ b/packages/path_provider/path_provider_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.10 + +* Switches to a package-internal implementation of the platform interface. + ## 2.0.9 * Updates Android compileSdkVersion to 31. diff --git a/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java index 3ff2416527d0..278ff58b59dc 100644 --- a/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java +++ b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java @@ -153,7 +153,7 @@ public void getApplicationSupportDirectory(@NonNull Result result) { public PathProviderPlugin() {} private void setup(BinaryMessenger messenger, Context context) { - String channelName = "plugins.flutter.io/path_provider"; + String channelName = "plugins.flutter.io/path_provider_android"; // TODO(gaaclarke): Remove reflection guard when https://github.com/flutter/engine/pull/29147 // becomes available on the stable branch. try { diff --git a/packages/path_provider/path_provider_android/lib/path_provider_android.dart b/packages/path_provider/path_provider_android/lib/path_provider_android.dart new file mode 100644 index 000000000000..b0f3808d2859 --- /dev/null +++ b/packages/path_provider/path_provider_android/lib/path_provider_android.dart @@ -0,0 +1,67 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; + +/// The Android implementation of [PathProviderPlatform]. +class PathProviderAndroid extends PathProviderPlatform { + /// The method channel used to interact with the native platform. + @visibleForTesting + MethodChannel methodChannel = + const MethodChannel('plugins.flutter.io/path_provider_android'); + + /// Registers this class as the default instance of [PathProviderPlatform]. + static void registerWith() { + PathProviderPlatform.instance = PathProviderAndroid(); + } + + @override + Future getTemporaryPath() { + return methodChannel.invokeMethod('getTemporaryDirectory'); + } + + @override + Future getApplicationSupportPath() { + return methodChannel.invokeMethod('getApplicationSupportDirectory'); + } + + @override + Future getLibraryPath() { + throw UnsupportedError('getLibraryPath is not supported on Android'); + } + + @override + Future getApplicationDocumentsPath() { + return methodChannel + .invokeMethod('getApplicationDocumentsDirectory'); + } + + @override + Future getExternalStoragePath() { + return methodChannel.invokeMethod('getStorageDirectory'); + } + + @override + Future?> getExternalCachePaths() { + return methodChannel + .invokeListMethod('getExternalCacheDirectories'); + } + + @override + Future?> getExternalStoragePaths({ + StorageDirectory? type, + }) async { + return methodChannel.invokeListMethod( + 'getExternalStorageDirectories', + {'type': type?.index}, + ); + } + + @override + Future getDownloadsPath() { + throw UnsupportedError('getDownloadsPath is not supported on Android'); + } +} diff --git a/packages/path_provider/path_provider_android/pubspec.yaml b/packages/path_provider/path_provider_android/pubspec.yaml index 09b26a5efedb..5664ebf7944c 100644 --- a/packages/path_provider/path_provider_android/pubspec.yaml +++ b/packages/path_provider/path_provider_android/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_android description: Android implementation of the path_provider plugin. repository: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.9 +version: 2.0.10 environment: sdk: ">=2.14.0 <3.0.0" @@ -15,11 +15,12 @@ flutter: android: package: io.flutter.plugins.pathprovider pluginClass: PathProviderPlugin + dartPluginClass: PathProviderAndroid dependencies: flutter: sdk: flutter - path_provider_platform_interface: ^2.0.0 + path_provider_platform_interface: ^2.0.1 dev_dependencies: flutter_driver: diff --git a/packages/path_provider/path_provider_android/test/path_provider_android_test.dart b/packages/path_provider/path_provider_android/test/path_provider_android_test.dart new file mode 100644 index 000000000000..d2f9682bf6d7 --- /dev/null +++ b/packages/path_provider/path_provider_android/test/path_provider_android_test.dart @@ -0,0 +1,136 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:path_provider_android/path_provider_android.dart'; +import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + const String kTemporaryPath = 'temporaryPath'; + const String kApplicationSupportPath = 'applicationSupportPath'; + const String kLibraryPath = 'libraryPath'; + const String kApplicationDocumentsPath = 'applicationDocumentsPath'; + const String kExternalCachePaths = 'externalCachePaths'; + const String kExternalStoragePaths = 'externalStoragePaths'; + const String kDownloadsPath = 'downloadsPath'; + + group('PathProviderAndroid', () { + late PathProviderAndroid pathProvider; + final List log = []; + + setUp(() async { + pathProvider = PathProviderAndroid(); + TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger + .setMockMethodCallHandler(pathProvider.methodChannel, + (MethodCall methodCall) async { + log.add(methodCall); + switch (methodCall.method) { + case 'getTemporaryDirectory': + return kTemporaryPath; + case 'getApplicationSupportDirectory': + return kApplicationSupportPath; + case 'getLibraryDirectory': + return kLibraryPath; + case 'getApplicationDocumentsDirectory': + return kApplicationDocumentsPath; + case 'getExternalStorageDirectories': + return [kExternalStoragePaths]; + case 'getExternalCacheDirectories': + return [kExternalCachePaths]; + case 'getDownloadsDirectory': + return kDownloadsPath; + default: + return null; + } + }); + }); + + tearDown(() { + log.clear(); + }); + + test('getTemporaryPath', () async { + final String? path = await pathProvider.getTemporaryPath(); + expect( + log, + [isMethodCall('getTemporaryDirectory', arguments: null)], + ); + expect(path, kTemporaryPath); + }); + + test('getApplicationSupportPath', () async { + final String? path = await pathProvider.getApplicationSupportPath(); + expect( + log, + [ + isMethodCall('getApplicationSupportDirectory', arguments: null) + ], + ); + expect(path, kApplicationSupportPath); + }); + + test('getLibraryPath fails', () async { + try { + await pathProvider.getLibraryPath(); + fail('should throw UnsupportedError'); + } catch (e) { + expect(e, isUnsupportedError); + } + }); + + test('getApplicationDocumentsPath', () async { + final String? path = await pathProvider.getApplicationDocumentsPath(); + expect( + log, + [ + isMethodCall('getApplicationDocumentsDirectory', arguments: null) + ], + ); + expect(path, kApplicationDocumentsPath); + }); + + test('getExternalCachePaths succeeds', () async { + final List? result = await pathProvider.getExternalCachePaths(); + expect( + log, + [isMethodCall('getExternalCacheDirectories', arguments: null)], + ); + expect(result!.length, 1); + expect(result.first, kExternalCachePaths); + }); + + for (final StorageDirectory? type in [ + null, + ...StorageDirectory.values + ]) { + test('getExternalStoragePaths (type: $type) android succeeds', () async { + final List? result = + await pathProvider.getExternalStoragePaths(type: type); + expect( + log, + [ + isMethodCall( + 'getExternalStorageDirectories', + arguments: {'type': type?.index}, + ) + ], + ); + + expect(result!.length, 1); + expect(result.first, kExternalStoragePaths); + }); + } // end of for-loop + + test('getDownloadsPath fails', () async { + try { + await pathProvider.getDownloadsPath(); + fail('should throw UnsupportedError'); + } catch (e) { + expect(e, isUnsupportedError); + } + }); + }); +} From 6a9de4f5a7c03cf24418febe01d9bfe09bdcc40b Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 23 Dec 2021 13:29:59 -0500 Subject: [PATCH 078/600] [path_provider] Fix Android compatibility with 2.5 (#4637) PR #4617 added a dependency on Dart auto-registration, but forgot to bump the SDK requirement to 2.8 (where that feature was introduced for Android). In order to fix older versions, this restores the previous channel name so that the implementation here is compatible with the default method channel registration used by Flutter 2.5 and earlier. A follow-up will restore the package-specific channel name, but also change the SDK requirement to >=2.8. This must be published first so that the last published version that claims 2.5 compatibility actually works with 2.5. Fixes https://github.com/flutter/flutter/issues/95706 --- packages/path_provider/path_provider_android/CHANGELOG.md | 5 +++++ .../flutter/plugins/pathprovider/PathProviderPlugin.java | 2 +- .../path_provider_android/lib/path_provider_android.dart | 2 +- packages/path_provider/path_provider_android/pubspec.yaml | 2 +- .../test/path_provider_android_test.dart | 8 ++++++++ 5 files changed, 16 insertions(+), 3 deletions(-) diff --git a/packages/path_provider/path_provider_android/CHANGELOG.md b/packages/path_provider/path_provider_android/CHANGELOG.md index f9d3543a92d4..1282ab6e20a9 100644 --- a/packages/path_provider/path_provider_android/CHANGELOG.md +++ b/packages/path_provider/path_provider_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.11 + +* Temporarily reverts the platform channel name change from 2.0.10 in order to + restore compatibility with Flutter versions earlier than 2.8. + ## 2.0.10 * Switches to a package-internal implementation of the platform interface. diff --git a/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java index 278ff58b59dc..3ff2416527d0 100644 --- a/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java +++ b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java @@ -153,7 +153,7 @@ public void getApplicationSupportDirectory(@NonNull Result result) { public PathProviderPlugin() {} private void setup(BinaryMessenger messenger, Context context) { - String channelName = "plugins.flutter.io/path_provider_android"; + String channelName = "plugins.flutter.io/path_provider"; // TODO(gaaclarke): Remove reflection guard when https://github.com/flutter/engine/pull/29147 // becomes available on the stable branch. try { diff --git a/packages/path_provider/path_provider_android/lib/path_provider_android.dart b/packages/path_provider/path_provider_android/lib/path_provider_android.dart index b0f3808d2859..281cc5141eda 100644 --- a/packages/path_provider/path_provider_android/lib/path_provider_android.dart +++ b/packages/path_provider/path_provider_android/lib/path_provider_android.dart @@ -11,7 +11,7 @@ class PathProviderAndroid extends PathProviderPlatform { /// The method channel used to interact with the native platform. @visibleForTesting MethodChannel methodChannel = - const MethodChannel('plugins.flutter.io/path_provider_android'); + const MethodChannel('plugins.flutter.io/path_provider'); /// Registers this class as the default instance of [PathProviderPlatform]. static void registerWith() { diff --git a/packages/path_provider/path_provider_android/pubspec.yaml b/packages/path_provider/path_provider_android/pubspec.yaml index 5664ebf7944c..393aef4cfd35 100644 --- a/packages/path_provider/path_provider_android/pubspec.yaml +++ b/packages/path_provider/path_provider_android/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_android description: Android implementation of the path_provider plugin. repository: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.10 +version: 2.0.11 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider_android/test/path_provider_android_test.dart b/packages/path_provider/path_provider_android/test/path_provider_android_test.dart index d2f9682bf6d7..2388059f0b34 100644 --- a/packages/path_provider/path_provider_android/test/path_provider_android_test.dart +++ b/packages/path_provider/path_provider_android/test/path_provider_android_test.dart @@ -52,6 +52,14 @@ void main() { log.clear(); }); + // TODO(stuartmorgan): Change this to a test that it uses a different name, + // to avoid potential confusion, once the SDK is changed to 2.8+. See + // https://github.com/flutter/plugins/pull/4617#discussion_r774673962 + test('channel name is compatible with shared method channel', () async { + expect( + pathProvider.methodChannel.name, 'plugins.flutter.io/path_provider'); + }); + test('getTemporaryPath', () async { final String? path = await pathProvider.getTemporaryPath(); expect( From a10ac2e0d8a8cd878fd0d0a54aa9a87c44c7d7c5 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 4 Jan 2022 10:34:28 -0500 Subject: [PATCH 079/600] [camera] Switch platform interface to new analysis options (#4613) Switches camera_platform_interface from the legacy analysis options to the new ones, fixing all violations they flag. Part of https://github.com/flutter/flutter/issues/76229 --- .../camera_platform_interface/CHANGELOG.md | 4 + .../analysis_options.yaml | 1 - .../lib/camera_platform_interface.dart | 6 +- .../lib/src/events/camera_event.dart | 136 +++--- .../lib/src/events/device_event.dart | 20 +- .../method_channel/method_channel_camera.dart | 81 ++-- .../lib/src/types/camera_description.dart | 9 +- .../lib/src/types/exposure_mode.dart | 4 +- .../lib/src/types/focus_mode.dart | 4 +- .../lib/src/types/types.dart | 6 +- .../lib/src/utils/utils.dart | 8 +- .../camera_platform_interface/pubspec.yaml | 2 +- .../test/camera_platform_interface_test.dart | 64 +-- .../test/events/camera_event_test.dart | 132 +++--- .../test/events/device_event_test.dart | 24 +- .../method_channel_camera_test.dart | 432 ++++++++++-------- .../test/types/camera_description_test.dart | 28 +- .../test/types/camera_exception_test.dart | 16 +- .../test/types/exposure_mode_test.dart | 14 +- .../test/types/flash_mode_test.dart | 6 +- .../test/types/focus_mode_test.dart | 14 +- .../test/types/resolution_preset_test.dart | 6 +- .../test/utils/method_channel_mock.dart | 18 +- .../test/utils/utils_test.dart | 12 +- script/configs/custom_analysis.yaml | 1 - 25 files changed, 572 insertions(+), 476 deletions(-) delete mode 100644 packages/camera/camera_platform_interface/analysis_options.yaml diff --git a/packages/camera/camera_platform_interface/CHANGELOG.md b/packages/camera/camera_platform_interface/CHANGELOG.md index 195e142fe10f..69e9200382ae 100644 --- a/packages/camera/camera_platform_interface/CHANGELOG.md +++ b/packages/camera/camera_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.2 + +* Adopts new analysis options and fixes all violations. + ## 2.1.1 * Add web-relevant docs to platform interface code. diff --git a/packages/camera/camera_platform_interface/analysis_options.yaml b/packages/camera/camera_platform_interface/analysis_options.yaml deleted file mode 100644 index 5aeb4e7c5e21..000000000000 --- a/packages/camera/camera_platform_interface/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../../analysis_options_legacy.yaml diff --git a/packages/camera/camera_platform_interface/lib/camera_platform_interface.dart b/packages/camera/camera_platform_interface/lib/camera_platform_interface.dart index 3ec66dd54894..6fab99b3d694 100644 --- a/packages/camera/camera_platform_interface/lib/camera_platform_interface.dart +++ b/packages/camera/camera_platform_interface/lib/camera_platform_interface.dart @@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +/// Expose XFile +export 'package:cross_file/cross_file.dart'; + export 'src/events/camera_event.dart'; export 'src/events/device_event.dart'; export 'src/platform_interface/camera_platform.dart'; export 'src/types/types.dart'; - -/// Expose XFile -export 'package:cross_file/cross_file.dart'; diff --git a/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart b/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart index 591d5a336356..dfd851ec4ab3 100644 --- a/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart +++ b/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'package:camera_platform_interface/src/types/focus_mode.dart'; +import 'package:meta/meta.dart'; import '../../camera_platform_interface.dart'; @@ -22,14 +23,15 @@ import '../../camera_platform_interface.dart'; /// See below for examples: `CameraClosingEvent`, `CameraErrorEvent`... /// These events are more semantic and more pleasant to use than raw generics. /// They can be (and in fact, are) filtered by the `instanceof`-operator. +@immutable abstract class CameraEvent { - /// The ID of the Camera this event is associated to. - final int cameraId; - /// Build a Camera Event, that relates a `cameraId`. /// /// The `cameraId` is the ID of the camera that triggered the event. - CameraEvent(this.cameraId) : assert(cameraId != null); + const CameraEvent(this.cameraId) : assert(cameraId != null); + + /// The ID of the Camera this event is associated to. + final int cameraId; @override bool operator ==(Object other) => @@ -44,30 +46,12 @@ abstract class CameraEvent { /// An event fired when the camera has finished initializing. class CameraInitializedEvent extends CameraEvent { - /// The width of the preview in pixels. - final double previewWidth; - - /// The height of the preview in pixels. - final double previewHeight; - - /// The default exposure mode - final ExposureMode exposureMode; - - /// The default focus mode - final FocusMode focusMode; - - /// Whether setting exposure points is supported. - final bool exposurePointSupported; - - /// Whether setting focus points is supported. - final bool focusPointSupported; - /// Build a CameraInitialized event triggered from the camera represented by /// `cameraId`. /// /// The `previewWidth` represents the width of the generated preview in pixels. /// The `previewHeight` represents the height of the generated preview in pixels. - CameraInitializedEvent( + const CameraInitializedEvent( int cameraId, this.previewWidth, this.previewHeight, @@ -80,17 +64,36 @@ class CameraInitializedEvent extends CameraEvent { /// Converts the supplied [Map] to an instance of the [CameraInitializedEvent] /// class. CameraInitializedEvent.fromJson(Map json) - : previewWidth = json['previewWidth'], - previewHeight = json['previewHeight'], - exposureMode = deserializeExposureMode(json['exposureMode']), - exposurePointSupported = json['exposurePointSupported'] ?? false, - focusMode = deserializeFocusMode(json['focusMode']), - focusPointSupported = json['focusPointSupported'] ?? false, - super(json['cameraId']); + : previewWidth = json['previewWidth']! as double, + previewHeight = json['previewHeight']! as double, + exposureMode = deserializeExposureMode(json['exposureMode']! as String), + exposurePointSupported = + (json['exposurePointSupported'] as bool?) ?? false, + focusMode = deserializeFocusMode(json['focusMode']! as String), + focusPointSupported = (json['focusPointSupported'] as bool?) ?? false, + super(json['cameraId']! as int); + + /// The width of the preview in pixels. + final double previewWidth; + + /// The height of the preview in pixels. + final double previewHeight; + + /// The default exposure mode + final ExposureMode exposureMode; + + /// The default focus mode + final FocusMode focusMode; + + /// Whether setting exposure points is supported. + final bool exposurePointSupported; + + /// Whether setting focus points is supported. + final bool focusPointSupported; /// Converts the [CameraInitializedEvent] instance into a [Map] instance that /// can be serialized to JSON. - Map toJson() => { + Map toJson() => { 'cameraId': cameraId, 'previewWidth': previewWidth, 'previewHeight': previewHeight, @@ -126,18 +129,12 @@ class CameraInitializedEvent extends CameraEvent { /// An event fired when the resolution preset of the camera has changed. class CameraResolutionChangedEvent extends CameraEvent { - /// The capture width in pixels. - final double captureWidth; - - /// The capture height in pixels. - final double captureHeight; - /// Build a CameraResolutionChanged event triggered from the camera /// represented by `cameraId`. /// /// The `captureWidth` represents the width of the resulting image in pixels. /// The `captureHeight` represents the height of the resulting image in pixels. - CameraResolutionChangedEvent( + const CameraResolutionChangedEvent( int cameraId, this.captureWidth, this.captureHeight, @@ -146,13 +143,19 @@ class CameraResolutionChangedEvent extends CameraEvent { /// Converts the supplied [Map] to an instance of the /// [CameraResolutionChangedEvent] class. CameraResolutionChangedEvent.fromJson(Map json) - : captureWidth = json['captureWidth'], - captureHeight = json['captureHeight'], - super(json['cameraId']); + : captureWidth = json['captureWidth']! as double, + captureHeight = json['captureHeight']! as double, + super(json['cameraId']! as int); + + /// The capture width in pixels. + final double captureWidth; + + /// The capture height in pixels. + final double captureHeight; /// Converts the [CameraResolutionChangedEvent] instance into a [Map] instance /// that can be serialized to JSON. - Map toJson() => { + Map toJson() => { 'cameraId': cameraId, 'captureWidth': captureWidth, 'captureHeight': captureHeight, @@ -162,7 +165,7 @@ class CameraResolutionChangedEvent extends CameraEvent { bool operator ==(Object other) => identical(this, other) || other is CameraResolutionChangedEvent && - super == (other) && + super == other && runtimeType == other.runtimeType && captureWidth == other.captureWidth && captureHeight == other.captureHeight; @@ -176,50 +179,53 @@ class CameraResolutionChangedEvent extends CameraEvent { class CameraClosingEvent extends CameraEvent { /// Build a CameraClosing event triggered from the camera represented by /// `cameraId`. - CameraClosingEvent(int cameraId) : super(cameraId); + const CameraClosingEvent(int cameraId) : super(cameraId); /// Converts the supplied [Map] to an instance of the [CameraClosingEvent] /// class. CameraClosingEvent.fromJson(Map json) - : super(json['cameraId']); + : super(json['cameraId']! as int); /// Converts the [CameraClosingEvent] instance into a [Map] instance that can /// be serialized to JSON. - Map toJson() => { + Map toJson() => { 'cameraId': cameraId, }; @override bool operator ==(Object other) => identical(this, other) || - super == (other) && + super == other && other is CameraClosingEvent && runtimeType == other.runtimeType; @override + // This is here even though it just calls super to make it less likely that + // operator== would be changed without changing `hashCode`. + // ignore: unnecessary_overrides int get hashCode => super.hashCode; } /// An event fired when an error occured while operating the camera. class CameraErrorEvent extends CameraEvent { - /// Description of the error. - final String description; - /// Build a CameraError event triggered from the camera represented by /// `cameraId`. /// /// The `description` represents the error occured on the camera. - CameraErrorEvent(int cameraId, this.description) : super(cameraId); + const CameraErrorEvent(int cameraId, this.description) : super(cameraId); /// Converts the supplied [Map] to an instance of the [CameraErrorEvent] /// class. CameraErrorEvent.fromJson(Map json) - : description = json['description'], - super(json['cameraId']); + : description = json['description']! as String, + super(json['cameraId']! as int); + + /// Description of the error. + final String description; /// Converts the [CameraErrorEvent] instance into a [Map] instance that can be /// serialized to JSON. - Map toJson() => { + Map toJson() => { 'cameraId': cameraId, 'description': description, }; @@ -227,7 +233,7 @@ class CameraErrorEvent extends CameraEvent { @override bool operator ==(Object other) => identical(this, other) || - super == (other) && + super == other && other is CameraErrorEvent && runtimeType == other.runtimeType && description == other.description; @@ -238,32 +244,32 @@ class CameraErrorEvent extends CameraEvent { /// An event fired when a video has finished recording. class VideoRecordedEvent extends CameraEvent { - /// XFile of the recorded video. - final XFile file; - - /// Maximum duration of the recorded video. - final Duration? maxVideoDuration; - /// Build a VideoRecordedEvent triggered from the camera with the `cameraId`. /// /// The `file` represents the file of the video. /// The `maxVideoDuration` shows if a maxVideoDuration shows if a maximum /// video duration was set. - VideoRecordedEvent(int cameraId, this.file, this.maxVideoDuration) + const VideoRecordedEvent(int cameraId, this.file, this.maxVideoDuration) : super(cameraId); /// Converts the supplied [Map] to an instance of the [VideoRecordedEvent] /// class. VideoRecordedEvent.fromJson(Map json) - : file = XFile(json['path']), + : file = XFile(json['path']! as String), maxVideoDuration = json['maxVideoDuration'] != null ? Duration(milliseconds: json['maxVideoDuration'] as int) : null, - super(json['cameraId']); + super(json['cameraId']! as int); + + /// XFile of the recorded video. + final XFile file; + + /// Maximum duration of the recorded video. + final Duration? maxVideoDuration; /// Converts the [VideoRecordedEvent] instance into a [Map] instance that can be /// serialized to JSON. - Map toJson() => { + Map toJson() => { 'cameraId': cameraId, 'path': file.path, 'maxVideoDuration': maxVideoDuration?.inMilliseconds diff --git a/packages/camera/camera_platform_interface/lib/src/events/device_event.dart b/packages/camera/camera_platform_interface/lib/src/events/device_event.dart index ac1c66e4df82..8a5b5f896149 100644 --- a/packages/camera/camera_platform_interface/lib/src/events/device_event.dart +++ b/packages/camera/camera_platform_interface/lib/src/events/device_event.dart @@ -4,6 +4,7 @@ import 'package:camera_platform_interface/src/utils/utils.dart'; import 'package:flutter/services.dart'; +import 'package:meta/meta.dart'; /// Generic Event coming from the native side of Camera, /// not related to a specific camera module. @@ -18,24 +19,29 @@ import 'package:flutter/services.dart'; /// See below for examples: `DeviceOrientationChangedEvent`... /// These events are more semantic and more pleasant to use than raw generics. /// They can be (and in fact, are) filtered by the `instanceof`-operator. -abstract class DeviceEvent {} +@immutable +abstract class DeviceEvent { + /// Creates a new device event. + const DeviceEvent(); +} /// The [DeviceOrientationChangedEvent] is fired every time the orientation of the device UI changes. class DeviceOrientationChangedEvent extends DeviceEvent { - /// The new orientation of the device - final DeviceOrientation orientation; - /// Build a new orientation changed event. - DeviceOrientationChangedEvent(this.orientation); + const DeviceOrientationChangedEvent(this.orientation); /// Converts the supplied [Map] to an instance of the [DeviceOrientationChangedEvent] /// class. DeviceOrientationChangedEvent.fromJson(Map json) - : orientation = deserializeDeviceOrientation(json['orientation']); + : orientation = + deserializeDeviceOrientation(json['orientation']! as String); + + /// The new orientation of the device + final DeviceOrientation orientation; /// Converts the [DeviceOrientationChangedEvent] instance into a [Map] instance that /// can be serialized to JSON. - Map toJson() => { + Map toJson() => { 'orientation': serializeDeviceOrientation(orientation), }; diff --git a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart index f932f253f491..0ba1373a991a 100644 --- a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart +++ b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart @@ -21,7 +21,15 @@ const MethodChannel _channel = MethodChannel('plugins.flutter.io/camera'); /// An implementation of [CameraPlatform] that uses method channels. class MethodChannelCamera extends CameraPlatform { - final Map _channels = {}; + /// Construct a new method channel camera instance. + MethodChannelCamera() { + const MethodChannel channel = + MethodChannel('flutter.io/cameraPlugin/device'); + channel.setMethodCallHandler( + (MethodCall call) => handleDeviceMethodCall(call)); + } + + final Map _channels = {}; /// The controller we need to broadcast the different events coming /// from handleMethodCall, specific to camera events. @@ -47,14 +55,7 @@ class MethodChannelCamera extends CameraPlatform { Stream _cameraEvents(int cameraId) => cameraEventStreamController.stream - .where((event) => event.cameraId == cameraId); - - /// Construct a new method channel camera instance. - MethodChannelCamera() { - final channel = MethodChannel('flutter.io/cameraPlugin/device'); - channel.setMethodCallHandler( - (MethodCall call) => handleDeviceMethodCall(call)); - } + .where((CameraEvent event) => event.cameraId == cameraId); @override Future> availableCameras() async { @@ -68,9 +69,10 @@ class MethodChannelCamera extends CameraPlatform { return cameras.map((Map camera) { return CameraDescription( - name: camera['name'], - lensDirection: parseCameraLensDirection(camera['lensFacing']), - sensorOrientation: camera['sensorOrientation'], + name: camera['name']! as String, + lensDirection: + parseCameraLensDirection(camera['lensFacing']! as String), + sensorOrientation: camera['sensorOrientation']! as int, ); }).toList(); } on PlatformException catch (e) { @@ -85,7 +87,7 @@ class MethodChannelCamera extends CameraPlatform { bool enableAudio = false, }) async { try { - final reply = await _channel + final Map? reply = await _channel .invokeMapMethod('create', { 'cameraName': cameraDescription.name, 'resolutionPreset': resolutionPreset != null @@ -94,7 +96,7 @@ class MethodChannelCamera extends CameraPlatform { 'enableAudio': enableAudio, }); - return reply!['cameraId']; + return reply!['cameraId']! as int; } on PlatformException catch (e) { throw CameraException(e.code, e.message); } @@ -106,15 +108,16 @@ class MethodChannelCamera extends CameraPlatform { ImageFormatGroup imageFormatGroup = ImageFormatGroup.unknown, }) { _channels.putIfAbsent(cameraId, () { - final channel = MethodChannel('flutter.io/cameraPlugin/camera$cameraId'); + final MethodChannel channel = + MethodChannel('flutter.io/cameraPlugin/camera$cameraId'); channel.setMethodCallHandler( (MethodCall call) => handleCameraMethodCall(call, cameraId)); return channel; }); - Completer _completer = Completer(); + final Completer _completer = Completer(); - onCameraInitialized(cameraId).first.then((value) { + onCameraInitialized(cameraId).first.then((CameraInitializedEvent value) { _completer.complete(); }); @@ -132,7 +135,7 @@ class MethodChannelCamera extends CameraPlatform { @override Future dispose(int cameraId) async { if (_channels.containsKey(cameraId)) { - final cameraChannel = _channels[cameraId]; + final MethodChannel? cameraChannel = _channels[cameraId]; cameraChannel?.setMethodCallHandler(null); _channels.remove(cameraId); } @@ -198,7 +201,7 @@ class MethodChannelCamera extends CameraPlatform { @override Future takePicture(int cameraId) async { - final path = await _channel.invokeMethod( + final String? path = await _channel.invokeMethod( 'takePicture', {'cameraId': cameraId}, ); @@ -231,7 +234,7 @@ class MethodChannelCamera extends CameraPlatform { @override Future stopVideoRecording(int cameraId) async { - final path = await _channel.invokeMethod( + final String? path = await _channel.invokeMethod( 'stopVideoRecording', {'cameraId': cameraId}, ); @@ -297,7 +300,7 @@ class MethodChannelCamera extends CameraPlatform { @override Future getMinExposureOffset(int cameraId) async { - final minExposureOffset = await _channel.invokeMethod( + final double? minExposureOffset = await _channel.invokeMethod( 'getMinExposureOffset', {'cameraId': cameraId}, ); @@ -307,7 +310,7 @@ class MethodChannelCamera extends CameraPlatform { @override Future getMaxExposureOffset(int cameraId) async { - final maxExposureOffset = await _channel.invokeMethod( + final double? maxExposureOffset = await _channel.invokeMethod( 'getMaxExposureOffset', {'cameraId': cameraId}, ); @@ -317,7 +320,7 @@ class MethodChannelCamera extends CameraPlatform { @override Future getExposureOffsetStepSize(int cameraId) async { - final stepSize = await _channel.invokeMethod( + final double? stepSize = await _channel.invokeMethod( 'getExposureOffsetStepSize', {'cameraId': cameraId}, ); @@ -327,7 +330,7 @@ class MethodChannelCamera extends CameraPlatform { @override Future setExposureOffset(int cameraId, double offset) async { - final appliedOffset = await _channel.invokeMethod( + final double? appliedOffset = await _channel.invokeMethod( 'setExposureOffset', { 'cameraId': cameraId, @@ -366,7 +369,7 @@ class MethodChannelCamera extends CameraPlatform { @override Future getMaxZoomLevel(int cameraId) async { - final maxZoomLevel = await _channel.invokeMethod( + final double? maxZoomLevel = await _channel.invokeMethod( 'getMaxZoomLevel', {'cameraId': cameraId}, ); @@ -376,7 +379,7 @@ class MethodChannelCamera extends CameraPlatform { @override Future getMinZoomLevel(int cameraId) async { - final minZoomLevel = await _channel.invokeMethod( + final double? minZoomLevel = await _channel.invokeMethod( 'getMinZoomLevel', {'cameraId': cameraId}, ); @@ -464,7 +467,8 @@ class MethodChannelCamera extends CameraPlatform { switch (call.method) { case 'orientation_changed': deviceEventStreamController.add(DeviceOrientationChangedEvent( - deserializeDeviceOrientation(call.arguments['orientation']))); + deserializeDeviceOrientation( + call.arguments['orientation']! as String))); break; default: throw MissingPluginException(); @@ -481,19 +485,19 @@ class MethodChannelCamera extends CameraPlatform { case 'initialized': cameraEventStreamController.add(CameraInitializedEvent( cameraId, - call.arguments['previewWidth'], - call.arguments['previewHeight'], - deserializeExposureMode(call.arguments['exposureMode']), - call.arguments['exposurePointSupported'], - deserializeFocusMode(call.arguments['focusMode']), - call.arguments['focusPointSupported'], + call.arguments['previewWidth']! as double, + call.arguments['previewHeight']! as double, + deserializeExposureMode(call.arguments['exposureMode']! as String), + call.arguments['exposurePointSupported']! as bool, + deserializeFocusMode(call.arguments['focusMode']! as String), + call.arguments['focusPointSupported']! as bool, )); break; case 'resolution_changed': cameraEventStreamController.add(CameraResolutionChangedEvent( cameraId, - call.arguments['captureWidth'], - call.arguments['captureHeight'], + call.arguments['captureWidth']! as double, + call.arguments['captureHeight']! as double, )); break; case 'camera_closing': @@ -504,16 +508,17 @@ class MethodChannelCamera extends CameraPlatform { case 'video_recorded': cameraEventStreamController.add(VideoRecordedEvent( cameraId, - XFile(call.arguments['path']), + XFile(call.arguments['path']! as String), call.arguments['maxVideoDuration'] != null - ? Duration(milliseconds: call.arguments['maxVideoDuration']) + ? Duration( + milliseconds: call.arguments['maxVideoDuration']! as int) : null, )); break; case 'error': cameraEventStreamController.add(CameraErrorEvent( cameraId, - call.arguments['description'], + call.arguments['description']! as String, )); break; default: diff --git a/packages/camera/camera_platform_interface/lib/src/types/camera_description.dart b/packages/camera/camera_platform_interface/lib/src/types/camera_description.dart index 98a39fd6c65e..8596c90ba91f 100644 --- a/packages/camera/camera_platform_interface/lib/src/types/camera_description.dart +++ b/packages/camera/camera_platform_interface/lib/src/types/camera_description.dart @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter/foundation.dart'; +import 'package:meta/meta.dart'; + /// The direction the camera is facing. enum CameraLensDirection { /// Front facing camera (a user looking at the screen is seen by the camera). @@ -15,9 +18,10 @@ enum CameraLensDirection { } /// Properties of a camera device. +@immutable class CameraDescription { /// Creates a new camera description with the given properties. - CameraDescription({ + const CameraDescription({ required this.name, required this.lensDirection, required this.sensorOrientation, @@ -51,6 +55,7 @@ class CameraDescription { @override String toString() { - return '$runtimeType($name, $lensDirection, $sensorOrientation)'; + return '${objectRuntimeType(this, 'CameraDescription')}(' + '$name, $lensDirection, $sensorOrientation)'; } } diff --git a/packages/camera/camera_platform_interface/lib/src/types/exposure_mode.dart b/packages/camera/camera_platform_interface/lib/src/types/exposure_mode.dart index 1debd19b3a26..56a05cd2d0f1 100644 --- a/packages/camera/camera_platform_interface/lib/src/types/exposure_mode.dart +++ b/packages/camera/camera_platform_interface/lib/src/types/exposure_mode.dart @@ -26,9 +26,9 @@ String serializeExposureMode(ExposureMode exposureMode) { /// Returns the exposure mode for a given String. ExposureMode deserializeExposureMode(String str) { switch (str) { - case "locked": + case 'locked': return ExposureMode.locked; - case "auto": + case 'auto': return ExposureMode.auto; default: throw ArgumentError('"$str" is not a valid ExposureMode value'); diff --git a/packages/camera/camera_platform_interface/lib/src/types/focus_mode.dart b/packages/camera/camera_platform_interface/lib/src/types/focus_mode.dart index 60a419155149..6baae0c1f63e 100644 --- a/packages/camera/camera_platform_interface/lib/src/types/focus_mode.dart +++ b/packages/camera/camera_platform_interface/lib/src/types/focus_mode.dart @@ -26,9 +26,9 @@ String serializeFocusMode(FocusMode focusMode) { /// Returns the focus mode for a given String. FocusMode deserializeFocusMode(String str) { switch (str) { - case "locked": + case 'locked': return FocusMode.locked; - case "auto": + case 'auto': return FocusMode.auto; default: throw ArgumentError('"$str" is not a valid FocusMode value'); diff --git a/packages/camera/camera_platform_interface/lib/src/types/types.dart b/packages/camera/camera_platform_interface/lib/src/types/types.dart index 0927458299df..0c24839d6445 100644 --- a/packages/camera/camera_platform_interface/lib/src/types/types.dart +++ b/packages/camera/camera_platform_interface/lib/src/types/types.dart @@ -3,9 +3,9 @@ // found in the LICENSE file. export 'camera_description.dart'; -export 'resolution_preset.dart'; export 'camera_exception.dart'; -export 'flash_mode.dart'; -export 'image_format_group.dart'; export 'exposure_mode.dart'; +export 'flash_mode.dart'; export 'focus_mode.dart'; +export 'image_format_group.dart'; +export 'resolution_preset.dart'; diff --git a/packages/camera/camera_platform_interface/lib/src/utils/utils.dart b/packages/camera/camera_platform_interface/lib/src/utils/utils.dart index 8c455867762f..663ec6da7a97 100644 --- a/packages/camera/camera_platform_interface/lib/src/utils/utils.dart +++ b/packages/camera/camera_platform_interface/lib/src/utils/utils.dart @@ -37,13 +37,13 @@ String serializeDeviceOrientation(DeviceOrientation orientation) { /// Returns the device orientation for a given String. DeviceOrientation deserializeDeviceOrientation(String str) { switch (str) { - case "portraitUp": + case 'portraitUp': return DeviceOrientation.portraitUp; - case "portraitDown": + case 'portraitDown': return DeviceOrientation.portraitDown; - case "landscapeRight": + case 'landscapeRight': return DeviceOrientation.landscapeRight; - case "landscapeLeft": + case 'landscapeLeft': return DeviceOrientation.landscapeLeft; default: throw ArgumentError('"$str" is not a valid DeviceOrientation value'); diff --git a/packages/camera/camera_platform_interface/pubspec.yaml b/packages/camera/camera_platform_interface/pubspec.yaml index 41c6a9705482..753e8d0d7aed 100644 --- a/packages/camera/camera_platform_interface/pubspec.yaml +++ b/packages/camera/camera_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/camera/camer issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.1.1 +version: 2.1.2 environment: sdk: '>=2.12.0 <3.0.0' diff --git a/packages/camera/camera_platform_interface/test/camera_platform_interface_test.dart b/packages/camera/camera_platform_interface/test/camera_platform_interface_test.dart index 750c27200692..3060089bef40 100644 --- a/packages/camera/camera_platform_interface/test/camera_platform_interface_test.dart +++ b/packages/camera/camera_platform_interface/test/camera_platform_interface_test.dart @@ -29,7 +29,7 @@ void main() { 'Default implementation of availableCameras() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -42,7 +42,7 @@ void main() { 'Default implementation of onCameraInitialized() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -55,7 +55,7 @@ void main() { 'Default implementation of onResolutionChanged() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -68,7 +68,7 @@ void main() { 'Default implementation of onCameraClosing() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -81,7 +81,7 @@ void main() { 'Default implementation of onCameraError() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -94,7 +94,7 @@ void main() { 'Default implementation of onDeviceOrientationChanged() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -107,7 +107,7 @@ void main() { 'Default implementation of lockCaptureOrientation() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -121,7 +121,7 @@ void main() { 'Default implementation of unlockCaptureOrientation() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -133,7 +133,7 @@ void main() { test('Default implementation of dispose() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -146,12 +146,12 @@ void main() { 'Default implementation of createCamera() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( () => cameraPlatform.createCamera( - CameraDescription( + const CameraDescription( name: 'back', lensDirection: CameraLensDirection.back, sensorOrientation: 0, @@ -166,7 +166,7 @@ void main() { 'Default implementation of initializeCamera() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -179,7 +179,7 @@ void main() { 'Default implementation of pauseVideoRecording() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -192,7 +192,7 @@ void main() { 'Default implementation of prepareForVideoRecording() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -205,7 +205,7 @@ void main() { 'Default implementation of resumeVideoRecording() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -218,7 +218,7 @@ void main() { 'Default implementation of setFlashMode() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -231,7 +231,7 @@ void main() { 'Default implementation of setExposureMode() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -244,7 +244,7 @@ void main() { 'Default implementation of setExposurePoint() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -257,7 +257,7 @@ void main() { 'Default implementation of getMinExposureOffset() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -270,7 +270,7 @@ void main() { 'Default implementation of getMaxExposureOffset() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -283,7 +283,7 @@ void main() { 'Default implementation of getExposureOffsetStepSize() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -296,7 +296,7 @@ void main() { 'Default implementation of setExposureOffset() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -309,7 +309,7 @@ void main() { 'Default implementation of setFocusMode() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -322,7 +322,7 @@ void main() { 'Default implementation of setFocusPoint() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -335,7 +335,7 @@ void main() { 'Default implementation of startVideoRecording() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -348,7 +348,7 @@ void main() { 'Default implementation of stopVideoRecording() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -361,7 +361,7 @@ void main() { 'Default implementation of takePicture() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -374,7 +374,7 @@ void main() { 'Default implementation of getMaxZoomLevel() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -387,7 +387,7 @@ void main() { 'Default implementation of getMinZoomLevel() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -400,7 +400,7 @@ void main() { 'Default implementation of setZoomLevel() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -413,7 +413,7 @@ void main() { 'Default implementation of pausePreview() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( @@ -426,7 +426,7 @@ void main() { 'Default implementation of resumePreview() should throw unimplemented error', () { // Arrange - final cameraPlatform = ExtendsCameraPlatform(); + final ExtendsCameraPlatform cameraPlatform = ExtendsCameraPlatform(); // Act & Assert expect( diff --git a/packages/camera/camera_platform_interface/test/events/camera_event_test.dart b/packages/camera/camera_platform_interface/test/events/camera_event_test.dart index 637358f557c3..a46486ed252c 100644 --- a/packages/camera/camera_platform_interface/test/events/camera_event_test.dart +++ b/packages/camera/camera_platform_interface/test/events/camera_event_test.dart @@ -12,7 +12,7 @@ void main() { group('CameraInitializedEvent tests', () { test('Constructor should initialize all properties', () { - final event = CameraInitializedEvent( + const CameraInitializedEvent event = CameraInitializedEvent( 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); expect(event.cameraId, 1); @@ -25,7 +25,8 @@ void main() { }); test('fromJson should initialize all properties', () { - final event = CameraInitializedEvent.fromJson({ + final CameraInitializedEvent event = + CameraInitializedEvent.fromJson(const { 'cameraId': 1, 'previewWidth': 1024.0, 'previewHeight': 640.0, @@ -45,10 +46,10 @@ void main() { }); test('toJson should return a map with all fields', () { - final event = CameraInitializedEvent( + const CameraInitializedEvent event = CameraInitializedEvent( 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); - final jsonMap = event.toJson(); + final Map jsonMap = event.toJson(); expect(jsonMap.length, 7); expect(jsonMap['cameraId'], 1); @@ -61,45 +62,45 @@ void main() { }); test('equals should return true if objects are the same', () { - final firstEvent = CameraInitializedEvent( + const CameraInitializedEvent firstEvent = CameraInitializedEvent( 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); - final secondEvent = CameraInitializedEvent( + const CameraInitializedEvent secondEvent = CameraInitializedEvent( 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); expect(firstEvent == secondEvent, true); }); test('equals should return false if cameraId is different', () { - final firstEvent = CameraInitializedEvent( + const CameraInitializedEvent firstEvent = CameraInitializedEvent( 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); - final secondEvent = CameraInitializedEvent( + const CameraInitializedEvent secondEvent = CameraInitializedEvent( 2, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); expect(firstEvent == secondEvent, false); }); test('equals should return false if previewWidth is different', () { - final firstEvent = CameraInitializedEvent( + const CameraInitializedEvent firstEvent = CameraInitializedEvent( 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); - final secondEvent = CameraInitializedEvent( + const CameraInitializedEvent secondEvent = CameraInitializedEvent( 1, 2048, 640, ExposureMode.auto, true, FocusMode.auto, true); expect(firstEvent == secondEvent, false); }); test('equals should return false if previewHeight is different', () { - final firstEvent = CameraInitializedEvent( + const CameraInitializedEvent firstEvent = CameraInitializedEvent( 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); - final secondEvent = CameraInitializedEvent( + const CameraInitializedEvent secondEvent = CameraInitializedEvent( 1, 1024, 980, ExposureMode.auto, true, FocusMode.auto, true); expect(firstEvent == secondEvent, false); }); test('equals should return false if exposureMode is different', () { - final firstEvent = CameraInitializedEvent( + const CameraInitializedEvent firstEvent = CameraInitializedEvent( 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); - final secondEvent = CameraInitializedEvent( + const CameraInitializedEvent secondEvent = CameraInitializedEvent( 1, 1024, 640, ExposureMode.locked, true, FocusMode.auto, true); expect(firstEvent == secondEvent, false); @@ -107,36 +108,36 @@ void main() { test('equals should return false if exposurePointSupported is different', () { - final firstEvent = CameraInitializedEvent( + const CameraInitializedEvent firstEvent = CameraInitializedEvent( 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); - final secondEvent = CameraInitializedEvent( + const CameraInitializedEvent secondEvent = CameraInitializedEvent( 1, 1024, 640, ExposureMode.auto, false, FocusMode.auto, true); expect(firstEvent == secondEvent, false); }); test('equals should return false if focusMode is different', () { - final firstEvent = CameraInitializedEvent( + const CameraInitializedEvent firstEvent = CameraInitializedEvent( 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); - final secondEvent = CameraInitializedEvent( + const CameraInitializedEvent secondEvent = CameraInitializedEvent( 1, 1024, 640, ExposureMode.auto, true, FocusMode.locked, true); expect(firstEvent == secondEvent, false); }); test('equals should return false if focusPointSupported is different', () { - final firstEvent = CameraInitializedEvent( + const CameraInitializedEvent firstEvent = CameraInitializedEvent( 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); - final secondEvent = CameraInitializedEvent( + const CameraInitializedEvent secondEvent = CameraInitializedEvent( 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, false); expect(firstEvent == secondEvent, false); }); test('hashCode should match hashCode of all properties', () { - final event = CameraInitializedEvent( + const CameraInitializedEvent event = CameraInitializedEvent( 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); - final expectedHashCode = event.cameraId.hashCode ^ + final int expectedHashCode = event.cameraId.hashCode ^ event.previewWidth.hashCode ^ event.previewHeight.hashCode ^ event.exposureMode.hashCode ^ @@ -150,7 +151,8 @@ void main() { group('CameraResolutionChangesEvent tests', () { test('Constructor should initialize all properties', () { - final event = CameraResolutionChangedEvent(1, 1024, 640); + const CameraResolutionChangedEvent event = + CameraResolutionChangedEvent(1, 1024, 640); expect(event.cameraId, 1); expect(event.captureWidth, 1024); @@ -158,7 +160,8 @@ void main() { }); test('fromJson should initialize all properties', () { - final event = CameraResolutionChangedEvent.fromJson({ + final CameraResolutionChangedEvent event = + CameraResolutionChangedEvent.fromJson(const { 'cameraId': 1, 'captureWidth': 1024.0, 'captureHeight': 640.0, @@ -170,9 +173,10 @@ void main() { }); test('toJson should return a map with all fields', () { - final event = CameraResolutionChangedEvent(1, 1024, 640); + const CameraResolutionChangedEvent event = + CameraResolutionChangedEvent(1, 1024, 640); - final jsonMap = event.toJson(); + final Map jsonMap = event.toJson(); expect(jsonMap.length, 3); expect(jsonMap['cameraId'], 1); @@ -181,36 +185,45 @@ void main() { }); test('equals should return true if objects are the same', () { - final firstEvent = CameraResolutionChangedEvent(1, 1024, 640); - final secondEvent = CameraResolutionChangedEvent(1, 1024, 640); + const CameraResolutionChangedEvent firstEvent = + CameraResolutionChangedEvent(1, 1024, 640); + const CameraResolutionChangedEvent secondEvent = + CameraResolutionChangedEvent(1, 1024, 640); expect(firstEvent == secondEvent, true); }); test('equals should return false if cameraId is different', () { - final firstEvent = CameraResolutionChangedEvent(1, 1024, 640); - final secondEvent = CameraResolutionChangedEvent(2, 1024, 640); + const CameraResolutionChangedEvent firstEvent = + CameraResolutionChangedEvent(1, 1024, 640); + const CameraResolutionChangedEvent secondEvent = + CameraResolutionChangedEvent(2, 1024, 640); expect(firstEvent == secondEvent, false); }); test('equals should return false if captureWidth is different', () { - final firstEvent = CameraResolutionChangedEvent(1, 1024, 640); - final secondEvent = CameraResolutionChangedEvent(1, 2048, 640); + const CameraResolutionChangedEvent firstEvent = + CameraResolutionChangedEvent(1, 1024, 640); + const CameraResolutionChangedEvent secondEvent = + CameraResolutionChangedEvent(1, 2048, 640); expect(firstEvent == secondEvent, false); }); test('equals should return false if captureHeight is different', () { - final firstEvent = CameraResolutionChangedEvent(1, 1024, 640); - final secondEvent = CameraResolutionChangedEvent(1, 1024, 980); + const CameraResolutionChangedEvent firstEvent = + CameraResolutionChangedEvent(1, 1024, 640); + const CameraResolutionChangedEvent secondEvent = + CameraResolutionChangedEvent(1, 1024, 980); expect(firstEvent == secondEvent, false); }); test('hashCode should match hashCode of all properties', () { - final event = CameraResolutionChangedEvent(1, 1024, 640); - final expectedHashCode = event.cameraId.hashCode ^ + const CameraResolutionChangedEvent event = + CameraResolutionChangedEvent(1, 1024, 640); + final int expectedHashCode = event.cameraId.hashCode ^ event.captureWidth.hashCode ^ event.captureHeight.hashCode; @@ -220,13 +233,14 @@ void main() { group('CameraClosingEvent tests', () { test('Constructor should initialize all properties', () { - final event = CameraClosingEvent(1); + const CameraClosingEvent event = CameraClosingEvent(1); expect(event.cameraId, 1); }); test('fromJson should initialize all properties', () { - final event = CameraClosingEvent.fromJson({ + final CameraClosingEvent event = + CameraClosingEvent.fromJson(const { 'cameraId': 1, }); @@ -234,31 +248,31 @@ void main() { }); test('toJson should return a map with all fields', () { - final event = CameraClosingEvent(1); + const CameraClosingEvent event = CameraClosingEvent(1); - final jsonMap = event.toJson(); + final Map jsonMap = event.toJson(); expect(jsonMap.length, 1); expect(jsonMap['cameraId'], 1); }); test('equals should return true if objects are the same', () { - final firstEvent = CameraClosingEvent(1); - final secondEvent = CameraClosingEvent(1); + const CameraClosingEvent firstEvent = CameraClosingEvent(1); + const CameraClosingEvent secondEvent = CameraClosingEvent(1); expect(firstEvent == secondEvent, true); }); test('equals should return false if cameraId is different', () { - final firstEvent = CameraClosingEvent(1); - final secondEvent = CameraClosingEvent(2); + const CameraClosingEvent firstEvent = CameraClosingEvent(1); + const CameraClosingEvent secondEvent = CameraClosingEvent(2); expect(firstEvent == secondEvent, false); }); test('hashCode should match hashCode of all properties', () { - final event = CameraClosingEvent(1); - final expectedHashCode = event.cameraId.hashCode; + const CameraClosingEvent event = CameraClosingEvent(1); + final int expectedHashCode = event.cameraId.hashCode; expect(event.hashCode, expectedHashCode); }); @@ -266,24 +280,24 @@ void main() { group('CameraErrorEvent tests', () { test('Constructor should initialize all properties', () { - final event = CameraErrorEvent(1, 'Error'); + const CameraErrorEvent event = CameraErrorEvent(1, 'Error'); expect(event.cameraId, 1); expect(event.description, 'Error'); }); test('fromJson should initialize all properties', () { - final event = CameraErrorEvent.fromJson( - {'cameraId': 1, 'description': 'Error'}); + final CameraErrorEvent event = CameraErrorEvent.fromJson( + const {'cameraId': 1, 'description': 'Error'}); expect(event.cameraId, 1); expect(event.description, 'Error'); }); test('toJson should return a map with all fields', () { - final event = CameraErrorEvent(1, 'Error'); + const CameraErrorEvent event = CameraErrorEvent(1, 'Error'); - final jsonMap = event.toJson(); + final Map jsonMap = event.toJson(); expect(jsonMap.length, 2); expect(jsonMap['cameraId'], 1); @@ -291,29 +305,29 @@ void main() { }); test('equals should return true if objects are the same', () { - final firstEvent = CameraErrorEvent(1, 'Error'); - final secondEvent = CameraErrorEvent(1, 'Error'); + const CameraErrorEvent firstEvent = CameraErrorEvent(1, 'Error'); + const CameraErrorEvent secondEvent = CameraErrorEvent(1, 'Error'); expect(firstEvent == secondEvent, true); }); test('equals should return false if cameraId is different', () { - final firstEvent = CameraErrorEvent(1, 'Error'); - final secondEvent = CameraErrorEvent(2, 'Error'); + const CameraErrorEvent firstEvent = CameraErrorEvent(1, 'Error'); + const CameraErrorEvent secondEvent = CameraErrorEvent(2, 'Error'); expect(firstEvent == secondEvent, false); }); test('equals should return false if description is different', () { - final firstEvent = CameraErrorEvent(1, 'Error'); - final secondEvent = CameraErrorEvent(1, 'Ooops'); + const CameraErrorEvent firstEvent = CameraErrorEvent(1, 'Error'); + const CameraErrorEvent secondEvent = CameraErrorEvent(1, 'Ooops'); expect(firstEvent == secondEvent, false); }); test('hashCode should match hashCode of all properties', () { - final event = CameraErrorEvent(1, 'Error'); - final expectedHashCode = + const CameraErrorEvent event = CameraErrorEvent(1, 'Error'); + final int expectedHashCode = event.cameraId.hashCode ^ event.description.hashCode; expect(event.hashCode, expectedHashCode); diff --git a/packages/camera/camera_platform_interface/test/events/device_event_test.dart b/packages/camera/camera_platform_interface/test/events/device_event_test.dart index f7cb657725a9..11f786c0df4c 100644 --- a/packages/camera/camera_platform_interface/test/events/device_event_test.dart +++ b/packages/camera/camera_platform_interface/test/events/device_event_test.dart @@ -11,13 +11,15 @@ void main() { group('DeviceOrientationChangedEvent tests', () { test('Constructor should initialize all properties', () { - final event = DeviceOrientationChangedEvent(DeviceOrientation.portraitUp); + const DeviceOrientationChangedEvent event = + DeviceOrientationChangedEvent(DeviceOrientation.portraitUp); expect(event.orientation, DeviceOrientation.portraitUp); }); test('fromJson should initialize all properties', () { - final event = DeviceOrientationChangedEvent.fromJson({ + final DeviceOrientationChangedEvent event = + DeviceOrientationChangedEvent.fromJson(const { 'orientation': 'portraitUp', }); @@ -25,35 +27,37 @@ void main() { }); test('toJson should return a map with all fields', () { - final event = DeviceOrientationChangedEvent(DeviceOrientation.portraitUp); + const DeviceOrientationChangedEvent event = + DeviceOrientationChangedEvent(DeviceOrientation.portraitUp); - final jsonMap = event.toJson(); + final Map jsonMap = event.toJson(); expect(jsonMap.length, 1); expect(jsonMap['orientation'], 'portraitUp'); }); test('equals should return true if objects are the same', () { - final firstEvent = + const DeviceOrientationChangedEvent firstEvent = DeviceOrientationChangedEvent(DeviceOrientation.portraitUp); - final secondEvent = + const DeviceOrientationChangedEvent secondEvent = DeviceOrientationChangedEvent(DeviceOrientation.portraitUp); expect(firstEvent == secondEvent, true); }); test('equals should return false if orientation is different', () { - final firstEvent = + const DeviceOrientationChangedEvent firstEvent = DeviceOrientationChangedEvent(DeviceOrientation.portraitUp); - final secondEvent = + const DeviceOrientationChangedEvent secondEvent = DeviceOrientationChangedEvent(DeviceOrientation.landscapeLeft); expect(firstEvent == secondEvent, false); }); test('hashCode should match hashCode of all properties', () { - final event = DeviceOrientationChangedEvent(DeviceOrientation.portraitUp); - final expectedHashCode = event.orientation.hashCode; + const DeviceOrientationChangedEvent event = + DeviceOrientationChangedEvent(DeviceOrientation.portraitUp); + final int expectedHashCode = event.orientation.hashCode; expect(event.hashCode, expectedHashCode); }); diff --git a/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart b/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart index ec71aa173fff..012b1907aff5 100644 --- a/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart +++ b/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart @@ -25,19 +25,19 @@ void main() { group('Creation, Initialization & Disposal Tests', () { test('Should send creation data and receive back a camera id', () async { // Arrange - MethodChannelMock cameraMockChannel = MethodChannelMock( + final MethodChannelMock cameraMockChannel = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: { - 'create': { + methods: { + 'create': { 'cameraId': 1, 'imageFormatGroup': 'unknown', } }); - final camera = MethodChannelCamera(); + final MethodChannelCamera camera = MethodChannelCamera(); // Act - final cameraId = await camera.createCamera( - CameraDescription( + final int cameraId = await camera.createCamera( + const CameraDescription( name: 'Test', lensDirection: CameraLensDirection.back, sensorOrientation: 0), @@ -48,7 +48,7 @@ void main() { expect(cameraMockChannel.log, [ isMethodCall( 'create', - arguments: { + arguments: { 'cameraName': 'Test', 'resolutionPreset': 'high', 'enableAudio': false @@ -62,18 +62,20 @@ void main() { 'Should throw CameraException when create throws a PlatformException', () { // Arrange - MethodChannelMock(channelName: 'plugins.flutter.io/camera', methods: { - 'create': PlatformException( - code: 'TESTING_ERROR_CODE', - message: 'Mock error message used during testing.', - ) - }); - final camera = MethodChannelCamera(); + MethodChannelMock( + channelName: 'plugins.flutter.io/camera', + methods: { + 'create': PlatformException( + code: 'TESTING_ERROR_CODE', + message: 'Mock error message used during testing.', + ) + }); + final MethodChannelCamera camera = MethodChannelCamera(); // Act expect( () => camera.createCamera( - CameraDescription( + const CameraDescription( name: 'Test', lensDirection: CameraLensDirection.back, sensorOrientation: 0, @@ -82,8 +84,9 @@ void main() { ), throwsA( isA() - .having((e) => e.code, 'code', 'TESTING_ERROR_CODE') - .having((e) => e.description, 'description', + .having( + (CameraException e) => e.code, 'code', 'TESTING_ERROR_CODE') + .having((CameraException e) => e.description, 'description', 'Mock error message used during testing.'), ), ); @@ -93,18 +96,20 @@ void main() { 'Should throw CameraException when create throws a PlatformException', () { // Arrange - MethodChannelMock(channelName: 'plugins.flutter.io/camera', methods: { - 'create': PlatformException( - code: 'TESTING_ERROR_CODE', - message: 'Mock error message used during testing.', - ) - }); - final camera = MethodChannelCamera(); + MethodChannelMock( + channelName: 'plugins.flutter.io/camera', + methods: { + 'create': PlatformException( + code: 'TESTING_ERROR_CODE', + message: 'Mock error message used during testing.', + ) + }); + final MethodChannelCamera camera = MethodChannelCamera(); // Act expect( () => camera.createCamera( - CameraDescription( + const CameraDescription( name: 'Test', lensDirection: CameraLensDirection.back, sensorOrientation: 0, @@ -113,8 +118,9 @@ void main() { ), throwsA( isA() - .having((e) => e.code, 'code', 'TESTING_ERROR_CODE') - .having((e) => e.description, 'description', + .having( + (CameraException e) => e.code, 'code', 'TESTING_ERROR_CODE') + .having((CameraException e) => e.description, 'description', 'Mock error message used during testing.'), ), ); @@ -122,18 +128,18 @@ void main() { test('Should send initialization data', () async { // Arrange - MethodChannelMock cameraMockChannel = MethodChannelMock( + final MethodChannelMock cameraMockChannel = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: { - 'create': { + methods: { + 'create': { 'cameraId': 1, 'imageFormatGroup': 'unknown', }, 'initialize': null }); - final camera = MethodChannelCamera(); - final cameraId = await camera.createCamera( - CameraDescription( + final MethodChannelCamera camera = MethodChannelCamera(); + final int cameraId = await camera.createCamera( + const CameraDescription( name: 'Test', lensDirection: CameraLensDirection.back, sensorOrientation: 0, @@ -142,7 +148,7 @@ void main() { ); // Act - Future initializeFuture = camera.initializeCamera(cameraId); + final Future initializeFuture = camera.initializeCamera(cameraId); camera.cameraEventStreamController.add(CameraInitializedEvent( cameraId, 1920, @@ -160,7 +166,7 @@ void main() { anything, isMethodCall( 'initialize', - arguments: { + arguments: { 'cameraId': 1, 'imageFormatGroup': 'unknown', }, @@ -170,24 +176,24 @@ void main() { test('Should send a disposal call on dispose', () async { // Arrange - MethodChannelMock cameraMockChannel = MethodChannelMock( + final MethodChannelMock cameraMockChannel = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: { - 'create': {'cameraId': 1}, + methods: { + 'create': {'cameraId': 1}, 'initialize': null, - 'dispose': {'cameraId': 1} + 'dispose': {'cameraId': 1} }); - final camera = MethodChannelCamera(); - final cameraId = await camera.createCamera( - CameraDescription( + final MethodChannelCamera camera = MethodChannelCamera(); + final int cameraId = await camera.createCamera( + const CameraDescription( name: 'Test', lensDirection: CameraLensDirection.back, sensorOrientation: 0, ), ResolutionPreset.high, ); - Future initializeFuture = camera.initializeCamera(cameraId); + final Future initializeFuture = camera.initializeCamera(cameraId); camera.cameraEventStreamController.add(CameraInitializedEvent( cameraId, 1920, @@ -209,7 +215,7 @@ void main() { anything, isMethodCall( 'dispose', - arguments: {'cameraId': 1}, + arguments: {'cameraId': 1}, ), ]); }); @@ -221,21 +227,21 @@ void main() { setUp(() async { MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: { - 'create': {'cameraId': 1}, + methods: { + 'create': {'cameraId': 1}, 'initialize': null }, ); camera = MethodChannelCamera(); cameraId = await camera.createCamera( - CameraDescription( + const CameraDescription( name: 'Test', lensDirection: CameraLensDirection.back, sensorOrientation: 0, ), ResolutionPreset.high, ); - Future initializeFuture = camera.initializeCamera(cameraId); + final Future initializeFuture = camera.initializeCamera(cameraId); camera.cameraEventStreamController.add(CameraInitializedEvent( cameraId, 1920, @@ -252,10 +258,11 @@ void main() { // Act final Stream eventStream = camera.onCameraInitialized(cameraId); - final streamQueue = StreamQueue(eventStream); + final StreamQueue streamQueue = + StreamQueue(eventStream); // Emit test events - final event = CameraInitializedEvent( + final CameraInitializedEvent event = CameraInitializedEvent( cameraId, 3840, 2160, @@ -278,11 +285,14 @@ void main() { // Act final Stream resolutionStream = camera.onCameraResolutionChanged(cameraId); - final streamQueue = StreamQueue(resolutionStream); + final StreamQueue streamQueue = + StreamQueue(resolutionStream); // Emit test events - final fhdEvent = CameraResolutionChangedEvent(cameraId, 1920, 1080); - final uhdEvent = CameraResolutionChangedEvent(cameraId, 3840, 2160); + final CameraResolutionChangedEvent fhdEvent = + CameraResolutionChangedEvent(cameraId, 1920, 1080); + final CameraResolutionChangedEvent uhdEvent = + CameraResolutionChangedEvent(cameraId, 3840, 2160); await camera.handleCameraMethodCall( MethodCall('resolution_changed', fhdEvent.toJson()), cameraId); await camera.handleCameraMethodCall( @@ -306,10 +316,11 @@ void main() { // Act final Stream eventStream = camera.onCameraClosing(cameraId); - final streamQueue = StreamQueue(eventStream); + final StreamQueue streamQueue = + StreamQueue(eventStream); // Emit test events - final event = CameraClosingEvent(cameraId); + final CameraClosingEvent event = CameraClosingEvent(cameraId); await camera.handleCameraMethodCall( MethodCall('camera_closing', event.toJson()), cameraId); await camera.handleCameraMethodCall( @@ -328,11 +339,14 @@ void main() { test('Should receive camera error events', () async { // Act - final errorStream = camera.onCameraError(cameraId); - final streamQueue = StreamQueue(errorStream); + final Stream errorStream = + camera.onCameraError(cameraId); + final StreamQueue streamQueue = + StreamQueue(errorStream); // Emit test events - final event = CameraErrorEvent(cameraId, 'Error Description'); + final CameraErrorEvent event = + CameraErrorEvent(cameraId, 'Error Description'); await camera.handleCameraMethodCall( MethodCall('error', event.toJson()), cameraId); await camera.handleCameraMethodCall( @@ -351,11 +365,13 @@ void main() { test('Should receive device orientation change events', () async { // Act - final eventStream = camera.onDeviceOrientationChanged(); - final streamQueue = StreamQueue(eventStream); + final Stream eventStream = + camera.onDeviceOrientationChanged(); + final StreamQueue streamQueue = + StreamQueue(eventStream); // Emit test events - final event = + const DeviceOrientationChangedEvent event = DeviceOrientationChangedEvent(DeviceOrientation.portraitUp); await camera.handleDeviceMethodCall( MethodCall('orientation_changed', event.toJson())); @@ -381,21 +397,21 @@ void main() { setUp(() async { MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: { - 'create': {'cameraId': 1}, + methods: { + 'create': {'cameraId': 1}, 'initialize': null }, ); camera = MethodChannelCamera(); cameraId = await camera.createCamera( - CameraDescription( + const CameraDescription( name: 'Test', lensDirection: CameraLensDirection.back, sensorOrientation: 0, ), ResolutionPreset.high, ); - Future initializeFuture = camera.initializeCamera(cameraId); + final Future initializeFuture = camera.initializeCamera(cameraId); camera.cameraEventStreamController.add( CameraInitializedEvent( cameraId, @@ -413,17 +429,25 @@ void main() { test('Should fetch CameraDescription instances for available cameras', () async { // Arrange - List> returnData = [ - {'name': 'Test 1', 'lensFacing': 'front', 'sensorOrientation': 1}, - {'name': 'Test 2', 'lensFacing': 'back', 'sensorOrientation': 2} + final List returnData = [ + { + 'name': 'Test 1', + 'lensFacing': 'front', + 'sensorOrientation': 1 + }, + { + 'name': 'Test 2', + 'lensFacing': 'back', + 'sensorOrientation': 2 + } ]; - MethodChannelMock channel = MethodChannelMock( + final MethodChannelMock channel = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: {'availableCameras': returnData}, + methods: {'availableCameras': returnData}, ); // Act - List cameras = await camera.availableCameras(); + final List cameras = await camera.availableCameras(); // Assert expect(channel.log, [ @@ -431,11 +455,11 @@ void main() { ]); expect(cameras.length, returnData.length); for (int i = 0; i < returnData.length; i++) { - CameraDescription cameraDescription = CameraDescription( - name: returnData[i]['name'], - lensDirection: - parseCameraLensDirection(returnData[i]['lensFacing']), - sensorOrientation: returnData[i]['sensorOrientation'], + final CameraDescription cameraDescription = CameraDescription( + name: returnData[i]['name']! as String, + lensDirection: parseCameraLensDirection( + returnData[i]['lensFacing']! as String), + sensorOrientation: returnData[i]['sensorOrientation']! as int, ); expect(cameras[i], cameraDescription); } @@ -445,20 +469,23 @@ void main() { 'Should throw CameraException when availableCameras throws a PlatformException', () { // Arrange - MethodChannelMock(channelName: 'plugins.flutter.io/camera', methods: { - 'availableCameras': PlatformException( - code: 'TESTING_ERROR_CODE', - message: 'Mock error message used during testing.', - ) - }); + MethodChannelMock( + channelName: 'plugins.flutter.io/camera', + methods: { + 'availableCameras': PlatformException( + code: 'TESTING_ERROR_CODE', + message: 'Mock error message used during testing.', + ) + }); // Act expect( camera.availableCameras, throwsA( isA() - .having((e) => e.code, 'code', 'TESTING_ERROR_CODE') - .having((e) => e.description, 'description', + .having( + (CameraException e) => e.code, 'code', 'TESTING_ERROR_CODE') + .having((CameraException e) => e.description, 'description', 'Mock error message used during testing.'), ), ); @@ -466,16 +493,16 @@ void main() { test('Should take a picture and return an XFile instance', () async { // Arrange - MethodChannelMock channel = MethodChannelMock( + final MethodChannelMock channel = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: {'takePicture': '/test/path.jpg'}); + methods: {'takePicture': '/test/path.jpg'}); // Act - XFile file = await camera.takePicture(cameraId); + final XFile file = await camera.takePicture(cameraId); // Assert expect(channel.log, [ - isMethodCall('takePicture', arguments: { + isMethodCall('takePicture', arguments: { 'cameraId': cameraId, }), ]); @@ -484,9 +511,9 @@ void main() { test('Should prepare for video recording', () async { // Arrange - MethodChannelMock channel = MethodChannelMock( + final MethodChannelMock channel = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: {'prepareForVideoRecording': null}, + methods: {'prepareForVideoRecording': null}, ); // Act @@ -500,9 +527,9 @@ void main() { test('Should start recording a video', () async { // Arrange - MethodChannelMock channel = MethodChannelMock( + final MethodChannelMock channel = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: {'startVideoRecording': null}, + methods: {'startVideoRecording': null}, ); // Act @@ -510,7 +537,7 @@ void main() { // Assert expect(channel.log, [ - isMethodCall('startVideoRecording', arguments: { + isMethodCall('startVideoRecording', arguments: { 'cameraId': cameraId, 'maxVideoDuration': null, }), @@ -520,37 +547,39 @@ void main() { test('Should pass maxVideoDuration when starting recording a video', () async { // Arrange - MethodChannelMock channel = MethodChannelMock( + final MethodChannelMock channel = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: {'startVideoRecording': null}, + methods: {'startVideoRecording': null}, ); // Act await camera.startVideoRecording( cameraId, - maxVideoDuration: Duration(seconds: 10), + maxVideoDuration: const Duration(seconds: 10), ); // Assert expect(channel.log, [ - isMethodCall('startVideoRecording', - arguments: {'cameraId': cameraId, 'maxVideoDuration': 10000}), + isMethodCall('startVideoRecording', arguments: { + 'cameraId': cameraId, + 'maxVideoDuration': 10000 + }), ]); }); test('Should stop a video recording and return the file', () async { // Arrange - MethodChannelMock channel = MethodChannelMock( + final MethodChannelMock channel = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: {'stopVideoRecording': '/test/path.mp4'}, + methods: {'stopVideoRecording': '/test/path.mp4'}, ); // Act - XFile file = await camera.stopVideoRecording(cameraId); + final XFile file = await camera.stopVideoRecording(cameraId); // Assert expect(channel.log, [ - isMethodCall('stopVideoRecording', arguments: { + isMethodCall('stopVideoRecording', arguments: { 'cameraId': cameraId, }), ]); @@ -559,9 +588,9 @@ void main() { test('Should pause a video recording', () async { // Arrange - MethodChannelMock channel = MethodChannelMock( + final MethodChannelMock channel = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: {'pauseVideoRecording': null}, + methods: {'pauseVideoRecording': null}, ); // Act @@ -569,7 +598,7 @@ void main() { // Assert expect(channel.log, [ - isMethodCall('pauseVideoRecording', arguments: { + isMethodCall('pauseVideoRecording', arguments: { 'cameraId': cameraId, }), ]); @@ -577,9 +606,9 @@ void main() { test('Should resume a video recording', () async { // Arrange - MethodChannelMock channel = MethodChannelMock( + final MethodChannelMock channel = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: {'resumeVideoRecording': null}, + methods: {'resumeVideoRecording': null}, ); // Act @@ -587,7 +616,7 @@ void main() { // Assert expect(channel.log, [ - isMethodCall('resumeVideoRecording', arguments: { + isMethodCall('resumeVideoRecording', arguments: { 'cameraId': cameraId, }), ]); @@ -595,9 +624,9 @@ void main() { test('Should set the flash mode', () async { // Arrange - MethodChannelMock channel = MethodChannelMock( + final MethodChannelMock channel = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: {'setFlashMode': null}, + methods: {'setFlashMode': null}, ); // Act @@ -608,22 +637,30 @@ void main() { // Assert expect(channel.log, [ - isMethodCall('setFlashMode', - arguments: {'cameraId': cameraId, 'mode': 'torch'}), - isMethodCall('setFlashMode', - arguments: {'cameraId': cameraId, 'mode': 'always'}), - isMethodCall('setFlashMode', - arguments: {'cameraId': cameraId, 'mode': 'auto'}), - isMethodCall('setFlashMode', - arguments: {'cameraId': cameraId, 'mode': 'off'}), + isMethodCall('setFlashMode', arguments: { + 'cameraId': cameraId, + 'mode': 'torch' + }), + isMethodCall('setFlashMode', arguments: { + 'cameraId': cameraId, + 'mode': 'always' + }), + isMethodCall('setFlashMode', arguments: { + 'cameraId': cameraId, + 'mode': 'auto' + }), + isMethodCall('setFlashMode', arguments: { + 'cameraId': cameraId, + 'mode': 'off' + }), ]); }); test('Should set the exposure mode', () async { // Arrange - MethodChannelMock channel = MethodChannelMock( + final MethodChannelMock channel = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: {'setExposureMode': null}, + methods: {'setExposureMode': null}, ); // Act @@ -632,33 +669,37 @@ void main() { // Assert expect(channel.log, [ - isMethodCall('setExposureMode', - arguments: {'cameraId': cameraId, 'mode': 'auto'}), - isMethodCall('setExposureMode', - arguments: {'cameraId': cameraId, 'mode': 'locked'}), + isMethodCall('setExposureMode', arguments: { + 'cameraId': cameraId, + 'mode': 'auto' + }), + isMethodCall('setExposureMode', arguments: { + 'cameraId': cameraId, + 'mode': 'locked' + }), ]); }); test('Should set the exposure point', () async { // Arrange - MethodChannelMock channel = MethodChannelMock( + final MethodChannelMock channel = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: {'setExposurePoint': null}, + methods: {'setExposurePoint': null}, ); // Act - await camera.setExposurePoint(cameraId, Point(0.5, 0.5)); + await camera.setExposurePoint(cameraId, const Point(0.5, 0.5)); await camera.setExposurePoint(cameraId, null); // Assert expect(channel.log, [ - isMethodCall('setExposurePoint', arguments: { + isMethodCall('setExposurePoint', arguments: { 'cameraId': cameraId, 'x': 0.5, 'y': 0.5, 'reset': false }), - isMethodCall('setExposurePoint', arguments: { + isMethodCall('setExposurePoint', arguments: { 'cameraId': cameraId, 'x': null, 'y': null, @@ -669,18 +710,19 @@ void main() { test('Should get the min exposure offset', () async { // Arrange - MethodChannelMock channel = MethodChannelMock( + final MethodChannelMock channel = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: {'getMinExposureOffset': 2.0}, + methods: {'getMinExposureOffset': 2.0}, ); // Act - final minExposureOffset = await camera.getMinExposureOffset(cameraId); + final double minExposureOffset = + await camera.getMinExposureOffset(cameraId); // Assert expect(minExposureOffset, 2.0); expect(channel.log, [ - isMethodCall('getMinExposureOffset', arguments: { + isMethodCall('getMinExposureOffset', arguments: { 'cameraId': cameraId, }), ]); @@ -688,18 +730,19 @@ void main() { test('Should get the max exposure offset', () async { // Arrange - MethodChannelMock channel = MethodChannelMock( + final MethodChannelMock channel = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: {'getMaxExposureOffset': 2.0}, + methods: {'getMaxExposureOffset': 2.0}, ); // Act - final maxExposureOffset = await camera.getMaxExposureOffset(cameraId); + final double maxExposureOffset = + await camera.getMaxExposureOffset(cameraId); // Assert expect(maxExposureOffset, 2.0); expect(channel.log, [ - isMethodCall('getMaxExposureOffset', arguments: { + isMethodCall('getMaxExposureOffset', arguments: { 'cameraId': cameraId, }), ]); @@ -707,37 +750,40 @@ void main() { test('Should get the exposure offset step size', () async { // Arrange - MethodChannelMock channel = MethodChannelMock( + final MethodChannelMock channel = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: {'getExposureOffsetStepSize': 0.25}, + methods: {'getExposureOffsetStepSize': 0.25}, ); // Act - final stepSize = await camera.getExposureOffsetStepSize(cameraId); + final double stepSize = + await camera.getExposureOffsetStepSize(cameraId); // Assert expect(stepSize, 0.25); expect(channel.log, [ - isMethodCall('getExposureOffsetStepSize', arguments: { - 'cameraId': cameraId, - }), + isMethodCall('getExposureOffsetStepSize', + arguments: { + 'cameraId': cameraId, + }), ]); }); test('Should set the exposure offset', () async { // Arrange - MethodChannelMock channel = MethodChannelMock( + final MethodChannelMock channel = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: {'setExposureOffset': 0.6}, + methods: {'setExposureOffset': 0.6}, ); // Act - final actualOffset = await camera.setExposureOffset(cameraId, 0.5); + final double actualOffset = + await camera.setExposureOffset(cameraId, 0.5); // Assert expect(actualOffset, 0.6); expect(channel.log, [ - isMethodCall('setExposureOffset', arguments: { + isMethodCall('setExposureOffset', arguments: { 'cameraId': cameraId, 'offset': 0.5, }), @@ -746,9 +792,9 @@ void main() { test('Should set the focus mode', () async { // Arrange - MethodChannelMock channel = MethodChannelMock( + final MethodChannelMock channel = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: {'setFocusMode': null}, + methods: {'setFocusMode': null}, ); // Act @@ -757,33 +803,37 @@ void main() { // Assert expect(channel.log, [ - isMethodCall('setFocusMode', - arguments: {'cameraId': cameraId, 'mode': 'auto'}), - isMethodCall('setFocusMode', - arguments: {'cameraId': cameraId, 'mode': 'locked'}), + isMethodCall('setFocusMode', arguments: { + 'cameraId': cameraId, + 'mode': 'auto' + }), + isMethodCall('setFocusMode', arguments: { + 'cameraId': cameraId, + 'mode': 'locked' + }), ]); }); test('Should set the exposure point', () async { // Arrange - MethodChannelMock channel = MethodChannelMock( + final MethodChannelMock channel = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: {'setFocusPoint': null}, + methods: {'setFocusPoint': null}, ); // Act - await camera.setFocusPoint(cameraId, Point(0.5, 0.5)); + await camera.setFocusPoint(cameraId, const Point(0.5, 0.5)); await camera.setFocusPoint(cameraId, null); // Assert expect(channel.log, [ - isMethodCall('setFocusPoint', arguments: { + isMethodCall('setFocusPoint', arguments: { 'cameraId': cameraId, 'x': 0.5, 'y': 0.5, 'reset': false }), - isMethodCall('setFocusPoint', arguments: { + isMethodCall('setFocusPoint', arguments: { 'cameraId': cameraId, 'x': null, 'y': null, @@ -794,7 +844,7 @@ void main() { test('Should build a texture widget as preview widget', () async { // Act - Widget widget = camera.buildPreview(cameraId); + final Widget widget = camera.buildPreview(cameraId); // Act expect(widget is Texture, isTrue); @@ -803,28 +853,28 @@ void main() { test('Should throw MissingPluginException when handling unknown method', () { - final camera = MethodChannelCamera(); + final MethodChannelCamera camera = MethodChannelCamera(); expect( - () => - camera.handleCameraMethodCall(MethodCall('unknown_method'), 1), + () => camera.handleCameraMethodCall( + const MethodCall('unknown_method'), 1), throwsA(isA())); }); test('Should get the max zoom level', () async { // Arrange - MethodChannelMock channel = MethodChannelMock( + final MethodChannelMock channel = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: {'getMaxZoomLevel': 10.0}, + methods: {'getMaxZoomLevel': 10.0}, ); // Act - final maxZoomLevel = await camera.getMaxZoomLevel(cameraId); + final double maxZoomLevel = await camera.getMaxZoomLevel(cameraId); // Assert expect(maxZoomLevel, 10.0); expect(channel.log, [ - isMethodCall('getMaxZoomLevel', arguments: { + isMethodCall('getMaxZoomLevel', arguments: { 'cameraId': cameraId, }), ]); @@ -832,18 +882,18 @@ void main() { test('Should get the min zoom level', () async { // Arrange - MethodChannelMock channel = MethodChannelMock( + final MethodChannelMock channel = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: {'getMinZoomLevel': 1.0}, + methods: {'getMinZoomLevel': 1.0}, ); // Act - final maxZoomLevel = await camera.getMinZoomLevel(cameraId); + final double maxZoomLevel = await camera.getMinZoomLevel(cameraId); // Assert expect(maxZoomLevel, 1.0); expect(channel.log, [ - isMethodCall('getMinZoomLevel', arguments: { + isMethodCall('getMinZoomLevel', arguments: { 'cameraId': cameraId, }), ]); @@ -851,9 +901,9 @@ void main() { test('Should set the zoom level', () async { // Arrange - MethodChannelMock channel = MethodChannelMock( + final MethodChannelMock channel = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: {'setZoomLevel': null}, + methods: {'setZoomLevel': null}, ); // Act @@ -862,7 +912,7 @@ void main() { // Assert expect(channel.log, [ isMethodCall('setZoomLevel', - arguments: {'cameraId': cameraId, 'zoom': 2.0}), + arguments: {'cameraId': cameraId, 'zoom': 2.0}), ]); }); @@ -871,7 +921,7 @@ void main() { // Arrange MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: { + methods: { 'setZoomLevel': PlatformException( code: 'ZOOM_ERROR', message: 'Illegal zoom error', @@ -884,16 +934,16 @@ void main() { expect( () => camera.setZoomLevel(cameraId, -1.0), throwsA(isA() - .having((e) => e.code, 'code', 'ZOOM_ERROR') - .having((e) => e.description, 'description', + .having((CameraException e) => e.code, 'code', 'ZOOM_ERROR') + .having((CameraException e) => e.description, 'description', 'Illegal zoom error'))); }); test('Should lock the capture orientation', () async { // Arrange - MethodChannelMock channel = MethodChannelMock( + final MethodChannelMock channel = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: {'lockCaptureOrientation': null}, + methods: {'lockCaptureOrientation': null}, ); // Act @@ -902,16 +952,18 @@ void main() { // Assert expect(channel.log, [ - isMethodCall('lockCaptureOrientation', - arguments: {'cameraId': cameraId, 'orientation': 'portraitUp'}), + isMethodCall('lockCaptureOrientation', arguments: { + 'cameraId': cameraId, + 'orientation': 'portraitUp' + }), ]); }); test('Should unlock the capture orientation', () async { // Arrange - MethodChannelMock channel = MethodChannelMock( + final MethodChannelMock channel = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: {'unlockCaptureOrientation': null}, + methods: {'unlockCaptureOrientation': null}, ); // Act @@ -920,15 +972,15 @@ void main() { // Assert expect(channel.log, [ isMethodCall('unlockCaptureOrientation', - arguments: {'cameraId': cameraId}), + arguments: {'cameraId': cameraId}), ]); }); test('Should pause the camera preview', () async { // Arrange - MethodChannelMock channel = MethodChannelMock( + final MethodChannelMock channel = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: {'pausePreview': null}, + methods: {'pausePreview': null}, ); // Act @@ -936,15 +988,16 @@ void main() { // Assert expect(channel.log, [ - isMethodCall('pausePreview', arguments: {'cameraId': cameraId}), + isMethodCall('pausePreview', + arguments: {'cameraId': cameraId}), ]); }); test('Should resume the camera preview', () async { // Arrange - MethodChannelMock channel = MethodChannelMock( + final MethodChannelMock channel = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: {'resumePreview': null}, + methods: {'resumePreview': null}, ); // Act @@ -952,7 +1005,8 @@ void main() { // Assert expect(channel.log, [ - isMethodCall('resumePreview', arguments: {'cameraId': cameraId}), + isMethodCall('resumePreview', + arguments: {'cameraId': cameraId}), ]); }); }); diff --git a/packages/camera/camera_platform_interface/test/types/camera_description_test.dart b/packages/camera/camera_platform_interface/test/types/camera_description_test.dart index 11a5210e831b..3d3aaaeb4086 100644 --- a/packages/camera/camera_platform_interface/test/types/camera_description_test.dart +++ b/packages/camera/camera_platform_interface/test/types/camera_description_test.dart @@ -10,13 +10,13 @@ void main() { group('CameraLensDirection tests', () { test('CameraLensDirection should contain 3 options', () { - final values = CameraLensDirection.values; + const List values = CameraLensDirection.values; expect(values.length, 3); }); - test("CameraLensDirection enum should have items in correct index", () { - final values = CameraLensDirection.values; + test('CameraLensDirection enum should have items in correct index', () { + const List values = CameraLensDirection.values; expect(values[0], CameraLensDirection.front); expect(values[1], CameraLensDirection.back); @@ -26,7 +26,7 @@ void main() { group('CameraDescription tests', () { test('Constructor should initialize all properties', () { - final description = CameraDescription( + const CameraDescription description = CameraDescription( name: 'Test', lensDirection: CameraLensDirection.front, sensorOrientation: 90, @@ -38,12 +38,12 @@ void main() { }); test('equals should return true if objects are the same', () { - final firstDescription = CameraDescription( + const CameraDescription firstDescription = CameraDescription( name: 'Test', lensDirection: CameraLensDirection.front, sensorOrientation: 90, ); - final secondDescription = CameraDescription( + const CameraDescription secondDescription = CameraDescription( name: 'Test', lensDirection: CameraLensDirection.front, sensorOrientation: 90, @@ -53,12 +53,12 @@ void main() { }); test('equals should return false if name is different', () { - final firstDescription = CameraDescription( + const CameraDescription firstDescription = CameraDescription( name: 'Test', lensDirection: CameraLensDirection.front, sensorOrientation: 90, ); - final secondDescription = CameraDescription( + const CameraDescription secondDescription = CameraDescription( name: 'Testing', lensDirection: CameraLensDirection.front, sensorOrientation: 90, @@ -68,12 +68,12 @@ void main() { }); test('equals should return false if lens direction is different', () { - final firstDescription = CameraDescription( + const CameraDescription firstDescription = CameraDescription( name: 'Test', lensDirection: CameraLensDirection.front, sensorOrientation: 90, ); - final secondDescription = CameraDescription( + const CameraDescription secondDescription = CameraDescription( name: 'Test', lensDirection: CameraLensDirection.back, sensorOrientation: 90, @@ -83,12 +83,12 @@ void main() { }); test('equals should return true if sensor orientation is different', () { - final firstDescription = CameraDescription( + const CameraDescription firstDescription = CameraDescription( name: 'Test', lensDirection: CameraLensDirection.front, sensorOrientation: 0, ); - final secondDescription = CameraDescription( + const CameraDescription secondDescription = CameraDescription( name: 'Test', lensDirection: CameraLensDirection.front, sensorOrientation: 90, @@ -98,12 +98,12 @@ void main() { }); test('hashCode should match hashCode of all properties', () { - final description = CameraDescription( + const CameraDescription description = CameraDescription( name: 'Test', lensDirection: CameraLensDirection.front, sensorOrientation: 0, ); - final expectedHashCode = description.name.hashCode ^ + final int expectedHashCode = description.name.hashCode ^ description.lensDirection.hashCode ^ description.sensorOrientation.hashCode; diff --git a/packages/camera/camera_platform_interface/test/types/camera_exception_test.dart b/packages/camera/camera_platform_interface/test/types/camera_exception_test.dart index 5fb753fb3616..27baa9cdbe51 100644 --- a/packages/camera/camera_platform_interface/test/types/camera_exception_test.dart +++ b/packages/camera/camera_platform_interface/test/types/camera_exception_test.dart @@ -7,21 +7,21 @@ import 'package:flutter_test/flutter_test.dart'; void main() { test('constructor should initialize properties', () { - final code = 'TEST_ERROR'; - final description = 'This is a test error'; - final exception = CameraException(code, description); + const String code = 'TEST_ERROR'; + const String description = 'This is a test error'; + final CameraException exception = CameraException(code, description); expect(exception.code, code); expect(exception.description, description); }); test('toString: Should return a description of the exception', () { - final code = 'TEST_ERROR'; - final description = 'This is a test error'; - final expected = 'CameraException($code, $description)'; - final exception = CameraException(code, description); + const String code = 'TEST_ERROR'; + const String description = 'This is a test error'; + const String expected = 'CameraException($code, $description)'; + final CameraException exception = CameraException(code, description); - final actual = exception.toString(); + final String actual = exception.toString(); expect(actual, expected); }); diff --git a/packages/camera/camera_platform_interface/test/types/exposure_mode_test.dart b/packages/camera/camera_platform_interface/test/types/exposure_mode_test.dart index 659b75e017f1..7dd382450228 100644 --- a/packages/camera/camera_platform_interface/test/types/exposure_mode_test.dart +++ b/packages/camera/camera_platform_interface/test/types/exposure_mode_test.dart @@ -8,24 +8,24 @@ import 'package:flutter_test/flutter_test.dart'; void main() { test('ExposureMode should contain 2 options', () { - final values = ExposureMode.values; + const List values = ExposureMode.values; expect(values.length, 2); }); - test("ExposureMode enum should have items in correct index", () { - final values = ExposureMode.values; + test('ExposureMode enum should have items in correct index', () { + const List values = ExposureMode.values; expect(values[0], ExposureMode.auto); expect(values[1], ExposureMode.locked); }); - test("serializeExposureMode() should serialize correctly", () { - expect(serializeExposureMode(ExposureMode.auto), "auto"); - expect(serializeExposureMode(ExposureMode.locked), "locked"); + test('serializeExposureMode() should serialize correctly', () { + expect(serializeExposureMode(ExposureMode.auto), 'auto'); + expect(serializeExposureMode(ExposureMode.locked), 'locked'); }); - test("deserializeExposureMode() should deserialize correctly", () { + test('deserializeExposureMode() should deserialize correctly', () { expect(deserializeExposureMode('auto'), ExposureMode.auto); expect(deserializeExposureMode('locked'), ExposureMode.locked); }); diff --git a/packages/camera/camera_platform_interface/test/types/flash_mode_test.dart b/packages/camera/camera_platform_interface/test/types/flash_mode_test.dart index d313bcf52b77..bfc38a09580a 100644 --- a/packages/camera/camera_platform_interface/test/types/flash_mode_test.dart +++ b/packages/camera/camera_platform_interface/test/types/flash_mode_test.dart @@ -7,13 +7,13 @@ import 'package:flutter_test/flutter_test.dart'; void main() { test('FlashMode should contain 4 options', () { - final values = FlashMode.values; + const List values = FlashMode.values; expect(values.length, 4); }); - test("FlashMode enum should have items in correct index", () { - final values = FlashMode.values; + test('FlashMode enum should have items in correct index', () { + const List values = FlashMode.values; expect(values[0], FlashMode.off); expect(values[1], FlashMode.auto); diff --git a/packages/camera/camera_platform_interface/test/types/focus_mode_test.dart b/packages/camera/camera_platform_interface/test/types/focus_mode_test.dart index 866f8ce07909..b7e5abfdee8e 100644 --- a/packages/camera/camera_platform_interface/test/types/focus_mode_test.dart +++ b/packages/camera/camera_platform_interface/test/types/focus_mode_test.dart @@ -7,24 +7,24 @@ import 'package:flutter_test/flutter_test.dart'; void main() { test('FocusMode should contain 2 options', () { - final values = FocusMode.values; + const List values = FocusMode.values; expect(values.length, 2); }); - test("FocusMode enum should have items in correct index", () { - final values = FocusMode.values; + test('FocusMode enum should have items in correct index', () { + const List values = FocusMode.values; expect(values[0], FocusMode.auto); expect(values[1], FocusMode.locked); }); - test("serializeFocusMode() should serialize correctly", () { - expect(serializeFocusMode(FocusMode.auto), "auto"); - expect(serializeFocusMode(FocusMode.locked), "locked"); + test('serializeFocusMode() should serialize correctly', () { + expect(serializeFocusMode(FocusMode.auto), 'auto'); + expect(serializeFocusMode(FocusMode.locked), 'locked'); }); - test("deserializeFocusMode() should deserialize correctly", () { + test('deserializeFocusMode() should deserialize correctly', () { expect(deserializeFocusMode('auto'), FocusMode.auto); expect(deserializeFocusMode('locked'), FocusMode.locked); }); diff --git a/packages/camera/camera_platform_interface/test/types/resolution_preset_test.dart b/packages/camera/camera_platform_interface/test/types/resolution_preset_test.dart index 55a4ac56cd9d..abc339729462 100644 --- a/packages/camera/camera_platform_interface/test/types/resolution_preset_test.dart +++ b/packages/camera/camera_platform_interface/test/types/resolution_preset_test.dart @@ -7,13 +7,13 @@ import 'package:flutter_test/flutter_test.dart'; void main() { test('ResolutionPreset should contain 6 options', () { - final values = ResolutionPreset.values; + const List values = ResolutionPreset.values; expect(values.length, 6); }); - test("ResolutionPreset enum should have items in correct index", () { - final values = ResolutionPreset.values; + test('ResolutionPreset enum should have items in correct index', () { + const List values = ResolutionPreset.values; expect(values[0], ResolutionPreset.low); expect(values[1], ResolutionPreset.medium); diff --git a/packages/camera/camera_platform_interface/test/utils/method_channel_mock.dart b/packages/camera/camera_platform_interface/test/utils/method_channel_mock.dart index 60d8def6a2e3..413c10633cc1 100644 --- a/packages/camera/camera_platform_interface/test/utils/method_channel_mock.dart +++ b/packages/camera/camera_platform_interface/test/utils/method_channel_mock.dart @@ -6,11 +6,6 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; class MethodChannelMock { - final Duration? delay; - final MethodChannel methodChannel; - final Map methods; - final log = []; - MethodChannelMock({ required String channelName, this.delay, @@ -19,7 +14,12 @@ class MethodChannelMock { methodChannel.setMockMethodCallHandler(_handler); } - Future _handler(MethodCall methodCall) async { + final Duration? delay; + final MethodChannel methodChannel; + final Map methods; + final List log = []; + + Future _handler(MethodCall methodCall) async { log.add(methodCall); if (!methods.containsKey(methodCall.method)) { @@ -27,13 +27,13 @@ class MethodChannelMock { '${methodCall.method} on channel ${methodChannel.name}'); } - return Future.delayed(delay ?? Duration.zero, () { - final result = methods[methodCall.method]; + return Future.delayed(delay ?? Duration.zero, () { + final dynamic result = methods[methodCall.method]; if (result is Exception) { throw result; } - return Future.value(result); + return Future.value(result); }); } } diff --git a/packages/camera/camera_platform_interface/test/utils/utils_test.dart b/packages/camera/camera_platform_interface/test/utils/utils_test.dart index f1960eeb2e72..0e4171d73aa6 100644 --- a/packages/camera/camera_platform_interface/test/utils/utils_test.dart +++ b/packages/camera/camera_platform_interface/test/utils/utils_test.dart @@ -35,18 +35,18 @@ void main() { ); }); - test("serializeDeviceOrientation() should serialize correctly", () { + test('serializeDeviceOrientation() should serialize correctly', () { expect(serializeDeviceOrientation(DeviceOrientation.portraitUp), - "portraitUp"); + 'portraitUp'); expect(serializeDeviceOrientation(DeviceOrientation.portraitDown), - "portraitDown"); + 'portraitDown'); expect(serializeDeviceOrientation(DeviceOrientation.landscapeRight), - "landscapeRight"); + 'landscapeRight'); expect(serializeDeviceOrientation(DeviceOrientation.landscapeLeft), - "landscapeLeft"); + 'landscapeLeft'); }); - test("deserializeDeviceOrientation() should deserialize correctly", () { + test('deserializeDeviceOrientation() should deserialize correctly', () { expect(deserializeDeviceOrientation('portraitUp'), DeviceOrientation.portraitUp); expect(deserializeDeviceOrientation('portraitDown'), diff --git a/script/configs/custom_analysis.yaml b/script/configs/custom_analysis.yaml index 71ce1417196f..721e2ef10e53 100644 --- a/script/configs/custom_analysis.yaml +++ b/script/configs/custom_analysis.yaml @@ -12,7 +12,6 @@ # TODO(ecosystem): Remove everything from this list. See: # https://github.com/flutter/flutter/issues/76229 - camera/camera -- camera/camera_platform_interface - camera/camera_web - google_maps_flutter/google_maps_flutter - google_maps_flutter/google_maps_flutter_platform_interface From cb381ced070d356799dddf24aca38ce0579d3d7b Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 5 Jan 2022 15:53:05 -0500 Subject: [PATCH 080/600] [ci] Pin Chromium version for web tests (#4620) Switches the web tests from using the version of Chrome installed by the Dockerfile, which is whatever happened to be stable when the image is generated, and thus not hermetic, to a pinned version of Chromium. This uses a slightly modified version of the script that is already used for flutter/packages. Since Chromium doesn't support mp4 playback, this updates the `video_player` integration tests to use WebM on web instead, to avoid having all the tests fail in CI. Part of https://github.com/flutter/flutter/issues/84712 --- .cirrus.yml | 12 +- .../video_player/video_player/CHANGELOG.md | 1 + .../example/assets/Butterfly-209.webm | Bin 0 -> 2113335 bytes .../controller_swap_test.dart | 12 +- .../integration_test/video_player_test.dart | 132 +++++++++++------- .../video_player/example/pubspec.yaml | 1 + script/install_chromium.sh | 33 +++++ script/tool/CHANGELOG.md | 6 +- .../tool/lib/src/drive_examples_command.dart | 4 +- .../test/drive_examples_command_test.dart | 52 +++++++ script/tool/test/mocks.dart | 3 + 11 files changed, 194 insertions(+), 62 deletions(-) create mode 100644 packages/video_player/video_player/example/assets/Butterfly-209.webm create mode 100755 script/install_chromium.sh diff --git a/.cirrus.yml b/.cirrus.yml index 50b6cca0362a..024ce5914dd7 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -246,13 +246,15 @@ task: matrix: CHANNEL: "master" CHANNEL: "stable" + CHROME_NO_SANDBOX: true + CHROME_DIR: /tmp/web_chromium/ + CHROME_EXECUTABLE: $CHROME_DIR/chrome-linux/chrome install_script: - - git clone https://github.com/flutter/web_installers.git - - cd web_installers/packages/web_drivers/ - - dart pub get + # Install a pinned version of Chromium and its corresponding ChromeDriver. + # Setting CHROME_EXECUTABLE above causes this version to be used for tests. + - ./script/install_chromium.sh "$CHROME_DIR" chromedriver_background_script: - - cd web_installers/packages/web_drivers/ - - dart lib/web_driver_installer.dart chromedriver --install-only + - cd "$CHROME_DIR" - ./chromedriver/chromedriver --port=4444 build_script: - ./script/tool_runner.sh build-examples --web diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 435484ba6e33..97b8a1698e3d 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -15,6 +15,7 @@ * Fixes integration tests. * Updates Android compileSdkVersion to 31. * Fixes a flaky integration test. +* Integration tests now use WebM on web, to allow running with Chromium. ## 2.2.7 diff --git a/packages/video_player/video_player/example/assets/Butterfly-209.webm b/packages/video_player/video_player/example/assets/Butterfly-209.webm new file mode 100644 index 0000000000000000000000000000000000000000..991bdc7108cc83640a9bbea6c5e08793293dfac9 GIT binary patch literal 2113335 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�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Ð~Bu#is!w2-nix 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

AlTtb-wjvCydlo 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 z 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
v;d_I;Dv>6@Du4XF=Q4rRR>e$FwM?C^ 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 z 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�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 |>*OJ_Rh2ebM8Z+Gk?0ETy$xVB; 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ă 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�Wt)qVNivMaV%qgZ{Zt!zAA>|nU} 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 z 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%0Dk$y?9TY5Ulp^C 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

_w&*r=ei5$gO7M=gS^&4T(tuQ(e| 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�J{}k!QFljQV5Sc+ugV4b{GSr>Erp#dyYWTNY zbGv`qI{&b_mNTu=r`nw6AjG zHA)vUC$_iQd7@iEW0|1N-2w#|`+tk3|K}tsBpw}f^Sax+^$-PpuCmwH{+bSm_I*5* z_CfR}hBa+7(a&$IU(vM<>d4L<0goEeY2;(f0%H%}aPrK{l!vkjTr=EG5_bNA!F=!j z`DU3iZENKwqk#@;wd#3y|F}&*-RoJ>{^BQtZN_(Xdf7W>qSKy}ZHhQe%{_I0xUVNb zSs4G~X5@YSh%0>WowJu z7L0E|#Oq88vS3a3UaqR%KwhWwbJCgLnzkp^36?L-W#ap3w7K@}#zD%w13$&5p*y98 z-&?P-Y3+Ij8w#@jsVEe3aniaoiQRJlzU>A3YI!;`+tZcOcr(L#B(Z}){@tYVgZ~Vi z%APZBmbPdUJ;=HGR1cZ(=5}29EE|%G-{nEMwbS`|KrL{RqMj6nc@!@}kL>F`fq#Vfd z-V=P(6D<)5I@sWnAD~&gX_Y{+$;n>WaP`$K!>V|dzO|=JL{FNFKP3!qpPl@rYUVP_ z?+~r@?KO@tKD!~mMEaOu{Dl0)qejJ7Nsr06TKO$4oeLi27N~k(2DT)nlmm_4BBQm^O$SER2=dAhps zY?HKu`0iIKOY05E2F%77$8~jyp`atrH}pqJqq!V&PrW6h$$tc^w3SWBYrd{{pSX0) zkk;&>lC|(P4?YG z$ArHDh0*zpBdqK1rT_<}EHTzp@(AVVga}#wiMM{#t19N?{H;9m$LXExJtBW<^xUHx zJ1Ljs4Whp|F>vdxH%v9MR)$t$Z#nYQ3B#h2Jgs!>W=97uc6Fx)&W1XK|1)rSUyeBM`1gWdV6wRxr3jg|7aadq;$$=5gAffddt8pfDz;^AJK|BN?zN*JXoig}}g z%u-mKw%ZRMQ*ZKaKfrE0h*czgIU;&NsBcSfTK4>hsDf)pdJjXuUS;m}oRm!T6_r3z zZeH5@2!70TIPrYT9WFU8+kj#T!>8;6k9`&H>b_UiDWxDFD!#0r&w4u6tYD;+jySFJ zNJuq|r93*X-ddMoJAp^7$6096PNuDs^C6U;>aVG5F!`3e9=9ZFO?O}%-L)O8x&3^4 z)LS|k^WYx+!6l&E5{=OqzJaASHNx*QmxQVK;l@3|SX}h(8#b3a0lJ6p*Md*+j8`!Y zB2i1UZrYkRPV0;h`c4~m`3T~RIg3CK@%fdHuY4YuQU7qJRZZ>cntZ$Uz{9cV-Ow-B zMEDF|PGHg?T)OEm5+Uh(_lQ`==+pFfmI(!u)DkSa*!^*rG>6DgPxjtz{?2i3`z58BZ~-;ijWcCq>>}d$f%uCG`fKY1GG(UoTZ`sa@{^9W7?i=C9y^*Fys1V zb+MhgEl{UGV$r!^HSy#4FHF&lqtt~V7}e0>#Of>9K7QR~imA9$MuvBfTVm@J$QbT3M8}RYWKBrbg77#eMUG z-6XDhEY>FJrP&bk{Ox>cC#{d6MK+0TOGbayCTLO~&{f9fiLT42QSP*b2{xe>;hdGH zP_pQYC60spqC#zf8&weUBE4jzLxP_+z$DQo=KCh~0NRH}XZKF_>#qn`YMv(wA9t;_ zTcb!YBY?1oua;>apm)Sy3O+!Xy3zz_LG21dDZvSUggHW(pEr^=gF#&MSbNHzlJ}Gg zo!>h#l3?cyLaN;8r`a-=^xV?650WF~k`^AYWMr`;3yD;zR9F>sfDK93cLR9-IFsCV z@qKKy7g8wJM|<~~c^bny))iA9FI_}2z;P;N6D{1U#s4Gq8f{ur)N>_vK6t!Zvkg3^ z&Fj%y8}{lYm2BROpA=!o{del!w|`^7a>Wo^a{(S93(;Ny1s35+e=z##`wUI7UYma6 z_SUh)X$Qdojr`^3Fao7DB9E8^$9L<={uEql8fsods;zpvF+D6-X;t-v2D5c9%5WTu zZ_PJ$HFVQvE9l!Y)&N&9C6miY8JwbWn+l^&93N+;y#69pC>zES8t=K8P9WniEV%tU zvi#ar6@BEV&gsv`f!VY+mU`lV$TaafHnv!jX~lvwhCry_71oEK=DC9)Q?ILK+FOq? zXOE$fC~#)Sj1m5RqPBN`*HlVc2(PvIY;oqt1D;KS*=C>=*8BAo%^hBCIjB4BEoeDa!LUa2Q(WlWuvP`s6?xmhuZE8Uwh zW+N4noWybBzk04r;6`gOP&nR+B%e>WU!%3h-Z9IzoLCY~7~V>ryC<=dRb9F~%YBj{ zR25I;LV{)Po)WdvJur%`7KqJ%WtLuht|ZB`LL$Q zs2dqAKsmQxhq!4FTQp(Lp109(6hVFORL3X~p-Z2ldY);b@ls(vQ>9M0yyBZo7(2^y z;#}$VA2vw=G>;TGkxyY36ST8V5giD=$U$yZrIGqN2_*UYTiY=ntwG|_E*7;vXsr*i z7nllHs?USMnA`t(J24@?j$b3Ykg|y{{%x1|>eN^-;I3ZD#?g0!WF46PH9-0bYULq^ zUKE=+2FW`y)wzar@ORF~SOlq)+ZE-qY?$8av!VSR3!}ikm3x{$ma%u&=-jhSs=Rp# z&ncoSi631f4KucyNn)D5W>I~$nm?YBEU)n;8_58-U5B7%Az)0R#5_yn(oyhs6?9u= zO5yfrazP2U6PdFS$SCH0)^*Wk!S1-rk(dz|N>Ju@wAAayz05$O;V7e_$1f%xQvfQj zkDEgtC?*YBEFcV0I2Guk(#Nu$RslY9cNmh+^oBpO30o)O6iny+BhFB(Bx8tu*+|5% zqQ(QYUy{<4n4IVhDBlaz(rW^{H59se6iYfk8fx`zRyguQ_9iGU2zxA-! z;)hvM=<8TEAm==$CiPxUD`W9ERnS%_$?xlF&=i$+{1lZ*=wDKVjC;c}Ci63oVDcd( zr>l!EYQkp1z2m(r(Jw~GYJU-FZP9;c+Gos0?KukQXX{70 z@_9~L@Ah4PY>_Xr;SrW4`OSWqjp?eSiC`nsF{@1>W?08mfjEn%KaJwGfl-;uFP^kx z{c5e+Y{c*~%(rV6<_Q$^D%*nrEDh@M z{I~Yt3jQDc9auZ%1;lkzIum5mU%Ptx?xD^wj?1g+e&Xm_FqJ9P3}KdwXnsLB^kHjM zfyBwlwA`y|feGq5jt!B3X)(o;B;=2Bjw+DK!oE{#q00b9Aqi)eDuBP*whk!+^)s?9 z&9Ve>yZ=Nrg<_Q`KPX>gG4`L-Nx2#F$`|n36G)jdW>fz$Y2L~%!H-B+dlmMvApt`o z`fQBH-qjf#>$fo%0rDcB&nSUNTEagcSnED5yLCUIB3nZOTRfL97R;s+h>E-1UMih7 z?}%d~HGF@9FtDCbhOCR>G z&J6ym8x-EqJF%kvJm>rLp$s(=#dKF-1Xt@hrfliaIq1%SR-S&TKH~etf(u-qm%vNF zbYwnAHT4muyJktS-R4S841D#CrNrnWhNi|N@ld|s z`_HV*7z_quANvwAvPZ@~_Ob7>M=6v-i5dI8ucfjRvPE`{ow62%R7jS{+fpK`?|gp0 z`EUMtp4aO+=RW7U&vIRxpoa;Fnn)kZQ`v6$A=wz#;=|%4I?M6~d0yL`E75wNWo&U} zsZvxD?5P07$R8H|$|B{$9x#DsEDHiUH@Ze{PczI@1R?(SDvm{s7OA9U}X$-5G@ z$NX~vYmI2D_qkU4hc$EM#H6xcEgRKFX&y)Znq?PiO36|^((9WKR?OKtR6Nu^ovF;2 zF#g$R9{H`7bf{h*CI%7zXkGJ{{WSb!Khg_t=&HIn$;GyQ@0#fkztbxNjD-XDwQOVq zmM(Bz3&;xfguT_BT9|uVRk&WDp7^@{$EH=`r2N3Xz4fH$hF0KX4VR)%vme6)B1+g& zU@v;z##e`K%xcDe?y&l{$~n}l`rKt0RaheAAKS03#z7`Xaaei}jB=gi~ zIfLJ)zin^&{tbO6U!)bf2eZ3$;b-RYxqU86L}gjD=?7-j`XyqAgO(omPz*y}cR%U) zymQbWE#!`~#lo@yS9~o;jfsQzI`Sc&J#FsP-;VcpM=OQt`+HFnmblOp>1^A3yL!zt{gG5#XM$k{XYz(I6N9 zF~}4mXJ==-BIrwdvU@k~j*MRvzkX*})p=W#jTle$#!@p^+G@RV9w`EYUdbnTWM^k* zqtrKy-Ds17{zVj9K!RaLF-%*Q%kX`?Z$YdLMJFXmJU!{%e%1T0^PwYz~EG zLHTn6cE7C_u=&Yvma^aWSb}<~swcKfCNS~pvUiE~nlHv5PisZrfx{zGc*n}sw+SF* zH6RyYwhuWhqJVnwHqDrCFB4ro;{S{Qb>pd$kK9dkR>BloG(Zwv#xDN~>}y1s2fMy; zn!WiEyyL9=_eX+Wb8p7K^~=&jsA-N;6Y~hUc)7eQl%L175kf*2`A!ofge3f>#1seK z6`Q;h+;(^tq#O1(?BjoDR(bnfHHPta&x=T8x$%T9(W8UgcI=qWMlatqQgj?KS}o&0@re5+cf>0X-K3?FfD6|2lSRo^p;6LR_@if{#@fORZxJz_Vl)m zx8aH8r1jK4=3^xz8p#-6Dk5UiMC}hQZIyrUll}djJ*#TYj;)&^Pvp37p5>E9KDQ{8 z93>3ufQ5m_pisbv$7hrR<;yq^fzNTmLCB~*;EeuqLg1buB5OmF>(;sN2Q z%Rh6)3#E|RJ~RXa6C^HLEY0PxdB675Q2&M=CMEwO<`#dBOt;kP>hhH<3MAo_^RpRk zMSrr5U1NtHxVfap6Md9pm5up}59M9H#bY4i9@o|k%jw(p*yVaTw#w}do%0k7w}tR} zl7{a_-W0;pP3q*Wq=esmNYd=OL#J>r?t@fMe}ElV+&T$7^rK{aI#+nwJ7=l}TI>P}5@nLyJCzxaAvdrR+F!tlMFt8ytylj3c6?0M zo%|OkH6VtxHC%cm%la`nMeM8MatU_yX>z6WIF!Q=<0r=}%rM=RcmHH={LoYG=}{+? zSEm?8sf@M|8=O$M%CdCk5te#;kG+E5%@+IMY(9o=$v>0ftHvmM1mg2=So$>VZUjcX zUBO>Qzm#n3aivqWM={JfbPjr}8gAT*EB=%~{rg~M{Nljc$dl5Duu}RnMLncQT3Ukp z2I3{Nzf&9g;a`OtM-RaNwDc||Kj9U~OhkTuBJ`ap0h$5a{gyg1@m!R(r6q#idT~R0 zvUkj^n+^FQ0TIa((Ae?~V|!fw#;6QT-vDh^{)L=7%=Ge0L}7<3Q!k4NJ*I3~D7CTN zX1;txobUzn$PL{(oNo5Se=i0@q&*&MP4s2b!;y2dve=wN~P zDyyKjmCvUL%fztrsPBJomPi?tf>S#OO`q8~C*|%Xj^1zDxhpI>rT^jpN#k!5iCi`z z=Kb1uOW_0mM&@tdR0p1-mpUO68l|I zy2MXC{Ykrl?h!0%gi02#r!lbGY{S2YFMi?uRmpQ(#BvrN2pS%GxtwRd_3l4|AoBHA zLWBCIc&el00zK$7N&c0Ox|JZ~rDk{V<<*#k52z>CFK_h?Ozc3OTMVMRLqL?S$epZW zRLjM#0LSAlQxIeX@x&&n1m|FC6M4bp*0hZqi9Esc(YJ}`yKbV;8!_Fm(*~FyOI53F zPA;Oj=?43gOio~mL_p(te7voVE1^Y?#`}Hu7nC27p5Z6o-T0mVnoTD%XDpwh`oOf{ zLx86HZvD_#=_*>o(iZkUL01=EiKTpImbDo9dP9{VL#&HV0((H$Z~dw4va+jMB1hNN z)Benrk6xFl+zSb6cUMj;xKj}FmVS1&67`0J*R`*8wfuv|B|IdKSc+0TS)#cw@A9fw zPTt%A2QlTxII$*b+%H{hA9HTv(W&ffE;f8;q2_Uq)ol1#=etgPChV$-$Zy}*T%;|> zgZCbH)Li3KGzlK}o9sOjW5Y`Sn;n-I%|Nmx6<_OmLf{3`pa2L8)Ixyo5{(l;b+K)O zQSB02k~;9hEq)+QhlEd?42Y+@RFk*3ToIkRBW>>jDkwFfi`Iyi7Y@cW2K7XXO6yC{ zUdiW}im_Z91qQJdF^yV8$@Xo~j)FUoo2=iLWDk{EH10LZd<1#qr}N4q;{~XX4mjqJ zB{C}p+rOtPpbFAg#wlAOfr};`ueDTAaTn=9icirl0fa+wy)dviM<|*5Zu^gGn;fB0 zd2XNJxDEU0P(l60_>Xchg`8;2E$jwdl88^473<(1r%@=(ugJ!3aQF!GJ_ufX*eqH-*A1(47=5&~B) zXkJ)sT`raa(wAh_rsFfGmo)M!IGSsGA*c1kUtC9pIUH0qsgTrQp2%$kB;AEHDV{r} zN0LOQURQ+5SILNKZ4%gBZcs8bFR}Uck96e%f?6<1qvws_`JdheUvDt!Z)by%mMIp~ zmMEyQE0x+dVg@P2Ly}%dEn~DLQfqowP*B_FDK`s# zKTu+{`j%*Hvv?YPy3<;!Isgl?o6hL>Dq?9WY=A_~p5FgMu~d3B-ltjqV(V+#J}UBH z={sm!v|jFt3X`G;WSYg4`SV%#QODE~O-$7?rvjgHsKm_FGb{l;Vb1^w_?fd>+T20~-cirA%>%+peJOKbG)sfJ>TG_+{;sPG9KW4ep2A z>#`Q!!+J}E%B-BS;-Pbs9+?*VDOyQ-Yr(0M(a*tmilV#(MF6begI6qIihB5qUg`OZ zyZyQagMWS9*`xR==f^S9)) z-N@wiBY+cU+mR~lhNY_|R}?50)u;^+Ek{&_4KB^6cCJU|Z zZLcq{)S=H3VrOA}G1FUZ(K=4N8buChcQ&&|^O%KOcbBi#o6Ot8B!Bfkq5eqJn@NQOm5h;BH>s$3&}88}+PyZUkD0YEqU6!;K~$ zQ^skYk)Xg7Lc|kQAIZgu%wJa$zNNVS#jPzc!r|*C;_S05lQITd>lUr?-uX+6ppo+) zo*FrTiB_MpeqeOl4=-CPML`5wIsEvQ49DquKtGlDUBdjH?bl~te7?Z$Xo7w$>@O7x zPQgnAN>1*GD?joYm`PRIjdmTknk1X^Q;jPSK~|{*uW^vsGIj{>c?ppKls8;`3q26= zZ^r`R}2|6nfj7QE^0_URG2M^<3LOHLy9m+D(Z8v5iP_d6lGZRtL=P6d`6NLe$ z5Cn)-NT|aao1VGhhQxRJh?1U070=2m0(rD>PkP^|xjjKNs)8MP%ON~VW|3HC3Za?=PvF}(ovh9c!#bgm;+K(CPU{QQkLnDW3h1RIU16lcT@#n{nU;}^2$TvmqGSfCj zlF=Q!cC>9d3)+G%mmPp=YgXFEfEdhzj3y4BFoIc`Vkrd|SFytiOE>Jn@}4|cFWY_= z5e&{_5fj&Fq(*xIx*3KWmcR7>f(1T$r@FZP+R4+Ni8zgneoK^Urp2upH=Z+S!3tgR z+^aO?B%$wHG4mx}pu$h*+FR*A#ErMOTXpo&GK7i;<^HCTrBi>qe$g?6MDlM{a`A8^_)x3 z9yCdb)d8ZPxn`wWa|dU49Z1QfHvqkCNQ3jhH+g!vslz<~IPvEo~xy}aU z04WmL(U*ATh9CCGDEqn!7?lIQ_DS&diwseK`1ymzT^(q`&D2ZY zme{OQn$Ycn8uHyil~08ku&Q?)Bq#W9EhL00_)?mESO@9f?R{c!JJnv6_|^2-5!|95 z(hhsue4o5&r-c;QAhHv93)XDD)#irNc6$Je6t8xgNrvaJ*txqjx+`_4=z91k*2o5xtnIAjOrX zN!Msk&Ru@e^<^V&<2&eo1|x>Be#kygQ|4r_&(6Dn)MjOhX(TSlMHU5EB3^A z)J^fNgdbqz2p3C~V7L*z2TeE{1q%^xWE+LDn9l6A9gzMQC`q&M6QrYd?wIn6tC^4F z;M24(v@YrT7=0@mv%W@wYr5q-7Xpm8r!f%0YD`pyhg3ACA}RuNCb>tMl4NZqP8NVO z{g_sBpQ^uou!a6PMc&iXm}!BLDmWnk?_gjdtw9I!+PTQeTQBI@y_^xz6$LuZvfGnM zf5_NM&1DQr(!sFvz}4H6L3GOWp6EM-n;2S-l?#AyOs~NyNqD2f=|@I)q1a!RkKHw@ z7!&rh+4_2?Tvpa3e?@(44_Fk%{cGXYn%SAYzh+Mi86!z4F;?gGcy!-?47{DN&8PDB zp-5WEzHYS8Ow`e)#b>;M*7znS8toC?|1aaXhx7GrkC!Drr|9t4JEAp?KDM$=k?Drv z4Ar&30qa=coPAZjVJ(v zha0IZvvAcUEuyxz96I>2*&1D6F!2V&db0mV$!gX9HRE%2GSsNJMA+F}oWhWxPWSHRjL^kX zkTFE+1hRwn$cc4aq-7`?Hlmj6`U8)*4XVX=&P+)pMI<{Ml$nz4S!^nW<{)|)};K;HEumMtwgNQQd`T#+tl~*&V~VD zDdhs8NTaqD)}hh-Q9|aMe}h;zT)HRMX#{NE-;&B#!$SNn;bG>`j!!U$qw4a>Ot-Ju zOTuXoF3GayN5UEL7mpU8=ghDL$8blPaZ6QWg;+oRFYTB$DJ9aT@l!lfEE(_g^3KrH z?4M!?&~6U@JGMiTNe)pNcTvbW?x1tlDO+TTP04van(Xg$+MnoR+QX6dG@KQqtEQD? zS2aoQ5h^|K0z9Zg*damL4taJ6Z~e6 zBty2R7LEGkUN0+8zY_9*o2}&0_nr&ERRG#8Lgf?hGr81cJ^-E0n8t3PX$UDO7wIFa z^ku0Yn^wgozUR(}=~{bQfJrE!nOt(5YmC5Vxqom$E!hBDL*3-G&MsXcG7%#42>6J=+so3RMQP$<6` zh;d9OQgC}*IY;~sSu0~VW|6OxhVTLq5>{v-JSa)8A7w!UvlkPHc+;{K&MwM-K}2RH zAsRNeoACqOo=iC4M)-f#-eDYK9$rbj02r=IE#3Nz>pAOuE1mkR(bP z>3TpgjXty}AxZBKcnZjFBDJE*9e?;&^wraIk;1@Apd_aZZTiJRTqUcvF|2#qbvWv? z5qC>t+`0Y7`s~#paCBN)oXXafmZz4vrlHp%&K0;J+z>v+^IBH9o5v?BAnT>4CWn7P zYIJ|+58{FAyv+N5XD=A_$-v3#z8GQJcv7+>5#b&LDu_vZKqu5X^bd4=F4{l*K4mP# z`{8WB7B4v{%`04DSQ0S@^QF0lD-u8Knc2BU&199oSZi+Ko{=jB;5*K<2}ir*2*XS34_?j_WH- z>NILpvXg75kp@2m%4y*{L`k&ye^-U>La@CcFD#`y6sfb=uL zF=JExfT3M`rv$3;2;VqJXcQQh_}-54EVeDRVqEzUlRvK=)k4z%(6WqzpO8Nph!h*Y z%G6fLq<@AuO`!jVik=C)3#(V%i5g$Jo)Xdcbe&jJQ~JLJbL!h^Ip5c1A6V^}IgeLD zG%r2x*;VJ~eiST!y&;R|Na<k-SOCxJPM)n=wuYb>keR6BAditQ_ zUk7i~UnXIr*+=W;in;&kyd97=JJ|NFFVs<&9hry^do0dj0P9UDd>$&(bLLtY`RSbI z`>#LKU7j>V_BSoZ*UVLI|N1V=_o&j%SEBjKHr?37nk>vCoz6sc>p_DCPk;5f%RZ5K zy712HC$3>Ey*~9{zIOMhPf2}>05@xLt_S_8_xx60_aHUPx25aVufn2hyMzA~-_FRQ zo-e=KS)~8#>TB1t_tUZ8lY0Mr%zE6A+T(llXLauK4L;dBFMOLkmlPbYT-y_RqF8Tz zvB$gq$B@xWyVZMi!h$IK&tT)f%{0w|GOKk89U9m_0)*J10Dqe$ZhZJ|#3LO?QH7do zHxMN zFAntPsI$K8F)hi=pRIXx>8Mvvhk-TH7Fs1$LUH}hNhs?7I&BdvUTid?vtqDf`$BlP zuO)CG@jo6;cYnV1*Yo|6KGhvFuRTpN-j+qbk5l+&$D%@S(uR>9lx3ICIuPF{-G6=f zr-AF@{vCy{dW$E_1$%jK$NqbWycQ(2e)+FFcc?I%;zj=W)ss%<#~g;G$6I37|9JD5 zA9kp%O1R(0D0-*=`WR6Hy{%W3J#=|xGO?%bU_s`Zm>B0*0atfFt9j4i=6`e{r|KCc zzDMtr8Gla{oljye7JzdYz<0gl!$mgs6!)JE9 z40#TPX8+A6UD$h@v@Xeia?$;FuQ!M(v%V=k?~VK03iI1HH|>TV-Nk$WT!aGCv?Q4s zD^(t+|IPL5dS5cIrtxfODtG7WMyA8FTJh^rhfBUPgJx0@UhF~5ZoCmUY7+l_6TF}~ zx5ww=!uF(5<=5-i{H2*UAD@}_xcQ>A9yS}1gnM`l3|v`Wnh)K(r@yGx<+azc-bO8* zcl@usg1>%PgF}vAODW3p^9vuPKHt^PcFCQyGx2wn0_qF2FWc+; z9CzM%AMRD&WdGa0=YF*U_sqkoF7pUg+upYMx=U4AWFwxF=|hhp?ac&-e$V=cWP^Ua z%~v0p@=NNtUXfUtr?@ZKp9AhCDxhb+nI@bQKNvEv3eyna%5UC$e<<>-c7Paq#i;Lf zOUveb9A?(_bS!4){JecF9y{~&3;vBDMUU=>-{)w>Y>(@$s$t&Jkk-8IV=d`mjN$F+ zc6y?Y_jrIKr#P?XyzrnQn%Aa(Zg%$L(#z|2qjdJ4$fMi}RzEGJ=|Bd9b-#g#%fC=j z#7P#Y82@G^eS}~xgTuf4i<{1)9~J3$&^@FG|BtQElf74EViAu%-ZpUk!m^@7cv~*8 zeM(aSiX1O`v0hf|aGQ0D&6%?!yf5OtlAC3Y#PT~XYLS0nuUYe-JH1(9_&%eezE2BP zf6yO2)1P2rE@sx%PC74MSZ(Hwv0ia5y*+USQdrl!l+!N4cJaBEkqDm%{UzcD3cb2| zyWeLOMy`mn|MuTyzd(rfq?gr0-r0N=RSO~SFAZzps=1tj$wf2zwRUbF3DfYumE?Pw zy=Pw{%RZbn-8q6Q_Wiqm*3F;xCTO%thNe3-(EhT8lW_JKAq{lFc=b1h)GWI0MA&Fu zSucFp5i#V^)MD*%D|!CbNZ{@Lr;ZF6fb3758+u?1*W!!DMMn=_)3aqPJiV9Xs-<(i zw(6HnsLiZWI`X{t`}|fQpwV*n@QZh`A@!%yl_(Q&$Z1W{tt&SkixU6bDk=LNtc?~E zkxDdvwxUkc-nJbPnm};bO1t%FWa$dub$T9wv5ZmpCwI`&m2^n}6yli|c9V7H3VB3u z;XGApKh5dw#a22nc(8Tt?#o1K?de(#K7ClGBtCoynlU!j*=Uwta@u|*=Qsj=2!0MX zqar_U)ST@MCOd?lWOf{_oK;G8D2KxOtwJ3y{kaOC*bf5qA%RO0ip4t)MY-ojzK0KE z6QtZEUgo@1(uP6~ia~L+Ez7+|Ul3+hy>Yvyim9%Jam5Kz8?Jx2p5l!)jNjKT3y27e zlVy7JucUXWT&Fk7`|rosqO~c;jk$#|QTYPIXXd3#pUyLMG@FDhgI77i-G8PV0A((v z^B8Q073o#MB<$g}p-M^*JpF^bMgof`>W}5Cul$i&%mEvO@-19}v7VzLGB~D2U`eTn zZ)@fvXan*H)%*D2rCLzwxdb7bhfdB;L1gAPp;znL*7j6a^P>a*^6$%0M1?AET{?y2 zW({oE`2Y*MyaoX$uBD+HkB0sc61!OMBLZcwCTHN~11dF}-|xTzM708LT4r*!7=2Pi z7x$VQS57N8X>yZa-_g5Gw&V+L`fFT0_3I9I;e_bbWYw!}VROf=(e}1|(@$SiW&PVJ zaGjExeRpg4>GXfLlEGPH*p7z8l!lZ=9NUp(44Arp8+HEXR9SgwBIU7kf?1?@YzOtP z266vo`BnZm=jIl^n}U5W+Q|+-GyvVStj=cbs64jicy?+gTdG*UYIbgQ0MP1KoaI`w zN>=b7_(t1vq~s2?S#gu9W`=|9Vq88HH~D!Neyr(fD|s!D@nS;uldn) zS0Mj)kIjX_eYG#9YdtooFi0aaGBKc8aYV|r{qYY8+}DW_3jR3i(}M^R{t~ZYSsx1V z@15}Ynuo>5w@!EqL|!JiTy#typ+8~qSlIFZSaL;AZ_U|LH+x`ZOp)i==jNvv|3`p4 z1ULf&2nfIl*E+9DAP@uX_4q+LsZ-_7!NWrGLbE=|5JB{O=(N3MDu^shf1!@O$PL=p zP+2mSc;lCQ@-<{?neItUX62Je^)HtRchI`NB()-3@jhEIHRC1|SXdXuP_FM=^b&Fk zxib!f_VBu~zLfjpN?a7Ao~>u2WQX)8b=_#IhmYBt&s=~k*QPJ@(Dr>0!$I_Dx~R^o zkWNn6M%>Av-*y^Q*#$K8vgIA`U!r?%Oy9?VT{7s?5n~lJUv9_ z0?tbzMWBS2h_p1=kRc9(WKKv$!9n-V1K$<%R5d87C(T_!1Vk2=OXR8*PFo?=Giew^ z9Zvw4)m}xgFWPx%Q3!Z{arsrH@-JQ`@3**ozJD=xAhANiQ*zrdSFsjO>e(MV$uJ;M zLy{cY?r#v8#XgNuE|!9GSjVw!#u;7QC*seyE>ztUDl9oN4%jlx^>Bo{wIJa zmnR1Y%5pyf1VKXTgPBnoYO>MAV6m?xU?n>l+$Zb`!-Equ?rA-qd!!d5B_%jUzdWEZ zSeny(6(lT;sUtU-ljY<@*obZlBzh`H;i|P1JCY0tvSRo-SsA0k(P&m)4on;ZUkLg8 z#gEF0h7XYlrf_HsPy3ousj8o%W-Gc$YwM@@2{3S^~KF0g>KP ziP}68eithu$V4+`A(~7+2Zm5Ur7yY*MKgJiZr`D1QqLs;cZXkZLaP$#&?z!-oLp%3*K&l&N; zMYQ>GYRe#1`ZGCr0tg51XDd(vvr~#5IRfVmfD$TDA`u?^3N#ZWOcVx1X=$y11hp(d z71x2~(+Y^JnJm!$hRg7i30Mw65>*koj)MkD4U^z1=_5LVLeC}cj6;)rI9QxKCTY*P z|Eqh0v6DXWGc^98y@7cTjpG66-bv2^NwibrV=WbU)i^tRHO{9%_uf#e%1V*LrQuB( zB37acfE)9})N?1S)zPH~(;_081A+`A#puSin%fei9VEjT;Z*?t#JCtqBf;_;|9gJH zpr3ZYrczpOt`Nk8&{54esvDg;L&Eg=LGIr|y^owOr=eiOkRpm|n@vBe`CGRPdP?sT z{VOgw4=N+jfu75v+Q;BWVWSRMPY90-06k_zi-GgpCNpAg-pwT=Y)#QyVQlJN?pMg7 zIUB$((CZ32Cg*Xd$G+9g+L`8*boZX8KP{Gbg9!GPq>WKQYQT%p*B1JOpXMLB;x#oQR+q5X`&m%8CHVUr3&dwnn_|`dsLR zB>;TB+Lp%wFR7R_H=v?I<_bKEXIG~;s~S5vpT++Wknre!WddFcgd5qm<)lcwLos4h zlEHgq7sSMO*pH3xg8*CrdL2j53QH^`fj6BqcjfFepKFDQkwc@rU{DV{Hbo!qcmKn* zkOHri03QlLC$KQ4oH5sE)Xfz7k4D=~RMLeIlnNxzh8Y{d*U;JAAIg&)ck@QxXze;< z0dbUXB#x6En4uTb-$cUBQPpsw7VNmaO#s>64!@V<&yQ^IlHqjjY12blK(PQ4JR{wTwd^FO$4N41$HQiyJ> zq%8g-Zu&suT;>!69TW{tcEh9s!!t%Z!zO;&lKxB zdN_|V5xf;DeV+rYgl=Nq&7T-gcET(OE0W}l>sJW8+m9={ZzZcwVtS7@u+Y@i+6~4m z-%ai-MvzrMX1EdZ$TU9{QYZzNt0mKTJXJVo=%_Kl*G(b_PSW+0RVO&Je*ubLFhm1h zjFKW40Tz+QFtuqlLDSvg(*P7~5Ay9Ez-h9a)Z8KQ3}}6AI4WpcNq%(GPPj+tK;r88 znq2)bmg40fQS(X1eF8sb-+j(W~f56x`uZ;1Lv0_zQa2*{G3l(B?${ zjj=IW!rslV5*A9oNcN=K{E;NFpo}Tf0Pg`;tN0~t zLhM(781v(KTy7`?6lb&ea@ixu8Z5jbXuOt8p!x>hTMRS{CpaQaZnIz^`ZZ5tk1>H5 z+rD`i+PM3SzH|gE-ALS96ZGMDZZI3ct<0E@U<<{Ez2606WaKZ?s7Q4>01TBS&?S_d zkWpOSg`=P3O*Dw|fP**&#?alsmT0#7kS8ofB`acn+Y_82br1nvxdM^rd2bfSswptd z*JKUm07u^$EfyG_BJWdS&47=XcJaAzb{L{Py@3%0gOrMql!!cxmLgn&_d4TDF%6R& zLs3A1IY3b&75j)m0LTrX+Kp}xv{%fLc*A@2chgZ5ZBiWOC#M5aB8Bs@0@J`>tiSl0 z+ix7S9&|2)5J!eqfI#WtQQR%vj^{`1<%kZ{;wEB(FG-(|lNA7@Jor;>WF}|p$#2w* zP}3LwYB*0ZKJ%MGxlB((by$AC{>-kio9|8rxQOHSyqiEYU|_b#Lyb?l0smDHxGCFQ z$G(0#z0FXAnXgvgARI?j!cte%F{W^Pn%+$eiX~yJZ>~g8n^1dV4fb^DhL~|s`34oJ zj3O?L2*AP#gavH7i=hVXNY*wolGkBSKY!nvV0e@DS0^}14x^>Zs$x6+iqQv*mXJWh zI1+**$#KA&0z@4jCzQes>Mz-+;Fig>>Zwx{9r}OOQ%Z7^Nm)w@iRqt#?ks1tHSVX% zMkuGr`KpGd2LDN$;1X+6YRWMKEn;@NTfE1r?q3MH`t9o1E$rL9S?V+6H{Pgtt%I~puQ_0^R90yrgU0(VUxLPW8+vmhJoLo5) zsIolO=JMoo;afHXzkt5s?0@*&yKZxJ&^W<=p{tK~k~)XiFgA%-?ng}YRw_My9-foB zt@iaom(oOmi`meYqe+c+AMxaKYWM5Nqc@ixAAJT z3`P3S^OVRrw>q>VY6HQAWIW5u+ z3&p%<4XqP%Yh;hmxeF@OvWTLSAom)qWia4y+NSNaO#nkC3!IMWT?*G}(X}R<9U{f4 zHHyU!1d!|cJd9=Djjpts&lmmP>AN0qb>ikvqG$31q&DPvJ-6&H^@l+n&Ur(dXV!Dw z-|6dxgAfa1Ax}mOG`C-YKjhZxSH26mkv_Xw{`ah|L^D`^;#JG}^ppGHb8DTO3PTEf zhe?ZfI@cyguT3sk{5Me2&^39xxnS+s>(lp7uasItKGzI8oI54`)5!4e?DY<)od5Vf zv@%O|2y^?}&0vcGQK#5L%A$H);=3Ek$3QDh(^2T~ zK+q8&%1#@(0tDtSlOMT97N^mL|Gkm+@Ai_`od2zAtP=zbvZ~? z9teuON9|%T`Yqx*eqev8D}~u~yq!UJPT$(>et6jJnBG$E*Dp*4FL4(;MH43KhK>p0%**1_U%?uGJld2wR%k zltw%W_%<{6*o30j_k_s1)|c9F-EGFD)b;_K~h_Id}Llf|LwIQ~^Yr4zE%*LU#IHs^!qMq%y+o6}B7kG~Jo z*kJ|1&;P;p#hNL5wGBSLPX4PcZ%2=i*FUx1evt7oxzkQ1p(Kiw^lwFX;Z^>Ep?NME z{0R$vwVk;vsw|dNRJ&vnm$J*J$UOR_YcQ)2{BGtQlQ=)&H%M$*mDzc4(W%2Qd-@}X zX{cNosj&0j)Lb9uzqq#3SobE6O&5XP#T(--5$KV34aYKSkBZM(<%y+i+NcZYK)$E{ zLchD8sK;qG%{7q!?N9ls|6{f6Jl2Mk(94l3{%K5#r(PSwuguEv8l8ji)UJsU2ZzGc zF!g@IGnbyWzlqU*q$3g;Qj)m+24BfCqoM+0$BxE699s^5op0oT7VSL-$d@ZgA@m2+ zp*n9|vUlROytMrq@9>4$h3)%r7Co9hug=IOVn2c!D0}T_T~Km_f5Ux-zA8)~ZEUO) z7HE6l!y?-8Y`$rD-f4`^~I)tCf59(3=`pFF8EH zwFKTB1|4*9|KslQOElc*d~=btMNuW|Fmto>NcCgz^+xqD=!R72!rIn@uDT`E?Ps@G z?f)%)-jKJ zfKGhz$_XtFVm<5<2+*77GJv;|;KTqg2d&ea{LBtPV)pE|c5PJeCMo|kkr-bHwG)G8~!TJj~{ zs4G+?(43rm?ZURJ(8>)3?xB_Cp2WFO**i48W2<`1S;~}aOzxDWp6qAmN0U6k{)2@J ze{MY=iUfV{?OQUH@n7z8U6fSjxOY2$U-aKcpYqeGZV6=X z{Vdx!ug8jNw7pL+nXq5~?lSdc-+uQDM$yi&%NFzf)8rm|;QcnbI`&X^!SKI>qvvA= zObNRkw}XaBc`rXtxQ7U`o~=J)&=V-d&o7^Q_{|D1w-?`)6I*8-Ca)fF=lQ2frRti! zS&l>oYhmvh(nZ&!62@Xi>cYc(R?CJC-~Hk_jZqp31PMAmWhnL&4w{O)_Tjy6>=KgM zIev4i`Pz>)8+s+t%=%j^k7LH5snj=duWQZrv*XEM+~e2x;)UUTfA7S$rP`<*UY4@j z^AfoHie3QKCa5C9&}+$$uH}ei+M#aYFZLKH@!XJjd9MCTayYA#x6u2A3~zYW0a`EjSt!2 zBDIO<5IYAB&71nvWo#4S^5MEv|IHOI!+)63IODJUm*4A4eKKXK(IPy0w6Ue=LAQ5B zvudAm4f#j&+r2~#U%1o6+=qeHIsE)b(+{fc?*To6(FD^l^8JL)zNf*hHzFj~D|Mm%uJenSg73$Vaf};=Jd^S%V81atR|<8Z5^f< z>*a(@YbX4k)3x`sF%f+6Rk8l{$lts#xktm7s8w+9MZ@1cUX2nh?tyo%n_1s{^Lwt~ zunM*L-Iy9Wh$=GC9kiVa`!aXyMZBJ{h4Gso8do>3%?Y-Apra3d%Q&PZOhV&ZIfg35 zo_2HSn_b7if0w;Ai9APcTmu8kVBjr{-9zMcuyT#}YUs4+W;v|}LZ=JBuhGVMUg3q% z4Z`tmw0T!qE>1oRDl?8i2pm0c4O$F_b{ngO`eg!6po8Hg0GlBK2Cr(hvJJ9EM#hcP zCx33;7@l=jeUJ*0t_4~lBErymTWg;Z+$(ct4FyRm+m|Y58#ycfe6mit2@F)ux2i1n zdowTK!Pe95K0*0s(ro7<9)e@3-=)OFJPS}R=c_#ov?wnV+5ZSJ6bnC%y_bKjg1x0D z6wgW~D^;J>0U%4zUKT$V=iX!WA6cK}C*(|TpdCsME*TcF7p=}wpo^$B9Lo}E55rU? z+h<4!t4D!L3bAHLhD#u*`{QNOMKFD_9`nj9=w6Pbp(%Ihoh|cX{^?It+WxB9UJt1} zX3NK8Nc1Cl*-=?Z5M8)mEDM@awHIi97eM`{&jt7b`cQ&DRr?q4xTLupB;w@)AwoWV zif&j=KUc~K<$XR24vPfa%zJ@#uYZ10y?Ru@1B<7kinqXTZCG{@%fjG%?=BIx zY`1wJMO({X_j2`KBu>yCi+9Xs8) z=?Tt(|t1b(-$A+r4XosHk=H6n2s z5)c^Lp5brZ9S4i*f6b#D_aaP=@#X<|PXGl4N8Bjs(n^KBQonQ#2i&uF|08z|j7G=J zsU6s!18#{W5<;_ffkr;gUpm9Naws~h8f_*eSMgB5eOv`Nv8U;2NB@aMgSvt0SB=1C z=#bO57;)*>BI6`eNiSz3AkDE6$TR0-%~dTX@k%I zVaJSSHR2K@Ez`6Oopi`FG5PjjPa$277tYKS_=mxGNsrW`UQ_eW77?7U{!nhN(V89jg$)=v|xGEtawy0sdfGqdrefJ01 z8zRAK|3z?u<6V-M47p@+a3PgJkI(ZkcAlJ>^wWkO=!BAX*M5xeQVM0Cv3XQLO|r8pRPb^F ztbWum@C_iLElZ%k2}oR$ljdN6rnY}9>0<>+=!p_-XoKc})C%*m2r&hU6Zn!6nsy|? z>U5##Mp1r!8SF%YMP4RW&R1GUfcXLS02~K8=LOOxwft`2QH)`3ud{tmZKf#6$CHUV zGb?#fSD*7;@W_XYP^&G9ReyEL7zpTej0G@J1JJ zeL8sXlsL5FODjXt+MSPB892R|$}bkZLyTn4Bktld(xE>Dux{`%%Vrf&kUg3r6>UG<^p+)$jZNbC!8qptU}7j zO4PAOW+7yg%t%5;9Vg+xIR3C?f@@#uVvZ>O%1WuS z6*pdpmon0c!&*RGPJHtInqm!>HCb9!qhO7B<+3Z;-EeJGimmo-Sg~amTws^)BMAOu zA5pkMZibb&6h5Dp({}fBqG2KWobqoF*|Wai~Q&22y66xydc_&G({nT?guC3Rs-Ah~)IOH*Yov<1eLrT8yu`k0B%07~<-{%keZ3A*_>+F3^xY`6mbw+?}DAVS9cmqV`@a zMtmPqhWz+T6YH)f2v7MINq7tfIe-P}vTw%1%x#TOb}agQ2&@}BoAQclUPz7JFswD% zWTF`S{YyZs5T^8_4`MUm%#XMz>fHHjPRKG1DUIm@kxPZBiF`)EZ`ZkUQzISV2{=&) zF`9ue6~UYd8gh?ge-LoU1dAZD0DJO&_C%L{b6ge-q{#VB^%RIh1{5d6Gf4J>r>32Y zL=2~nYO5{jVIpJ0W>II_y!Zgf=NRNGlgh^0kzC@6uCx&?lsO%aTL zopd7T6oV1T44A|Mb)X8@=E}wa+Ana9&lXGF9SeNCtUg>W%$yUodUjqr1OF(fUu2!#Ad(~@x}Ur>}59|4!*Q=7sI`~tYk?)kP(yNvlZ_r z-C+M^r^vhq`vi4C--T(Fj2gl{!ABxKZE<3;ShUu8^$RO!1H~R31%U{+7bEld7JWTI z>{Sx#&Ap>x8{?RM!Dj2LQ6KOVvZNVB3jwX>ZuH-1`T_B!6(okJ)EY0YZ6OuWX7Hx5 zg>UltZDh@qtL(M*E4R4^PNS29gEB(CE|&XhUQrtGKn$lnm&0p#CKCUKRpOdS!lH}r zqFTx>VLjlZ%StZCw0KgdY%=$SQr{dj$M%!7A6{IoseolEJnRmf!@8z`Xf#G!DhBSg zj3dy@RLYLoMC>_4-5oSHBDkKT%`8dzCf2K}8=A`8%UQo*VUAK>fq492zMr^|kdPG2 z>q?5R$Faesy3VY7uD7gC6mVn(pQ&kicX>M>rIHf(=rA0=fFTq2SGH)MxQT4B!6q%lPtY2y=mEq3*IUl(;P^i_TiF`CWgnQu=X{v=ei~hlbm% zm0N-CnchbSjoTyE9Og%oIwFikVS%?iudugX-M(p9J)N^OJP`8zeaYm5qp9=boYy;^ zGJX9M=+~<)k7RCr+Bo~3E3^6i;jO$M)ynosrhnd7%70RH{vFR?_n%bje*-&eoYF!4 zXOHdj^4^@BouQOoE}DM+`)+YVCQI4Q`Ns3H(a-18x14BCcdZQjgO&W`ef?>4gy-(U3qTKrQ|^1#qUK=TeK zrk-zWaQ8{)*1-4$d1k zUX#WJWAZqd45>G|FOwIqq_5F?`X}}mxo}}nFR{tWsn`BnUQlFF5K3$YhM*1)^g@8G zS~2~}!EC{}k4-BTk>_sCkD!CB9opA@A}+@HcHVcR z*(&k6)YSp58@g~|gj<2k#}4_5p)dbod#FErnPb%ASnpx}BDd}TyZpsRyeYLr4El#c zr^&4^z5^cbHZC4Uz4um&bmG|&3z4{4wJmn(6SgV|6Nhm8o@ot}7vUGkzG!^@a1@!x zCUNs_?aDm@yD$35`U7J#Rn>G}SGx383NtaJdNC?7OT#YK?6ZC^kqkC=%nPWm4VZ z$8jKM)@HvFCnwjM{%$u~!)7bIYVPvGWP46_pXmyU-h=KdQYR7qk6*D$esbF$ZqWQ= z)@Smh%9;PE|KU^4C;Qat6kGvw@BFNYTz`D_RzJJmwAedkPwi^ptI;|>eXSfXM$J#v zuGZzIzauZcsx-MKPHvcC`Fd!xpMS0a6Xdxb zI&$Okq8?hkOuD~%o9oLwMcNFywadnY*5cyAERL^L@?bb`Uej_~Fqy{yB$=5@#f{WX zDescD@%_w`ox7@1%ZJs554PA+3oj0ouYt&Y=(F5(yf}3a%Sd84xwXpZazMPZ%eO< zi-(-v?E=Rvs?M@w^JNkbVQ-`gA0)_Ip1t;vVG>NJ%L*vJu2t~V>^Ae%qg(@Tv*p}7 z63$d&>~s3X;f7qJ2~yluQ>*l`-&oPIe_AtdQ%z_*Kz37G2v~jR_(e$ZBdyo^GAU8< z1=$g{U@aT5=cF)k!t-P2%kuwOU1i-Z9aR)pkhJ3 z5>DJZGg6@Kxot0k-QjedjP(Wmr?#t+#_9=Dzl-D=nxjwinACMyu%wFDJ+ zv5R;2;}NKOdw5c{?i=rXr}mccTyb{xvm2T-v~z5q2kyPWY&a4q8W;ktM8#(|Bg*W} z$FO}`bqc}nC=8w#W$Y)qMoG^<=3_`MnzRF_+m~e}EJpS(1#C5kO=cSmhMe%iT*A*z z^WVHa&nTgIcnXIZ$Nl%`MtjUg&D^%;6Vln2T9TtyGp;lW!R}8~btj+3a}2-047~No z`Gt}Up?0lkzv;lbM-=}sz4C5=NNK#{k4uFWr@!zjx{*KP2u$HFv@7m5HT}?BZe3&N zY9AoE_v6c*r})QbDm2GAB)!?%^PgCJ1MBe`QkKO(=P8})X{Q(3oQ+++-OlV!f7IxE zH~oh8K%nonONt2nDBlE?3iWMTcM@G4!yndygBc@P71g38D0&uaZ&j(#*cVK4JT+g6 z@5pZCa0KBFN=<)dQxV+)ahy;4Qr^(jP(8JmEr`7M>nAdo=MA=8Kh07yUvAl#ZG0AV0w~;Koy*AOce#!;bZf9ikm(={$#-kH6 z_mE8K%$?VnWgRAx{|lZQFNeLOVX&{#Kpu%7bX5s{ZXXmA-~Y@ zHfM&z`5GDTMeBt5IktRVa&6&FO~dC??>v0^?)PJ|VAv3;y~3Z?Ky z1~1a#+tow3~0gEhRLUQlQM(E?lYgR<6R|= zxWD}O^fv#}^bLG!PH0&?O%Uh7ukmUTzl;nT9e|*6y+ms(L}XvuT?M?fA+fA zH=um;KI~O)+zPT}#AtAz{>X<2R7CpIQHR_WPGz9;Sv!!&%B<~6|4OVQ_OUO|$6$*v zy4L<%*w+e!`T`3)`1@Hh*C>6nRZ9F<9qX^tU$^gdRp5GO(|yg2@34A&thX%W7wVnh z328Ei|638%;IrqfdmkUa3tjQrd^h#r`B&ax>uIq^PU`LRoLme}d5y1FeIt(n0Q?g4 zHKB(s)@RPEwi?{D8VQ0U-C?rrsxl6S_PFi7zco42YK1F|Hjf#G)p80XEo(%*Z1iuC zua4G1Aaf;&UA-(e2XEAU?)~3&>XLQR3pU(*< zoYy}Ev7aV!9=wTr_=(DIuRLZjHI@At2Vcj97T-KxhEizP^sZ! z<~G+9%ah0W^TBxH+r9c%Ob8=7fRn*KUw z8k@Dhc$N=p99WQ)G4Lu&iC`7c)W2+#DlJl8AZxg=^SrV;$ZAMe>CK;(wC2cV%leHu zE<160OMx#YSbFA-Im2zt#^2?(ku!^GY%j)yGrWXk1utn@@wjx=e7H5%a*eDS{Os;UXcr>ZbKha%o04At zA(Ui~QdoZ8tGKTQUmeAX<-@}|kl}G<@rg7#5?6HIzg0?{aTV*BQN0pgqC$$H8_FF{ zG`-7udnKrYx=QVuhmy{`?;Y=RRl3WxYCa(I{bwa&DZ)~#zmB+Y40ol7yrp8>C&0$F zVo^*Hh`&Uh#eI8UDbA*HHlm`Z%SyVZkQHa9g@v`n)4vwq{z>0PS-n^dmrhzto^I0^ zYbnE9UE^kua(xf-f(F})QTSLLJV|o5{?MbV3HvwOCJX3N)sgSHlyFp4dq;oeIXtLj z*UxmN(J5_qcnuw&9*vm8WcVG@C#qq(u($}k6yfo=w)7TN5yu~j1u{hP(Lu0e8<41uo4lG*_o$9UsevZDC#V}pp|0FI5BcgdfeY5u+HU0Ky_8#o> zH$r|LeyX2@(x*GGV?lgZUavOIo_#sl8R^1tR+#QHa`XW*Z6K;)58u#f&H4IWXd*ck zKNVSPc&$zxZmXlUD;+U|H-(X{rP3`I4617V@!dq*m6D{?hcCmjw3NTN-TefQpyqH! zB_pk{;@>@01|vL!hAJ}AEVI@lLvQ-pNVxcA0e_bM5?GHzES24C^}dSI*{PIDdyEj# z@53fXP_klZh&`_0!tSe*Hohy)z=S(-7el)Wkg8y9*ZeIwPd$b6Rw%#xYI1OL@`y81ibe=c%=^H(yA1ah~p3KTKl|sm#k0@ukUoo z6jUsYs1XPKxkRWA%D?4;PEl4Tue!{Ij#lt9po%iUdXn1SPzr1j*c}>%q3ySr{|xdo z?XcIxY~-fd7;*b-SOFox#A{0=0}Z+ zesM@y{!&uI0AdG&QhYn#e_R2T#up$yJDD_Eqchw!LzBHQ^;c+Dj>6AIpvk&xA=n(H z9<11xFIn07UiFw#uM}Sg3#RRS`wEMYH7pZ5bg~c~z#3UOIo|xi`JUHp@#uO-{SrP$ zfXmj&q@y{~6 zT*CP4V;Z*9&jz7;p-8NmK7&LGTFN2+_l>VZ1S>e6?_fck%dj&l&rAkAgeGAf@kGMc zcEz7A@&=mBtDLo{48=W;Z>~ixFwvV)i|Xm-OzbFHJ)A{a`OwY3V?vB1iwZ|2;OB8D z*tl06DKjkMKB)(VS-Z9skc)x~akKXr@ASc3-mta@Q;oDxX=$o-*CJ*It$n_$>arR) zn8(0d3MQTTORF9x5}qi1Ybz^k*ZN+pfPA!F{&M(??L}Bbp(Y-lDt=F=T?M5hv$p70 ziFls(JTDp~gS?_Di~>(G>qr#CwPe0nR_CtR4}p?t-31Yxbtwu$3rb}R>Ih@_LA#ka ze3&6JA9aH*Z;%74{N{#b21)^aX-r$t>ukM>1tnph z`E&S$bj9tTjSdBEJ+qQCWgg`Yd#246Yu_yRya>5mnG#|2fugSPS$Y;F7K!CZ1{rWy zAOHy~X=YM@%*U{SloyZg-Ta~D{EXnHEnG>%#YAtJCb=iF*={UrvV27y%_%=M3Mb*D z41yu1fM5demEYDChEdl@`F<@FX-U$)hefOS<)aYm(!Q>jT+OhLQI|I}NbE0C`AL)t z&533)z%c^ze(8*tcYzH%67Mq^c{Oq}!{Hi|;Q>m9n@-=}`jD@z@Hae5%uAf9E~3q* zmw|raW<(p|q|OM>CvUkhLvFpCE&Y6Jll+(hE|Tgl5`+`%_xZiDesjlD@p zbuZG=0|TmwtrybBBLW{eXD-O>1Ya*GXf;si(&p$gUu^!HP;z~dO6`-}uaT_3`3?1g z>d8d`PzU^rxC4b7jPXcSZl3{%ypC(x^v_v;hiAL6PiW;U(A#4Wg-Znv+GXplDYx-@ zL)y5K?9|;L3*Ytqwlb2!yW}D`3%Bj%h-=Sv zBjw}%9%B)K;HxWhI0a#Ew#}`IH^aJn3r{!CFE1y5#nrImk;6z0mw+f8A}$^_t(I-= z-pf7rzH7Zz`k;J}VsNy32_+6o)@E(Tu!IQ_&rx2rR?R6;YhyuCTLiRAlEbLA;kvX# zr-iJ948I=vtEIfEv3xl7@TBX2_|EZ*ZieuyKu9>?PGF$R&5x{YL9Xu?(vQ~@F16Ir zVR4KCf`Rj-Trmq_J(SAE=%zBHERyN@ToA9$k-50KL_ zCqz$Xo9-bW5VQR2D;Kxzlw`mx{z4ydskeh6aT1Q2$pAJ1P_j=ZOh#sNsj;NF6ASK}6J+1TB=E15$55b=_uc49 zT%_!q`D&XOdXPkR~ikH`fIOZ)nUPp%o9H15Bt5H+nhL$&7N*FeO`W> zF)zb(HyK%U?(dqkhKGHT{|M)TuJTSRd(iO$)xr|0=<1F;A z@v)A0tpD&+3%a+Sj*)9fR!RfBL#wP1h%u6iOr|4N+a|Ab#679~@-o5wOsk^sA-()7Ap;kCA?l_heA zyBAADGevVxo4FK?kis-tVF8KvM=V0kn8luZcrfmY{$Jq7N&O}&k4YD>M3_p1=Y#8x z-wRb*rI93QvbQ2aS;L8>E4i33aiS3#I07*pc$}x|uE(xwC95M)xG%vK0OHIfcOtkr zu<}u%fm}R!VXZ;a5h~Shx#O)sU{WmIS*R}+sY6%mx|RIM^MgP*u15DiDq7RbIhE~c z=r*nX_T~g0H6O#vI6zb6WHgaWJWlzB{sV0~_!3Ljs*)`5@M;Gi-qKr9T!7hCu#l#S})AC@X)t3KL_?ABaXlRf29u<}&e{ROsdLoTC)s$SK z@jY(}{ThrFoFWnMCmB&z_MIKPG^;;GTg-*llK(?Qye68HeR0NX@koy7?qfJxfEv0E zFYxJpu(xw>67te89$O+xNHd)c3u z+EvD{do&lyhC7E5TSD3J6&zV&eylO=A6gm$F($b{p#B!$!R_F$9tq{DPm`>o@ILea z4BY)hG0__I>7l7W>&0nRvY!8ycN2f6?47hEa}!F6R>w8$9gjj%WamL_S~OYatihRJG>E{)yLRN zV>za6#a7!&-|z(=?|eN7UcGUZ`gY-Y8x)_mX11h*FV|E!}Zww`}`d68G}XUgJD z2k<>K^oe@KFjKxfe4P0zjq&m5{Sh@8d~fxmfvG;`-*o* z@`J^dU1UPcouq`TV{ZFt%eCWT_s(0ZiIe_&VQ{qVzge(dSzdhVcj7p8ZBTz;Yw>A^ zm3r&z!`9%^R--3vN1Pqvz8BSs=BtC>gbWAFe_145e)CIXz_}x-?Ays$@XiPFji-n^ z1AU)9$|2U2H4*_(eTslqQu<9UWFSE$dnPdnRGU1?gV{4`+ARvd*2wOC);`I-$uIF)NjRn zOq$lM)~zk=i+ZH&dq z=TPV+t0IXPOvkg^DX*;MR6hH=^rw=9s0BtB<$bhze{-?FLq7j3#72LecvbF-ERJ`+ zpKj>R`bAtw_XiD<)HzB`;l|}B^4BW=TaclXjoipIt3BNQ9ec6vC#zs_(PA1tdnk-q zUDpx{vtxD?U$7S%zxIbbykFVh^r&knq}SPN!BiDb zZ!XzALLNCi4h&uVAYoP%9lDot%;HbnY_el1GpOGa{N63uK45r_OT5q8>G7UG#r5bk z)@>>cx`>-^$GfSXEB8I&zq`_VW7>A1>pfg2P33dZ$fqAeFLWoRxzk5~OiNuG{ z*{?^}g!NO9_U>i3NS+?zbulY>F>-QG zrL2tsN%74S?6t!5v&|C*wd^Kkw0*#8o%zEot- zbsyJnyhmSpmw0TwDAZ;aJxJ+;5)W>@M4E`P{1p|b!(`kb)n^=UYxp)*LzAT?Ep8)j z{FPfx&*)PtdjSw_X7cSiyZdDOylC>y%`b#9i_MQEg7TLdbDS#~#Onypf?03iTy`?Q z+R7j8yFiZ)kO{vNGC82D%3P^4pSv9ydu6{NV}g6go$zM*ZN5y+R?@h%$e)+9l1A*T zLHLRED>@lA%C2s)T;)Y}8f$}3bD z*-Sa%##VAl)S8)1Q>I&Qt1(pcG~DGGh#1|~2_>7H%%fTH)np7PJe|Jfb-$@(=?EKO z6Uco3&YJtLvars*2fJkDLB@U0y>I`%$ZGVQA|{b53n&WO;6pc3ZHZm&{E=LgQK0T- zO_FbQBG;IQc&uXkYU+>Foq#PKhs%MAwhM9%JaVC7JceCW6r=6#w@%p19C4Aks#Rz= zncqS$wl4IzWn(t|UbEg2$Q`biw9`oR7`SDxp(3kKkXH~^bw3f6E8YE3{Wd3GX$n>j zZOK|@aou5^De+IebXJ+AAnjZ+FvamnZ*wqepm{OPp{S^FWy5r!uDnoWM^)o*mgC8M z?Y7&MnH{=;^9v6d(gHj3+bcY!hP^}ywjpZ%=_h?@lOeOek2(2Y{s|bEr#{(XwGN%! zCY6z6w3W{YchPIoOSzsDrd+r!^>f(v?zIPUwxXtTKhSDboeQe>6z@kJZRy0DeXx)N z8J)R#R6$QS&R%HoX>$&+)Nt+_sD={=eQm~sG-b@&j-1&>g{oD`)28A3|!U!{#c*uTP{=W6DG1|Ajdm&xj1a|)rSrr z@qH}lm-nG~9c_Ir7!&rZ)2g&m-GH>vO@SAuFludnVx?I-^vPQ*5>SOX!;yydbvc@YIQf%r3mHM$+;?aAJ!q9nk zqjMIf`kB#Vo|8u@7|nNAUVWvT;A@=d*gzG$ml0_WST2&>A4Y%L3N|TD3Oe~Jg4!eh zDPMvNFRO}@jgi14s%hj&lzx)Y5-Qe~W?<|ab9$L3z z3IyY1xs4X$e@gth?K&aXUC3o-^5W5XNpx`L)l~z*GiE_CsK+7EoS!E=vs+J{eKI)5 zu8_N%T=r4eC3>>&g3hZQu8fBlK}OpZvTwU?%%NQ9^4dw%V&aXP*afpW-FST5?|$Mw zThz86C+(~bHF)4p>774>D``#yNcU+g1Nl8RG;(6-hJ$MLU`);n`Y%sZe@Gx`pzQ=HlGUXPRF5$d0npj(%&dJW+D1~DZS^oxSBme11tGVjh znvPwqVO(`JN5H+95SrqUG=tcF?~0IL@fj@5BC~)N;O0d}`7cH}^9aDpD1W4mF*p6DK9{%3K#f*>>;zP>6po{Z{A->RyXI z$MdL)>O#wyyf6DTBWt8urbd3d#SA3~rx}7`YPSMYDNi#~w9#LQi8nR{bDp#!B|q?_cU=Q-u_#M&9|OKYD&o zeQ=<$&n}a-B_<3fzjDhCxGkrvGQ4+{LMflBu*mEhlC4AZ?`3aR7H!Ay-j8F3&-n@l zDIOp7dBsUGO}CAciHa|1Nh;p9Gr}90Sgl!6!0dZh1oXDW7T0 z?f5R{te!g2#j&H})?u09#cwUs6w^FV*^ zu=`t7RCw@TGo0nXlHkyr#>3hT5`KG>Qc&5$%#VV5~ z=Vy^o-DKds3p%@J$eHLvJC6uM-LHb$mt-^r;nn4|A%1`Drd7S@X-xAER|q$5Ka{2M zc+b)2wyO~HvNo-@Ot_Lnsm{5i6aOt(yCw3XUA&L&8-dtLYqH{j68WrHQW`~;sB*d( zP2N|y{NpppfcdjcnApQ&CpVHKwP@xVf8F755@CGNEeBqH)Dj??=$K z$>c%jVwd_ZF1Fp1S=FZK0?jQ)V$g z=YzNKEmPP~{(Qn8?DS*nu7i9IfuHDgHEY7dCwteqK5A(?|NiUH&zlb0tVq~ZZGPCG z{;CGAbhYb@>h~Kt7vaE>+1{fcA>xe`bZ3t5BYrCQ)P88!o3yXS!{mp9#fr5^4oLD103sU??C?lmJU}r4 zVos=n$BII8zL+Foy=aipn46sq3FU>v0ZdaL5F3L;XxJzD#2wi{?KXfZ0oVp$I9>p{ z81PaEkP9IuE&w1YC7FNcf@8&Fp;-ZUnq4y3Vbbv$0;@_u#Hq9ZdO``H)+Tpw1xPd( z+3_geGFlZ6NH&M?JY$LZ&?PX&JO36$65o^p0LM@=Ow$;#cBu@|+Pz*15ztmB={X$w zrCh_8z;C3dUEEpoF7%0xx8)s8S?Z&S3OlVRo$dQs@gho#2fv6Lr9L;kL1%g8Z%K5_ z#B^xsr2{|!WEddG1#S^QCJqn*K!yW)5dgg40@DCh3Z(Jy&U_G^0brNJ0go~U=)QS{ z1Ei4z0Q67u851Lg0M+7OkRA=~iD?t#0mUF}NgS{dz+F7x^a3bx0M7ulB>={W1>gxD zu?t{$0LupyEdY}y1H3om)tXFELHAIMP4Uee{G-U=X)>2V;#60d(lij=gjQaNhd+Ye zz{pJ;(8mFhg@1p{04DUWQ3(kKT;T!~0E}-DtEI)xeI!u&0ui>~lGWzKlvd;^VF1`2 z=uD0jM841Rd5nxrJId0v3sgYE!jcO>z~rfMT%>|GpLsJp$e1|X^XtAF zc1Dq(U|uKArAOd|dm^zw5&&q8{Q!jrJOC^Js(C=l3GoL5%?*_gwKjy?6QFc9AjN@T zEQ|(#OaMJ%MUo7dGiv}PfOG%i0zVGyX;Egbk zkiOajm3(4XQ$Hek^a^~N2lx>Yh%SqX0|ktwH`=ZkQOncoC<`R<5}eIueSl~z@NJ^* zAHH6rm0vX9Uw+g?;t?oq5_sre;7>raT-~B`Lt=@VVU)Fj$OS2>!@lPk|CROp7$i}i z)x)Lv$-OQTr`0Ci;(KtjTf$|KT{ZDL&!<76j!IWC>6GZf=dZHax!vc!3AwcpJwi_v z^KC59C90n=(;6-J%ts{ge`fsC8do$`pE2U+m_bkels&`7PWID#~gU>$y zj_2z%=AaaQO1P7Jck#fkbc&H@iSXLz6@xgQlwf=l)=G$|us~1o1b`3JJpdhky#~+^jnO!R&!JoWBWxHPQ3fQrL8F6RNC4;v z%1P>>1TcV<=ulep`8`n+j6T(nz=KPsJ$q=M~YSOAUraYb1Oyan9v_gu_@UKR(Mo7!plKj9M;t6Pv8i z14%brTMZD;D}FwO&1Gm9X>Vne+xyn#S^G+f)|`@xQj>%-*vplC znTuh5iLY+rXw~?7)-h3StSdYQ-)DY5I8@P0=qo2+u{pRO#e?(Gbiu*T0-GpwwO?lu zExY`ON7Ov!4qOx(d(n73@TFIBr_-Fp_rwMAia!eW!uJC1lNoP$bT4-eXEHCU5yk*x zeEiJ|vyj)pt*lb(iHq6QPvYfen>E#TsyZK!W&XCOn>oSN;qU^__T??^pD$_SR$ySX zU#&pkwJDJjKujE6{Ojw z%ICpA8w$^e=o<*n=?^ojc#=?!@6yFw-~ZY+AMM}&o@xcyw>VILQuuZ`^seW7C8@F+ z%TOb07=T#xXT4(+?h26)$z@*EOBu&h36UFbb1e}7P~bs4fNT;f8-OhU+lA3P1CR=+ zVjzwazo1?KaQiwhgcn-)1QNDq>9zih7+5albUv)y6)-gg^cLI{dJjmuO`{hmi#HT) z-wNas041L6dn_r-k!YwtV0b!%D|74gKfK_lyqz=0KIb;rI0dT$r z0B_?z=Rh(Ehz23!0W<#hdx~Q~p$kAF4zPSB06{1}-v}tj0X!9OutHx04K-Zrgg$`% z0RUM(ZFOk@=!eZb43B!B>+t?@1K*jn{UJR`i%}u$#Pi6AZU6OOHkAr2NBWqf-rhA} zS~bCw=vDdLg*I7nkz!B-Ces3dvNg0-4b+vBQkYKk0V4wm0!6?w_ceqObhX*l3quB< z%`@y#xBN40rw)uXvGrUs06dg=q}zz71#GRbrATI#)FZzyoOSY9Xd^3CPJhAkvkoQd zkbFFV+R{7YVR0Yrq}m04zL^xszgn?!d6zBOO!rxRN%T$-7P)NB`0X+XtD@4#d3Ei9 ztNE?=9QfZ?9lXcHsuqYjzaQSZo?lyTH zEG(TVlk1=DCBcI?0KCTjGsXrupq&pfntL=Kh*J=N?mt_B1t1!a@W8`Tr9^R9%L{-g zN331fnj-+c{{X_H+@7BVT)mG3V3+`UucVWcI;ah|hy#&)fY$z}-YhtPN(LkZfFA#6 z$e|J00pb|Iu>dk5!~}9_NY(LxcmWsypos@DfRq3z?6GH01k^O(1JpO{cNtbmF|hAm zjT%}Wlr)8vJ!&LRk3BCx4X_A3R80uoM#TVFYXvF)aVsid{08dw400pEIlUP;A zAKio=j9JRypQH=4r+x8>xX*D54vsbK;nzQ?d=Y^vts7sb{!4>_zm}gT0DvGJIt&8F zr#-rnT~}9-0o>J)mpn_JP8HIymJF z1c6Unc41TiP(qx;0cd%0SU~lUW0`3=lz;W$lb@H~m>EvA9@VejQ;5 zv`u;w?fyo9CugYhRMggA7d$M+1e!UFq~L(givhaxPbVaJ!Dqh-=?m&rVDn!b^qCSC zSr}bz8Jlxpilg|uGm{^{i$5T#DoUjecapvVEB--6zJd zGQSWe@u)(PntY9UYm-N}mLy=K^$r(1e9oT4V|6!U`&xfrr1L(&iZEt!D3S$lK5Bg3 z1C$*1o~+=IFg|GgM8kCcm0a_*u6}sZN@R^J7f?`+lT+x$T4+WAqC|?4?&odoxcqDt zh?}ruSHB#c;V58L6jAfihv2PJ3++OdDRi*~D!cN^L0d=^M?#N}@bDO-4rI*{R;8K| zCRQLF|3jef=KYfuid}}6QLpsze=>k07pT86J>Q+u?O6HNALxo(R z*qxekC<4Shh=N^2C4hO5FUJ3ODKcpcm2h}08^A-89jm_!NcFKe0U^xmmgpC;9XF`H z;R*F~3wqB0@zpCn1iUF1YZ;>s?$VWmt{gx%Y6qt=B>?yv@!wAY5&}{GLqjKd=KPEP&|(DrZ=}C=M84 z0EPh?kN5? zo^-waHn0`NocVJaK?l8y7E7j5vj|VxarUBF(n;9$GGs44T2EEDnG4oPEH;R$7Z6yS2M;f#kDo_2 z!xpeCz#gbNf91Uuh8*hjs znZgnf1dh-gvGG~W66@#q-UqtkE(9R)KBoJ=3XQ!DbpFG+lu|D*>OPsPnPeghk zE34(jjE5$S+&2ddtGdu0tr}1{Lb=)RNNs26wU5#h?47LMuZ;)^yNfU}PaX>0V z0?$m`?{nwz5M3}66B7p=4VpRs!sj;*iXGG`vt{^vm=|C&a)mX^wMvzA`C(Pby)%TX zEat8MRl0I19SI{8POwl7M`E|YTY-pOkxNCLh(uwJM~QWGSECY&A_o#z#VyonVj9Gj zUOo%v%}vZR?v0@wU)rT~xr8P_Q!m|eP}yk*+~f)nBp;>?DZ9t?>T%a{bU-*45bD7~ z&Bm5AfkJ|`B}=?&aad@Iamu3#6X~PlvaVeL{m_XE9{&`A(MzJ5XPWhdHb8pkOiUek zx`Kqv?uK;cMnDAJgUpCQeYegVp%GNNK=S|ywy(mwyQ(m98F;eKR&3!fdu;e`56)*M z*3^-$0pfa^&9}S(uQ5P1bPX_5KR`$CAPF&(z7PN73>FR?KX)VVp@tlSEfzAxM3+3F z2$Qn?@T@6Qb5aKNP2k#@u$bOT{rAPClPxcm%dgxa9(@aW z_84lF361GtZ5IuTl_Db>9TO6I4yL;`0x=<`$t3v^T-0>(FHZy?oJ*iVI-rFR$$Lmr zwjWy?IS&WZeK8-Y6e%=7j%g^1PX#^`cgohD^likdeQvS&Ry*(?*BQfGJ`tEd9H;zY z8URwyf6x!GECA?VTRyPj!GlX!a08k@aG&J*XSx0pbI}4CI5J^tcf*)OHt=}x`>zUQ z@yF;8TI)ne>_X``GY$Z0JHP~Cfg^zT{u>Jj2LDfH7XT3e5J3PGg2O3DQptoYqT;xr zq-r330W-pfx0CsC3g`CNj^s{6S@DM+HrS4R9|ZKZP(PJ!?M<<#s^dVaP%$4MJZl~C z+IUFR<3JoE6I_!>6cEM7K#gOo08+dC9759gI=Cb$PCRUN7rLDgc#UWsG^@GaU&1sOVgF);MVik;^upoYEIMh63U z(dk$0-va^wr;nduQPeAqQF~GVnO`^GWU-pZk`zfl(<}Qp2HizHCmAk}jp~BqUYrmivGhC0!DkuhfGs zxwr(!!2hb5)J*7ZT16~?|Ipz!0xupU;{jC>6%^lD!YkyDi)q_w;8Pdl0IFR4%(spJ zvxwm`ob-VFZ$3nlc;S2k6!-q8y!_?a-~%~}qc_+zcbX1P(1Rs??IjzUPJC%^CDV7S z7_1s|E~?X*erKCi(4p;9(LluC255+{FXEFA`{6j~=$x|#6FmVRF3$KqAPg-LgO9=@ z&LO(OWVu(gRK;ep37|XLCE}yl>wG8@!9aY1l_&vl22h#+rvJ}^u>m;W{{y?=Kj>?K zZi9%u3$76Wx&|O2+h)B0qM&Tq7KQ*|P@v}uA!OJ~0FweJ!as2S|1%v7co@{N8^AiT z$sYkxSH2_{%CVK`;70;%eFBLJFCkjotG z%fSy=SzQ514D+DXE!Y-71g`jalUsq(SZj76lLY{m#{vi_W_tlNp2~4HCFNfKC%oCs znu+E9J;_H?usGU>cmOt-7XbPhWg(O)tfPw_)bYOF*}F}U#$#1}#>@ewk80~m8y83F zNc0`*_TGernMX=6Z6e;ZiaKWQGq?^D7A%AzuymDk%wdn8tBQm}F)Z9FG!5k80ht*JgJN0OhujGl03>M*+K*P@$p98>PUWiK;01IF z{F9q_UhXz-H=&v0BRyx{y`>ajBN$1ud_${zlhxi|z|z4u5N^WWF80NSkl7#!P|2KU zF02fwA-N49Woqh$)bIXV-rWsV9wZXL4$sB~chsi!esT?fpQu562-h)ku6z_PvsFqW z8%IDLQ74-iWL#Qj5AR$PD#2LyYdAHzMqGy=o| zc$5YJfye_kjtB5qm)`5&se#HMP#N2tcG!g{!rP9YToEL`VDilcmrVuo%rdf766c^U zesnETMH*W3c;aJY6dn0O%A+?CLYF^dNtz%QyExSL|Ds=kOx>?ygiu}fY7OQ)00oL4 zsqUVCQK8Hy>F>DCm=Eh4kFZh#{G9+W{mBM*UR9c|093R%4p4@>8v`;JVRjt+717(} zDfHEc{Jr0cSPncu=mUu2KP%>LvUmY4t7pehzz!0h;|GzW?Nq-3XK2Lg>CPy$KBW;z zSqAa}3hmSgtyTrVFn}0PL}dUpA8bm7ko-&n02(|91}Mm$p91PS`aQxwcnUuk#P5cD zPGE@vEVBz3h%9l(U@?6iz`b?|{|Lvm21w}tsU5r$1N?LW1UZ0!0Q3z2q5lvD6&7~! ze`OvK$Z|!Zjf5m=3Pnk_N}H}VqDY&xjYx`0s8q@&LQ%Fv zsU~gOq?9D(|IP33>G5dhuHQM|d*1V&^W96rAb<BGSP=M0MZo%OgBZ`slp7# zshM5?GK%Qn=seJgexbD4gAX>F0o&v*&gQ@ep^M{U0YHY02x#iBTTBA1Ha?Acy+-lQ z>0Qk2OdDF{716KsDBv+&MjwKq8r|x;0y+ms4s^Q*Q?*leNNyq}J8Mo9nWMV{J#;A~ zSWpm!l4!sv^Z`ZydJ~qk5HiQWh5%-SnB6fdhG|dN0z9@LWt;Q_uv?wSj(P^zk9NQ= z{se4IGhopNLXcvNFitEFA~-~*j_*NdJ}$m+ukV!y_ugp=I$C13#y`8QWwgdy9c|*Z zI;3viUxm|WJ_!M1OYAydwe`+?!|a}bBcf&g5)8pPNKYk}!L(-bFm+|} ziZB5h7ZD(_2 zL)+yFxu8v;8ST#z!qlt47#HE&?Lny#80C6ciyBB9RU-fbNQnY89LNn$DvQt{hV>7x+YhtmO8N!aK{WzRj>%o8-0b1X}uYNl?3{%`DIXmn`Z(y&Fhl$KWMcb>EkUs=@sBOOZvC@T-y17>ZiD`!>h|j@^{4*WUV1qR_i)2-Iz z>aN;RGQApcwFu?tB8T?q4$v`HX6Txw%fPVyzSS0cjLvTERK8Gcjx^Thj6N?9>hY^QdpjXWW zAv7e{gf_(T7-1ImnqFu!PS`j+9q&=r{8?tFYZgTtZFZW8P60Bjm!uod&xnqHP`UoX zOuGf~MKYT=pK-Bywsza1R@6#ju7#+oQw7N^uCIi=*({dHede(OD{pY*l#!RWQ7krSBHuYkJ8Q6UZT20mn z-z^G@>-l=K0u+RQ$c5HE~QI}mR{hz z^U-bbIz&onym@3d>)}Cb?!w&jE0jxj{J5SNTzN{spBha7Zo!8Dl7=F1JI>Ps_9@_y zE5m@tM?itRL>1|5ZH4vEq8L2ac7)feV(6uRWXap8>7jJGGva<)ISMYN<*W8+qxeJR zf2uTX8gxt_YzK*JG&N9k1;&`TXo$ck3h3y3JYaYbkS~Vc`RJ@(2oLeaB&LJnFbN2a zydaK%#ROauGdVzEoR8eW@QHAcK5)Z%K}2N9cX{*y=->FF28=~zZR8B?-(F_ANAJ7t zK~WT7?by^DfoUVEZxRDJGRFRBhMYSjl)A3=4bfeDa_)zhPn02o*=+E+(3K%=6Lu@Y zJG1}W0Wc8euik?x65~JDo?xKksM}cVL<)ZVk(T%joD6LG08Z+R)E4T0%rRAAIp}(DuVk{6bTY)x@yU9L%Q2%n9~m1N^|gw zOKld_3(Q_M=Now<|6kPqC7ry*;Hq*yCpB)`I5%&zgvMUHyGq?3e@x1}pw=Nl?;~cOMhU?g96ewn#9zNVTTZof)ytL3wJ=(3((|=1Lhhu zVbSdXb56QmI;IePdP_eDcrnlQB9koO#jK-oYk#|Tr)}26=zKG0I`~qE{UADlQY92i z;(-22Tp5!v1xc|kPCo#&zv5%dQAi^}WRXFDg)+Jpf!JgI;~>0u+W=7|EDU7Vy$PrC zjfBdR_e}r9?QkIM#~bS-*cWEdHaNZ!7^fvj75Mhf>)$zkGv7~ZeMwEsvcwsG9xp%t zb+&(z-J+3$DT)WHWc=S&z&4M5-SwH=>q$9ac;%n(>NGFeUZI=sJYvSzLwy8b_8JqC zH%?#OIGX_n=eCnA4#62QePQR5b{frKd2Is5HuubhZ0TWkdU;qY#D^Dff6X}vjOH?n zqQL6}+{QL5?Ta~oq2#yBjH(oUvG97ngHN>3h%#u zXok71^FA&%wer)>KiORw?1Cx{QViNKkJ$?mi60N&c=}& z-(Nt}%m6JkUUaLsqX9CdAp^jgOinOnw8!{KZ}U1d2J{@PGhQxY;;4bc`lepH+oaS& z9S-lh!x86=?$3$U>2z$QsR|JqK6n9>53EiKGswfWMIb?lr|T3%0Ly_^4suAF3Iv#s zP>R4X5(Ff;p^k*yjF^07VmNsoeJpFyZ&s@Bbg@>?pwK)QSFX!JMg*Jqp!Z9NR@l*m zfDTs$Y(z}OMKC%bf50EyCqaUQ_U3VUj=o0m4f4~pv`Z8mZzbVoC873a$d$(Brs_n_+A?!++9 zw$Bd;Zq5kHP5qFKX+&ZLzy;|y#vliedZe8H$H3Z$p+3FP-XT7-Lf{D-- z2~s5Lw-LJnde;ILgY%U5O6E{Oh(&irQXC3AK}E$8BCXgC01HHoNeMF^fiNsF zkHwQ;Z3LJ;x^MI&i}FY0>MrT%Z_R8GLpX;7lsUFz0#YK1`i~GZ;{o3C!HNWFiD(j) zGn;b-db-EVYp~eV*Z6YSxo21@OsP7yGgkQmJO9y?66?JH4fEy>$7~2r;3?YiDD20Z z%qB;dups?urxOriXnEjcF@a{}goX0fp{HM~pDHf|xp`7GM4ZQU1v?77yvy#tBSE2# zjiI;Q|M5NLWlr0#N6O<0mg;QRcOcQ48Q-E{#Cnud6`s3yThcpoN7)cm=zVeVSI)Cu z^&ABK>FC{pC&nwJ|8ZV!hEe9(LJ^a&6E zsGNut>q$rz!o5I-1Q%+JHwQ6*9I>_;OaeaB7v*dJ#rQl_pw0oByLd|;!p@5SryPec z;T^ao_E8_|ve@M&69B5zO$9GjGG0is-iGf(4!!ppF(tW!VrBn3A4+z^1 z4mVLhp3GT(qoUY5qo+;noNerlN!7D{cs!k%Y z*qW<#T3~5iCx+(j$A6xc2a_D(EYr)gN(Ai#=NJYAzCKBjC1pDB+%4@43g;oW1)(9i z&g*7rJxn{St8Cc*x05=l#{CvhMj(5-K@4Mg=8}6c5%0s-AEVeUFPk)sfAi`Wy|0Lp zym#QC&fZ`j3E+H;oZHBzE&hjQJOLi+?UJ#D3`;e^ws&nt%G0UMNgFk`y_NV%EB=>} zs4jcaLAdaxt<52Pe$v?uwb1S4S6F11F?q$gxU7ZlBUi_-3h2ToxSWKsmEYBjQvB@F zUFv&CPK#&B<_l+uQH-&IZJ^z0rG#-yZaV^9L-*ak{F4Z;~kbE&`l}$yMw|N zj0UbYn0RaDJ$J{1&vsR@J6&1Hj>ZN8caXQiDm01XGnr2N-0OCv8y{;K<2kwMd+hmM z^yXO8#n6cZ$M%J5sz}!is}0*OQRNWme6v=M{{UQon)#N6SUFTnxRf#gyd~FOmwv>b zELtVJzIqp6Q6Ku{6G6JwAcVjetDUT!7E5uo?Sc7LFKb()LtoXb5 zal_?IYcnT9NYb!TifFj0iv*DptLjLI;VGA~?fy*m*%!sbu@n~Cok2yt$x-R|tT z=XIyw7I=>pUmLO=<~ zc>$XVD6q)`7@;=|Avk+L;+`)>uvQJ>ToVOa$cLK6d%3t0yUmc0u2;AbS?q0P&)fR!@tJ(`Rd&zPL zbJ4!XZhb32#@Ya3j<6`FPwj;lKGTLljmOfYcav1mp#(#FhKR>fj{28uFg6 zN^S$pP7}rOeJLZL?mU?6qjOF99TqNeHb;+dC@4XGe>Xa~a-pPF+sY`%=hAJ4G~8R2 zF-OpB_I1-G5Z1?NilGFfDoBVPn@T8aG zLKnZ^8pDavw$G|Ay~*WUi_Lu-hMQk*s*3kaKSE4_f#8~H5CNjD?X zX!9o_d(~Jscge!~1S{%z3jP^sv=1ATFzu}sA&}Kxr%XqqZ!+}?V784Sz~`e`jkhLF zl{dFf$wX$&Bw!bb0va+FTO_-Lj#v!9$MM1>mACNVWqEL7usP$>s`JxJ^w06c#tV%h|>^|gbXx=E<3 zFUf8FIr~{dEa_lUC+}vs=#6RZc%SmPwsG{CIowJl0qfx~CEEj&uV ztp=He&w*8r?qi==1i)l~Mlht^@G9w6_>3*YcnghCpD+pY7fxAsn}SoiD1`v5bp

k+XI^wZ~URz|lgm9CA-DC{nv#{gCjEOSsqpMqjjJd%b;8XfSn zs8vEtH>TJ@+Bu|W-vb~t^)@*s^A&fQEfMII9%YWv%jIkoQ3AwUUXMyEp}{U%9F~;t z)68$iI4}d^Fdo@_{Zjtt?@?HmXnq94BQ{5T68vD>rdPhnTlVm`oT(U2b+1hHYgd2D z{o(_Q_3E3qlX<#I@m%Yx&-jdc?v(BWi^8P&u$vDz(bx;Ye(=AxI5bkb&QpMC0WF3UdnJIxe&jYtU%|uB ze`uX=KuQQ<1nAmp60#TqiXgzdK~{(vM1j&up%{=5b4T83fuuTl$}rkv$74oVm;~5S zS`^}D*cyFgu6BOR*zzaWZ(8m@aEsP9baaMk=+yHbQZn}Wvubp1#O&&=|J%`KOHRMV zOG{ruyURIy$K~;zl&#MPs*Js`t`NvB8#3#3`%~H>vriyR$nDyxa(jL52q%f=HMuh} zr|{{(q_=-f(xrmm(I4v_TOnk*2!1A*LwU5{%aUo3e1`@rV{8|PmOGNilQ z+aK<~JgO+SwfYf0Gg)F+$gBg=Tl2ildgnebe4F7t{5|M|;*7-v*J?m)dDs>~}XgU8WhOT;J6<{`sZLYc(!6@tk+#X?kO%#WC|2W-MU-ezoMU zuv`L55z8Dx1Ug?6R)q+P2;<6B=p&5x75QR+@fZ8d*saX+#JQY)YDgap$*vMmz ztI#LKe8y=$*!z;SFyMBQ2;pcVU;#`c!9)aHK;1FWMS|L@7XUe^!m3Z884klU`9N<* zUW0sxHlM_F7~43tC)sY`S(q4Z4H9Y2PYFC$b%L-Gj840d`i`?_8tz_HI3U{_I9;J# ziGEaAK5=Q4?%qoS(sOudl*>49kBV50OXntP!OF6Ep+qN<|hJ0Y~4CnQCFdz zb)S&D=nf`C{j(zPUekvP0-M_6CkBB3&$xonPp;@QVO60?Q_ z=73WAV8!dLl_N0++>PqpG(M-xn6vpXz{8fO$a1uTh;`{Ccru4NwHD!y!Xv2XERXyB zbob{@jC6F_o*l{0Q=?GMIxMc+A+FGeX0X@*Kx&mEL!r(&A)v@0Nx{oX#Iy#% zW42Xl53?WeD?M`;s|><1#)j|fDdw^bI^`0Y#fy;fVTvBmPLMbS>cisxIEgTj05~Z| z2Ni@^6f8l^7uXmgo+Ks!o-_$s!vBOF6nVG=&`&b{$#N-~*UgBFo{KINaVJR0r&07Y z$E9zam4A&4Or{qMCQyg*1x}!_5fop40u>P=L+LjT$?f(A)S&`vH7lcL!o7wJ)omYQ z3hAR(xpGv}#ibI2W!Vr+e{_r5sq~h^2e)45Y>z0*Y1$&Rf5+y@*`MB6kB@Zn77Le* z8uENZ;W07e5)`BoN?Wn)Wn8AJbTiOJHiETw8i9`Y(xnpjZj$8Knu5SAR<`DVVme1tokKegpwA%q?(5 zr_~S}3-c4#K){GZu^LMU{M>(5(<8sckhXse77oE-Jn3YPL{=5=SdyU_kk37nNISn%~ zHwT_>{VY2*Uc3-~a434jpJC-$XP5Wt`{Nf*Pifmamz?4orZKFRZqBwoFy+QH`m^f@ zL6*C`)q2B!YIEh8`ns;Nab>%#NbpSko|xuiYab$=x|!j-D$@M8KK9&PNqFc&v@&K` z!+LR|SgLsFZa(Y9{LQx=$+b296CXz&GvE7d9(b==|02X`3l!URtCVIi@c>06gyq(S38sOLSHDB z8S4P@D9FqMHc}Q!hms-}LT$Y$+(ta%s{y-9A~q*w4mhftvBQua3~h#>PGCFufXXJ* z2y~2^V3F9XQ1R8tT}(Kw@UvGvh zPZ7CMyRZ7djH7z@b5m6b2y*vAYkucvei+U6lDx88!Ln@*8&^{bGT0AxD7xG&LGbWq z>L9mxVim=kp`1zv|U@>@>@QdoNtH#>2&sGzzo${h3^*W|uwOx`%Y@Xt@T;u$@TeL7yg zTWeQpeR@@gZ?^?WW}{_0fMrTG7*!*^0wpn!4j3ga(W82;I|=Xk5R2y{|Fsh%1h%P2 z%=}Rkv^a=MA&1K$QQr~PK_XYe_W&$~c^%5=`_zSE2jmSSEgz@ErGDf{4X8qZFq~c# z`yh6L#F^}n=RYqT@k2fr=a>)Ey+eSvZ@(dIfrx+79$ImsUk6@(?P|U9rhLyjU|Hl& zkJ!dc@|cY#khJ493NjK;{?Rntb^C;C_poevpDJl%Jb!@SZ?y@akW#x!DW7@JG!K;% z;vH68rR{628xbIZaD{?(PCsq0qB|&{&S}CGj49$UdaOFAD3LsmN4=}Au2&CLuZ+K0 zmd35D=1W>&=@C(3OZGn5+9UTxf>!yn2+k^lU3(jEB;{LD>DO}EhzAtzs9U2FcBO@K zmZo6P+nw`4#UveUfnzAOd%pzjbFfJ3esdGW<+*H;Y?&hLzeQj++|lFv|K&4<+eU3v zE?Ds3^2oc#hjYMjRj{>`-IKJpMIHi7adq{qK$idP{69P)-GMGMd&J! z>PI<3mWJAsHY_{M5t>-(CCavMA8XNXO}_8}b?&CQJNI9cx0OEYM)_%OTlK*qAz+ej z{hXm4t9=Za+5(sQ3Owv($l6Ggvfw+P&u?8#A2Oe%x#8Mjj_tH}tM(i_;qt~NDA$*2 zOK&`HIO}t3M&7ibqt561SFD^W5j}u8T|7rLg(JrLcn!YqGhB`5stK9^!*PoK=BU|OeYEk2;hj&XV}2eDqtK&A{uJaM@k3@ zNU|Y7vl&Tipbs=Y!&6_`$NR_VD4Q0r`ofp7j6&-IiyaTu^R!gD13?Q@miNk4!uz4)Ws6He&33=yw{?<~wuvZp{Y_x?ot&7?{Cds1c8YS4`UoyyIU&Zv52cihbB z_L1Ca^YK-521@1Y-zusRH2^Q!$F+guV4f61HyOdSD6FAd2#g=w*4LdX1JuDHB6*v3 zh&t7=(=+h=wC{K zO%E4VD|eI=G7+D3R=HtOfye<~laD^b5nZ*vZ}c8esaGpVY;awAMY7dh9b^}Y`w*bz z@bI<3Vy7@z1TEE!vx22T8B<1QRh1|!DFf!l^DBxAIbXaVymFJ1&}cJDH`i>~GHpH* ztd!~L)g-35?MA;!MoTMKjt`iB#wsmdvXHhs9^czxcsi6XA-(9^w->;9OFSz0{jS!p zG!Gm_sJ&>#;R4f}v`U;W8((vfIJV{c=KNOz0NOJlz^!G;i`7Jv86v0YfX_m>(Sd5|E>t-vevS8Bu{x^ z6;{J^UjU3$ucIV7l-(c3YZBz+p<4dg=L@!3Ki~?1M+}yREZfh=*@J*3>b@bt^sgaQ zDZ~o9HmpT6oM!myVgP6V9+u?x%rRL$l-#5w6I^1BBXrm!yOTnVf_LBEK%f9T$%pHX z`Ch^1=-RFxN_?%;apT1$WYgq2(Q%_XuD^i`1(>P!Vh-J}082gyk!I3z{t`K;18z+c zp?mB3^BmkS1Ux?LP{1kyI!b3r*i6!Rfc1X_q~b}olHyP}j}L}I@W+e~oDj!6CV^^H zSO;LJ#~?X>MP`IeZ?s9n9!%_y=4XvIg*+ZdMPr-;9=I;!!F9p4hVNGFuH=`WeRuRL%} zv0JlOmn>K(f}oI1Hq}#lmsx*G^y@1xL8R{<+Y2T9*+>BXnC*zhZ>7oy&T#g+1gTPX^;+4R1L2U5*VL%f-`%IW(Q$wzcU3%eVM&zJYgr$CV*cZErJQ%2R8L z1ll6Gh(e^70{p|MQ136P;<5K}EMmH9+$v-hY>y2b!fZ*@IDu`gxdi<8LaB*iz?4W# z4zMMJxbdJ(TwrEg1H4&$=Kq%7sG{2kc&JE%IW9+=Zh<JJS~6l8 zsLp|A2V#DxTcjSa*&tZ~@JNmp#cE&>ia-nML`?{WZ_sXd8fy^bcSfPw5~fJn$@3XL zC>BS-Gzy~R(f85=s^)0PQ+B>hgf)-b>8Ig)^V| zUuoMLL&*xfZ|dxMD#NoAUNu(BPA#;I&p4`g>G1Fs_-Dks?tPCLWZGlSHLY0dZf>5L zbk%+Qqy?@w2Z18$K9R7-l}Ng8D1ZW5cDNE~QeHq4h!+P&60n>=+YGSWfTO= zvF=?0n3Gz5m0+EW(Fu{Jz);#f`YegJ<}Ten&FBL>07g~RFqtIc3uZ60TSV?>@ycE z6G`1SSI~N`KM~!?u10qxeOHL284-A>=%!Hd4D?(Ql?MYcJXp>!8r%Kk*y5gTNqL|4 zHt>~8XDkwh?%rh^KpRkxP(K9H?A@c&6Ut)Q$fnjEKMqB_zV{m|Hj(hUslzv9;rc6d zY^;5@3AXYj>^u|SFGj1A=5PM3l4_9^h}zc2d<9FTF^-+eEi1#|B~Pw;B()!#z9+3c zzUGi(%%gU)52(LJ-q+&1%6`vTVkLDxAQSesm8UP=8nte!aqLLg!w0XQYUX(h`+Hvh zFuS-q&i>sISZ`Ljx5zzepO)LwQpbSNuAd==&#H#HQwVuswT;&H2NG}D_Z;?W+}m_@ zNzcneX}`C}h3I`u7T^tA2Va+!qU43gK%@oH*$ z9~W2-4zg&qh=al6&*tl6(znVQ1bwUvz8QYkx=I9+!}vAif2IN)2(a7@oMIlL!$K79 z36d^_go2!Tg9s8#0n;L(suye|@NbASp*76LXyI}{l28FL6y^!QP62{8+91GdzPMw` z)f`9xP-?(6NwH}687Emn5vjCmwQ^#>i44ItL>Y8qqFidOEB>7KE9@d_JH)*E?p_;i zzg)>w_E_=f&V|CLxl;`FRqS0)FI9Vd?Sofh@!0?`>UtBwYu^Z|XJqE`vp54L7D}V^ zHLD4@1rv|T9y3r(OAfl8N5aFTx2 zC@Y9RaeX*<@Y}4_4-QAxbsyRhAE-M*U~4Sjq&U7wO+`Mj+ke+2*w`4`CrfDDQ~Uim zdHBPZdwsv2o(Z+zb=55oAR9oEL>|GiM7`@I_5)@Yn1iGUR7kA*Ab`0F36Ufyip17V zKZ07JazOSRGQRZlGavn7 zpn?~U%AMvgoq%2vO3`&Y?^Xdk7W>)dg#rStXCU{O`Q^XlOwm2y%-h6Mx|y;d^-Epf z6t{Nr#e8{~)<#=;2&z%Iy6d#HQ1Fg@zl`zgl}~SS)fJK@SHGjCIC(_1uHuzieJOVE z$J~{Ps;})1mh4OChWX%>-1Q%*5MV)xrYv2dU-V_lV}i}Zm%Y9d8Bd&M&cW-;9`GT< zIn;}w&zChfL4mLSpI&&(etVFX+W0$&GE;a1X^XREf98DUkA%`b_ZJlKF;F zu-W|cq!mb$p!8?Wr3)lR4`0`Nj>k;tb#|A0GAZL_XSihDA}P1u;ftMzV zo5lmp9zXz6_Wv(qpkYN4_=zOMT$Z_%0W<&4T7OK&YE21doEgAgjJlhpkgg(tG)k5Q^w2y#()3d}z696q97z9Xr0jepY-d`08TKMAhM9@?PQ(nH! zs@(FC(mGz??$~m?$*;Hi!QJ#>-!j3RopZv`@gB*`sXw(b5ZT1NDkA=|CFN9d>kuZ;Uhl)JF@D`v9gyVu;3$`eXQ5-#*4R(p~FF~S)z-DU&&3z(Ouj!uLC zsCDX&uj2~nbZqba`js~lmxJe;*lt2r(epxjO<&3h=yl)MYdZgPxub}qH;A0W{|I3< z^XO~U-V-ACZKX?-5)8ev8l{$;uXx6aGhKZ6+|V5T=|5I3sJ;3y;If0;hA}^z)eEzK zmu9VZ{c=|3Tx!uLj~Opl*U5jyUT@K&vbF-|0xXU`C!^(xfSfyP9`laFXTTiBZJJAF z=#wTVqQVP`V@k7Sn@h3&WApqa{!BZOyyv=&LW$8Ug~_*P#z_)obBm(&l5{ooJ{5{= zFK$^o@AXXKm5nb0`;AV&eW|mKEhl*oU-VLoE)x$P*OCYcr@x01%O-!TJQ7?!Ow!Gm zH}_9_bz_+&YCHF7mtVA2M!k+#_2^qua8 zq?Ddvvr;G>9YzyD7toNVkpru4WingMC_Cw_x%44*!;{YPGxxe2G{v5lTw0UgdZEY0 zItx|pX31R|^w=1lH3?u(Lp3Kv21dslzp>-6lQN$rES)x7`4mg4f3mEe^JskOZr+|U z*8ye6EUg|Jia)Dv-5s(mXCmrgbdnw#N@z`KeSCXhW1Lg@4D%bF;1_&YUwwO2mqN$3 z;Xm*CRkrr=G!xJNusyp-`zTa{L&h(T)vGgaQTj5?<%QkZ+!3PnJ&8BM>LD#Bse=cJ z_2g4$@=^J${nwW9v5zJ(zhYv^-z-f0j|I!#9)OCMNDuP`5qn17j3GUVp_kjNttN!+b=}VNg z&zJ@Y0UwSsP&^;UcK8~gxo3DmvgL~)PGA`yj?|Pt8(%T3TEJdaX9872yM6F;)f-lq zn-qrRz0qKHRi2hKc~a*8EjY?eF)KQskI^$1X4|`|PWfI9z|EH_5CCRM&bvrqqf@x2 zuOTBkS_0T0@=t=jIdD>dg9gwS(FIDJ1~JaVh8AKL!ler&3Ku98EPV=izj&<#osD=8 z$fCTNk47MK3CaQpoFM`>AJGf+6P@C(6eTs_pBR~_5_#hz!*y+Q{JhdK_?7FniM23Y z^=^Y{bGl?Xkd&9efO^2tuza3klZxa5OG@VG6T{Rq1}j5GG0DF#<5#R6XYapywhtu7 zp424@rOTODZI(5r9ojGusbt6$p!K&(*x~XeRfbMXQJ|X$^oC*LLW7E%D_iX@U+1b7 z@qQvwd3lPq`d`vuW>5p*1y_deD$}-EuKx1i<-B=^Psi`=xz-*@JL_DFLD1qqXI(31 z!th^=OEG%UjZ9m=EG9u>9iIY)KmEo-TfUl72CIM-&R>@={L=Ts_QMPkb_tC_L&9bi zKOhV|_1mNjvCcv*;G#Vijc*BwVm0Nu*W7HI+%F}>e40iimke$+u%5hgPM-H}5Y1J% zrNE{wo!b9O`cxvb&Ut9~{7xyCVNB{y@vhcGb<;l{Pbk+ru8_PPq;*rK7OSo+o^9ml zY=eayNR>!XS-gKs$UJE@^PO|@fbl1hp4s|L;cd(8*uZAR%Fm1z?X%dk4uLe?Xrdd9 zOqWg$z^=LnEmBu1ZTF>otS!qRV2L492q^ab_BzWvtGsu!k>g*P+8FW@OP}^>vEaSd zakJEicGw^LZ3-)5A6a~x!P-KC+?)f4oAf?abc^P6ynN-XD*r<6 z(sJ$l7v?l|ocxn{A?f_D^wOB;uC1rk$|^tg`nW|tZIDqi7aif@_kX=@$F977N1#YW zYgtTU3kdwbZX0(hjDf(72a>D(r?c_eBR(SY6?9bE+yyo$OVw~wUNdNcn z^=T73ffFFi|z&JFK~6YHNY>dB^ugkO+A9Y7L1(K2|GanV6U6fK1>{ zc*?L*qtxpxq=l%mbR_(sIUJdJjOVU!mLwu-0FRjQ>jvE<-i_WOqMv#LkiX04!}hT| znHwRtQSlCF2qoWqTYEfeXK!U&q7Bfe3@C15mzl$v?SBWy_a1rKxApis8Wc2cjR(zt z9&bq**#)2h5yj5Y0K|BsM*6CKfvvL$-k1n$*4g=;zIxXXCiMD)Rvd(0tjS~IAU$UG7S^S*6}J%UxquX-pl zr98OTyFG(y&-bKA==jqeZP;3zTvN0-_sX%TvLc}!h3Szi+zrrz4_~B0I*-f&k@Rcx zobbAZg(VY3kZY;jW%HQ~{gWg@&$06*Pwou&oo-COz$L88loUuznk#kP?3lhMNKISU zK*~BVG}K?TH>PiJwdqp|i?CW$n5u8&>n_|BlXxCLqAajVc@&$rpaD_B1)Iji@+at{ z6(}U?hk{x$jYaaq4`gq!ltgj{3AEop`vKgupt`6ZXx@Y3c!}!enxR0S0Hj^A5kiiF z+*1Oy5cL`a+6SR{?|Of*Ido7ER1!|pM{Sn(2LcD#v}@SEV;ZoM4XRT6os-lg?=>SF z@s)4Cv%c%0ciO6;jQ7V>tiGZB$0yz)Z@syHiyM68N{o%cXSd0B+gl%gzH;Z;oce8$1hLz5*(PgTtX%XY49(nc%Yn74JIhIrLy z`D|)-;i%GLyRSUsZZ)}Dh8yqeS%D27gYp6rdLteieFabz66L!?u_bbr=F<U7PoKuMQuRrbM}2wc2CP^@UiL-rWl%Ozw+4bO-3JJQUl7 z05T^J2G;^lSPPOrc4ZjQ#)amq+7bLrFx-(*59l0m`o&6Lxn~NxiF-Cz<5G^x>o1Ac zLZa2Eys`6W7l~|vZ$0MKiJ;R!dDa@;j{}FEFT|v?U`cM~P}v~*TBGrHXgjw2mw zoIx;?d$62H5~h3x$>XGW37-X|KO%sEat)we+k%Yb@gc<_xb!8|+2vodeD>x3AJ$#3 zhO5h$&)4olkJ~78-jKvfC4&rnI7$K{Ens+Y^8q_sw1~m|M%k`ehh|S* zGQj{)f8RKA;Mkww=}QytINDupeGn=;HTZaz?eDkwmR$$KpL-_wXJ5ZJ{Mk5D>C*GA z$V9)Bt(W^h`Dee6jI}JY*6TU&)broFe|--h{QGp}-K@|}7e4LVb)&M?=H>gKmYecx zF|Pg9!^fI#CY(~L{`dXQPgrATZ2S4c&zYLO-g_XXHf_g?c{{&cHu$q4um5W0=)X%X zuS&+Jo{7aIN1K3F#JGf?CE z6=}~qL*M0l>^{ct4qV>vG(xQiEIE6!@%%I?ORKZDe`pUoo*$@vn3WSfKJs6FFz3ilexWGL&uJzm{*zoa}O zLXilFU%o|q+1D*y%9Q=)v!|COzx@hwP`IQbk#!iN*_eZZIcS{d)42P1kA=>cz#r*N z4@}e^*yXM={UKP%8dp!>*V|OXiB(TmrLLB;P7SQo*u3ylhOW`kl1tndcAUDB!s3+u z!Sj;f%%q_=Pex^P>xYuRFWU}0SvddS4HHev_4$Vb?Dpn7Y|D20^N2RuIb&w+lpDi| zN49LND@an?yy@jm#ni!DOD1&mW?r^__?9P`Vz}n%+XC9pGillVVJz=&bALHW*9F~t z*FI}{U7=jn@7CW}J+6HT`nv8*;J+>M$gkXz9uRZ6-zcw|W<&cVfNm&#%#b ze&#=HQ<#r-r%_)sOt%;QS@%Bw{%nJwDWyM~_I};B==_5@78;6Y%RW>q(o8BiTyRKAxMY?i54Q{8Z3-=Z(=^kYg&av>7!&DO1tjplbRyXF1K?8T2&;eYdM zvSj@g48WZa?{Y>7V^ui^`QbzQNel`PT#d?W?Ez^t|AjV&Sfz zcN#Bh99|Q^ax%Or6=bX=stVrs*483@LE1?!XA$#xSyJMPBEn<(%*3jhL$f`fg{>tk)E`>0{&1TOo(r&ey3NC|=X<*PS$C^Mzi~k;i_o7rNV`Fq*$i zUwx@oZrTl_DW~sgk67uLSB0ORRs40Wr}pQvs33`bqhHK^|I*%Y?;`E2d%ffS&$D|w zLgbI@&ULjrVrsFr=>0eEzTH+E=1=qn7uQ{1uk`G;r_j1GUh@1(zsdbZC+gpZZ>2}7 zjwq~Jy6Yh;BH#K-2k+>?C;K*TIv9{H@$|wXxy&EK#i!?H7WK}{vRy2<^#1!9p&okE z&jcqoeSEK9KbD?+UYn);!I^w}Q^rRj&g`2+=-TJUN^_2vozGj~*59|h7FU!Aezulv zb^VB${pVi)98Kc4Z%BLeoNW#VdVfY9t(>*+n2l?OS9`tAr+~&PwcA4*ay@#R_2%SB z+`b@_&AfIv;$uqM!O9S}UFxNPVd<1kMQLdzB}800M5J39X+avPC8eZA zP`X19qy^!>zxSPw^Wj|AT<4i*&U4Ql`AJLSgFEF@cLSss8HlRaa*Rk~aT3PLy&y#; zu*lsQ*X#y9b$16&&xfKDY&g~8D=z1jC^g=kAB&gNT0M5RK~C#668-^0U*4MS*7*7g zlcg7VD~&dW)_uIe3ne76`hYrDeUuILyORGtr5d7BH$6L-PDp(n74VEPiamCUQLBW{ zQ)#&xd6fsF&awEmb7z3L#s`7l_}_j|hqe-lkz`3QD=T2S;dGclK- znR}2W3-j@5?{?Q>@kUHEp^fu6nq9^E>6=9DF#1=iiebUA?4hzO@E3`9a>N#c=58YBtYAleW`r0^0d1^HNV--cegDyc1Yn7 zs*hJnW0BrF!xR&CdxnQBi`rClMHX&CKxgFk6&CqJ#!mr>uOFFD1HPnwk(~4zP81@fAvkwOv)12YdZneA`S=esnY5#9}Rz2rlVwJo`O7B<)yOP`(~>oI$5wnQ1MT&fzL+N45Z|%Wca(z^%tCMBB=u&Udwk-;4XvBIpDB!N#NJM9vV)`DS%~xp1xk37< z7bnT4FqVb45#nfGC_Y}ZJ>80C&&}{}YFCFQo$E?*?6T74It{?*AfLva>@IUvG87<7 zLw`sz!-4hC@w78xBL!JvF)<1i?EDB2sr?{LAV31-f>@%)fAXMc)P_BwFDQeZ`w5XP zzv8q8SOf6E0BHQ4SS@Yf84`5z1HF{bk^*r8NFICQFc1OC@+T--dqLv^af{ws49}7!XGURd5l6CbY~JQP~4P3=c;L8mw&s_gDwOv zp%if6)7Y4jQg{MQ7<`nmhs@J^H=hIpKtB1zy%ByG*E9E27CSt06G7&m z8Dpo!p&cxA^(>U=D|(a@$Ss+0D_~o;v1jK;B6w$9L%We~v=N9NkZIVau>L`UC_Ng+ zXCo(jh^ANl3lavDo7tv{`p;cMXxLT9C{};1jUZb}Rk2$K{@QBXQe_ql0hEr%i zN|pV7pn)w|Q7sMYQ#PpaOz>WjR$s`r>sPm45rDbX8eH35aNk9|poIjBj(`@m@LoA$ zz|5p9K}px#L87~eBPuE5a=f@3$x-|@-{`h;Pi5lks13}w39wR{h8USIdi%`ZY6$UFae05?C z8Z37rwZ;d^H&}s}YZy*ww|E9RL;JYeR{c|uKY-9O>B4;L)6M}74@wB-`+_oI!$-F zSpLPn6LxFqFXeYIiC{D$><*yCaJNDhN^p~L9EbtPmV&X%4*oxE&43?x5Yp8$t-T3E zjx$T=tMe$$u{vh6PVS&v89<`&d1V>1Tmzrwk-_SjR?E13Pf~tHj0#wY;pk2D0@7!;Up83>~l*E;QfguA`t~h zflkZ!bN0J;g8)f|R|;5qQ@7K*KMP`p^;kkm1OBTZ0LAS@4b*+OpFJYVviJ_>!WB&| z3$M)L_SM7+G&ko!$7nbqs)}8x9xg|Mrp5F+ritwthEW2f6HF5{=WK23nPD>T;r)H# zs5q|vZg+jG4H!~C9uaU%2p0CxjZ}ogF#e!?ki8Q)fmEE?8fa#>e}e)=$Ag>%sNjQ$ z5-`y_Aqu<*Mtz~nC;668$Q*QarZp*)2iiRgCdBxMqklo8%_sVjXF&>^8#gYJCmhSrriE%~ z5Gl9BY7+vU;NSZ}9(prYaHgDT0yMy=_n2Gx9nlZTKCCDM!9s9<)W-qe0)T!$Mhv_# z^)Ml0&1J*jYEq%_Y;oW67;E@P{?S9Y6YYb)_$6_YKGWh>`n+lvAcU3wya+9R3Lh0d z0vbp$I*PaeLr8imkC5yhQ>IoG{w~#BK2QdjGNI)uF*Crod|5jvNZbOV5v2}*zG;9A z(RuIYp5R!2@B*2iYfHVlwGh%?eVHr9k&knPkbtBML

a1iKe$jjHI-eh0byEC)QE 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 z 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

wX$y_KfSSRz1 z3wjCXtxtUs-LYI+R-(q2l7Vqy?6I-cP4fN9_fwTPmD0Q=v7dY{j(i*m~j@BUQOfRe?U znHZC&i&lv;tnX$(f0nu*lGP;@?B)dZjKkRu4k)xPCXu*VTg>1?PjR;?xZE zz$cy~FGR>0Z1p&H75^8SREm?$J+wOOQPAFA;5UKqUZ#KAR_}ooFl}GwtCEMN zQg71%x(^3(F0Zrj!E8X(c}Rn!3|7|7574Gjo=LR|3DWv3c~)Oj2(BqY{}8Vk(|k+BE@J3 z2Z+8dAh{>L^_yuMA`Aehb%Tt9j+Qu}EHCDQIGDuae;$BhV9uBv33>T&sHGn5dGM0G zJX6G|z!|OQY=T~GMafyD3dHRJA}@LXvV+>2?@&@&F!tnE0-T4Wr<+x^lyMRVdb&je zs`|B8=_67&%*yIzJ9pkmfQt0u{l0PJ$R-PQmTA2cweI z$DB{3X0;nKG;;&wUFLCys3`Jj*U56`9lSx1F)?@ zsfGqT4CP}b{d6~!K8g(+o-w8KDA-9`eH{FvQ}SrS(6>0rZoRy!FCkRnxbA`*vFGEd037%P%#la|Z!@R) z?$%a5`J24|3X>fMQ}j#{1^1=7K1Fk|HuL%IT?;bT9~awMS$U*xdCJ|#%yy>}3Apd6 zRU@A|G*x~rT#$52wx-DY1v?$5e8wLo#@lYZ%b|IzQ-$=5yoWRg#8{CLIU>tPf{1`& zajQmWJr2FT9Fxs#U(6INtE>HwG=c*MVirdT$T)+y$OlIZ2e{jo;MenDt+aW0WQX2S z=lw04&rT`5h6GW|VosBJ7CxQ0j{vVSJ@Fl?C?pjuf~5v@CkfcU4qalvm-9sl*g(q@ zhbawu0I>v0!EmE21occHp7nE7pqca$BgKsA8E zZyu$n^b{s)JYaOj;YA@?I5HX7 z1zH?v7K^jQ)YkwNyEo?Dax@-65=!P?TkaEn88a1(9L#vqyHmFfP1+ASHNjX$!!{Y8MB0R&`S9NXSKGYswRtRu z`pfXWWaQa)Cu9!!n5YOPFb6r@fQ)N6fqRmJz8VHQz(KfnvB&g;e&oGkoU-JFP%$$$ zsIdQ79zc?Rtztb^Vxc?vv^O^$7buW2;MG*qK#nqGZza%~>^8JjCbWQ5JGZPl5{ljE zxKsn#*@jCKqAV&V_}C(qk_VJ<(9a5)KMW$_>0$rdR`N|aU=eRJU!sPThaQQ$iQvDU z!8X{t>Jbm75gB&?4EFL6B+fozSe6*J!S_fzPxvJhyBE!8HGp;T3JFLyzDOGf{)7WT zsTf;Y3G9-5rR#1oX1noC#9w?WI+AVQ$@sgPaDDBMuZQxB@Gufl3()<$0ATr&^m#pA zd`qNgPPWt+JUw9*xrce-Z1&aRV_ZrV9U+2Em`Q7p&$yEukU3PWOUUA4Q!3^RZZx}# z!UgCC4#{^91}Kp=WKaFKAYh{~n+42?!r?bngmdjNK3#L0ZVhEtf?Q};;S^Bw<^q!H z^Hp=WDo3-|dv=WJt)l6Gd1N5*aJkT;-Snrjn>3R{%9|yjRGLsV1>6N(%uSpvYp_EP zQ#H_@(#&SWDDWBRIg;4ejY!@UpN%S-(s_mri-}2hA%m6OqrDKksw%zxv{OZZOo(Yx zofnuFa~4FRy-o~YHQbWIRRO08QDp-MKkpriIt)NJs$RIB?l&w)%&0Vu;A8Rf0hirBjE*Z+sejz^9f9M7)Ve*lWH+JbhWv z4y_Uxz>B8VfjDw(9P?o2bMJ1?v~T4ePNDRDxi?h*1?gN%{mMPb>nMN+Og+mEz6a#-%jD68R$`JZaw8_k!fj|9c%kw|QvDRrH4{o|lzJJ>equ zT`D!-d8Ti2=118eT|jETXx8E|lQ5hWie!^Ozk#*#BnOE5D8pMMC zOwnz9PLo#Y1<3>^$bTgB#kV1m%zHFci|I+68wSWPhiA$w5wRiVg(TA91GK5nKi$ZmN1+fSS2%AK?M(tj-y<%*+s<>T zC}>W3@|dU?+8co9M?FPtN!wHig_VU~l2~aRwrsm9^P)m^4AWpHchJD#WP(Y(j_(nt z@b`=2>xWgK&>czSl6b3$QtdU8-k@0I#<7YvxEiiD}t-=<~X{=lD5~%&f zgacoaFZZ=EVst&RfqW!f zYmHPn(v*q<;Bq85Sr=%%)FF;ElAtQBtEtezYB1C7QjvJ>B#T3lpLjO#LtrBRzZF{s5tn)_~H~&h8-v0j$mJ3VzMd60CN&ZjFQp35ac*O>wSR+cGj6q zGdJ8(bQ0*cNa_lYUmY{K5QBsG&?!1{u_Je2`Ia>#*@?~5vI&laAQi}qU5)C1MJEwP zjmm7zEQtte^B_&$5{Yfio=|R=QK&)-qF$YJNUA{#q5G&xa7XimgLB?6G1v+G%M&oj ziC|-Z&Vi`Z9|EzW?O*D1_eUx)1LpI&NTCZ@4y*Xb$LJHQ?Z&F2P%u3L_UPK<6RD{p ztQ`K$@ih&kL!6s{ZV*udP%OF-_~D2+2cX~cP>jJlv3plxvVf}Jiki+2T(0aH98u4_txc@mA@EFP<_jg@ z9vQCWs5?MEV{%D{0bNu2?d{r>68C{C$cv;q2L7PY8B8O=5Xxg+` zP}PNmlg;GXtr7{WZ`wn^*u*z(r)BOuYvkl7=?9glfdRs&a-=8X-RUlw;X(hk}5oB*A3RyPM1_OW+ zr73VgDpe&y2@l^0IZH{ZK28o2fMy_OvV#KVII_h#0`bG-p_O`1n0XHG;jg^1Ny zFKB*upICghpXZmQ7bZ>`R{0I&@}N$F*W_>zm74;X>*GQk!ka?$ext^~h8cA@f#khv z4bzv48)nr=L9I<1I64Ms1THCmkyK@h`CnfP`|bEbCRYtx%0=0%DWDOmCQJq54w)wh zWN#904}F^DK{+8~%3>L@ZP^xt#H0(3c4^E-?8#h2=$+psmAU^KMh|M@1B2o^=Wt99 zi}=+1^eE~mp9)kSb4fdzS1bcJTlwbvKx0y^E>~qq+&7N6Odilo%~BW~>wDx#4`u#} zrz~46*8gDjZ7rQ{0*LE3NI6I&VOTN`FH*#u22*90U}XB*!XSkjTuVP8?>>PXC1jlW z+V~uja9ZgcUTcNy=w;5bLg!VL6s|N_m?w3Oo-jnOB*QUCBC-cmJIKE39OkFUZdQ7R zw+aEex(w0ET<}^SW#I=Aw=N_oTnzYPqhamTP0@8E?9?)g_BnvMC_BMXDXB9{>8*q< z@Cj~Fz*WT>6K^DIX%h-D^JAf${E6Dookm!8`OOP#0qE={Uu-(%h(D%?F59D!^#poEDGt3ByoZp0)Z|0|7`Jpa9q0V3y3i%r8Rz6e$5>{y3k3 zzdLDd-DLOrb#tt&05*gFLE_HFVkmQ3AtIo&`If=v3E9bBVSv5PnRO8l3$?y;f- ziO#Ke0*X^0xTT&W@<5Ssv}y#Pd$IC~{{{rIPG;DD5IZQ;`U#N@#vqcIp&5C|3!V90 zlK&(XAp~%kL_a3MoT$~)GIsJzH?^y~9+I3vyjTp>gl93L!X79RV52wKFyi2+1m|j%_?nIES-UwNcHa%JxCDAs8hs@ne1xNNpf{ z=u8(l@J`xnbl!Rd6!#;Cf^tCwWCt2_$nNl6{%-DFSo`?mk%J5Tw$0+g)RA)-K$sjj zJ`xZ^uSPH@fKm0Vxq7F^WmT5rdbxDlAF^v#Ij62ShyDrU*Yp{DtwXG9=d3Js^k^6(fV=xF5LqAW^^v*a7e zc;ST@XYOAEr2M}Yr;bD&=5@uLa8K8&w6b+GIU-uGC`gFK3FcaoL z(ZNTg`ExA z25q1d+{_bB2o2~??h5;$M<}puo z>KJ+P8KZF_2%xh;e+`h6S{d*bNA9OD2b?vu0n!;~5Ql->dHX+%zxe55KSY47d63)F ztrumi!UXuvZZ`+0vpHo*BGHs~iir^b0pc7SHW|g#)Z@c!_1BJg1Tev6DOx4~Z3eTl zMve zyw-XKhfP@oWjhD`R~RtE3qio9^n7B51`X7bq^<>RF<55oVajTFwFGAjZb8V_=9?IZ z0@=^z$kJONgG>M&g|ve?Kw=f7Vz|f;pvT2C^M@@y5OEUy?Ho8VE@V_;fPzzYl|c^X z@5zhgc`F)}!s4BFWY@alKs;M2)?A5Y5Wz*(Pwm)u!SZ~h8P0qm=wiZWsDi*JSVYm4 zg00kW%{4fTT?GHQ>ZsE2J?KW}<;K?cVTzn2J3df~n5^A*!pVcas0V|=t^vw(97!|^ z*_#6xR!POrR`v3crGOs(RR8>*&$@~@D8emW{>%cXB;P~Ymt;Vx;K<_~-Xm|)07By+ z0(l2;h=OdVsJ0nV(C=D8OVTgD!dAfY+WR zUmZgFmUtG~jRyS1w#h9&?O$o)z@g^dlYQoR-3$NtR2>WKLQYiB>C;N|=NT0Cj$p{@ zPXOdN1{UJ*d)eA9i&m2Me3+qpVJ&DS@;xGj;PV-#4D5t-KT z)zVziOL@ln7vJ&78owt8^Kpq9o^sMq?-WW~)5_c_Ex;h-4WwE-6EIiNFb$ z9GzC1^|=fq5?3wr23?^{0dr5-@WF_=48Ug0A1jh5;$y?EXpXI?@GU~mD#3L{Nk z`m`QHw0i=~=ldSQFgP8OQ#Sh{*cCGB`)_ZgJOKi5fFvH2@D_JmVn7U`-AZIJq@Fwv z+h=WixdI?35A%TqP{2w3=w|`N*;gVPOP!=XG^YpcXAYZ4xjw@Oo!J?SVf^ssv4v;~ zK5+{Y&2z@v`B_76c17|mHw^9Qk#@mJ}s1iw_v=L}11Y8QEVc#Ysp%kz6OId+Epf5Q%> zN&D^l+5K>dZ-2cu`oXY@@a0(e1WTwF0 zNLEe=kSU{?aan=`O#1}LueYltoo1#o zz?u(%bXjP82iogyf(Za#mYg6&)GmttyM5&g;QW@O?Z9$#cwd_JhQ-pI6eB_qe(BB= zbDT;D1#DuW*7<4uDpjCf^C?dps3HDv0gbx3Twzv;S~PVqq z`695Ls={=to!4Ya*x>30*)G)CtZ3KQ4UU@m(Ky1_%h2B5YiUvc5&dR&vaW6@Kd_*{ z^M*AY0O5rP4=(0AjjwkoR19lUl`MHMzPxkM{^CPL^U|Wmer;;>DE_v-i~U8lvt!h{ z)qZxU+S`?}E7PC-`1RFaew)2v<>-&ssg;{v?4EXw3&OA4zbd|H5FYotet~?MSnFXW z+rOv3k90?-U%XkoI7;v8*{Kzy0c-v`{YuMu`Q!e*szV`V`&TE9e1GkF_UdBwns1Xj z=4mzycRyj`Hm4qrd|lHtuy*rLUiOpNWPK^pyR=K;fi)@byk(vnZaw;1@#mQ_Y-zYf z>ZUut)?{)Oh96~FsyVOOeErbwmtA$GC7f`RpFaZTk?8Ibg1n%Fr+j*`N2#7If-TzEZkisxcTJ&# zy+!S=hn(;%SoE3sH!SYA&sD03T)EQQEcKgiqSB;`d-Q0+8YRa=x91pt?-{vqZeT01 z>RzR0&f`Ss1F=uVUrnvu*1BwB^yZe+V*TRh(u#ZUC(r%Cmk6+XY%%h?bocb-w(vI( zUtW6uKR=anTii_^Vo<2U6Txq5_Ji0z^OW?t=@KTTGv8P|y zc`xo8ncx_WHU>_liJ$)ZqfPq8@0a_F?>;;3`J3|T@tuUT53JK|`Wz~cy}XnZ_l+emed4!118z=sw15^YAx^#%5hg-&2w0x$WHsxhQ_~DV-J5CP=F8 zVcGG^w_GAMd6V1aiPVcP#{-}L)mag=k7D-b;*zpGAHSt+?N@Pl;!cfPDBRM$ZzWg| zRHm@DXO8xwjFH993J?F9nus0W3ZDFwe9aOkbG{fw9XO3t`_Xt3eqe(HqAWK7ly_K+ z|2gNjjg}=hn@0QzKEv()iw(BqFgICU-SlZ&_l9S^;eRhqU2T0nTl!;JnscdkS>@M! z1%(HV&-n(y>bM#81X_8iB_wGc(x)Y5*c|=q0@~wz%+;tP<>t=so1JdP>>CMl$Zc7z zsTZg`CYCaAcZ+fJIlZX+eW_SZy_lY~oXT<6jXI}pk8sC_wvdkt?$kQAF-Z?w%(e&H zx$?lz^-*-4zFsk+qxgmA+CN`^MEO>K3?J*ac$n)xM!J?eH@c`LjKb@yF{85?d(5EX zs+VE>#nQQPN(E*8__wkPg^iMP%H%3C-d#0b@(X*}bT=~e;jR4Md)>9umQ58e4_&Y< zwFw^ivraiFoMLB^cy~N28(@+iTu?mmzBPAAARq1io-OysFXh+Ht=m9Mhy1oJN6)w0x)bhEfdMlw_5d;BAQj**Xn>(ch7JrCp_!;Oz_ zwI^eycK4r6%Gr45;F>+Gc|4!1F2PQPmln3Vc4jSZ-;PU!kQT=LMo+V)!lr1tiyvI> z@+Ww$wul10m$a=tXpk4gIJjL4C(Zsfv=^eW$S=sml-V%fOU$^Pot-L8eMp=%#W`%(gG=_P>k8u_L$QqXx$ zs?f|Y?ptTlBJ5Dqaot6O8&bsH$d_&cszvoDV(wphyw^VcrHyy>yqk9~ZT30sQJ>$OFLNy#v zUv0w&3#|o0hxYuH;OU2b|Effpv1sA~+hm?gM@A2Z3n$B_T^;`e!HN94AU^m}I zM(AfJtMpo4;Y8r?)~3TtaFA-b=6m4JX>X4Dk_9`AvveEw+j8}n{8f9Yd3@ZI$rC<3QPi#MPXG3pmHmBEa_6>lC2uyTv|b8O=Wiam-O(@{+n0Dhc`sLY zg38l>e@nbvvhDh#KsXiT1Ri*ma;_}(2_B;u7~{H^g~S}eUtvh=ro(3xQoim&HV3VL~nwf?9oNrrOL$ddlLfv=AP5%A7{~;Z`hlH1fW{n+%5Y7 zCu?~u&o&RSY1kQvYu{5;RO;&8SJz`Hv3R`8X_i^>5xG6Uauc$Ylbd_(0In~VvCkv) zqCegJ#N3-MsZ-ZJWIJ6Ue#=gQHBa&>u?5a$7gmIP8u3)RIX1FquYuXPf6JS2=QZd1 zBqRLh7TH!uOPNpY48IykX)EPqLuS|d$(rlNrd1OCJ;LDYoY;-K z@=4NrK4%Ir@$V13t_PgFVZ1=_^WH6a?W*bYH6?*=?NQl1+*mibb^yoaja`uEV zOYX*!va&YL-HWE^lRi>}f z>h-g_S4SV$dT$@7Y1sPZ$CdD!B)2`>5F~wFz>RM6YuXDd&4V56?x~7z&Uy0TYZ3FZ z*!k2YrF%bo)3`SJ<$iFwt;X?m3!1HB)9w#LKj$+2Pw4O0HR#!_<{l}3ba~*=Ud5P! z#GOZfj7WJ*>W#cZvfMi#_V1L4i>~So6|CGPP(k~>%XJi+)8^FdsJtifo_^8q;N#ik z17liWZ6B>V-6qiDuhM)L<=P)kYpcbzS4NFFT2CH~IUGG1^L}-4=Z19``o13i@MfNT zL+A4gNrC&szdcxI>#1LAMB}1=GE|0`JpqO83H>zGZyiVB+LKV1Q&BG^@#|$uD>~-> zOdZcSVM2>uaB5$}XwBh2!4jKJdb}U2+*q-F9&M@nu8CIO@{dk6^IBCy{|vU+E(Nob zpL<%*{&A|P*Qz3T@rwsH$WGe} zcBd#hS=gw@>LC~ezv{gWnD`sx4AE93j?O#xqu9n{yW3P zn;6vE(OrxZY2+|?Dw^UznBLCNgQ#glW5PYH?dbX$ej}+Bf81UDt!9)S~YsF zt!kTkQ;RCj?QZOg>ozhytblIQVavjG@l65vr2kmF;j0tT?i)HAYCe1UI?vhGF<*o9 zLI!S)_0R-6)WnCwjNOkIIK($J{3-pO6l1Q8Lw&j-Mh?2UtnL7rQ`?SWdj(4pzgUPp zYuJV*7mZ(f*F=wdq>r@yW{5;?kF>V8%)AlEc)M1(`1nXBVIBHIM$Y^tZh zA2ZngF`vreF^5gl>C2$tZWT%#(4G=4BdG<)sxo+xn?~dKXa_w@2xp2pGGg_ipVk6- z<#hniIBoIG@+ZA@9(?FL0-WHXhu3GR1{!-7+MCXalwPzgxJZ+GYp1t8icHsPp6zMq-Oe$W6S^<0{(Y}x#rBAy516wc)jh@7k=%wjWcw>fv4OM=W5F^(sUjG)uY^p**mjrL=sOd*0%*X9Lvw!+8LoJbu~ASJnU1Uw%3-`9WW=-2BbR`<213 zBv0;g?M6U+l&(Fi5XI#S4+Yg)EpcBTV^(j`YiL(7ArBaDsl5w;SckT9(XDb7RsM?I zrB&4RI4$8!kr%&$wCRqLYot*?$lG(>cHnXs6x{@$8IjMTsh!-0DJfo zLn;{?ecSXmY$*Cbe)I170@($R4QHEwWC-leRjzg05R>$Ci}&3x!rlZzGG@S2Q+2+C z$*R&0`&I)l#d4Jn&dK&iZDjDMAnySIl4MmkojazTkAt}!AOLE(-}RF17ea34Bf+KW zLHg#?$(b8cyVATXyf0&FRc^bD!)vrhDR?|TJncf6x#N9$z>m$Q1WUE!gJB*j6a2Qmok2_w*Xr*8ux_5i%`RA%izl`EcAdTagg_F!gG~=g7u;YWXOdl)4W({yxI#xG zfmG$6H5<*HrH!m^ewkY|*NXrd&1fycT_{x&F)pn2n^se9Xxx}r_w%)O&=SmrW^v%E6Ir_h)B&=KGk5(xIqApi zORasjFLWvI?}f__a9g0Pd(fHu$-VUr>I3>GuXkSCr%|o>nG5A4r+7=Me&>hDx=XRc=WUn0|B3V!!RF3t!RW;KW3$cf9r2JyyLlax)sY9J zoqY&(=Dns=;G`_B;Hc&6FT*7ze!=PQKJm#%Wh?v_aZ|+*o#s`mlIKckRg^g|(tQc< zOCQyu=9Q!!dbf8-rqzN45Q{+cYu`DIa;iP9e6}r+01|f_f1AvLLKuoBEm@4$TonL} z?wFQOHF}+D7%nUWaF=|A3mIX_d9ao}-;V;|z%C97M6S5#??_XE`yiDmY-P{viiVcw zmda9$`CSX{@5t4ONiNCFqX?7LrK+8+Xx~Xi$l$Ac0R!!A`7PAT%9*wtJnRNcjqZU<}}ifU((;31-i-^$>wkZb65TMy#L zS{$GUo|pF|DX`;Yo8C|!ZDd+Xz*Dmxe?7S$J_)$7slm+e8+6zF1sX`{iz6$um*qIi zzMG#h-}FwVo&UfvQ*0TlH`H!NS!-EvXUXS3l3a|-6)P#;p6AXE>1o)e(O1@o>HK{*%Y z)N*NUa!4F2vT9IiMOlvY)QS8>QDm20b4~d#2D~3=z5z=T09d*|QF?$PAZ6DUgdDo~ zq_i>^4m(!mnT5GJVYc;5FBrYR)ReqPZ-z^|mjC0C~!VUWZOm89~kH=6?JQ zE`=LhQ@*C&DI1Fnm^RgwSkAZZF|>WV(OpMdaqsS*gv!~jFRit_?kkE7;7#m3sKg;A z5uj$P;Birzv8guBL>fxwZcg#59%N0OMmJzxt71|7eTF)c(08LU)E6*4wh%z^Gaxtt zUnSLLYPdR9hj04NQIqUy|9pRAbgmbF?=JTR85yry@cnf3eE)Ru{98R9fC?|4(#_c4 z-{M=OWtU)BkadF84dBpCrbPA&xf7o!>@X)-*U` z5tp|Tb<2O*EUZ2 zzf_4_e6sl1F#F~4p2o#?pUMwf)!gzAI(>OhfwO5)e9Yk$ZyV-*uSjb9SC0fm8{>*@Dj_` zVMfoFL3IY-C>4wg0={2ck&^!8*U62iVqSdA|2B@gTwR%kdMw?Wy!mR*%el9&G$;Qa zVT6WNsO~!u9}^M3v7u|JpNhCNQ|slWmEHTKxl!V_z1vMewO1NXA_+);0IE>pr2h7! zGjaI0ece{|qoezWwB_arg*#83hw=KQZB0U`hcuh<`y%n>3U(A>P5#BGWf|$)D~W!nAV+% z!iCFvy5_f(YqGPY!u`#byy4Yt35qvRAGW$X@u0>2T}i%c>x&9|hn%xyT5MD2sX4g^ zUHnxt#afpwkiF!qaFU}WS@(K+U%34-U8y-E_a`*+UkCVe6}LY$zWw{)!gW3QDnAE? zvRcbB6T`1`2A^QX)mo7vzqfQ$+0uIN6+HWcKeTiEUeMW~MDj)2%Jopi(5ODCuyDvnL-(eJPBZzG$)Vm+BdnZin3flVbg;Us)&jivA(-ncn~t z^Y$i-ZC-I>^8PgA%&b2vR9ukL7nH-CmM_`yS1l!@ock`Sp$mNQ|`LWjIlle zljiy*`Q6L2zk?;(3p~*Gh8y>k->7b>>)7@ubN;%KAN!sNDms!sa4r;gUM`+G14yOS zrFF~GG+up!s~PhaXa0KfeW24ad;QeWXTO7*B2KH0tyY9X4jVrrIOy)-UGQf$yzIch zHrZGe^ZgOK_V&aMpXwh5Z@U8+AZH4tk2i8P74&-;$!%waumZr_*xEFJ)VGBsXa~()Au%taLsXmA$di;_@rs zFLmngnsuE19Me-RiuDP*vdq68gk^6Qy!BC?(;*up|4R1kIZp5)QrxlAe&$m^D%Zy& zSgud5E@5eM3r4!$!}E^amHcJhNBATN+@ONn$Ns_%>8#e9H#c*oUc|<3Tw^ zhwQ6gPkJg{wz;|o&Ou6cyzCRY`c-1Oi6?sse$0O6;COEQ==)ghj{(>FLZ~0S0dNO$ z3)lT#2QY=nd;ZyjUDd|UdoDA|LgHksUr+mrmxBfKpLw3|ysR8{2qB`X=bQ(LQp~4yrp+XUs+$ye0{3-Wmet9jT6S4&;7RZB$a(!5<-l$a*-u}NXS{vxU+8fDBZYScK`=%6)|^c)|6o%G-*=#sskPhN zz-4(w*r$=HYrRpuft3{Sfel0oXFLUk<-nTO%Ta`QTUj(0^IAyqouDz4{^nVRZRJKb zFc80g_6u0?dM~kC^0Td<2-PglCDlT=Z!be=A@+@sx}()V9K#;!IJK<9TWo?2(axS4 zdYY{PtsA%_^!mlo_~Q(H*+SKYFOvxjV{tGxwIT1Zr=VF-zi3%wdU#U|l)T^u){29l zcuq*Z01F(oGmm`Ry_pZw$tUdN3My!x(VfZTr^)-Q1d!r#2!zcTXf@sMU)ze8jlYxg zwGb9B=xoaR**T`&Of`fu1VHiob&E4&g5y|Kry-9~(P+VFD_AP|st{HA0O^aeiHfhU zZSRWOuCsqw_(tQ7{y)Y2S|iv)kNMlov)x>BJ?T7RvqDYWV}6q?zEwvH;ECHZvaJ(h zX=KLjL*+Od&eVOzn$p_RmzI%O2qa7Hs*-ADpE+;kKZDUNV0B17B2c8}6U5>@R^-pk ztUl!aAdnUKB{W6G`mlkm$m39IaRA3qRi$t6<~KSDYMxg;n{P!V000Ld)}ZL^MWQv8AL)&Khmw?tJ2l~VsMAQP%QK}hX!k5ZgqtRU%#x(=A3ZFof%{31bpt zR%zzMN{EfcIcy3y!DShRwCKs^mGKV+DwjKzk}G~IXy2?WhkA^6pM}kGe4n7wwBr9C zR0Gs%Ec;VYT5SiU$&ZK)BmRX`P_9a#TUx zE{)pa;vz5>B4Jd(l{IUFHEuz#7OOm{20OvYX%atnS>KI*^oBqd9W z*dEB9nK=-6S|)yGaqmFcQlUo$F#wlb0Pr|+rCU0q+Bs5CkU~X7Y76b1QS9c}b@}v? zQv#rNgEwKWgr1urAqO%yrGNy&TSN_yGR;OVn~y~*%@$amsXEdz?gAIwt;s;AxYEQz zU^uFRdS;Uu!{NM4A|ZBg6_F#F!(JdRYyl(z)4|Rr9s+U|6%s`aD&cGvR@z=l&KHE> z@g&4=l$V;7WsRqy3NV@%vcEL&pll|M67xWjVkO|0RAZv;Y5*T~HFr!tzdvgg)JU7b18dtX`+Et~I;LETk!k;hKR{Y$8|Jtu-FN{I}{)s=aTGE<2?zui`ar5NKe48l(V7Q z98#09K83-%NSF3R0CQqt5WOSoSO6I(DygJ0HaZ2i9iR^q7vcFIJSymUWf_eY5|WxI+8JB zu8;Ff*Y0o0{$E!L>WV&oumRjvS3^g_OJc&(2Ym~`A_(Xc;0Pvan*alV09WF+<@hB4 z-F1CO?0hH(?2{ut)mOs^eAu|WvCu4HOtj|+S={DjDAk6$a-4X`7Mm+tFf_NEV{wQ8^CK%^sy<}B zO=77#oK8t|laD~{Ee4wd?Xbxv({{w;y(8n99ce0Te>ZB zX4ZLJXQ?(UT~`6Kd!N0(^lOsrL(Tn_oHOh1IcLsb`V{$`y-!OVWj%C1K&sE_{^Ddm z+MUn5b286NimCmTNWb?B=J{%#d)v)^_rS~yc&`4<=5cZIOg~=hJay(v#k;##)ziaN zOWir4{yrv0Kbp#idGRDLYxP{Yv*>@qOHC1-u%0;Q_&(Om%UYo8!8|aT6xr{|&blmQ z8EpIgW$;-02*9=wZrpqhN}E0RH`A?TL~k%yi1C@RPxnUs#}(6#bgE?FjeWMiythC9 z-d<`~Dj&0Gf9`m$eWRa@16v58Z^#&Ife~1;{K0{wjTP^<3!_10f(?>@ekbIC3(6qK zP?P=%0g!-xAWBff=f#X*-{LjmBUdG~VpLS+F8}}l>D)fI`uP5E1J2XgS=nc?S!U$s z#k?CMmYIBgPw(W=g0tbb^^jUGGLG$q6AqWZ-)N*3Lo&+{)0FSDSoqlW7CEeQ?zMhz1qUUe*siN7ZE7&w7#?EMR)RjD7J^?*(?pkdi?jCF+Zd?k*Qqh7Pk%`w^^(o+^spo_ zXhk-TWd6_hSIK8_uD#89E4Awzx-+(7TZV7e&)?)f0HaA-k9 zDpbA^Xhv#S2;OTXX=QerRV8??UO!WhP3VVB00Vs$xjCH;dM-%=k=A<+b%Ly<-~xW~ zh#I8`mUhebwoh{I$>MKg^~7i`Tn2w8$ISEeBn#ZL@b5hlP4_uabU6^s!G$O3`D4P4 z{VFX4_#0W(v@|kK<`uJuGDis}eZqVl7x@a~Q*ts(VC|{F7dk0q3p|G2$I;&z=ey1K z7Bb>~-L1jnvcpMwybE2P)^GqyxF5Pn=Y9KF$na45aAE%B0)W%$$xR{}osLT`qU=y)=R_+@->s5md$<8^Mr5gQ zAh5Sg_=4rHqpU2kVomb4y)YT@f?(K?|TmUO#WC1RcK zKAi;Wmt^;Bn>RJJ5*kaZ-JjYN;;QVoQW!q}eos49=r9V;Snfbx16hFGSHb$cvs@oX zGe>(pE&H~}GrWyPm>)rR`{xI@*&AYNO zZO(-wq=N?d97j20e`QbNhXN}+V7mtZ<*gx=GZaX36RG}sVAwe+`ho1L&TR8ZZKp;j%y4d0WeuGDWY=R$(gU)?_fl zN>H!wn^bwpO0pV9g>fU{G|k@Fn1hw)iVCHogyaB+TuKp&1E0$SXb`oY6Co514y4I1 z8FM4Sn{8~k67_s?@y27?O%6T$QpOlUuUN$N!?-&B*V2T4ys^WGVs=%Y$OL246Im&Z z-XQg~pveMz8P{j09o*EM^W3~7NsR>byhCAcCpojR#Ovtxuq|3E@$MW65o%dW>3zfM zbIGDM3=Nv}Yg>Rg2I4b&B+3glJcn>Z4AFWwnj!s93VB%7J%CD~cGY;dtl(GZi6B+7 zujwO@k@_(`;_J1RAIul6dgR^}AgHN)L#N&a;^(0D_ALuWUtWzUqj*}u-`(flmMt>J ztgaYPhpm>F>eIQc~8!9&)7=T;KKo?-`7prxctV`@f z6{OXx9lDGq4crivl4rB`3pq94lomv_Q>OUn9oHLRvblo6(ICdq+gi^RPDgQz@bI&% zS&amA9AHnr2br|VBnSzmx0sLsRsjI_008v>0F3StNQ-k&tf0n)TzV&dOl7M&hMZ%S z?B7sH_E?>-^0I>lNM@Sy3grhW7D?pDpvt4j%V=~nsk+If|E69Nmgq0tv?;2SL4Ughly-93yG9b38zeA1~M*Rn**z-T1ir>lxIYvi1a2z{3z|hRk9&Fd-If)&kZdF#DeJV+p%$ z1?tXiZYY3+KfVWn3LcQbMyegz7U^4kW!vSq6>kwWPj!6(owv3d1$*Xy(oyTa#8R7g z%J&L;BNHeEON@Unidk+Fgm0bsjYmpG`r@;;V7c=2sBD^4B0&V7_DwO)1P+>;GSsea z5yrgdnWvgIH$LRwyWceDrwDMI-}`Z*9_EuMpndQ!o%l?)?I4fo#50ZcyHHv9uGLR% z*Q-tSD1-Vk@(4xP9{v<+7RmFPBVV{D-j`QQ z=6Ys$trQa?vuJJj>jK4FwOq1csovgq)clB#d++J`fO-;*^Sn@+?*K?Ny1;w`&`52# z;whuys{sM%!x(b(J}m0S-02MqO%ofrJEcjn%ErTT8EYXHn0rTSRSIG%m?e@*~56iz)-YqTq{Nrz6q<`P@rji zU+CuX&}XXV#^RT|{fB;{d5n{UlHky4Gs|QJp~`W1E#H&sCJjDhxkYII`P_R#Y{$z@ zfL8~h@|meg=PC3oX0DqlnD#T4xZ==2*b4tOcj0?Qc;JHVO29hWqspqBtz=jhz2#A4 ze6=VSCYeEQiYbHbqkIX3uyKoxMqpf@_35wWozwspMGT;6v1wLalk|vyL`wnyeW4q8m-y_Q_O30ze0T&<>YCu?Fe@Hxv+rM`8=un$sF zlwZA(ghPYMk3yW#J=b+Rl)HM9@=l;BIMt+ST{wB|}0&%{IgX(Q2^A@~^=>N|xWQ=cBvR_w&KEKTI9>_N}bW4)@bJ zu7#jmnN*&7aEh*tZFjMXH#%r?a~X>K3UDG#UpdhzPy^FW=^%K z=(_Jc3-y?l=bCl={TbGM)sFw2D|!|b$(=)1LlHV%Gyi@qE^p_~?px)o8uu2qwe;^X zI+vtZ+>r5OBV=3R;_CbTdfh&Lq{Y8|eoXQcJEh`WajTa>dCreXE;>jR*4D2Y^tmB_ zeQ0BlL9)LG!U%vfNWdVYN%MgXwgAzm01*`M02pO%>pOqfyz{qLTtRH%EE-&0m;c*w@pG6UYrvo_{= z1jJ3dbi(;Uh(weFUPf%cx5$85kG=Mr90Florz&AhfKRPw@}%gQT<5~t-u*||%Pb2) z)+Aq5ROPRPQ#KIOyj&|rI0-X+qTWM0%@h)Dn1sNWN|lBR1wn;!OY10n84@VS{A}~a zg#=2fE|fNK<>~_fXAs;G*dqO7L)=b6y;!*H5iKdsU39Q0Icy3P0RC#s3_@s0?ujnhH%L7C`j1|I} zrW95Q6^%gU+bq)#D7kpx%J+Z)KpNTBsSNGuN>E0h+!(ey-GvBM6UrwgpP1-~+~lZv zeJ?wUnq^&6@`>l)FHu)MozwVWUBnhefJ9G}1PH6e3GZVI9B*RelaUblzV0J$$;oJq zarg(pTJ<0jG=g>)ilfRwbz&K~Tr&pxbd-)uD(Q%&YvfaN+325(YBO(0hb#NtuNmP- z2(0*J)s_{{akHLdQ69YtTz861oWaR|+FoDpoFhDqy$)iX_3oAh%I^?=*c$<9OAO`p z2Lzju1YPGmvMfD0Oit8Day^^B>WTU$NS<|P884L~q3@3m@VYNnoxkPmZI+o1YxySR zHxYLO^4^9lPJ&1TI_zlm>(H=D589KXCd>%wY^DV_L(34IArTVcE)Fp{xl}PdT0b&> zpnGMtI5>!LKd4h-*7uE0a128Z0T6XHatV8+4V!&)nZ4Yx^@#jgm*3ju|xp!R7!JyG!|ev7cH6p_jtF8)Qx-)B}c~lY?s?jbFkRJxv4hpUaSgK(N0W@h!ng=c@J6T& z&!8cB6eZ{o%qjHT;j)xWWK>lwak()l1`m2?D%M=#HWW`U&;ku|I--+1OWK7O{QQYD zF8i&Z<*Ne}f*w4j3b|Anfps8r<*b(50NoprINhN|E+{`Hp?H<4)poO5pHp1M5T>1Z zyZR9CcvIhVao=MpSYpMBCfcCGFLjXe%ujc~r4=9K2;sai{i<|YzE5*r_4?rhXV*4I z94Q+Z)7v0+9|TyLnN`$<2B(;}7BKloBoUZGL7LNOSHeY~JTY~ogt@6!TI?9+9{T8wr#n9x3Q#{5$sfg{#dS^1iKCe%>7 zS3d4L1Y?r+{z5i8M%%P-c3wx2@owWv;i?uHPnsARtp!1wVoUXP7H$tI+Rj$Q-nleX z?*ocYW0i}@{QTUI4B-G$0RR*M02u)QWd|C@r#*E~5xydsUG8#=j1=U6++{@fb}SAf zULpzyrb>|8^1~O8Epn;1TR!XfbuQ8NW6sHP zoEWz*fr3;2VR_vD@XjR}uHC~ZnD|n7hXHB{v~%D$3zn3U9n{Cd#8y1U+J-Cb{zYHZ zoCYkpsupBoO$52!lWq%ng+i4V#Y0D82hYt8=Zt`I($VLhccREKF;oain0lCI24$%J zF;kd$kwOiFp^6SPXaIQs&reMqVpIxH*(XKK%kbc%mKX|pT_Q67P_)-+s)vB&MgSLl zu^@K0V~fTbFUWMFk=P%;eZT|SM7)5+dk8@izUkTkM{CQ+bdPu-c1RinJn?6Ep6hh} zBh3H>u}}OOG=xTty;;pW%vlCFqJ|v^f&uL@#kM7;Rz7Z2qLXMj_ImmIuDa^TX?XJj zFVxBFlP?N|1eh`!Q*RX{P-{4WCtaNQeZM8NS!yd@Jaj!(Y9}}a_44|R$Z#t z6+s2MnMc7ES{UMq>zp6;)JzOk0AT%A`BYIMuL312Y8=djV)xzQfjn_-47bWD1Vk1=;Q%_-Qrc+hXr1?N zf&(0kL3efbk$FwmC;;Fm_PacaNup4_v9}n44DwS--m+qpk&80wkyafy;mc6{)tz4=l zV>~LJPb{gUld=1nuLv3v*^ompI1xaWj>&qYD6OeV(ZYpDmPB}hy-baDCk7N#wCNcJ zVt8y-w76~>1)TeUChxxc>oC$9rkUzh0N|v6=1A4z?ep1@IQN=5IC`rnEj6mW98m{+ zU*xAHV@G*rj_Fu9K=7_x_^fS5p4A}Q>?*fy^suC&nCy_kW9t4tIm~4BvMCT+_wk%& z|0c1$4VdbmwMvJ3B0>5PgY|rdap6eId%B9+0HzP3$~*uI;))MQn`z{%hmeP!Y_8gM zD_&pe+#}4~T$sVP#AM{cPGXRMF$-Y-vxhr&Eyl!fJp;~V;2>N?a_}SR zrr}MQ%GiID%~=u=THax6eNg`rZ{Y>i$`shQDa6Wd5kO;{(-gqHiGjmw+N=oh{pv|t z!-8?3Gf$Iws}W{8CPfTMe+Voc+N8H1Sgy`6P3)01mtT~(>3Y}fC)NxvQPIFAk-&t{ zInK`D=ldM3Iwm&>rh;}KJ?G=a6VIMTT2=cp%6Br`-i~25*;f5J1DvdOvLe^pV8Oqzr64!O2@Ad34IocutjI^S_I@7sF+% zgmyZBAGEAPyAz=2#{|9vz@tG%f(^ld{&lQ%{vEq`m?cwlR;tXpF-b*-ZN^UAlD2 zoqYK4gO7t>AI&>&H*@hl*C*G7Rka4S2DJwDF10STs#i7B>_grc_g?P3+`B7{%o1i0 zPteKc=Fs&}EnSZ+#}{+Blm9-YUHw1(sq?Al<7ayrFIf1ZlG;>KInA`zUcy{Fs?nJ| z^s2swb(<+oE#rlCVO6i9l1hrKPWO&AoHRWR%bJy1nz started = Completer(); - final Completer ended = Completer(); - networkController.addListener(() { - if (!started.isCompleted && networkController.value.isBuffering) { - started.complete(); - } - if (started.isCompleted && - !networkController.value.isBuffering && - !ended.isCompleted) { - ended.complete(); - } - }); - - await networkController.play(); - await networkController.seekTo(const Duration(seconds: 5)); - await tester.pumpAndSettle(_playDuration); - await networkController.pause(); - - expect(networkController.value.isPlaying, false); - expect(networkController.value.position, - (Duration position) => position > const Duration(seconds: 0)); - - await expectLater(started.future, completes); - await expectLater(ended.future, completes); - }, - skip: !(kIsWeb || defaultTargetPlatform == TargetPlatform.android), - ); - testWidgets( 'live stream duration != 0', (WidgetTester tester) async { @@ -221,23 +198,78 @@ void main() { skip: kIsWeb || // Web does not support local assets. // Extremely flaky on iOS: https://github.com/flutter/flutter/issues/86915 defaultTargetPlatform == TargetPlatform.iOS); + }); - testWidgets('test video player using static file() method as constructor', - (WidgetTester tester) async { + group('file-based videos', () { + setUp(() async { + // Load the data from the asset. String tempDir = (await getTemporaryDirectory()).path; - ByteData bytes = await rootBundle.load('assets/Butterfly-209.mp4'); + ByteData bytes = await rootBundle.load(_videoAssetKey); - File file = File('$tempDir/Butterfly-209.mp4'); + // Write it to a file to use as a source. + final String filename = _videoAssetKey.split('/').last; + File file = File('$tempDir/$filename'); await file.writeAsBytes(bytes.buffer.asInt8List()); - VideoPlayerController fileController = VideoPlayerController.file(file); - await fileController.initialize(); + _controller = VideoPlayerController.file(file); + }); - await fileController.play(); - expect(fileController.value.isPlaying, true); + testWidgets('test video player using static file() method as constructor', + (WidgetTester tester) async { + await _controller.initialize(); - await fileController.pause(); - expect(fileController.value.isPlaying, false); + await _controller.play(); + expect(_controller.value.isPlaying, true); + + await _controller.pause(); + expect(_controller.value.isPlaying, false); }, skip: kIsWeb); }); + + group('network videos', () { + setUp(() { + // TODO(stuartmorgan): Remove this conditional and update the hash in + // getUrlForAssetAsNetworkSource as a follow-up, once the webm asset is + // checked in. + final String videoUrl = kIsWeb + ? 'https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm' + : getUrlForAssetAsNetworkSource(_videoAssetKey); + _controller = VideoPlayerController.network(videoUrl); + }); + + testWidgets( + 'reports buffering status', + (WidgetTester tester) async { + await _controller.initialize(); + // Mute to allow playing without DOM interaction on Web. + // See https://developers.google.com/web/updates/2017/09/autoplay-policy-changes + await _controller.setVolume(0); + final Completer started = Completer(); + final Completer ended = Completer(); + _controller.addListener(() { + if (!started.isCompleted && _controller.value.isBuffering) { + started.complete(); + } + if (started.isCompleted && + !_controller.value.isBuffering && + !ended.isCompleted) { + ended.complete(); + } + }); + + await _controller.play(); + await _controller.seekTo(const Duration(seconds: 5)); + await tester.pumpAndSettle(_playDuration); + await _controller.pause(); + + expect(_controller.value.isPlaying, false); + expect(_controller.value.position, + (Duration position) => position > const Duration(seconds: 0)); + + await expectLater(started.future, completes); + await expectLater(ended.future, completes); + }, + skip: !(kIsWeb || defaultTargetPlatform == TargetPlatform.android), + ); + }); } diff --git a/packages/video_player/video_player/example/pubspec.yaml b/packages/video_player/video_player/example/pubspec.yaml index ce1787e2b427..6fa02c459c53 100644 --- a/packages/video_player/video_player/example/pubspec.yaml +++ b/packages/video_player/video_player/example/pubspec.yaml @@ -33,5 +33,6 @@ flutter: assets: - assets/flutter-mark-square-64.png - assets/Butterfly-209.mp4 + - assets/Butterfly-209.webm - assets/bumble_bee_captions.srt - assets/bumble_bee_captions.vtt diff --git a/script/install_chromium.sh b/script/install_chromium.sh new file mode 100755 index 000000000000..1cb38af05496 --- /dev/null +++ b/script/install_chromium.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# 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. +set -e +set -x + +readonly TARGET_DIR=$1 + +# The build of Chromium used to test web functionality. +# +# Chromium builds can be located here: https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Linux_x64/ +readonly CHROMIUM_BUILD=768968 +# The ChromeDriver version corresponding to the build above. See +# https://chromedriver.chromium.org/downloads +# for versions mappings when updating Chromium. +readonly CHROME_DRIVER_VERSION=84.0.4147.30 + +# Install Chromium. +mkdir "$TARGET_DIR" +wget --no-verbose "https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64%2F${CHROMIUM_BUILD}%2Fchrome-linux.zip?alt=media" -O "$TARGET_DIR"/chromium.zip +unzip "$TARGET_DIR"/chromium.zip -d "$TARGET_DIR"/ + +# Install ChromeDriver. +readonly DRIVER_ZIP_FILE="$TARGET_DIR/chromedriver.zip" +wget --no-verbose "https://chromedriver.storage.googleapis.com/$CHROME_DRIVER_VERSION/chromedriver_linux64.zip" -O "$DRIVER_ZIP_FILE" +unzip "$DRIVER_ZIP_FILE" -d "$TARGET_DIR/chromedriver" + +# Echo info at the end for ease of debugging. +export CHROME_EXECUTABLE="$TARGET_DIR"/chrome-linux/chrome +echo $CHROME_EXECUTABLE +$CHROME_EXECUTABLE --version +echo "ChromeDriver $CHROME_DRIVER_VERSION" diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 12ccf17d4d0b..101bfac2b9c8 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -10,11 +10,13 @@ `--packages=path_provide_ios` now works. - `--run-on-changed-packages` now includes only the changed packages in a federated plugin, not all packages in that plugin. -- Fix `federation-safety-check` handling of plugin deletion, and of top-level +- Fixes `federation-safety-check` handling of plugin deletion, and of top-level files in unfederated plugins whose names match federated plugin heuristics (e.g., `packages/foo/foo_android.iml`). -- Add an auto-retry for failed Firebase Test Lab tests as a short-term patch +- Adds an auto-retry for failed Firebase Test Lab tests as a short-term patch for flake issues. +- Adds support for `CHROME_EXECUTABLE` in `drive-examples` to match similar + `flutter` behavior. ## 0.7.3 diff --git a/script/tool/lib/src/drive_examples_command.dart b/script/tool/lib/src/drive_examples_command.dart index 593e557fa395..5bf0298e4e32 100644 --- a/script/tool/lib/src/drive_examples_command.dart +++ b/script/tool/lib/src/drive_examples_command.dart @@ -120,7 +120,9 @@ class DriveExamplesCommand extends PackageLoopingCommand { '-d', 'web-server', '--web-port=7357', - '--browser-name=chrome' + '--browser-name=chrome', + if (platform.environment.containsKey('CHROME_EXECUTABLE')) + '--chrome-binary=${platform.environment['CHROME_EXECUTABLE']}', ], if (getBoolArg(kPlatformWindows)) kPlatformWindows: ['-d', 'windows'], diff --git a/script/tool/test/drive_examples_command_test.dart b/script/tool/test/drive_examples_command_test.dart index a7a1652c2fc2..3c93d8bfe10c 100644 --- a/script/tool/test/drive_examples_command_test.dart +++ b/script/tool/test/drive_examples_command_test.dart @@ -584,6 +584,58 @@ void main() { ])); }); + test('driving a web plugin with CHROME_EXECUTABLE', () async { + final Directory pluginDirectory = createFakePlugin( + 'plugin', + packagesDir, + extraFiles: [ + 'example/test_driver/plugin_test.dart', + 'example/test_driver/plugin.dart', + ], + platformSupport: { + kPlatformWeb: const PlatformDetails(PlatformSupport.inline), + }, + ); + + final Directory pluginExampleDirectory = + pluginDirectory.childDirectory('example'); + + mockPlatform.environment['CHROME_EXECUTABLE'] = '/path/to/chrome'; + + final List output = await runCapturingPrint(runner, [ + 'drive-examples', + '--web', + ]); + + expect( + output, + containsAllInOrder([ + contains('Running for plugin'), + contains('No issues found!'), + ]), + ); + + expect( + processRunner.recordedCalls, + orderedEquals([ + ProcessCall( + getFlutterCommand(mockPlatform), + const [ + 'drive', + '-d', + 'web-server', + '--web-port=7357', + '--browser-name=chrome', + '--chrome-binary=/path/to/chrome', + '--driver', + 'test_driver/plugin_test.dart', + '--target', + 'test_driver/plugin.dart' + ], + pluginExampleDirectory.path), + ])); + }); + test('driving when plugin does not suppport Windows is a no-op', () async { createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/test_driver/plugin_test.dart', diff --git a/script/tool/test/mocks.dart b/script/tool/test/mocks.dart index 3d0aef1b3971..f6333ebd367d 100644 --- a/script/tool/test/mocks.dart +++ b/script/tool/test/mocks.dart @@ -30,6 +30,9 @@ class MockPlatform extends Mock implements Platform { Uri get script => isWindows ? Uri.file(r'C:\foo\bar', windows: true) : Uri.file('/foo/bar', windows: false); + + @override + Map environment = {}; } class MockProcess extends Mock implements io.Process { From 72634e0c404ca0cef18227dec5f8eaa4b6445998 Mon Sep 17 00:00:00 2001 From: Collin Jackson Date: Wed, 5 Jan 2022 17:43:43 -0800 Subject: [PATCH 081/600] [plugin_platform_interface] Add a new method `verify` that prevents use of `const Object()` as token. (#4640) - Introduce `verify`, a more future-proof name. It throws `AssertionError` if `const Object()` used as the instance's token. - Soft-deprecate `verifyToken` with a comment. It will actually deprecated in a future release, to avoid breaking tests with this minor change. - Update documentation for `PlatformInterface` to show new usage of `verify` and other cosmetic fixes. - Add a test for the new assertion. Fixes https://github.com/flutter/flutter/issues/96178. --- .../plugin_platform_interface/CHANGELOG.md | 5 ++ .../lib/plugin_platform_interface.dart | 34 ++++++-- .../plugin_platform_interface/pubspec.yaml | 10 +-- .../test/plugin_platform_interface_test.dart | 85 ++++++++++++++++--- 4 files changed, 109 insertions(+), 25 deletions(-) diff --git a/packages/plugin_platform_interface/CHANGELOG.md b/packages/plugin_platform_interface/CHANGELOG.md index af79d119c5f6..950da032e10f 100644 --- a/packages/plugin_platform_interface/CHANGELOG.md +++ b/packages/plugin_platform_interface/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.1.0 + +* Introduce `verify`, which prevents use of `const Object()` as instance token. +* Add a comment indicating that `verifyToken` will be deprecated in a future release. + ## 2.0.2 * Update package description. diff --git a/packages/plugin_platform_interface/lib/plugin_platform_interface.dart b/packages/plugin_platform_interface/lib/plugin_platform_interface.dart index d9bd88168422..f12fd3c39cc7 100644 --- a/packages/plugin_platform_interface/lib/plugin_platform_interface.dart +++ b/packages/plugin_platform_interface/lib/plugin_platform_interface.dart @@ -12,7 +12,7 @@ import 'package:meta/meta.dart'; /// implemented using `extends` instead of `implements`. /// /// Platform interface classes are expected to have a private static token object which will be -/// be passed to [verifyToken] along with a platform interface object for verification. +/// be passed to [verify] along with a platform interface object for verification. /// /// Sample usage: /// @@ -22,14 +22,14 @@ import 'package:meta/meta.dart'; /// /// static UrlLauncherPlatform _instance = MethodChannelUrlLauncher(); /// -/// static const Object _token = Object(); +/// static final Object _token = Object(); /// /// static UrlLauncherPlatform get instance => _instance; /// /// /// Platform-specific plugins should set this with their own platform-specific /// /// class that extends [UrlLauncherPlatform] when they register themselves. /// static set instance(UrlLauncherPlatform instance) { -/// PlatformInterface.verifyToken(instance, _token); +/// PlatformInterface.verify(instance, _token); /// _instance = instance; /// } /// @@ -40,13 +40,16 @@ import 'package:meta/meta.dart'; /// to include the [MockPlatformInterfaceMixin] for the verification to be temporarily disabled. See /// [MockPlatformInterfaceMixin] for a sample of using Mockito to mock a platform interface. abstract class PlatformInterface { - /// Pass a private, class-specific `const Object()` as the `token`. + /// Constructs a PlatformInterface, for use only in constructors of abstract + /// derived classes. + /// + /// @param token The same, non-`const` `Object` that will be passed to `verify`. PlatformInterface({required Object token}) : _instanceToken = token; final Object? _instanceToken; - /// Ensures that the platform instance has a token that matches the - /// provided token and throws [AssertionError] if not. + /// Ensures that the platform instance was constructed with a non-`const` token + /// that matches the provided token and throws [AssertionError] if not. /// /// This is used to ensure that implementers are using `extends` rather than /// `implements`. @@ -56,7 +59,22 @@ abstract class PlatformInterface { /// /// This is implemented as a static method so that it cannot be overridden /// with `noSuchMethod`. + static void verify(PlatformInterface instance, Object token) { + if (identical(instance._instanceToken, const Object())) { + throw AssertionError('`const Object()` cannot be used as the token.'); + } + _verify(instance, token); + } + + /// Performs the same checks as `verify` but without throwing an + /// [AssertionError] if `const Object()` is used as the instance token. + /// + /// This method will be deprecated in a future release. static void verifyToken(PlatformInterface instance, Object token) { + _verify(instance, token); + } + + static void _verify(PlatformInterface instance, Object token) { if (instance is MockPlatformInterfaceMixin) { bool assertionsEnabled = false; assert(() { @@ -78,12 +96,12 @@ abstract class PlatformInterface { /// A [PlatformInterface] mixin that can be combined with mockito's `Mock`. /// -/// It passes the [PlatformInterface.verifyToken] check even though it isn't +/// It passes the [PlatformInterface.verify] check even though it isn't /// using `extends`. /// /// This class is intended for use in tests only. /// -/// Sample usage (assuming UrlLauncherPlatform extends [PlatformInterface]: +/// Sample usage (assuming `UrlLauncherPlatform` extends [PlatformInterface]): /// /// ```dart /// class UrlLauncherPlatformMock extends Mock diff --git a/packages/plugin_platform_interface/pubspec.yaml b/packages/plugin_platform_interface/pubspec.yaml index 0b4b1782b526..0559b8daee8f 100644 --- a/packages/plugin_platform_interface/pubspec.yaml +++ b/packages/plugin_platform_interface/pubspec.yaml @@ -9,13 +9,13 @@ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+ # # This package is used as a second level dependency for many plugins, a major version bump here # is guaranteed to lead the ecosystem to a version lock (the first plugin that upgrades to version -# 2 of this package cannot be used with any other plugin that have not yet migrated). +# 3 of this package cannot be used with any other plugin that have not yet migrated). # # Please consider carefully before bumping the major version of this package, ideally it should only -# be done when absolutely necessary and after the ecosystem has already migrated to 1.X.Y version -# that is forward compatible with 2.0.0 (ideally the ecosystem have migrated to depend on: -# `plugin_platform_interface: >=1.X.Y <3.0.0`). -version: 2.0.2 +# be done when absolutely necessary and after the ecosystem has already migrated to 2.X.Y version +# that is forward compatible with 3.0.0 (ideally the ecosystem have migrated to depend on: +# `plugin_platform_interface: >=2.X.Y <4.0.0`). +version: 2.1.0 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart b/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart index 967fa79d6dc3..a00462ffad50 100644 --- a/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart +++ b/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart @@ -12,7 +12,7 @@ class SamplePluginPlatform extends PlatformInterface { static final Object _token = Object(); static set instance(SamplePluginPlatform instance) { - PlatformInterface.verifyToken(instance, _token); + PlatformInterface.verify(instance, _token); // A real implementation would set a static instance field here. } } @@ -26,20 +26,81 @@ class ImplementsSamplePluginPlatformUsingMockPlatformInterfaceMixin extends Mock class ExtendsSamplePluginPlatform extends SamplePluginPlatform {} +class ConstTokenPluginPlatform extends PlatformInterface { + ConstTokenPluginPlatform() : super(token: _token); + + static const Object _token = Object(); // invalid + + static set instance(ConstTokenPluginPlatform instance) { + PlatformInterface.verify(instance, _token); + } +} + +class ExtendsConstTokenPluginPlatform extends ConstTokenPluginPlatform {} + +class VerifyTokenPluginPlatform extends PlatformInterface { + VerifyTokenPluginPlatform() : super(token: _token); + + static final Object _token = Object(); + + static set instance(VerifyTokenPluginPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + // A real implementation would set a static instance field here. + } +} + +class ImplementsVerifyTokenPluginPlatform extends Mock + implements VerifyTokenPluginPlatform {} + +class ImplementsVerifyTokenPluginPlatformUsingMockPlatformInterfaceMixin + extends Mock + with MockPlatformInterfaceMixin + implements VerifyTokenPluginPlatform {} + +class ExtendsVerifyTokenPluginPlatform extends VerifyTokenPluginPlatform {} + void main() { - test('Cannot be implemented with `implements`', () { - expect(() { - SamplePluginPlatform.instance = ImplementsSamplePluginPlatform(); - }, throwsA(isA())); - }); + group('`verify`', () { + test('prevents implementation with `implements`', () { + expect(() { + SamplePluginPlatform.instance = ImplementsSamplePluginPlatform(); + }, throwsA(isA())); + }); + + test('allows mocking with `implements`', () { + final SamplePluginPlatform mock = + ImplementsSamplePluginPlatformUsingMockPlatformInterfaceMixin(); + SamplePluginPlatform.instance = mock; + }); - test('Can be mocked with `implements`', () { - final SamplePluginPlatform mock = - ImplementsSamplePluginPlatformUsingMockPlatformInterfaceMixin(); - SamplePluginPlatform.instance = mock; + test('allows extending', () { + SamplePluginPlatform.instance = ExtendsSamplePluginPlatform(); + }); + + test('prevents `const Object()` token', () { + expect(() { + ConstTokenPluginPlatform.instance = ExtendsConstTokenPluginPlatform(); + }, throwsA(isA())); + }); }); - test('Can be extended', () { - SamplePluginPlatform.instance = ExtendsSamplePluginPlatform(); + // Tests of the earlier, to-be-deprecated `verifyToken` method + group('`verifyToken`', () { + test('prevents implementation with `implements`', () { + expect(() { + VerifyTokenPluginPlatform.instance = + ImplementsVerifyTokenPluginPlatform(); + }, throwsA(isA())); + }); + + test('allows mocking with `implements`', () { + final VerifyTokenPluginPlatform mock = + ImplementsVerifyTokenPluginPlatformUsingMockPlatformInterfaceMixin(); + VerifyTokenPluginPlatform.instance = mock; + }); + + test('allows extending', () { + VerifyTokenPluginPlatform.instance = ExtendsVerifyTokenPluginPlatform(); + }); }); } From 7686be7ecbe1cc049423715b5e2d897cb63a43c1 Mon Sep 17 00:00:00 2001 From: godofredoc Date: Thu, 6 Jan 2022 10:50:45 -0800 Subject: [PATCH 082/600] Change the token used for mirroring. (#4646) This is required to be able to close the repo during the default branch migration. Bug: https://github.com/flutter/flutter/issues/90476 --- .github/workflows/mirror.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mirror.yml b/.github/workflows/mirror.yml index 47af473696c4..c9780f7bec20 100644 --- a/.github/workflows/mirror.yml +++ b/.github/workflows/mirror.yml @@ -20,6 +20,6 @@ jobs: id: mirror uses: google/mirror-branch-action@c6b07e441a7ffc5ae15860c1d0a8107a3a151db8 with: - github-token: ${{ secrets.FLUTTERGITHUBBOT_TOKEN }} + github-token: ${{ secrets.FLUTTERMIRRORINGBOT_TOKEN }} source: 'master' dest: 'main' From dc9a20fe72437171e6fc90ef6d4ae58d6e4e6d24 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 6 Jan 2022 17:28:51 -0500 Subject: [PATCH 083/600] Switch release action to 'main' (#4649) Part of switching default from `master` to `main` --- .github/workflows/release.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a64acf7692f9..1bf50a0d1dfc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,7 +2,7 @@ name: release on: push: branches: - - master + - main jobs: release: @@ -49,6 +49,3 @@ jobs: git config --global user.email ${{ secrets.USER_EMAIL }} dart ./script/tool/lib/src/main.dart publish-plugin --all-changed --base-sha=HEAD~ --skip-confirmation --remote=origin env: {PUB_CREDENTIALS: "${{ secrets.PUB_CREDENTIALS }}"} - - env: - DEFAULT_BRANCH: master From 87adc2ad587dafb28bb8f7058e5df9ac072a3fec Mon Sep 17 00:00:00 2001 From: Collin Jackson Date: Thu, 6 Jan 2022 19:50:12 -0500 Subject: [PATCH 084/600] Update all plugins that use `verifyToken` to use `verify` instead (#4643) --- packages/camera/camera_platform_interface/CHANGELOG.md | 4 ++++ .../lib/src/platform_interface/camera_platform.dart | 2 +- packages/camera/camera_platform_interface/pubspec.yaml | 4 ++-- .../file_selector_platform_interface/CHANGELOG.md | 3 ++- .../lib/src/platform_interface/file_selector_interface.dart | 2 +- .../file_selector_platform_interface/pubspec.yaml | 4 ++-- .../google_maps_flutter_platform_interface/CHANGELOG.md | 4 ++++ .../src/platform_interface/google_maps_flutter_platform.dart | 2 +- .../google_maps_flutter_platform_interface/pubspec.yaml | 4 ++-- .../image_picker/image_picker_platform_interface/CHANGELOG.md | 4 ++++ .../lib/src/platform_interface/image_picker_platform.dart | 2 +- .../image_picker/image_picker_platform_interface/pubspec.yaml | 4 ++-- .../in_app_purchase_platform_interface/CHANGELOG.md | 4 ++++ .../lib/src/in_app_purchase_platform.dart | 2 +- .../in_app_purchase_platform_interface/pubspec.yaml | 4 ++-- .../path_provider_platform_interface/CHANGELOG.md | 4 ++++ .../lib/path_provider_platform_interface.dart | 2 +- .../path_provider_platform_interface/pubspec.yaml | 4 ++-- .../quick_actions_platform_interface/CHANGELOG.md | 3 ++- .../lib/platform_interface/quick_actions_platform.dart | 2 +- .../quick_actions_platform_interface/pubspec.yaml | 4 ++-- .../url_launcher/url_launcher_platform_interface/CHANGELOG.md | 3 ++- .../lib/url_launcher_platform_interface.dart | 2 +- .../url_launcher/url_launcher_platform_interface/pubspec.yaml | 4 ++-- .../video_player/video_player_platform_interface/CHANGELOG.md | 4 ++++ .../lib/video_player_platform_interface.dart | 2 +- .../video_player/video_player_platform_interface/pubspec.yaml | 4 ++-- .../webview_flutter_platform_interface/CHANGELOG.md | 4 ++++ .../lib/src/platform_interface/webview_cookie_manager.dart | 2 +- .../webview_flutter_platform_interface/pubspec.yaml | 4 ++-- 30 files changed, 64 insertions(+), 33 deletions(-) diff --git a/packages/camera/camera_platform_interface/CHANGELOG.md b/packages/camera/camera_platform_interface/CHANGELOG.md index 69e9200382ae..04149913fbf5 100644 --- a/packages/camera/camera_platform_interface/CHANGELOG.md +++ b/packages/camera/camera_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.3 + +* Update to use the `verify` method introduced in platform_plugin_interface 2.1.0. + ## 2.1.2 * Adopts new analysis options and fixes all violations. diff --git a/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart b/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart index aafeef890f1b..0d240496086d 100644 --- a/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart +++ b/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart @@ -39,7 +39,7 @@ abstract class CameraPlatform extends PlatformInterface { /// Platform-specific plugins should set this with their own platform-specific /// class that extends [CameraPlatform] when they register themselves. static set instance(CameraPlatform instance) { - PlatformInterface.verifyToken(instance, _token); + PlatformInterface.verify(instance, _token); _instance = instance; } diff --git a/packages/camera/camera_platform_interface/pubspec.yaml b/packages/camera/camera_platform_interface/pubspec.yaml index 753e8d0d7aed..3578fc423da9 100644 --- a/packages/camera/camera_platform_interface/pubspec.yaml +++ b/packages/camera/camera_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/camera/camer issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.1.2 +version: 2.1.3 environment: sdk: '>=2.12.0 <3.0.0' @@ -15,7 +15,7 @@ dependencies: flutter: sdk: flutter meta: ^1.3.0 - plugin_platform_interface: ^2.0.0 + plugin_platform_interface: ^2.1.0 stream_transform: ^2.0.0 dev_dependencies: diff --git a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md index 95701504fed5..b71bbb6b7bd1 100644 --- a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md +++ b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 2.0.3 * Minor code cleanup for new analysis rules. +* Update to use the `verify` method introduced in plugin_platform_interface 2.1.0. ## 2.0.2 diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart index 54a6557c4428..f8fa83bd18d2 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart @@ -33,7 +33,7 @@ abstract class FileSelectorPlatform extends PlatformInterface { /// Platform-specific plugins should set this with their own platform-specific /// class that extends [FileSelectorPlatform] when they register themselves. static set instance(FileSelectorPlatform instance) { - PlatformInterface.verifyToken(instance, _token); + PlatformInterface.verify(instance, _token); _instance = instance; } diff --git a/packages/file_selector/file_selector_platform_interface/pubspec.yaml b/packages/file_selector/file_selector_platform_interface/pubspec.yaml index ed0780537a80..3a00e9905a03 100644 --- a/packages/file_selector/file_selector_platform_interface/pubspec.yaml +++ b/packages/file_selector/file_selector_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/file_selecto issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.0.2 +version: 2.0.3 environment: sdk: ">=2.12.0 <3.0.0" @@ -16,7 +16,7 @@ dependencies: sdk: flutter http: ^0.13.0 meta: ^1.3.0 - plugin_platform_interface: ^2.0.0 + plugin_platform_interface: ^2.1.0 dev_dependencies: flutter_test: diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md index 3a22dde8b659..f426171a9942 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.4 + +* Update to use the `verify` method introduced in plugin_platform_interface 2.1.0. + ## 2.1.3 * `LatLng` constructor maintains longitude precision when given within diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/platform_interface/google_maps_flutter_platform.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/platform_interface/google_maps_flutter_platform.dart index 08b4872ad5dd..6b39973134be 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/platform_interface/google_maps_flutter_platform.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/platform_interface/google_maps_flutter_platform.dart @@ -39,7 +39,7 @@ abstract class GoogleMapsFlutterPlatform extends PlatformInterface { /// Platform-specific plugins should set this with their own platform-specific /// class that extends [GoogleMapsFlutterPlatform] when they register themselves. static set instance(GoogleMapsFlutterPlatform instance) { - PlatformInterface.verifyToken(instance, _token); + PlatformInterface.verify(instance, _token); _instance = instance; } diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml index d3d7653b746e..5d527c526757 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/google_maps_ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.1.3 +version: 2.1.4 environment: sdk: '>=2.12.0 <3.0.0' @@ -15,7 +15,7 @@ dependencies: flutter: sdk: flutter meta: ^1.3.0 - plugin_platform_interface: ^2.0.0 + plugin_platform_interface: ^2.1.0 stream_transform: ^2.0.0 dev_dependencies: diff --git a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md index d637ac1a277e..447204b2163e 100644 --- a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md +++ b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.4.2 + +* Update to use the `verify` method introduced in plugin_platform_interface 2.1.0. + ## 2.4.1 * Reverts the changes from 2.4.0, which was a breaking change that diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart b/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart index 5c1c8b698442..dbc8d89f8ac0 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart @@ -34,7 +34,7 @@ abstract class ImagePickerPlatform extends PlatformInterface { // TODO(amirh): Extract common platform interface logic. // https://github.com/flutter/flutter/issues/43368 static set instance(ImagePickerPlatform instance) { - PlatformInterface.verifyToken(instance, _token); + PlatformInterface.verify(instance, _token); _instance = instance; } diff --git a/packages/image_picker/image_picker_platform_interface/pubspec.yaml b/packages/image_picker/image_picker_platform_interface/pubspec.yaml index e41137fcb06b..f4cdb676cce1 100644 --- a/packages/image_picker/image_picker_platform_interface/pubspec.yaml +++ b/packages/image_picker/image_picker_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.4.1 +version: 2.4.2 environment: sdk: ">=2.12.0 <3.0.0" @@ -15,7 +15,7 @@ dependencies: sdk: flutter http: ^0.13.0 meta: ^1.3.0 - plugin_platform_interface: ^2.0.0 + plugin_platform_interface: ^2.1.0 cross_file: ^0.3.1+1 dev_dependencies: diff --git a/packages/in_app_purchase/in_app_purchase_platform_interface/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_platform_interface/CHANGELOG.md index e01f5277b6f1..a52d8d244f5f 100644 --- a/packages/in_app_purchase/in_app_purchase_platform_interface/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.3.1 + +* Update to use the `verify` method introduced in plugin_platform_interface 2.1.0. + ## 1.3.0 * Added new `PurchaseStatus` named `canceled` to distinguish between an error and user cancellation. diff --git a/packages/in_app_purchase/in_app_purchase_platform_interface/lib/src/in_app_purchase_platform.dart b/packages/in_app_purchase/in_app_purchase_platform_interface/lib/src/in_app_purchase_platform.dart index eac4a0712078..d62e8e5f39b0 100644 --- a/packages/in_app_purchase/in_app_purchase_platform_interface/lib/src/in_app_purchase_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_platform_interface/lib/src/in_app_purchase_platform.dart @@ -31,7 +31,7 @@ abstract class InAppPurchasePlatform extends PlatformInterface { // TODO(amirh): Extract common platform interface logic. // https://github.com/flutter/flutter/issues/43368 static set instance(InAppPurchasePlatform instance) { - PlatformInterface.verifyToken(instance, _token); + PlatformInterface.verify(instance, _token); _instance = instance; } diff --git a/packages/in_app_purchase/in_app_purchase_platform_interface/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_platform_interface/pubspec.yaml index fc306acbd41e..b697121025c6 100644 --- a/packages/in_app_purchase/in_app_purchase_platform_interface/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/in_app_purch issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 1.3.0 +version: 1.3.1 environment: sdk: ">=2.12.0 <3.0.0" @@ -13,7 +13,7 @@ environment: dependencies: flutter: sdk: flutter - plugin_platform_interface: ^2.0.0 + plugin_platform_interface: ^2.1.0 dev_dependencies: flutter_test: diff --git a/packages/path_provider/path_provider_platform_interface/CHANGELOG.md b/packages/path_provider/path_provider_platform_interface/CHANGELOG.md index eec0fe3866b5..a1607412cdd5 100644 --- a/packages/path_provider/path_provider_platform_interface/CHANGELOG.md +++ b/packages/path_provider/path_provider_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.2 + +* Update to use the `verify` method introduced in plugin_platform_interface 2.1.0. + ## 2.0.1 * Update platform_plugin_interface version requirement. diff --git a/packages/path_provider/path_provider_platform_interface/lib/path_provider_platform_interface.dart b/packages/path_provider/path_provider_platform_interface/lib/path_provider_platform_interface.dart index 99e600d05263..517ac74d8fa0 100644 --- a/packages/path_provider/path_provider_platform_interface/lib/path_provider_platform_interface.dart +++ b/packages/path_provider/path_provider_platform_interface/lib/path_provider_platform_interface.dart @@ -32,7 +32,7 @@ abstract class PathProviderPlatform extends PlatformInterface { /// Platform-specific plugins should set this with their own platform-specific /// class that extends [PathProviderPlatform] when they register themselves. static set instance(PathProviderPlatform instance) { - PlatformInterface.verifyToken(instance, _token); + PlatformInterface.verify(instance, _token); _instance = instance; } diff --git a/packages/path_provider/path_provider_platform_interface/pubspec.yaml b/packages/path_provider/path_provider_platform_interface/pubspec.yaml index 7fe5e8dfc232..eca47e32c8ba 100644 --- a/packages/path_provider/path_provider_platform_interface/pubspec.yaml +++ b/packages/path_provider/path_provider_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/path_provide issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.0.1 +version: 2.0.2 environment: sdk: ">=2.12.0 <3.0.0" @@ -15,7 +15,7 @@ dependencies: sdk: flutter meta: ^1.3.0 platform: ^3.0.0 - plugin_platform_interface: ^2.0.0 + plugin_platform_interface: ^2.1.0 dev_dependencies: flutter_test: diff --git a/packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md b/packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md index 9568f8064171..b66fc145bf9d 100644 --- a/packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 1.0.1 * Updates code for analyzer changes. +* Update to use the `verify` method introduced in plugin_platform_interface 2.1.0. ## 1.0.0 diff --git a/packages/quick_actions/quick_actions_platform_interface/lib/platform_interface/quick_actions_platform.dart b/packages/quick_actions/quick_actions_platform_interface/lib/platform_interface/quick_actions_platform.dart index 13e98857ef05..7a70bba5c81d 100644 --- a/packages/quick_actions/quick_actions_platform_interface/lib/platform_interface/quick_actions_platform.dart +++ b/packages/quick_actions/quick_actions_platform_interface/lib/platform_interface/quick_actions_platform.dart @@ -32,7 +32,7 @@ abstract class QuickActionsPlatform extends PlatformInterface { // TODO(amirh): Extract common platform interface logic. // https://github.com/flutter/flutter/issues/43368 static set instance(QuickActionsPlatform instance) { - PlatformInterface.verifyToken(instance, _token); + PlatformInterface.verify(instance, _token); _instance = instance; } diff --git a/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml b/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml index 4b9542eb1649..c8ea8dfb4a29 100644 --- a/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml +++ b/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/quick_action issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+quick_actions%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 1.0.0 +version: 1.0.1 environment: sdk: ">=2.12.0 <3.0.0" @@ -14,7 +14,7 @@ dependencies: flutter: sdk: flutter meta: ^1.3.0 - plugin_platform_interface: ^2.0.0 + plugin_platform_interface: ^2.1.0 dev_dependencies: flutter_test: diff --git a/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md b/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md index 02e2d0ab10a2..1a9c575c27cb 100644 --- a/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 2.0.5 * Updates code for new analysis options. +* Update to use the `verify` method introduced in platform_plugin_interface 2.1.0. ## 2.0.4 diff --git a/packages/url_launcher/url_launcher_platform_interface/lib/url_launcher_platform_interface.dart b/packages/url_launcher/url_launcher_platform_interface/lib/url_launcher_platform_interface.dart index e9435b8dc4e3..18d64eff8dcb 100644 --- a/packages/url_launcher/url_launcher_platform_interface/lib/url_launcher_platform_interface.dart +++ b/packages/url_launcher/url_launcher_platform_interface/lib/url_launcher_platform_interface.dart @@ -34,7 +34,7 @@ abstract class UrlLauncherPlatform extends PlatformInterface { // TODO(amirh): Extract common platform interface logic. // https://github.com/flutter/flutter/issues/43368 static set instance(UrlLauncherPlatform instance) { - PlatformInterface.verifyToken(instance, _token); + PlatformInterface.verify(instance, _token); _instance = instance; } diff --git a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml index de1e71fbad08..3f9605b42320 100644 --- a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml +++ b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/url_launcher issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.0.4 +version: 2.0.5 environment: sdk: ">=2.12.0 <3.0.0" @@ -13,7 +13,7 @@ environment: dependencies: flutter: sdk: flutter - plugin_platform_interface: ^2.0.0 + plugin_platform_interface: ^2.1.0 dev_dependencies: flutter_test: diff --git a/packages/video_player/video_player_platform_interface/CHANGELOG.md b/packages/video_player/video_player_platform_interface/CHANGELOG.md index e0e6a11065ee..bf51960bcb40 100644 --- a/packages/video_player/video_player_platform_interface/CHANGELOG.md +++ b/packages/video_player/video_player_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 5.0.1 + +* Update to use the `verify` method introduced in platform_plugin_interface 2.1.0. + ## 5.0.0 * **BREAKING CHANGES**: diff --git a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart index 66b6d709e9fe..451537ad1f12 100644 --- a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart +++ b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart @@ -32,7 +32,7 @@ abstract class VideoPlayerPlatform extends PlatformInterface { /// platform-specific class that extends [VideoPlayerPlatform] when they /// register themselves. static set instance(VideoPlayerPlatform instance) { - PlatformInterface.verifyToken(instance, _token); + PlatformInterface.verify(instance, _token); _instance = instance; } diff --git a/packages/video_player/video_player_platform_interface/pubspec.yaml b/packages/video_player/video_player_platform_interface/pubspec.yaml index b8404772bffa..986ca913be62 100644 --- a/packages/video_player/video_player_platform_interface/pubspec.yaml +++ b/packages/video_player/video_player_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/video_player issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 5.0.0 +version: 5.0.1 environment: sdk: ">=2.12.0 <3.0.0" @@ -13,7 +13,7 @@ environment: dependencies: flutter: sdk: flutter - plugin_platform_interface: ^2.0.0 + plugin_platform_interface: ^2.1.0 dev_dependencies: flutter_test: diff --git a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md index ac61d482ff16..500bee7d2622 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.8.1 + +* Update to use the `verify` method introduced in platform_plugin_interface 2.1.0. + ## 1.8.0 * Adds the `loadFlutterAsset` method to the platform interface. diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_cookie_manager.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_cookie_manager.dart index d960926e419c..1f87e6a26ba6 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_cookie_manager.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_cookie_manager.dart @@ -30,7 +30,7 @@ abstract class WebViewCookieManagerPlatform extends PlatformInterface { throw AssertionError( 'Platform interfaces can only be set to a non-null instance'); } - PlatformInterface.verifyToken(instance, _token); + PlatformInterface.verify(instance, _token); _instance = instance; } diff --git a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml index 5950cab1e46f..cdff5f2395b3 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/webview_flut issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview_flutter%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 1.8.0 +version: 1.8.1 environment: sdk: ">=2.12.0 <3.0.0" @@ -13,7 +13,7 @@ environment: dependencies: flutter: sdk: flutter - plugin_platform_interface: ^2.0.0 + plugin_platform_interface: ^2.1.0 dev_dependencies: flutter_test: From 7a64cad9a77698928175defac04e7a9eee4fafb5 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 7 Jan 2022 10:01:18 -0800 Subject: [PATCH 085/600] [plugin_platform_interface] Fix 'verify' for Fakes (#4651) The new `verify` method worked with `Mock` (since `_instanceToken` returns a stub function in that case), but not `Fake` or a manual fake since it was unconditionally accessing `_instanceToken`. This fixes that new check to be gated behind the `MockPlatformInterfaceMixin` test, and adds a unit test that covers `Fake` so this can't regress in the future. Fixes post-publish breakage in the tree. --- .../plugin_platform_interface/CHANGELOG.md | 4 ++++ .../lib/plugin_platform_interface.dart | 20 ++++++++++++------- .../plugin_platform_interface/pubspec.yaml | 2 +- .../test/plugin_platform_interface_test.dart | 10 ++++++++++ 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/packages/plugin_platform_interface/CHANGELOG.md b/packages/plugin_platform_interface/CHANGELOG.md index 950da032e10f..ac6f24710fc7 100644 --- a/packages/plugin_platform_interface/CHANGELOG.md +++ b/packages/plugin_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.1 + +* Fixes `verify` to work with fake objects, not just mocks. + ## 2.1.0 * Introduce `verify`, which prevents use of `const Object()` as instance token. diff --git a/packages/plugin_platform_interface/lib/plugin_platform_interface.dart b/packages/plugin_platform_interface/lib/plugin_platform_interface.dart index f12fd3c39cc7..a03c9ce2d367 100644 --- a/packages/plugin_platform_interface/lib/plugin_platform_interface.dart +++ b/packages/plugin_platform_interface/lib/plugin_platform_interface.dart @@ -60,10 +60,7 @@ abstract class PlatformInterface { /// This is implemented as a static method so that it cannot be overridden /// with `noSuchMethod`. static void verify(PlatformInterface instance, Object token) { - if (identical(instance._instanceToken, const Object())) { - throw AssertionError('`const Object()` cannot be used as the token.'); - } - _verify(instance, token); + _verify(instance, token, preventConstObject: true); } /// Performs the same checks as `verify` but without throwing an @@ -71,10 +68,14 @@ abstract class PlatformInterface { /// /// This method will be deprecated in a future release. static void verifyToken(PlatformInterface instance, Object token) { - _verify(instance, token); + _verify(instance, token, preventConstObject: false); } - static void _verify(PlatformInterface instance, Object token) { + static void _verify( + PlatformInterface instance, + Object token, { + required bool preventConstObject, + }) { if (instance is MockPlatformInterfaceMixin) { bool assertionsEnabled = false; assert(() { @@ -87,6 +88,10 @@ abstract class PlatformInterface { } return; } + if (preventConstObject && + identical(instance._instanceToken, const Object())) { + throw AssertionError('`const Object()` cannot be used as the token.'); + } if (!identical(token, instance._instanceToken)) { throw AssertionError( 'Platform interfaces must not be implemented with `implements`'); @@ -94,7 +99,8 @@ abstract class PlatformInterface { } } -/// A [PlatformInterface] mixin that can be combined with mockito's `Mock`. +/// A [PlatformInterface] mixin that can be combined with fake or mock objects, +/// such as test's `Fake` or mockito's `Mock`. /// /// It passes the [PlatformInterface.verify] check even though it isn't /// using `extends`. diff --git a/packages/plugin_platform_interface/pubspec.yaml b/packages/plugin_platform_interface/pubspec.yaml index 0559b8daee8f..fdb1e4c53c24 100644 --- a/packages/plugin_platform_interface/pubspec.yaml +++ b/packages/plugin_platform_interface/pubspec.yaml @@ -15,7 +15,7 @@ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+ # be done when absolutely necessary and after the ecosystem has already migrated to 2.X.Y version # that is forward compatible with 3.0.0 (ideally the ecosystem have migrated to depend on: # `plugin_platform_interface: >=2.X.Y <4.0.0`). -version: 2.1.0 +version: 2.1.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart b/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart index a00462ffad50..8775873770aa 100644 --- a/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart +++ b/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart @@ -24,6 +24,10 @@ class ImplementsSamplePluginPlatformUsingMockPlatformInterfaceMixin extends Mock with MockPlatformInterfaceMixin implements SamplePluginPlatform {} +class ImplementsSamplePluginPlatformUsingFakePlatformInterfaceMixin extends Fake + with MockPlatformInterfaceMixin + implements SamplePluginPlatform {} + class ExtendsSamplePluginPlatform extends SamplePluginPlatform {} class ConstTokenPluginPlatform extends PlatformInterface { @@ -73,6 +77,12 @@ void main() { SamplePluginPlatform.instance = mock; }); + test('allows faking with `implements`', () { + final SamplePluginPlatform fake = + ImplementsSamplePluginPlatformUsingFakePlatformInterfaceMixin(); + SamplePluginPlatform.instance = fake; + }); + test('allows extending', () { SamplePluginPlatform.instance = ExtendsSamplePluginPlatform(); }); From 9934a40e87d7310fe7fd0115bef3a5953bab81de Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 7 Jan 2022 12:06:22 -0800 Subject: [PATCH 086/600] Swap the mirroring order. (#4650) (#4654) This is to make main the default branch. Co-authored-by: godofredoc --- .github/workflows/mirror.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/mirror.yml b/.github/workflows/mirror.yml index c9780f7bec20..9026643d7b2e 100644 --- a/.github/workflows/mirror.yml +++ b/.github/workflows/mirror.yml @@ -6,7 +6,7 @@ on: push: branches: - - 'master' + - 'main' jobs: mirror_job: @@ -14,12 +14,12 @@ jobs: pull-requests: write runs-on: ubuntu-latest if: ${{ github.repository == 'flutter/plugins' }} - name: Mirror master branch to main branch + name: Mirror main branch to master branch steps: - name: Mirror action step id: mirror uses: google/mirror-branch-action@c6b07e441a7ffc5ae15860c1d0a8107a3a151db8 with: github-token: ${{ secrets.FLUTTERMIRRORINGBOT_TOKEN }} - source: 'master' - dest: 'main' + source: 'main' + dest: 'master' From b44b979f8bf9fe325a8a0877873824a090d72579 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 7 Jan 2022 16:00:21 -0800 Subject: [PATCH 087/600] [plugin_platform_interface] Update README and add a test (#4652) --- .../plugin_platform_interface/CHANGELOG.md | 6 ++++++ packages/plugin_platform_interface/README.md | 13 ++++++------ .../plugin_platform_interface/pubspec.yaml | 2 +- .../test/plugin_platform_interface_test.dart | 20 +++++++++++++++++++ 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/packages/plugin_platform_interface/CHANGELOG.md b/packages/plugin_platform_interface/CHANGELOG.md index ac6f24710fc7..4f4984208c9b 100644 --- a/packages/plugin_platform_interface/CHANGELOG.md +++ b/packages/plugin_platform_interface/CHANGELOG.md @@ -1,3 +1,9 @@ +## 2.1.2 + +* Updates README to demonstrate `verify` rather than `verifyToken`, and to note + that the test mixin applies to fakes as well as mocks. +* Adds an additional test for `verifyToken`. + ## 2.1.1 * Fixes `verify` to work with fake objects, not just mocks. diff --git a/packages/plugin_platform_interface/README.md b/packages/plugin_platform_interface/README.md index 2fe44328c7dc..1b1f80425f76 100644 --- a/packages/plugin_platform_interface/README.md +++ b/packages/plugin_platform_interface/README.md @@ -25,7 +25,7 @@ abstract class UrlLauncherPlatform extends PlatformInterface { /// Platform-specific plugins should set this with their own platform-specific /// class that extends [UrlLauncherPlatform] when they register themselves. static set instance(UrlLauncherPlatform instance) { - PlatformInterface.verifyToken(instance, _token); + PlatformInterface.verify(instance, _token); _instance = instance; } @@ -35,14 +35,15 @@ abstract class UrlLauncherPlatform extends PlatformInterface { This guarantees that UrlLauncherPlatform.instance cannot be set to an object that `implements` UrlLauncherPlatform (it can only be set to an object that `extends` UrlLauncherPlatform). -## Mocking platform interfaces with Mockito +## Mocking or faking platform interfaces -Mockito mocks of platform interfaces will fail the verification done by `verifyToken`. -This package provides a `MockPlatformInterfaceMixin` which can be used in test code only to disable -the `extends` enforcement. +Test implementations of platform interfaces, such as those using `mockito`'s +`Mock` or `test`'s `Fake`, will fail the verification done by `verify`. +This package provides a `MockPlatformInterfaceMixin` which can be used in test +code only to disable the `extends` enforcement. -A Mockito mock of a platform interface can be created with: +For example, a Mockito mock of a platform interface can be created with: ```dart class UrlLauncherPlatformMock extends Mock diff --git a/packages/plugin_platform_interface/pubspec.yaml b/packages/plugin_platform_interface/pubspec.yaml index fdb1e4c53c24..e73d5207d4b4 100644 --- a/packages/plugin_platform_interface/pubspec.yaml +++ b/packages/plugin_platform_interface/pubspec.yaml @@ -15,7 +15,7 @@ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+ # be done when absolutely necessary and after the ecosystem has already migrated to 2.X.Y version # that is forward compatible with 3.0.0 (ideally the ecosystem have migrated to depend on: # `plugin_platform_interface: >=2.X.Y <4.0.0`). -version: 2.1.1 +version: 2.1.2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart b/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart index 8775873770aa..d36f21c806eb 100644 --- a/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart +++ b/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart @@ -63,6 +63,21 @@ class ImplementsVerifyTokenPluginPlatformUsingMockPlatformInterfaceMixin class ExtendsVerifyTokenPluginPlatform extends VerifyTokenPluginPlatform {} +class ConstVerifyTokenPluginPlatform extends PlatformInterface { + ConstVerifyTokenPluginPlatform() : super(token: _token); + + static const Object _token = Object(); // invalid + + static set instance(ConstVerifyTokenPluginPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + } +} + +class ImplementsConstVerifyTokenPluginPlatform extends PlatformInterface + implements ConstVerifyTokenPluginPlatform { + ImplementsConstVerifyTokenPluginPlatform() : super(token: const Object()); +} + void main() { group('`verify`', () { test('prevents implementation with `implements`', () { @@ -112,5 +127,10 @@ void main() { test('allows extending', () { VerifyTokenPluginPlatform.instance = ExtendsVerifyTokenPluginPlatform(); }); + + test('does not prevent `const Object()` token', () { + ConstVerifyTokenPluginPlatform.instance = + ImplementsConstVerifyTokenPluginPlatform(); + }); }); } From 9aca084f4e02b8a887130e3a8cced87fb218cbf0 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Sat, 8 Jan 2022 11:50:10 -0500 Subject: [PATCH 088/600] [ci] Switch the fetch head to 'main' (#4653) The default branch is now `main`, so that should be the default base revision for the tooling to compare against. --- .cirrus.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index 024ce5914dd7..bbaa39e5fdf6 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -9,7 +9,7 @@ env: tool_setup_template: &TOOL_SETUP_TEMPLATE tool_setup_script: - - git fetch origin master # To set FETCH_HEAD for "git merge-base" to work + - git fetch origin main # To set FETCH_HEAD for "git merge-base" to work - cd script/tool - dart pub get From a0b7daa5ec1f168342bcc36132bd06f3f6a60432 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 10 Jan 2022 13:42:40 -0500 Subject: [PATCH 089/600] Remove dependencies on 'meta' (#4647) Flutter re-exports everything from meta that we actually use, and all plugins by definition require Flutter, so there's no need to use `meta` instead of Flutter to access common annotations (e.g., immutable, visibleForTesting). This removes all use of `meta`, as well as dependencies on the package, from all plugins. Fixes https://github.com/flutter/flutter/issues/95658 --- packages/camera/camera_platform_interface/CHANGELOG.md | 4 ++++ .../lib/src/events/camera_event.dart | 2 +- .../lib/src/events/device_event.dart | 2 +- .../lib/src/method_channel/method_channel_camera.dart | 1 - .../lib/src/types/camera_description.dart | 1 - packages/camera/camera_platform_interface/pubspec.yaml | 3 +-- .../file_selector_platform_interface/CHANGELOG.md | 4 ++++ .../src/method_channel/method_channel_file_selector.dart | 2 +- .../file_selector_platform_interface/pubspec.yaml | 3 +-- packages/file_selector/file_selector_web/CHANGELOG.md | 3 ++- .../file_selector_web/lib/file_selector_web.dart | 2 +- .../file_selector/file_selector_web/lib/src/dom_helper.dart | 2 +- packages/file_selector/file_selector_web/pubspec.yaml | 3 +-- .../google_maps_flutter_platform_interface/CHANGELOG.md | 4 ++++ .../lib/src/types/cap.dart | 2 +- .../lib/src/types/circle.dart | 2 +- .../lib/src/types/joint_type.dart | 2 +- .../lib/src/types/location.dart | 2 +- .../lib/src/types/maps_object.dart | 3 +-- .../lib/src/types/marker.dart | 4 ++-- .../lib/src/types/pattern_item.dart | 2 +- .../lib/src/types/polygon.dart | 4 ++-- .../lib/src/types/polyline.dart | 4 ++-- .../lib/src/types/screen_coordinate.dart | 2 +- .../lib/src/types/tile.dart | 2 +- .../lib/src/types/tile_overlay.dart | 3 +-- .../google_maps_flutter_platform_interface/pubspec.yaml | 3 +-- .../google_maps_flutter_web/CHANGELOG.md | 6 +++++- .../google_maps_flutter_web/pubspec.yaml | 3 +-- packages/google_sign_in/google_sign_in/CHANGELOG.md | 3 ++- packages/google_sign_in/google_sign_in/pubspec.yaml | 3 +-- .../google_sign_in_platform_interface/CHANGELOG.md | 4 ++++ .../lib/google_sign_in_platform_interface.dart | 4 +++- .../lib/src/method_channel_google_sign_in.dart | 2 +- .../google_sign_in_platform_interface/pubspec.yaml | 3 +-- packages/google_sign_in/google_sign_in_web/CHANGELOG.md | 4 ++++ .../google_sign_in_web/lib/google_sign_in_web.dart | 2 +- .../google_sign_in_web/lib/src/load_gapi.dart | 2 +- packages/google_sign_in/google_sign_in_web/pubspec.yaml | 3 +-- packages/image_picker/image_picker_for_web/CHANGELOG.md | 4 ++++ .../image_picker_for_web/lib/image_picker_for_web.dart | 2 +- packages/image_picker/image_picker_for_web/pubspec.yaml | 3 +-- .../image_picker_platform_interface/CHANGELOG.md | 4 ++++ .../lib/src/method_channel/method_channel_image_picker.dart | 1 - .../lib/src/types/picked_file/base.dart | 2 +- .../image_picker_platform_interface/pubspec.yaml | 3 +-- .../in_app_purchase/in_app_purchase_android/CHANGELOG.md | 4 ++++ .../in_app_purchase/in_app_purchase_android/pubspec.yaml | 3 +-- .../in_app_purchase/in_app_purchase_storekit/CHANGELOG.md | 6 +++++- .../src/store_kit_wrappers/sk_payment_queue_wrapper.dart | 1 - .../in_app_purchase/in_app_purchase_storekit/pubspec.yaml | 3 +-- packages/local_auth/CHANGELOG.md | 4 ++++ packages/local_auth/lib/local_auth.dart | 2 +- packages/local_auth/pubspec.yaml | 3 +-- packages/path_provider/path_provider_linux/CHANGELOG.md | 4 ++++ .../lib/src/get_application_id_real.dart | 2 +- packages/path_provider/path_provider_linux/pubspec.yaml | 3 +-- packages/path_provider/path_provider_macos/CHANGELOG.md | 4 ++++ .../path_provider_macos/lib/path_provider_macos.dart | 2 +- packages/path_provider/path_provider_macos/pubspec.yaml | 3 +-- .../path_provider_platform_interface/CHANGELOG.md | 4 ++++ .../lib/src/method_channel_path_provider.dart | 2 +- .../path_provider_platform_interface/pubspec.yaml | 3 +-- packages/path_provider/path_provider_windows/CHANGELOG.md | 4 ++++ .../lib/src/path_provider_windows_real.dart | 2 +- packages/path_provider/path_provider_windows/pubspec.yaml | 3 +-- packages/quick_actions/quick_actions/CHANGELOG.md | 3 ++- packages/quick_actions/quick_actions/pubspec.yaml | 3 +-- .../quick_actions_platform_interface/CHANGELOG.md | 4 ++++ .../lib/method_channel/method_channel_quick_actions.dart | 1 - .../quick_actions_platform_interface/pubspec.yaml | 3 +-- packages/shared_preferences/shared_preferences/CHANGELOG.md | 4 ++++ .../shared_preferences/lib/shared_preferences.dart | 2 +- packages/shared_preferences/shared_preferences/pubspec.yaml | 3 +-- .../shared_preferences_android/CHANGELOG.md | 4 ++++ .../shared_preferences_android/pubspec.yaml | 3 +-- .../shared_preferences/shared_preferences_ios/CHANGELOG.md | 4 ++++ .../shared_preferences/shared_preferences_ios/pubspec.yaml | 3 +-- .../shared_preferences_linux/CHANGELOG.md | 4 ++++ .../lib/shared_preferences_linux.dart | 2 +- .../shared_preferences_linux/pubspec.yaml | 3 +-- .../shared_preferences/shared_preferences_web/CHANGELOG.md | 3 ++- .../shared_preferences/shared_preferences_web/pubspec.yaml | 3 +-- .../shared_preferences_windows/CHANGELOG.md | 4 ++++ .../lib/shared_preferences_windows.dart | 2 +- .../shared_preferences_windows/pubspec.yaml | 3 +-- packages/url_launcher/url_launcher/CHANGELOG.md | 4 ++++ packages/url_launcher/url_launcher/lib/src/link.dart | 1 - packages/url_launcher/url_launcher/pubspec.yaml | 3 +-- packages/url_launcher/url_launcher_android/CHANGELOG.md | 3 ++- packages/url_launcher/url_launcher_android/pubspec.yaml | 3 +-- packages/url_launcher/url_launcher_ios/CHANGELOG.md | 3 ++- packages/url_launcher/url_launcher_ios/pubspec.yaml | 3 +-- packages/url_launcher/url_launcher_web/CHANGELOG.md | 4 ++++ .../url_launcher/url_launcher_web/lib/url_launcher_web.dart | 2 +- packages/url_launcher/url_launcher_web/pubspec.yaml | 3 +-- packages/video_player/video_player/CHANGELOG.md | 4 ++++ packages/video_player/video_player/lib/video_player.dart | 1 - packages/video_player/video_player/pubspec.yaml | 3 +-- packages/video_player/video_player_web/CHANGELOG.md | 4 ++++ packages/video_player/video_player_web/pubspec.yaml | 3 +-- 101 files changed, 182 insertions(+), 114 deletions(-) diff --git a/packages/camera/camera_platform_interface/CHANGELOG.md b/packages/camera/camera_platform_interface/CHANGELOG.md index 04149913fbf5..35a39e1a0568 100644 --- a/packages/camera/camera_platform_interface/CHANGELOG.md +++ b/packages/camera/camera_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.4 + +* Removes dependency on `meta`. + ## 2.1.3 * Update to use the `verify` method introduced in platform_plugin_interface 2.1.0. diff --git a/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart b/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart index dfd851ec4ab3..a91e538e4be5 100644 --- a/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart +++ b/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart @@ -3,7 +3,7 @@ // found in the LICENSE file. import 'package:camera_platform_interface/src/types/focus_mode.dart'; -import 'package:meta/meta.dart'; +import 'package:flutter/foundation.dart' show immutable; import '../../camera_platform_interface.dart'; diff --git a/packages/camera/camera_platform_interface/lib/src/events/device_event.dart b/packages/camera/camera_platform_interface/lib/src/events/device_event.dart index 8a5b5f896149..d6bb5df05980 100644 --- a/packages/camera/camera_platform_interface/lib/src/events/device_event.dart +++ b/packages/camera/camera_platform_interface/lib/src/events/device_event.dart @@ -3,8 +3,8 @@ // found in the LICENSE file. import 'package:camera_platform_interface/src/utils/utils.dart'; +import 'package:flutter/foundation.dart' show immutable; import 'package:flutter/services.dart'; -import 'package:meta/meta.dart'; /// Generic Event coming from the native side of Camera, /// not related to a specific camera module. diff --git a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart index 0ba1373a991a..ab6676939aaa 100644 --- a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart +++ b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart @@ -14,7 +14,6 @@ import 'package:cross_file/cross_file.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; -import 'package:meta/meta.dart'; import 'package:stream_transform/stream_transform.dart'; const MethodChannel _channel = MethodChannel('plugins.flutter.io/camera'); diff --git a/packages/camera/camera_platform_interface/lib/src/types/camera_description.dart b/packages/camera/camera_platform_interface/lib/src/types/camera_description.dart index 8596c90ba91f..df7263631252 100644 --- a/packages/camera/camera_platform_interface/lib/src/types/camera_description.dart +++ b/packages/camera/camera_platform_interface/lib/src/types/camera_description.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'package:flutter/foundation.dart'; -import 'package:meta/meta.dart'; /// The direction the camera is facing. enum CameraLensDirection { diff --git a/packages/camera/camera_platform_interface/pubspec.yaml b/packages/camera/camera_platform_interface/pubspec.yaml index 3578fc423da9..cff5add8b2d3 100644 --- a/packages/camera/camera_platform_interface/pubspec.yaml +++ b/packages/camera/camera_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/camera/camer issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.1.3 +version: 2.1.4 environment: sdk: '>=2.12.0 <3.0.0' @@ -14,7 +14,6 @@ dependencies: cross_file: ^0.3.1 flutter: sdk: flutter - meta: ^1.3.0 plugin_platform_interface: ^2.1.0 stream_transform: ^2.0.0 diff --git a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md index b71bbb6b7bd1..b633bd35a59e 100644 --- a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md +++ b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.4 + +* Removes dependency on `meta`. + ## 2.0.3 * Minor code cleanup for new analysis rules. diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart index 28ec41db6dde..f1fa82b6f3c6 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart @@ -4,8 +4,8 @@ import 'package:cross_file/cross_file.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter/services.dart'; -import 'package:meta/meta.dart'; const MethodChannel _channel = MethodChannel('plugins.flutter.io/file_selector'); diff --git a/packages/file_selector/file_selector_platform_interface/pubspec.yaml b/packages/file_selector/file_selector_platform_interface/pubspec.yaml index 3a00e9905a03..f7b66a763b9a 100644 --- a/packages/file_selector/file_selector_platform_interface/pubspec.yaml +++ b/packages/file_selector/file_selector_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/file_selecto issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.0.3 +version: 2.0.4 environment: sdk: ">=2.12.0 <3.0.0" @@ -15,7 +15,6 @@ dependencies: flutter: sdk: flutter http: ^0.13.0 - meta: ^1.3.0 plugin_platform_interface: ^2.1.0 dev_dependencies: diff --git a/packages/file_selector/file_selector_web/CHANGELOG.md b/packages/file_selector/file_selector_web/CHANGELOG.md index dabd7173868c..5927239ef9e3 100644 --- a/packages/file_selector/file_selector_web/CHANGELOG.md +++ b/packages/file_selector/file_selector_web/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 0.8.1+3 * Minor code cleanup for new analysis rules. +* Removes dependency on `meta`. ## 0.8.1+2 diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 8f4ca202593e..915a2a806496 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -7,8 +7,8 @@ import 'dart:async'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:file_selector_web/src/dom_helper.dart'; import 'package:file_selector_web/src/utils.dart'; +import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; -import 'package:meta/meta.dart'; /// The web implementation of [FileSelectorPlatform]. /// diff --git a/packages/file_selector/file_selector_web/lib/src/dom_helper.dart b/packages/file_selector/file_selector_web/lib/src/dom_helper.dart index 06c13d968484..1c3442f8dab5 100644 --- a/packages/file_selector/file_selector_web/lib/src/dom_helper.dart +++ b/packages/file_selector/file_selector_web/lib/src/dom_helper.dart @@ -6,8 +6,8 @@ import 'dart:async'; import 'dart:html'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter/services.dart'; -import 'package:meta/meta.dart'; /// Class to manipulate the DOM with the intention of reading files from it. class DomHelper { diff --git a/packages/file_selector/file_selector_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml index bbad45bf2d6b..9cb8824afafd 100644 --- a/packages/file_selector/file_selector_web/pubspec.yaml +++ b/packages/file_selector/file_selector_web/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_web description: Web platform implementation of file_selector repository: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.1+2 +version: 0.8.1+3 environment: sdk: ">=2.12.0 <3.0.0" @@ -22,7 +22,6 @@ dependencies: sdk: flutter flutter_web_plugins: sdk: flutter - meta: ^1.3.0 dev_dependencies: flutter_test: diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md index f426171a9942..d1b4ef8c18cf 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.5 + +Removes dependency on `meta`. + ## 2.1.4 * Update to use the `verify` method introduced in plugin_platform_interface 2.1.0. diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/cap.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/cap.dart index f5f43209d828..5bef7baf0bf4 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/cap.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/cap.dart @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:meta/meta.dart' show immutable; +import 'package:flutter/foundation.dart' show immutable; import 'types.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/circle.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/circle.dart index 1845195b31c6..1e9b2181778e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/circle.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/circle.dart @@ -4,7 +4,7 @@ import 'package:flutter/foundation.dart' show VoidCallback; import 'package:flutter/material.dart' show Color, Colors; -import 'package:meta/meta.dart' show immutable; +import 'package:flutter/foundation.dart' show immutable; import 'types.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/joint_type.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/joint_type.dart index 64e7a3d8cbdc..b986025b27a6 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/joint_type.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/joint_type.dart @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:meta/meta.dart' show immutable; +import 'package:flutter/foundation.dart' show immutable; /// Joint types for [Polyline]. @immutable diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/location.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/location.dart index 7a1aaf051388..e2ca635be75e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/location.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/location.dart @@ -4,7 +4,7 @@ import 'dart:ui' show hashValues; -import 'package:meta/meta.dart'; +import 'package:flutter/foundation.dart' show visibleForTesting; /// A pair of latitude and longitude coordinates, stored as degrees. class LatLng { diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/maps_object.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/maps_object.dart index 77d958be01e2..be629e174143 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/maps_object.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/maps_object.dart @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:flutter/foundation.dart' show objectRuntimeType; -import 'package:meta/meta.dart' show immutable; +import 'package:flutter/foundation.dart' show immutable, objectRuntimeType; /// Uniquely identifies object an among [GoogleMap] collections of a specific /// type. diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/marker.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/marker.dart index 52255f84f4cc..5e62f7d9ffba 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/marker.dart @@ -4,8 +4,8 @@ import 'dart:ui' show hashValues, Offset; -import 'package:flutter/foundation.dart' show ValueChanged, VoidCallback; -import 'package:meta/meta.dart' show immutable; +import 'package:flutter/foundation.dart' + show immutable, ValueChanged, VoidCallback; import 'types.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/pattern_item.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/pattern_item.dart index 89f29d25e4cc..033210b8c5ae 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/pattern_item.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/pattern_item.dart @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:meta/meta.dart' show immutable; +import 'package:flutter/foundation.dart' show immutable; /// Item used in the stroke pattern for a Polyline. @immutable diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/polygon.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/polygon.dart index 569bd4c1f553..7b6f24831e59 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/polygon.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/polygon.dart @@ -3,9 +3,9 @@ // found in the LICENSE file. import 'package:collection/collection.dart'; -import 'package:flutter/foundation.dart' show listEquals, VoidCallback; +import 'package:flutter/foundation.dart' + show immutable, listEquals, VoidCallback; import 'package:flutter/material.dart' show Color, Colors; -import 'package:meta/meta.dart' show immutable; import 'types.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/polyline.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/polyline.dart index c324aeb5f492..00c718646229 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/polyline.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/polyline.dart @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:flutter/foundation.dart' show listEquals, VoidCallback; +import 'package:flutter/foundation.dart' + show immutable, listEquals, VoidCallback; import 'package:flutter/material.dart' show Color, Colors; -import 'package:meta/meta.dart' show immutable; import 'types.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/screen_coordinate.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/screen_coordinate.dart index 8c9c083913ce..46bc6c12e509 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/screen_coordinate.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/screen_coordinate.dart @@ -4,7 +4,7 @@ import 'dart:ui' show hashValues; -import 'package:meta/meta.dart' show immutable; +import 'package:flutter/foundation.dart' show immutable; /// Represents a point coordinate in the [GoogleMap]'s view. /// diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/tile.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/tile.dart index d602b127f06c..d73701511059 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/tile.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/tile.dart @@ -3,7 +3,7 @@ // found in the LICENSE file. import 'dart:typed_data'; -import 'package:meta/meta.dart' show immutable; +import 'package:flutter/foundation.dart' show immutable; /// Contains information about a Tile that is returned by a [TileProvider]. @immutable diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/tile_overlay.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/tile_overlay.dart index 8cdd2c4699e1..db2b53d63512 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/tile_overlay.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/tile_overlay.dart @@ -4,8 +4,7 @@ import 'dart:ui' show hashValues; -import 'package:flutter/foundation.dart'; -import 'package:meta/meta.dart' show immutable; +import 'package:flutter/foundation.dart' show immutable; import 'types.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml index 5d527c526757..687640c0797b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/google_maps_ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.1.4 +version: 2.1.5 environment: sdk: '>=2.12.0 <3.0.0' @@ -14,7 +14,6 @@ dependencies: collection: ^1.15.0 flutter: sdk: flutter - meta: ^1.3.0 plugin_platform_interface: ^2.1.0 stream_transform: ^2.0.0 diff --git a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md index b2fe086f5c9d..8a3b94151de2 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md @@ -1,6 +1,10 @@ +## 0.3.2+1 + +* Removes dependency on `meta`. + ## 0.3.2 -Add `onDragStart` and `onDrag` to `Marker` +* Add `onDragStart` and `onDrag` to `Marker` ## 0.3.1 diff --git a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml index 080726ee6d85..c0c27433bea8 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter_web description: Web platform implementation of google_maps_flutter repository: https://github.com/flutter/plugins/tree/master/packages/google_maps_flutter/google_maps_flutter_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 0.3.2 +version: 0.3.2+1 environment: sdk: ">=2.12.0 <3.0.0" @@ -23,7 +23,6 @@ dependencies: sdk: flutter google_maps_flutter_platform_interface: ^2.1.2 google_maps: ^5.2.0 - meta: ^1.3.0 pedantic: ^1.10.0 sanitize_html: ^2.0.0 stream_transform: ^2.0.0 diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index 7ff50a4bb24a..3c2dfbe5a7be 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 5.2.2 * Updates Android compileSdkVersion to 31. +* Removes depenedncy on `meta`. ## 5.2.1 diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index 03e583a662d8..fd47802f1bad 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Google Sign-In, a secure authentication system for signing in with a Google account on Android and iOS. repository: https://github.com/flutter/plugins/tree/master/packages/google_sign_in/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.2.1 +version: 5.2.2 environment: sdk: ">=2.14.0 <3.0.0" @@ -25,7 +25,6 @@ dependencies: sdk: flutter google_sign_in_platform_interface: ^2.1.0 google_sign_in_web: ^0.10.0 - meta: ^1.3.0 dev_dependencies: flutter_driver: diff --git a/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md b/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md index 917864173f7d..928186029a48 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.1 + +* Removes dependency on `meta`. + ## 2.1.0 * Add serverAuthCode attribute to user data diff --git a/packages/google_sign_in/google_sign_in_platform_interface/lib/google_sign_in_platform_interface.dart b/packages/google_sign_in/google_sign_in_platform_interface/lib/google_sign_in_platform_interface.dart index 42038879e90b..50f261bfa578 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/lib/google_sign_in_platform_interface.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/lib/google_sign_in_platform_interface.dart @@ -3,7 +3,9 @@ // found in the LICENSE file. import 'dart:async'; -import 'package:meta/meta.dart' show visibleForTesting; + +import 'package:flutter/foundation.dart' show visibleForTesting; + import 'src/method_channel_google_sign_in.dart'; import 'src/types.dart'; diff --git a/packages/google_sign_in/google_sign_in_platform_interface/lib/src/method_channel_google_sign_in.dart b/packages/google_sign_in/google_sign_in_platform_interface/lib/src/method_channel_google_sign_in.dart index 23c35ac240b9..c569133a1b34 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/lib/src/method_channel_google_sign_in.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/lib/src/method_channel_google_sign_in.dart @@ -4,8 +4,8 @@ import 'dart:async'; +import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter/services.dart'; -import 'package:meta/meta.dart' show visibleForTesting; import '../google_sign_in_platform_interface.dart'; import 'types.dart'; diff --git a/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml b/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml index aa41039cdf5a..fcf83bc34d5c 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/google_sign_ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.1.0 +version: 2.1.1 environment: sdk: ">=2.12.0 <3.0.0" @@ -13,7 +13,6 @@ environment: dependencies: flutter: sdk: flutter - meta: ^1.3.0 quiver: ^3.0.0 dev_dependencies: diff --git a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md index 556f69524026..e65ce0fb23ba 100644 --- a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.10.0+4 + +* Removes dependency on `meta`. + ## 0.10.0+3 * Updated URL to the `google_sign_in` package in README. diff --git a/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart b/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart index f40b42b1881e..ff0d8e4cea89 100644 --- a/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart +++ b/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart @@ -5,11 +5,11 @@ import 'dart:async'; import 'dart:html' as html; +import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter/services.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; import 'package:js/js.dart'; -import 'package:meta/meta.dart'; import 'src/generated/gapiauth2.dart' as auth2; import 'src/load_gapi.dart' as gapi; diff --git a/packages/google_sign_in/google_sign_in_web/lib/src/load_gapi.dart b/packages/google_sign_in/google_sign_in_web/lib/src/load_gapi.dart index 6d8c566f0412..621e1673d636 100644 --- a/packages/google_sign_in/google_sign_in_web/lib/src/load_gapi.dart +++ b/packages/google_sign_in/google_sign_in_web/lib/src/load_gapi.dart @@ -8,7 +8,7 @@ library gapi_onload; import 'dart:async'; import 'package:js/js.dart'; -import 'package:meta/meta.dart'; +import 'package:flutter/foundation.dart' show visibleForTesting; import 'generated/gapi.dart' as gapi; import 'utils.dart' show injectJSLibraries; diff --git a/packages/google_sign_in/google_sign_in_web/pubspec.yaml b/packages/google_sign_in/google_sign_in_web/pubspec.yaml index 7388ea138883..ec61f30a77fb 100644 --- a/packages/google_sign_in/google_sign_in_web/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_web/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Google Sign-In, a secure authentication system for signing in with a Google account on Android, iOS and Web. repository: https://github.com/flutter/plugins/tree/master/packages/google_sign_in/google_sign_in_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 0.10.0+3 +version: 0.10.0+4 environment: sdk: ">=2.12.0 <3.0.0" @@ -24,7 +24,6 @@ dependencies: sdk: flutter google_sign_in_platform_interface: ^2.0.0 js: ^0.6.3 - meta: ^1.3.0 pedantic: ^1.10.0 dev_dependencies: diff --git a/packages/image_picker/image_picker_for_web/CHANGELOG.md b/packages/image_picker/image_picker_for_web/CHANGELOG.md index aee8b0863c13..78629a4d252d 100644 --- a/packages/image_picker/image_picker_for_web/CHANGELOG.md +++ b/packages/image_picker/image_picker_for_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.5 + +* Removes dependency on `meta`. + ## 2.1.4 * Implemented `maxWidth`, `maxHeight` and `imageQuality` when selecting images diff --git a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart index f13aeb1f0a4e..132087576c1d 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart @@ -5,9 +5,9 @@ import 'dart:async'; import 'dart:html' as html; +import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:image_picker_for_web/src/image_resizer.dart'; -import 'package:meta/meta.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; final String _kImagePickerInputsDomId = '__image_picker_web-file-input'; diff --git a/packages/image_picker/image_picker_for_web/pubspec.yaml b/packages/image_picker/image_picker_for_web/pubspec.yaml index a104dce66fe9..4a50a22af653 100644 --- a/packages/image_picker/image_picker_for_web/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_for_web description: Web platform implementation of image_picker repository: https://github.com/flutter/plugins/tree/master/packages/image_picker/image_picker_for_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 2.1.4 +version: 2.1.5 environment: sdk: ">=2.12.0 <3.0.0" @@ -22,7 +22,6 @@ dependencies: flutter_web_plugins: sdk: flutter image_picker_platform_interface: ^2.2.0 - meta: ^1.3.0 pedantic: ^1.10.0 dev_dependencies: diff --git a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md index 447204b2163e..8f80610f307a 100644 --- a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md +++ b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.4.3 + +* Removes dependency on `meta`. + ## 2.4.2 * Update to use the `verify` method introduced in plugin_platform_interface 2.1.0. diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart index b02284e957fa..3731c5626ea2 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart @@ -6,7 +6,6 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; -import 'package:meta/meta.dart' show visibleForTesting; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart index de259e0611dd..d63bc6aad993 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart @@ -5,7 +5,7 @@ import 'dart:convert'; import 'dart:typed_data'; -import 'package:meta/meta.dart'; +import 'package:flutter/foundation.dart' show immutable; /// The interface for a PickedFile. /// diff --git a/packages/image_picker/image_picker_platform_interface/pubspec.yaml b/packages/image_picker/image_picker_platform_interface/pubspec.yaml index f4cdb676cce1..16b64ee86cf1 100644 --- a/packages/image_picker/image_picker_platform_interface/pubspec.yaml +++ b/packages/image_picker/image_picker_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.4.2 +version: 2.4.3 environment: sdk: ">=2.12.0 <3.0.0" @@ -14,7 +14,6 @@ dependencies: flutter: sdk: flutter http: ^0.13.0 - meta: ^1.3.0 plugin_platform_interface: ^2.1.0 cross_file: ^0.3.1+1 diff --git a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md index 5c34de267737..ff0ce6e28243 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.2+1 + +* Removes the dependency on `meta`. + ## 0.2.2 * Fixes the `purchaseStream` incorrectly reporting `PurchaseStatus.error` when user upgrades subscription by deferred proration mode. diff --git a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml index 4d8864357b57..bd26abe8248e 100644 --- a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_android description: An implementation for the Android platform of the Flutter `in_app_purchase` plugin. This uses the Android BillingClient APIs. repository: https://github.com/flutter/plugins/tree/master/packages/in_app_purchase/in_app_purchase_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.2.2 +version: 0.2.2+1 environment: sdk: ">=2.14.0 <3.0.0" @@ -22,7 +22,6 @@ dependencies: sdk: flutter in_app_purchase_platform_interface: ^1.3.0 json_annotation: ^4.3.0 - meta: ^1.3.0 dev_dependencies: build_runner: ^2.0.0 diff --git a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md index e565812bf6ee..fdc16d27945a 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,6 +1,10 @@ +## 0.3.0+1 + +* Removes dependency on `meta`. + ## 0.3.0 -* **BREAKING CHANGE:** `InAppPurchaseStoreKitPlatform.restorePurchase()` emits an empty instance of `List` when there were no transactions to restore, indicating that the restore procedure has finished. +* **BREAKING CHANGE:** `InAppPurchaseStoreKitPlatform.restorePurchase()` emits an empty instance of `List` when there were no transactions to restore, indicating that the restore procedure has finished. ## 0.2.1 diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart index 72315a302922..d55b07ab4f10 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart @@ -10,7 +10,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:in_app_purchase_storekit/store_kit_wrappers.dart'; import 'package:json_annotation/json_annotation.dart'; -import 'package:meta/meta.dart'; import '../channel.dart'; import '../in_app_purchase_storekit_platform.dart'; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml index 1fe291d4b3f2..b4e371473fbd 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_storekit description: An implementation for the iOS platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/plugins/tree/master/packages/in_app_purchase/in_app_purchase_storekit issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.3.0 +version: 0.3.0+1 environment: sdk: ">=2.14.0 <3.0.0" @@ -21,7 +21,6 @@ dependencies: sdk: flutter in_app_purchase_platform_interface: ^1.3.0 json_annotation: ^4.3.0 - meta: ^1.3.0 dev_dependencies: build_runner: ^2.0.0 diff --git a/packages/local_auth/CHANGELOG.md b/packages/local_auth/CHANGELOG.md index 7fe3a98ed08f..d17eb4a97a21 100644 --- a/packages/local_auth/CHANGELOG.md +++ b/packages/local_auth/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.1.10 + +* Removes dependency on `meta`. + ## 1.1.9 * Updates code for analysis option changes. diff --git a/packages/local_auth/lib/local_auth.dart b/packages/local_auth/lib/local_auth.dart index af02d57a7359..3e925c00e5ae 100644 --- a/packages/local_auth/lib/local_auth.dart +++ b/packages/local_auth/lib/local_auth.dart @@ -10,8 +10,8 @@ import 'dart:async'; +import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter/services.dart'; -import 'package:meta/meta.dart'; import 'package:platform/platform.dart'; import 'auth_strings.dart'; diff --git a/packages/local_auth/pubspec.yaml b/packages/local_auth/pubspec.yaml index 9303b0c8cc74..454c8c11e86d 100644 --- a/packages/local_auth/pubspec.yaml +++ b/packages/local_auth/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Android and iOS devices to allow local authentication via fingerprint, touch ID, face ID, passcode, pin, or pattern. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.1.9 +version: 1.1.10 environment: sdk: ">=2.14.0 <3.0.0" @@ -23,7 +23,6 @@ dependencies: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.1 intl: ^0.17.0 - meta: ^1.3.0 platform: ^3.0.0 dev_dependencies: diff --git a/packages/path_provider/path_provider_linux/CHANGELOG.md b/packages/path_provider/path_provider_linux/CHANGELOG.md index cfea5702dbe1..55235c3542f9 100644 --- a/packages/path_provider/path_provider_linux/CHANGELOG.md +++ b/packages/path_provider/path_provider_linux/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.5 + +* Removes dependency on `meta`. + ## 2.1.4 * Fixes `getApplicationSupportPath` handling of applications where the diff --git a/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart b/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart index d2e60fb6ff82..f01c3e4ee15e 100644 --- a/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart +++ b/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart @@ -4,7 +4,7 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; -import 'package:meta/meta.dart'; +import 'package:flutter/foundation.dart' show visibleForTesting; // GApplication* g_application_get_default(); typedef _GApplicationGetDefaultC = IntPtr Function(); diff --git a/packages/path_provider/path_provider_linux/pubspec.yaml b/packages/path_provider/path_provider_linux/pubspec.yaml index ec1d8d8b3148..9dcf2cd9935b 100644 --- a/packages/path_provider/path_provider_linux/pubspec.yaml +++ b/packages/path_provider/path_provider_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_linux description: Linux implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.1.4 +version: 2.1.5 environment: sdk: ">=2.12.0 <3.0.0" @@ -19,7 +19,6 @@ dependencies: ffi: ^1.1.2 flutter: sdk: flutter - meta: ^1.3.0 path: ^1.8.0 path_provider_platform_interface: ^2.0.0 xdg_directories: ^0.2.0 diff --git a/packages/path_provider/path_provider_macos/CHANGELOG.md b/packages/path_provider/path_provider_macos/CHANGELOG.md index 49c205882a1b..047792f8bcc4 100644 --- a/packages/path_provider/path_provider_macos/CHANGELOG.md +++ b/packages/path_provider/path_provider_macos/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.5 + +* Removes dependency on `meta`. + ## 2.0.4 * Switches to a package-internal implementation of the platform interface. diff --git a/packages/path_provider/path_provider_macos/lib/path_provider_macos.dart b/packages/path_provider/path_provider_macos/lib/path_provider_macos.dart index 6b3a6fa81b9a..5dc3176e9b89 100644 --- a/packages/path_provider/path_provider_macos/lib/path_provider_macos.dart +++ b/packages/path_provider/path_provider_macos/lib/path_provider_macos.dart @@ -4,8 +4,8 @@ import 'dart:io'; +import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter/services.dart'; -import 'package:meta/meta.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; /// The macOS implementation of [PathProviderPlatform]. diff --git a/packages/path_provider/path_provider_macos/pubspec.yaml b/packages/path_provider/path_provider_macos/pubspec.yaml index 9eb1bb4d646b..1c88e24c1247 100644 --- a/packages/path_provider/path_provider_macos/pubspec.yaml +++ b/packages/path_provider/path_provider_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_macos description: macOS implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.4 +version: 2.0.5 environment: sdk: ">=2.12.0 <3.0.0" @@ -19,7 +19,6 @@ flutter: dependencies: flutter: sdk: flutter - meta: ^1.3.0 path_provider_platform_interface: ^2.0.1 dev_dependencies: diff --git a/packages/path_provider/path_provider_platform_interface/CHANGELOG.md b/packages/path_provider/path_provider_platform_interface/CHANGELOG.md index a1607412cdd5..8be2da70c20d 100644 --- a/packages/path_provider/path_provider_platform_interface/CHANGELOG.md +++ b/packages/path_provider/path_provider_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.3 + +* Removes dependency on `meta`. + ## 2.0.2 * Update to use the `verify` method introduced in plugin_platform_interface 2.1.0. diff --git a/packages/path_provider/path_provider_platform_interface/lib/src/method_channel_path_provider.dart b/packages/path_provider/path_provider_platform_interface/lib/src/method_channel_path_provider.dart index 007787444adb..c3e5eccbffba 100644 --- a/packages/path_provider/path_provider_platform_interface/lib/src/method_channel_path_provider.dart +++ b/packages/path_provider/path_provider_platform_interface/lib/src/method_channel_path_provider.dart @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter/services.dart'; -import 'package:meta/meta.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; import 'package:platform/platform.dart'; diff --git a/packages/path_provider/path_provider_platform_interface/pubspec.yaml b/packages/path_provider/path_provider_platform_interface/pubspec.yaml index eca47e32c8ba..8f4af6f04ba4 100644 --- a/packages/path_provider/path_provider_platform_interface/pubspec.yaml +++ b/packages/path_provider/path_provider_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/path_provide issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.0.2 +version: 2.0.3 environment: sdk: ">=2.12.0 <3.0.0" @@ -13,7 +13,6 @@ environment: dependencies: flutter: sdk: flutter - meta: ^1.3.0 platform: ^3.0.0 plugin_platform_interface: ^2.1.0 diff --git a/packages/path_provider/path_provider_windows/CHANGELOG.md b/packages/path_provider/path_provider_windows/CHANGELOG.md index d7eaeeef133d..cd33e85a851d 100644 --- a/packages/path_provider/path_provider_windows/CHANGELOG.md +++ b/packages/path_provider/path_provider_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.5 + +* Removes dependency on `meta`. + ## 2.0.4 * Removed obsolete `pluginClass: none` from pubpsec. diff --git a/packages/path_provider/path_provider_windows/lib/src/path_provider_windows_real.dart b/packages/path_provider/path_provider_windows/lib/src/path_provider_windows_real.dart index 2b87d51c1c49..bfffa848981a 100644 --- a/packages/path_provider/path_provider_windows/lib/src/path_provider_windows_real.dart +++ b/packages/path_provider/path_provider_windows/lib/src/path_provider_windows_real.dart @@ -6,7 +6,7 @@ import 'dart:ffi'; import 'dart:io'; import 'package:ffi/ffi.dart'; -import 'package:meta/meta.dart'; +import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:path/path.dart' as path; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; import 'package:win32/win32.dart'; diff --git a/packages/path_provider/path_provider_windows/pubspec.yaml b/packages/path_provider/path_provider_windows/pubspec.yaml index 78d403a8576c..598ce9e0a1df 100644 --- a/packages/path_provider/path_provider_windows/pubspec.yaml +++ b/packages/path_provider/path_provider_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_windows description: Windows implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.4 +version: 2.0.5 environment: sdk: ">=2.12.0 <3.0.0" @@ -19,7 +19,6 @@ dependencies: ffi: ^1.0.0 flutter: sdk: flutter - meta: ^1.3.0 path: ^1.8.0 path_provider_platform_interface: ^2.0.0 win32: ^2.0.0 diff --git a/packages/quick_actions/quick_actions/CHANGELOG.md b/packages/quick_actions/quick_actions/CHANGELOG.md index f684e747bf08..33e80498a63c 100644 --- a/packages/quick_actions/quick_actions/CHANGELOG.md +++ b/packages/quick_actions/quick_actions/CHANGELOG.md @@ -1,7 +1,8 @@ -## NEXT +## 0.6.0+9 * Updates Android compileSdkVersion to 31. * Updates code for analyzer changes. +* Removes dependency on `meta`. ## 0.6.0+8 diff --git a/packages/quick_actions/quick_actions/pubspec.yaml b/packages/quick_actions/quick_actions/pubspec.yaml index 137156d8d9a2..92b8edf1e8dc 100644 --- a/packages/quick_actions/quick_actions/pubspec.yaml +++ b/packages/quick_actions/quick_actions/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for creating shortcuts on home screen, also known as Quick Actions on iOS and App Shortcuts on Android. repository: https://github.com/flutter/plugins/tree/master/packages/quick_actions/quick_actions issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+quick_actions%22 -version: 0.6.0+8 +version: 0.6.0+9 environment: sdk: ">=2.14.0 <3.0.0" @@ -21,7 +21,6 @@ flutter: dependencies: flutter: sdk: flutter - meta: ^1.3.0 quick_actions_platform_interface: ^1.0.0 dev_dependencies: diff --git a/packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md b/packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md index b66fc145bf9d..ad959de03be8 100644 --- a/packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.2 + +* Removes dependency on `meta`. + ## 1.0.1 * Updates code for analyzer changes. diff --git a/packages/quick_actions/quick_actions_platform_interface/lib/method_channel/method_channel_quick_actions.dart b/packages/quick_actions/quick_actions_platform_interface/lib/method_channel/method_channel_quick_actions.dart index fa76c0f238c9..560c199ee77a 100644 --- a/packages/quick_actions/quick_actions_platform_interface/lib/method_channel/method_channel_quick_actions.dart +++ b/packages/quick_actions/quick_actions_platform_interface/lib/method_channel/method_channel_quick_actions.dart @@ -4,7 +4,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; -import 'package:meta/meta.dart' show visibleForTesting; import 'package:quick_actions_platform_interface/types/types.dart'; import '../platform_interface/quick_actions_platform.dart'; diff --git a/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml b/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml index c8ea8dfb4a29..0bf23cd5e0a7 100644 --- a/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml +++ b/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/quick_action issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+quick_actions%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 1.0.1 +version: 1.0.2 environment: sdk: ">=2.12.0 <3.0.0" @@ -13,7 +13,6 @@ environment: dependencies: flutter: sdk: flutter - meta: ^1.3.0 plugin_platform_interface: ^2.1.0 dev_dependencies: diff --git a/packages/shared_preferences/shared_preferences/CHANGELOG.md b/packages/shared_preferences/shared_preferences/CHANGELOG.md index d8c4331133aa..978fab91389e 100644 --- a/packages/shared_preferences/shared_preferences/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.12 + +* Removes dependency on `meta`. + ## 2.0.11 * Corrects example for mocking in readme. diff --git a/packages/shared_preferences/shared_preferences/lib/shared_preferences.dart b/packages/shared_preferences/shared_preferences/lib/shared_preferences.dart index 8e9f08729355..5e2a65889bee 100644 --- a/packages/shared_preferences/shared_preferences/lib/shared_preferences.dart +++ b/packages/shared_preferences/shared_preferences/lib/shared_preferences.dart @@ -4,7 +4,7 @@ import 'dart:async'; -import 'package:meta/meta.dart'; +import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; /// Wraps NSUserDefaults (on iOS) and SharedPreferences (on Android), providing diff --git a/packages/shared_preferences/shared_preferences/pubspec.yaml b/packages/shared_preferences/shared_preferences/pubspec.yaml index a16cd2009738..a54d73fcc1c0 100644 --- a/packages/shared_preferences/shared_preferences/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for reading and writing simple key-value pairs. Wraps NSUserDefaults on iOS and SharedPreferences on Android. repository: https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.11 +version: 2.0.12 environment: sdk: ">=2.14.0 <3.0.0" @@ -28,7 +28,6 @@ flutter: dependencies: flutter: sdk: flutter - meta: ^1.3.0 shared_preferences_android: ^2.0.8 shared_preferences_ios: ^2.0.8 shared_preferences_linux: ^2.0.1 diff --git a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md index 16b8cc5b64f8..527d64d7b007 100644 --- a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.10 + +* Removes dependency on `meta`. + ## 2.0.9 * Updates compileSdkVersion to 31. diff --git a/packages/shared_preferences/shared_preferences_android/pubspec.yaml b/packages/shared_preferences/shared_preferences_android/pubspec.yaml index 3f749d0b1939..2e954206c47b 100644 --- a/packages/shared_preferences/shared_preferences_android/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_android/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_android description: Android implementation of the shared_preferences plugin repository: https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.9 +version: 2.0.10 environment: sdk: ">=2.14.0 <3.0.0" @@ -19,7 +19,6 @@ flutter: dependencies: flutter: sdk: flutter - meta: ^1.3.0 shared_preferences_platform_interface: ^2.0.0 dev_dependencies: diff --git a/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md b/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md index 85baf316f0ae..84fe4f8879f9 100644 --- a/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.9 + +* Removes dependency on `meta`. + ## 2.0.8 * Split from `shared_preferences` as a federated implementation. diff --git a/packages/shared_preferences/shared_preferences_ios/pubspec.yaml b/packages/shared_preferences/shared_preferences_ios/pubspec.yaml index 13ef43debc3f..0c8313050994 100644 --- a/packages/shared_preferences/shared_preferences_ios/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_ios description: iOS implementation of the shared_preferences plugin repository: https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.8 +version: 2.0.9 environment: sdk: ">=2.14.0 <3.0.0" @@ -18,7 +18,6 @@ flutter: dependencies: flutter: sdk: flutter - meta: ^1.3.0 shared_preferences_platform_interface: ^2.0.0 dev_dependencies: diff --git a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md index e0178ed23879..bfc48b705e39 100644 --- a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.4 + +* Removes dependency on `meta`. + ## 2.0.3 * Removed obsolete `pluginClass: none` from pubpsec. diff --git a/packages/shared_preferences/shared_preferences_linux/lib/shared_preferences_linux.dart b/packages/shared_preferences/shared_preferences_linux/lib/shared_preferences_linux.dart index cf887ccb7ad1..d6dcceb47af6 100644 --- a/packages/shared_preferences/shared_preferences_linux/lib/shared_preferences_linux.dart +++ b/packages/shared_preferences/shared_preferences_linux/lib/shared_preferences_linux.dart @@ -7,7 +7,7 @@ import 'dart:convert' show json; import 'package:file/file.dart'; import 'package:file/local.dart'; -import 'package:meta/meta.dart'; +import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:path/path.dart' as path; import 'package:path_provider_linux/path_provider_linux.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; diff --git a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml index 9eb4bcdea4cf..6a37346ff6cf 100644 --- a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_linux description: Linux implementation of the shared_preferences plugin repository: https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.3 +version: 2.0.4 environment: sdk: ">=2.12.0 <3.0.0" @@ -19,7 +19,6 @@ dependencies: file: ^6.0.0 flutter: sdk: flutter - meta: ^1.3.0 path: ^1.8.0 path_provider_linux: ^2.0.0 shared_preferences_platform_interface: ^2.0.0 diff --git a/packages/shared_preferences/shared_preferences_web/CHANGELOG.md b/packages/shared_preferences/shared_preferences_web/CHANGELOG.md index 6332d01aa743..2a6ffa20e37b 100644 --- a/packages/shared_preferences/shared_preferences_web/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_web/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 2.0.3 * Fixes newly enabled analyzer options. +* Removes dependency on `meta`. ## 2.0.2 diff --git a/packages/shared_preferences/shared_preferences_web/pubspec.yaml b/packages/shared_preferences/shared_preferences_web/pubspec.yaml index c878903ac236..37ed5d5e81d2 100644 --- a/packages/shared_preferences/shared_preferences_web/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_web/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_web description: Web platform implementation of shared_preferences repository: https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.2 +version: 2.0.3 environment: sdk: ">=2.12.0 <3.0.0" @@ -21,7 +21,6 @@ dependencies: sdk: flutter flutter_web_plugins: sdk: flutter - meta: ^1.3.0 shared_preferences_platform_interface: ^2.0.0 dev_dependencies: diff --git a/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md b/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md index 544322f2ca0f..18ae9d125ed9 100644 --- a/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.4 + +* Removes dependency on `meta`. + ## 2.0.3 * Removed obsolete `pluginClass: none` from pubpsec. diff --git a/packages/shared_preferences/shared_preferences_windows/lib/shared_preferences_windows.dart b/packages/shared_preferences/shared_preferences_windows/lib/shared_preferences_windows.dart index 02f9f85d6381..e3551d2dd079 100644 --- a/packages/shared_preferences/shared_preferences_windows/lib/shared_preferences_windows.dart +++ b/packages/shared_preferences/shared_preferences_windows/lib/shared_preferences_windows.dart @@ -7,7 +7,7 @@ import 'dart:convert' show json; import 'package:file/file.dart'; import 'package:file/local.dart'; -import 'package:meta/meta.dart'; +import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:path/path.dart' as path; import 'package:path_provider_windows/path_provider_windows.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; diff --git a/packages/shared_preferences/shared_preferences_windows/pubspec.yaml b/packages/shared_preferences/shared_preferences_windows/pubspec.yaml index cae748dee49c..107dfa20b201 100644 --- a/packages/shared_preferences/shared_preferences_windows/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_windows description: Windows implementation of shared_preferences repository: https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.3 +version: 2.0.4 environment: sdk: '>=2.12.0 <3.0.0' @@ -19,7 +19,6 @@ dependencies: file: ^6.0.0 flutter: sdk: flutter - meta: ^1.3.0 path: ^1.8.0 path_provider_platform_interface: ^2.0.0 path_provider_windows: ^2.0.0 diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index 2290fd049c7d..80065cd4476c 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.0.18 + +* Removes dependency on `meta`. + ## 6.0.17 * Updates code for new analysis options. diff --git a/packages/url_launcher/url_launcher/lib/src/link.dart b/packages/url_launcher/url_launcher/lib/src/link.dart index 67aa24430ca7..016f97daacbf 100644 --- a/packages/url_launcher/url_launcher/lib/src/link.dart +++ b/packages/url_launcher/url_launcher/lib/src/link.dart @@ -6,7 +6,6 @@ import 'dart:async'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; -import 'package:meta/meta.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher_platform_interface/link.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index 4ccf48b9db22..128c3801fd35 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes. repository: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.0.17 +version: 6.0.18 environment: sdk: ">=2.14.0 <3.0.0" @@ -28,7 +28,6 @@ flutter: dependencies: flutter: sdk: flutter - meta: ^1.3.0 url_launcher_android: ^6.0.13 url_launcher_ios: ^6.0.13 url_launcher_linux: ^2.0.0 diff --git a/packages/url_launcher/url_launcher_android/CHANGELOG.md b/packages/url_launcher/url_launcher_android/CHANGELOG.md index 1b60e86048d5..819b0cc056d5 100644 --- a/packages/url_launcher/url_launcher_android/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_android/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 6.0.14 * Updates code for new analysis options. +* Removes dependency on `meta`. ## 6.0.13 diff --git a/packages/url_launcher/url_launcher_android/pubspec.yaml b/packages/url_launcher/url_launcher_android/pubspec.yaml index 959e6d07c715..b660af992c3e 100644 --- a/packages/url_launcher/url_launcher_android/pubspec.yaml +++ b/packages/url_launcher/url_launcher_android/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_android description: Android implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.0.13 +version: 6.0.14 environment: sdk: ">=2.14.0 <3.0.0" @@ -19,7 +19,6 @@ flutter: dependencies: flutter: sdk: flutter - meta: ^1.3.0 url_launcher_platform_interface: ^2.0.3 dev_dependencies: diff --git a/packages/url_launcher/url_launcher_ios/CHANGELOG.md b/packages/url_launcher/url_launcher_ios/CHANGELOG.md index 0f9190da3e0a..83d4e4efc847 100644 --- a/packages/url_launcher/url_launcher_ios/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_ios/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 6.0.14 * Updates code for new analysis options. +* Removes dependency on `meta`. ## 6.0.13 diff --git a/packages/url_launcher/url_launcher_ios/pubspec.yaml b/packages/url_launcher/url_launcher_ios/pubspec.yaml index 1e1ee87983b2..87bb0e9784bf 100644 --- a/packages/url_launcher/url_launcher_ios/pubspec.yaml +++ b/packages/url_launcher/url_launcher_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_ios description: iOS implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.0.13 +version: 6.0.14 environment: sdk: ">=2.14.0 <3.0.0" @@ -18,7 +18,6 @@ flutter: dependencies: flutter: sdk: flutter - meta: ^1.3.0 url_launcher_platform_interface: ^2.0.3 dev_dependencies: diff --git a/packages/url_launcher/url_launcher_web/CHANGELOG.md b/packages/url_launcher/url_launcher_web/CHANGELOG.md index 5790e116c355..ebc4ab975d07 100644 --- a/packages/url_launcher/url_launcher_web/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.6 + +* Removes dependency on `meta`. + ## 2.0.5 * Updates code for new analysis options. diff --git a/packages/url_launcher/url_launcher_web/lib/url_launcher_web.dart b/packages/url_launcher/url_launcher_web/lib/url_launcher_web.dart index 571acaffed7b..76ef1d17f64e 100644 --- a/packages/url_launcher/url_launcher_web/lib/url_launcher_web.dart +++ b/packages/url_launcher/url_launcher_web/lib/url_launcher_web.dart @@ -5,8 +5,8 @@ import 'dart:async'; import 'dart:html' as html; +import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; -import 'package:meta/meta.dart'; import 'package:url_launcher_platform_interface/link.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; diff --git a/packages/url_launcher/url_launcher_web/pubspec.yaml b/packages/url_launcher/url_launcher_web/pubspec.yaml index 37d3193cb4b2..fd882060e130 100644 --- a/packages/url_launcher/url_launcher_web/pubspec.yaml +++ b/packages/url_launcher/url_launcher_web/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_web description: Web platform implementation of url_launcher repository: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 2.0.5 +version: 2.0.6 environment: sdk: ">=2.12.0 <3.0.0" @@ -21,7 +21,6 @@ dependencies: sdk: flutter flutter_web_plugins: sdk: flutter - meta: ^1.3.0 # null safe url_launcher_platform_interface: ^2.0.0 dev_dependencies: diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 97b8a1698e3d..9f23fd099c03 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.2.11 + +* Removes dependency on `meta`. + ## 2.2.10 * iOS: Updates texture on `seekTo`. diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index 523a1adc5425..a6035f285608 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -8,7 +8,6 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:meta/meta.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; export 'package:video_player_platform_interface/video_player_platform_interface.dart' diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index fcb6b844249a..0eb7a4ed41df 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for displaying inline video with other Flutter widgets on Android, iOS, and web. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.2.10 +version: 2.2.11 environment: sdk: ">=2.14.0 <3.0.0" @@ -23,7 +23,6 @@ flutter: dependencies: flutter: sdk: flutter - meta: ^1.3.0 video_player_platform_interface: ">=4.2.0 <6.0.0" video_player_web: ^2.0.0 html: ^0.15.0 diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md index 13cbf2e24dae..246cd229b364 100644 --- a/packages/video_player/video_player_web/CHANGELOG.md +++ b/packages/video_player/video_player_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.6 + +* Removes dependency on `meta`. + ## 2.0.5 * Adds compatibility with `video_player_platform_interface` 5.0, which does not diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml index ec2377ea1fb1..7c7d5113320c 100644 --- a/packages/video_player/video_player_web/pubspec.yaml +++ b/packages/video_player/video_player_web/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_web description: Web platform implementation of video_player. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.0.5 +version: 2.0.6 environment: sdk: ">=2.12.0 <3.0.0" @@ -21,7 +21,6 @@ dependencies: sdk: flutter flutter_web_plugins: sdk: flutter - meta: ^1.3.0 pedantic: ^1.10.0 video_player_platform_interface: ">=4.2.0 <6.0.0" From 5899fe568b6dacc0aefa85fc3942a1bb563d8dac Mon Sep 17 00:00:00 2001 From: godofredoc Date: Mon, 10 Jan 2022 12:30:22 -0800 Subject: [PATCH 090/600] Run tests only on main branch. (#4645) --- .ci.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.ci.yaml b/.ci.yaml index 2cd36d5b36dd..d935fb6f0e34 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -7,7 +7,6 @@ # * https://github.com/flutter/cocoon/blob/main/CI_YAML.md enabled_branches: - main - - master platform_properties: linux: From 94e80fccb2910a5a22c5757dc87f3ae29bf64d2e Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Tue, 11 Jan 2022 11:20:11 +1300 Subject: [PATCH 091/600] [url_launcher] Fix minor memory leak in Linux url-launcher tests (#4648) --- packages/url_launcher/url_launcher_linux/CHANGELOG.md | 1 + .../linux/test/url_launcher_linux_test.cc | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/url_launcher/url_launcher_linux/CHANGELOG.md b/packages/url_launcher/url_launcher_linux/CHANGELOG.md index f47d4fde4269..7079a4e34430 100644 --- a/packages/url_launcher/url_launcher_linux/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_linux/CHANGELOG.md @@ -1,6 +1,7 @@ ## NEXT * Updates code for new analysis options. +* Fix minor memory leak in Linux url_launcher tests. ## 2.0.2 diff --git a/packages/url_launcher/url_launcher_linux/linux/test/url_launcher_linux_test.cc b/packages/url_launcher/url_launcher_linux/linux/test/url_launcher_linux_test.cc index e655638c4ed7..775e2fa3b36c 100644 --- a/packages/url_launcher/url_launcher_linux/linux/test/url_launcher_linux_test.cc +++ b/packages/url_launcher/url_launcher_linux/linux/test/url_launcher_linux_test.cc @@ -18,7 +18,7 @@ TEST(UrlLauncherPlugin, CanLaunchSuccess) { g_autoptr(FlValue) args = fl_value_new_map(); fl_value_set_string_take(args, "url", fl_value_new_string("https://flutter.dev")); - FlMethodResponse* response = can_launch(nullptr, args); + g_autoptr(FlMethodResponse) response = can_launch(nullptr, args); ASSERT_NE(response, nullptr); ASSERT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response)); g_autoptr(FlValue) expected = fl_value_new_bool(true); @@ -30,7 +30,7 @@ TEST(UrlLauncherPlugin, CanLaunchSuccess) { TEST(UrlLauncherPlugin, CanLaunchFailureUnhandled) { g_autoptr(FlValue) args = fl_value_new_map(); fl_value_set_string_take(args, "url", fl_value_new_string("madeup:scheme")); - FlMethodResponse* response = can_launch(nullptr, args); + g_autoptr(FlMethodResponse) response = can_launch(nullptr, args); ASSERT_NE(response, nullptr); ASSERT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response)); g_autoptr(FlValue) expected = fl_value_new_bool(false); @@ -44,7 +44,7 @@ TEST(UrlLauncherPlugin, CanLaunchFailureUnhandled) { TEST(UrlLauncherPlugin, CanLaunchFailureInvalidUrl) { g_autoptr(FlValue) args = fl_value_new_map(); fl_value_set_string_take(args, "url", fl_value_new_string("")); - FlMethodResponse* response = can_launch(nullptr, args); + g_autoptr(FlMethodResponse) response = can_launch(nullptr, args); ASSERT_NE(response, nullptr); ASSERT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response)); g_autoptr(FlValue) expected = fl_value_new_bool(false); From 146b75179665f3355a7a9c3de9e2794a16452b35 Mon Sep 17 00:00:00 2001 From: Sebastian Roth Date: Wed, 12 Jan 2022 01:50:21 +0000 Subject: [PATCH 092/600] [google_sign_in] Bump GMS dependency and remove the need for Jetifier (#4657) --- packages/google_sign_in/google_sign_in/CHANGELOG.md | 6 +++++- packages/google_sign_in/google_sign_in/android/build.gradle | 4 ++-- .../io/flutter/plugins/googlesignin/GoogleSignInPlugin.java | 2 +- .../google_sign_in/example/android/gradle.properties | 1 - packages/google_sign_in/google_sign_in/pubspec.yaml | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index 3c2dfbe5a7be..183edaddf625 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,7 +1,11 @@ +## 5.2.3 + +* Bumps the Android dependency on `com.google.android.gms:play-services-auth` and therefore removes the need for `jetifier`. + ## 5.2.2 * Updates Android compileSdkVersion to 31. -* Removes depenedncy on `meta`. +* Removes dependency on `meta`. ## 5.2.1 diff --git a/packages/google_sign_in/google_sign_in/android/build.gradle b/packages/google_sign_in/google_sign_in/android/build.gradle index 5db929b173a8..8a2b69cb1789 100644 --- a/packages/google_sign_in/google_sign_in/android/build.gradle +++ b/packages/google_sign_in/google_sign_in/android/build.gradle @@ -48,8 +48,8 @@ android { } dependencies { - implementation 'com.google.android.gms:play-services-auth:16.0.1' - implementation 'com.google.guava:guava:20.0' + implementation 'com.google.android.gms:play-services-auth:20.0.1' + implementation 'com.google.guava:guava:28.1-android' testImplementation 'junit:junit:4.12' testImplementation 'org.mockito:mockito-inline:3.9.0' } diff --git a/packages/google_sign_in/google_sign_in/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java b/packages/google_sign_in/google_sign_in/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java index 3a63f785aa9f..1be023c678bb 100644 --- a/packages/google_sign_in/google_sign_in/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java +++ b/packages/google_sign_in/google_sign_in/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java @@ -183,7 +183,7 @@ public void onMethodCall(MethodCall call, Result result) { /** * A delegate interface that exposes all of the sign-in functionality for other plugins to use. - * The below {@link #Delegate} implementation should be used by any clients unless they need to + * The below {@link Delegate} implementation should be used by any clients unless they need to * override some of these functions, such as for testing. */ public interface IDelegate { diff --git a/packages/google_sign_in/google_sign_in/example/android/gradle.properties b/packages/google_sign_in/google_sign_in/example/android/gradle.properties index 38c8d4544ff1..d12b9a8297e5 100644 --- a/packages/google_sign_in/google_sign_in/example/android/gradle.properties +++ b/packages/google_sign_in/google_sign_in/example/android/gradle.properties @@ -1,4 +1,3 @@ org.gradle.jvmargs=-Xmx1536M android.enableR8=true android.useAndroidX=true -android.enableJetifier=true diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index fd47802f1bad..85608794e03d 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Google Sign-In, a secure authentication system for signing in with a Google account on Android and iOS. repository: https://github.com/flutter/plugins/tree/master/packages/google_sign_in/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.2.2 +version: 5.2.3 environment: sdk: ">=2.14.0 <3.0.0" From ec80d3439adb860d5f44c2d8427d459b10cda9d0 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 13 Jan 2022 15:36:40 -0500 Subject: [PATCH 093/600] [webview_flutter] Remove duplicate integration test (#4666) This test is for a change that was entirely in Android Java code when it was written, so is testing behavior that is internal to the Android implementation, and a copy of this test already runs webview_flutter_android. Given that it's testing internal Android behavior, only that copy is needed. For unknown reasons, this copy seems to hang test runs, while the webview_flutter_android copy does not. Since it's a duplicate that we want to remove anyway, this simply removes it to fix the tree without meaningful loss of coverage. Also fixes a race condition in an integration test, where a future could be completed more than once. Part of https://github.com/flutter/flutter/issues/91004 --- .../webview_flutter/CHANGELOG.md | 5 ++ .../webview_flutter_test.dart | 73 +------------------ .../webview_flutter_android/CHANGELOG.md | 4 + .../webview_flutter_test.dart | 2 +- .../webview_flutter_wkwebview/CHANGELOG.md | 6 +- .../webview_flutter_test.dart | 4 +- 6 files changed, 19 insertions(+), 75 deletions(-) diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index aa117c9d72a8..662070437cf7 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,3 +1,8 @@ +## NEXT + +* Removes a duplicate Android-specific integration test. +* Fixes an integration test race condition. + ## 3.0.0 * **BREAKING CHANGE**: On Android, hybrid composition (SurfaceAndroidWebView) diff --git a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart index 4c2d1fb0f4ff..7588dc8e02fb 100644 --- a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart @@ -480,7 +480,7 @@ void main() { onMessageReceived: (JavascriptMessage message) { final double currentTime = double.parse(message.message); // Let it play for at least 1 second to make sure the related video's properties are set. - if (currentTime > 1) { + if (currentTime > 1 && !videoPlaying.isCompleted) { videoPlaying.complete(null); } }, @@ -532,7 +532,7 @@ void main() { onMessageReceived: (JavascriptMessage message) { final double currentTime = double.parse(message.message); // Let it play for at least 1 second to make sure the related video's properties are set. - if (currentTime > 1) { + if (currentTime > 1 && !videoPlaying.isCompleted) { videoPlaying.complete(null); } }, @@ -1202,75 +1202,6 @@ void main() { }, skip: _skipDueToIssue86757, ); - - testWidgets( - 'JavaScript does not run in parent window', - (WidgetTester tester) async { - const String iframe = ''' - - - '''; - final String iframeTestBase64 = - base64Encode(const Utf8Encoder().convert(iframe)); - - final String openWindowTest = ''' - - - - XSS test - - - - - - '''; - final String openWindowTestBase64 = - base64Encode(const Utf8Encoder().convert(openWindowTest)); - final Completer controllerCompleter = - Completer(); - final Completer pageLoadCompleter = Completer(); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: WebView( - key: GlobalKey(), - onWebViewCreated: (WebViewController controller) { - controllerCompleter.complete(controller); - }, - javascriptMode: JavascriptMode.unrestricted, - initialUrl: - 'data:text/html;charset=utf-8;base64,$openWindowTestBase64', - onPageFinished: (String url) { - pageLoadCompleter.complete(); - }, - ), - ), - ); - - final WebViewController controller = await controllerCompleter.future; - await pageLoadCompleter.future; - - expect(controller.runJavascriptReturningResult('iframeLoaded'), - completion('true')); - expect( - controller.runJavascriptReturningResult( - 'document.querySelector("p") && document.querySelector("p").textContent'), - completion('null'), - ); - }, - skip: !Platform.isAndroid, - ); } // JavaScript booleans evaluate to different string values on Android and iOS. diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 818a13439c95..145bc8fcc068 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Fixes an integration test race condition. + ## 2.8.2 * Adds the `WebSettings.setAllowFileAccess()` method and ensure that file access is allowed when the `WebViewAndroidWidget.loadFile()` method is executed. diff --git a/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart index 91fc860dffae..544089f1a7ab 100644 --- a/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart @@ -484,7 +484,7 @@ void main() { onMessageReceived: (JavascriptMessage message) { final double currentTime = double.parse(message.message); // Let it play for at least 1 second to make sure the related video's properties are set. - if (currentTime > 1) { + if (currentTime > 1 && !videoPlaying.isCompleted) { videoPlaying.complete(null); } }, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index db8338df5565..c3dd8726273e 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,6 +1,10 @@ +## NEXT + +* Fixes an integration test race condition. + ## 2.7.1 -* Fixes header import for cookie manager to be relative only. +* Fixes header import for cookie manager to be relative only. ## 2.7.0 diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart index 1e4adb9f7de0..5824d5d32d0b 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart @@ -466,7 +466,7 @@ void main() { onMessageReceived: (JavascriptMessage message) { final double currentTime = double.parse(message.message); // Let it play for at least 1 second to make sure the related video's properties are set. - if (currentTime > 1) { + if (currentTime > 1 && !videoPlaying.isCompleted) { videoPlaying.complete(null); } }, @@ -517,7 +517,7 @@ void main() { onMessageReceived: (JavascriptMessage message) { final double currentTime = double.parse(message.message); // Let it play for at least 1 second to make sure the related video's properties are set. - if (currentTime > 1) { + if (currentTime > 1 && !videoPlaying.isCompleted) { videoPlaying.complete(null); } }, From 07f48a6034b4f44b80512cf615dcdbe4ecceb238 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 14 Jan 2022 13:51:42 -0500 Subject: [PATCH 094/600] [ci] Pin master in Cirrus (#4667) This uses a pinned version from a file instead of `master` when checking out Flutter for `master` channel testing in Cirrus. Currently it's a static version, but this will be updated by an auto-roller. This uses a version of `master` from several days ago to avoid the current tree breakage due to https://github.com/flutter/flutter/issues/96661 This will not affect LUCI tests; that will require a separate recipe change. Part of https://github.com/flutter/flutter/issues/93811 --- .ci/flutter_master.version | 1 + .cirrus.yml | 20 +++++++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) create mode 100644 .ci/flutter_master.version diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version new file mode 100644 index 000000000000..39e290b7650a --- /dev/null +++ b/.ci/flutter_master.version @@ -0,0 +1 @@ +3e6e996f9eff50d77214d37c1c5f8b71bcc0d559 diff --git a/.cirrus.yml b/.cirrus.yml index bbaa39e5fdf6..f2372a91f2f5 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -15,15 +15,25 @@ tool_setup_template: &TOOL_SETUP_TEMPLATE flutter_upgrade_template: &FLUTTER_UPGRADE_TEMPLATE upgrade_flutter_script: + # Master uses a pinned, auto-rolled version to prevent out-of-band CI + # failures due to changes in Flutter. + # TODO(stuartmorgan): Investigate an autoroller for stable as well. + - TARGET_TREEISH=$CHANNEL + - if [[ "$CHANNEL" == "master" ]]; then + - TARGET_TREEISH=$(< .ci/flutter_$CHANNEL.version) + - fi # Ensure that the repository has all the branches. - cd $FLUTTER_HOME - git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" - git fetch origin - # Switch to the requested branch. - - git checkout $CHANNEL - # Reset to upstream branch, rather than using pull, since the base image - # can sometimes be in a state where it has diverged from upstream (!). - - git reset --hard @{u} + # Switch to the requested channel. + - git checkout $TARGET_TREEISH + # When using a branch rather than a hash, reset to the upstream branch + # rather than using pull, since the base image can sometimes be in a state + # where it has diverged from upstream (!). + - if [[ "$TARGET_TREEISH" == "$CHANNEL" ]]; then + - git reset --hard @{u} + - fi # Run doctor to allow auditing of what version of Flutter the run is using. - flutter doctor -v << : *TOOL_SETUP_TEMPLATE From 3638e67ca4b3e29149e57eb86ea33637a9a486e1 Mon Sep 17 00:00:00 2001 From: Konstantin Scheglov Date: Fri, 14 Jan 2022 14:21:52 -0800 Subject: [PATCH 095/600] Fix UNUSED_ELEMENT_PARAMETER for field formal initializers. (#4664) * Fix UNUSED_ELEMENT_PARAMETER for field formal initializers. * Update pubspec.yaml and CHANGELOG.md --- packages/ios_platform_images/CHANGELOG.md | 4 ++++ packages/ios_platform_images/lib/ios_platform_images.dart | 4 ---- packages/ios_platform_images/pubspec.yaml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/ios_platform_images/CHANGELOG.md b/packages/ios_platform_images/CHANGELOG.md index 2ebd1d1d2d7c..443be3f7bfde 100644 --- a/packages/ios_platform_images/CHANGELOG.md +++ b/packages/ios_platform_images/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.0+3 + +* Internal fix for unused field formal parameter. + ## 0.2.0+2 * Update minimum Flutter SDK to 2.5 and iOS deployment target to 9.0. diff --git a/packages/ios_platform_images/lib/ios_platform_images.dart b/packages/ios_platform_images/lib/ios_platform_images.dart index e9bc0b342239..856562c7598f 100644 --- a/packages/ios_platform_images/lib/ios_platform_images.dart +++ b/packages/ios_platform_images/lib/ios_platform_images.dart @@ -16,21 +16,18 @@ class _FutureImageStreamCompleter extends ImageStreamCompleter { _FutureImageStreamCompleter({ required Future codec, required this.futureScale, - this.informationCollector, }) { codec.then(_onCodecReady, onError: (dynamic error, StackTrace stack) { reportError( context: ErrorDescription('resolving a single-frame image stream'), exception: error, stack: stack, - informationCollector: informationCollector, silent: true, ); }); } final Future futureScale; - final InformationCollector? informationCollector; Future _onCodecReady(ui.Codec codec) async { try { @@ -42,7 +39,6 @@ class _FutureImageStreamCompleter extends ImageStreamCompleter { context: ErrorDescription('resolving an image frame'), exception: exception, stack: stack, - informationCollector: this.informationCollector, silent: true, ); } diff --git a/packages/ios_platform_images/pubspec.yaml b/packages/ios_platform_images/pubspec.yaml index adc8dc08011e..010bd41a4767 100644 --- a/packages/ios_platform_images/pubspec.yaml +++ b/packages/ios_platform_images/pubspec.yaml @@ -2,7 +2,7 @@ name: ios_platform_images description: A plugin to share images between Flutter and iOS in add-to-app setups. repository: https://github.com/flutter/plugins/tree/master/packages/ios_platform_images issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+ios_platform_images%22 -version: 0.2.0+2 +version: 0.2.0+3 environment: sdk: ">=2.14.0 <3.0.0" From 4f1c6f30931a850d052e180d24d0732b398aa520 Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Mon, 17 Jan 2022 01:35:24 +0200 Subject: [PATCH 096/600] [url_launcher_linux] file resource URI support for canLaunch (#4491) --- .ci/Dockerfile | 3 ++- .../url_launcher_linux/CHANGELOG.md | 3 ++- .../linux/test/url_launcher_linux_test.cc | 25 +++++++++++++++++++ .../linux/url_launcher_plugin.cc | 14 +++++++++++ .../url_launcher_linux/pubspec.yaml | 2 +- 5 files changed, 44 insertions(+), 3 deletions(-) diff --git a/.ci/Dockerfile b/.ci/Dockerfile index 60f82eb005ed..e9e34fa8cfc7 100644 --- a/.ci/Dockerfile +++ b/.ci/Dockerfile @@ -41,6 +41,7 @@ RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo ap RUN echo 'deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main' | sudo tee /etc/apt/sources.list.d/google-chrome.list RUN apt-get update && apt-get install -y --no-install-recommends google-chrome-stable -# Make Chrome the default so http: has a handler for url_launcher tests. +# Make Chrome the default so http: and file: has a handler for url_launcher tests. RUN apt-get install -y xdg-utils RUN xdg-settings set default-web-browser google-chrome.desktop +RUN xdg-mime default google-chrome.desktop inode/directory diff --git a/packages/url_launcher/url_launcher_linux/CHANGELOG.md b/packages/url_launcher/url_launcher_linux/CHANGELOG.md index 7079a4e34430..a641f83a9aaa 100644 --- a/packages/url_launcher/url_launcher_linux/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_linux/CHANGELOG.md @@ -1,7 +1,8 @@ -## NEXT +## 2.0.3 * Updates code for new analysis options. * Fix minor memory leak in Linux url_launcher tests. +* Fixes canLaunch detection for URIs addressing on local or network file systems ## 2.0.2 diff --git a/packages/url_launcher/url_launcher_linux/linux/test/url_launcher_linux_test.cc b/packages/url_launcher/url_launcher_linux/linux/test/url_launcher_linux_test.cc index 775e2fa3b36c..2aa37aabb0b1 100644 --- a/packages/url_launcher/url_launcher_linux/linux/test/url_launcher_linux_test.cc +++ b/packages/url_launcher/url_launcher_linux/linux/test/url_launcher_linux_test.cc @@ -39,6 +39,31 @@ TEST(UrlLauncherPlugin, CanLaunchFailureUnhandled) { expected)); } +TEST(UrlLauncherPlugin, CanLaunchFileSuccess) { + g_autoptr(FlValue) args = fl_value_new_map(); + fl_value_set_string_take(args, "url", fl_value_new_string("file:///")); + g_autoptr(FlMethodResponse) response = can_launch(nullptr, args); + ASSERT_NE(response, nullptr); + ASSERT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response)); + g_autoptr(FlValue) expected = fl_value_new_bool(true); + EXPECT_TRUE(fl_value_equal(fl_method_success_response_get_result( + FL_METHOD_SUCCESS_RESPONSE(response)), + expected)); +} + +TEST(UrlLauncherPlugin, CanLaunchFailureInvalidFileExtension) { + g_autoptr(FlValue) args = fl_value_new_map(); + fl_value_set_string_take( + args, "url", fl_value_new_string("file:///madeup.madeupextension")); + g_autoptr(FlMethodResponse) response = can_launch(nullptr, args); + ASSERT_NE(response, nullptr); + ASSERT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response)); + g_autoptr(FlValue) expected = fl_value_new_bool(false); + EXPECT_TRUE(fl_value_equal(fl_method_success_response_get_result( + FL_METHOD_SUCCESS_RESPONSE(response)), + expected)); +} + // For consistency with the established mobile implementations, // an invalid URL should return false, not an error. TEST(UrlLauncherPlugin, CanLaunchFailureInvalidUrl) { diff --git a/packages/url_launcher/url_launcher_linux/linux/url_launcher_plugin.cc b/packages/url_launcher/url_launcher_linux/linux/url_launcher_plugin.cc index d3f454ee7198..d0f6168171c1 100644 --- a/packages/url_launcher/url_launcher_linux/linux/url_launcher_plugin.cc +++ b/packages/url_launcher/url_launcher_linux/linux/url_launcher_plugin.cc @@ -45,6 +45,16 @@ static gchar* get_url(FlValue* args, GError** error) { return g_strdup(fl_value_get_string(url_value)); } +// Checks if URI has launchable file resource. +static gboolean can_launch_uri_with_file_resource(FlUrlLauncherPlugin* self, + const gchar* url) { + g_autoptr(GError) error = nullptr; + g_autoptr(GFile) file = g_file_new_for_uri(url); + g_autoptr(GAppInfo) app_info = + g_file_query_default_handler(file, NULL, &error); + return app_info != nullptr; +} + // Called to check if a URL can be launched. FlMethodResponse* can_launch(FlUrlLauncherPlugin* self, FlValue* args) { g_autoptr(GError) error = nullptr; @@ -60,6 +70,10 @@ FlMethodResponse* can_launch(FlUrlLauncherPlugin* self, FlValue* args) { g_autoptr(GAppInfo) app_info = g_app_info_get_default_for_uri_scheme(scheme); is_launchable = app_info != nullptr; + + if (!is_launchable) { + is_launchable = can_launch_uri_with_file_resource(self, url); + } } g_autoptr(FlValue) result = fl_value_new_bool(is_launchable); diff --git a/packages/url_launcher/url_launcher_linux/pubspec.yaml b/packages/url_launcher/url_launcher_linux/pubspec.yaml index 960216851e5d..a75977912ced 100644 --- a/packages/url_launcher/url_launcher_linux/pubspec.yaml +++ b/packages/url_launcher/url_launcher_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_linux description: Linux implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 2.0.2 +version: 2.0.3 environment: sdk: ">=2.12.0 <3.0.0" From c2d3def5e6525c993b8a0d10a2821412c5558857 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 18 Jan 2022 13:05:22 -0500 Subject: [PATCH 097/600] [ci] Update the Dockerfile to a newer base image (#4614) --- .ci/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/Dockerfile b/.ci/Dockerfile index e9e34fa8cfc7..73efa3103922 100644 --- a/.ci/Dockerfile +++ b/.ci/Dockerfile @@ -1,7 +1,7 @@ # The Flutter version is not important here, since the CI scripts update Flutter # before running. What matters is that the base image is pinned to minimize # unintended changes when modifying this file. -FROM cirrusci/flutter:2.2.2 +FROM cirrusci/flutter:2.8.0 RUN apt-get update -y From de6bb34fc5bb9985ba4144f2e274269064c8b141 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 18 Jan 2022 14:04:30 -0500 Subject: [PATCH 098/600] Remove the PR review section (#4679) The plugins repository is now aligned with normal Flutter review process. There are no longer any open issues with the `backlog` label, and the intent is not to use it going forward due to the issues (bitrot, authors no longer being available, duplication of work) caused by leaving PRs open in limbo for a long period of time. --- CONTRIBUTING.md | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c7c429a94663..0d135b3e80cc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -42,28 +42,6 @@ use, and use auto-formatters: - [Objective-C](https://google.github.io/styleguide/objcguide.html) formatted with `clang-format` -### The review process - -Reviewing PRs often requires a non-trivial amount of time. We prioritize issues, not PRs, so that we use our maintainers' time in the most impactful way. Issues pertaining to this repository are managed in the [flutter/flutter issue tracker and are labeled with "plugin"](https://github.com/flutter/flutter/issues?q=is%3Aopen+is%3Aissue+label%3Aplugin+sort%3Areactions-%2B1-desc). Non-trivial PRs should have an associated issue that will be used for prioritization. See the [prioritization section](https://github.com/flutter/flutter/wiki/Issue-hygiene#prioritization) in the Flutter wiki to understand how issues are prioritized. - -Newly opened PRs first go through initial triage which results in one of: - * **Merging the PR** - if the PR can be quickly reviewed and looks good. - * **Requesting minor changes** - if the PR can be quickly reviewed, but needs changes. - * **Moving the PR to the backlog** - if the review requires non-trivial effort and the issue isn't currently a priority; in this case the maintainer will: - * Add the "backlog" label to the issue. - * Leave a comment on the PR explaining that the review is not trivial and that the issue will be looked at according to priority order. - * **Starting a non-trivial review** - if the review requires non-trivial effort and the issue is a priority; in this case the maintainer will: - * Add the "in review" label to the issue. - * Self assign the PR. - * **Closing the PR** - if the PR maintainer decides that the PR should not be merged. - -Please be aware that there is currently a significant backlog, so reviews for plugin PRs will -in most cases take significantly longer to begin than the two-week timeframe given in the -main Flutter PR guide. An effort is underway to work through the backlog, but it will -take time. If you are interested in hepling out (e.g., by doing initial reviews looking -for obvious problems like missing or failing tests), please reach out -[on Discord](https://github.com/flutter/flutter/wiki/Chat) in `#hackers-ecosystem`. - ### Releasing If you are a team member landing a PR, or just want to know what the release From 688f052a256d2a90bd786f279fa5ac11c673d201 Mon Sep 17 00:00:00 2001 From: Alex Li Date: Thu, 20 Jan 2022 01:05:25 +0800 Subject: [PATCH 099/600] [video_player] Validate size only when assets contain video tracks on iOS (#4639) --- .../video_player/video_player/CHANGELOG.md | 4 ++ .../video_player/example/assets/Audio.mp3 | Bin 0 -> 121715 bytes .../integration_test/video_player_test.dart | 64 ++++++++++++++++++ .../video_player/example/pubspec.yaml | 1 + .../ios/Classes/FLTVideoPlayerPlugin.m | 11 ++- .../video_player/video_player/pubspec.yaml | 2 +- 6 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 packages/video_player/video_player/example/assets/Audio.mp3 diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 9f23fd099c03..2513b20feee3 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.2.12 + +* iOS: Validate size only when assets contain video tracks. + ## 2.2.11 * Removes dependency on `meta`. diff --git a/packages/video_player/video_player/example/assets/Audio.mp3 b/packages/video_player/video_player/example/assets/Audio.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..355eb9b2e1fb8cce9121fcb7ff191c216cc8547f GIT binary patch literal 121715 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

-#k+cPoW)2o9yq&>ML(h%|7KI#3AlB|fkH|lkdPWRzDwjovqa=Z1o54@dkdj8%PJ^Gw9mFa zDHM zth#Ju$yJ4rGVR)tokjC>0ED#9^2)PgB1_X4-FZ}o39rtRT0!g0cK_C+^IPO;x6Z28 z+F~XWK*#i5=vJjBL~Ad(o?fb|xk~lhRmO;;33x2;9RV-bSpc$t5o(>s164Qd&o=%O z&@pz`VpH=@U6+}D+A(Wc!ncU04GaIldCW3l@@8t4b5=M90GIqq*XEL-bfcPFFnp0SvWrV z0-5eLL-(b$CzbwSx6MESfVXK8qvf#z7;eR6^zbCCY?g?Wb?CD%0yEs(xcZ`w3IK0 z^mbY``KaAoJFt36$?86y>c2KUem)I!g>C72IrJRW2wsT6?X*hou6kv&|u|HIsSCNAkJJhkHG=6l{OzS zg&b@WE9efr)QFBGN?C*zQ}unUJQM;&Q)547MVrkJNTPU;b3e9yx81JK{<8SVW)-)c zjQ->N`Av{xEOx5AF?#IgmaS-i!6W{asyTcb9=OmuQky~AzGTXvW;wY=cl-%0AyFgy zahZC0p%DViARN;hp&VJeFCs3Y*>Wf3m~?2D0YN*32zuAw@kPwgR36IvH_-3_$KYsJ zGvL0r>Ku`^ABqA^QgWrC?iAF8XM^#wy77@6iR(Hdh#W;nx9Rgl@S2gnxe3YF}qzO7LeqiWXf<$#MoqHK}&e&CnA>_ zVU$ns+J%NH6x3>GQIASw+DlW?c^Tk&rY+Km!T06SHuuA!b>w(MRX8Qj*>g}p31?J} zyEvS-=H$)OMeQSd}Y*jDtygEgi9 z_%f`y|E)lnP&Ze?8UuWgeG_TvRJ5mrhaJLCfv9>=-B#0E5nv3`VX!^*mUZ@X9@T%S z;}O|Xyr5(G3?q1WPSzEpaMyQVvIh)S7ms`6t*RMDkh7WIih-gf(a%?a+!|~=L^xnL z^Hjg`6@0Ac&l^J^;t+PKSAz1*#_@?DfGwd^&@h;V_+aIopYj&YdRb#P>r~G6oM22o zeql=ZzSKR_v#R1y$|JVJhet2FB$1t`#>2Fc_X+Q5>J|eM{Lhs zz^jfFBWe#$72gcVMBZ)a_;SXfO|I&RP0^m)hYvop_MGq6HC~z?CkqSyz)%|6B z*^-UMR{ zM&O6}V*gV-xv`vtQ zXY`NO*B=veN_O6{#t9gqpOa4y)5e|7?6Fc}f$&3-NCJf3ZNmZqngXnpgt$x85Nz;t z=PsuPK&A{1X{De69_2zcE{nX4=K3Z=tBG!Y<3PIccH93pU1DTp%6p_W3u-Y@FTFj&5KDFv?h zs-qQ;UGEdxd)yeEFoRc+iql;GJyO2Jd-EA=dE?HfAI?2ClvJ^s?ny~|5AwpfNowDD zyle``Du0iT!P9{T{ChWz}D;Lr#T=$nl%ozZtfCvnVqA zkexhv=&v{vLA$YnjJA$s8tpGL6sq+|QIy`==Z;ddns-Z1^-qouHe7tI;?POqmC417 zDqZ6oN<1BKTX;SP_!x72J%OX+a0&PO?~%UZbp8C`!>SHJiMy83nG*4|f42VG>dX({ z-7_yjd={2A#HODI4t&0y>}MR2eCJvHz`okBww6?}StQzCO+Eie6tc8o7&yFmPf9+9 zb?beD8C?bnz@-4yyd&{8t~`drwH^{58w*X2)>aW;>SU&$1ldj9TJtZ$ojoWAH|vlsXbD`@6c zl&R7;rR3CFcj4DW(8%Eq3~tmz4TYB-!!154Q%v&CD)GO&8`BJW=l1MIYt8pFJ0Yj6 zu=bvIB$hM>*hi=bF0jN$E^$LVpaH;&4gio)f3X5r0`1657DhPTh`c(1GpV7PA+L_0 z9NQbznj(@ng6;(c+^OttH0v-Y$BLG}ujx46i_SDi_{DuTy1_X;8jvcqV zVc{>nx7~j5QQ_(<=GJs02Isc^=1FuzPhLZ;c|cd%Mo@d&x{c`PSPkjx%3?=&pxcgD z!&ckNpG_xLR^kFgJ$&stJ0*rhiJ7qkUMk`$=TH>?;iI2TTP339KWvMIC)@oYOC@); og^9 position > const Duration(milliseconds: 0), + ); + }); + + testWidgets('can seek', (WidgetTester tester) async { + await _controller.initialize(); + await _controller.seekTo(const Duration(seconds: 3)); + + expect(_controller.value.position, const Duration(seconds: 3)); + }); + + testWidgets('can be paused', (WidgetTester tester) async { + await _controller.initialize(); + // Mute to allow playing without DOM interaction on Web. + // See https://developers.google.com/web/updates/2017/09/autoplay-policy-changes + await _controller.setVolume(0); + + // Play for a second, then pause, and then wait a second. + await _controller.play(); + await tester.pumpAndSettle(_playDuration); + await _controller.pause(); + final Duration pausedPosition = _controller.value.position; + await tester.pumpAndSettle(_playDuration); + + // Verify that we stopped playing after the pause. + expect(_controller.value.isPlaying, false); + expect(_controller.value.position, pausedPosition); + }); + }); } diff --git a/packages/video_player/video_player/example/pubspec.yaml b/packages/video_player/video_player/example/pubspec.yaml index 6fa02c459c53..eef6eb7ab63b 100644 --- a/packages/video_player/video_player/example/pubspec.yaml +++ b/packages/video_player/video_player/example/pubspec.yaml @@ -36,3 +36,4 @@ flutter: - assets/Butterfly-209.webm - assets/bumble_bee_captions.srt - assets/bumble_bee_captions.vtt + - assets/Audio.mp3 diff --git a/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m b/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m index b581cd8b7b5e..d5bea174e61d 100644 --- a/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m +++ b/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m @@ -337,12 +337,14 @@ - (void)updatePlayingState { - (void)setupEventSinkIfReadyToPlay { if (_eventSink && !_isInitialized) { + BOOL hasVideoTracks = + [[self.player.currentItem.asset tracksWithMediaType:AVMediaTypeVideo] count] != 0; CGSize size = [self.player currentItem].presentationSize; CGFloat width = size.width; CGFloat height = size.height; - // The player has not yet initialized. - if (height == CGSizeZero.height && width == CGSizeZero.width) { + // The player has not yet initialized when it contains video tracks. + if (hasVideoTracks && height == CGSizeZero.height && width == CGSizeZero.width) { return; } // The player may be initialized but still needs to determine the duration. @@ -375,7 +377,10 @@ - (int64_t)position { } - (int64_t)duration { - return FLTCMTimeToMillis([[_player currentItem] duration]); + // Note: https://openradar.appspot.com/radar?id=4968600712511488 + // `[AVPlayerItem duration]` can be `kCMTimeIndefinite`, + // use `[[AVPlayerItem asset] duration]` instead. + return FLTCMTimeToMillis([[[_player currentItem] asset] duration]); } - (void)seekTo:(int)location { diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 0eb7a4ed41df..e725db74509c 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for displaying inline video with other Flutter widgets on Android, iOS, and web. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.2.11 +version: 2.2.12 environment: sdk: ">=2.14.0 <3.0.0" From fe6d94e104d343ebe07e08918006c9848c563fc1 Mon Sep 17 00:00:00 2001 From: IlyaMax <33570996+IlyaMax@users.noreply.github.com> Date: Wed, 19 Jan 2022 21:38:04 +0200 Subject: [PATCH 100/600] [video_player] Fix persisting of errorDescription even after successful reinitialize (#4644) This PR fixes bug which persist errorDescription even after successful initialize Fixes https://github.com/flutter/flutter/issues/82772 --- .../video_player/video_player/CHANGELOG.md | 4 ++ .../video_player/lib/video_player.dart | 11 ++++- .../video_player/video_player/pubspec.yaml | 2 +- .../video_player/test/video_player_test.dart | 43 +++++++++++++++++-- 4 files changed, 53 insertions(+), 7 deletions(-) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 2513b20feee3..58fa0844bbe7 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.2.13 + +* Fixes persisting of hasError even after successful initialize. + ## 2.2.12 * iOS: Validate size only when assets contain video tracks. diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index a6035f285608..3d4e1c6aa599 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -32,6 +32,10 @@ VideoPlayerPlatform get _videoPlayerPlatform { /// The duration, current position, buffering state, error state and settings /// of a [VideoPlayerController]. class VideoPlayerValue { + /// This constant is just to indicate that parameter is not passed to [copyWith] + /// workaround for this issue https://github.com/dart-lang/language/issues/2009 + static const _defaultErrorDescription = 'defaultErrorDescription'; + /// Constructs a video with the given values. Only [duration] is required. The /// rest will initialize with default values when unset. VideoPlayerValue({ @@ -138,7 +142,7 @@ class VideoPlayerValue { bool? isBuffering, double? volume, double? playbackSpeed, - String? errorDescription, + String? errorDescription = _defaultErrorDescription, }) { return VideoPlayerValue( duration: duration ?? this.duration, @@ -152,7 +156,9 @@ class VideoPlayerValue { isBuffering: isBuffering ?? this.isBuffering, volume: volume ?? this.volume, playbackSpeed: playbackSpeed ?? this.playbackSpeed, - errorDescription: errorDescription ?? this.errorDescription, + errorDescription: errorDescription != _defaultErrorDescription + ? errorDescription + : this.errorDescription, ); } @@ -349,6 +355,7 @@ class VideoPlayerController extends ValueNotifier { duration: event.duration, size: event.size, isInitialized: event.duration != null, + errorDescription: null, ); initializingCompleter.complete(null); _applyLooping(); diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index e725db74509c..98c9b950871b 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for displaying inline video with other Flutter widgets on Android, iOS, and web. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.2.12 +version: 2.2.13 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player/test/video_player_test.dart b/packages/video_player/video_player/test/video_player_test.dart index 959f98f25e28..08eecb618f41 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -280,6 +280,19 @@ void main() { expect(fakeVideoPlayerPlatform.dataSources[0].uri, 'file://a.avi'); }); + + test('successful initialize on controller with error clears error', + () async { + final VideoPlayerController controller = VideoPlayerController.network( + 'https://127.0.0.1', + ); + fakeVideoPlayerPlatform.forceInitError = true; + await controller.initialize().catchError((dynamic e) {}); + expect(controller.value.hasError, equals(true)); + fakeVideoPlayerPlatform.forceInitError = false; + await controller.initialize(); + expect(controller.value.hasError, equals(false)); + }); }); test('contentUri', () async { @@ -721,11 +734,33 @@ void main() { 'errorDescription: null)'); }); - test('copyWith()', () { - final VideoPlayerValue original = VideoPlayerValue.uninitialized(); - final VideoPlayerValue exactCopy = original.copyWith(); + group('copyWith()', () { + test('exact copy', () { + final VideoPlayerValue original = VideoPlayerValue.uninitialized(); + final VideoPlayerValue exactCopy = original.copyWith(); - expect(exactCopy.toString(), original.toString()); + expect(exactCopy.toString(), original.toString()); + }); + test('errorDescription is not persisted when copy with null', () { + final VideoPlayerValue original = VideoPlayerValue.erroneous('error'); + final VideoPlayerValue copy = original.copyWith(errorDescription: null); + + expect(copy.errorDescription, null); + }); + test('errorDescription is changed when copy with another error', () { + final VideoPlayerValue original = VideoPlayerValue.erroneous('error'); + final VideoPlayerValue copy = + original.copyWith(errorDescription: 'new error'); + + expect(copy.errorDescription, 'new error'); + }); + test('errorDescription is changed when copy with error', () { + final VideoPlayerValue original = VideoPlayerValue.uninitialized(); + final VideoPlayerValue copy = + original.copyWith(errorDescription: 'new error'); + + expect(copy.errorDescription, 'new error'); + }); }); group('aspectRatio', () { From 3e4483dfa3edbfec74014ab9b5a4534565578949 Mon Sep 17 00:00:00 2001 From: Almazbek Muhtarov Date: Thu, 20 Jan 2022 22:21:34 +0600 Subject: [PATCH 101/600] [google_maps_flutter] Clear duplicated dependencies (#4484) Removes dependencies that only only needed in the example. --- .../google_maps_flutter/google_maps_flutter/CHANGELOG.md | 1 + .../google_maps_flutter/google_maps_flutter/pubspec.yaml | 5 ----- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index e47ecb7725c9..b9178a74bb70 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Removes dependencies from `pubspec.yaml` that are only needed in `example/pubspec.yaml` * Updates Android compileSdkVersion to 31. ## 2.1.1 diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index 72054d37d272..f20c574e831e 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -27,11 +27,6 @@ dev_dependencies: flutter_test: sdk: flutter - # TODO(iskakaushik): The following dependencies can be removed once - # https://github.com/dart-lang/pub/issues/2101 is resolved. - flutter_driver: - sdk: flutter - test: ^1.16.0 pedantic: ^1.10.0 plugin_platform_interface: ^2.0.0 stream_transform: ^2.0.0 From c1bdc81caae391e5420d1541e4322278e23dcc23 Mon Sep 17 00:00:00 2001 From: Collin Jackson Date: Thu, 20 Jan 2022 14:05:22 -0800 Subject: [PATCH 102/600] [plugin_platform_interface] Test that `PlatformInterface` and `MockPlatformInterfaceMixinTest` have no instance methods (#4659) --- packages/plugin_platform_interface/CHANGELOG.md | 4 ++++ .../test/plugin_platform_interface_test.dart | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/packages/plugin_platform_interface/CHANGELOG.md b/packages/plugin_platform_interface/CHANGELOG.md index 4f4984208c9b..72229cb63410 100644 --- a/packages/plugin_platform_interface/CHANGELOG.md +++ b/packages/plugin_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adds additional tests for `PlatformInterface` and `MockPlatformInterfaceMixin`. + ## 2.1.2 * Updates README to demonstrate `verify` rather than `verifyToken`, and to note diff --git a/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart b/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart index d36f21c806eb..9e1ddc09e92b 100644 --- a/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart +++ b/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart @@ -78,6 +78,14 @@ class ImplementsConstVerifyTokenPluginPlatform extends PlatformInterface ImplementsConstVerifyTokenPluginPlatform() : super(token: const Object()); } +// Ensures that `PlatformInterface` has no instance methods. Adding an +// instance method is discouraged and may be a breaking change if it +// conflicts with instance methods in subclasses. +class StaticMethodsOnlyPlatformInterfaceTest implements PlatformInterface {} + +class StaticMethodsOnlyMockPlatformInterfaceMixinTest + implements MockPlatformInterfaceMixin {} + void main() { group('`verify`', () { test('prevents implementation with `implements`', () { From de267d35fbb4112b73b2544e0339030024c2cefa Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Thu, 20 Jan 2022 15:16:26 -0800 Subject: [PATCH 103/600] [camera]call engine API in main thread to fix a crash (#4661) * [camera]call engine API in main thread to fix a crash * [camera]addess a few comments by moving XCTestExpectations to each test cases, and update wrappers comment * [camera]remove setUp function in tests, and update comments for thread safe wrappers * [camera]handle event channel's threading properly * [camera]address various nits, mainly the test expectation refactor and QueueHelper refactor --- packages/camera/camera/CHANGELOG.md | 6 +- .../ios/Runner.xcodeproj/project.pbxproj | 18 ++- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- .../ios/RunnerTests/QueueHelperTests.m | 38 ++++++ .../RunnerTests/ThreadSafeEventChannelTests.m | 66 +++++++++++ .../ThreadSafeFlutterResultTests.m | 30 ++--- .../ThreadSafeMethodChannelTests.m | 54 +++++++++ .../ThreadSafeTextureRegistryTests.m | 108 ++++++++++++++++++ .../camera/camera/ios/Classes/CameraPlugin.m | 78 +++++++++---- .../ios/Classes/FLTThreadSafeEventChannel.h | 30 +++++ .../ios/Classes/FLTThreadSafeEventChannel.m | 30 +++++ .../ios/Classes/FLTThreadSafeFlutterResult.h | 13 ++- .../ios/Classes/FLTThreadSafeFlutterResult.m | 11 +- .../ios/Classes/FLTThreadSafeMethodChannel.h | 28 +++++ .../ios/Classes/FLTThreadSafeMethodChannel.m | 28 +++++ .../Classes/FLTThreadSafeTextureRegistry.h | 46 ++++++++ .../Classes/FLTThreadSafeTextureRegistry.m | 41 +++++++ .../camera/camera/ios/Classes/QueueHelper.h | 13 +++ .../camera/camera/ios/Classes/QueueHelper.m | 15 +++ .../camera/ios/Classes/camera-umbrella.h | 4 + packages/camera/camera/pubspec.yaml | 2 +- 21 files changed, 604 insertions(+), 57 deletions(-) create mode 100644 packages/camera/camera/example/ios/RunnerTests/QueueHelperTests.m create mode 100644 packages/camera/camera/example/ios/RunnerTests/ThreadSafeEventChannelTests.m create mode 100644 packages/camera/camera/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m create mode 100644 packages/camera/camera/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m create mode 100644 packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.h create mode 100644 packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.m create mode 100644 packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.h create mode 100644 packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.m create mode 100644 packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.h create mode 100644 packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.m create mode 100644 packages/camera/camera/ios/Classes/QueueHelper.h create mode 100644 packages/camera/camera/ios/Classes/QueueHelper.m diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 1a6eceb957b1..e9db5de89e54 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,4 +1,8 @@ -## 0.9.4+5 +## 0.9.4+6 + +* Fixes a crash in iOS when using image stream due to calling Flutter engine API on non-main thread. + +## 0.9.4+5 * Fixes bug where calling a method after the camera was closed resulted in a Java `IllegalStateException` exception. * Fixes integration tests. diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj index feb789f2ecba..32b770eb6783 100644 --- a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj @@ -20,6 +20,10 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + E01EE4A82799F3A5008C1950 /* QueueHelperTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E01EE4A72799F3A5008C1950 /* QueueHelperTests.m */; }; + E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */; }; + E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */; }; + E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */; }; E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */; }; F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */ = {isa = PBXBuildFile; fileRef = F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */; }; /* End PBXBuildFile section */ @@ -74,6 +78,10 @@ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9C5CC6CAD53AD388B2694F3A /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; A24F9E418BA48BCC7409B117 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + E01EE4A72799F3A5008C1950 /* QueueHelperTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QueueHelperTests.m; sourceTree = ""; }; + E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeMethodChannelTests.m; sourceTree = ""; }; + E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeTextureRegistryTests.m; sourceTree = ""; }; + E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeEventChannelTests.m; sourceTree = ""; }; E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPreviewPauseTests.m; sourceTree = ""; }; F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockFLTThreadSafeFlutterResult.h; sourceTree = ""; }; F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockFLTThreadSafeFlutterResult.m; sourceTree = ""; }; @@ -107,6 +115,10 @@ 03BB766C2665316900CE5A93 /* Info.plist */, 033B94BD269C40A200B4DF97 /* CameraMethodChannelTests.m */, 03F6F8B126CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m */, + E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */, + E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */, + E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */, + E01EE4A72799F3A5008C1950 /* QueueHelperTests.m */, E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */, F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */, F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */, @@ -239,7 +251,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1100; + LastUpgradeCheck = 1300; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { 03BB76672665316900CE5A93 = { @@ -378,6 +390,10 @@ E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */, F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */, 334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */, + E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */, + E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */, + E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */, + E01EE4A82799F3A5008C1950 /* QueueHelperTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/camera/camera/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 1447e08231be..f4b3c1099001 100644 --- a/packages/camera/camera/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/camera/camera/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ + +@interface ThreadSafeEventChannelTests : XCTestCase +@end + +@implementation ThreadSafeEventChannelTests + +- (void)testSetStreamHandler_shouldStayOnMainThreadIfCalledFromMainThread { + FlutterEventChannel *mockEventChannel = OCMClassMock([FlutterEventChannel class]); + FLTThreadSafeEventChannel *threadSafeEventChannel = + [[FLTThreadSafeEventChannel alloc] initWithEventChannel:mockEventChannel]; + + XCTestExpectation *mainThreadExpectation = + [self expectationWithDescription:@"setStreamHandler must be called on the main thread"]; + XCTestExpectation *mainThreadCompletionExpectation = + [self expectationWithDescription: + @"setStreamHandler's completion block must be called on the main thread"]; + OCMStub([mockEventChannel setStreamHandler:[OCMArg any]]).andDo(^(NSInvocation *invocation) { + if (NSThread.isMainThread) { + [mainThreadExpectation fulfill]; + } + }); + + [threadSafeEventChannel setStreamHandler:nil + completion:^{ + if (NSThread.isMainThread) { + [mainThreadCompletionExpectation fulfill]; + } + }]; + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testSetStreamHandler_shouldDispatchToMainThreadIfCalledFromBackgroundThread { + FlutterEventChannel *mockEventChannel = OCMClassMock([FlutterEventChannel class]); + FLTThreadSafeEventChannel *threadSafeEventChannel = + [[FLTThreadSafeEventChannel alloc] initWithEventChannel:mockEventChannel]; + + XCTestExpectation *mainThreadExpectation = + [self expectationWithDescription:@"setStreamHandler must be called on the main thread"]; + XCTestExpectation *mainThreadCompletionExpectation = + [self expectationWithDescription: + @"setStreamHandler's completion block must be called on the main thread"]; + OCMStub([mockEventChannel setStreamHandler:[OCMArg any]]).andDo(^(NSInvocation *invocation) { + if (NSThread.isMainThread) { + [mainThreadExpectation fulfill]; + } + }); + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [threadSafeEventChannel setStreamHandler:nil + completion:^{ + if (NSThread.isMainThread) { + [mainThreadCompletionExpectation fulfill]; + } + }]; + }); + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +@end diff --git a/packages/camera/camera/example/ios/RunnerTests/ThreadSafeFlutterResultTests.m b/packages/camera/camera/example/ios/RunnerTests/ThreadSafeFlutterResultTests.m index 8cd4b8bc8c2a..a01b53154f62 100644 --- a/packages/camera/camera/example/ios/RunnerTests/ThreadSafeFlutterResultTests.m +++ b/packages/camera/camera/example/ios/RunnerTests/ThreadSafeFlutterResultTests.m @@ -10,8 +10,7 @@ @interface ThreadSafeFlutterResultTests : XCTestCase @implementation ThreadSafeFlutterResultTests - (void)testAsyncSendSuccess_ShouldCallResultOnMainThread { - XCTestExpectation* expectation = - [[XCTestExpectation alloc] initWithDescription:@"Result finished"]; + XCTestExpectation* expectation = [self expectationWithDescription:@"Result finished"]; FLTThreadSafeFlutterResult* threadSafeFlutterResult = [[FLTThreadSafeFlutterResult alloc] initWithResult:^(id _Nullable result) { @@ -23,12 +22,11 @@ - (void)testAsyncSendSuccess_ShouldCallResultOnMainThread { [threadSafeFlutterResult sendSuccess]; }); - [self waitForExpectations:[NSArray arrayWithObject:expectation] timeout:1]; + [self waitForExpectationsWithTimeout:1 handler:nil]; } - (void)testSyncSendSuccess_ShouldCallResultOnMainThread { - XCTestExpectation* expectation = - [[XCTestExpectation alloc] initWithDescription:@"Result finished"]; + XCTestExpectation* expectation = [self expectationWithDescription:@"Result finished"]; FLTThreadSafeFlutterResult* threadSafeFlutterResult = [[FLTThreadSafeFlutterResult alloc] initWithResult:^(id _Nullable result) { @@ -36,12 +34,11 @@ - (void)testSyncSendSuccess_ShouldCallResultOnMainThread { [expectation fulfill]; }]; [threadSafeFlutterResult sendSuccess]; - [self waitForExpectations:[NSArray arrayWithObject:expectation] timeout:1]; + [self waitForExpectationsWithTimeout:1 handler:nil]; } - (void)testSendNotImplemented_ShouldSendNotImplementedToFlutterResult { - XCTestExpectation* expectation = - [[XCTestExpectation alloc] initWithDescription:@"Result finished"]; + XCTestExpectation* expectation = [self expectationWithDescription:@"Result finished"]; FLTThreadSafeFlutterResult* threadSafeFlutterResult = [[FLTThreadSafeFlutterResult alloc] initWithResult:^(id _Nullable result) { @@ -53,15 +50,14 @@ - (void)testSendNotImplemented_ShouldSendNotImplementedToFlutterResult { [threadSafeFlutterResult sendNotImplemented]; }); - [self waitForExpectations:[NSArray arrayWithObject:expectation] timeout:1]; + [self waitForExpectationsWithTimeout:1 handler:nil]; } - (void)testSendErrorDetails_ShouldSendErrorToFlutterResult { NSString* errorCode = @"errorCode"; NSString* errorMessage = @"message"; NSString* errorDetails = @"error details"; - XCTestExpectation* expectation = - [[XCTestExpectation alloc] initWithDescription:@"Result finished"]; + XCTestExpectation* expectation = [self expectationWithDescription:@"Result finished"]; FLTThreadSafeFlutterResult* threadSafeFlutterResult = [[FLTThreadSafeFlutterResult alloc] initWithResult:^(id _Nullable result) { @@ -77,13 +73,12 @@ - (void)testSendErrorDetails_ShouldSendErrorToFlutterResult { [threadSafeFlutterResult sendErrorWithCode:errorCode message:errorMessage details:errorDetails]; }); - [self waitForExpectations:[NSArray arrayWithObject:expectation] timeout:1]; + [self waitForExpectationsWithTimeout:1 handler:nil]; } - (void)testSendNSError_ShouldSendErrorToFlutterResult { NSError* originalError = [[NSError alloc] initWithDomain:NSURLErrorDomain code:404 userInfo:nil]; - XCTestExpectation* expectation = - [[XCTestExpectation alloc] initWithDescription:@"Result finished"]; + XCTestExpectation* expectation = [self expectationWithDescription:@"Result finished"]; FLTThreadSafeFlutterResult* threadSafeFlutterResult = [[FLTThreadSafeFlutterResult alloc] initWithResult:^(id _Nullable result) { @@ -99,13 +94,12 @@ - (void)testSendNSError_ShouldSendErrorToFlutterResult { [threadSafeFlutterResult sendError:originalError]; }); - [self waitForExpectations:[NSArray arrayWithObject:expectation] timeout:1]; + [self waitForExpectationsWithTimeout:1 handler:nil]; } - (void)testSendResult_ShouldSendResultToFlutterResult { NSString* resultData = @"resultData"; - XCTestExpectation* expectation = - [[XCTestExpectation alloc] initWithDescription:@"Result finished"]; + XCTestExpectation* expectation = [self expectationWithDescription:@"Result finished"]; FLTThreadSafeFlutterResult* threadSafeFlutterResult = [[FLTThreadSafeFlutterResult alloc] initWithResult:^(id _Nullable result) { @@ -117,6 +111,6 @@ - (void)testSendResult_ShouldSendResultToFlutterResult { [threadSafeFlutterResult sendSuccessWithData:resultData]; }); - [self waitForExpectations:[NSArray arrayWithObject:expectation] timeout:1]; + [self waitForExpectationsWithTimeout:1 handler:nil]; } @end diff --git a/packages/camera/camera/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m b/packages/camera/camera/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m new file mode 100644 index 000000000000..5075be7a81e2 --- /dev/null +++ b/packages/camera/camera/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m @@ -0,0 +1,54 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import camera; +@import XCTest; +#import + +@interface ThreadSafeMethodChannelTests : XCTestCase +@end + +@implementation ThreadSafeMethodChannelTests + +- (void)testInvokeMethod_shouldStayOnMainThreadIfCalledFromMainThread { + FlutterMethodChannel *mockMethodChannel = OCMClassMock([FlutterMethodChannel class]); + FLTThreadSafeMethodChannel *threadSafeMethodChannel = + [[FLTThreadSafeMethodChannel alloc] initWithMethodChannel:mockMethodChannel]; + + XCTestExpectation *mainThreadExpectation = + [self expectationWithDescription:@"invokeMethod must be called on the main thread"]; + + OCMStub([mockMethodChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]) + .andDo(^(NSInvocation *invocation) { + if (NSThread.isMainThread) { + [mainThreadExpectation fulfill]; + } + }); + + [threadSafeMethodChannel invokeMethod:@"foo" arguments:nil]; + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testInvokeMethod__shouldDispatchToMainThreadIfCalledFromBackgroundThread { + FlutterMethodChannel *mockMethodChannel = OCMClassMock([FlutterMethodChannel class]); + FLTThreadSafeMethodChannel *threadSafeMethodChannel = + [[FLTThreadSafeMethodChannel alloc] initWithMethodChannel:mockMethodChannel]; + + XCTestExpectation *mainThreadExpectation = + [self expectationWithDescription:@"invokeMethod must be called on the main thread"]; + + OCMStub([mockMethodChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]) + .andDo(^(NSInvocation *invocation) { + if (NSThread.isMainThread) { + [mainThreadExpectation fulfill]; + } + }); + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [threadSafeMethodChannel invokeMethod:@"foo" arguments:nil]; + }); + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +@end diff --git a/packages/camera/camera/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m b/packages/camera/camera/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m new file mode 100644 index 000000000000..067ebab3642f --- /dev/null +++ b/packages/camera/camera/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m @@ -0,0 +1,108 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import camera; +@import XCTest; +#import + +@interface ThreadSafeTextureRegistryTests : XCTestCase +@end + +@implementation ThreadSafeTextureRegistryTests + +- (void)testShouldStayOnMainThreadIfCalledFromMainThread { + NSObject *mockTextureRegistry = + OCMProtocolMock(@protocol(FlutterTextureRegistry)); + FLTThreadSafeTextureRegistry *threadSafeTextureRegistry = + [[FLTThreadSafeTextureRegistry alloc] initWithTextureRegistry:mockTextureRegistry]; + + XCTestExpectation *registerTextureExpectation = + [self expectationWithDescription:@"registerTexture must be called on the main thread"]; + XCTestExpectation *unregisterTextureExpectation = + [self expectationWithDescription:@"unregisterTexture must be called on the main thread"]; + XCTestExpectation *textureFrameAvailableExpectation = + [self expectationWithDescription:@"textureFrameAvailable must be called on the main thread"]; + XCTestExpectation *registerTextureCompletionExpectation = + [self expectationWithDescription: + @"registerTexture's completion block must be called on the main thread"]; + + OCMStub([mockTextureRegistry registerTexture:[OCMArg any]]).andDo(^(NSInvocation *invocation) { + if (NSThread.isMainThread) { + [registerTextureExpectation fulfill]; + } + }); + + OCMStub([mockTextureRegistry unregisterTexture:0]).andDo(^(NSInvocation *invocation) { + if (NSThread.isMainThread) { + [unregisterTextureExpectation fulfill]; + } + }); + + OCMStub([mockTextureRegistry textureFrameAvailable:0]).andDo(^(NSInvocation *invocation) { + if (NSThread.isMainThread) { + [textureFrameAvailableExpectation fulfill]; + } + }); + + NSObject *anyTexture = OCMProtocolMock(@protocol(FlutterTexture)); + [threadSafeTextureRegistry registerTexture:anyTexture + completion:^(int64_t textureId) { + if (NSThread.isMainThread) { + [registerTextureCompletionExpectation fulfill]; + } + }]; + [threadSafeTextureRegistry textureFrameAvailable:0]; + [threadSafeTextureRegistry unregisterTexture:0]; + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testShouldDispatchToMainThreadIfCalledFromBackgroundThread { + NSObject *mockTextureRegistry = + OCMProtocolMock(@protocol(FlutterTextureRegistry)); + FLTThreadSafeTextureRegistry *threadSafeTextureRegistry = + [[FLTThreadSafeTextureRegistry alloc] initWithTextureRegistry:mockTextureRegistry]; + + XCTestExpectation *registerTextureExpectation = + [self expectationWithDescription:@"registerTexture must be called on the main thread"]; + XCTestExpectation *unregisterTextureExpectation = + [self expectationWithDescription:@"unregisterTexture must be called on the main thread"]; + XCTestExpectation *textureFrameAvailableExpectation = + [self expectationWithDescription:@"textureFrameAvailable must be called on the main thread"]; + XCTestExpectation *registerTextureCompletionExpectation = + [self expectationWithDescription: + @"registerTexture's completion block must be called on the main thread"]; + + OCMStub([mockTextureRegistry registerTexture:[OCMArg any]]).andDo(^(NSInvocation *invocation) { + if (NSThread.isMainThread) { + [registerTextureExpectation fulfill]; + } + }); + + OCMStub([mockTextureRegistry unregisterTexture:0]).andDo(^(NSInvocation *invocation) { + if (NSThread.isMainThread) { + [unregisterTextureExpectation fulfill]; + } + }); + + OCMStub([mockTextureRegistry textureFrameAvailable:0]).andDo(^(NSInvocation *invocation) { + if (NSThread.isMainThread) { + [textureFrameAvailableExpectation fulfill]; + } + }); + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSObject *anyTexture = OCMProtocolMock(@protocol(FlutterTexture)); + [threadSafeTextureRegistry registerTexture:anyTexture + completion:^(int64_t textureId) { + if (NSThread.isMainThread) { + [registerTextureCompletionExpectation fulfill]; + } + }]; + [threadSafeTextureRegistry textureFrameAvailable:0]; + [threadSafeTextureRegistry unregisterTexture:0]; + }); + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +@end diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.m b/packages/camera/camera/ios/Classes/CameraPlugin.m index 2c12081da807..09b5f0ff2e30 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin.m +++ b/packages/camera/camera/ios/Classes/CameraPlugin.m @@ -10,7 +10,10 @@ #import #import #import +#import "FLTThreadSafeEventChannel.h" #import "FLTThreadSafeFlutterResult.h" +#import "FLTThreadSafeMethodChannel.h" +#import "FLTThreadSafeTextureRegistry.h" @interface FLTSavePhotoDelegate : NSObject @property(readonly, nonatomic) NSString *path; @@ -18,19 +21,34 @@ @interface FLTSavePhotoDelegate : NSObject @end @interface FLTImageStreamHandler : NSObject +// The queue on which `eventSink` property should be accessed +@property(nonatomic, strong) dispatch_queue_t dispatchQueue; +// `eventSink` property should be accessed on `dispatchQueue`. +// The block itself should be invoked on the main queue. @property FlutterEventSink eventSink; @end @implementation FLTImageStreamHandler +- (instancetype)initWithDispatchQueue:(dispatch_queue_t)dispatchQueue { + self = [super init]; + NSAssert(self, @"super init cannot be nil"); + _dispatchQueue = dispatchQueue; + return self; +} + - (FlutterError *_Nullable)onCancelWithArguments:(id _Nullable)arguments { - _eventSink = nil; + dispatch_async(self.dispatchQueue, ^{ + self.eventSink = nil; + }); return nil; } - (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments eventSink:(nonnull FlutterEventSink)events { - _eventSink = events; + dispatch_async(self.dispatchQueue, ^{ + self.eventSink = events; + }); return nil; } @end @@ -305,7 +323,7 @@ @interface FLTCam : NSObject *)messen FlutterEventChannel *eventChannel = [FlutterEventChannel eventChannelWithName:@"plugins.flutter.io/camera/imageStream" binaryMessenger:messenger]; - - _imageStreamHandler = [[FLTImageStreamHandler alloc] init]; - [eventChannel setStreamHandler:_imageStreamHandler]; - - _isStreamingImages = YES; + FLTThreadSafeEventChannel *threadSafeEventChannel = + [[FLTThreadSafeEventChannel alloc] initWithEventChannel:eventChannel]; + + _imageStreamHandler = [[FLTImageStreamHandler alloc] initWithDispatchQueue:_dispatchQueue]; + [threadSafeEventChannel setStreamHandler:_imageStreamHandler + completion:^{ + dispatch_async(self->_dispatchQueue, ^{ + self.isStreamingImages = YES; + }); + }]; } else { [_methodChannel invokeMethod:errorMethod arguments:@"Images from camera are already streaming!"]; @@ -1285,10 +1313,10 @@ - (void)setUpCaptureSessionForAudio { @end @interface CameraPlugin () -@property(readonly, nonatomic) NSObject *registry; +@property(readonly, nonatomic) FLTThreadSafeTextureRegistry *registry; @property(readonly, nonatomic) NSObject *messenger; @property(readonly, nonatomic) FLTCam *camera; -@property(readonly, nonatomic) FlutterMethodChannel *deviceEventMethodChannel; +@property(readonly, nonatomic) FLTThreadSafeMethodChannel *deviceEventMethodChannel; @end @implementation CameraPlugin { @@ -1308,7 +1336,7 @@ - (instancetype)initWithRegistry:(NSObject *)registry messenger:(NSObject *)messenger { self = [super init]; NSAssert(self, @"super init cannot be nil"); - _registry = registry; + _registry = [[FLTThreadSafeTextureRegistry alloc] initWithTextureRegistry:registry]; _messenger = messenger; [self initDeviceEventMethodChannel]; [self startOrientationListener]; @@ -1316,9 +1344,11 @@ - (instancetype)initWithRegistry:(NSObject *)registry } - (void)initDeviceEventMethodChannel { - _deviceEventMethodChannel = + FlutterMethodChannel *methodChannel = [FlutterMethodChannel methodChannelWithName:@"flutter.io/cameraPlugin/device" binaryMessenger:_messenger]; + _deviceEventMethodChannel = + [[FLTThreadSafeMethodChannel alloc] initWithMethodChannel:methodChannel]; } - (void)startOrientationListener { @@ -1417,11 +1447,13 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call if (_camera) { [_camera close]; } - int64_t textureId = [self.registry registerTexture:cam]; _camera = cam; - [result sendSuccessWithData:@{ - @"cameraId" : @(textureId), - }]; + [self.registry registerTexture:cam + completion:^(int64_t textureId) { + [result sendSuccessWithData:@{ + @"cameraId" : @(textureId), + }]; + }]; } } else if ([@"startImageStream" isEqualToString:call.method]) { [_camera startImageStreamWithMessenger:_messenger]; @@ -1446,8 +1478,10 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call methodChannelWithName:[NSString stringWithFormat:@"flutter.io/cameraPlugin/camera%lu", (unsigned long)cameraId] binaryMessenger:_messenger]; - _camera.methodChannel = methodChannel; - [methodChannel + FLTThreadSafeMethodChannel *threadSafeMethodChannel = + [[FLTThreadSafeMethodChannel alloc] initWithMethodChannel:methodChannel]; + _camera.methodChannel = threadSafeMethodChannel; + [threadSafeMethodChannel invokeMethod:@"initialized" arguments:@{ @"previewWidth" : @(_camera.previewSize.width), diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.h b/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.h new file mode 100644 index 000000000000..ddfa75487a28 --- /dev/null +++ b/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.h @@ -0,0 +1,30 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * A thread safe wrapper for FlutterEventChannel that can be called from any thread, by dispatching + * its underlying engine calls to the main thread. + */ +@interface FLTThreadSafeEventChannel : NSObject + +/** + * Creates a FLTThreadSafeEventChannel by wrapping a FlutterEventChannel object. + * @param channel The FlutterEventChannel object to be wrapped. + */ +- (instancetype)initWithEventChannel:(FlutterEventChannel *)channel; + +/* + * Registers a handler on the main thread for stream setup requests from the Flutter side. + # The completion block runs on the main thread. + */ +- (void)setStreamHandler:(nullable NSObject *)handler + completion:(void (^)(void))completion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.m b/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.m new file mode 100644 index 000000000000..02a36f152bd8 --- /dev/null +++ b/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.m @@ -0,0 +1,30 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FLTThreadSafeEventChannel.h" +#import "QueueHelper.h" + +@interface FLTThreadSafeEventChannel () +@property(nonatomic, strong) FlutterEventChannel *channel; +@end + +@implementation FLTThreadSafeEventChannel + +- (instancetype)initWithEventChannel:(FlutterEventChannel *)channel { + self = [super init]; + if (self) { + _channel = channel; + } + return self; +} + +- (void)setStreamHandler:(NSObject *)handler + completion:(void (^)(void))completion { + [QueueHelper ensureToRunOnMainQueue:^{ + [self.channel setStreamHandler:handler]; + completion(); + }]; +} + +@end diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.h b/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.h index f290ca0fcd05..787be4c8faca 100644 --- a/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.h +++ b/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.h @@ -5,7 +5,8 @@ #import /** - * Wrapper for FlutterResult that always delivers the result on the main thread. + * A thread safe wrapper for FlutterResult that can be called from any thread, by dispatching its + * underlying engine calls to the main thread. */ @interface FLTThreadSafeFlutterResult : NSObject @@ -21,31 +22,31 @@ - (nonnull instancetype)initWithResult:(nonnull FlutterResult)result; /** - * Sends a successful result without any data. + * Sends a successful result on the main thread without any data. */ - (void)sendSuccess; /** - * Sends a successful result with data. + * Sends a successful result on the main thread with data. * @param data Result data that is send to the Flutter Dart side. */ - (void)sendSuccessWithData:(nonnull id)data; /** - * Sends an NSError as result + * Sends an NSError as result on the main thread. * @param error Error that will be send as FlutterError. */ - (void)sendError:(nonnull NSError*)error; /** - * Sends a FlutterError as result. + * Sends a FlutterError as result on the main thread. */ - (void)sendErrorWithCode:(nonnull NSString*)code message:(nullable NSString*)message details:(nullable id)details; /** - * Sends FlutterMethodNotImplemented as result. + * Sends FlutterMethodNotImplemented as result on the main thread. */ - (void)sendNotImplemented; @end diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.m b/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.m index caa4788d8dc8..2e426ccbe337 100644 --- a/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.m +++ b/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.m @@ -4,6 +4,7 @@ #import "FLTThreadSafeFlutterResult.h" #import +#import "QueueHelper.h" @implementation FLTThreadSafeFlutterResult { } @@ -46,13 +47,9 @@ - (void)sendNotImplemented { * Sends result to flutterResult on the main thread. */ - (void)send:(id _Nullable)result { - if (!NSThread.isMainThread) { - dispatch_async(dispatch_get_main_queue(), ^{ - self->_flutterResult(result); - }); - } else { - _flutterResult(result); - } + [QueueHelper ensureToRunOnMainQueue:^{ + self.flutterResult(result); + }]; } @end diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.h b/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.h new file mode 100644 index 000000000000..0f6611db03ce --- /dev/null +++ b/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.h @@ -0,0 +1,28 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * A thread safe wrapper for FlutterMethodChannel that can be called from any thread, by dispatching + * its underlying engine calls to the main thread. + */ +@interface FLTThreadSafeMethodChannel : NSObject + +/** + * Creates a FLTThreadSafeMethodChannel by wrapping a FlutterMethodChannel object. + * @param channel The FlutterMethodChannel object to be wrapped. + */ +- (instancetype)initWithMethodChannel:(FlutterMethodChannel *)channel; + +/** + * Invokes the specified flutter method on the main thread with the specified arguments. + */ +- (void)invokeMethod:(NSString *)method arguments:(nullable id)arguments; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.m b/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.m new file mode 100644 index 000000000000..ad4da87c8e91 --- /dev/null +++ b/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.m @@ -0,0 +1,28 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FLTThreadSafeMethodChannel.h" +#import "QueueHelper.h" + +@interface FLTThreadSafeMethodChannel () +@property(nonatomic, strong) FlutterMethodChannel *channel; +@end + +@implementation FLTThreadSafeMethodChannel + +- (instancetype)initWithMethodChannel:(FlutterMethodChannel *)channel { + self = [super init]; + if (self) { + _channel = channel; + } + return self; +} + +- (void)invokeMethod:(NSString *)method arguments:(id)arguments { + [QueueHelper ensureToRunOnMainQueue:^{ + [self.channel invokeMethod:method arguments:arguments]; + }]; +} + +@end diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.h b/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.h new file mode 100644 index 000000000000..030e2dbc7818 --- /dev/null +++ b/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.h @@ -0,0 +1,46 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * A thread safe wrapper for FlutterTextureRegistry that can be called from any thread, by + * dispatching its underlying engine calls to the main thread. + */ +@interface FLTThreadSafeTextureRegistry : NSObject + +/** + * Creates a FLTThreadSafeTextureRegistry by wrapping an object conforming to + * FlutterTextureRegistry. + * @param registry The FlutterTextureRegistry object to be wrapped. + */ +- (instancetype)initWithTextureRegistry:(NSObject *)registry; + +/** + * Registers a `FlutterTexture` on the main thread for usage in Flutter and returns an id that can + * be used to reference that texture when calling into Flutter with channels. + * + * On success the completion block completes with the pointer to the registered texture, else with + * 0. The completion block runs on the main thread. + */ +- (void)registerTexture:(NSObject *)texture + completion:(void (^)(int64_t))completion; + +/** + * Notifies the Flutter engine on the main thread that the given texture has been updated. + */ +- (void)textureFrameAvailable:(int64_t)textureId; + +/** + * Notifies the Flutter engine on the main thread to unregister a `FlutterTexture` that has been + * previously registered with `registerTexture:`. + * @param textureId The result that was previously returned from `registerTexture:`. + */ +- (void)unregisterTexture:(int64_t)textureId; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.m b/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.m new file mode 100644 index 000000000000..5eb2443e4ee2 --- /dev/null +++ b/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.m @@ -0,0 +1,41 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FLTThreadSafeTextureRegistry.h" +#import "QueueHelper.h" + +@interface FLTThreadSafeTextureRegistry () +@property(nonatomic, strong) NSObject *registry; +@end + +@implementation FLTThreadSafeTextureRegistry + +- (instancetype)initWithTextureRegistry:(NSObject *)registry { + self = [super init]; + if (self) { + _registry = registry; + } + return self; +} + +- (void)registerTexture:(NSObject *)texture + completion:(void (^)(int64_t))completion { + [QueueHelper ensureToRunOnMainQueue:^{ + completion([self.registry registerTexture:texture]); + }]; +} + +- (void)textureFrameAvailable:(int64_t)textureId { + [QueueHelper ensureToRunOnMainQueue:^{ + [self.registry textureFrameAvailable:textureId]; + }]; +} + +- (void)unregisterTexture:(int64_t)textureId { + [QueueHelper ensureToRunOnMainQueue:^{ + [self.registry unregisterTexture:textureId]; + }]; +} + +@end diff --git a/packages/camera/camera/ios/Classes/QueueHelper.h b/packages/camera/camera/ios/Classes/QueueHelper.h new file mode 100644 index 000000000000..c2548148c499 --- /dev/null +++ b/packages/camera/camera/ios/Classes/QueueHelper.h @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface QueueHelper : NSObject ++ (void)ensureToRunOnMainQueue:(void (^)(void))block; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera/ios/Classes/QueueHelper.m b/packages/camera/camera/ios/Classes/QueueHelper.m new file mode 100644 index 000000000000..194dfa9cfb95 --- /dev/null +++ b/packages/camera/camera/ios/Classes/QueueHelper.m @@ -0,0 +1,15 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "QueueHelper.h" + +@implementation QueueHelper ++ (void)ensureToRunOnMainQueue:(void (^)(void))block { + if (!NSThread.isMainThread) { + dispatch_async(dispatch_get_main_queue(), block); + } else { + block(); + } +} +@end diff --git a/packages/camera/camera/ios/Classes/camera-umbrella.h b/packages/camera/camera/ios/Classes/camera-umbrella.h index b0fd493b24df..428b125d3a43 100644 --- a/packages/camera/camera/ios/Classes/camera-umbrella.h +++ b/packages/camera/camera/ios/Classes/camera-umbrella.h @@ -4,7 +4,11 @@ #import #import +#import #import +#import +#import +#import FOUNDATION_EXPORT double cameraVersionNumber; FOUNDATION_EXPORT const unsigned char cameraVersionString[]; diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 58e1ca3ca98c..4c6c7e7e52d1 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/master/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+5 +version: 0.9.4+6 environment: sdk: ">=2.14.0 <3.0.0" From 2d0cec9fb4a33e4025b97860a1ced03d506cbedb Mon Sep 17 00:00:00 2001 From: Igor Poltavskiy Date: Fri, 21 Jan 2022 17:46:43 +0200 Subject: [PATCH 104/600] [url_launcher] Improving unit tests on mac (#4472) Add unit tests for url_launcher canLaunch and launch methods on mac with success and failure results. Fixes #92969 --- .../url_launcher_macos/CHANGELOG.md | 3 +- .../macos/RunnerTests/RunnerTests.swift | 138 +++++++++++++++++- .../macos/Classes/UrlLauncherPlugin.swift | 32 +++- .../url_launcher_macos/pubspec.yaml | 2 +- 4 files changed, 167 insertions(+), 8 deletions(-) diff --git a/packages/url_launcher/url_launcher_macos/CHANGELOG.md b/packages/url_launcher/url_launcher_macos/CHANGELOG.md index 7b7677a1ec47..9281c910d52d 100644 --- a/packages/url_launcher/url_launcher_macos/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_macos/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 2.0.3 * Updates code for new analysis options. +* Updates unit tests. ## 2.0.2 diff --git a/packages/url_launcher/url_launcher_macos/example/macos/RunnerTests/RunnerTests.swift b/packages/url_launcher/url_launcher_macos/example/macos/RunnerTests/RunnerTests.swift index d08f66464454..adbd1144c8b9 100644 --- a/packages/url_launcher/url_launcher_macos/example/macos/RunnerTests/RunnerTests.swift +++ b/packages/url_launcher/url_launcher_macos/example/macos/RunnerTests/RunnerTests.swift @@ -6,19 +6,149 @@ import FlutterMacOS import XCTest import url_launcher_macos +/// A stub to simulate the system Url handler. +class StubWorkspace: SystemURLHandler { + + var isSuccessful = true + + func open(_ url: URL) -> Bool { + return isSuccessful + } + + func urlForApplication(toOpen: URL) -> URL? { + return toOpen + } +} + class RunnerTests: XCTestCase { - func testCanLaunch() throws { + + func testCanLaunchSuccessReturnsTrue() throws { + let expectation = XCTestExpectation(description: "Check if the URL can be launched") let plugin = UrlLauncherPlugin() + let call = FlutterMethodCall( methodName: "canLaunch", arguments: ["url": "https://flutter.dev"]) - var canLaunch: Bool? + + plugin.handle( + call, + result: { (result: Any?) -> Void in + XCTAssertEqual(result as? Bool, true) + expectation.fulfill() + }) + + wait(for: [expectation], timeout: 10.0) + } + + func testCanLaunchNoAppIsAbleToOpenUrlReturnsFalse() throws { + let expectation = XCTestExpectation(description: "Check if the URL can be launched") + let plugin = UrlLauncherPlugin() + + let call = FlutterMethodCall( + methodName: "canLaunch", + arguments: ["url": "example://flutter.dev"]) + + plugin.handle( + call, + result: { (result: Any?) -> Void in + XCTAssertEqual(result as? Bool, false) + expectation.fulfill() + }) + + wait(for: [expectation], timeout: 10.0) + } + + func testCanLaunchInvalidUrlReturnsFalse() throws { + let expectation = XCTestExpectation(description: "Check if the URL can be launched") + let plugin = UrlLauncherPlugin() + + let call = FlutterMethodCall( + methodName: "canLaunch", + arguments: ["url": "brokenUrl"]) + + plugin.handle( + call, + result: { (result: Any?) -> Void in + XCTAssertEqual(result as? Bool, false) + expectation.fulfill() + }) + + wait(for: [expectation], timeout: 10.0) + } + + func testCanLaunchMissingArgumentReturnsFlutterError() throws { + let expectation = XCTestExpectation(description: "Check if the URL can be launched") + let plugin = UrlLauncherPlugin() + + let call = FlutterMethodCall( + methodName: "canLaunch", + arguments: []) + plugin.handle( call, result: { (result: Any?) -> Void in - canLaunch = result as? Bool + XCTAssertTrue(result is FlutterError) + expectation.fulfill() + }) + + wait(for: [expectation], timeout: 10.0) + } + + func testLaunchSuccessReturnsTrue() throws { + let expectation = XCTestExpectation(description: "Try to open the URL") + let workspace = StubWorkspace() + let pluginWithStubWorkspace = UrlLauncherPlugin(workspace) + + let call = FlutterMethodCall( + methodName: "launch", + arguments: ["url": "https://flutter.dev"]) + + pluginWithStubWorkspace.handle( + call, + result: { (result: Any?) -> Void in + XCTAssertEqual(result as? Bool, true) + expectation.fulfill() + }) + + wait(for: [expectation], timeout: 10.0) + } + + func testLaunchNoAppIsAbleToOpenUrlReturnsFalse() throws { + let expectation = XCTestExpectation(description: "Try to open the URL") + let workspace = StubWorkspace() + workspace.isSuccessful = false + let pluginWithStubWorkspace = UrlLauncherPlugin(workspace) + + let call = FlutterMethodCall( + methodName: "launch", + arguments: ["url": "schemethatdoesnotexist://flutter.dev"]) + + pluginWithStubWorkspace.handle( + call, + result: { (result: Any?) -> Void in + XCTAssertEqual(result as? Bool, false) + expectation.fulfill() + }) + + wait(for: [expectation], timeout: 10.0) + } + + func testLaunchMissingArgumentReturnsFlutterError() throws { + let expectation = XCTestExpectation(description: "Try to open the URL") + let workspace = StubWorkspace() + let pluginWithStubWorkspace = UrlLauncherPlugin(workspace) + + let call = FlutterMethodCall( + methodName: "launch", + arguments: []) + + pluginWithStubWorkspace.handle( + call, + result: { (result: Any?) -> Void in + XCTAssertTrue(result is FlutterError) + expectation.fulfill() }) - XCTAssertTrue(canLaunch == true) + wait(for: [expectation], timeout: 10.0) } } diff --git a/packages/url_launcher/url_launcher_macos/macos/Classes/UrlLauncherPlugin.swift b/packages/url_launcher/url_launcher_macos/macos/Classes/UrlLauncherPlugin.swift index ab89038fa01d..c311b8575017 100644 --- a/packages/url_launcher/url_launcher_macos/macos/Classes/UrlLauncherPlugin.swift +++ b/packages/url_launcher/url_launcher_macos/macos/Classes/UrlLauncherPlugin.swift @@ -5,7 +5,35 @@ import FlutterMacOS import Foundation +/// A handler that can launch other apps, check if any app is able to open the URL. +public protocol SystemURLHandler { + + /// Opens the location at the specified URL. + /// + /// - Parameters: + /// - url: A URL specifying the location to open. + /// - Returns: true if the location was successfully opened; otherwise, false. + func open(_ url: URL) -> Bool + + /// Returns the URL to the default app that would be opened. + /// + /// - Parameters: + /// - toOpen: The URL of the file to open. + /// - Returns: The URL of the default app that would open the specified url. + /// Returns nil if no app is able to open the URL, or if the file URL does not exist. + func urlForApplication(toOpen: URL) -> URL? +} + +extension NSWorkspace: SystemURLHandler {} + public class UrlLauncherPlugin: NSObject, FlutterPlugin { + + private var workspace: SystemURLHandler + + public init(_ workspace: SystemURLHandler = NSWorkspace.shared) { + self.workspace = workspace + } + public static func register(with registrar: FlutterPluginRegistrar) { let channel = FlutterMethodChannel( name: "plugins.flutter.io/url_launcher", @@ -24,7 +52,7 @@ public class UrlLauncherPlugin: NSObject, FlutterPlugin { result(invalidURLError(urlString)) return } - result(NSWorkspace.shared.urlForApplication(toOpen: url) != nil) + result(workspace.urlForApplication(toOpen: url) != nil) case "launch": guard let unwrappedURLString = urlString, let url = URL.init(string: unwrappedURLString) @@ -32,7 +60,7 @@ public class UrlLauncherPlugin: NSObject, FlutterPlugin { result(invalidURLError(urlString)) return } - result(NSWorkspace.shared.open(url)) + result(workspace.open(url)) default: result(FlutterMethodNotImplemented) } diff --git a/packages/url_launcher/url_launcher_macos/pubspec.yaml b/packages/url_launcher/url_launcher_macos/pubspec.yaml index 01f1db0439eb..55e8ec3f6aad 100644 --- a/packages/url_launcher/url_launcher_macos/pubspec.yaml +++ b/packages/url_launcher/url_launcher_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_macos description: macOS implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 2.0.2 +version: 2.0.3 environment: sdk: ">=2.12.0 <3.0.0" From f70794df658b4f83dd73a07e1e8ce8b90ff1bc89 Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Fri, 21 Jan 2022 13:05:12 -0800 Subject: [PATCH 105/600] [camera]rename dispatchQueue to captureSessionQueue (#4687) --- packages/camera/camera/CHANGELOG.md | 4 ++ .../camera/camera/ios/Classes/CameraPlugin.m | 41 ++++++++++--------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index e9db5de89e54..0acd28b1d29e 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Minor internal code cleanup. + ## 0.9.4+6 * Fixes a crash in iOS when using image stream due to calling Flutter engine API on non-main thread. diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.m b/packages/camera/camera/ios/Classes/CameraPlugin.m index 09b5f0ff2e30..f8574dc60732 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin.m +++ b/packages/camera/camera/ios/Classes/CameraPlugin.m @@ -22,23 +22,23 @@ @interface FLTSavePhotoDelegate : NSObject @interface FLTImageStreamHandler : NSObject // The queue on which `eventSink` property should be accessed -@property(nonatomic, strong) dispatch_queue_t dispatchQueue; -// `eventSink` property should be accessed on `dispatchQueue`. +@property(nonatomic, strong) dispatch_queue_t captureSessionQueue; +// `eventSink` property should be accessed on `captureSessionQueue`. // The block itself should be invoked on the main queue. @property FlutterEventSink eventSink; @end @implementation FLTImageStreamHandler -- (instancetype)initWithDispatchQueue:(dispatch_queue_t)dispatchQueue { +- (instancetype)initWithCaptureSessionQueue:(dispatch_queue_t)captureSessionQueue { self = [super init]; NSAssert(self, @"super init cannot be nil"); - _dispatchQueue = dispatchQueue; + _captureSessionQueue = captureSessionQueue; return self; } - (FlutterError *_Nullable)onCancelWithArguments:(id _Nullable)arguments { - dispatch_async(self.dispatchQueue, ^{ + dispatch_async(self.captureSessionQueue, ^{ self.eventSink = nil; }); return nil; @@ -46,7 +46,7 @@ - (FlutterError *_Nullable)onCancelWithArguments:(id _Nullable)arguments { - (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments eventSink:(nonnull FlutterEventSink)events { - dispatch_async(self.dispatchQueue, ^{ + dispatch_async(self.captureSessionQueue, ^{ self.eventSink = events; }); return nil; @@ -360,7 +360,9 @@ @interface FLTCam : NSObject *)messen FLTThreadSafeEventChannel *threadSafeEventChannel = [[FLTThreadSafeEventChannel alloc] initWithEventChannel:eventChannel]; - _imageStreamHandler = [[FLTImageStreamHandler alloc] initWithDispatchQueue:_dispatchQueue]; + _imageStreamHandler = + [[FLTImageStreamHandler alloc] initWithCaptureSessionQueue:_captureSessionQueue]; [threadSafeEventChannel setStreamHandler:_imageStreamHandler completion:^{ - dispatch_async(self->_dispatchQueue, ^{ + dispatch_async(self->_captureSessionQueue, ^{ self.isStreamingImages = YES; }); }]; @@ -1268,7 +1271,7 @@ - (BOOL)setupWriterForPath:(NSString *)path { _audioWriterInput.expectsMediaDataInRealTime = YES; [_videoWriter addInput:_audioWriterInput]; - [_audioOutput setSampleBufferDelegate:self queue:_dispatchQueue]; + [_audioOutput setSampleBufferDelegate:self queue:_captureSessionQueue]; } if (_flashMode == FlashModeTorch) { @@ -1279,7 +1282,7 @@ - (BOOL)setupWriterForPath:(NSString *)path { [_videoWriter addInput:_videoWriterInput]; - [_captureVideoOutput setSampleBufferDelegate:self queue:_dispatchQueue]; + [_captureVideoOutput setSampleBufferDelegate:self queue:_captureSessionQueue]; return YES; } @@ -1320,7 +1323,7 @@ @interface CameraPlugin () @end @implementation CameraPlugin { - dispatch_queue_t _dispatchQueue; + dispatch_queue_t _captureSessionQueue; } + (void)registerWithRegistrar:(NSObject *)registrar { @@ -1382,12 +1385,12 @@ - (void)sendDeviceOrientation:(UIDeviceOrientation)orientation { } - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { - if (_dispatchQueue == nil) { - _dispatchQueue = dispatch_queue_create("io.flutter.camera.dispatchqueue", NULL); + if (_captureSessionQueue == nil) { + _captureSessionQueue = dispatch_queue_create("io.flutter.camera.dispatchqueue", NULL); } // Invoke the plugin on another dispatch queue to avoid blocking the UI. - dispatch_async(_dispatchQueue, ^{ + dispatch_async(_captureSessionQueue, ^{ FLTThreadSafeFlutterResult *threadSafeResult = [[FLTThreadSafeFlutterResult alloc] initWithResult:result]; @@ -1438,7 +1441,7 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call resolutionPreset:resolutionPreset enableAudio:[enableAudio boolValue] orientation:[[UIDevice currentDevice] orientation] - dispatchQueue:_dispatchQueue + captureSessionQueue:_captureSessionQueue error:&error]; if (error) { @@ -1504,7 +1507,7 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call } else if ([@"dispose" isEqualToString:call.method]) { [_registry unregisterTexture:cameraId]; [_camera close]; - _dispatchQueue = nil; + _captureSessionQueue = nil; [result sendSuccess]; } else if ([@"prepareForVideoRecording" isEqualToString:call.method]) { [_camera setUpCaptureSessionForAudio]; From 3fb9e2c13b4062424e04e4b84e05d42980f637fe Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Fri, 21 Jan 2022 13:54:18 -0800 Subject: [PATCH 106/600] [video_player] Remove KVO observer on AVPlayerItem on iOS (#4683) --- .../video_player/video_player/CHANGELOG.md | 4 + .../ios/RunnerTests/VideoPlayerTests.m | 52 ++++++-- .../ios/Classes/FLTVideoPlayerPlugin.m | 114 ++++++++---------- .../video_player/video_player/pubspec.yaml | 2 +- 4 files changed, 98 insertions(+), 74 deletions(-) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 58fa0844bbe7..dfd4a31121aa 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.2.14 + +* Removes KVO observer on AVPlayerItem on iOS. + ## 2.2.13 * Fixes persisting of hasError even after successful initialize. diff --git a/packages/video_player/video_player/example/ios/RunnerTests/VideoPlayerTests.m b/packages/video_player/video_player/example/ios/RunnerTests/VideoPlayerTests.m index deea83350bce..90c7dc2ee95d 100644 --- a/packages/video_player/video_player/example/ios/RunnerTests/VideoPlayerTests.m +++ b/packages/video_player/video_player/example/ios/RunnerTests/VideoPlayerTests.m @@ -2,34 +2,37 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +@import AVFoundation; @import video_player; @import XCTest; #import +@interface FLTVideoPlayer : NSObject +@property(readonly, nonatomic) AVPlayer *player; +@end + +@interface FLTVideoPlayerPlugin (Test) +@property(readonly, strong, nonatomic) + NSMutableDictionary *playersByTextureId; +@end + @interface VideoPlayerTests : XCTestCase @end @implementation VideoPlayerTests -- (void)testPlugin { - FLTVideoPlayerPlugin *plugin = [[FLTVideoPlayerPlugin alloc] init]; - XCTAssertNotNil(plugin); -} - - (void)testSeekToInvokesTextureFrameAvailableOnTextureRegistry { NSObject *mockTextureRegistry = OCMProtocolMock(@protocol(FlutterTextureRegistry)); NSObject *registry = (NSObject *)[[UIApplication sharedApplication] delegate]; NSObject *registrar = - [registry registrarForPlugin:@"TEST_FLTVideoPlayerPlugin"]; + [registry registrarForPlugin:@"SeekToInvokestextureFrameAvailable"]; NSObject *partialRegistrar = OCMPartialMock(registrar); OCMStub([partialRegistrar textures]).andReturn(mockTextureRegistry); - [FLTVideoPlayerPlugin registerWithRegistrar:partialRegistrar]; - FLTVideoPlayerPlugin *videoPlayerPlugin = - (FLTVideoPlayerPlugin *)[[FLTVideoPlayerPlugin alloc] - initWithRegistrar:partialRegistrar]; + FLTVideoPlayerPlugin *videoPlayerPlugin = + (FLTVideoPlayerPlugin *)[[FLTVideoPlayerPlugin alloc] initWithRegistrar:partialRegistrar]; FLTPositionMessage *message = [[FLTPositionMessage alloc] init]; message.textureId = @101; message.position = @0; @@ -38,4 +41,33 @@ - (void)testSeekToInvokesTextureFrameAvailableOnTextureRegistry { OCMVerify([mockTextureRegistry textureFrameAvailable:message.textureId.intValue]); } +- (void)testDeregistersFromPlayer { + NSObject *registry = + (NSObject *)[[UIApplication sharedApplication] delegate]; + NSObject *registrar = + [registry registrarForPlugin:@"testDeregistersFromPlayer"]; + FLTVideoPlayerPlugin *videoPlayerPlugin = + (FLTVideoPlayerPlugin *)[[FLTVideoPlayerPlugin alloc] initWithRegistrar:registrar]; + + FlutterError *error; + [videoPlayerPlugin initialize:&error]; + XCTAssertNil(error); + + FLTCreateMessage *create = [[FLTCreateMessage alloc] init]; + create.uri = @"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4"; + FLTTextureMessage *textureMessage = [videoPlayerPlugin create:create error:&error]; + XCTAssertNil(error); + XCTAssertNotNil(textureMessage); + FLTVideoPlayer *player = videoPlayerPlugin.playersByTextureId[textureMessage.textureId]; + XCTAssertNotNil(player); + AVPlayer *avPlayer = player.player; + + [videoPlayerPlugin dispose:textureMessage error:&error]; + XCTAssertEqual(videoPlayerPlugin.playersByTextureId.count, 0); + XCTAssertNil(error); + + [self keyValueObservingExpectationForObject:avPlayer keyPath:@"currentItem" expectedValue:nil]; + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + @end diff --git a/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m b/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m index d5bea174e61d..a67e37138b9a 100644 --- a/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m +++ b/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m @@ -37,17 +37,13 @@ @interface FLTVideoPlayer : NSObject @property(nonatomic) FlutterEventChannel* eventChannel; @property(nonatomic) FlutterEventSink eventSink; @property(nonatomic) CGAffineTransform preferredTransform; -@property(nonatomic, readonly) bool disposed; -@property(nonatomic, readonly) bool isPlaying; -@property(nonatomic) bool isLooping; -@property(nonatomic, readonly) bool isInitialized; +@property(nonatomic, readonly) BOOL disposed; +@property(nonatomic, readonly) BOOL isPlaying; +@property(nonatomic) BOOL isLooping; +@property(nonatomic, readonly) BOOL isInitialized; - (instancetype)initWithURL:(NSURL*)url frameUpdater:(FLTFrameUpdater*)frameUpdater httpHeaders:(NSDictionary*)headers; -- (void)play; -- (void)pause; -- (void)setIsLooping:(bool)isLooping; -- (void)updatePlayingState; @end static void* timeRangeContext = &timeRangeContext; @@ -114,7 +110,7 @@ - (void)itemDidPlayToEndTime:(NSNotification*)notification { const int64_t TIME_UNSET = -9223372036854775807; -static inline int64_t FLTCMTimeToMillis(CMTime time) { +NS_INLINE int64_t FLTCMTimeToMillis(CMTime time) { // When CMTIME_IS_INDEFINITE return a value that matches TIME_UNSET from ExoPlayer2 on Android. // Fixes https://github.com/flutter/flutter/issues/48670 if (CMTIME_IS_INDEFINITE(time)) return TIME_UNSET; @@ -122,14 +118,14 @@ static inline int64_t FLTCMTimeToMillis(CMTime time) { return time.value * 1000 / time.timescale; } -static inline CGFloat radiansToDegrees(CGFloat radians) { +NS_INLINE CGFloat radiansToDegrees(CGFloat radians) { // Input range [-pi, pi] or [-180, 180] CGFloat degrees = GLKMathRadiansToDegrees((float)radians); if (degrees < 0) { // Convert -90 to 270 and -180 to 180 return degrees + 360; } - // Output degrees in between [0, 360[ + // Output degrees in between [0, 360] return degrees; }; @@ -217,9 +213,6 @@ - (CGAffineTransform)fixTransform:(AVAssetTrack*)videoTrack { - (instancetype)initWithPlayerItem:(AVPlayerItem*)item frameUpdater:(FLTFrameUpdater*)frameUpdater { self = [super init]; NSAssert(self, @"super init cannot be nil"); - _isInitialized = false; - _isPlaying = false; - _disposed = false; AVAsset* asset = [item asset]; void (^assetCompletionHandler)(void) = ^{ @@ -352,7 +345,7 @@ - (void)setupEventSinkIfReadyToPlay { return; } - _isInitialized = true; + _isInitialized = YES; _eventSink(@{ @"event" : @"initialized", @"duration" : @([self duration]), @@ -363,12 +356,12 @@ - (void)setupEventSinkIfReadyToPlay { } - (void)play { - _isPlaying = true; + _isPlaying = YES; [self updatePlayingState]; } - (void)pause { - _isPlaying = false; + _isPlaying = NO; [self updatePlayingState]; } @@ -389,7 +382,7 @@ - (void)seekTo:(int)location { toleranceAfter:kCMTimeZero]; } -- (void)setIsLooping:(bool)isLooping { +- (void)setIsLooping:(BOOL)isLooping { _isLooping = isLooping; } @@ -457,22 +450,18 @@ - (FlutterError* _Nullable)onListenWithArguments:(id _Nullable)arguments /// is useful for the case where the Engine is in the process of deconstruction /// so the channel is going to die or is already dead. - (void)disposeSansEventChannel { - _disposed = true; + _disposed = YES; [_displayLink invalidate]; - [[_player currentItem] removeObserver:self forKeyPath:@"status" context:statusContext]; - [[_player currentItem] removeObserver:self - forKeyPath:@"loadedTimeRanges" - context:timeRangeContext]; - [[_player currentItem] removeObserver:self - forKeyPath:@"playbackLikelyToKeepUp" - context:playbackLikelyToKeepUpContext]; - [[_player currentItem] removeObserver:self - forKeyPath:@"playbackBufferEmpty" - context:playbackBufferEmptyContext]; - [[_player currentItem] removeObserver:self - forKeyPath:@"playbackBufferFull" - context:playbackBufferFullContext]; - [_player replaceCurrentItemWithPlayerItem:nil]; + AVPlayerItem* currentItem = self.player.currentItem; + [currentItem removeObserver:self forKeyPath:@"status"]; + [currentItem removeObserver:self forKeyPath:@"loadedTimeRanges"]; + [currentItem removeObserver:self forKeyPath:@"presentationSize"]; + [currentItem removeObserver:self forKeyPath:@"duration"]; + [currentItem removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"]; + [currentItem removeObserver:self forKeyPath:@"playbackBufferEmpty"]; + [currentItem removeObserver:self forKeyPath:@"playbackBufferFull"]; + + [self.player replaceCurrentItemWithPlayerItem:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self]; } @@ -486,7 +475,8 @@ - (void)dispose { @interface FLTVideoPlayerPlugin () @property(readonly, weak, nonatomic) NSObject* registry; @property(readonly, weak, nonatomic) NSObject* messenger; -@property(readonly, strong, nonatomic) NSMutableDictionary* players; +@property(readonly, strong, nonatomic) + NSMutableDictionary* playersByTextureId; @property(readonly, strong, nonatomic) NSObject* registrar; @end @@ -503,16 +493,13 @@ - (instancetype)initWithRegistrar:(NSObject*)registrar { _registry = [registrar textures]; _messenger = [registrar messenger]; _registrar = registrar; - _players = [NSMutableDictionary dictionaryWithCapacity:1]; + _playersByTextureId = [NSMutableDictionary dictionaryWithCapacity:1]; return self; } - (void)detachFromEngineForRegistrar:(NSObject*)registrar { - for (NSNumber* textureId in _players.allKeys) { - FLTVideoPlayer* player = _players[textureId]; - [player disposeSansEventChannel]; - } - [_players removeAllObjects]; + [self.playersByTextureId.allValues makeObjectsPerformSelector:@selector(disposeSansEventChannel)]; + [self.playersByTextureId removeAllObjects]; // TODO(57151): This should be commented out when 57151's fix lands on stable. // This is the correct behavior we never did it in the past and the engine // doesn't currently support it. @@ -521,7 +508,7 @@ - (void)detachFromEngineForRegistrar:(NSObject*)registra - (FLTTextureMessage*)onPlayerSetup:(FLTVideoPlayer*)player frameUpdater:(FLTFrameUpdater*)frameUpdater { - int64_t textureId = [_registry registerTexture:player]; + int64_t textureId = [self.registry registerTexture:player]; frameUpdater.textureId = textureId; FlutterEventChannel* eventChannel = [FlutterEventChannel eventChannelWithName:[NSString stringWithFormat:@"flutter.io/videoPlayer/videoEvents%lld", @@ -529,7 +516,7 @@ - (FLTTextureMessage*)onPlayerSetup:(FLTVideoPlayer*)player binaryMessenger:_messenger]; [eventChannel setStreamHandler:player]; player.eventChannel = eventChannel; - _players[@(textureId)] = player; + self.playersByTextureId[@(textureId)] = player; FLTTextureMessage* result = [[FLTTextureMessage alloc] init]; result.textureId = @(textureId); return result; @@ -539,11 +526,12 @@ - (void)initialize:(FlutterError* __autoreleasing*)error { // Allow audio playback when the Ring/Silent switch is set to silent [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil]; - for (NSNumber* textureId in _players) { - [_registry unregisterTexture:[textureId unsignedIntegerValue]]; - [_players[textureId] dispose]; - } - [_players removeAllObjects]; + [self.playersByTextureId + enumerateKeysAndObjectsUsingBlock:^(NSNumber* textureId, FLTVideoPlayer* player, BOOL* stop) { + [self.registry unregisterTexture:textureId.unsignedIntegerValue]; + [player dispose]; + }]; + [self.playersByTextureId removeAllObjects]; } - (FLTTextureMessage*)create:(FLTCreateMessage*)input error:(FlutterError**)error { @@ -570,9 +558,9 @@ - (FLTTextureMessage*)create:(FLTCreateMessage*)input error:(FlutterError**)erro } - (void)dispose:(FLTTextureMessage*)input error:(FlutterError**)error { - FLTVideoPlayer* player = _players[input.textureId]; - [_registry unregisterTexture:input.textureId.intValue]; - [_players removeObjectForKey:input.textureId]; + FLTVideoPlayer* player = self.playersByTextureId[input.textureId]; + [self.registry unregisterTexture:input.textureId.intValue]; + [self.playersByTextureId removeObjectForKey:input.textureId]; // If the Flutter contains https://github.com/flutter/engine/pull/12695, // the `player` is disposed via `onTextureUnregistered` at the right time. // Without https://github.com/flutter/engine/pull/12695, there is no guarantee that the @@ -592,46 +580,46 @@ - (void)dispose:(FLTTextureMessage*)input error:(FlutterError**)error { } - (void)setLooping:(FLTLoopingMessage*)input error:(FlutterError**)error { - FLTVideoPlayer* player = _players[input.textureId]; - [player setIsLooping:[input.isLooping boolValue]]; + FLTVideoPlayer* player = self.playersByTextureId[input.textureId]; + player.isLooping = input.isLooping.boolValue; } - (void)setVolume:(FLTVolumeMessage*)input error:(FlutterError**)error { - FLTVideoPlayer* player = _players[input.textureId]; - [player setVolume:[input.volume doubleValue]]; + FLTVideoPlayer* player = self.playersByTextureId[input.textureId]; + [player setVolume:input.volume.doubleValue]; } - (void)setPlaybackSpeed:(FLTPlaybackSpeedMessage*)input error:(FlutterError**)error { - FLTVideoPlayer* player = _players[input.textureId]; - [player setPlaybackSpeed:[input.speed doubleValue]]; + FLTVideoPlayer* player = self.playersByTextureId[input.textureId]; + [player setPlaybackSpeed:input.speed.doubleValue]; } - (void)play:(FLTTextureMessage*)input error:(FlutterError**)error { - FLTVideoPlayer* player = _players[input.textureId]; + FLTVideoPlayer* player = self.playersByTextureId[input.textureId]; [player play]; } - (FLTPositionMessage*)position:(FLTTextureMessage*)input error:(FlutterError**)error { - FLTVideoPlayer* player = _players[input.textureId]; + FLTVideoPlayer* player = self.playersByTextureId[input.textureId]; FLTPositionMessage* result = [[FLTPositionMessage alloc] init]; result.position = @([player position]); return result; } - (void)seekTo:(FLTPositionMessage*)input error:(FlutterError**)error { - FLTVideoPlayer* player = _players[input.textureId]; - [player seekTo:[input.position intValue]]; - [_registry textureFrameAvailable:input.textureId.intValue]; + FLTVideoPlayer* player = self.playersByTextureId[input.textureId]; + [player seekTo:input.position.intValue]; + [self.registry textureFrameAvailable:input.textureId.intValue]; } - (void)pause:(FLTTextureMessage*)input error:(FlutterError**)error { - FLTVideoPlayer* player = _players[input.textureId]; + FLTVideoPlayer* player = self.playersByTextureId[input.textureId]; [player pause]; } - (void)setMixWithOthers:(FLTMixWithOthersMessage*)input error:(FlutterError* _Nullable __autoreleasing*)error { - if ([input.mixWithOthers boolValue]) { + if (input.mixWithOthers.boolValue) { [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil]; diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 98c9b950871b..0260b8474d7a 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for displaying inline video with other Flutter widgets on Android, iOS, and web. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.2.13 +version: 2.2.14 environment: sdk: ">=2.14.0 <3.0.0" From d6619d8e8b47aecf8513dd120cc33625ddd56e77 Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Tue, 25 Jan 2022 00:02:03 +0530 Subject: [PATCH 107/600] Typo fixes in repository documentation (#4674) --- CONTRIBUTING.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0d135b3e80cc..52518202fa84 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,7 +18,7 @@ Additional resources specific to the plugins repository: - [Plugin tests](https://github.com/flutter/flutter/wiki/Plugin-Tests), which explains the different kinds of tests used for plugins, where to find them, and how to run them. As explained in the Flutter guide, - [**PRs needs tests**](https://github.com/flutter/flutter/wiki/Tree-hygiene#tests), so + [**PRs need tests**](https://github.com/flutter/flutter/wiki/Tree-hygiene#tests), so this is critical to read before submitting a PR. - [Contributing to Plugins and Packages](https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages), for more information about how to make PRs for this repository, especially when diff --git a/README.md b/README.md index 8e369c3b8b62..30c6a2f00e66 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ see the documentation for [developing packages](https://flutter.dev/developing-p [platform channels](https://flutter.dev/platform-channels/). You can store your plugin source code in any GitHub repository (the present repo is only intended for plugins developed by the core Flutter team). Once your plugin -is ready you can [publish](https://flutter.dev/developing-packages/#publish) +is ready, you can [publish](https://flutter.dev/developing-packages/#publish) it to the [pub repository](https://pub.dev/). If you wish to contribute a change to any of the existing plugins in this repo, From 9861088164461ee26c5411bb8409a22d01dcb30b Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 24 Jan 2022 16:30:31 -0500 Subject: [PATCH 108/600] [video_player] Update iOS README section (#4688) Removes the suggestion to just use the strongly-discouraged-by-Apple `NSAllowsArbitraryLoads`, and instead links to Apple's docs about the various options. This removes the example entirely since there's no one-size-fits-all case; some clients would just need `NSExceptionDomains` (this seems likely to be the more common case?), others `NSAllowsArbitraryLoadsForMedia`, others `NSAllowsArbitraryLoadsForMedia`+` NSAllowsArbitraryLoads` (to support iOS 9). Given that the narrowest option that works for someone is the best option, having them actually research the options is the best thing to steer people toward, even if that's not as simple as "copy and paste the following". Also opportunistically removes a couple of obsolete items: - An incorrect note that `android.permission.INTERNET` is likely already present in the main Android manifest file; it was made debug-only a while ago. - An old note about web version compatibility; we only support Flutter 2+ now anyway. --- .../video_player/video_player/CHANGELOG.md | 4 ++++ packages/video_player/video_player/README.md | 23 ++++++------------- .../video_player/video_player/pubspec.yaml | 2 +- 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index dfd4a31121aa..b7225a8603ab 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.2.15 + +* Updates README discussion of permissions. + ## 2.2.14 * Removes KVO observer on AVPlayerItem on iOS. diff --git a/packages/video_player/video_player/README.md b/packages/video_player/video_player/README.md index d5e7528fa973..84b3ae7dfb54 100644 --- a/packages/video_player/video_player/README.md +++ b/packages/video_player/video_player/README.md @@ -12,32 +12,23 @@ First, add `video_player` as a [dependency in your pubspec.yaml file](https://fl ### iOS -This plugin requires iOS 9.0 or higher. Add the following entry to your _Info.plist_ file, located in `/ios/Runner/Info.plist`: - -```xml -NSAppTransportSecurity - - NSAllowsArbitraryLoads - - -``` - -This entry allows your app to access video files by URL. +If you need to access videos using `http` (rather than `https`) URLs, you will need to add +the appropriate `NSAppTransportSecurity` permissions to your app's _Info.plist_ file, located +in `/ios/Runner/Info.plist`. See +[Apple's documentation](https://developer.apple.com/documentation/bundleresources/information_property_list/nsapptransportsecurity) +to determine the right combination of entries for your use case and supported iOS versions. ### Android -Ensure the following permission is present in your Android Manifest file, located in `/android/app/src/main/AndroidManifest.xml`: +If you are using network-based videos, ensure that the following permission is present in your +Android Manifest file, located in `/android/app/src/main/AndroidManifest.xml`: ```xml ``` -The Flutter project template adds it, so it may already be there. - ### Web -This plugin compiles for the web platform since version `0.10.5`, in recent enough versions of Flutter (`>=1.12.13+hotfix.4`). - > The Web platform does **not** suppport `dart:io`, so avoid using the `VideoPlayerController.file` constructor for the plugin. Using the constructor attempts to create a `VideoPlayerController.file` that will throw an `UnimplementedError`. Different web browsers may have different video-playback capabilities (supported formats, autoplay...). Check [package:video_player_web](https://pub.dev/packages/video_player_web) for more web-specific information. diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 0260b8474d7a..24aef067654f 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for displaying inline video with other Flutter widgets on Android, iOS, and web. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.2.14 +version: 2.2.15 environment: sdk: ">=2.14.0 <3.0.0" From c0b403222a4f1ade3bd034b93887469c4184f593 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 24 Jan 2022 19:30:31 -0500 Subject: [PATCH 109/600] [webview_flutter_web] Explain web registration (#4668) The README instructions currently use the standard description of using an unendorsed implementation of a federated package, which assumes auto-registration. Because `webview_flutter_web` doesn't use autoregistration due to the current structural issues in `webview_flutter`'s federation, those instructions aren't complete. This adds an explanation of registering the implementation, including an example of using conditional imports for multi-platform projects. --- .../webview_flutter_web/CHANGELOG.md | 7 +- .../webview_flutter_web/README.md | 64 ++++++++++++++++++- .../webview_flutter_web/pubspec.yaml | 2 +- 3 files changed, 67 insertions(+), 6 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md index 11cfedaaf7e4..3f9f2c80c6af 100644 --- a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md @@ -1,4 +1,7 @@ -## 0.1.0 +## 0.1.0+1 + +* Adds an explanation of registering the implementation in the README. -* First web implementation for webview_flutter +## 0.1.0 +* First web implementation for webview_flutter diff --git a/packages/webview_flutter/webview_flutter_web/README.md b/packages/webview_flutter/webview_flutter_web/README.md index 9e7e1c6499fe..a7711ee171e2 100644 --- a/packages/webview_flutter/webview_flutter_web/README.md +++ b/packages/webview_flutter/webview_flutter_web/README.md @@ -14,7 +14,65 @@ Nothing else is currently supported. ## Usage -### Depend on the package +This package is not an endorsed implementation of the `webview_flutter` plugin +yet, so it currently requires extra setup to use: -This package is not an endorsed implementation of the `webview_flutter` plugin yet, so you'll need to -[add it explicitly](https://pub.dev/packages/webview_flutter_web/install). \ No newline at end of file +* [Add this package](https://pub.dev/packages/webview_flutter_web/install) + as an explicit dependency of your project, in addition to depending on + `webview_flutter`. +* Register `WebWebViewPlatform` as the `WebView.platform` before creating a + `WebView`. See below for examples. + +Once those steps below are complete, the APIs from `webview_flutter` listed +above can be used as normal on web. + +### Registering the implementation + +Before creating a `WebView` (for instance, at the start of `main`), you will +need to register the web implementation. + +#### Web-only project example + +```dart +... +import 'package:webview_flutter/webview_flutter.dart'; +import 'package:webview_flutter_web/webview_flutter_web.dart'; + +main() { + WebView.platform = WebWebViewPlatform(); + ... +``` + +#### Multi-platform project example + +If your project supports platforms other than web, you will need to use a +conditional import to avoid directly including `webview_flutter_web.dart` on +non-web platforms. For example: + +`register_web_webview.dart`: +```dart +import 'package:webview_flutter/webview_flutter.dart'; +import 'package:webview_flutter_web/webview_flutter_web.dart'; + +void registerWebViewWebImplementation() { + WebView.platform = WebWebViewPlatform(); +} +``` + +`register_web_webview_stub.dart`: +```dart +void registerWebViewWebImplementation() { + // No-op. +} +``` + +`main.dart`: +```dart +... +import 'register_web_webview_stub.dart' + if (dart.library.html) 'register_web.dart'; + +main() { + registerWebViewWebImplementation(); + ... +``` diff --git a/packages/webview_flutter/webview_flutter_web/pubspec.yaml b/packages/webview_flutter/webview_flutter_web/pubspec.yaml index 3b24476283ec..3588e52bfda9 100644 --- a/packages/webview_flutter/webview_flutter_web/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_web/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_web description: A Flutter plugin that provides a WebView widget on web. repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 0.1.0 +version: 0.1.0+1 environment: sdk: ">=2.14.0 <3.0.0" From f48198e40ee3bc2b4390f0a8b86c448c5444f616 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 24 Jan 2022 23:25:15 -0500 Subject: [PATCH 110/600] [all] Update repo links to 'main' (#4690) --- packages/camera/camera/README.md | 2 +- packages/camera/camera/ios/camera.podspec | 2 +- packages/camera/camera/pubspec.yaml | 2 +- packages/camera/camera_platform_interface/pubspec.yaml | 2 +- packages/camera/camera_web/pubspec.yaml | 2 +- packages/espresso/pubspec.yaml | 2 +- packages/file_selector/file_selector/pubspec.yaml | 2 +- .../file_selector/file_selector_platform_interface/pubspec.yaml | 2 +- packages/file_selector/file_selector_web/pubspec.yaml | 2 +- packages/flutter_plugin_android_lifecycle/pubspec.yaml | 2 +- .../google_maps_flutter/ios/google_maps_flutter.podspec | 2 +- packages/google_maps_flutter/google_maps_flutter/pubspec.yaml | 2 +- .../google_maps_flutter_platform_interface/pubspec.yaml | 2 +- .../google_maps_flutter/google_maps_flutter_web/pubspec.yaml | 2 +- .../google_sign_in/google_sign_in/ios/google_sign_in.podspec | 2 +- packages/google_sign_in/google_sign_in/pubspec.yaml | 2 +- .../google_sign_in_platform_interface/pubspec.yaml | 2 +- packages/google_sign_in/google_sign_in_web/pubspec.yaml | 2 +- packages/image_picker/image_picker/ios/image_picker.podspec | 2 +- packages/image_picker/image_picker/pubspec.yaml | 2 +- packages/image_picker/image_picker_for_web/pubspec.yaml | 2 +- .../image_picker/image_picker_platform_interface/pubspec.yaml | 2 +- packages/in_app_purchase/in_app_purchase/pubspec.yaml | 2 +- packages/in_app_purchase/in_app_purchase_android/pubspec.yaml | 2 +- .../in_app_purchase_platform_interface/pubspec.yaml | 2 +- .../ios/in_app_purchase_storekit.podspec | 2 +- packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml | 2 +- packages/ios_platform_images/ios/ios_platform_images.podspec | 2 +- packages/ios_platform_images/pubspec.yaml | 2 +- packages/local_auth/ios/local_auth.podspec | 2 +- packages/local_auth/pubspec.yaml | 2 +- packages/path_provider/path_provider/pubspec.yaml | 2 +- packages/path_provider/path_provider_android/pubspec.yaml | 2 +- .../path_provider_ios/ios/path_provider_ios.podspec | 2 +- packages/path_provider/path_provider_ios/pubspec.yaml | 2 +- packages/path_provider/path_provider_linux/pubspec.yaml | 2 +- .../path_provider_macos/macos/path_provider_macos.podspec | 2 +- packages/path_provider/path_provider_macos/pubspec.yaml | 2 +- .../path_provider/path_provider_platform_interface/pubspec.yaml | 2 +- packages/path_provider/path_provider_windows/pubspec.yaml | 2 +- packages/plugin_platform_interface/pubspec.yaml | 2 +- packages/quick_actions/quick_actions/ios/quick_actions.podspec | 2 +- packages/quick_actions/quick_actions/pubspec.yaml | 2 +- .../quick_actions/quick_actions_platform_interface/pubspec.yaml | 2 +- packages/shared_preferences/shared_preferences/pubspec.yaml | 2 +- .../shared_preferences/shared_preferences_android/pubspec.yaml | 2 +- .../shared_preferences_ios/ios/shared_preferences_ios.podspec | 2 +- packages/shared_preferences/shared_preferences_ios/pubspec.yaml | 2 +- .../shared_preferences/shared_preferences_linux/pubspec.yaml | 2 +- .../macos/shared_preferences_macos.podspec | 2 +- .../shared_preferences/shared_preferences_macos/pubspec.yaml | 2 +- .../shared_preferences_platform_interface/pubspec.yaml | 2 +- packages/shared_preferences/shared_preferences_web/pubspec.yaml | 2 +- .../shared_preferences/shared_preferences_windows/pubspec.yaml | 2 +- packages/url_launcher/url_launcher/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_android/pubspec.yaml | 2 +- .../url_launcher/url_launcher_ios/ios/url_launcher_ios.podspec | 2 +- packages/url_launcher/url_launcher_ios/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_linux/pubspec.yaml | 2 +- .../url_launcher_macos/macos/url_launcher_macos.podspec | 2 +- packages/url_launcher/url_launcher_macos/pubspec.yaml | 2 +- .../url_launcher/url_launcher_platform_interface/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_web/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_windows/pubspec.yaml | 2 +- packages/video_player/video_player/ios/video_player.podspec | 2 +- packages/video_player/video_player/pubspec.yaml | 2 +- .../video_player/video_player_platform_interface/pubspec.yaml | 2 +- packages/video_player/video_player_web/pubspec.yaml | 2 +- packages/webview_flutter/webview_flutter/pubspec.yaml | 2 +- packages/webview_flutter/webview_flutter_android/pubspec.yaml | 2 +- .../webview_flutter_platform_interface/pubspec.yaml | 2 +- packages/webview_flutter/webview_flutter_web/pubspec.yaml | 2 +- .../ios/webview_flutter_wkwebview.podspec | 2 +- packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml | 2 +- script/tool/pubspec.yaml | 2 +- 75 files changed, 75 insertions(+), 75 deletions(-) diff --git a/packages/camera/camera/README.md b/packages/camera/camera/README.md index 24566e76bbfc..6cca254f8690 100644 --- a/packages/camera/camera/README.md +++ b/packages/camera/camera/README.md @@ -130,7 +130,7 @@ class _CameraAppState extends State { ``` -For a more elaborate usage example see [here](https://github.com/flutter/plugins/tree/master/packages/camera/camera/example). +For a more elaborate usage example see [here](https://github.com/flutter/plugins/tree/main/packages/camera/camera/example). *Note*: This plugin is still under development, and some APIs might not be available yet. [Feedback welcome](https://github.com/flutter/flutter/issues) and diff --git a/packages/camera/camera/ios/camera.podspec b/packages/camera/camera/ios/camera.podspec index 5906bf98af45..098b011a47a9 100644 --- a/packages/camera/camera/ios/camera.podspec +++ b/packages/camera/camera/ios/camera.podspec @@ -11,7 +11,7 @@ A Flutter plugin to use the camera from your Flutter app. s.homepage = 'https://github.com/flutter/plugins' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/camera' } + s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/camera' } s.documentation_url = 'https://pub.dev/packages/camera' s.source_files = 'Classes/**/*.{h,m}' s.public_header_files = 'Classes/**/*.h' diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 4c6c7e7e52d1..05c8b9df35cf 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -2,7 +2,7 @@ name: camera description: A Flutter plugin for controlling the camera. Supports previewing the camera feed, capturing images and video, and streaming image buffers to Dart. -repository: https://github.com/flutter/plugins/tree/master/packages/camera/camera +repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 version: 0.9.4+6 diff --git a/packages/camera/camera_platform_interface/pubspec.yaml b/packages/camera/camera_platform_interface/pubspec.yaml index cff5add8b2d3..bd6aa5a1cda2 100644 --- a/packages/camera/camera_platform_interface/pubspec.yaml +++ b/packages/camera/camera_platform_interface/pubspec.yaml @@ -1,6 +1,6 @@ name: camera_platform_interface description: A common platform interface for the camera plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/camera/camera_platform_interface +repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_platform_interface issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index ce09f7ed9d64..c75183de1e12 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -1,6 +1,6 @@ name: camera_web description: A Flutter plugin for getting information about and controlling the camera on Web. -repository: https://github.com/flutter/plugins/tree/master/packages/camera/camera_web +repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 version: 0.2.1+1 diff --git a/packages/espresso/pubspec.yaml b/packages/espresso/pubspec.yaml index c0f3b00d556c..1836e7afb575 100644 --- a/packages/espresso/pubspec.yaml +++ b/packages/espresso/pubspec.yaml @@ -1,7 +1,7 @@ name: espresso description: Java classes for testing Flutter apps using Espresso. Allows driving Flutter widgets from a native Espresso test. -repository: https://github.com/flutter/plugins/tree/master/packages/espresso +repository: https://github.com/flutter/plugins/tree/main/packages/espresso issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+espresso%22 version: 0.1.0+4 diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index d69217f208ef..e4857c2ddd6e 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -1,7 +1,7 @@ name: file_selector description: Flutter plugin for opening and saving files, or selecting directories, using native file selection UI. -repository: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector +repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 version: 0.8.2+1 diff --git a/packages/file_selector/file_selector_platform_interface/pubspec.yaml b/packages/file_selector/file_selector_platform_interface/pubspec.yaml index f7b66a763b9a..42917751ed58 100644 --- a/packages/file_selector/file_selector_platform_interface/pubspec.yaml +++ b/packages/file_selector/file_selector_platform_interface/pubspec.yaml @@ -1,6 +1,6 @@ name: file_selector_platform_interface description: A common platform interface for the file_selector plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector_platform_interface +repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_platform_interface issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes diff --git a/packages/file_selector/file_selector_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml index 9cb8824afafd..74d0412b440f 100644 --- a/packages/file_selector/file_selector_web/pubspec.yaml +++ b/packages/file_selector/file_selector_web/pubspec.yaml @@ -1,6 +1,6 @@ name: file_selector_web description: Web platform implementation of file_selector -repository: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector_web +repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 version: 0.8.1+3 diff --git a/packages/flutter_plugin_android_lifecycle/pubspec.yaml b/packages/flutter_plugin_android_lifecycle/pubspec.yaml index 20cae3526e82..b359cc55c351 100644 --- a/packages/flutter_plugin_android_lifecycle/pubspec.yaml +++ b/packages/flutter_plugin_android_lifecycle/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_plugin_android_lifecycle description: Flutter plugin for accessing an Android Lifecycle within other plugins. -repository: https://github.com/flutter/plugins/tree/master/packages/flutter_plugin_android_lifecycle +repository: https://github.com/flutter/plugins/tree/main/packages/flutter_plugin_android_lifecycle issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_plugin_android_lifecycle%22 version: 2.0.5 diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/google_maps_flutter.podspec b/packages/google_maps_flutter/google_maps_flutter/ios/google_maps_flutter.podspec index 35e4f3faf871..f2ed5fc56ea0 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/google_maps_flutter.podspec +++ b/packages/google_maps_flutter/google_maps_flutter/ios/google_maps_flutter.podspec @@ -12,7 +12,7 @@ Downloaded by pub (not CocoaPods). s.homepage = 'https://github.com/flutter/plugins' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/google_maps_flutter/google_maps_flutter' } + s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/google_maps_flutter/google_maps_flutter' } s.documentation_url = 'https://pub.dev/packages/google_maps_flutter' s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index f20c574e831e..e8bd391810d7 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -1,6 +1,6 @@ name: google_maps_flutter description: A Flutter plugin for integrating Google Maps in iOS and Android applications. -repository: https://github.com/flutter/plugins/tree/master/packages/google_maps_flutter/google_maps_flutter +repository: https://github.com/flutter/plugins/tree/main/packages/google_maps_flutter/google_maps_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 version: 2.1.1 diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml index 687640c0797b..6125dd43d9f6 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml @@ -1,6 +1,6 @@ name: google_maps_flutter_platform_interface description: A common platform interface for the google_maps_flutter plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/google_maps_flutter/google_maps_flutter_platform_interface +repository: https://github.com/flutter/plugins/tree/main/packages/google_maps_flutter/google_maps_flutter_platform_interface issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes diff --git a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml index c0c27433bea8..2780175d29e2 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -1,6 +1,6 @@ name: google_maps_flutter_web description: Web platform implementation of google_maps_flutter -repository: https://github.com/flutter/plugins/tree/master/packages/google_maps_flutter/google_maps_flutter_web +repository: https://github.com/flutter/plugins/tree/main/packages/google_maps_flutter/google_maps_flutter_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 version: 0.3.2+1 diff --git a/packages/google_sign_in/google_sign_in/ios/google_sign_in.podspec b/packages/google_sign_in/google_sign_in/ios/google_sign_in.podspec index 270ca274f3e4..19ea753520a7 100644 --- a/packages/google_sign_in/google_sign_in/ios/google_sign_in.podspec +++ b/packages/google_sign_in/google_sign_in/ios/google_sign_in.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.description = <<-DESC Enables Google Sign-In in Flutter apps. DESC - s.homepage = 'https://github.com/flutter/plugins/tree/master/packages/google_sign_in' + s.homepage = 'https://github.com/flutter/plugins/tree/main/packages/google_sign_in' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Team' => 'flutter-dev@googlegroups.com' } s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/google_sign_in' } diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index 85608794e03d..40f8d0f5f02c 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/pubspec.yaml @@ -1,7 +1,7 @@ name: google_sign_in description: Flutter plugin for Google Sign-In, a secure authentication system for signing in with a Google account on Android and iOS. -repository: https://github.com/flutter/plugins/tree/master/packages/google_sign_in/google_sign_in +repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 version: 5.2.3 diff --git a/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml b/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml index fcf83bc34d5c..f5a7eb866ef9 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml @@ -1,6 +1,6 @@ name: google_sign_in_platform_interface description: A common platform interface for the google_sign_in plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/google_sign_in/google_sign_in_platform_interface +repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in_platform_interface issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes diff --git a/packages/google_sign_in/google_sign_in_web/pubspec.yaml b/packages/google_sign_in/google_sign_in_web/pubspec.yaml index ec61f30a77fb..2f033d9a008a 100644 --- a/packages/google_sign_in/google_sign_in_web/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_web/pubspec.yaml @@ -1,7 +1,7 @@ name: google_sign_in_web description: Flutter plugin for Google Sign-In, a secure authentication system for signing in with a Google account on Android, iOS and Web. -repository: https://github.com/flutter/plugins/tree/master/packages/google_sign_in/google_sign_in_web +repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 version: 0.10.0+4 diff --git a/packages/image_picker/image_picker/ios/image_picker.podspec b/packages/image_picker/image_picker/ios/image_picker.podspec index 0d6cb0304723..a2bba5c5397b 100644 --- a/packages/image_picker/image_picker/ios/image_picker.podspec +++ b/packages/image_picker/image_picker/ios/image_picker.podspec @@ -12,7 +12,7 @@ Downloaded by pub (not CocoaPods). s.homepage = 'https://github.com/flutter/plugins' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/image_picker' } + s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/image_picker' } s.documentation_url = 'https://pub.dev/packages/image_picker' s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 44a7c9b80749..5d8a5b5d2f25 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -1,7 +1,7 @@ name: image_picker description: Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. -repository: https://github.com/flutter/plugins/tree/master/packages/image_picker/image_picker +repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 version: 0.8.4+4 diff --git a/packages/image_picker/image_picker_for_web/pubspec.yaml b/packages/image_picker/image_picker_for_web/pubspec.yaml index 4a50a22af653..285656ac9e4f 100644 --- a/packages/image_picker/image_picker_for_web/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/pubspec.yaml @@ -1,6 +1,6 @@ name: image_picker_for_web description: Web platform implementation of image_picker -repository: https://github.com/flutter/plugins/tree/master/packages/image_picker/image_picker_for_web +repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_for_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 version: 2.1.5 diff --git a/packages/image_picker/image_picker_platform_interface/pubspec.yaml b/packages/image_picker/image_picker_platform_interface/pubspec.yaml index 16b64ee86cf1..0dfe2c009d59 100644 --- a/packages/image_picker/image_picker_platform_interface/pubspec.yaml +++ b/packages/image_picker/image_picker_platform_interface/pubspec.yaml @@ -1,6 +1,6 @@ name: image_picker_platform_interface description: A common platform interface for the image_picker plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/image_picker/image_picker_platform_interface +repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_platform_interface issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes diff --git a/packages/in_app_purchase/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/pubspec.yaml index c7bb11f38f8f..b18853be2372 100644 --- a/packages/in_app_purchase/in_app_purchase/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/pubspec.yaml @@ -1,6 +1,6 @@ name: in_app_purchase description: A Flutter plugin for in-app purchases. Exposes APIs for making in-app purchases through the App Store and Google Play. -repository: https://github.com/flutter/plugins/tree/master/packages/in_app_purchase/in_app_purchase +repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 version: 2.0.1 diff --git a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml index bd26abe8248e..08e971ad863a 100644 --- a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml @@ -1,6 +1,6 @@ name: in_app_purchase_android description: An implementation for the Android platform of the Flutter `in_app_purchase` plugin. This uses the Android BillingClient APIs. -repository: https://github.com/flutter/plugins/tree/master/packages/in_app_purchase/in_app_purchase_android +repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 version: 0.2.2+1 diff --git a/packages/in_app_purchase/in_app_purchase_platform_interface/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_platform_interface/pubspec.yaml index b697121025c6..2a0a6bf061d4 100644 --- a/packages/in_app_purchase/in_app_purchase_platform_interface/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_platform_interface/pubspec.yaml @@ -1,6 +1,6 @@ name: in_app_purchase_platform_interface description: A common platform interface for the in_app_purchase plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/in_app_purchase/in_app_purchase_platform_interface +repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase_platform_interface issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes diff --git a/packages/in_app_purchase/in_app_purchase_storekit/ios/in_app_purchase_storekit.podspec b/packages/in_app_purchase/in_app_purchase_storekit/ios/in_app_purchase_storekit.podspec index 45000bb5a876..dd83234ac4ad 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/ios/in_app_purchase_storekit.podspec +++ b/packages/in_app_purchase/in_app_purchase_storekit/ios/in_app_purchase_storekit.podspec @@ -12,7 +12,7 @@ Downloaded by pub (not CocoaPods). s.homepage = 'https://github.com/flutter/plugins' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/in_app_purchase/in_app_purchase_storekit' } + s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase_storekit' } # TODO(mvanbeusekom): update URL when in_app_purchase_storekit package is published. # Updating it before the package is published will cause a lint error and block the tree. s.documentation_url = 'https://pub.dev/packages/in_app_purchase' diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml index b4e371473fbd..3cb04cd815df 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -1,6 +1,6 @@ name: in_app_purchase_storekit description: An implementation for the iOS platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. -repository: https://github.com/flutter/plugins/tree/master/packages/in_app_purchase/in_app_purchase_storekit +repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase_storekit issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 version: 0.3.0+1 diff --git a/packages/ios_platform_images/ios/ios_platform_images.podspec b/packages/ios_platform_images/ios/ios_platform_images.podspec index ccbb9f9bda8a..3549277e9d86 100644 --- a/packages/ios_platform_images/ios/ios_platform_images.podspec +++ b/packages/ios_platform_images/ios/ios_platform_images.podspec @@ -13,7 +13,7 @@ Downloaded by pub (not CocoaPods). s.homepage = 'https://github.com/flutter/plugins' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/ios_platform_images' } + s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/ios_platform_images' } s.documentation_url = 'https://pub.dev/packages/ios_platform_images' s.source_files = 'Classes/**/*' s.dependency 'Flutter' diff --git a/packages/ios_platform_images/pubspec.yaml b/packages/ios_platform_images/pubspec.yaml index 010bd41a4767..d5aa9eefda2c 100644 --- a/packages/ios_platform_images/pubspec.yaml +++ b/packages/ios_platform_images/pubspec.yaml @@ -1,6 +1,6 @@ name: ios_platform_images description: A plugin to share images between Flutter and iOS in add-to-app setups. -repository: https://github.com/flutter/plugins/tree/master/packages/ios_platform_images +repository: https://github.com/flutter/plugins/tree/main/packages/ios_platform_images issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+ios_platform_images%22 version: 0.2.0+3 diff --git a/packages/local_auth/ios/local_auth.podspec b/packages/local_auth/ios/local_auth.podspec index 917c4bf2a0eb..4ab779ad50cd 100644 --- a/packages/local_auth/ios/local_auth.podspec +++ b/packages/local_auth/ios/local_auth.podspec @@ -12,7 +12,7 @@ Downloaded by pub (not CocoaPods). s.homepage = 'https://github.com/flutter/plugins' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/local_auth' } + s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/local_auth' } s.documentation_url = 'https://pub.dev/packages/local_auth' s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' diff --git a/packages/local_auth/pubspec.yaml b/packages/local_auth/pubspec.yaml index 454c8c11e86d..08215c566040 100644 --- a/packages/local_auth/pubspec.yaml +++ b/packages/local_auth/pubspec.yaml @@ -1,7 +1,7 @@ name: local_auth description: Flutter plugin for Android and iOS devices to allow local authentication via fingerprint, touch ID, face ID, passcode, pin, or pattern. -repository: https://github.com/flutter/plugins/tree/master/packages/local_auth +repository: https://github.com/flutter/plugins/tree/main/packages/local_auth issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 version: 1.1.10 diff --git a/packages/path_provider/path_provider/pubspec.yaml b/packages/path_provider/path_provider/pubspec.yaml index d8bca0853cc7..0c2b391ca0de 100644 --- a/packages/path_provider/path_provider/pubspec.yaml +++ b/packages/path_provider/path_provider/pubspec.yaml @@ -1,6 +1,6 @@ name: path_provider description: Flutter plugin for getting commonly used locations on host platform file systems, such as the temp and app data directories. -repository: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider +repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 version: 2.0.8 diff --git a/packages/path_provider/path_provider_android/pubspec.yaml b/packages/path_provider/path_provider_android/pubspec.yaml index 393aef4cfd35..24632ed18eb6 100644 --- a/packages/path_provider/path_provider_android/pubspec.yaml +++ b/packages/path_provider/path_provider_android/pubspec.yaml @@ -1,6 +1,6 @@ name: path_provider_android description: Android implementation of the path_provider plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_android +repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 version: 2.0.11 diff --git a/packages/path_provider/path_provider_ios/ios/path_provider_ios.podspec b/packages/path_provider/path_provider_ios/ios/path_provider_ios.podspec index 5d79a49e045b..f1f94e996093 100644 --- a/packages/path_provider/path_provider_ios/ios/path_provider_ios.podspec +++ b/packages/path_provider/path_provider_ios/ios/path_provider_ios.podspec @@ -12,7 +12,7 @@ Downloaded by pub (not CocoaPods). s.homepage = 'https://github.com/flutter/plugins' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_ios' } + s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_ios' } s.documentation_url = 'https://pub.dev/packages/path_provider' s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' diff --git a/packages/path_provider/path_provider_ios/pubspec.yaml b/packages/path_provider/path_provider_ios/pubspec.yaml index 9818cbfcd391..dc64972dce21 100644 --- a/packages/path_provider/path_provider_ios/pubspec.yaml +++ b/packages/path_provider/path_provider_ios/pubspec.yaml @@ -1,6 +1,6 @@ name: path_provider_ios description: iOS implementation of the path_provider plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_ios +repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 version: 2.0.7 diff --git a/packages/path_provider/path_provider_linux/pubspec.yaml b/packages/path_provider/path_provider_linux/pubspec.yaml index 9dcf2cd9935b..b652e8d01845 100644 --- a/packages/path_provider/path_provider_linux/pubspec.yaml +++ b/packages/path_provider/path_provider_linux/pubspec.yaml @@ -1,6 +1,6 @@ name: path_provider_linux description: Linux implementation of the path_provider plugin -repository: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_linux +repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 version: 2.1.5 diff --git a/packages/path_provider/path_provider_macos/macos/path_provider_macos.podspec b/packages/path_provider/path_provider_macos/macos/path_provider_macos.podspec index 66b5872c9ac9..1f28f2bf7cf0 100644 --- a/packages/path_provider/path_provider_macos/macos/path_provider_macos.podspec +++ b/packages/path_provider/path_provider_macos/macos/path_provider_macos.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.description = <<-DESC A macOS implementation of the Flutter plugin for getting commonly used locations on the filesystem. DESC - s.homepage = 'https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_macos' + s.homepage = 'https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_macos' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_macos' } diff --git a/packages/path_provider/path_provider_macos/pubspec.yaml b/packages/path_provider/path_provider_macos/pubspec.yaml index 1c88e24c1247..e0948fed5ea8 100644 --- a/packages/path_provider/path_provider_macos/pubspec.yaml +++ b/packages/path_provider/path_provider_macos/pubspec.yaml @@ -1,6 +1,6 @@ name: path_provider_macos description: macOS implementation of the path_provider plugin -repository: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_macos +repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 version: 2.0.5 diff --git a/packages/path_provider/path_provider_platform_interface/pubspec.yaml b/packages/path_provider/path_provider_platform_interface/pubspec.yaml index 8f4af6f04ba4..68f3f8a4d8b2 100644 --- a/packages/path_provider/path_provider_platform_interface/pubspec.yaml +++ b/packages/path_provider/path_provider_platform_interface/pubspec.yaml @@ -1,6 +1,6 @@ name: path_provider_platform_interface description: A common platform interface for the path_provider plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_platform_interface +repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_platform_interface issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes diff --git a/packages/path_provider/path_provider_windows/pubspec.yaml b/packages/path_provider/path_provider_windows/pubspec.yaml index 598ce9e0a1df..ce0b17f57cde 100644 --- a/packages/path_provider/path_provider_windows/pubspec.yaml +++ b/packages/path_provider/path_provider_windows/pubspec.yaml @@ -1,6 +1,6 @@ name: path_provider_windows description: Windows implementation of the path_provider plugin -repository: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_windows +repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 version: 2.0.5 diff --git a/packages/plugin_platform_interface/pubspec.yaml b/packages/plugin_platform_interface/pubspec.yaml index e73d5207d4b4..1b601db1a388 100644 --- a/packages/plugin_platform_interface/pubspec.yaml +++ b/packages/plugin_platform_interface/pubspec.yaml @@ -1,7 +1,7 @@ name: plugin_platform_interface description: Reusable base class for platform interfaces of Flutter federated plugins, to help enforce best practices. -repository: https://github.com/flutter/plugins/tree/master/packages/plugin_platform_interface +repository: https://github.com/flutter/plugins/tree/main/packages/plugin_platform_interface issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+plugin_platform_interface%22 # DO NOT MAKE A BREAKING CHANGE TO THIS PACKAGE diff --git a/packages/quick_actions/quick_actions/ios/quick_actions.podspec b/packages/quick_actions/quick_actions/ios/quick_actions.podspec index 9452fd8c983d..7bc36f9d535c 100644 --- a/packages/quick_actions/quick_actions/ios/quick_actions.podspec +++ b/packages/quick_actions/quick_actions/ios/quick_actions.podspec @@ -12,7 +12,7 @@ Downloaded by pub (not CocoaPods). s.homepage = 'https://github.com/flutter/plugins' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/quick_actions' } + s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/quick_actions' } s.documentation_url = 'https://pub.dev/packages/quick_actions' s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' diff --git a/packages/quick_actions/quick_actions/pubspec.yaml b/packages/quick_actions/quick_actions/pubspec.yaml index 92b8edf1e8dc..0d51e828fbdd 100644 --- a/packages/quick_actions/quick_actions/pubspec.yaml +++ b/packages/quick_actions/quick_actions/pubspec.yaml @@ -1,7 +1,7 @@ name: quick_actions description: Flutter plugin for creating shortcuts on home screen, also known as Quick Actions on iOS and App Shortcuts on Android. -repository: https://github.com/flutter/plugins/tree/master/packages/quick_actions/quick_actions +repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+quick_actions%22 version: 0.6.0+9 diff --git a/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml b/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml index 0bf23cd5e0a7..aef2e248bade 100644 --- a/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml +++ b/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml @@ -1,6 +1,6 @@ name: quick_actions_platform_interface description: A common platform interface for the quick_actions plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/quick_actions/quick_actions_platform_interface +repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions_platform_interface issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+quick_actions%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes diff --git a/packages/shared_preferences/shared_preferences/pubspec.yaml b/packages/shared_preferences/shared_preferences/pubspec.yaml index a54d73fcc1c0..d942b2b2084c 100644 --- a/packages/shared_preferences/shared_preferences/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/pubspec.yaml @@ -1,7 +1,7 @@ name: shared_preferences description: Flutter plugin for reading and writing simple key-value pairs. Wraps NSUserDefaults on iOS and SharedPreferences on Android. -repository: https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences +repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 version: 2.0.12 diff --git a/packages/shared_preferences/shared_preferences_android/pubspec.yaml b/packages/shared_preferences/shared_preferences_android/pubspec.yaml index 2e954206c47b..8adb2402cb51 100644 --- a/packages/shared_preferences/shared_preferences_android/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_android/pubspec.yaml @@ -1,6 +1,6 @@ name: shared_preferences_android description: Android implementation of the shared_preferences plugin -repository: https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences_android +repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 version: 2.0.10 diff --git a/packages/shared_preferences/shared_preferences_ios/ios/shared_preferences_ios.podspec b/packages/shared_preferences/shared_preferences_ios/ios/shared_preferences_ios.podspec index bc5feef09257..4126ed0a8cc2 100644 --- a/packages/shared_preferences/shared_preferences_ios/ios/shared_preferences_ios.podspec +++ b/packages/shared_preferences/shared_preferences_ios/ios/shared_preferences_ios.podspec @@ -11,7 +11,7 @@ Wraps NSUserDefaults, providing a persistent store for simple key-value pairs. s.homepage = 'https://github.com/flutter/plugins' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences_ios' } + s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_ios' } s.documentation_url = 'https://pub.dev/packages/shared_preferences' s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' diff --git a/packages/shared_preferences/shared_preferences_ios/pubspec.yaml b/packages/shared_preferences/shared_preferences_ios/pubspec.yaml index 0c8313050994..ad3f20bf97c0 100644 --- a/packages/shared_preferences/shared_preferences_ios/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_ios/pubspec.yaml @@ -1,6 +1,6 @@ name: shared_preferences_ios description: iOS implementation of the shared_preferences plugin -repository: https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences_ios +repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 version: 2.0.9 diff --git a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml index 6a37346ff6cf..1b84644b9233 100644 --- a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml @@ -1,6 +1,6 @@ name: shared_preferences_linux description: Linux implementation of the shared_preferences plugin -repository: https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences_linux +repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 version: 2.0.4 diff --git a/packages/shared_preferences/shared_preferences_macos/macos/shared_preferences_macos.podspec b/packages/shared_preferences/shared_preferences_macos/macos/shared_preferences_macos.podspec index 9f364c5b6bc1..df140fbb1eec 100644 --- a/packages/shared_preferences/shared_preferences_macos/macos/shared_preferences_macos.podspec +++ b/packages/shared_preferences/shared_preferences_macos/macos/shared_preferences_macos.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.description = <<-DESC Wraps NSUserDefaults, providing a persistent store for simple key-value pairs. DESC - s.homepage = 'https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences_macos' + s.homepage = 'https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_macos' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Team' => 'flutter-dev@googlegroups.com' } s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences_macos' } diff --git a/packages/shared_preferences/shared_preferences_macos/pubspec.yaml b/packages/shared_preferences/shared_preferences_macos/pubspec.yaml index 6e351e86fb1a..4f06c52dc652 100644 --- a/packages/shared_preferences/shared_preferences_macos/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_macos/pubspec.yaml @@ -1,6 +1,6 @@ name: shared_preferences_macos description: macOS implementation of the shared_preferences plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences_macos +repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 version: 2.0.2 diff --git a/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml b/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml index 39f5a3ce42e9..8d775ab8b58c 100644 --- a/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml @@ -1,6 +1,6 @@ name: shared_preferences_platform_interface description: A common platform interface for the shared_preferences plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences_platform_interface +repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_platform_interface issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 version: 2.0.0 diff --git a/packages/shared_preferences/shared_preferences_web/pubspec.yaml b/packages/shared_preferences/shared_preferences_web/pubspec.yaml index 37ed5d5e81d2..232c87c426fc 100644 --- a/packages/shared_preferences/shared_preferences_web/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_web/pubspec.yaml @@ -1,6 +1,6 @@ name: shared_preferences_web description: Web platform implementation of shared_preferences -repository: https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences_web +repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 version: 2.0.3 diff --git a/packages/shared_preferences/shared_preferences_windows/pubspec.yaml b/packages/shared_preferences/shared_preferences_windows/pubspec.yaml index 107dfa20b201..a3938ba461b1 100644 --- a/packages/shared_preferences/shared_preferences_windows/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_windows/pubspec.yaml @@ -1,6 +1,6 @@ name: shared_preferences_windows description: Windows implementation of shared_preferences -repository: https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences_windows +repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 version: 2.0.4 diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index 128c3801fd35..b0aac361f709 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -1,7 +1,7 @@ name: url_launcher description: Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes. -repository: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher +repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 version: 6.0.18 diff --git a/packages/url_launcher/url_launcher_android/pubspec.yaml b/packages/url_launcher/url_launcher_android/pubspec.yaml index b660af992c3e..ffd00e58c9bc 100644 --- a/packages/url_launcher/url_launcher_android/pubspec.yaml +++ b/packages/url_launcher/url_launcher_android/pubspec.yaml @@ -1,6 +1,6 @@ name: url_launcher_android description: Android implementation of the url_launcher plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_android +repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 version: 6.0.14 diff --git a/packages/url_launcher/url_launcher_ios/ios/url_launcher_ios.podspec b/packages/url_launcher/url_launcher_ios/ios/url_launcher_ios.podspec index 1ee2e8776b85..e22ba3db9854 100644 --- a/packages/url_launcher/url_launcher_ios/ios/url_launcher_ios.podspec +++ b/packages/url_launcher/url_launcher_ios/ios/url_launcher_ios.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.description = <<-DESC A Flutter plugin for making the underlying platform (Android or iOS) launch a URL. DESC - s.homepage = 'https://github.com/flutter/plugins/tree/master/packages/url_launcher' + s.homepage = 'https://github.com/flutter/plugins/tree/main/packages/url_launcher' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_ios' } diff --git a/packages/url_launcher/url_launcher_ios/pubspec.yaml b/packages/url_launcher/url_launcher_ios/pubspec.yaml index 87bb0e9784bf..a2e0ac2ef577 100644 --- a/packages/url_launcher/url_launcher_ios/pubspec.yaml +++ b/packages/url_launcher/url_launcher_ios/pubspec.yaml @@ -1,6 +1,6 @@ name: url_launcher_ios description: iOS implementation of the url_launcher plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_ios +repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 version: 6.0.14 diff --git a/packages/url_launcher/url_launcher_linux/pubspec.yaml b/packages/url_launcher/url_launcher_linux/pubspec.yaml index a75977912ced..95b6c3ca04f5 100644 --- a/packages/url_launcher/url_launcher_linux/pubspec.yaml +++ b/packages/url_launcher/url_launcher_linux/pubspec.yaml @@ -1,6 +1,6 @@ name: url_launcher_linux description: Linux implementation of the url_launcher plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_linux +repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 version: 2.0.3 diff --git a/packages/url_launcher/url_launcher_macos/macos/url_launcher_macos.podspec b/packages/url_launcher/url_launcher_macos/macos/url_launcher_macos.podspec index 3db112b64be2..408df1f9ef45 100644 --- a/packages/url_launcher/url_launcher_macos/macos/url_launcher_macos.podspec +++ b/packages/url_launcher/url_launcher_macos/macos/url_launcher_macos.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.description = <<-DESC A macOS implementation of the url_launcher plugin. DESC - s.homepage = 'https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_macos' + s.homepage = 'https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_macos' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Team' => 'flutter-dev@googlegroups.com' } s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_macos' } diff --git a/packages/url_launcher/url_launcher_macos/pubspec.yaml b/packages/url_launcher/url_launcher_macos/pubspec.yaml index 55e8ec3f6aad..e993bb69015d 100644 --- a/packages/url_launcher/url_launcher_macos/pubspec.yaml +++ b/packages/url_launcher/url_launcher_macos/pubspec.yaml @@ -1,6 +1,6 @@ name: url_launcher_macos description: macOS implementation of the url_launcher plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_macos +repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 version: 2.0.3 diff --git a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml index 3f9605b42320..cdbdfefba93a 100644 --- a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml +++ b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml @@ -1,6 +1,6 @@ name: url_launcher_platform_interface description: A common platform interface for the url_launcher plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_platform_interface +repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_platform_interface issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes diff --git a/packages/url_launcher/url_launcher_web/pubspec.yaml b/packages/url_launcher/url_launcher_web/pubspec.yaml index fd882060e130..d0b1ff7c6b57 100644 --- a/packages/url_launcher/url_launcher_web/pubspec.yaml +++ b/packages/url_launcher/url_launcher_web/pubspec.yaml @@ -1,6 +1,6 @@ name: url_launcher_web description: Web platform implementation of url_launcher -repository: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_web +repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 version: 2.0.6 diff --git a/packages/url_launcher/url_launcher_windows/pubspec.yaml b/packages/url_launcher/url_launcher_windows/pubspec.yaml index a92e91ee4568..b0acdabc00a3 100644 --- a/packages/url_launcher/url_launcher_windows/pubspec.yaml +++ b/packages/url_launcher/url_launcher_windows/pubspec.yaml @@ -1,6 +1,6 @@ name: url_launcher_windows description: Windows implementation of the url_launcher plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_windows +repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 version: 2.0.2 diff --git a/packages/video_player/video_player/ios/video_player.podspec b/packages/video_player/video_player/ios/video_player.podspec index 86230f65db7c..12ea7e91514b 100644 --- a/packages/video_player/video_player/ios/video_player.podspec +++ b/packages/video_player/video_player/ios/video_player.podspec @@ -12,7 +12,7 @@ Downloaded by pub (not CocoaPods). s.homepage = 'https://github.com/flutter/plugins' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/video_player/video_player' } + s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/video_player/video_player' } s.documentation_url = 'https://pub.dev/packages/video_player' s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 24aef067654f..858a7cedd5c2 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -1,7 +1,7 @@ name: video_player description: Flutter plugin for displaying inline video with other Flutter widgets on Android, iOS, and web. -repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player +repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 version: 2.2.15 diff --git a/packages/video_player/video_player_platform_interface/pubspec.yaml b/packages/video_player/video_player_platform_interface/pubspec.yaml index 986ca913be62..f0448f7d9c35 100644 --- a/packages/video_player/video_player_platform_interface/pubspec.yaml +++ b/packages/video_player/video_player_platform_interface/pubspec.yaml @@ -1,6 +1,6 @@ name: video_player_platform_interface description: A common platform interface for the video_player plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_platform_interface +repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player_platform_interface issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml index 7c7d5113320c..353253bbcc46 100644 --- a/packages/video_player/video_player_web/pubspec.yaml +++ b/packages/video_player/video_player_web/pubspec.yaml @@ -1,6 +1,6 @@ name: video_player_web description: Web platform implementation of video_player. -repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_web +repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 version: 2.0.6 diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index 4af446248307..afa615cd8b2a 100644 --- a/packages/webview_flutter/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/pubspec.yaml @@ -1,6 +1,6 @@ name: webview_flutter description: A Flutter plugin that provides a WebView widget on Android and iOS. -repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter +repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 version: 3.0.0 diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 8905d7fb66e2..b2329d3fb904 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -1,6 +1,6 @@ name: webview_flutter_android description: A Flutter plugin that provides a WebView widget on Android. -repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter_android +repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 version: 2.8.2 diff --git a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml index cdff5f2395b3..50b816d51d5d 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml @@ -1,6 +1,6 @@ name: webview_flutter_platform_interface description: A common platform interface for the webview_flutter plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter_platform_interface +repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_platform_interface issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview_flutter%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes diff --git a/packages/webview_flutter/webview_flutter_web/pubspec.yaml b/packages/webview_flutter/webview_flutter_web/pubspec.yaml index 3588e52bfda9..bd154387097e 100644 --- a/packages/webview_flutter/webview_flutter_web/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_web/pubspec.yaml @@ -1,6 +1,6 @@ name: webview_flutter_web description: A Flutter plugin that provides a WebView widget on web. -repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter_web +repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 version: 0.1.0+1 diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/webview_flutter_wkwebview.podspec b/packages/webview_flutter/webview_flutter_wkwebview/ios/webview_flutter_wkwebview.podspec index 89c06237b203..479ecf5f256a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/webview_flutter_wkwebview.podspec +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/webview_flutter_wkwebview.podspec @@ -12,7 +12,7 @@ Downloaded by pub (not CocoaPods). s.homepage = 'https://github.com/flutter/plugins' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter_wkwebview' } + s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_wkwebview' } s.documentation_url = 'https://pub.dev/packages/webview_flutter' s.source_files = 'Classes/**/*.{h,m}' s.public_header_files = 'Classes/**/*.h' diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index d39f42b0ecdf..e61eac3faf22 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -1,6 +1,6 @@ name: webview_flutter_wkwebview description: A Flutter plugin that provides a WebView widget based on Apple's WKWebView control. -repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter_wkwebview +repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_wkwebview issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 version: 2.7.1 diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index 8cde4dd46d80..4849feee7b85 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages -repository: https://github.com/flutter/plugins/tree/master/script/tool +repository: https://github.com/flutter/plugins/tree/main/script/tool version: 0.7.2 dependencies: From 4d8e98d6270347d0e6e58270d14f01daa5cd0a89 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Tue, 25 Jan 2022 16:10:21 +0100 Subject: [PATCH 111/600] [image_picker] Improves documentation around the destruction of the MainActivity. (#4672) --- .../image_picker/image_picker/CHANGELOG.md | 3 ++- packages/image_picker/image_picker/README.md | 22 +++++++++++++++---- .../image_picker/image_picker/pubspec.yaml | 2 +- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index ce29eb522e3d..1b7d0ffd4b57 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.8.4+5 +* Improves the documentation on handling MainActivity being killed by the Android OS. * Updates Android compileSdkVersion to 31. * Fix iOS RunnerUITests search paths. diff --git a/packages/image_picker/image_picker/README.md b/packages/image_picker/image_picker/README.md index 100606c3658a..46a7795b748a 100755 --- a/packages/image_picker/image_picker/README.md +++ b/packages/image_picker/image_picker/README.md @@ -26,7 +26,11 @@ Add the following keys to your _Info.plist_ file, located in `/ios Starting with version **0.8.1** the Android implementation support to pick (multiple) images on Android 4.3 or higher. -No configuration required - the plugin should work out of the box. +No configuration required - the plugin should work out of the box. It is +however highly recommended to prepare for Android killing the application when +low on memory. How to prepare for this is discussed in the [Handling +MainActivity destruction on Android](#handling-mainactivity-destruction-on-android) +section. It is no longer required to add `android:requestLegacyExternalStorage="true"` as an attribute to the `` tag in AndroidManifest.xml, as `image_picker` has been updated to make use of scoped storage. @@ -55,7 +59,14 @@ import 'package:image_picker/image_picker.dart'; ### Handling MainActivity destruction on Android -Android system -- although very rarely -- sometimes kills the MainActivity after the image_picker finishes. When this happens, we lost the data selected from the image_picker. You can use `retrieveLostData` to retrieve the lost data in this situation. For example: +When under high memory pressure the Android system may kill the MainActivity of +the application using the image_picker. On Android the image_picker makes use +of the default `Intent.ACTION_GET_CONTENT` or `MediaStore.ACTION_IMAGE_CAPTURE` +intents. This means that while the intent is executing the source application +is moved to the background and becomes eligable for cleanup when the system is +low on memory. When the intent finishes executing, Android will restart the +application. Since the data is never returned to the original call use the +`ImagePicker.retrieveLostData()` method to retrieve the lost data. For example: ```dart Future getLostData() async { @@ -65,7 +76,7 @@ Future getLostData() async { return; } if (response.files != null) { - for(final XFile file in response.files) { + for (final XFile file in response.files) { _handleFile(file); } } else { @@ -74,7 +85,10 @@ Future getLostData() async { } ``` -There's no way to detect when this happens, so calling this method at the right place is essential. We recommend to wire this into some kind of start up check. Please refer to the example app to see how we used it. +This check should always be run at startup in order to detect and handle this +case. Please refer to the +[example app](https://pub.dev/packages/image_picker/example) for a more +complete example of handling this flow. ## Migrating to 0.8.2+ diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 5d8a5b5d2f25..836c3ca93f3c 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.4+4 +version: 0.8.4+5 environment: sdk: ">=2.14.0 <3.0.0" From 6524fb615f0c3902a6a21642c7b56466514b9c16 Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Tue, 25 Jan 2022 10:45:21 -0800 Subject: [PATCH 112/600] [camera]fix crash due to race condition in dispatch queue (#4619) --- packages/camera/camera/CHANGELOG.md | 5 ++- .../ios/Runner.xcodeproj/project.pbxproj | 4 ++ ...eraCaptureSessionQueueRaceConditionTests.m | 44 +++++++++++++++++++ .../camera/camera/ios/Classes/CameraPlugin.m | 10 +---- .../camera/ios/Classes/CameraPlugin_Test.h | 3 ++ packages/camera/camera/pubspec.yaml | 2 +- 6 files changed, 57 insertions(+), 11 deletions(-) create mode 100644 packages/camera/camera/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 0acd28b1d29e..cb560f5a34f5 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 0.9.4+7 -* Minor internal code cleanup. +* Fixes a crash in iOS when passing null queue pointer into AVFoundation API due to race condition. +* Minor iOS internal code cleanup. ## 0.9.4+6 diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj index 32b770eb6783..80f672b5b9cf 100644 --- a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj @@ -21,6 +21,7 @@ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; E01EE4A82799F3A5008C1950 /* QueueHelperTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E01EE4A72799F3A5008C1950 /* QueueHelperTests.m */; }; + E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */; }; E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */; }; E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */; }; E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */; }; @@ -79,6 +80,7 @@ 9C5CC6CAD53AD388B2694F3A /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; A24F9E418BA48BCC7409B117 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; E01EE4A72799F3A5008C1950 /* QueueHelperTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QueueHelperTests.m; sourceTree = ""; }; + E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CameraCaptureSessionQueueRaceConditionTests.m; sourceTree = ""; }; E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeMethodChannelTests.m; sourceTree = ""; }; E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeTextureRegistryTests.m; sourceTree = ""; }; E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeEventChannelTests.m; sourceTree = ""; }; @@ -122,6 +124,7 @@ E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */, F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */, F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */, + E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */, ); path = RunnerTests; sourceTree = ""; @@ -390,6 +393,7 @@ E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */, F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */, 334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */, + E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */, E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */, E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */, E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */, diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m b/packages/camera/camera/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m new file mode 100644 index 000000000000..667a122d9375 --- /dev/null +++ b/packages/camera/camera/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m @@ -0,0 +1,44 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import camera; +@import camera.Test; +@import XCTest; + +@interface CameraCaptureSessionQueueRaceConditionTests : XCTestCase +@end + +@implementation CameraCaptureSessionQueueRaceConditionTests + +- (void)testFixForCaptureSessionQueueNullPointerCrashDueToRaceCondition { + CameraPlugin *camera = [[CameraPlugin alloc] initWithRegistry:nil messenger:nil]; + + XCTestExpectation *disposeExpectation = + [self expectationWithDescription:@"dispose's result block must be called"]; + XCTestExpectation *createExpectation = + [self expectationWithDescription:@"create's result block must be called"]; + FlutterMethodCall *disposeCall = [FlutterMethodCall methodCallWithMethodName:@"dispose" + arguments:nil]; + FlutterMethodCall *createCall = [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"resolutionPreset" : @"medium", @"enableAudio" : @(1)}]; + // Mimic a dispose call followed by a create call, which can be triggered by slightly dragging the + // home bar, causing the app to be inactive, and immediately regain active. + [camera handleMethodCall:disposeCall + result:^(id _Nullable result) { + [disposeExpectation fulfill]; + }]; + [camera handleMethodCall:createCall + result:^(id _Nullable result) { + [createExpectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:1 handler:nil]; + // `captureSessionQueue` must not be nil after `create` call. Otherwise a nil + // `captureSessionQueue` passed into `AVCaptureVideoDataOutput::setSampleBufferDelegate:queue:` + // API will cause a crash. + XCTAssertNotNil(camera.captureSessionQueue, + @"captureSessionQueue must not be nil after create method. "); +} + +@end diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.m b/packages/camera/camera/ios/Classes/CameraPlugin.m index f8574dc60732..5d706520cc5b 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin.m +++ b/packages/camera/camera/ios/Classes/CameraPlugin.m @@ -1322,9 +1322,7 @@ @interface CameraPlugin () @property(readonly, nonatomic) FLTThreadSafeMethodChannel *deviceEventMethodChannel; @end -@implementation CameraPlugin { - dispatch_queue_t _captureSessionQueue; -} +@implementation CameraPlugin + (void)registerWithRegistrar:(NSObject *)registrar { FlutterMethodChannel *channel = @@ -1341,6 +1339,7 @@ - (instancetype)initWithRegistry:(NSObject *)registry NSAssert(self, @"super init cannot be nil"); _registry = [[FLTThreadSafeTextureRegistry alloc] initWithTextureRegistry:registry]; _messenger = messenger; + _captureSessionQueue = dispatch_queue_create("io.flutter.camera.captureSessionQueue", NULL); [self initDeviceEventMethodChannel]; [self startOrientationListener]; return self; @@ -1385,10 +1384,6 @@ - (void)sendDeviceOrientation:(UIDeviceOrientation)orientation { } - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { - if (_captureSessionQueue == nil) { - _captureSessionQueue = dispatch_queue_create("io.flutter.camera.dispatchqueue", NULL); - } - // Invoke the plugin on another dispatch queue to avoid blocking the UI. dispatch_async(_captureSessionQueue, ^{ FLTThreadSafeFlutterResult *threadSafeResult = @@ -1507,7 +1502,6 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call } else if ([@"dispose" isEqualToString:call.method]) { [_registry unregisterTexture:cameraId]; [_camera close]; - _captureSessionQueue = nil; [result sendSuccess]; } else if ([@"prepareForVideoRecording" isEqualToString:call.method]) { [_camera setUpCaptureSessionForAudio]; diff --git a/packages/camera/camera/ios/Classes/CameraPlugin_Test.h b/packages/camera/camera/ios/Classes/CameraPlugin_Test.h index afbf6864a1f8..e36b4ab2fa6d 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin_Test.h +++ b/packages/camera/camera/ios/Classes/CameraPlugin_Test.h @@ -10,6 +10,9 @@ /// Methods exposed for unit testing. @interface CameraPlugin () +// All FLTCam's state access and capture session related operations should be on run on this queue. +@property(nonatomic, strong) dispatch_queue_t captureSessionQueue; + /// Inject @p FlutterTextureRegistry and @p FlutterBinaryMessenger for unit testing. - (instancetype)initWithRegistry:(NSObject *)registry messenger:(NSObject *)messenger diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 05c8b9df35cf..e5a5f7656dbc 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+6 +version: 0.9.4+7 environment: sdk: ">=2.14.0 <3.0.0" From d20d3bf48deb5432dfc44c8ae0debab0626e7830 Mon Sep 17 00:00:00 2001 From: Ian Hickson Date: Tue, 25 Jan 2022 13:10:21 -0800 Subject: [PATCH 113/600] [webview_flutter] Remove dependency on flutter.dev and google.com (#4691) --- .../webview_flutter_test.dart | 57 ++++++++++++------- script/tool/README.md | 7 ++- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart index 7588dc8e02fb..02ee1976eb67 100644 --- a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart @@ -2,6 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// This test is run using `flutter drive` by the CI (see /script/tool/README.md +// in this repository for details on driving that tooling manually), but can +// also be run using `flutter test` directly during development. + import 'dart:async'; import 'dart:convert'; import 'dart:io'; @@ -16,19 +20,32 @@ import 'package:integration_test/integration_test.dart'; import 'package:webview_flutter/platform_interface.dart'; import 'package:webview_flutter/webview_flutter.dart'; -void main() { +Future main() async { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - // URLs to navigate to in tests. These need to be URLs that we are confident will - // always be accessible, and won't do redirection. (E.g., just - // 'https://www.google.com/' will sometimes redirect traffic that looks - // like it's coming from a bot, which is true of these tests). - const String primaryUrl = 'https://flutter.dev/'; - const String secondaryUrl = 'https://www.google.com/robots.txt'; - const bool _skipDueToIssue86757 = true; - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757. + final HttpServer server = await HttpServer.bind(InternetAddress.anyIPv4, 0); + server.forEach((HttpRequest request) { + if (request.uri.path == '/hello.txt') { + request.response.writeln('Hello, world.'); + } else if (request.uri.path == '/secondary.txt') { + request.response.writeln('How are you today?'); + } else if (request.uri.path == '/headers') { + request.response.writeln('${request.headers}'); + } else if (request.uri.path == '/favicon.ico') { + request.response.statusCode = HttpStatus.notFound; + } else { + fail('unexpected request: ${request.method} ${request.uri}'); + } + request.response.close(); + }); + final String prefixUrl = 'http://${server.address.address}:${server.port}'; + final String primaryUrl = '$prefixUrl/hello.txt'; + final String secondaryUrl = '$prefixUrl/secondary.txt'; + final String headersUrl = '$prefixUrl/headers'; + + // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets('initialUrl', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); @@ -49,7 +66,7 @@ void main() { expect(currentUrl, primaryUrl); }, skip: _skipDueToIssue86757); - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757. + // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets('loadUrl', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); @@ -93,7 +110,7 @@ void main() { expect(result, equals('2')); }); - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757. + // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets('loadUrl with headers', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); @@ -122,10 +139,9 @@ void main() { final Map headers = { 'test_header': 'flutter_test_header' }; - await controller.loadUrl('https://flutter-header-echo.herokuapp.com/', - headers: headers); + await controller.loadUrl(headersUrl, headers: headers); final String? currentUrl = await controller.currentUrl(); - expect(currentUrl, 'https://flutter-header-echo.herokuapp.com/'); + expect(currentUrl, headersUrl); await pageStarts.stream.firstWhere((String url) => url == currentUrl); await pageLoads.stream.firstWhere((String url) => url == currentUrl); @@ -135,7 +151,7 @@ void main() { expect(content.contains('flutter_test_header'), isTrue); }, skip: Platform.isAndroid && _skipDueToIssue86757); - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757. + // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets('JavascriptChannel', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); @@ -247,7 +263,7 @@ void main() { expect(customUserAgent2, 'Custom_User_Agent2'); }); - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757. + // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets('use default platform userAgent after webView is rebuilt', (WidgetTester tester) async { final Completer controllerCompleter = @@ -782,7 +798,7 @@ void main() { }); group('Programmatic Scroll', () { - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757. + // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets('setAndGetScrollPosition', (WidgetTester tester) async { const String scrollTestPage = ''' @@ -1133,6 +1149,7 @@ void main() { expect(currentUrl, primaryUrl); }); + // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets('target _blank opens in same window', (WidgetTester tester) async { final Completer controllerCompleter = @@ -1158,11 +1175,9 @@ void main() { await pageLoaded.future; final String? currentUrl = await controller.currentUrl(); expect(currentUrl, primaryUrl); - }, - // Flaky on Android: https://github.com/flutter/flutter/issues/86757 - skip: Platform.isAndroid && _skipDueToIssue86757); + }, skip: Platform.isAndroid && _skipDueToIssue86757); - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757. + // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets( 'can open new window and go back', (WidgetTester tester) async { diff --git a/script/tool/README.md b/script/tool/README.md index 613cb5456e0a..265d3868fc37 100644 --- a/script/tool/README.md +++ b/script/tool/README.md @@ -84,10 +84,13 @@ dart run ./script/tool/bin/flutter_plugin_tools.dart test --packages plugin_name ```sh cd -dart run ./script/tool/bin/flutter_plugin_tools.dart build-examples --packages plugin_name -dart run ./script/tool/bin/flutter_plugin_tools.dart drive-examples --packages plugin_name +dart run ./script/tool/bin/flutter_plugin_tools.dart build-examples --apk --packages plugin_name +dart run ./script/tool/bin/flutter_plugin_tools.dart drive-examples --android --packages plugin_name ``` +Replace `--apk`/`--android` with the platform you want to test against +(omit it to get a list of valid options). + ### Run Native Tests `native-test` takes one or more platform flags to run tests for. By default it From 01b0a8ff748f414f3a29108a31fb646ab53f04a1 Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Tue, 25 Jan 2022 17:12:23 -0800 Subject: [PATCH 114/600] [camera_web] Update camera_web to support Dart's new async requestFullscreen API (#4694) No version change: No change to plugin functionality --- packages/camera/camera_web/CHANGELOG.md | 4 ++++ packages/camera/camera_web/lib/src/camera_web.dart | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index dd9225f48ff4..4ac140ec4386 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Implemented support for new Dart SDKs with an async requestFullscreen API. + ## 0.2.1+1 * Update usage documentation. diff --git a/packages/camera/camera_web/lib/src/camera_web.dart b/packages/camera/camera_web/lib/src/camera_web.dart index 0021ee47cbde..5fbd820103c7 100644 --- a/packages/camera/camera_web/lib/src/camera_web.dart +++ b/packages/camera/camera_web/lib/src/camera_web.dart @@ -380,7 +380,10 @@ class CameraPlugin extends CameraPlatform { // Full-screen mode may be required to modify the device orientation. // See: https://w3c.github.io/screen-orientation/#interaction-with-fullscreen-api - documentElement.requestFullscreen(); + // Recent versions of Dart changed requestFullscreen to return a Future instead of void. + // This wrapper allows use of both the old and new APIs. + dynamic fullScreen() => documentElement.requestFullscreen(); + await fullScreen(); await orientation.lock(orientationType.toString()); } else { throw PlatformException( From cd97c195232819a610c43d5aa4ea1b6fac1eebe1 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 25 Jan 2022 21:40:23 -0500 Subject: [PATCH 115/600] [file_selector] Add Windows support (#4424) --- .ci/scripts/drive_examples_win32.sh | 2 +- .../file_selector_windows/.gitignore | 5 + .../file_selector_windows/.metadata | 10 + .../file_selector_windows/AUTHORS | 6 + .../file_selector_windows/CHANGELOG.md | 16 + .../file_selector_windows/LICENSE | 25 + .../file_selector_windows/README.md | 19 + .../file_selector_windows/example/.gitignore | 48 ++ .../file_selector_windows/example/.metadata | 10 + .../file_selector_windows/example/README.md | 4 + .../example/lib/get_directory_page.dart | 77 +++ .../example/lib/home_page.dart | 57 +++ .../example/lib/main.dart | 37 ++ .../example/lib/open_image_page.dart | 87 ++++ .../lib/open_multiple_images_page.dart | 99 ++++ .../example/lib/open_text_page.dart | 84 ++++ .../example/lib/save_text_page.dart | 78 +++ .../example/pubspec.yaml | 27 + .../example/windows/.gitignore | 17 + .../example/windows/CMakeLists.txt | 100 ++++ .../example/windows/flutter/CMakeLists.txt | 103 ++++ .../windows/flutter/generated_plugins.cmake | 16 + .../example/windows/runner/CMakeLists.txt | 17 + .../example/windows/runner/Runner.rc | 121 +++++ .../example/windows/runner/flutter_window.cpp | 64 +++ .../example/windows/runner/flutter_window.h | 36 ++ .../example/windows/runner/main.cpp | 45 ++ .../example/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../windows/runner/runner.exe.manifest | 20 + .../example/windows/runner/utils.cpp | 66 +++ .../example/windows/runner/utils.h | 22 + .../example/windows/runner/win32_window.cpp | 240 +++++++++ .../example/windows/runner/win32_window.h | 98 ++++ .../lib/file_selector_windows.dart | 97 ++++ .../file_selector_windows/pubspec.yaml | 25 + .../test/file_selector_windows_test.dart | 257 ++++++++++ .../file_selector_windows/windows/.gitignore | 17 + .../windows/CMakeLists.txt | 78 +++ .../windows/file_dialog_controller.cpp | 66 +++ .../windows/file_dialog_controller.h | 62 +++ .../windows/file_selector_plugin.cpp | 362 ++++++++++++++ .../windows/file_selector_plugin.h | 47 ++ .../windows/file_selector_windows.cpp | 15 + .../file_selector_windows.h | 26 + .../windows/string_utils.cpp | 60 +++ .../windows/string_utils.h | 21 + .../test/file_selector_plugin_test.cpp | 471 ++++++++++++++++++ .../test/test_file_dialog_controller.cpp | 106 ++++ .../test/test_file_dialog_controller.h | 101 ++++ .../windows/test/test_main.cpp | 16 + .../windows/test/test_utils.cpp | 45 ++ .../windows/test/test_utils.h | 91 ++++ script/configs/exclude_integration_win32.yaml | 2 + 54 files changed, 3636 insertions(+), 1 deletion(-) create mode 100644 packages/file_selector/file_selector_windows/.gitignore create mode 100644 packages/file_selector/file_selector_windows/.metadata create mode 100644 packages/file_selector/file_selector_windows/AUTHORS create mode 100644 packages/file_selector/file_selector_windows/CHANGELOG.md create mode 100644 packages/file_selector/file_selector_windows/LICENSE create mode 100644 packages/file_selector/file_selector_windows/README.md create mode 100644 packages/file_selector/file_selector_windows/example/.gitignore create mode 100644 packages/file_selector/file_selector_windows/example/.metadata create mode 100644 packages/file_selector/file_selector_windows/example/README.md create mode 100644 packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart create mode 100644 packages/file_selector/file_selector_windows/example/lib/home_page.dart create mode 100644 packages/file_selector/file_selector_windows/example/lib/main.dart create mode 100644 packages/file_selector/file_selector_windows/example/lib/open_image_page.dart create mode 100644 packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart create mode 100644 packages/file_selector/file_selector_windows/example/lib/open_text_page.dart create mode 100644 packages/file_selector/file_selector_windows/example/lib/save_text_page.dart create mode 100644 packages/file_selector/file_selector_windows/example/pubspec.yaml create mode 100644 packages/file_selector/file_selector_windows/example/windows/.gitignore create mode 100644 packages/file_selector/file_selector_windows/example/windows/CMakeLists.txt create mode 100644 packages/file_selector/file_selector_windows/example/windows/flutter/CMakeLists.txt create mode 100644 packages/file_selector/file_selector_windows/example/windows/flutter/generated_plugins.cmake create mode 100644 packages/file_selector/file_selector_windows/example/windows/runner/CMakeLists.txt create mode 100644 packages/file_selector/file_selector_windows/example/windows/runner/Runner.rc create mode 100644 packages/file_selector/file_selector_windows/example/windows/runner/flutter_window.cpp create mode 100644 packages/file_selector/file_selector_windows/example/windows/runner/flutter_window.h create mode 100644 packages/file_selector/file_selector_windows/example/windows/runner/main.cpp create mode 100644 packages/file_selector/file_selector_windows/example/windows/runner/resource.h create mode 100644 packages/file_selector/file_selector_windows/example/windows/runner/resources/app_icon.ico create mode 100644 packages/file_selector/file_selector_windows/example/windows/runner/runner.exe.manifest create mode 100644 packages/file_selector/file_selector_windows/example/windows/runner/utils.cpp create mode 100644 packages/file_selector/file_selector_windows/example/windows/runner/utils.h create mode 100644 packages/file_selector/file_selector_windows/example/windows/runner/win32_window.cpp create mode 100644 packages/file_selector/file_selector_windows/example/windows/runner/win32_window.h create mode 100644 packages/file_selector/file_selector_windows/lib/file_selector_windows.dart create mode 100644 packages/file_selector/file_selector_windows/pubspec.yaml create mode 100644 packages/file_selector/file_selector_windows/test/file_selector_windows_test.dart create mode 100644 packages/file_selector/file_selector_windows/windows/.gitignore create mode 100644 packages/file_selector/file_selector_windows/windows/CMakeLists.txt create mode 100644 packages/file_selector/file_selector_windows/windows/file_dialog_controller.cpp create mode 100644 packages/file_selector/file_selector_windows/windows/file_dialog_controller.h create mode 100644 packages/file_selector/file_selector_windows/windows/file_selector_plugin.cpp create mode 100644 packages/file_selector/file_selector_windows/windows/file_selector_plugin.h create mode 100644 packages/file_selector/file_selector_windows/windows/file_selector_windows.cpp create mode 100644 packages/file_selector/file_selector_windows/windows/include/file_selector_windows/file_selector_windows.h create mode 100644 packages/file_selector/file_selector_windows/windows/string_utils.cpp create mode 100644 packages/file_selector/file_selector_windows/windows/string_utils.h create mode 100644 packages/file_selector/file_selector_windows/windows/test/file_selector_plugin_test.cpp create mode 100644 packages/file_selector/file_selector_windows/windows/test/test_file_dialog_controller.cpp create mode 100644 packages/file_selector/file_selector_windows/windows/test/test_file_dialog_controller.h create mode 100644 packages/file_selector/file_selector_windows/windows/test/test_main.cpp create mode 100644 packages/file_selector/file_selector_windows/windows/test/test_utils.cpp create mode 100644 packages/file_selector/file_selector_windows/windows/test/test_utils.h create mode 100644 script/configs/exclude_integration_win32.yaml diff --git a/.ci/scripts/drive_examples_win32.sh b/.ci/scripts/drive_examples_win32.sh index 67f4fde5a70a..c3e2e7bc5447 100644 --- a/.ci/scripts/drive_examples_win32.sh +++ b/.ci/scripts/drive_examples_win32.sh @@ -4,4 +4,4 @@ # found in the LICENSE file. dart ./script/tool/bin/flutter_plugin_tools.dart drive-examples --windows \ - --packages-for-branch --log-timing + --exclude=script/configs/exclude_integration_win32.yaml --packages-for-branch --log-timing diff --git a/packages/file_selector/file_selector_windows/.gitignore b/packages/file_selector/file_selector_windows/.gitignore new file mode 100644 index 000000000000..0393a47ff732 --- /dev/null +++ b/packages/file_selector/file_selector_windows/.gitignore @@ -0,0 +1,5 @@ +.dart_tool +.packages +.flutter-plugins +.flutter-plugins-dependencies +pubspec.lock diff --git a/packages/file_selector/file_selector_windows/.metadata b/packages/file_selector/file_selector_windows/.metadata new file mode 100644 index 000000000000..720a4596c087 --- /dev/null +++ b/packages/file_selector/file_selector_windows/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 6d1c244b79f3a2747281f718297ce248bd5ad099 + channel: master + +project_type: plugin diff --git a/packages/file_selector/file_selector_windows/AUTHORS b/packages/file_selector/file_selector_windows/AUTHORS new file mode 100644 index 000000000000..557dff97933b --- /dev/null +++ b/packages/file_selector/file_selector_windows/AUTHORS @@ -0,0 +1,6 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. diff --git a/packages/file_selector/file_selector_windows/CHANGELOG.md b/packages/file_selector/file_selector_windows/CHANGELOG.md new file mode 100644 index 000000000000..63999f245d82 --- /dev/null +++ b/packages/file_selector/file_selector_windows/CHANGELOG.md @@ -0,0 +1,16 @@ +## 0.8.2 + +* Moves source to flutter/plugins, and restructures to allow for unit testing. +* Switches to an internal method channel implementation. + +## 0.0.2+1 + +* Update README + +## 0.0.2 + +* Update SDK constraint to signal compatibility with null safety. + +## 0.0.1 + +* Initial Windows implementation of `file_selector`. diff --git a/packages/file_selector/file_selector_windows/LICENSE b/packages/file_selector/file_selector_windows/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/file_selector/file_selector_windows/LICENSE @@ -0,0 +1,25 @@ +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/file_selector/file_selector_windows/README.md b/packages/file_selector/file_selector_windows/README.md new file mode 100644 index 000000000000..69fb088d599e --- /dev/null +++ b/packages/file_selector/file_selector_windows/README.md @@ -0,0 +1,19 @@ +# file\_selector\_windows + +The Windows implementation of [`file_selector`][1]. + +## Usage + +### Importing the package + +This implementation has not yet been endorsed, meaning that you need to +[depend on `file_selector_windows`][2] in addition to +[depending on `file_selector`][3]. + +Once your pubspec includes the Windows implementation, you can use the +`file_selector` APIs normally. You should not use the `file_selector_windows` +APIs directly. + +[1]: https://pub.dev/packages/file_selector +[2]: https://pub.dev/packages/file_selector_windows/install +[3]: https://pub.dev/packages/file_selector/install diff --git a/packages/file_selector/file_selector_windows/example/.gitignore b/packages/file_selector/file_selector_windows/example/.gitignore new file mode 100644 index 000000000000..7abd0753cfc3 --- /dev/null +++ b/packages/file_selector/file_selector_windows/example/.gitignore @@ -0,0 +1,48 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Currently only web supported +android/ +ios/ + +# Exceptions to above rules. +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages diff --git a/packages/file_selector/file_selector_windows/example/.metadata b/packages/file_selector/file_selector_windows/example/.metadata new file mode 100644 index 000000000000..897381f2373f --- /dev/null +++ b/packages/file_selector/file_selector_windows/example/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 7736f3bc90270dcb0480db2ccffbf1d13c28db85 + channel: dev + +project_type: app diff --git a/packages/file_selector/file_selector_windows/example/README.md b/packages/file_selector/file_selector_windows/example/README.md new file mode 100644 index 000000000000..c8a3cce44a9a --- /dev/null +++ b/packages/file_selector/file_selector_windows/example/README.md @@ -0,0 +1,4 @@ +# `file_selector_windows` Example + +Demonstrates Windows implementation of the +[`file_selector` plugin](https://pub.dev/packages/file_selector). diff --git a/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart b/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart new file mode 100644 index 000000000000..b282b9030d67 --- /dev/null +++ b/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart @@ -0,0 +1,77 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select a directory using `getDirectoryPath`, +/// then displays the selected directory in a dialog. +class GetDirectoryPage extends StatelessWidget { + Future _getDirectoryPath(BuildContext context) async { + const String confirmButtonText = 'Choose'; + final String? directoryPath = + await FileSelectorPlatform.instance.getDirectoryPath( + confirmButtonText: confirmButtonText, + ); + if (directoryPath == null) { + // Operation was canceled by the user. + return; + } + await showDialog( + context: context, + builder: (BuildContext context) => TextDisplay(directoryPath), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Open a text file'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Colors.blue, + onPrimary: Colors.white, + ), + child: const Text('Press to ask user to choose a directory'), + onPressed: () => _getDirectoryPath(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays a text file in a dialog. +class TextDisplay extends StatelessWidget { + /// Creates a `TextDisplay`. + const TextDisplay(this.directoryPath); + + /// The path selected in the dialog. + final String directoryPath; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text('Selected Directory'), + content: Scrollbar( + child: SingleChildScrollView( + child: Text(directoryPath), + ), + ), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () => Navigator.pop(context), + ), + ], + ); + } +} diff --git a/packages/file_selector/file_selector_windows/example/lib/home_page.dart b/packages/file_selector/file_selector_windows/example/lib/home_page.dart new file mode 100644 index 000000000000..958680be0e3b --- /dev/null +++ b/packages/file_selector/file_selector_windows/example/lib/home_page.dart @@ -0,0 +1,57 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; + +/// Home Page of the application. +class HomePage extends StatelessWidget { + @override + Widget build(BuildContext context) { + final ButtonStyle style = ElevatedButton.styleFrom( + primary: Colors.blue, + onPrimary: Colors.white, + ); + return Scaffold( + appBar: AppBar( + title: const Text('File Selector Demo Home Page'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: style, + child: const Text('Open a text file'), + onPressed: () => Navigator.pushNamed(context, '/open/text'), + ), + const SizedBox(height: 10), + ElevatedButton( + style: style, + child: const Text('Open an image'), + onPressed: () => Navigator.pushNamed(context, '/open/image'), + ), + const SizedBox(height: 10), + ElevatedButton( + style: style, + child: const Text('Open multiple images'), + onPressed: () => Navigator.pushNamed(context, '/open/images'), + ), + const SizedBox(height: 10), + ElevatedButton( + style: style, + child: const Text('Save a file'), + onPressed: () => Navigator.pushNamed(context, '/save/text'), + ), + const SizedBox(height: 10), + ElevatedButton( + style: style, + child: const Text('Open a get directory dialog'), + onPressed: () => Navigator.pushNamed(context, '/directory'), + ), + ], + ), + ), + ); + } +} diff --git a/packages/file_selector/file_selector_windows/example/lib/main.dart b/packages/file_selector/file_selector_windows/example/lib/main.dart new file mode 100644 index 000000000000..a49ebac1aea5 --- /dev/null +++ b/packages/file_selector/file_selector_windows/example/lib/main.dart @@ -0,0 +1,37 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:example/get_directory_page.dart'; +import 'package:example/home_page.dart'; +import 'package:example/open_image_page.dart'; +import 'package:example/open_multiple_images_page.dart'; +import 'package:example/open_text_page.dart'; +import 'package:example/save_text_page.dart'; +import 'package:flutter/material.dart'; + +void main() { + runApp(MyApp()); +} + +/// MyApp is the Main Application. +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'File Selector Demo', + theme: ThemeData( + primarySwatch: Colors.blue, + visualDensity: VisualDensity.adaptivePlatformDensity, + ), + home: HomePage(), + routes: { + '/open/image': (BuildContext context) => OpenImagePage(), + '/open/images': (BuildContext context) => OpenMultipleImagesPage(), + '/open/text': (BuildContext context) => OpenTextPage(), + '/save/text': (BuildContext context) => SaveTextPage(), + '/directory': (BuildContext context) => GetDirectoryPage(), + }, + ); + } +} diff --git a/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart new file mode 100644 index 000000000000..aaf083603e72 --- /dev/null +++ b/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart @@ -0,0 +1,87 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io'; + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select an image file using +/// `openFiles`, then displays the selected images in a gallery dialog. +class OpenImagePage extends StatelessWidget { + Future _openImageFile(BuildContext context) async { + final XTypeGroup typeGroup = XTypeGroup( + label: 'images', + extensions: ['jpg', 'png'], + ); + final XFile? file = await FileSelectorPlatform.instance + .openFile(acceptedTypeGroups: [typeGroup]); + if (file == null) { + // Operation was canceled by the user. + return; + } + final String fileName = file.name; + final String filePath = file.path; + + await showDialog( + context: context, + builder: (BuildContext context) => ImageDisplay(fileName, filePath), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Open an image'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Colors.blue, + onPrimary: Colors.white, + ), + child: const Text('Press to open an image file(png, jpg)'), + onPressed: () => _openImageFile(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays an image in a dialog. +class ImageDisplay extends StatelessWidget { + /// Default Constructor. + const ImageDisplay(this.fileName, this.filePath); + + /// The name of the selected file. + final String fileName; + + /// The path to the selected file. + final String filePath; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(fileName), + // On web the filePath is a blob url + // while on other platforms it is a system path. + content: kIsWeb ? Image.network(filePath) : Image.file(File(filePath)), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ); + } +} diff --git a/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart new file mode 100644 index 000000000000..a030b8b4b10b --- /dev/null +++ b/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart @@ -0,0 +1,99 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io'; + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select multiple image files using +/// `openFiles`, then displays the selected images in a gallery dialog. +class OpenMultipleImagesPage extends StatelessWidget { + Future _openImageFile(BuildContext context) async { + final XTypeGroup jpgsTypeGroup = XTypeGroup( + label: 'JPEGs', + extensions: ['jpg', 'jpeg'], + ); + final XTypeGroup pngTypeGroup = XTypeGroup( + label: 'PNGs', + extensions: ['png'], + ); + final List files = await FileSelectorPlatform.instance + .openFiles(acceptedTypeGroups: [ + jpgsTypeGroup, + pngTypeGroup, + ]); + if (files.isEmpty) { + // Operation was canceled by the user. + return; + } + await showDialog( + context: context, + builder: (BuildContext context) => MultipleImagesDisplay(files), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Open multiple images'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Colors.blue, + onPrimary: Colors.white, + ), + child: const Text('Press to open multiple images (png, jpg)'), + onPressed: () => _openImageFile(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays a text file in a dialog. +class MultipleImagesDisplay extends StatelessWidget { + /// Default Constructor. + const MultipleImagesDisplay(this.files); + + /// The files containing the images. + final List files; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text('Gallery'), + // On web the filePath is a blob url + // while on other platforms it is a system path. + content: Center( + child: Row( + children: [ + ...files.map( + (XFile file) => Flexible( + child: kIsWeb + ? Image.network(file.path) + : Image.file(File(file.path))), + ) + ], + ), + ), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ); + } +} diff --git a/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart new file mode 100644 index 000000000000..fa281a0020d5 --- /dev/null +++ b/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart @@ -0,0 +1,84 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select a text file using `openFile`, then +/// displays its contents in a dialog. +class OpenTextPage extends StatelessWidget { + Future _openTextFile(BuildContext context) async { + final XTypeGroup typeGroup = XTypeGroup( + label: 'text', + extensions: ['txt', 'json'], + ); + final XFile? file = await FileSelectorPlatform.instance + .openFile(acceptedTypeGroups: [typeGroup]); + if (file == null) { + // Operation was canceled by the user. + return; + } + final String fileName = file.name; + final String fileContent = await file.readAsString(); + + await showDialog( + context: context, + builder: (BuildContext context) => TextDisplay(fileName, fileContent), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Open a text file'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Colors.blue, + onPrimary: Colors.white, + ), + child: const Text('Press to open a text file (json, txt)'), + onPressed: () => _openTextFile(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays a text file in a dialog. +class TextDisplay extends StatelessWidget { + /// Default Constructor. + const TextDisplay(this.fileName, this.fileContent); + + /// The name of the selected file. + final String fileName; + + /// The contents of the text file. + final String fileContent; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(fileName), + content: Scrollbar( + child: SingleChildScrollView( + child: Text(fileContent), + ), + ), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () => Navigator.pop(context), + ), + ], + ); + } +} diff --git a/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart b/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart new file mode 100644 index 000000000000..b87a51c3877d --- /dev/null +++ b/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart @@ -0,0 +1,78 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:typed_data'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select a save location using `getSavePath`, +/// then writes text to a file at that location. +class SaveTextPage extends StatelessWidget { + final TextEditingController _nameController = TextEditingController(); + final TextEditingController _contentController = TextEditingController(); + + Future _saveFile() async { + final String fileName = _nameController.text; + final String? path = await FileSelectorPlatform.instance.getSavePath( + // Operation was canceled by the user. + suggestedName: fileName, + ); + if (path == null) { + return; + } + final String text = _contentController.text; + final Uint8List fileData = Uint8List.fromList(text.codeUnits); + const String fileMimeType = 'text/plain'; + final XFile textFile = + XFile.fromData(fileData, mimeType: fileMimeType, name: fileName); + await textFile.saveTo(path); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Save text into a file'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 300, + child: TextField( + minLines: 1, + maxLines: 12, + controller: _nameController, + decoration: const InputDecoration( + hintText: '(Optional) Suggest File Name', + ), + ), + ), + Container( + width: 300, + child: TextField( + minLines: 1, + maxLines: 12, + controller: _contentController, + decoration: const InputDecoration( + hintText: 'Enter File Contents', + ), + ), + ), + const SizedBox(height: 10), + ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Colors.blue, + onPrimary: Colors.white, + ), + child: const Text('Press to save a text file'), + onPressed: _saveFile, + ), + ], + ), + ), + ); + } +} diff --git a/packages/file_selector/file_selector_windows/example/pubspec.yaml b/packages/file_selector/file_selector_windows/example/pubspec.yaml new file mode 100644 index 000000000000..b66a2023deb2 --- /dev/null +++ b/packages/file_selector/file_selector_windows/example/pubspec.yaml @@ -0,0 +1,27 @@ +name: example +description: Example for file_selector_windows implementation. +publish_to: 'none' +version: 1.0.0 + +environment: + sdk: ">=2.12.0 <3.0.0" + flutter: ">=2.0.0" + +dependencies: + file_selector_platform_interface: ^2.0.0 + file_selector_windows: + # When depending on this package from a real application you should use: + # file_selector_windows: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: .. + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/file_selector/file_selector_windows/example/windows/.gitignore b/packages/file_selector/file_selector_windows/example/windows/.gitignore new file mode 100644 index 000000000000..d492d0d98c8f --- /dev/null +++ b/packages/file_selector/file_selector_windows/example/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/packages/file_selector/file_selector_windows/example/windows/CMakeLists.txt b/packages/file_selector/file_selector_windows/example/windows/CMakeLists.txt new file mode 100644 index 000000000000..57d4c0c59d30 --- /dev/null +++ b/packages/file_selector/file_selector_windows/example/windows/CMakeLists.txt @@ -0,0 +1,100 @@ +cmake_minimum_required(VERSION 3.14) +project(example LANGUAGES CXX) + +set(BINARY_NAME "example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() + +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build +add_subdirectory("runner") + +# Enable the test target +set(include_file_selector_windows_tests TRUE) +# Provide an alias for the test target using the name expected by repo tooling. +add_custom_target(unit_tests DEPENDS file_selector_windows_test) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/packages/file_selector/file_selector_windows/example/windows/flutter/CMakeLists.txt b/packages/file_selector/file_selector_windows/example/windows/flutter/CMakeLists.txt new file mode 100644 index 000000000000..b2e4bd8d658b --- /dev/null +++ b/packages/file_selector/file_selector_windows/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,103 @@ +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/packages/file_selector/file_selector_windows/example/windows/flutter/generated_plugins.cmake b/packages/file_selector/file_selector_windows/example/windows/flutter/generated_plugins.cmake new file mode 100644 index 000000000000..63eda9b7b59f --- /dev/null +++ b/packages/file_selector/file_selector_windows/example/windows/flutter/generated_plugins.cmake @@ -0,0 +1,16 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + file_selector_windows +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) diff --git a/packages/file_selector/file_selector_windows/example/windows/runner/CMakeLists.txt b/packages/file_selector/file_selector_windows/example/windows/runner/CMakeLists.txt new file mode 100644 index 000000000000..de2d8916b72b --- /dev/null +++ b/packages/file_selector/file_selector_windows/example/windows/runner/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) +apply_standard_settings(${BINARY_NAME}) +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/packages/file_selector/file_selector_windows/example/windows/runner/Runner.rc b/packages/file_selector/file_selector_windows/example/windows/runner/Runner.rc new file mode 100644 index 000000000000..51812dcd4878 --- /dev/null +++ b/packages/file_selector/file_selector_windows/example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#ifdef FLUTTER_BUILD_NUMBER +#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#else +#define VERSION_AS_NUMBER 1,0,0 +#endif + +#ifdef FLUTTER_BUILD_NAME +#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "A new Flutter project." "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2021 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "example.exe" "\0" + VALUE "ProductName", "example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/packages/file_selector/file_selector_windows/example/windows/runner/flutter_window.cpp b/packages/file_selector/file_selector_windows/example/windows/runner/flutter_window.cpp new file mode 100644 index 000000000000..217bf9b69e67 --- /dev/null +++ b/packages/file_selector/file_selector_windows/example/windows/runner/flutter_window.cpp @@ -0,0 +1,64 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/packages/file_selector/file_selector_windows/example/windows/runner/flutter_window.h b/packages/file_selector/file_selector_windows/example/windows/runner/flutter_window.h new file mode 100644 index 000000000000..7cbf3d3ebbb2 --- /dev/null +++ b/packages/file_selector/file_selector_windows/example/windows/runner/flutter_window.h @@ -0,0 +1,36 @@ +// 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. +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/packages/file_selector/file_selector_windows/example/windows/runner/main.cpp b/packages/file_selector/file_selector_windows/example/windows/runner/main.cpp new file mode 100644 index 000000000000..1285aabf714a --- /dev/null +++ b/packages/file_selector/file_selector_windows/example/windows/runner/main.cpp @@ -0,0 +1,45 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t* command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/packages/file_selector/file_selector_windows/example/windows/runner/resource.h b/packages/file_selector/file_selector_windows/example/windows/runner/resource.h new file mode 100644 index 000000000000..d5d958dc4257 --- /dev/null +++ b/packages/file_selector/file_selector_windows/example/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/packages/file_selector/file_selector_windows/example/windows/runner/resources/app_icon.ico b/packages/file_selector/file_selector_windows/example/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/packages/file_selector/file_selector_windows/example/windows/runner/runner.exe.manifest b/packages/file_selector/file_selector_windows/example/windows/runner/runner.exe.manifest new file mode 100644 index 000000000000..c977c4a42589 --- /dev/null +++ b/packages/file_selector/file_selector_windows/example/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/packages/file_selector/file_selector_windows/example/windows/runner/utils.cpp b/packages/file_selector/file_selector_windows/example/windows/runner/utils.cpp new file mode 100644 index 000000000000..8b8eaa54539a --- /dev/null +++ b/packages/file_selector/file_selector_windows/example/windows/runner/utils.cpp @@ -0,0 +1,66 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE* unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = + ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, + nullptr, 0, nullptr, nullptr); + if (target_length == 0) { + return std::string(); + } + std::string utf8_string; + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/packages/file_selector/file_selector_windows/example/windows/runner/utils.h b/packages/file_selector/file_selector_windows/example/windows/runner/utils.h new file mode 100644 index 000000000000..6d1cc48f0426 --- /dev/null +++ b/packages/file_selector/file_selector_windows/example/windows/runner/utils.h @@ -0,0 +1,22 @@ +// 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. +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/packages/file_selector/file_selector_windows/example/windows/runner/win32_window.cpp b/packages/file_selector/file_selector_windows/example/windows/runner/win32_window.cpp new file mode 100644 index 000000000000..34738de2d35b --- /dev/null +++ b/packages/file_selector/file_selector_windows/example/windows/runner/win32_window.cpp @@ -0,0 +1,240 @@ +// 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. +#include "win32_window.h" + +#include + +#include "resource.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + FreeLibrary(user32_module); + } +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { ++g_active_window_count; } + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::CreateAndShow(const std::wstring& title, const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { return window_handle_; } + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} diff --git a/packages/file_selector/file_selector_windows/example/windows/runner/win32_window.h b/packages/file_selector/file_selector_windows/example/windows/runner/win32_window.h new file mode 100644 index 000000000000..0f8bd1b7f920 --- /dev/null +++ b/packages/file_selector/file_selector_windows/example/windows/runner/win32_window.h @@ -0,0 +1,98 @@ +// 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. +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates and shows a win32 window with |title| and position and size using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring& title, const Point& origin, + const Size& size); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/packages/file_selector/file_selector_windows/lib/file_selector_windows.dart b/packages/file_selector/file_selector_windows/lib/file_selector_windows.dart new file mode 100644 index 000000000000..a8b159711e2a --- /dev/null +++ b/packages/file_selector/file_selector_windows/lib/file_selector_windows.dart @@ -0,0 +1,97 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:cross_file/cross_file.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/foundation.dart' show visibleForTesting; +import 'package:flutter/services.dart'; + +const MethodChannel _channel = + MethodChannel('plugins.flutter.io/file_selector_windows'); + +/// An implementation of [FileSelectorPlatform] for Windows. +class FileSelectorWindows extends FileSelectorPlatform { + /// The MethodChannel that is being used by this implementation of the plugin. + @visibleForTesting + MethodChannel get channel => _channel; + + /// Registers the Windows implementation. + static void registerWith() { + FileSelectorPlatform.instance = FileSelectorWindows(); + } + + @override + Future openFile({ + List? acceptedTypeGroups, + String? initialDirectory, + String? confirmButtonText, + }) async { + final List? path = await _channel.invokeListMethod( + 'openFile', + { + 'acceptedTypeGroups': acceptedTypeGroups + ?.map((XTypeGroup group) => group.toJSON()) + .toList(), + 'initialDirectory': initialDirectory, + 'confirmButtonText': confirmButtonText, + 'multiple': false, + }, + ); + return path == null ? null : XFile(path.first); + } + + @override + Future> openFiles({ + List? acceptedTypeGroups, + String? initialDirectory, + String? confirmButtonText, + }) async { + final List? pathList = await _channel.invokeListMethod( + 'openFile', + { + 'acceptedTypeGroups': acceptedTypeGroups + ?.map((XTypeGroup group) => group.toJSON()) + .toList(), + 'initialDirectory': initialDirectory, + 'confirmButtonText': confirmButtonText, + 'multiple': true, + }, + ); + return pathList?.map((String path) => XFile(path)).toList() ?? []; + } + + @override + Future getSavePath({ + List? acceptedTypeGroups, + String? initialDirectory, + String? suggestedName, + String? confirmButtonText, + }) async { + return _channel.invokeMethod( + 'getSavePath', + { + 'acceptedTypeGroups': acceptedTypeGroups + ?.map((XTypeGroup group) => group.toJSON()) + .toList(), + 'initialDirectory': initialDirectory, + 'suggestedName': suggestedName, + 'confirmButtonText': confirmButtonText, + }, + ); + } + + @override + Future getDirectoryPath({ + String? initialDirectory, + String? confirmButtonText, + }) async { + return _channel.invokeMethod( + 'getDirectoryPath', + { + 'initialDirectory': initialDirectory, + 'confirmButtonText': confirmButtonText, + }, + ); + } +} diff --git a/packages/file_selector/file_selector_windows/pubspec.yaml b/packages/file_selector/file_selector_windows/pubspec.yaml new file mode 100644 index 000000000000..7b035e974293 --- /dev/null +++ b/packages/file_selector/file_selector_windows/pubspec.yaml @@ -0,0 +1,25 @@ +name: file_selector_windows +description: Windows implementation of the file_selector plugin. +repository: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector_windows +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 +version: 0.8.2 + +environment: + sdk: ">=2.12.0 <3.0.0" + flutter: ">=2.5.0" + +flutter: + plugin: + implements: file_selector + platforms: + windows: + dartPluginClass: FileSelectorWindows + pluginClass: FileSelectorWindows + +dependencies: + cross_file: ^0.3.1 + file_selector_platform_interface: ^2.0.4 + flutter: + sdk: flutter + flutter_test: + sdk: flutter diff --git a/packages/file_selector/file_selector_windows/test/file_selector_windows_test.dart b/packages/file_selector/file_selector_windows/test/file_selector_windows_test.dart new file mode 100644 index 000000000000..72604dd1668c --- /dev/null +++ b/packages/file_selector/file_selector_windows/test/file_selector_windows_test.dart @@ -0,0 +1,257 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:file_selector_windows/file_selector_windows.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('$FileSelectorWindows()', () { + final FileSelectorWindows plugin = FileSelectorWindows(); + + final List log = []; + + setUp(() { + plugin.channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + return null; + }); + + log.clear(); + }); + + test('registered instance', () { + FileSelectorWindows.registerWith(); + expect(FileSelectorPlatform.instance, isA()); + }); + + group('#openFile', () { + test('passes the accepted type groups correctly', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + extensions: ['txt'], + mimeTypes: ['text/plain'], + macUTIs: ['public.text'], + ); + + final XTypeGroup groupTwo = XTypeGroup( + label: 'image', + extensions: ['jpg'], + mimeTypes: ['image/jpg'], + macUTIs: ['public.image'], + webWildCards: ['image/*']); + + await plugin + .openFile(acceptedTypeGroups: [group, groupTwo]); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypeGroups': >[ + group.toJSON(), + groupTwo.toJSON() + ], + 'initialDirectory': null, + 'confirmButtonText': null, + 'multiple': false, + }), + ], + ); + }); + test('passes initialDirectory correctly', () async { + await plugin.openFile(initialDirectory: '/example/directory'); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypeGroups': null, + 'initialDirectory': '/example/directory', + 'confirmButtonText': null, + 'multiple': false, + }), + ], + ); + }); + test('passes confirmButtonText correctly', () async { + await plugin.openFile(confirmButtonText: 'Open File'); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypeGroups': null, + 'initialDirectory': null, + 'confirmButtonText': 'Open File', + 'multiple': false, + }), + ], + ); + }); + }); + group('#openFiles', () { + test('passes the accepted type groups correctly', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + extensions: ['txt'], + mimeTypes: ['text/plain'], + macUTIs: ['public.text'], + ); + + final XTypeGroup groupTwo = XTypeGroup( + label: 'image', + extensions: ['jpg'], + mimeTypes: ['image/jpg'], + macUTIs: ['public.image'], + webWildCards: ['image/*']); + + await plugin + .openFiles(acceptedTypeGroups: [group, groupTwo]); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypeGroups': >[ + group.toJSON(), + groupTwo.toJSON() + ], + 'initialDirectory': null, + 'confirmButtonText': null, + 'multiple': true, + }), + ], + ); + }); + test('passes initialDirectory correctly', () async { + await plugin.openFiles(initialDirectory: '/example/directory'); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypeGroups': null, + 'initialDirectory': '/example/directory', + 'confirmButtonText': null, + 'multiple': true, + }), + ], + ); + }); + test('passes confirmButtonText correctly', () async { + await plugin.openFiles(confirmButtonText: 'Open File'); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypeGroups': null, + 'initialDirectory': null, + 'confirmButtonText': 'Open File', + 'multiple': true, + }), + ], + ); + }); + }); + + group('#getSavePath', () { + test('passes the accepted type groups correctly', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + extensions: ['txt'], + mimeTypes: ['text/plain'], + macUTIs: ['public.text'], + ); + + final XTypeGroup groupTwo = XTypeGroup( + label: 'image', + extensions: ['jpg'], + mimeTypes: ['image/jpg'], + macUTIs: ['public.image'], + webWildCards: ['image/*']); + + await plugin + .getSavePath(acceptedTypeGroups: [group, groupTwo]); + + expect( + log, + [ + isMethodCall('getSavePath', arguments: { + 'acceptedTypeGroups': >[ + group.toJSON(), + groupTwo.toJSON() + ], + 'initialDirectory': null, + 'suggestedName': null, + 'confirmButtonText': null, + }), + ], + ); + }); + test('passes initialDirectory correctly', () async { + await plugin.getSavePath(initialDirectory: '/example/directory'); + + expect( + log, + [ + isMethodCall('getSavePath', arguments: { + 'acceptedTypeGroups': null, + 'initialDirectory': '/example/directory', + 'suggestedName': null, + 'confirmButtonText': null, + }), + ], + ); + }); + test('passes confirmButtonText correctly', () async { + await plugin.getSavePath(confirmButtonText: 'Open File'); + + expect( + log, + [ + isMethodCall('getSavePath', arguments: { + 'acceptedTypeGroups': null, + 'initialDirectory': null, + 'suggestedName': null, + 'confirmButtonText': 'Open File', + }), + ], + ); + }); + group('#getDirectoryPath', () { + test('passes initialDirectory correctly', () async { + await plugin.getDirectoryPath(initialDirectory: '/example/directory'); + + expect( + log, + [ + isMethodCall('getDirectoryPath', arguments: { + 'initialDirectory': '/example/directory', + 'confirmButtonText': null, + }), + ], + ); + }); + test('passes confirmButtonText correctly', () async { + await plugin.getDirectoryPath(confirmButtonText: 'Open File'); + + expect( + log, + [ + isMethodCall('getDirectoryPath', arguments: { + 'initialDirectory': null, + 'confirmButtonText': 'Open File', + }), + ], + ); + }); + }); + }); + }); +} diff --git a/packages/file_selector/file_selector_windows/windows/.gitignore b/packages/file_selector/file_selector_windows/windows/.gitignore new file mode 100644 index 000000000000..b3eb2be169a5 --- /dev/null +++ b/packages/file_selector/file_selector_windows/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/packages/file_selector/file_selector_windows/windows/CMakeLists.txt b/packages/file_selector/file_selector_windows/windows/CMakeLists.txt new file mode 100644 index 000000000000..01c9f58b98a2 --- /dev/null +++ b/packages/file_selector/file_selector_windows/windows/CMakeLists.txt @@ -0,0 +1,78 @@ +cmake_minimum_required(VERSION 3.14) +set(PROJECT_NAME "file_selector_windows") +project(${PROJECT_NAME} LANGUAGES CXX) + +set(PLUGIN_NAME "${PROJECT_NAME}_plugin") + +list(APPEND PLUGIN_SOURCES + "file_dialog_controller.cpp" + "file_dialog_controller.h" + "file_selector_plugin.cpp" + "file_selector_plugin.h" + "string_utils.cpp" + "string_utils.h" +) + +add_library(${PLUGIN_NAME} SHARED + "file_selector_windows.cpp" + "include/file_selector_windows/file_selector_windows.h" + ${PLUGIN_SOURCES} +) +apply_standard_settings(${PLUGIN_NAME}) +set_target_properties(${PLUGIN_NAME} PROPERTIES CXX_VISIBILITY_PRESET hidden) +target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) +target_include_directories(${PLUGIN_NAME} INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin) + +# List of absolute paths to libraries that should be bundled with the plugin +set(file_selector_bundled_libraries + "" + PARENT_SCOPE +) + + +# === Tests === + +if (${include_${PROJECT_NAME}_tests}) +set(TEST_RUNNER "${PROJECT_NAME}_test") +enable_testing() +# TODO(stuartmorgan): Consider using a single shared, pre-checked-in googletest +# instance rather than downloading for each plugin. This approach makes sense +# for a template, but not for a monorepo with many plugins. +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/release-1.11.0.zip +) +# Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +# Disable install commands for gtest so it doesn't end up in the bundle. +set(INSTALL_GTEST OFF CACHE BOOL "Disable installation of googletest" FORCE) + +FetchContent_MakeAvailable(googletest) + +# The plugin's C API is not very useful for unit testing, so build the sources +# directly into the test binary rather than using the DLL. +add_executable(${TEST_RUNNER} + test/file_selector_plugin_test.cpp + test/test_main.cpp + test/test_file_dialog_controller.cpp + test/test_file_dialog_controller.h + test/test_utils.cpp + test/test_utils.h + ${PLUGIN_SOURCES} +) +apply_standard_settings(${TEST_RUNNER}) +target_include_directories(${TEST_RUNNER} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}") +target_link_libraries(${TEST_RUNNER} PRIVATE flutter_wrapper_plugin) +target_link_libraries(${TEST_RUNNER} PRIVATE gtest gmock) +# flutter_wrapper_plugin has link dependencies on the Flutter DLL. +add_custom_command(TARGET ${TEST_RUNNER} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${FLUTTER_LIBRARY}" $ +) + +include(GoogleTest) +gtest_discover_tests(${TEST_RUNNER}) +endif() diff --git a/packages/file_selector/file_selector_windows/windows/file_dialog_controller.cpp b/packages/file_selector/file_selector_windows/windows/file_dialog_controller.cpp new file mode 100644 index 000000000000..e4b1a2afcdde --- /dev/null +++ b/packages/file_selector/file_selector_windows/windows/file_dialog_controller.cpp @@ -0,0 +1,66 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "file_dialog_controller.h" + +#include +#include +#include + +_COM_SMARTPTR_TYPEDEF(IFileOpenDialog, IID_IFileOpenDialog); + +namespace file_selector_windows { + +FileDialogController::FileDialogController(IFileDialog* dialog) + : dialog_(dialog) {} + +FileDialogController::~FileDialogController() {} + +HRESULT FileDialogController::SetDefaultFolder(IShellItem* folder) { + return dialog_->SetDefaultFolder(folder); +} + +HRESULT FileDialogController::SetFileName(const wchar_t* name) { + return dialog_->SetFileName(name); +} + +HRESULT FileDialogController::SetFileTypes(UINT count, + COMDLG_FILTERSPEC* filters) { + return dialog_->SetFileTypes(count, filters); +} + +HRESULT FileDialogController::SetOkButtonLabel(const wchar_t* text) { + return dialog_->SetOkButtonLabel(text); +} + +HRESULT FileDialogController::GetOptions( + FILEOPENDIALOGOPTIONS* out_options) const { + return dialog_->GetOptions(out_options); +} + +HRESULT FileDialogController::SetOptions(FILEOPENDIALOGOPTIONS options) { + return dialog_->SetOptions(options); +} + +HRESULT FileDialogController::Show(HWND parent) { + return dialog_->Show(parent); +} + +HRESULT FileDialogController::GetResult(IShellItem** out_item) const { + return dialog_->GetResult(out_item); +} + +HRESULT FileDialogController::GetResults(IShellItemArray** out_items) const { + IFileOpenDialogPtr open_dialog; + HRESULT result = dialog_->QueryInterface(IID_PPV_ARGS(&open_dialog)); + if (!SUCCEEDED(result)) { + return result; + } + result = open_dialog->GetResults(out_items); + return result; +} + +FileDialogControllerFactory::~FileDialogControllerFactory() {} + +} // namespace file_selector_windows diff --git a/packages/file_selector/file_selector_windows/windows/file_dialog_controller.h b/packages/file_selector/file_selector_windows/windows/file_dialog_controller.h new file mode 100644 index 000000000000..e7357338243e --- /dev/null +++ b/packages/file_selector/file_selector_windows/windows/file_dialog_controller.h @@ -0,0 +1,62 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#ifndef PACKAGES_FILE_SELECTOR_FILE_SELECTOR_WINDOWS_WINDOWS_FILE_DIALOG_CONTROLLER_H_ +#define PACKAGES_FILE_SELECTOR_FILE_SELECTOR_WINDOWS_WINDOWS_FILE_DIALOG_CONTROLLER_H_ + +#include +#include +#include +#include + +#include + +_COM_SMARTPTR_TYPEDEF(IFileDialog, IID_IFileDialog); + +namespace file_selector_windows { + +// A thin wrapper for IFileDialog to allow for faking and inspection in tests. +// +// Since this class defines the end of what can be unit tested, it should +// contain as little logic as possible. +class FileDialogController { + public: + // Creates a controller managing |dialog|. + FileDialogController(IFileDialog* dialog); + virtual ~FileDialogController(); + + // Disallow copy and assign. + FileDialogController(const FileDialogController&) = delete; + FileDialogController& operator=(const FileDialogController&) = delete; + + // IFileDialog wrappers: + virtual HRESULT SetDefaultFolder(IShellItem* folder); + virtual HRESULT SetFileName(const wchar_t* name); + virtual HRESULT SetFileTypes(UINT count, COMDLG_FILTERSPEC* filters); + virtual HRESULT SetOkButtonLabel(const wchar_t* text); + virtual HRESULT GetOptions(FILEOPENDIALOGOPTIONS* out_options) const; + virtual HRESULT SetOptions(FILEOPENDIALOGOPTIONS options); + virtual HRESULT Show(HWND parent); + virtual HRESULT GetResult(IShellItem** out_item) const; + + // IFileOpenDialog wrapper. This will fail if the IFileDialog* provided to the + // constructor was not an IFileOpenDialog instance. + virtual HRESULT GetResults(IShellItemArray** out_items) const; + + private: + IFileDialogPtr dialog_ = nullptr; +}; + +// Interface for creating FileDialogControllers, to allow for dependency +// injection. +class FileDialogControllerFactory { + public: + virtual ~FileDialogControllerFactory(); + + virtual std::unique_ptr CreateController( + IFileDialog* dialog) const = 0; +}; + +} // namespace file_selector_windows + +#endif // PACKAGES_FILE_SELECTOR_FILE_SELECTOR_WINDOWS_WINDOWS_FILE_DIALOG_CONTROLLER_H_ diff --git a/packages/file_selector/file_selector_windows/windows/file_selector_plugin.cpp b/packages/file_selector/file_selector_windows/windows/file_selector_plugin.cpp new file mode 100644 index 000000000000..870bc281b6f6 --- /dev/null +++ b/packages/file_selector/file_selector_windows/windows/file_selector_plugin.cpp @@ -0,0 +1,362 @@ +// 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. +#include "file_selector_plugin.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "file_dialog_controller.h" +#include "string_utils.h" + +_COM_SMARTPTR_TYPEDEF(IEnumShellItems, IID_IEnumShellItems); +_COM_SMARTPTR_TYPEDEF(IFileDialog, IID_IFileDialog); +_COM_SMARTPTR_TYPEDEF(IShellItem, IID_IShellItem); +_COM_SMARTPTR_TYPEDEF(IShellItemArray, IID_IShellItemArray); + +namespace file_selector_windows { + +namespace { + +using flutter::EncodableList; +using flutter::EncodableMap; +using flutter::EncodableValue; + +// From file_selector_windows.dart +constexpr char kChannelName[] = "plugins.flutter.io/file_selector_windows"; + +constexpr char kOpenFileMethod[] = "openFile"; +constexpr char kGetSavePathMethod[] = "getSavePath"; +constexpr char kGetDirectoryPathMethod[] = "getDirectoryPath"; + +constexpr char kAcceptedTypeGroupsKey[] = "acceptedTypeGroups"; +constexpr char kConfirmButtonTextKey[] = "confirmButtonText"; +constexpr char kInitialDirectoryKey[] = "initialDirectory"; +constexpr char kMultipleKey[] = "multiple"; +constexpr char kSuggestedNameKey[] = "suggestedName"; + +// From x_type_group.dart +// Only 'extensions' are supported by Windows for filtering. +constexpr char kTypeGroupLabelKey[] = "label"; +constexpr char kTypeGroupExtensionsKey[] = "extensions"; + +// Looks for |key| in |map|, returning the associated value if it is present, or +// a nullptr if not. +const EncodableValue* ValueOrNull(const EncodableMap& map, const char* key) { + auto it = map.find(EncodableValue(key)); + if (it == map.end()) { + return nullptr; + } + return &(it->second); +} + +// Returns the path for |shell_item| as a UTF-8 string, or an +// empty string on failure. +std::string GetPathForShellItem(IShellItem* shell_item) { + if (shell_item == nullptr) { + return ""; + } + wchar_t* wide_path = nullptr; + if (!SUCCEEDED(shell_item->GetDisplayName(SIGDN_FILESYSPATH, &wide_path))) { + return ""; + } + std::string path = Utf8FromUtf16(wide_path); + ::CoTaskMemFree(wide_path); + return path; +} + +// Implementation of FileDialogControllerFactory that makes standard +// FileDialogController instances. +class DefaultFileDialogControllerFactory : public FileDialogControllerFactory { + public: + DefaultFileDialogControllerFactory() {} + virtual ~DefaultFileDialogControllerFactory() {} + + // Disallow copy and assign. + DefaultFileDialogControllerFactory( + const DefaultFileDialogControllerFactory&) = delete; + DefaultFileDialogControllerFactory& operator=( + const DefaultFileDialogControllerFactory&) = delete; + + std::unique_ptr CreateController( + IFileDialog* dialog) const override { + assert(dialog != nullptr); + return std::make_unique(dialog); + } +}; + +// Wraps an IFileDialog, managing object lifetime as a scoped object and +// providing a simplified API for interacting with it as needed for the plugin. +class DialogWrapper { + public: + explicit DialogWrapper(const FileDialogControllerFactory& dialog_factory, + IID type) { + is_open_dialog_ = type == CLSID_FileOpenDialog; + IFileDialogPtr dialog = nullptr; + last_result_ = CoCreateInstance(type, nullptr, CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&dialog)); + dialog_controller_ = dialog_factory.CreateController(dialog); + } + + // Attempts to set the default folder for the dialog to |path|, + // if it exists. + void SetDefaultFolder(const std::string& path) { + std::wstring wide_path = Utf16FromUtf8(path); + IShellItemPtr item; + last_result_ = SHCreateItemFromParsingName(wide_path.c_str(), nullptr, + IID_PPV_ARGS(&item)); + if (!SUCCEEDED(last_result_)) { + return; + } + dialog_controller_->SetDefaultFolder(item); + } + + // Sets the file name that is initially shown in the dialog. + void SetFileName(const std::string& name) { + std::wstring wide_name = Utf16FromUtf8(name); + last_result_ = dialog_controller_->SetFileName(wide_name.c_str()); + } + + // Sets the label of the confirmation button. + void SetOkButtonLabel(const std::string& label) { + std::wstring wide_label = Utf16FromUtf8(label); + last_result_ = dialog_controller_->SetOkButtonLabel(wide_label.c_str()); + } + + // Adds the given options to the dialog's current option set. + void AddOptions(FILEOPENDIALOGOPTIONS new_options) { + FILEOPENDIALOGOPTIONS options; + last_result_ = dialog_controller_->GetOptions(&options); + if (!SUCCEEDED(last_result_)) { + return; + } + options |= new_options; + if (options & FOS_PICKFOLDERS) { + opening_directory_ = true; + } + last_result_ = dialog_controller_->SetOptions(options); + } + + // Sets the filters for allowed file types to select. + void SetFileTypeFilters(const EncodableList& filters) { + const std::wstring spec_delimiter = L";"; + const std::wstring file_wildcard = L"*."; + std::vector filter_specs; + // Temporary ownership of the constructed strings whose data is used in + // filter_specs, so that they live until the call to SetFileTypes is done. + std::vector filter_names; + std::vector filter_extensions; + filter_extensions.reserve(filters.size()); + filter_names.reserve(filters.size()); + + for (const EncodableValue& filter_info_value : filters) { + const auto& filter_info = std::get(filter_info_value); + const auto* filter_name = std::get_if( + ValueOrNull(filter_info, kTypeGroupLabelKey)); + const auto* extensions = std::get_if( + ValueOrNull(filter_info, kTypeGroupExtensionsKey)); + filter_names.push_back(filter_name ? Utf16FromUtf8(*filter_name) : L""); + filter_extensions.push_back(L""); + std::wstring& spec = filter_extensions.back(); + if (!extensions || extensions->empty()) { + spec += L"*.*"; + } else { + for (const EncodableValue& extension : *extensions) { + if (!spec.empty()) { + spec += spec_delimiter; + } + spec += + file_wildcard + Utf16FromUtf8(std::get(extension)); + } + } + filter_specs.push_back({filter_names.back().c_str(), spec.c_str()}); + } + last_result_ = dialog_controller_->SetFileTypes( + static_cast(filter_specs.size()), filter_specs.data()); + } + + // Displays the dialog, and returns the selected file or files as an + // EncodableValue of type List (for open) or String (for save), or a null + // EncodableValue on cancel or error. + EncodableValue Show(HWND parent_window) { + assert(dialog_controller_); + last_result_ = dialog_controller_->Show(parent_window); + if (!SUCCEEDED(last_result_)) { + return EncodableValue(); + } + + if (is_open_dialog_) { + IShellItemArrayPtr shell_items; + last_result_ = dialog_controller_->GetResults(&shell_items); + if (!SUCCEEDED(last_result_)) { + return EncodableValue(); + } + IEnumShellItemsPtr item_enumerator; + last_result_ = shell_items->EnumItems(&item_enumerator); + if (!SUCCEEDED(last_result_)) { + return EncodableValue(); + } + EncodableList files; + IShellItemPtr shell_item; + while (item_enumerator->Next(1, &shell_item, nullptr) == S_OK) { + files.push_back(EncodableValue(GetPathForShellItem(shell_item))); + } + if (opening_directory_) { + // The directory option expects a String, not a List. + if (files.empty()) { + return EncodableValue(); + } + return EncodableValue(files[0]); + } else { + return EncodableValue(std::move(files)); + } + } else { + IShellItemPtr shell_item; + last_result_ = dialog_controller_->GetResult(&shell_item); + if (!SUCCEEDED(last_result_)) { + return EncodableValue(); + } + EncodableValue file(GetPathForShellItem(shell_item)); + return file; + } + } + + // Returns the result of the last Win32 API call related to this object. + HRESULT last_result() { return last_result_; } + + private: + // The dialog controller that all interactions are mediated through, to allow + // for unit testing. + std::unique_ptr dialog_controller_; + bool is_open_dialog_; + bool opening_directory_ = false; + HRESULT last_result_; +}; + +// Displays the open or save dialog (according to |method|) and sends the +// selected file path(s) back to the engine via |result|, or sends an +// error on failure. +// +// |result| is guaranteed to be resolved by this function. +void ShowDialog(const FileDialogControllerFactory& dialog_factory, + HWND parent_window, const std::string& method, + const EncodableMap& args, + std::unique_ptr> result) { + IID dialog_type = method.compare(kGetSavePathMethod) == 0 + ? CLSID_FileSaveDialog + : CLSID_FileOpenDialog; + DialogWrapper dialog(dialog_factory, dialog_type); + if (!SUCCEEDED(dialog.last_result())) { + result->Error("System error", "Could not create dialog", + EncodableValue(dialog.last_result())); + return; + } + + FILEOPENDIALOGOPTIONS dialog_options = 0; + if (method.compare(kGetDirectoryPathMethod) == 0) { + dialog_options |= FOS_PICKFOLDERS; + } + const auto* allow_multiple_selection = + std::get_if(ValueOrNull(args, kMultipleKey)); + if (allow_multiple_selection && *allow_multiple_selection) { + dialog_options |= FOS_ALLOWMULTISELECT; + } + if (dialog_options != 0) { + dialog.AddOptions(dialog_options); + } + + const auto* initial_dir = + std::get_if(ValueOrNull(args, kInitialDirectoryKey)); + if (initial_dir) { + dialog.SetDefaultFolder(*initial_dir); + } + const auto* suggested_name = + std::get_if(ValueOrNull(args, kSuggestedNameKey)); + if (suggested_name) { + dialog.SetFileName(*suggested_name); + } + const auto* confirm_label = + std::get_if(ValueOrNull(args, kConfirmButtonTextKey)); + if (confirm_label) { + dialog.SetOkButtonLabel(*confirm_label); + } + const auto* accepted_types = + std::get_if(ValueOrNull(args, kAcceptedTypeGroupsKey)); + if (accepted_types && !accepted_types->empty()) { + dialog.SetFileTypeFilters(*accepted_types); + } + + EncodableValue files = dialog.Show(parent_window); + if (files.IsNull() && + dialog.last_result() != HRESULT_FROM_WIN32(ERROR_CANCELLED)) { + ; + result->Error("System error", "Could not show dialog", + EncodableValue(dialog.last_result())); + } + result->Success(files); +} + +// Returns the top-level window that owns |view|. +HWND GetRootWindow(flutter::FlutterView* view) { + return ::GetAncestor(view->GetNativeWindow(), GA_ROOT); +} + +} // namespace + +// static +void FileSelectorPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarWindows* registrar) { + auto channel = std::make_unique>( + registrar->messenger(), kChannelName, + &flutter::StandardMethodCodec::GetInstance()); + + std::unique_ptr plugin = + std::make_unique( + [registrar] { return GetRootWindow(registrar->GetView()); }, + std::make_unique()); + + channel->SetMethodCallHandler( + [plugin_pointer = plugin.get()](const auto& call, auto result) { + plugin_pointer->HandleMethodCall(call, std::move(result)); + }); + + registrar->AddPlugin(std::move(plugin)); +} + +FileSelectorPlugin::FileSelectorPlugin( + FlutterRootWindowProvider window_provider, + std::unique_ptr dialog_controller_factory) + : get_root_window_(std::move(window_provider)), + controller_factory_(std::move(dialog_controller_factory)) {} + +FileSelectorPlugin::~FileSelectorPlugin() = default; + +void FileSelectorPlugin::HandleMethodCall( + const flutter::MethodCall<>& method_call, + std::unique_ptr> result) { + const std::string& method_name = method_call.method_name(); + if (method_name.compare(kOpenFileMethod) == 0 || + method_name.compare(kGetSavePathMethod) == 0 || + method_name.compare(kGetDirectoryPathMethod) == 0) { + const auto* arguments = + std::get_if(method_call.arguments()); + assert(arguments); + ShowDialog(*controller_factory_, get_root_window_(), method_name, + *arguments, std::move(result)); + } else { + result->NotImplemented(); + } +} + +} // namespace file_selector_windows diff --git a/packages/file_selector/file_selector_windows/windows/file_selector_plugin.h b/packages/file_selector/file_selector_windows/windows/file_selector_plugin.h new file mode 100644 index 000000000000..292d312bea30 --- /dev/null +++ b/packages/file_selector/file_selector_windows/windows/file_selector_plugin.h @@ -0,0 +1,47 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#ifndef PACKAGES_FILE_SELECTOR_FILE_SELECTOR_WINDOWS_WINDOWS_FILE_SELECTOR_PLUGIN_H_ +#define PACKAGES_FILE_SELECTOR_FILE_SELECTOR_WINDOWS_WINDOWS_FILE_SELECTOR_PLUGIN_H_ + +#include +#include + +#include + +#include "file_dialog_controller.h" + +namespace file_selector_windows { + +// Abstraction for accessing the Flutter view's root window, to allow for faking +// in unit tests without creating fake window hierarchies, as well as to work +// around https://github.com/flutter/flutter/issues/90694. +using FlutterRootWindowProvider = std::function; + +class FileSelectorPlugin : public flutter::Plugin { + public: + static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); + + // Creates a new plugin instance for the given registar, using the given + // factory to create native dialog controllers. + FileSelectorPlugin( + FlutterRootWindowProvider window_provider, + std::unique_ptr dialog_controller_factory); + + virtual ~FileSelectorPlugin(); + + // Called when a method is called on plugin channel; + void HandleMethodCall(const flutter::MethodCall<>& method_call, + std::unique_ptr> result); + + private: + // The provider for the root window to attach the dialog to. + FlutterRootWindowProvider get_root_window_; + + // The factory for creating dialog controller instances. + std::unique_ptr controller_factory_; +}; + +} // namespace file_selector_windows + +#endif // PACKAGES_FILE_SELECTOR_FILE_SELECTOR_WINDOWS_WINDOWS_FILE_SELECTOR_PLUGIN_H_ diff --git a/packages/file_selector/file_selector_windows/windows/file_selector_windows.cpp b/packages/file_selector/file_selector_windows/windows/file_selector_windows.cpp new file mode 100644 index 000000000000..e4d2c15fd89b --- /dev/null +++ b/packages/file_selector/file_selector_windows/windows/file_selector_windows.cpp @@ -0,0 +1,15 @@ +// 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. +#include "include/file_selector_windows/file_selector_windows.h" + +#include + +#include "file_selector_plugin.h" + +void FileSelectorWindowsRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar) { + file_selector_windows::FileSelectorPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarManager::GetInstance() + ->GetRegistrar(registrar)); +} diff --git a/packages/file_selector/file_selector_windows/windows/include/file_selector_windows/file_selector_windows.h b/packages/file_selector/file_selector_windows/windows/include/file_selector_windows/file_selector_windows.h new file mode 100644 index 000000000000..7ee6ed3d29ff --- /dev/null +++ b/packages/file_selector/file_selector_windows/windows/include/file_selector_windows/file_selector_windows.h @@ -0,0 +1,26 @@ +// 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. +#ifndef PACKAGES_FILE_SELECTOR_FILE_SELECTOR_WINDOWS_WINDOWS_INCLUDE_FILE_SELECTOR_WINDOWS_FILE_SELECTOR_WINDOWS_H_ +#define PACKAGES_FILE_SELECTOR_FILE_SELECTOR_WINDOWS_WINDOWS_INCLUDE_FILE_SELECTOR_WINDOWS_FILE_SELECTOR_WINDOWS_H_ + +#include + +#ifdef FLUTTER_PLUGIN_IMPL +#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport) +#else +#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport) +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +FLUTTER_PLUGIN_EXPORT void FileSelectorWindowsRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // PACKAGES_FILE_SELECTOR_FILE_SELECTOR_WINDOWS_WINDOWS_INCLUDE_FILE_SELECTOR_WINDOWS_FILE_SELECTOR_WINDOWS_H_ diff --git a/packages/file_selector/file_selector_windows/windows/string_utils.cpp b/packages/file_selector/file_selector_windows/windows/string_utils.cpp new file mode 100644 index 000000000000..933500f34445 --- /dev/null +++ b/packages/file_selector/file_selector_windows/windows/string_utils.cpp @@ -0,0 +1,60 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "string_utils.h" + +#include +#include + +#include + +namespace file_selector_windows { + +// Converts the given UTF-16 string to UTF-8. +std::string Utf8FromUtf16(const std::wstring& utf16_string) { + if (utf16_string.empty()) { + return std::string(); + } + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string.data(), + static_cast(utf16_string.length()), nullptr, 0, nullptr, nullptr); + if (target_length == 0) { + return std::string(); + } + std::string utf8_string; + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string.data(), + static_cast(utf16_string.length()), utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} + +// Converts the given UTF-8 string to UTF-16. +std::wstring Utf16FromUtf8(const std::string& utf8_string) { + if (utf8_string.empty()) { + return std::wstring(); + } + int target_length = + ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8_string.data(), + static_cast(utf8_string.length()), nullptr, 0); + if (target_length == 0) { + return std::wstring(); + } + std::wstring utf16_string; + utf16_string.resize(target_length); + int converted_length = + ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8_string.data(), + static_cast(utf8_string.length()), + utf16_string.data(), target_length); + if (converted_length == 0) { + return std::wstring(); + } + return utf16_string; +} + +} // namespace file_selector_windows diff --git a/packages/file_selector/file_selector_windows/windows/string_utils.h b/packages/file_selector/file_selector_windows/windows/string_utils.h new file mode 100644 index 000000000000..74c7d4f93934 --- /dev/null +++ b/packages/file_selector/file_selector_windows/windows/string_utils.h @@ -0,0 +1,21 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#ifndef PACKAGES_FILE_SELECTOR_FILE_SELECTOR_WINDOWS_WINDOWS_STRING_UTILS_H_ +#define PACKAGES_FILE_SELECTOR_FILE_SELECTOR_WINDOWS_WINDOWS_STRING_UTILS_H_ + +#include + +#include + +namespace file_selector_windows { + +// Converts the given UTF-16 string to UTF-8. +std::string Utf8FromUtf16(const std::wstring& utf16_string); + +// Converts the given UTF-8 string to UTF-16. +std::wstring Utf16FromUtf8(const std::string& utf8_string); + +} // namespace file_selector_windows + +#endif // PACKAGES_FILE_SELECTOR_FILE_SELECTOR_WINDOWS_WINDOWS_STRING_UTILS_H_ diff --git a/packages/file_selector/file_selector_windows/windows/test/file_selector_plugin_test.cpp b/packages/file_selector/file_selector_windows/windows/test/file_selector_plugin_test.cpp new file mode 100644 index 000000000000..20e1bba2794f --- /dev/null +++ b/packages/file_selector/file_selector_windows/windows/test/file_selector_plugin_test.cpp @@ -0,0 +1,471 @@ +// 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. +#include "file_selector_plugin.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "file_dialog_controller.h" +#include "string_utils.h" +#include "test/test_file_dialog_controller.h" +#include "test/test_utils.h" + +namespace file_selector_windows { +namespace test { + +namespace { + +using flutter::EncodableList; +using flutter::EncodableMap; +using flutter::EncodableValue; +using flutter::MethodCall; +using ::testing::DoAll; +using ::testing::Pointee; +using ::testing::Return; +using ::testing::SetArgPointee; + +class MockMethodResult : public flutter::MethodResult<> { + public: + MOCK_METHOD(void, SuccessInternal, (const EncodableValue* result), + (override)); + MOCK_METHOD(void, ErrorInternal, + (const std::string& error_code, const std::string& error_message, + const EncodableValue* details), + (override)); + MOCK_METHOD(void, NotImplementedInternal, (), (override)); +}; + +} // namespace + +TEST(FileSelectorPlugin, TestOpenSimple) { + const HWND fake_window = reinterpret_cast(1337); + ScopedTestShellItem fake_selected_file; + IShellItemArrayPtr fake_result_array; + ::SHCreateShellItemArrayFromShellItem(fake_selected_file.file(), + IID_PPV_ARGS(&fake_result_array)); + + std::unique_ptr result = + std::make_unique(); + + bool shown = false; + MockShow show_validator = [&shown, fake_result_array, fake_window]( + const TestFileDialogController& dialog, + HWND parent) { + shown = true; + EXPECT_EQ(parent, fake_window); + + // Validate options. + FILEOPENDIALOGOPTIONS options; + dialog.GetOptions(&options); + EXPECT_EQ(options & FOS_ALLOWMULTISELECT, 0U); + EXPECT_EQ(options & FOS_PICKFOLDERS, 0U); + + return MockShowResult(fake_result_array); + }; + EncodableValue expected_paths(EncodableList({ + EncodableValue(Utf8FromUtf16(fake_selected_file.path())), + })); + // Expect the mock path. + EXPECT_CALL(*result, SuccessInternal(Pointee(expected_paths))); + + FileSelectorPlugin plugin( + [fake_window] { return fake_window; }, + std::make_unique(show_validator)); + plugin.HandleMethodCall( + MethodCall("openFile", std::make_unique(EncodableMap())), + std::move(result)); + + EXPECT_TRUE(shown); +} + +TEST(FileSelectorPlugin, TestOpenWithArguments) { + const HWND fake_window = reinterpret_cast(1337); + ScopedTestShellItem fake_selected_file; + IShellItemArrayPtr fake_result_array; + ::SHCreateShellItemArrayFromShellItem(fake_selected_file.file(), + IID_PPV_ARGS(&fake_result_array)); + + std::unique_ptr result = + std::make_unique(); + + bool shown = false; + MockShow show_validator = [&shown, fake_result_array, fake_window]( + const TestFileDialogController& dialog, + HWND parent) { + shown = true; + EXPECT_EQ(parent, fake_window); + + // Validate arguments. + EXPECT_EQ(dialog.GetDefaultFolderPath(), L"C:\\Program Files"); + EXPECT_EQ(dialog.GetFileName(), L"a name"); + EXPECT_EQ(dialog.GetOkButtonLabel(), L"Open it!"); + + return MockShowResult(fake_result_array); + }; + EncodableValue expected_paths(EncodableList({ + EncodableValue(Utf8FromUtf16(fake_selected_file.path())), + })); + // Expect the mock path. + EXPECT_CALL(*result, SuccessInternal(Pointee(expected_paths))); + + FileSelectorPlugin plugin( + [fake_window] { return fake_window; }, + std::make_unique(show_validator)); + plugin.HandleMethodCall( + MethodCall( + "openFile", + std::make_unique(EncodableMap({ + // This directory must exist. + {EncodableValue("initialDirectory"), + EncodableValue("C:\\Program Files")}, + {EncodableValue("suggestedName"), EncodableValue("a name")}, + {EncodableValue("confirmButtonText"), EncodableValue("Open it!")}, + }))), + std::move(result)); + + EXPECT_TRUE(shown); +} + +TEST(FileSelectorPlugin, TestOpenMultiple) { + const HWND fake_window = reinterpret_cast(1337); + ScopedTestFileIdList fake_selected_file_1; + ScopedTestFileIdList fake_selected_file_2; + LPCITEMIDLIST fake_selected_files[] = { + fake_selected_file_1.file(), + fake_selected_file_2.file(), + }; + IShellItemArrayPtr fake_result_array; + ::SHCreateShellItemArrayFromIDLists(2, fake_selected_files, + &fake_result_array); + + std::unique_ptr result = + std::make_unique(); + + bool shown = false; + MockShow show_validator = [&shown, fake_result_array, fake_window]( + const TestFileDialogController& dialog, + HWND parent) { + shown = true; + EXPECT_EQ(parent, fake_window); + + // Validate options. + FILEOPENDIALOGOPTIONS options; + dialog.GetOptions(&options); + EXPECT_NE(options & FOS_ALLOWMULTISELECT, 0U); + EXPECT_EQ(options & FOS_PICKFOLDERS, 0U); + + return MockShowResult(fake_result_array); + }; + EncodableValue expected_paths(EncodableList({ + EncodableValue(Utf8FromUtf16(fake_selected_file_1.path())), + EncodableValue(Utf8FromUtf16(fake_selected_file_2.path())), + })); + // Expect the mock path. + EXPECT_CALL(*result, SuccessInternal(Pointee(expected_paths))); + + FileSelectorPlugin plugin( + [fake_window] { return fake_window; }, + std::make_unique(show_validator)); + plugin.HandleMethodCall( + MethodCall("openFile", + std::make_unique(EncodableMap({ + {EncodableValue("multiple"), EncodableValue(true)}, + }))), + std::move(result)); + + EXPECT_TRUE(shown); +} + +TEST(FileSelectorPlugin, TestOpenWithFilter) { + const HWND fake_window = reinterpret_cast(1337); + ScopedTestShellItem fake_selected_file; + IShellItemArrayPtr fake_result_array; + ::SHCreateShellItemArrayFromShellItem(fake_selected_file.file(), + IID_PPV_ARGS(&fake_result_array)); + + std::unique_ptr result = + std::make_unique(); + + const EncodableValue text_group = EncodableValue(EncodableMap({ + {EncodableValue("label"), EncodableValue("Text")}, + {EncodableValue("extensions"), EncodableValue(EncodableList({ + EncodableValue("txt"), + EncodableValue("json"), + }))}, + })); + const EncodableValue image_group = EncodableValue(EncodableMap({ + {EncodableValue("label"), EncodableValue("Images")}, + {EncodableValue("extensions"), EncodableValue(EncodableList({ + EncodableValue("png"), + EncodableValue("gif"), + EncodableValue("jpeg"), + }))}, + })); + const EncodableValue any_group = EncodableValue(EncodableMap({ + {EncodableValue("label"), EncodableValue("Any")}, + })); + + bool shown = false; + MockShow show_validator = [&shown, fake_result_array, fake_window]( + const TestFileDialogController& dialog, + HWND parent) { + shown = true; + EXPECT_EQ(parent, fake_window); + + // Validate filter. + const std::vector& filters = dialog.GetFileTypes(); + EXPECT_EQ(filters.size(), 3U); + if (filters.size() == 3U) { + EXPECT_EQ(filters[0].name, L"Text"); + EXPECT_EQ(filters[0].spec, L"*.txt;*.json"); + EXPECT_EQ(filters[1].name, L"Images"); + EXPECT_EQ(filters[1].spec, L"*.png;*.gif;*.jpeg"); + EXPECT_EQ(filters[2].name, L"Any"); + EXPECT_EQ(filters[2].spec, L"*.*"); + } + + return MockShowResult(fake_result_array); + }; + EncodableValue expected_paths(EncodableList({ + EncodableValue(Utf8FromUtf16(fake_selected_file.path())), + })); + // Expect the mock path. + EXPECT_CALL(*result, SuccessInternal(Pointee(expected_paths))); + + FileSelectorPlugin plugin( + [fake_window] { return fake_window; }, + std::make_unique(show_validator)); + plugin.HandleMethodCall( + MethodCall("openFile", std::make_unique(EncodableMap({ + {EncodableValue("acceptedTypeGroups"), + EncodableValue(EncodableList({ + text_group, + image_group, + any_group, + }))}, + }))), + std::move(result)); + + EXPECT_TRUE(shown); +} + +TEST(FileSelectorPlugin, TestOpenCancel) { + const HWND fake_window = reinterpret_cast(1337); + + std::unique_ptr result = + std::make_unique(); + + bool shown = false; + MockShow show_validator = [&shown, fake_window]( + const TestFileDialogController& dialog, + HWND parent) { + shown = true; + return MockShowResult(); + }; + // Cancel should return a null for the paths. + EncodableValue expected_paths; + // Expect the mock path. + EXPECT_CALL(*result, SuccessInternal(Pointee(expected_paths))); + + FileSelectorPlugin plugin( + [fake_window] { return fake_window; }, + std::make_unique(show_validator)); + plugin.HandleMethodCall( + MethodCall("openFile", std::make_unique(EncodableMap())), + std::move(result)); + + EXPECT_TRUE(shown); +} + +TEST(FileSelectorPlugin, TestSaveSimple) { + const HWND fake_window = reinterpret_cast(1337); + ScopedTestShellItem fake_selected_file; + + std::unique_ptr result = + std::make_unique(); + + bool shown = false; + MockShow show_validator = + [&shown, fake_result = fake_selected_file.file(), fake_window]( + const TestFileDialogController& dialog, HWND parent) { + shown = true; + EXPECT_EQ(parent, fake_window); + + // Validate options. + FILEOPENDIALOGOPTIONS options; + dialog.GetOptions(&options); + EXPECT_EQ(options & FOS_ALLOWMULTISELECT, 0U); + EXPECT_EQ(options & FOS_PICKFOLDERS, 0U); + + return MockShowResult(fake_result); + }; + EncodableValue expected_path(Utf8FromUtf16(fake_selected_file.path())); + // Expect the mock path. + EXPECT_CALL(*result, SuccessInternal(Pointee(expected_path))); + + FileSelectorPlugin plugin( + [fake_window] { return fake_window; }, + std::make_unique(show_validator)); + plugin.HandleMethodCall( + MethodCall("getSavePath", + std::make_unique(EncodableMap())), + std::move(result)); + + EXPECT_TRUE(shown); +} + +TEST(FileSelectorPlugin, TestSaveWithArguments) { + const HWND fake_window = reinterpret_cast(1337); + ScopedTestShellItem fake_selected_file; + + std::unique_ptr result = + std::make_unique(); + + bool shown = false; + MockShow show_validator = + [&shown, fake_result = fake_selected_file.file(), fake_window]( + const TestFileDialogController& dialog, HWND parent) { + shown = true; + EXPECT_EQ(parent, fake_window); + + // Validate arguments. + EXPECT_EQ(dialog.GetDefaultFolderPath(), L"C:\\Program Files"); + EXPECT_EQ(dialog.GetOkButtonLabel(), L"Save it!"); + + return MockShowResult(fake_result); + }; + EncodableValue expected_path(Utf8FromUtf16(fake_selected_file.path())); + // Expect the mock path. + EXPECT_CALL(*result, SuccessInternal(Pointee(expected_path))); + + FileSelectorPlugin plugin( + [fake_window] { return fake_window; }, + std::make_unique(show_validator)); + plugin.HandleMethodCall( + MethodCall( + "getSavePath", + std::make_unique(EncodableMap({ + // This directory must exist. + {EncodableValue("initialDirectory"), + EncodableValue("C:\\Program Files")}, + {EncodableValue("confirmButtonText"), EncodableValue("Save it!")}, + }))), + std::move(result)); + + EXPECT_TRUE(shown); +} + +TEST(FileSelectorPlugin, TestSaveCancel) { + const HWND fake_window = reinterpret_cast(1337); + + std::unique_ptr result = + std::make_unique(); + + bool shown = false; + MockShow show_validator = [&shown, fake_window]( + const TestFileDialogController& dialog, + HWND parent) { + shown = true; + return MockShowResult(); + }; + // Cancel should return a null for the path. + EncodableValue expected_path; + // Expect the mock path. + EXPECT_CALL(*result, SuccessInternal(Pointee(expected_path))); + + FileSelectorPlugin plugin( + [fake_window] { return fake_window; }, + std::make_unique(show_validator)); + plugin.HandleMethodCall( + MethodCall("getSavePath", + std::make_unique(EncodableMap())), + std::move(result)); + + EXPECT_TRUE(shown); +} + +TEST(FileSelectorPlugin, TestGetDirectorySimple) { + const HWND fake_window = reinterpret_cast(1337); + IShellItemPtr fake_selected_directory; + // This must be a directory that actually exists. + ::SHCreateItemFromParsingName(L"C:\\Program Files", nullptr, + IID_PPV_ARGS(&fake_selected_directory)); + IShellItemArrayPtr fake_result_array; + ::SHCreateShellItemArrayFromShellItem(fake_selected_directory, + IID_PPV_ARGS(&fake_result_array)); + + std::unique_ptr result = + std::make_unique(); + + bool shown = false; + MockShow show_validator = [&shown, fake_result_array, fake_window]( + const TestFileDialogController& dialog, + HWND parent) { + shown = true; + EXPECT_EQ(parent, fake_window); + + // Validate options. + FILEOPENDIALOGOPTIONS options; + dialog.GetOptions(&options); + EXPECT_EQ(options & FOS_ALLOWMULTISELECT, 0U); + EXPECT_NE(options & FOS_PICKFOLDERS, 0U); + + return MockShowResult(fake_result_array); + }; + EncodableValue expected_path("C:\\Program Files"); + // Expect the mock path. + EXPECT_CALL(*result, SuccessInternal(Pointee(expected_path))); + + FileSelectorPlugin plugin( + [fake_window] { return fake_window; }, + std::make_unique(show_validator)); + plugin.HandleMethodCall( + MethodCall("getDirectoryPath", + std::make_unique(EncodableMap())), + std::move(result)); + + EXPECT_TRUE(shown); +} + +TEST(FileSelectorPlugin, TestGetDirectoryCancel) { + const HWND fake_window = reinterpret_cast(1337); + + std::unique_ptr result = + std::make_unique(); + + bool shown = false; + MockShow show_validator = [&shown, fake_window]( + const TestFileDialogController& dialog, + HWND parent) { + shown = true; + return MockShowResult(); + }; + // Cancel should return a null for the path. + EncodableValue expected_path; + // Expect the mock path. + EXPECT_CALL(*result, SuccessInternal(Pointee(expected_path))); + + FileSelectorPlugin plugin( + [fake_window] { return fake_window; }, + std::make_unique(show_validator)); + plugin.HandleMethodCall( + MethodCall("getDirectoryPath", + std::make_unique(EncodableMap())), + std::move(result)); + + EXPECT_TRUE(shown); +} + +} // namespace test +} // namespace file_selector_windows diff --git a/packages/file_selector/file_selector_windows/windows/test/test_file_dialog_controller.cpp b/packages/file_selector/file_selector_windows/windows/test/test_file_dialog_controller.cpp new file mode 100644 index 000000000000..a98b686ddd6e --- /dev/null +++ b/packages/file_selector/file_selector_windows/windows/test/test_file_dialog_controller.cpp @@ -0,0 +1,106 @@ +// 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. +#include "test/test_file_dialog_controller.h" + +#include + +#include +#include +#include + +namespace file_selector_windows { +namespace test { + +TestFileDialogController::TestFileDialogController(IFileDialog* dialog, + MockShow mock_show) + : dialog_(dialog), + mock_show_(std::move(mock_show)), + FileDialogController(dialog) {} + +TestFileDialogController::~TestFileDialogController() {} + +HRESULT TestFileDialogController::SetFileTypes(UINT count, + COMDLG_FILTERSPEC* filters) { + filter_groups_.clear(); + for (unsigned int i = 0; i < count; ++i) { + filter_groups_.push_back( + DialogFilter(filters[i].pszName, filters[i].pszSpec)); + } + return FileDialogController::SetFileTypes(count, filters); +} + +HRESULT TestFileDialogController::SetOkButtonLabel(const wchar_t* text) { + ok_button_label_ = text; + return FileDialogController::SetOkButtonLabel(text); +} + +HRESULT TestFileDialogController::Show(HWND parent) { + mock_result_ = mock_show_(*this, parent); + if (std::holds_alternative(mock_result_)) { + return HRESULT_FROM_WIN32(ERROR_CANCELLED); + } + return S_OK; +} + +HRESULT TestFileDialogController::GetResult(IShellItem** out_item) const { + *out_item = std::get(mock_result_); + (*out_item)->AddRef(); + return S_OK; +} + +HRESULT TestFileDialogController::GetResults( + IShellItemArray** out_items) const { + *out_items = std::get(mock_result_); + (*out_items)->AddRef(); + return S_OK; +} + +std::wstring TestFileDialogController::GetDefaultFolderPath() const { + IShellItemPtr item; + if (!SUCCEEDED(dialog_->GetFolder(&item))) { + return L""; + } + + wchar_t* path_chars = nullptr; + if (!SUCCEEDED(item->GetDisplayName(SIGDN_FILESYSPATH, &path_chars))) { + return L""; + } + std::wstring path(path_chars); + ::CoTaskMemFree(path_chars); + return path; +} + +std::wstring TestFileDialogController::GetFileName() const { + wchar_t* name_chars = nullptr; + if (!SUCCEEDED(dialog_->GetFileName(&name_chars))) { + return L""; + } + std::wstring name(name_chars); + ::CoTaskMemFree(name_chars); + return name; +} + +const std::vector& TestFileDialogController::GetFileTypes() + const { + return filter_groups_; +} + +std::wstring TestFileDialogController::GetOkButtonLabel() const { + return ok_button_label_; +} + +// ---------------------------------------- + +TestFileDialogControllerFactory::TestFileDialogControllerFactory( + MockShow mock_show) + : mock_show_(std::move(mock_show)) {} +TestFileDialogControllerFactory::~TestFileDialogControllerFactory() {} + +std::unique_ptr +TestFileDialogControllerFactory::CreateController(IFileDialog* dialog) const { + return std::make_unique(dialog, mock_show_); +} + +} // namespace test +} // namespace file_selector_windows diff --git a/packages/file_selector/file_selector_windows/windows/test/test_file_dialog_controller.h b/packages/file_selector/file_selector_windows/windows/test/test_file_dialog_controller.h new file mode 100644 index 000000000000..2e7292bad4b2 --- /dev/null +++ b/packages/file_selector/file_selector_windows/windows/test/test_file_dialog_controller.h @@ -0,0 +1,101 @@ +// 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. +#ifndef PACKAGES_FILE_SELECTOR_FILE_SELECTOR_WINDOWS_WINDOWS_TEST_TEST_FILE_DIALOG_CONTROLLER_H_ +#define PACKAGES_FILE_SELECTOR_FILE_SELECTOR_WINDOWS_WINDOWS_TEST_TEST_FILE_DIALOG_CONTROLLER_H_ + +#include +#include +#include + +#include +#include +#include +#include + +#include "file_dialog_controller.h" +#include "test/test_utils.h" + +_COM_SMARTPTR_TYPEDEF(IFileDialog, IID_IFileDialog); + +namespace file_selector_windows { +namespace test { + +class TestFileDialogController; + +// A value to use for GetResult(s) in TestFileDialogController. The type depends +// on whether the dialog is an open or save dialog. +using MockShowResult = + std::variant; +// Called for TestFileDialogController::Show, to do validation and provide a +// mock return value for GetResult(s). +using MockShow = + std::function; + +// A C++-friendly version of a COMDLG_FILTERSPEC. +struct DialogFilter { + std::wstring name; + std::wstring spec; + + DialogFilter(const wchar_t* name, const wchar_t* spec) + : name(name), spec(spec) {} +}; + +// An extension of the normal file dialog controller that: +// - Allows for inspection of set values. +// - Allows faking the 'Show' interaction, providing tests an opportunity to +// validate the dialog settings and provide a return value, via MockShow. +class TestFileDialogController : public FileDialogController { + public: + TestFileDialogController(IFileDialog* dialog, MockShow mock_show); + ~TestFileDialogController(); + + // FileDialogController: + HRESULT SetFileTypes(UINT count, COMDLG_FILTERSPEC* filters) override; + HRESULT SetOkButtonLabel(const wchar_t* text) override; + HRESULT Show(HWND parent) override; + HRESULT GetResult(IShellItem** out_item) const override; + HRESULT GetResults(IShellItemArray** out_items) const override; + + // Accessors for validating IFileDialogController setter calls. + std::wstring GetDefaultFolderPath() const; + std::wstring GetFileName() const; + const std::vector& GetFileTypes() const; + std::wstring GetOkButtonLabel() const; + + private: + IFileDialogPtr dialog_; + MockShow mock_show_; + MockShowResult mock_result_; + + // The last set values, for IFileDialog properties that have setters but no + // corresponding getters. + std::wstring ok_button_label_; + std::vector filter_groups_; +}; + +// A controller factory that vends TestFileDialogController instances. +class TestFileDialogControllerFactory : public FileDialogControllerFactory { + public: + // Creates a factory whose instances use mock_show for the Show callback. + TestFileDialogControllerFactory(MockShow mock_show); + virtual ~TestFileDialogControllerFactory(); + + // Disallow copy and assign. + TestFileDialogControllerFactory(const TestFileDialogControllerFactory&) = + delete; + TestFileDialogControllerFactory& operator=( + const TestFileDialogControllerFactory&) = delete; + + // FileDialogControllerFactory: + std::unique_ptr CreateController( + IFileDialog* dialog) const override; + + private: + MockShow mock_show_; +}; + +} // namespace test +} // namespace file_selector_windows + +#endif // PACKAGES_FILE_SELECTOR_FILE_SELECTOR_WINDOWS_WINDOWS_TEST_TEST_FILE_DIALOG_CONTROLLER_H_ diff --git a/packages/file_selector/file_selector_windows/windows/test/test_main.cpp b/packages/file_selector/file_selector_windows/windows/test/test_main.cpp new file mode 100644 index 000000000000..5a49b52c1c76 --- /dev/null +++ b/packages/file_selector/file_selector_windows/windows/test/test_main.cpp @@ -0,0 +1,16 @@ +// 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. +#include +#include + +int main(int argc, char** argv) { + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + testing::InitGoogleTest(&argc, argv); + int exit_code = RUN_ALL_TESTS(); + + ::CoUninitialize(); + + return exit_code; +} diff --git a/packages/file_selector/file_selector_windows/windows/test/test_utils.cpp b/packages/file_selector/file_selector_windows/windows/test/test_utils.cpp new file mode 100644 index 000000000000..3e3ab98a734a --- /dev/null +++ b/packages/file_selector/file_selector_windows/windows/test/test_utils.cpp @@ -0,0 +1,45 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#include "test/test_utils.h" + +#include +#include + +#include + +namespace file_selector_windows { +namespace test { + +namespace { + +// Creates a temp file and returns its path. +std::wstring CreateTempFile() { + wchar_t temp_dir[MAX_PATH]; + wchar_t temp_file[MAX_PATH]; + wchar_t long_path[MAX_PATH]; + ::GetTempPath(MAX_PATH, temp_dir); + ::GetTempFileName(temp_dir, L"test", 0, temp_file); + // Convert to long form to match what IShellItem queries will return. + ::GetLongPathName(temp_file, long_path, MAX_PATH); + return long_path; +} + +} // namespace + +ScopedTestShellItem::ScopedTestShellItem() { + path_ = CreateTempFile(); + ::SHCreateItemFromParsingName(path_.c_str(), nullptr, IID_PPV_ARGS(&item_)); +} + +ScopedTestShellItem::~ScopedTestShellItem() { ::DeleteFile(path_.c_str()); } + +ScopedTestFileIdList::ScopedTestFileIdList() { + path_ = CreateTempFile(); + item_ = ItemIdListPtr(::ILCreateFromPath(path_.c_str())); +} + +ScopedTestFileIdList::~ScopedTestFileIdList() { ::DeleteFile(path_.c_str()); } + +} // namespace test +} // namespace file_selector_windows diff --git a/packages/file_selector/file_selector_windows/windows/test/test_utils.h b/packages/file_selector/file_selector_windows/windows/test/test_utils.h new file mode 100644 index 000000000000..34106c50092f --- /dev/null +++ b/packages/file_selector/file_selector_windows/windows/test/test_utils.h @@ -0,0 +1,91 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#ifndef PACKAGES_FILE_SELECTOR_FILE_SELECTOR_WINDOWS_WINDOWS_TEST_TEST_UTILS_H_ +#define PACKAGES_FILE_SELECTOR_FILE_SELECTOR_WINDOWS_WINDOWS_TEST_TEST_UTILS_H_ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "file_dialog_controller.h" + +_COM_SMARTPTR_TYPEDEF(IShellItem, IID_IShellItem); +_COM_SMARTPTR_TYPEDEF(IShellItemArray, IID_IShellItemArray); + +namespace file_selector_windows { +namespace test { + +// Creates a temp file, managed as an IShellItem, which will be deleted when +// the instance goes out of scope. +// +// This creates a file on the filesystem since creating IShellItem instances for +// files that don't exist is non-trivial. +class ScopedTestShellItem { + public: + ScopedTestShellItem(); + ~ScopedTestShellItem(); + + // Disallow copy and assign. + ScopedTestShellItem(const ScopedTestShellItem&) = delete; + ScopedTestShellItem& operator=(const ScopedTestShellItem&) = delete; + + // Returns the file's IShellItem reference. + IShellItemPtr file() { return item_; } + + // Returns the file's path. + const std::wstring& path() { return path_; } + + private: + IShellItemPtr item_; + std::wstring path_; +}; + +// Creates a temp file, managed as an ITEMIDLIST, which will be deleted when +// the instance goes out of scope. +// +// This creates a file on the filesystem since creating IShellItem instances for +// files that don't exist is non-trivial, and this is intended for use in +// creating IShellItemArray instances. +class ScopedTestFileIdList { + public: + ScopedTestFileIdList(); + ~ScopedTestFileIdList(); + + // Disallow copy and assign. + ScopedTestFileIdList(const ScopedTestFileIdList&) = delete; + ScopedTestFileIdList& operator=(const ScopedTestFileIdList&) = delete; + + // Returns the file's ITEMIDLIST reference. + PIDLIST_ABSOLUTE file() { return item_.get(); } + + // Returns the file's path. + const std::wstring& path() { return path_; } + + private: + // Smart pointer for managing ITEMIDLIST instances. + struct ItemIdListDeleter { + void operator()(LPITEMIDLIST item) { + if (item) { + ::ILFree(item); + } + } + }; + using ItemIdListPtr = std::unique_ptr, + ItemIdListDeleter>; + + ItemIdListPtr item_; + std::wstring path_; +}; + +} // namespace test +} // namespace file_selector_windows + +#endif // PACKAGES_FILE_SELECTOR_FILE_SELECTOR_WINDOWS_WINDOWS_TEST_TEST_UTILS_H_ diff --git a/script/configs/exclude_integration_win32.yaml b/script/configs/exclude_integration_win32.yaml new file mode 100644 index 000000000000..68aac9df1c4b --- /dev/null +++ b/script/configs/exclude_integration_win32.yaml @@ -0,0 +1,2 @@ +# Can't use Flutter integration tests due to native modal UI. +- file_selector_windows From bd7dfb78d8f94cc02bd02b58e253a96f5a929963 Mon Sep 17 00:00:00 2001 From: Yaroslav Pronin Date: Wed, 26 Jan 2022 07:15:23 +0300 Subject: [PATCH 116/600] [camera_platform_interface] Fix asynchronous exceptions handling of the `initializeCamera` method (#4660) --- .../camera_platform_interface/CHANGELOG.md | 4 +++ .../method_channel/method_channel_camera.dart | 10 ++++++ .../camera_platform_interface/pubspec.yaml | 2 +- .../method_channel_camera_test.dart | 32 +++++++++++++++++++ 4 files changed, 47 insertions(+), 1 deletion(-) diff --git a/packages/camera/camera_platform_interface/CHANGELOG.md b/packages/camera/camera_platform_interface/CHANGELOG.md index 35a39e1a0568..6ed662b2afa0 100644 --- a/packages/camera/camera_platform_interface/CHANGELOG.md +++ b/packages/camera/camera_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.5 + +* Fixes asynchronous exceptions handling of the `initializeCamera` method. + ## 2.1.4 * Removes dependency on `meta`. diff --git a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart index ab6676939aaa..ec84c204b2c6 100644 --- a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart +++ b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart @@ -126,6 +126,16 @@ class MethodChannelCamera extends CameraPlatform { 'cameraId': cameraId, 'imageFormatGroup': imageFormatGroup.name(), }, + ).catchError( + (Object error, StackTrace stackTrace) { + if (error is! PlatformException) { + throw error; + } + _completer.completeError( + CameraException(error.code, error.message), + stackTrace, + ); + }, ); return _completer.future; diff --git a/packages/camera/camera_platform_interface/pubspec.yaml b/packages/camera/camera_platform_interface/pubspec.yaml index bd6aa5a1cda2..b28008d8d58e 100644 --- a/packages/camera/camera_platform_interface/pubspec.yaml +++ b/packages/camera/camera_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.1.4 +version: 2.1.5 environment: sdk: '>=2.12.0 <3.0.0' diff --git a/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart b/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart index 012b1907aff5..27fe7c6b7166 100644 --- a/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart +++ b/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart @@ -126,6 +126,38 @@ void main() { ); }); + test( + 'Should throw CameraException when initialize throws a PlatformException', + () { + // Arrange + MethodChannelMock( + channelName: 'plugins.flutter.io/camera', + methods: { + 'initialize': PlatformException( + code: 'TESTING_ERROR_CODE', + message: 'Mock error message used during testing.', + ) + }, + ); + final MethodChannelCamera camera = MethodChannelCamera(); + + // Act + expect( + () => camera.initializeCamera(0), + throwsA( + isA() + .having((CameraException e) => e.code, 'code', + 'TESTING_ERROR_CODE') + .having( + (CameraException e) => e.description, + 'description', + 'Mock error message used during testing.', + ), + ), + ); + }, + ); + test('Should send initialization data', () async { // Arrange final MethodChannelMock cameraMockChannel = MethodChannelMock( From 36d592f9f31acfeea5cf9c467a643a717d975417 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 26 Jan 2022 16:30:12 -0500 Subject: [PATCH 117/600] [file_selector] Endorse Windows (#4696) --- .../file_selector/file_selector/CHANGELOG.md | 4 + .../file_selector/example/windows/.gitignore | 17 ++ .../example/windows/CMakeLists.txt | 101 ++++++++ .../example/windows/flutter/CMakeLists.txt | 104 ++++++++ .../windows/flutter/generated_plugins.cmake | 16 ++ .../example/windows/runner/CMakeLists.txt | 32 +++ .../example/windows/runner/Runner.rc | 121 +++++++++ .../example/windows/runner/flutter_window.cpp | 65 +++++ .../example/windows/runner/flutter_window.h | 37 +++ .../example/windows/runner/main.cpp | 46 ++++ .../example/windows/runner/resource.h | 16 ++ .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../windows/runner/runner.exe.manifest | 20 ++ .../example/windows/runner/utils.cpp | 67 +++++ .../example/windows/runner/utils.h | 23 ++ .../example/windows/runner/win32_window.cpp | 241 ++++++++++++++++++ .../example/windows/runner/win32_window.h | 99 +++++++ .../file_selector/file_selector/pubspec.yaml | 5 +- script/configs/exclude_integration_win32.yaml | 1 + 19 files changed, 1014 insertions(+), 1 deletion(-) create mode 100644 packages/file_selector/file_selector/example/windows/.gitignore create mode 100644 packages/file_selector/file_selector/example/windows/CMakeLists.txt create mode 100644 packages/file_selector/file_selector/example/windows/flutter/CMakeLists.txt create mode 100644 packages/file_selector/file_selector/example/windows/flutter/generated_plugins.cmake create mode 100644 packages/file_selector/file_selector/example/windows/runner/CMakeLists.txt create mode 100644 packages/file_selector/file_selector/example/windows/runner/Runner.rc create mode 100644 packages/file_selector/file_selector/example/windows/runner/flutter_window.cpp create mode 100644 packages/file_selector/file_selector/example/windows/runner/flutter_window.h create mode 100644 packages/file_selector/file_selector/example/windows/runner/main.cpp create mode 100644 packages/file_selector/file_selector/example/windows/runner/resource.h create mode 100644 packages/file_selector/file_selector/example/windows/runner/resources/app_icon.ico create mode 100644 packages/file_selector/file_selector/example/windows/runner/runner.exe.manifest create mode 100644 packages/file_selector/file_selector/example/windows/runner/utils.cpp create mode 100644 packages/file_selector/file_selector/example/windows/runner/utils.h create mode 100644 packages/file_selector/file_selector/example/windows/runner/win32_window.cpp create mode 100644 packages/file_selector/file_selector/example/windows/runner/win32_window.h diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index f34ed78d4e7f..65c41651935c 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.3 + +* Adds an endorsed Windows implementation. + ## 0.8.2+1 * Minor code cleanup for new analysis rules. diff --git a/packages/file_selector/file_selector/example/windows/.gitignore b/packages/file_selector/file_selector/example/windows/.gitignore new file mode 100644 index 000000000000..d492d0d98c8f --- /dev/null +++ b/packages/file_selector/file_selector/example/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/packages/file_selector/file_selector/example/windows/CMakeLists.txt b/packages/file_selector/file_selector/example/windows/CMakeLists.txt new file mode 100644 index 000000000000..c0270746b1b9 --- /dev/null +++ b/packages/file_selector/file_selector/example/windows/CMakeLists.txt @@ -0,0 +1,101 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(example LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "example") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/packages/file_selector/file_selector/example/windows/flutter/CMakeLists.txt b/packages/file_selector/file_selector/example/windows/flutter/CMakeLists.txt new file mode 100644 index 000000000000..930d2071a324 --- /dev/null +++ b/packages/file_selector/file_selector/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,104 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/packages/file_selector/file_selector/example/windows/flutter/generated_plugins.cmake b/packages/file_selector/file_selector/example/windows/flutter/generated_plugins.cmake new file mode 100644 index 000000000000..63eda9b7b59f --- /dev/null +++ b/packages/file_selector/file_selector/example/windows/flutter/generated_plugins.cmake @@ -0,0 +1,16 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + file_selector_windows +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) diff --git a/packages/file_selector/file_selector/example/windows/runner/CMakeLists.txt b/packages/file_selector/file_selector/example/windows/runner/CMakeLists.txt new file mode 100644 index 000000000000..b9e550fba8e1 --- /dev/null +++ b/packages/file_selector/file_selector/example/windows/runner/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/packages/file_selector/file_selector/example/windows/runner/Runner.rc b/packages/file_selector/file_selector/example/windows/runner/Runner.rc new file mode 100644 index 000000000000..5fdea291cf19 --- /dev/null +++ b/packages/file_selector/file_selector/example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#ifdef FLUTTER_BUILD_NUMBER +#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#else +#define VERSION_AS_NUMBER 1,0,0 +#endif + +#ifdef FLUTTER_BUILD_NAME +#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "example" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2022 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "example.exe" "\0" + VALUE "ProductName", "example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/packages/file_selector/file_selector/example/windows/runner/flutter_window.cpp b/packages/file_selector/file_selector/example/windows/runner/flutter_window.cpp new file mode 100644 index 000000000000..8254bd9ff3c1 --- /dev/null +++ b/packages/file_selector/file_selector/example/windows/runner/flutter_window.cpp @@ -0,0 +1,65 @@ +// 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. + +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/packages/file_selector/file_selector/example/windows/runner/flutter_window.h b/packages/file_selector/file_selector/example/windows/runner/flutter_window.h new file mode 100644 index 000000000000..f1fc669093d0 --- /dev/null +++ b/packages/file_selector/file_selector/example/windows/runner/flutter_window.h @@ -0,0 +1,37 @@ +// 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. + +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/packages/file_selector/file_selector/example/windows/runner/main.cpp b/packages/file_selector/file_selector/example/windows/runner/main.cpp new file mode 100644 index 000000000000..a890d8730772 --- /dev/null +++ b/packages/file_selector/file_selector/example/windows/runner/main.cpp @@ -0,0 +1,46 @@ +// 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. + +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/packages/file_selector/file_selector/example/windows/runner/resource.h b/packages/file_selector/file_selector/example/windows/runner/resource.h new file mode 100644 index 000000000000..d5d958dc4257 --- /dev/null +++ b/packages/file_selector/file_selector/example/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/packages/file_selector/file_selector/example/windows/runner/resources/app_icon.ico b/packages/file_selector/file_selector/example/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/packages/file_selector/file_selector/example/windows/runner/runner.exe.manifest b/packages/file_selector/file_selector/example/windows/runner/runner.exe.manifest new file mode 100644 index 000000000000..c977c4a42589 --- /dev/null +++ b/packages/file_selector/file_selector/example/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/packages/file_selector/file_selector/example/windows/runner/utils.cpp b/packages/file_selector/file_selector/example/windows/runner/utils.cpp new file mode 100644 index 000000000000..fb7e945a63b7 --- /dev/null +++ b/packages/file_selector/file_selector/example/windows/runner/utils.cpp @@ -0,0 +1,67 @@ +// 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. + +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE* unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = + ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, + nullptr, 0, nullptr, nullptr); + if (target_length == 0) { + return std::string(); + } + std::string utf8_string; + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/packages/file_selector/file_selector/example/windows/runner/utils.h b/packages/file_selector/file_selector/example/windows/runner/utils.h new file mode 100644 index 000000000000..bd81e1e02338 --- /dev/null +++ b/packages/file_selector/file_selector/example/windows/runner/utils.h @@ -0,0 +1,23 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/packages/file_selector/file_selector/example/windows/runner/win32_window.cpp b/packages/file_selector/file_selector/example/windows/runner/win32_window.cpp new file mode 100644 index 000000000000..85aa3614e8ad --- /dev/null +++ b/packages/file_selector/file_selector/example/windows/runner/win32_window.cpp @@ -0,0 +1,241 @@ +// 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. + +#include "win32_window.h" + +#include + +#include "resource.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + FreeLibrary(user32_module); + } +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { ++g_active_window_count; } + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::CreateAndShow(const std::wstring& title, const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { return window_handle_; } + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} diff --git a/packages/file_selector/file_selector/example/windows/runner/win32_window.h b/packages/file_selector/file_selector/example/windows/runner/win32_window.h new file mode 100644 index 000000000000..d2a730052223 --- /dev/null +++ b/packages/file_selector/file_selector/example/windows/runner/win32_window.h @@ -0,0 +1,99 @@ +// 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. + +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates and shows a win32 window with |title| and position and size using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring& title, const Point& origin, + const Size& size); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index e4857c2ddd6e..41ff35307867 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for opening and saving files, or selecting directories, using native file selection UI. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.2+1 +version: 0.8.3 environment: sdk: ">=2.12.0 <3.0.0" @@ -14,10 +14,13 @@ flutter: platforms: web: default_package: file_selector_web + windows: + default_package: file_selector_windows dependencies: file_selector_platform_interface: ^2.0.0 file_selector_web: ^0.8.1 + file_selector_windows: ^0.8.2 flutter: sdk: flutter diff --git a/script/configs/exclude_integration_win32.yaml b/script/configs/exclude_integration_win32.yaml index 68aac9df1c4b..4626fbd79ce7 100644 --- a/script/configs/exclude_integration_win32.yaml +++ b/script/configs/exclude_integration_win32.yaml @@ -1,2 +1,3 @@ # Can't use Flutter integration tests due to native modal UI. +- file_selector - file_selector_windows From 411f09e3d8b13cb51eb14d2e05bab8583622dcaa Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 26 Jan 2022 17:59:32 -0500 Subject: [PATCH 118/600] Enforce asterisk alignment for C++ and ObjC pointers (#4703) So far we've been using the default mode of prevailing-in-file, which means we aren't consistent within each language what mode we use. Now that clang-format can identify ObjC headers (which didn't used to be the case), we can enforce different styles for the two languages. This sets left-aligned for C++ to match the Flutter engine, and right-aligned for ObjC to match the prevaling Apple style. --- .clang-format | 8 + .../camera/camera/example/ios/Runner/main.m | 2 +- .../ThreadSafeFlutterResultTests.m | 40 +-- .../ios/Classes/FLTThreadSafeFlutterResult.h | 6 +- .../ios/Classes/FLTThreadSafeFlutterResult.m | 8 +- .../example/windows/runner/main.cpp | 2 +- .../example/ios/Runner/AppDelegate.m | 6 +- .../example/ios/Runner/main.m | 2 +- .../example/ios/RunnerTests/GoogleMapsTests.m | 2 +- .../ios/RunnerUITests/GoogleMapsUITests.m | 34 +-- .../FLTGoogleMapTileOverlayController.m | 82 +++---- .../ios/Classes/FLTGoogleMapsPlugin.m | 14 +- .../ios/Classes/GoogleMapCircleController.h | 26 +- .../ios/Classes/GoogleMapCircleController.m | 96 ++++---- .../ios/Classes/GoogleMapController.m | 210 ++++++++-------- .../ios/Classes/GoogleMapMarkerController.h | 38 +-- .../ios/Classes/GoogleMapMarkerController.m | 160 ++++++------ .../ios/Classes/GoogleMapPolygonController.h | 32 +-- .../ios/Classes/GoogleMapPolygonController.m | 118 ++++----- .../ios/Classes/GoogleMapPolylineController.h | 28 +-- .../ios/Classes/GoogleMapPolylineController.m | 102 ++++---- .../ios/Classes/JsonConversions.h | 20 +- .../ios/Classes/JsonConversions.m | 32 +-- .../google_sign_in/example/ios/Runner/main.m | 2 +- .../ios/RunnerUITests/GoogleSignInUITests.m | 10 +- .../image_picker/example/ios/Runner/main.m | 2 +- .../ios/RunnerTests/ImagePickerTestImages.m | 24 +- .../ImagePickerFromGalleryUITests.m | 38 +-- .../ImagePickerFromLimitedGalleryUITests.m | 28 +-- .../in_app_purchase/example/ios/Runner/main.m | 2 +- .../example/ios/Runner/main.m | 2 +- .../RunnerTests/InAppPurchasePluginTests.m | 144 +++++------ .../ios/RunnerTests/IosPlatformImagesTests.m | 2 +- .../ios/Classes/IosPlatformImagesPlugin.m | 20 +- .../ios/Classes/UIImage+ios_platform_images.h | 2 +- .../ios/Classes/UIImage+ios_platform_images.m | 12 +- packages/local_auth/example/ios/Runner/main.m | 2 +- .../ios/RunnerTests/FLTLocalAuthPluginTests.m | 50 ++-- .../path_provider/example/ios/Runner/main.m | 2 +- .../ios/RunnerTests/PathProviderTests.m | 2 +- .../example/windows/runner/main.cpp | 2 +- .../example/windows/runner/utils.cpp | 2 +- .../example/ios/Runner/main.m | 2 +- .../ios/RunnerTests/PathProviderTests.m | 2 +- .../ios/Classes/FLTPathProviderPlugin.m | 26 +- .../example/windows/runner/main.cpp | 2 +- .../example/windows/runner/utils.cpp | 2 +- .../quick_actions/example/ios/Runner/main.m | 2 +- .../example/ios/RunnerTests/RunnerTests.m | 2 +- .../example/ios/Runner/main.m | 2 +- .../example/windows/runner/main.cpp | 2 +- .../example/windows/runner/utils.cpp | 2 +- .../example/ios/Runner/main.m | 2 +- .../ios/RunnerTests/SharedPreferencesTests.m | 2 +- .../example/windows/runner/main.cpp | 2 +- .../example/windows/runner/utils.cpp | 2 +- .../url_launcher/example/ios/Runner/main.m | 2 +- .../example/windows/runner/main.cpp | 2 +- .../example/windows/runner/utils.cpp | 2 +- .../example/ios/Runner/main.m | 2 +- .../ios/RunnerTests/URLLauncherTests.m | 2 +- .../ios/RunnerUITests/URLLauncherUITests.m | 12 +- .../example/windows/runner/main.cpp | 2 +- .../example/windows/runner/utils.cpp | 2 +- .../video_player/example/ios/Runner/main.m | 2 +- .../ios/RunnerUITests/VideoPlayerUITests.m | 26 +- .../ios/Classes/FLTVideoPlayerPlugin.h | 2 +- .../ios/Classes/FLTVideoPlayerPlugin.m | 183 +++++++------- .../webview_flutter/example/ios/Runner/main.m | 2 +- .../ios/RunnerUITests/FLTWebViewUITests.m | 26 +- .../example/ios/Runner/main.m | 2 +- .../ios/RunnerUITests/FLTWebViewUITests.m | 46 ++-- .../ios/Classes/FLTCookieManager.h | 6 +- .../ios/Classes/FLTWebViewFlutterPlugin.m | 4 +- .../ios/Classes/FlutterWebView.h | 12 +- .../ios/Classes/FlutterWebView.m | 228 +++++++++--------- .../ios/Classes/FlutterWebView_Test.h | 6 +- .../ios/Classes/JavaScriptChannelHandler.h | 4 +- .../ios/Classes/JavaScriptChannelHandler.m | 14 +- script/tool/lib/src/format_command.dart | 2 +- script/tool/test/format_command_test.dart | 6 +- 81 files changed, 1037 insertions(+), 1028 deletions(-) diff --git a/.clang-format b/.clang-format index f6cb8ad931f5..ac9a91a08008 100644 --- a/.clang-format +++ b/.clang-format @@ -1 +1,9 @@ BasedOnStyle: Google +--- +Language: Cpp +DerivePointerAlignment: false +PointerAlignment: Left +--- +Language: ObjC +DerivePointerAlignment: false +PointerAlignment: Right diff --git a/packages/camera/camera/example/ios/Runner/main.m b/packages/camera/camera/example/ios/Runner/main.m index f97b9ef5c8a1..f143297b30d6 100644 --- a/packages/camera/camera/example/ios/Runner/main.m +++ b/packages/camera/camera/example/ios/Runner/main.m @@ -6,7 +6,7 @@ #import #import "AppDelegate.h" -int main(int argc, char* argv[]) { +int main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } diff --git a/packages/camera/camera/example/ios/RunnerTests/ThreadSafeFlutterResultTests.m b/packages/camera/camera/example/ios/RunnerTests/ThreadSafeFlutterResultTests.m index a01b53154f62..5fdbd49e5d40 100644 --- a/packages/camera/camera/example/ios/RunnerTests/ThreadSafeFlutterResultTests.m +++ b/packages/camera/camera/example/ios/RunnerTests/ThreadSafeFlutterResultTests.m @@ -10,9 +10,9 @@ @interface ThreadSafeFlutterResultTests : XCTestCase @implementation ThreadSafeFlutterResultTests - (void)testAsyncSendSuccess_ShouldCallResultOnMainThread { - XCTestExpectation* expectation = [self expectationWithDescription:@"Result finished"]; + XCTestExpectation *expectation = [self expectationWithDescription:@"Result finished"]; - FLTThreadSafeFlutterResult* threadSafeFlutterResult = + FLTThreadSafeFlutterResult *threadSafeFlutterResult = [[FLTThreadSafeFlutterResult alloc] initWithResult:^(id _Nullable result) { XCTAssert(NSThread.isMainThread); [expectation fulfill]; @@ -26,9 +26,9 @@ - (void)testAsyncSendSuccess_ShouldCallResultOnMainThread { } - (void)testSyncSendSuccess_ShouldCallResultOnMainThread { - XCTestExpectation* expectation = [self expectationWithDescription:@"Result finished"]; + XCTestExpectation *expectation = [self expectationWithDescription:@"Result finished"]; - FLTThreadSafeFlutterResult* threadSafeFlutterResult = + FLTThreadSafeFlutterResult *threadSafeFlutterResult = [[FLTThreadSafeFlutterResult alloc] initWithResult:^(id _Nullable result) { XCTAssert(NSThread.isMainThread); [expectation fulfill]; @@ -38,9 +38,9 @@ - (void)testSyncSendSuccess_ShouldCallResultOnMainThread { } - (void)testSendNotImplemented_ShouldSendNotImplementedToFlutterResult { - XCTestExpectation* expectation = [self expectationWithDescription:@"Result finished"]; + XCTestExpectation *expectation = [self expectationWithDescription:@"Result finished"]; - FLTThreadSafeFlutterResult* threadSafeFlutterResult = + FLTThreadSafeFlutterResult *threadSafeFlutterResult = [[FLTThreadSafeFlutterResult alloc] initWithResult:^(id _Nullable result) { XCTAssert([result isKindOfClass:FlutterMethodNotImplemented.class]); [expectation fulfill]; @@ -54,15 +54,15 @@ - (void)testSendNotImplemented_ShouldSendNotImplementedToFlutterResult { } - (void)testSendErrorDetails_ShouldSendErrorToFlutterResult { - NSString* errorCode = @"errorCode"; - NSString* errorMessage = @"message"; - NSString* errorDetails = @"error details"; - XCTestExpectation* expectation = [self expectationWithDescription:@"Result finished"]; + NSString *errorCode = @"errorCode"; + NSString *errorMessage = @"message"; + NSString *errorDetails = @"error details"; + XCTestExpectation *expectation = [self expectationWithDescription:@"Result finished"]; - FLTThreadSafeFlutterResult* threadSafeFlutterResult = + FLTThreadSafeFlutterResult *threadSafeFlutterResult = [[FLTThreadSafeFlutterResult alloc] initWithResult:^(id _Nullable result) { XCTAssert([result isKindOfClass:FlutterError.class]); - FlutterError* error = (FlutterError*)result; + FlutterError *error = (FlutterError *)result; XCTAssertEqualObjects(error.code, errorCode); XCTAssertEqualObjects(error.message, errorMessage); XCTAssertEqualObjects(error.details, errorDetails); @@ -77,14 +77,14 @@ - (void)testSendErrorDetails_ShouldSendErrorToFlutterResult { } - (void)testSendNSError_ShouldSendErrorToFlutterResult { - NSError* originalError = [[NSError alloc] initWithDomain:NSURLErrorDomain code:404 userInfo:nil]; - XCTestExpectation* expectation = [self expectationWithDescription:@"Result finished"]; + NSError *originalError = [[NSError alloc] initWithDomain:NSURLErrorDomain code:404 userInfo:nil]; + XCTestExpectation *expectation = [self expectationWithDescription:@"Result finished"]; - FLTThreadSafeFlutterResult* threadSafeFlutterResult = + FLTThreadSafeFlutterResult *threadSafeFlutterResult = [[FLTThreadSafeFlutterResult alloc] initWithResult:^(id _Nullable result) { XCTAssert([result isKindOfClass:FlutterError.class]); - FlutterError* error = (FlutterError*)result; - NSString* constructedErrorCode = + FlutterError *error = (FlutterError *)result; + NSString *constructedErrorCode = [NSString stringWithFormat:@"Error %d", (int)originalError.code]; XCTAssertEqualObjects(error.code, constructedErrorCode); [expectation fulfill]; @@ -98,10 +98,10 @@ - (void)testSendNSError_ShouldSendErrorToFlutterResult { } - (void)testSendResult_ShouldSendResultToFlutterResult { - NSString* resultData = @"resultData"; - XCTestExpectation* expectation = [self expectationWithDescription:@"Result finished"]; + NSString *resultData = @"resultData"; + XCTestExpectation *expectation = [self expectationWithDescription:@"Result finished"]; - FLTThreadSafeFlutterResult* threadSafeFlutterResult = + FLTThreadSafeFlutterResult *threadSafeFlutterResult = [[FLTThreadSafeFlutterResult alloc] initWithResult:^(id _Nullable result) { XCTAssertEqualObjects(result, resultData); [expectation fulfill]; diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.h b/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.h index 787be4c8faca..70c9f868eda9 100644 --- a/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.h +++ b/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.h @@ -36,13 +36,13 @@ * Sends an NSError as result on the main thread. * @param error Error that will be send as FlutterError. */ -- (void)sendError:(nonnull NSError*)error; +- (void)sendError:(nonnull NSError *)error; /** * Sends a FlutterError as result on the main thread. */ -- (void)sendErrorWithCode:(nonnull NSString*)code - message:(nullable NSString*)message +- (void)sendErrorWithCode:(nonnull NSString *)code + message:(nullable NSString *)message details:(nullable id)details; /** diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.m b/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.m index 2e426ccbe337..821d7561f782 100644 --- a/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.m +++ b/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.m @@ -26,16 +26,16 @@ - (void)sendSuccessWithData:(id)data { [self send:data]; } -- (void)sendError:(NSError*)error { +- (void)sendError:(NSError *)error { [self sendErrorWithCode:[NSString stringWithFormat:@"Error %d", (int)error.code] message:error.localizedDescription details:error.domain]; } -- (void)sendErrorWithCode:(NSString*)code - message:(NSString* _Nullable)message +- (void)sendErrorWithCode:(NSString *)code + message:(NSString *_Nullable)message details:(id _Nullable)details { - FlutterError* flutterError = [FlutterError errorWithCode:code message:message details:details]; + FlutterError *flutterError = [FlutterError errorWithCode:code message:message details:details]; [self send:flutterError]; } diff --git a/packages/file_selector/file_selector/example/windows/runner/main.cpp b/packages/file_selector/file_selector/example/windows/runner/main.cpp index a890d8730772..df379fa0be93 100644 --- a/packages/file_selector/file_selector/example/windows/runner/main.cpp +++ b/packages/file_selector/file_selector/example/windows/runner/main.cpp @@ -10,7 +10,7 @@ #include "utils.h" int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, - _In_ wchar_t *command_line, _In_ int show_command) { + _In_ wchar_t* command_line, _In_ int show_command) { // Attach to console when present (e.g., 'flutter run') or create a // new console when running with a debugger. if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner/AppDelegate.m b/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner/AppDelegate.m index d050cf771c8f..55733442b4cf 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner/AppDelegate.m +++ b/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner/AppDelegate.m @@ -9,10 +9,10 @@ @implementation AppDelegate -- (BOOL)application:(UIApplication*)application - didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Provide the GoogleMaps API key. - NSString* mapsApiKey = [[NSProcessInfo processInfo] environment][@"MAPS_API_KEY"]; + NSString *mapsApiKey = [[NSProcessInfo processInfo] environment][@"MAPS_API_KEY"]; if ([mapsApiKey length] == 0) { mapsApiKey = @"YOUR KEY HERE"; } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner/main.m b/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner/main.m index f97b9ef5c8a1..f143297b30d6 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner/main.m +++ b/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner/main.m @@ -6,7 +6,7 @@ #import #import "AppDelegate.h" -int main(int argc, char* argv[]) { +int main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/GoogleMapsTests.m b/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/GoogleMapsTests.m index 5249145f0c87..a833c7441243 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/GoogleMapsTests.m +++ b/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/GoogleMapsTests.m @@ -11,7 +11,7 @@ @interface GoogleMapsTests : XCTestCase @implementation GoogleMapsTests - (void)testPlugin { - FLTGoogleMapsPlugin* plugin = [[FLTGoogleMapsPlugin alloc] init]; + FLTGoogleMapsPlugin *plugin = [[FLTGoogleMapsPlugin alloc] init]; XCTAssertNotNil(plugin); } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerUITests/GoogleMapsUITests.m b/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerUITests/GoogleMapsUITests.m index 3aaf7d3d1f89..2f0c0fa6d615 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerUITests/GoogleMapsUITests.m +++ b/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerUITests/GoogleMapsUITests.m @@ -6,7 +6,7 @@ @import os.log; @interface GoogleMapsUITests : XCTestCase -@property(nonatomic, strong) XCUIApplication* app; +@property(nonatomic, strong) XCUIApplication *app; @end @implementation GoogleMapsUITests @@ -21,9 +21,9 @@ - (void)setUp { // See: https://github.com/flutter/flutter/issues/93325. [self addUIInterruptionMonitorWithDescription:@"Permission popups" - handler:^BOOL(XCUIElement* _Nonnull interruptingElement) { + handler:^BOOL(XCUIElement *_Nonnull interruptingElement) { if (@available(iOS 14, *)) { - XCUIElement* locationPermission = + XCUIElement *locationPermission = interruptingElement.buttons[@"Allow While Using App"]; if (![locationPermission waitForExistenceWithTimeout:30.0]) { @@ -33,7 +33,7 @@ - (void)setUp { [locationPermission tap]; } else { - XCUIElement* allow = + XCUIElement *allow = interruptingElement.buttons[@"Allow"]; if (![allow waitForExistenceWithTimeout:30.0]) { XCTFail(@"Failed due to not able to find Allow button"); @@ -46,19 +46,19 @@ - (void)setUp { // Temporarily disabled due to https://github.com/flutter/flutter/issues/93325 - (void)skip_testUserInterface { - XCUIApplication* app = self.app; - XCUIElement* userInteface = app.staticTexts[@"User interface"]; + XCUIApplication *app = self.app; + XCUIElement *userInteface = app.staticTexts[@"User interface"]; if (![userInteface waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); XCTFail(@"Failed due to not able to find User interface"); } [userInteface tap]; - XCUIElement* platformView = app.otherElements[@"platform_view[0]"]; + XCUIElement *platformView = app.otherElements[@"platform_view[0]"]; if (![platformView waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); XCTFail(@"Failed due to not able to find platform view"); } - XCUIElement* compass = app.buttons[@"disable compass"]; + XCUIElement *compass = app.buttons[@"disable compass"]; if (![compass waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); XCTFail(@"Failed due to not able to find compass button"); @@ -67,21 +67,21 @@ - (void)skip_testUserInterface { } - (void)testMapCoordinatesPage { - XCUIApplication* app = self.app; - XCUIElement* mapCoordinates = app.staticTexts[@"Map coordinates"]; + XCUIApplication *app = self.app; + XCUIElement *mapCoordinates = app.staticTexts[@"Map coordinates"]; if (![mapCoordinates waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); XCTFail(@"Failed due to not able to find 'Map coordinates''"); } [mapCoordinates tap]; - XCUIElement* platformView = app.otherElements[@"platform_view[0]"]; + XCUIElement *platformView = app.otherElements[@"platform_view[0]"]; if (![platformView waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); XCTFail(@"Failed due to not able to find platform view"); } - XCUIElement* getVisibleRegionBoundsButton = app.buttons[@"Get Visible Region Bounds"]; + XCUIElement *getVisibleRegionBoundsButton = app.buttons[@"Get Visible Region Bounds"]; if (![getVisibleRegionBoundsButton waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); XCTFail(@"Failed due to not able to find 'Get Visible Region Bounds''"); @@ -90,15 +90,15 @@ - (void)testMapCoordinatesPage { } - (void)testMapClickPage { - XCUIApplication* app = self.app; - XCUIElement* mapClick = app.staticTexts[@"Map click"]; + XCUIApplication *app = self.app; + XCUIElement *mapClick = app.staticTexts[@"Map click"]; if (![mapClick waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); XCTFail(@"Failed due to not able to find 'Map click''"); } [mapClick tap]; - XCUIElement* platformView = app.otherElements[@"platform_view[0]"]; + XCUIElement *platformView = app.otherElements[@"platform_view[0]"]; if (![platformView waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); XCTFail(@"Failed due to not able to find platform view"); @@ -106,7 +106,7 @@ - (void)testMapClickPage { [platformView tap]; - XCUIElement* tapped = app.staticTexts[@"Tapped"]; + XCUIElement *tapped = app.staticTexts[@"Tapped"]; if (![tapped waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); XCTFail(@"Failed due to not able to find 'tapped''"); @@ -114,7 +114,7 @@ - (void)testMapClickPage { [platformView pressForDuration:5.0]; - XCUIElement* longPressed = app.staticTexts[@"Long pressed"]; + XCUIElement *longPressed = app.staticTexts[@"Long pressed"]; if (![longPressed waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); XCTFail(@"Failed due to not able to find 'longPressed''"); diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapTileOverlayController.m b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapTileOverlayController.m index fb391380c92c..6baa753ef999 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapTileOverlayController.m +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapTileOverlayController.m @@ -5,30 +5,30 @@ #import "FLTGoogleMapTileOverlayController.h" #import "JsonConversions.h" -static void InterpretTileOverlayOptions(NSDictionary* data, +static void InterpretTileOverlayOptions(NSDictionary *data, id sink, - NSObject* registrar) { - NSNumber* visible = data[@"visible"]; + NSObject *registrar) { + NSNumber *visible = data[@"visible"]; if (visible != nil) { [sink setVisible:visible.boolValue]; } - NSNumber* transparency = data[@"transparency"]; + NSNumber *transparency = data[@"transparency"]; if (transparency != nil) { [sink setTransparency:transparency.floatValue]; } - NSNumber* zIndex = data[@"zIndex"]; + NSNumber *zIndex = data[@"zIndex"]; if (zIndex != nil) { [sink setZIndex:zIndex.intValue]; } - NSNumber* fadeIn = data[@"fadeIn"]; + NSNumber *fadeIn = data[@"fadeIn"]; if (fadeIn != nil) { [sink setFadeIn:fadeIn.boolValue]; } - NSNumber* tileSize = data[@"tileSize"]; + NSNumber *tileSize = data[@"tileSize"]; if (tileSize != nil) { [sink setTileSize:tileSize.integerValue]; } @@ -36,14 +36,14 @@ static void InterpretTileOverlayOptions(NSDictionary* data, @interface FLTGoogleMapTileOverlayController () -@property(strong, nonatomic) GMSTileLayer* layer; -@property(weak, nonatomic) GMSMapView* mapView; +@property(strong, nonatomic) GMSTileLayer *layer; +@property(weak, nonatomic) GMSMapView *mapView; @end @implementation FLTGoogleMapTileOverlayController -- (instancetype)initWithTileLayer:(GMSTileLayer*)tileLayer mapView:(GMSMapView*)mapView { +- (instancetype)initWithTileLayer:(GMSTileLayer *)tileLayer mapView:(GMSMapView *)mapView { self = [super init]; if (self) { self.layer = tileLayer; @@ -60,8 +60,8 @@ - (void)clearTileCache { [self.layer clearTileCache]; } -- (NSDictionary*)getTileOverlayInfo { - NSMutableDictionary* info = [[NSMutableDictionary alloc] init]; +- (NSDictionary *)getTileOverlayInfo { + NSMutableDictionary *info = [[NSMutableDictionary alloc] init]; BOOL visible = self.layer.map != nil; info[@"visible"] = @(visible); info[@"fadeIn"] = @(self.layer.fadeIn); @@ -97,14 +97,14 @@ - (void)setTileSize:(NSInteger)tileSize { @interface FLTTileProviderController () -@property(weak, nonatomic) FlutterMethodChannel* methodChannel; -@property(copy, nonatomic, readwrite) NSString* tileOverlayId; +@property(weak, nonatomic) FlutterMethodChannel *methodChannel; +@property(copy, nonatomic, readwrite) NSString *tileOverlayId; @end @implementation FLTTileProviderController -- (instancetype)init:(FlutterMethodChannel*)methodChannel tileOverlayId:(NSString*)tileOverlayId { +- (instancetype)init:(FlutterMethodChannel *)methodChannel tileOverlayId:(NSString *)tileOverlayId { self = [super init]; if (self) { self.methodChannel = methodChannel; @@ -128,9 +128,9 @@ - (void)requestTileForX:(NSUInteger)x @"zoom" : @(zoom) } result:^(id _Nullable result) { - UIImage* tileImage; + UIImage *tileImage; if ([result isKindOfClass:[NSDictionary class]]) { - FlutterStandardTypedData* typedData = (FlutterStandardTypedData*)result[@"data"]; + FlutterStandardTypedData *typedData = (FlutterStandardTypedData *)result[@"data"]; if (typedData == nil) { tileImage = kGMSTileLayerNoTile; } else { @@ -138,7 +138,7 @@ - (void)requestTileForX:(NSUInteger)x } } else { if ([result isKindOfClass:[FlutterError class]]) { - FlutterError* error = (FlutterError*)result; + FlutterError *error = (FlutterError *)result; NSLog(@"Can't get tile: errorCode = %@, errorMessage = %@, details = %@", [error code], [error message], [error details]); } @@ -156,18 +156,18 @@ - (void)requestTileForX:(NSUInteger)x @interface FLTTileOverlaysController () -@property(strong, nonatomic) NSMutableDictionary* tileOverlayIdToController; -@property(weak, nonatomic) FlutterMethodChannel* methodChannel; -@property(weak, nonatomic) NSObject* registrar; -@property(weak, nonatomic) GMSMapView* mapView; +@property(strong, nonatomic) NSMutableDictionary *tileOverlayIdToController; +@property(weak, nonatomic) FlutterMethodChannel *methodChannel; +@property(weak, nonatomic) NSObject *registrar; +@property(weak, nonatomic) GMSMapView *mapView; @end @implementation FLTTileOverlaysController -- (instancetype)init:(FlutterMethodChannel*)methodChannel - mapView:(GMSMapView*)mapView - registrar:(NSObject*)registrar { +- (instancetype)init:(FlutterMethodChannel *)methodChannel + mapView:(GMSMapView *)mapView + registrar:(NSObject *)registrar { self = [super init]; if (self) { self.methodChannel = methodChannel; @@ -178,12 +178,12 @@ - (instancetype)init:(FlutterMethodChannel*)methodChannel return self; } -- (void)addTileOverlays:(NSArray*)tileOverlaysToAdd { - for (NSDictionary* tileOverlay in tileOverlaysToAdd) { - NSString* tileOverlayId = [FLTTileOverlaysController getTileOverlayId:tileOverlay]; - FLTTileProviderController* tileProvider = +- (void)addTileOverlays:(NSArray *)tileOverlaysToAdd { + for (NSDictionary *tileOverlay in tileOverlaysToAdd) { + NSString *tileOverlayId = [FLTTileOverlaysController getTileOverlayId:tileOverlay]; + FLTTileProviderController *tileProvider = [[FLTTileProviderController alloc] init:self.methodChannel tileOverlayId:tileOverlayId]; - FLTGoogleMapTileOverlayController* controller = + FLTGoogleMapTileOverlayController *controller = [[FLTGoogleMapTileOverlayController alloc] initWithTileLayer:tileProvider mapView:self.mapView]; InterpretTileOverlayOptions(tileOverlay, controller, self.registrar); @@ -191,19 +191,19 @@ - (void)addTileOverlays:(NSArray*)tileOverlaysToAdd { } } -- (void)changeTileOverlays:(NSArray*)tileOverlaysToChange { - for (NSDictionary* tileOverlay in tileOverlaysToChange) { - NSString* tileOverlayId = [FLTTileOverlaysController getTileOverlayId:tileOverlay]; - FLTGoogleMapTileOverlayController* controller = self.tileOverlayIdToController[tileOverlayId]; +- (void)changeTileOverlays:(NSArray *)tileOverlaysToChange { + for (NSDictionary *tileOverlay in tileOverlaysToChange) { + NSString *tileOverlayId = [FLTTileOverlaysController getTileOverlayId:tileOverlay]; + FLTGoogleMapTileOverlayController *controller = self.tileOverlayIdToController[tileOverlayId]; if (!controller) { continue; } InterpretTileOverlayOptions(tileOverlay, controller, self.registrar); } } -- (void)removeTileOverlayIds:(NSArray*)tileOverlayIdsToRemove { - for (NSString* tileOverlayId in tileOverlayIdsToRemove) { - FLTGoogleMapTileOverlayController* controller = self.tileOverlayIdToController[tileOverlayId]; +- (void)removeTileOverlayIds:(NSArray *)tileOverlayIdsToRemove { + for (NSString *tileOverlayId in tileOverlayIdsToRemove) { + FLTGoogleMapTileOverlayController *controller = self.tileOverlayIdToController[tileOverlayId]; if (!controller) { continue; } @@ -212,22 +212,22 @@ - (void)removeTileOverlayIds:(NSArray*)tileOverlayIdsToRemove { } } -- (void)clearTileCache:(NSString*)tileOverlayId { - FLTGoogleMapTileOverlayController* controller = self.tileOverlayIdToController[tileOverlayId]; +- (void)clearTileCache:(NSString *)tileOverlayId { + FLTGoogleMapTileOverlayController *controller = self.tileOverlayIdToController[tileOverlayId]; if (!controller) { return; } [controller clearTileCache]; } -- (nullable NSDictionary*)getTileOverlayInfo:(NSString*)tileverlayId { +- (nullable NSDictionary *)getTileOverlayInfo:(NSString *)tileverlayId { if (self.tileOverlayIdToController[tileverlayId] == nil) { return nil; } return [self.tileOverlayIdToController[tileverlayId] getTileOverlayInfo]; } -+ (NSString*)getTileOverlayId:(NSDictionary*)tileOverlay { ++ (NSString *)getTileOverlayId:(NSDictionary *)tileOverlay { return tileOverlay[@"tileOverlayId"]; } diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapsPlugin.m b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapsPlugin.m index 7ce2cf1c204d..e78a505ecfb0 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapsPlugin.m +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapsPlugin.m @@ -7,22 +7,22 @@ #pragma mark - GoogleMaps plugin implementation @implementation FLTGoogleMapsPlugin { - NSObject* _registrar; - FlutterMethodChannel* _channel; - NSMutableDictionary* _mapControllers; + NSObject *_registrar; + FlutterMethodChannel *_channel; + NSMutableDictionary *_mapControllers; } -+ (void)registerWithRegistrar:(NSObject*)registrar { - FLTGoogleMapFactory* googleMapFactory = [[FLTGoogleMapFactory alloc] initWithRegistrar:registrar]; ++ (void)registerWithRegistrar:(NSObject *)registrar { + FLTGoogleMapFactory *googleMapFactory = [[FLTGoogleMapFactory alloc] initWithRegistrar:registrar]; [registrar registerViewFactory:googleMapFactory withId:@"plugins.flutter.io/google_maps" gestureRecognizersBlockingPolicy: FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded]; } -- (FLTGoogleMapController*)mapFromCall:(FlutterMethodCall*)call error:(FlutterError**)error { +- (FLTGoogleMapController *)mapFromCall:(FlutterMethodCall *)call error:(FlutterError **)error { id mapId = call.arguments[@"map"]; - FLTGoogleMapController* controller = _mapControllers[mapId]; + FLTGoogleMapController *controller = _mapControllers[mapId]; if (!controller && error) { *error = [FlutterError errorWithCode:@"unknown_map" message:nil details:mapId]; } diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapCircleController.h b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapCircleController.h index 2e7a9967ebd3..26b7ce573bdf 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapCircleController.h +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapCircleController.h @@ -9,9 +9,9 @@ @protocol FLTGoogleMapCircleOptionsSink - (void)setConsumeTapEvents:(BOOL)consume; - (void)setVisible:(BOOL)visible; -- (void)setStrokeColor:(UIColor*)color; +- (void)setStrokeColor:(UIColor *)color; - (void)setStrokeWidth:(CGFloat)width; -- (void)setFillColor:(UIColor*)color; +- (void)setFillColor:(UIColor *)color; - (void)setCenter:(CLLocationCoordinate2D)center; - (void)setRadius:(CLLocationDistance)radius; - (void)setZIndex:(int)zIndex; @@ -19,21 +19,21 @@ // Defines circle controllable by Flutter. @interface FLTGoogleMapCircleController : NSObject -@property(atomic, readonly) NSString* circleId; +@property(atomic, readonly) NSString *circleId; - (instancetype)initCircleWithPosition:(CLLocationCoordinate2D)position radius:(CLLocationDistance)radius - circleId:(NSString*)circleId - mapView:(GMSMapView*)mapView; + circleId:(NSString *)circleId + mapView:(GMSMapView *)mapView; - (void)removeCircle; @end @interface FLTCirclesController : NSObject -- (instancetype)init:(FlutterMethodChannel*)methodChannel - mapView:(GMSMapView*)mapView - registrar:(NSObject*)registrar; -- (void)addCircles:(NSArray*)circlesToAdd; -- (void)changeCircles:(NSArray*)circlesToChange; -- (void)removeCircleIds:(NSArray*)circleIdsToRemove; -- (void)onCircleTap:(NSString*)circleId; -- (bool)hasCircleWithId:(NSString*)circleId; +- (instancetype)init:(FlutterMethodChannel *)methodChannel + mapView:(GMSMapView *)mapView + registrar:(NSObject *)registrar; +- (void)addCircles:(NSArray *)circlesToAdd; +- (void)changeCircles:(NSArray *)circlesToChange; +- (void)removeCircleIds:(NSArray *)circleIdsToRemove; +- (void)onCircleTap:(NSString *)circleId; +- (bool)hasCircleWithId:(NSString *)circleId; @end diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapCircleController.m b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapCircleController.m index bdf36484aaf7..d97de587fb17 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapCircleController.m +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapCircleController.m @@ -6,13 +6,13 @@ #import "JsonConversions.h" @implementation FLTGoogleMapCircleController { - GMSCircle* _circle; - GMSMapView* _mapView; + GMSCircle *_circle; + GMSMapView *_mapView; } - (instancetype)initCircleWithPosition:(CLLocationCoordinate2D)position radius:(CLLocationDistance)radius - circleId:(NSString*)circleId - mapView:(GMSMapView*)mapView { + circleId:(NSString *)circleId + mapView:(GMSMapView *)mapView { self = [super init]; if (self) { _circle = [GMSCircle circleWithPosition:position radius:radius]; @@ -45,83 +45,83 @@ - (void)setRadius:(CLLocationDistance)radius { _circle.radius = radius; } -- (void)setStrokeColor:(UIColor*)color { +- (void)setStrokeColor:(UIColor *)color { _circle.strokeColor = color; } - (void)setStrokeWidth:(CGFloat)width { _circle.strokeWidth = width; } -- (void)setFillColor:(UIColor*)color { +- (void)setFillColor:(UIColor *)color { _circle.fillColor = color; } @end -static int ToInt(NSNumber* data) { return [FLTGoogleMapJsonConversions toInt:data]; } +static int ToInt(NSNumber *data) { return [FLTGoogleMapJsonConversions toInt:data]; } -static BOOL ToBool(NSNumber* data) { return [FLTGoogleMapJsonConversions toBool:data]; } +static BOOL ToBool(NSNumber *data) { return [FLTGoogleMapJsonConversions toBool:data]; } -static CLLocationCoordinate2D ToLocation(NSArray* data) { +static CLLocationCoordinate2D ToLocation(NSArray *data) { return [FLTGoogleMapJsonConversions toLocation:data]; } -static CLLocationDistance ToDistance(NSNumber* data) { +static CLLocationDistance ToDistance(NSNumber *data) { return [FLTGoogleMapJsonConversions toFloat:data]; } -static UIColor* ToColor(NSNumber* data) { return [FLTGoogleMapJsonConversions toColor:data]; } +static UIColor *ToColor(NSNumber *data) { return [FLTGoogleMapJsonConversions toColor:data]; } -static void InterpretCircleOptions(NSDictionary* data, id sink, - NSObject* registrar) { - NSNumber* consumeTapEvents = data[@"consumeTapEvents"]; +static void InterpretCircleOptions(NSDictionary *data, id sink, + NSObject *registrar) { + NSNumber *consumeTapEvents = data[@"consumeTapEvents"]; if (consumeTapEvents != nil) { [sink setConsumeTapEvents:ToBool(consumeTapEvents)]; } - NSNumber* visible = data[@"visible"]; + NSNumber *visible = data[@"visible"]; if (visible != nil) { [sink setVisible:ToBool(visible)]; } - NSNumber* zIndex = data[@"zIndex"]; + NSNumber *zIndex = data[@"zIndex"]; if (zIndex != nil) { [sink setZIndex:ToInt(zIndex)]; } - NSArray* center = data[@"center"]; + NSArray *center = data[@"center"]; if (center) { [sink setCenter:ToLocation(center)]; } - NSNumber* radius = data[@"radius"]; + NSNumber *radius = data[@"radius"]; if (radius != nil) { [sink setRadius:ToDistance(radius)]; } - NSNumber* strokeColor = data[@"strokeColor"]; + NSNumber *strokeColor = data[@"strokeColor"]; if (strokeColor != nil) { [sink setStrokeColor:ToColor(strokeColor)]; } - NSNumber* strokeWidth = data[@"strokeWidth"]; + NSNumber *strokeWidth = data[@"strokeWidth"]; if (strokeWidth != nil) { [sink setStrokeWidth:ToInt(strokeWidth)]; } - NSNumber* fillColor = data[@"fillColor"]; + NSNumber *fillColor = data[@"fillColor"]; if (fillColor != nil) { [sink setFillColor:ToColor(fillColor)]; } } @implementation FLTCirclesController { - NSMutableDictionary* _circleIdToController; - FlutterMethodChannel* _methodChannel; - NSObject* _registrar; - GMSMapView* _mapView; -} -- (instancetype)init:(FlutterMethodChannel*)methodChannel - mapView:(GMSMapView*)mapView - registrar:(NSObject*)registrar { + NSMutableDictionary *_circleIdToController; + FlutterMethodChannel *_methodChannel; + NSObject *_registrar; + GMSMapView *_mapView; +} +- (instancetype)init:(FlutterMethodChannel *)methodChannel + mapView:(GMSMapView *)mapView + registrar:(NSObject *)registrar { self = [super init]; if (self) { _methodChannel = methodChannel; @@ -131,12 +131,12 @@ - (instancetype)init:(FlutterMethodChannel*)methodChannel } return self; } -- (void)addCircles:(NSArray*)circlesToAdd { - for (NSDictionary* circle in circlesToAdd) { +- (void)addCircles:(NSArray *)circlesToAdd { + for (NSDictionary *circle in circlesToAdd) { CLLocationCoordinate2D position = [FLTCirclesController getPosition:circle]; CLLocationDistance radius = [FLTCirclesController getRadius:circle]; - NSString* circleId = [FLTCirclesController getCircleId:circle]; - FLTGoogleMapCircleController* controller = + NSString *circleId = [FLTCirclesController getCircleId:circle]; + FLTGoogleMapCircleController *controller = [[FLTGoogleMapCircleController alloc] initCircleWithPosition:position radius:radius circleId:circleId @@ -145,22 +145,22 @@ - (void)addCircles:(NSArray*)circlesToAdd { _circleIdToController[circleId] = controller; } } -- (void)changeCircles:(NSArray*)circlesToChange { - for (NSDictionary* circle in circlesToChange) { - NSString* circleId = [FLTCirclesController getCircleId:circle]; - FLTGoogleMapCircleController* controller = _circleIdToController[circleId]; +- (void)changeCircles:(NSArray *)circlesToChange { + for (NSDictionary *circle in circlesToChange) { + NSString *circleId = [FLTCirclesController getCircleId:circle]; + FLTGoogleMapCircleController *controller = _circleIdToController[circleId]; if (!controller) { continue; } InterpretCircleOptions(circle, controller, _registrar); } } -- (void)removeCircleIds:(NSArray*)circleIdsToRemove { - for (NSString* circleId in circleIdsToRemove) { +- (void)removeCircleIds:(NSArray *)circleIdsToRemove { + for (NSString *circleId in circleIdsToRemove) { if (!circleId) { continue; } - FLTGoogleMapCircleController* controller = _circleIdToController[circleId]; + FLTGoogleMapCircleController *controller = _circleIdToController[circleId]; if (!controller) { continue; } @@ -168,31 +168,31 @@ - (void)removeCircleIds:(NSArray*)circleIdsToRemove { [_circleIdToController removeObjectForKey:circleId]; } } -- (bool)hasCircleWithId:(NSString*)circleId { +- (bool)hasCircleWithId:(NSString *)circleId { if (!circleId) { return false; } return _circleIdToController[circleId] != nil; } -- (void)onCircleTap:(NSString*)circleId { +- (void)onCircleTap:(NSString *)circleId { if (!circleId) { return; } - FLTGoogleMapCircleController* controller = _circleIdToController[circleId]; + FLTGoogleMapCircleController *controller = _circleIdToController[circleId]; if (!controller) { return; } [_methodChannel invokeMethod:@"circle#onTap" arguments:@{@"circleId" : circleId}]; } -+ (CLLocationCoordinate2D)getPosition:(NSDictionary*)circle { - NSArray* center = circle[@"center"]; ++ (CLLocationCoordinate2D)getPosition:(NSDictionary *)circle { + NSArray *center = circle[@"center"]; return ToLocation(center); } -+ (CLLocationDistance)getRadius:(NSDictionary*)circle { - NSNumber* radius = circle[@"radius"]; ++ (CLLocationDistance)getRadius:(NSDictionary *)circle { + NSNumber *radius = circle[@"radius"]; return ToDistance(radius); } -+ (NSString*)getCircleId:(NSDictionary*)circle { ++ (NSString *)getCircleId:(NSDictionary *)circle { return circle[@"circleId"]; } @end diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m index 1428de69885b..df4e8761e6b2 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m @@ -8,22 +8,22 @@ #pragma mark - Conversion of JSON-like values sent via platform channels. Forward declarations. -static NSDictionary* PositionToJson(GMSCameraPosition* position); -static NSDictionary* PointToJson(CGPoint point); -static NSArray* LocationToJson(CLLocationCoordinate2D position); -static CGPoint ToCGPoint(NSDictionary* json); -static GMSCameraPosition* ToOptionalCameraPosition(NSDictionary* json); -static GMSCoordinateBounds* ToOptionalBounds(NSArray* json); -static GMSCameraUpdate* ToCameraUpdate(NSArray* data); -static NSDictionary* GMSCoordinateBoundsToJson(GMSCoordinateBounds* bounds); -static void InterpretMapOptions(NSDictionary* data, id sink); -static double ToDouble(NSNumber* data) { return [FLTGoogleMapJsonConversions toDouble:data]; } +static NSDictionary *PositionToJson(GMSCameraPosition *position); +static NSDictionary *PointToJson(CGPoint point); +static NSArray *LocationToJson(CLLocationCoordinate2D position); +static CGPoint ToCGPoint(NSDictionary *json); +static GMSCameraPosition *ToOptionalCameraPosition(NSDictionary *json); +static GMSCoordinateBounds *ToOptionalBounds(NSArray *json); +static GMSCameraUpdate *ToCameraUpdate(NSArray *data); +static NSDictionary *GMSCoordinateBoundsToJson(GMSCoordinateBounds *bounds); +static void InterpretMapOptions(NSDictionary *data, id sink); +static double ToDouble(NSNumber *data) { return [FLTGoogleMapJsonConversions toDouble:data]; } @implementation FLTGoogleMapFactory { - NSObject* _registrar; + NSObject *_registrar; } -- (instancetype)initWithRegistrar:(NSObject*)registrar { +- (instancetype)initWithRegistrar:(NSObject *)registrar { self = [super init]; if (self) { _registrar = registrar; @@ -31,13 +31,13 @@ - (instancetype)initWithRegistrar:(NSObject*)registrar { return self; } -- (NSObject*)createArgsCodec { +- (NSObject *)createArgsCodec { return [FlutterStandardMessageCodec sharedInstance]; } -- (NSObject*)createWithFrame:(CGRect)frame - viewIdentifier:(int64_t)viewId - arguments:(id _Nullable)args { +- (NSObject *)createWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args { return [[FLTGoogleMapController alloc] initWithFrame:frame viewIdentifier:viewId arguments:args @@ -46,37 +46,37 @@ - (instancetype)initWithRegistrar:(NSObject*)registrar { @end @implementation FLTGoogleMapController { - GMSMapView* _mapView; + GMSMapView *_mapView; int64_t _viewId; - FlutterMethodChannel* _channel; + FlutterMethodChannel *_channel; BOOL _trackCameraPosition; - NSObject* _registrar; + NSObject *_registrar; BOOL _cameraDidInitialSetup; - FLTMarkersController* _markersController; - FLTPolygonsController* _polygonsController; - FLTPolylinesController* _polylinesController; - FLTCirclesController* _circlesController; - FLTTileOverlaysController* _tileOverlaysController; + FLTMarkersController *_markersController; + FLTPolygonsController *_polygonsController; + FLTPolylinesController *_polylinesController; + FLTCirclesController *_circlesController; + FLTTileOverlaysController *_tileOverlaysController; } - (instancetype)initWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args - registrar:(NSObject*)registrar { + registrar:(NSObject *)registrar { if (self = [super init]) { _viewId = viewId; - GMSCameraPosition* camera = ToOptionalCameraPosition(args[@"initialCameraPosition"]); + GMSCameraPosition *camera = ToOptionalCameraPosition(args[@"initialCameraPosition"]); _mapView = [GMSMapView mapWithFrame:frame camera:camera]; _mapView.accessibilityElementsHidden = NO; _trackCameraPosition = NO; InterpretMapOptions(args[@"options"], self); - NSString* channelName = + NSString *channelName = [NSString stringWithFormat:@"plugins.flutter.io/google_maps_%lld", viewId]; _channel = [FlutterMethodChannel methodChannelWithName:channelName binaryMessenger:registrar.messenger]; __weak __typeof__(self) weakSelf = self; - [_channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { + [_channel setMethodCallHandler:^(FlutterMethodCall *call, FlutterResult result) { if (weakSelf) { [weakSelf onMethodCall:call result:result]; } @@ -123,15 +123,15 @@ - (instancetype)initWithFrame:(CGRect)frame return self; } -- (UIView*)view { +- (UIView *)view { [_mapView addObserver:self forKeyPath:@"frame" options:0 context:nil]; return _mapView; } -- (void)observeValueForKeyPath:(NSString*)keyPath +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object - change:(NSDictionary*)change - context:(void*)context { + change:(NSDictionary *)change + context:(void *)context { if (_cameraDidInitialSetup) { // We only observe the frame for initial setup. [_mapView removeObserver:self forKeyPath:@"frame"]; @@ -154,7 +154,7 @@ - (void)observeValueForKeyPath:(NSString*)keyPath } } -- (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { +- (void)onMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { if ([call.method isEqualToString:@"map#show"]) { [self showAtX:ToDouble(call.arguments[@"x"]) Y:ToDouble(call.arguments[@"y"])]; result(nil); @@ -173,7 +173,7 @@ - (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { } else if ([call.method isEqualToString:@"map#getVisibleRegion"]) { if (_mapView != nil) { GMSVisibleRegion visibleRegion = _mapView.projection.visibleRegion; - GMSCoordinateBounds* bounds = [[GMSCoordinateBounds alloc] initWithRegion:visibleRegion]; + GMSCoordinateBounds *bounds = [[GMSCoordinateBounds alloc] initWithRegion:visibleRegion]; result(GMSCoordinateBoundsToJson(bounds)); } else { @@ -206,12 +206,12 @@ - (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { } else if ([call.method isEqualToString:@"map#takeSnapshot"]) { if (@available(iOS 10.0, *)) { if (_mapView != nil) { - UIGraphicsImageRendererFormat* format = [UIGraphicsImageRendererFormat defaultFormat]; + UIGraphicsImageRendererFormat *format = [UIGraphicsImageRendererFormat defaultFormat]; format.scale = [[UIScreen mainScreen] scale]; - UIGraphicsImageRenderer* renderer = + UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:_mapView.frame.size format:format]; - UIImage* image = [renderer imageWithActions:^(UIGraphicsImageRendererContext* context) { + UIImage *image = [renderer imageWithActions:^(UIGraphicsImageRendererContext *context) { [_mapView.layer renderInContext:context.CGContext]; }]; result([FlutterStandardTypedData typedDataWithBytes:UIImagePNGRepresentation(image)]); @@ -326,50 +326,50 @@ - (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { [_tileOverlaysController clearTileCache:rawTileOverlayId]; result(nil); } else if ([call.method isEqualToString:@"map#isCompassEnabled"]) { - NSNumber* isCompassEnabled = @(_mapView.settings.compassButton); + NSNumber *isCompassEnabled = @(_mapView.settings.compassButton); result(isCompassEnabled); } else if ([call.method isEqualToString:@"map#isMapToolbarEnabled"]) { - NSNumber* isMapToolbarEnabled = @NO; + NSNumber *isMapToolbarEnabled = @NO; result(isMapToolbarEnabled); } else if ([call.method isEqualToString:@"map#getMinMaxZoomLevels"]) { - NSArray* zoomLevels = @[ @(_mapView.minZoom), @(_mapView.maxZoom) ]; + NSArray *zoomLevels = @[ @(_mapView.minZoom), @(_mapView.maxZoom) ]; result(zoomLevels); } else if ([call.method isEqualToString:@"map#getZoomLevel"]) { result(@(_mapView.camera.zoom)); } else if ([call.method isEqualToString:@"map#isZoomGesturesEnabled"]) { - NSNumber* isZoomGesturesEnabled = @(_mapView.settings.zoomGestures); + NSNumber *isZoomGesturesEnabled = @(_mapView.settings.zoomGestures); result(isZoomGesturesEnabled); } else if ([call.method isEqualToString:@"map#isZoomControlsEnabled"]) { - NSNumber* isZoomControlsEnabled = @NO; + NSNumber *isZoomControlsEnabled = @NO; result(isZoomControlsEnabled); } else if ([call.method isEqualToString:@"map#isTiltGesturesEnabled"]) { - NSNumber* isTiltGesturesEnabled = @(_mapView.settings.tiltGestures); + NSNumber *isTiltGesturesEnabled = @(_mapView.settings.tiltGestures); result(isTiltGesturesEnabled); } else if ([call.method isEqualToString:@"map#isRotateGesturesEnabled"]) { - NSNumber* isRotateGesturesEnabled = @(_mapView.settings.rotateGestures); + NSNumber *isRotateGesturesEnabled = @(_mapView.settings.rotateGestures); result(isRotateGesturesEnabled); } else if ([call.method isEqualToString:@"map#isScrollGesturesEnabled"]) { - NSNumber* isScrollGesturesEnabled = @(_mapView.settings.scrollGestures); + NSNumber *isScrollGesturesEnabled = @(_mapView.settings.scrollGestures); result(isScrollGesturesEnabled); } else if ([call.method isEqualToString:@"map#isMyLocationButtonEnabled"]) { - NSNumber* isMyLocationButtonEnabled = @(_mapView.settings.myLocationButton); + NSNumber *isMyLocationButtonEnabled = @(_mapView.settings.myLocationButton); result(isMyLocationButtonEnabled); } else if ([call.method isEqualToString:@"map#isTrafficEnabled"]) { - NSNumber* isTrafficEnabled = @(_mapView.trafficEnabled); + NSNumber *isTrafficEnabled = @(_mapView.trafficEnabled); result(isTrafficEnabled); } else if ([call.method isEqualToString:@"map#isBuildingsEnabled"]) { - NSNumber* isBuildingsEnabled = @(_mapView.buildingsEnabled); + NSNumber *isBuildingsEnabled = @(_mapView.buildingsEnabled); result(isBuildingsEnabled); } else if ([call.method isEqualToString:@"map#setStyle"]) { - NSString* mapStyle = [call arguments]; - NSString* error = [self setMapStyle:mapStyle]; + NSString *mapStyle = [call arguments]; + NSString *error = [self setMapStyle:mapStyle]; if (error == nil) { result(@[ @(YES) ]); } else { result(@[ @(NO), error ]); } } else if ([call.method isEqualToString:@"map#getTileOverlayInfo"]) { - NSString* rawTileOverlayId = call.arguments[@"tileOverlayId"]; + NSString *rawTileOverlayId = call.arguments[@"tileOverlayId"]; result([_tileOverlaysController getTileOverlayInfo:rawTileOverlayId]); } else { result(FlutterMethodNotImplemented); @@ -386,15 +386,15 @@ - (void)hide { _mapView.hidden = YES; } -- (void)animateWithCameraUpdate:(GMSCameraUpdate*)cameraUpdate { +- (void)animateWithCameraUpdate:(GMSCameraUpdate *)cameraUpdate { [_mapView animateWithCameraUpdate:cameraUpdate]; } -- (void)moveWithCameraUpdate:(GMSCameraUpdate*)cameraUpdate { +- (void)moveWithCameraUpdate:(GMSCameraUpdate *)cameraUpdate { [_mapView moveCamera:cameraUpdate]; } -- (GMSCameraPosition*)cameraPosition { +- (GMSCameraPosition *)cameraPosition { if (_trackCameraPosition) { return _mapView.camera; } else { @@ -404,11 +404,11 @@ - (GMSCameraPosition*)cameraPosition { #pragma mark - FLTGoogleMapOptionsSink methods -- (void)setCamera:(GMSCameraPosition*)camera { +- (void)setCamera:(GMSCameraPosition *)camera { _mapView.camera = camera; } -- (void)setCameraTargetBounds:(GMSCoordinateBounds*)bounds { +- (void)setCameraTargetBounds:(GMSCoordinateBounds *)bounds { _mapView.cameraTargetBounds = bounds; } @@ -468,13 +468,13 @@ - (void)setMyLocationButtonEnabled:(BOOL)enabled { _mapView.settings.myLocationButton = enabled; } -- (NSString*)setMapStyle:(NSString*)mapStyle { +- (NSString *)setMapStyle:(NSString *)mapStyle { if (mapStyle == (id)[NSNull null] || mapStyle.length == 0) { _mapView.mapStyle = nil; return nil; } - NSError* error; - GMSMapStyle* style = [GMSMapStyle styleWithJSONString:mapStyle error:&error]; + NSError *error; + GMSMapStyle *style = [GMSMapStyle styleWithJSONString:mapStyle error:&error]; if (!style) { return [error localizedDescription]; } else { @@ -485,46 +485,46 @@ - (NSString*)setMapStyle:(NSString*)mapStyle { #pragma mark - GMSMapViewDelegate methods -- (void)mapView:(GMSMapView*)mapView willMove:(BOOL)gesture { +- (void)mapView:(GMSMapView *)mapView willMove:(BOOL)gesture { [_channel invokeMethod:@"camera#onMoveStarted" arguments:@{@"isGesture" : @(gesture)}]; } -- (void)mapView:(GMSMapView*)mapView didChangeCameraPosition:(GMSCameraPosition*)position { +- (void)mapView:(GMSMapView *)mapView didChangeCameraPosition:(GMSCameraPosition *)position { if (_trackCameraPosition) { [_channel invokeMethod:@"camera#onMove" arguments:@{@"position" : PositionToJson(position)}]; } } -- (void)mapView:(GMSMapView*)mapView idleAtCameraPosition:(GMSCameraPosition*)position { +- (void)mapView:(GMSMapView *)mapView idleAtCameraPosition:(GMSCameraPosition *)position { [_channel invokeMethod:@"camera#onIdle" arguments:@{}]; } -- (BOOL)mapView:(GMSMapView*)mapView didTapMarker:(GMSMarker*)marker { - NSString* markerId = marker.userData[0]; +- (BOOL)mapView:(GMSMapView *)mapView didTapMarker:(GMSMarker *)marker { + NSString *markerId = marker.userData[0]; return [_markersController onMarkerTap:markerId]; } -- (void)mapView:(GMSMapView*)mapView didEndDraggingMarker:(GMSMarker*)marker { - NSString* markerId = marker.userData[0]; +- (void)mapView:(GMSMapView *)mapView didEndDraggingMarker:(GMSMarker *)marker { + NSString *markerId = marker.userData[0]; [_markersController onMarkerDragEnd:markerId coordinate:marker.position]; } -- (void)mapView:(GMSMapView*)mapView didStartDraggingMarker:(GMSMarker*)marker { - NSString* markerId = marker.userData[0]; +- (void)mapView:(GMSMapView *)mapView didStartDraggingMarker:(GMSMarker *)marker { + NSString *markerId = marker.userData[0]; [_markersController onMarkerDragStart:markerId coordinate:marker.position]; } -- (void)mapView:(GMSMapView*)mapView didDragMarker:(GMSMarker*)marker { - NSString* markerId = marker.userData[0]; +- (void)mapView:(GMSMapView *)mapView didDragMarker:(GMSMarker *)marker { + NSString *markerId = marker.userData[0]; [_markersController onMarkerDrag:markerId coordinate:marker.position]; } -- (void)mapView:(GMSMapView*)mapView didTapInfoWindowOfMarker:(GMSMarker*)marker { - NSString* markerId = marker.userData[0]; +- (void)mapView:(GMSMapView *)mapView didTapInfoWindowOfMarker:(GMSMarker *)marker { + NSString *markerId = marker.userData[0]; [_markersController onInfoWindowTap:markerId]; } -- (void)mapView:(GMSMapView*)mapView didTapOverlay:(GMSOverlay*)overlay { - NSString* overlayId = overlay.userData[0]; +- (void)mapView:(GMSMapView *)mapView didTapOverlay:(GMSOverlay *)overlay { + NSString *overlayId = overlay.userData[0]; if ([_polylinesController hasPolylineWithId:overlayId]) { [_polylinesController onPolylineTap:overlayId]; } else if ([_polygonsController hasPolygonWithId:overlayId]) { @@ -534,11 +534,11 @@ - (void)mapView:(GMSMapView*)mapView didTapOverlay:(GMSOverlay*)overlay { } } -- (void)mapView:(GMSMapView*)mapView didTapAtCoordinate:(CLLocationCoordinate2D)coordinate { +- (void)mapView:(GMSMapView *)mapView didTapAtCoordinate:(CLLocationCoordinate2D)coordinate { [_channel invokeMethod:@"map#onTap" arguments:@{@"position" : LocationToJson(coordinate)}]; } -- (void)mapView:(GMSMapView*)mapView didLongPressAtCoordinate:(CLLocationCoordinate2D)coordinate { +- (void)mapView:(GMSMapView *)mapView didLongPressAtCoordinate:(CLLocationCoordinate2D)coordinate { [_channel invokeMethod:@"map#onLongPress" arguments:@{@"position" : LocationToJson(coordinate)}]; } @@ -546,11 +546,11 @@ - (void)mapView:(GMSMapView*)mapView didLongPressAtCoordinate:(CLLocationCoordin #pragma mark - Implementations of JSON conversion functions. -static NSArray* LocationToJson(CLLocationCoordinate2D position) { +static NSArray *LocationToJson(CLLocationCoordinate2D position) { return @[ @(position.latitude), @(position.longitude) ]; } -static NSDictionary* PositionToJson(GMSCameraPosition* position) { +static NSDictionary *PositionToJson(GMSCameraPosition *position) { if (!position) { return nil; } @@ -562,14 +562,14 @@ - (void)mapView:(GMSMapView*)mapView didLongPressAtCoordinate:(CLLocationCoordin }; } -static NSDictionary* PointToJson(CGPoint point) { +static NSDictionary *PointToJson(CGPoint point) { return @{ @"x" : @(lroundf(point.x)), @"y" : @(lroundf(point.y)), }; } -static NSDictionary* GMSCoordinateBoundsToJson(GMSCoordinateBounds* bounds) { +static NSDictionary *GMSCoordinateBoundsToJson(GMSCoordinateBounds *bounds) { if (!bounds) { return nil; } @@ -579,51 +579,51 @@ - (void)mapView:(GMSMapView*)mapView didLongPressAtCoordinate:(CLLocationCoordin }; } -static float ToFloat(NSNumber* data) { return [FLTGoogleMapJsonConversions toFloat:data]; } +static float ToFloat(NSNumber *data) { return [FLTGoogleMapJsonConversions toFloat:data]; } -static CLLocationCoordinate2D ToLocation(NSArray* data) { +static CLLocationCoordinate2D ToLocation(NSArray *data) { return [FLTGoogleMapJsonConversions toLocation:data]; } -static int ToInt(NSNumber* data) { return [FLTGoogleMapJsonConversions toInt:data]; } +static int ToInt(NSNumber *data) { return [FLTGoogleMapJsonConversions toInt:data]; } -static BOOL ToBool(NSNumber* data) { return [FLTGoogleMapJsonConversions toBool:data]; } +static BOOL ToBool(NSNumber *data) { return [FLTGoogleMapJsonConversions toBool:data]; } -static CGPoint ToPoint(NSArray* data) { return [FLTGoogleMapJsonConversions toPoint:data]; } +static CGPoint ToPoint(NSArray *data) { return [FLTGoogleMapJsonConversions toPoint:data]; } -static GMSCameraPosition* ToCameraPosition(NSDictionary* data) { +static GMSCameraPosition *ToCameraPosition(NSDictionary *data) { return [GMSCameraPosition cameraWithTarget:ToLocation(data[@"target"]) zoom:ToFloat(data[@"zoom"]) bearing:ToDouble(data[@"bearing"]) viewingAngle:ToDouble(data[@"tilt"])]; } -static GMSCameraPosition* ToOptionalCameraPosition(NSDictionary* json) { +static GMSCameraPosition *ToOptionalCameraPosition(NSDictionary *json) { return json ? ToCameraPosition(json) : nil; } -static CGPoint ToCGPoint(NSDictionary* json) { +static CGPoint ToCGPoint(NSDictionary *json) { double x = ToDouble(json[@"x"]); double y = ToDouble(json[@"y"]); return CGPointMake(x, y); } -static GMSCoordinateBounds* ToBounds(NSArray* data) { +static GMSCoordinateBounds *ToBounds(NSArray *data) { return [[GMSCoordinateBounds alloc] initWithCoordinate:ToLocation(data[0]) coordinate:ToLocation(data[1])]; } -static GMSCoordinateBounds* ToOptionalBounds(NSArray* data) { +static GMSCoordinateBounds *ToOptionalBounds(NSArray *data) { return (data[0] == [NSNull null]) ? nil : ToBounds(data[0]); } -static GMSMapViewType ToMapViewType(NSNumber* json) { +static GMSMapViewType ToMapViewType(NSNumber *json) { int value = ToInt(json); return (GMSMapViewType)(value == 0 ? 5 : value); } -static GMSCameraUpdate* ToCameraUpdate(NSArray* data) { - NSString* update = data[0]; +static GMSCameraUpdate *ToCameraUpdate(NSArray *data) { + NSString *update = data[0]; if ([update isEqualToString:@"newCameraPosition"]) { return [GMSCameraUpdate setCamera:ToCameraPosition(data[1])]; } else if ([update isEqualToString:@"newLatLng"]) { @@ -650,12 +650,12 @@ static GMSMapViewType ToMapViewType(NSNumber* json) { return nil; } -static void InterpretMapOptions(NSDictionary* data, id sink) { - NSArray* cameraTargetBounds = data[@"cameraTargetBounds"]; +static void InterpretMapOptions(NSDictionary *data, id sink) { + NSArray *cameraTargetBounds = data[@"cameraTargetBounds"]; if (cameraTargetBounds) { [sink setCameraTargetBounds:ToOptionalBounds(cameraTargetBounds)]; } - NSNumber* compassEnabled = data[@"compassEnabled"]; + NSNumber *compassEnabled = data[@"compassEnabled"]; if (compassEnabled != nil) { [sink setCompassEnabled:ToBool(compassEnabled)]; } @@ -675,13 +675,13 @@ static void InterpretMapOptions(NSDictionary* data, id if (mapType) { [sink setMapType:ToMapViewType(mapType)]; } - NSArray* zoomData = data[@"minMaxZoomPreference"]; + NSArray *zoomData = data[@"minMaxZoomPreference"]; if (zoomData) { float minZoom = (zoomData[0] == [NSNull null]) ? kGMSMinZoomLevel : ToFloat(zoomData[0]); float maxZoom = (zoomData[1] == [NSNull null]) ? kGMSMaxZoomLevel : ToFloat(zoomData[1]); [sink setMinZoom:minZoom maxZoom:maxZoom]; } - NSArray* paddingData = data[@"padding"]; + NSArray *paddingData = data[@"padding"]; if (paddingData) { float top = (paddingData[0] == [NSNull null]) ? 0 : ToFloat(paddingData[0]); float left = (paddingData[1] == [NSNull null]) ? 0 : ToFloat(paddingData[1]); @@ -690,31 +690,31 @@ static void InterpretMapOptions(NSDictionary* data, id [sink setPaddingTop:top left:left bottom:bottom right:right]; } - NSNumber* rotateGesturesEnabled = data[@"rotateGesturesEnabled"]; + NSNumber *rotateGesturesEnabled = data[@"rotateGesturesEnabled"]; if (rotateGesturesEnabled != nil) { [sink setRotateGesturesEnabled:ToBool(rotateGesturesEnabled)]; } - NSNumber* scrollGesturesEnabled = data[@"scrollGesturesEnabled"]; + NSNumber *scrollGesturesEnabled = data[@"scrollGesturesEnabled"]; if (scrollGesturesEnabled != nil) { [sink setScrollGesturesEnabled:ToBool(scrollGesturesEnabled)]; } - NSNumber* tiltGesturesEnabled = data[@"tiltGesturesEnabled"]; + NSNumber *tiltGesturesEnabled = data[@"tiltGesturesEnabled"]; if (tiltGesturesEnabled != nil) { [sink setTiltGesturesEnabled:ToBool(tiltGesturesEnabled)]; } - NSNumber* trackCameraPosition = data[@"trackCameraPosition"]; + NSNumber *trackCameraPosition = data[@"trackCameraPosition"]; if (trackCameraPosition != nil) { [sink setTrackCameraPosition:ToBool(trackCameraPosition)]; } - NSNumber* zoomGesturesEnabled = data[@"zoomGesturesEnabled"]; + NSNumber *zoomGesturesEnabled = data[@"zoomGesturesEnabled"]; if (zoomGesturesEnabled != nil) { [sink setZoomGesturesEnabled:ToBool(zoomGesturesEnabled)]; } - NSNumber* myLocationEnabled = data[@"myLocationEnabled"]; + NSNumber *myLocationEnabled = data[@"myLocationEnabled"]; if (myLocationEnabled != nil) { [sink setMyLocationEnabled:ToBool(myLocationEnabled)]; } - NSNumber* myLocationButtonEnabled = data[@"myLocationButtonEnabled"]; + NSNumber *myLocationButtonEnabled = data[@"myLocationButtonEnabled"]; if (myLocationButtonEnabled != nil) { [sink setMyLocationButtonEnabled:ToBool(myLocationButtonEnabled)]; } diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapMarkerController.h b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapMarkerController.h index 56251872c4fb..8734c06fe929 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapMarkerController.h +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapMarkerController.h @@ -15,9 +15,9 @@ NS_ASSUME_NONNULL_BEGIN - (void)setConsumeTapEvents:(BOOL)consume; - (void)setDraggable:(BOOL)draggable; - (void)setFlat:(BOOL)flat; -- (void)setIcon:(UIImage*)icon; +- (void)setIcon:(UIImage *)icon; - (void)setInfoWindowAnchor:(CGPoint)anchor; -- (void)setInfoWindowTitle:(NSString*)title snippet:(NSString*)snippet; +- (void)setInfoWindowTitle:(NSString *)title snippet:(NSString *)snippet; - (void)setPosition:(CLLocationCoordinate2D)position; - (void)setRotation:(CLLocationDegrees)rotation; - (void)setVisible:(BOOL)visible; @@ -26,10 +26,10 @@ NS_ASSUME_NONNULL_BEGIN // Defines marker controllable by Flutter. @interface FLTGoogleMapMarkerController : NSObject -@property(atomic, readonly) NSString* markerId; +@property(atomic, readonly) NSString *markerId; - (instancetype)initMarkerWithPosition:(CLLocationCoordinate2D)position - markerId:(NSString*)markerId - mapView:(GMSMapView*)mapView; + markerId:(NSString *)markerId + mapView:(GMSMapView *)mapView; - (void)showInfoWindow; - (void)hideInfoWindow; - (BOOL)isInfoWindowShown; @@ -38,20 +38,20 @@ NS_ASSUME_NONNULL_BEGIN @end @interface FLTMarkersController : NSObject -- (instancetype)init:(FlutterMethodChannel*)methodChannel - mapView:(GMSMapView*)mapView - registrar:(NSObject*)registrar; -- (void)addMarkers:(NSArray*)markersToAdd; -- (void)changeMarkers:(NSArray*)markersToChange; -- (void)removeMarkerIds:(NSArray*)markerIdsToRemove; -- (BOOL)onMarkerTap:(NSString*)markerId; -- (void)onMarkerDragStart:(NSString*)markerId coordinate:(CLLocationCoordinate2D)coordinate; -- (void)onMarkerDragEnd:(NSString*)markerId coordinate:(CLLocationCoordinate2D)coordinate; -- (void)onMarkerDrag:(NSString*)markerId coordinate:(CLLocationCoordinate2D)coordinate; -- (void)onInfoWindowTap:(NSString*)markerId; -- (void)showMarkerInfoWindow:(NSString*)markerId result:(FlutterResult)result; -- (void)hideMarkerInfoWindow:(NSString*)markerId result:(FlutterResult)result; -- (void)isMarkerInfoWindowShown:(NSString*)markerId result:(FlutterResult)result; +- (instancetype)init:(FlutterMethodChannel *)methodChannel + mapView:(GMSMapView *)mapView + registrar:(NSObject *)registrar; +- (void)addMarkers:(NSArray *)markersToAdd; +- (void)changeMarkers:(NSArray *)markersToChange; +- (void)removeMarkerIds:(NSArray *)markerIdsToRemove; +- (BOOL)onMarkerTap:(NSString *)markerId; +- (void)onMarkerDragStart:(NSString *)markerId coordinate:(CLLocationCoordinate2D)coordinate; +- (void)onMarkerDragEnd:(NSString *)markerId coordinate:(CLLocationCoordinate2D)coordinate; +- (void)onMarkerDrag:(NSString *)markerId coordinate:(CLLocationCoordinate2D)coordinate; +- (void)onInfoWindowTap:(NSString *)markerId; +- (void)showMarkerInfoWindow:(NSString *)markerId result:(FlutterResult)result; +- (void)hideMarkerInfoWindow:(NSString *)markerId result:(FlutterResult)result; +- (void)isMarkerInfoWindowShown:(NSString *)markerId result:(FlutterResult)result; @end NS_ASSUME_NONNULL_END diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapMarkerController.m b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapMarkerController.m index 51ed825fa7d7..c2877e2bd78f 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapMarkerController.m +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapMarkerController.m @@ -5,17 +5,17 @@ #import "GoogleMapMarkerController.h" #import "JsonConversions.h" -static UIImage* ExtractIcon(NSObject* registrar, NSArray* icon); -static void InterpretInfoWindow(id sink, NSDictionary* data); +static UIImage *ExtractIcon(NSObject *registrar, NSArray *icon); +static void InterpretInfoWindow(id sink, NSDictionary *data); @implementation FLTGoogleMapMarkerController { - GMSMarker* _marker; - GMSMapView* _mapView; + GMSMarker *_marker; + GMSMapView *_mapView; BOOL _consumeTapEvents; } - (instancetype)initMarkerWithPosition:(CLLocationCoordinate2D)position - markerId:(NSString*)markerId - mapView:(GMSMapView*)mapView { + markerId:(NSString *)markerId + mapView:(GMSMapView *)mapView { self = [super init]; if (self) { _marker = [GMSMarker markerWithPosition:position]; @@ -61,13 +61,13 @@ - (void)setDraggable:(BOOL)draggable { - (void)setFlat:(BOOL)flat { _marker.flat = flat; } -- (void)setIcon:(UIImage*)icon { +- (void)setIcon:(UIImage *)icon { _marker.icon = icon; } - (void)setInfoWindowAnchor:(CGPoint)anchor { _marker.infoWindowAnchor = anchor; } -- (void)setInfoWindowTitle:(NSString*)title snippet:(NSString*)snippet { +- (void)setInfoWindowTitle:(NSString *)title snippet:(NSString *)snippet { _marker.title = title; _marker.snippet = snippet; } @@ -85,86 +85,86 @@ - (void)setZIndex:(int)zIndex { } @end -static double ToDouble(NSNumber* data) { return [FLTGoogleMapJsonConversions toDouble:data]; } +static double ToDouble(NSNumber *data) { return [FLTGoogleMapJsonConversions toDouble:data]; } -static float ToFloat(NSNumber* data) { return [FLTGoogleMapJsonConversions toFloat:data]; } +static float ToFloat(NSNumber *data) { return [FLTGoogleMapJsonConversions toFloat:data]; } -static CLLocationCoordinate2D ToLocation(NSArray* data) { +static CLLocationCoordinate2D ToLocation(NSArray *data) { return [FLTGoogleMapJsonConversions toLocation:data]; } -static int ToInt(NSNumber* data) { return [FLTGoogleMapJsonConversions toInt:data]; } +static int ToInt(NSNumber *data) { return [FLTGoogleMapJsonConversions toInt:data]; } -static BOOL ToBool(NSNumber* data) { return [FLTGoogleMapJsonConversions toBool:data]; } +static BOOL ToBool(NSNumber *data) { return [FLTGoogleMapJsonConversions toBool:data]; } -static CGPoint ToPoint(NSArray* data) { return [FLTGoogleMapJsonConversions toPoint:data]; } +static CGPoint ToPoint(NSArray *data) { return [FLTGoogleMapJsonConversions toPoint:data]; } -static NSArray* PositionToJson(CLLocationCoordinate2D data) { +static NSArray *PositionToJson(CLLocationCoordinate2D data) { return [FLTGoogleMapJsonConversions positionToJson:data]; } -static void InterpretMarkerOptions(NSDictionary* data, id sink, - NSObject* registrar) { - NSNumber* alpha = data[@"alpha"]; +static void InterpretMarkerOptions(NSDictionary *data, id sink, + NSObject *registrar) { + NSNumber *alpha = data[@"alpha"]; if (alpha != nil) { [sink setAlpha:ToFloat(alpha)]; } - NSArray* anchor = data[@"anchor"]; + NSArray *anchor = data[@"anchor"]; if (anchor) { [sink setAnchor:ToPoint(anchor)]; } - NSNumber* draggable = data[@"draggable"]; + NSNumber *draggable = data[@"draggable"]; if (draggable != nil) { [sink setDraggable:ToBool(draggable)]; } - NSArray* icon = data[@"icon"]; + NSArray *icon = data[@"icon"]; if (icon) { - UIImage* image = ExtractIcon(registrar, icon); + UIImage *image = ExtractIcon(registrar, icon); [sink setIcon:image]; } - NSNumber* flat = data[@"flat"]; + NSNumber *flat = data[@"flat"]; if (flat != nil) { [sink setFlat:ToBool(flat)]; } - NSNumber* consumeTapEvents = data[@"consumeTapEvents"]; + NSNumber *consumeTapEvents = data[@"consumeTapEvents"]; if (consumeTapEvents != nil) { [sink setConsumeTapEvents:ToBool(consumeTapEvents)]; } InterpretInfoWindow(sink, data); - NSArray* position = data[@"position"]; + NSArray *position = data[@"position"]; if (position) { [sink setPosition:ToLocation(position)]; } - NSNumber* rotation = data[@"rotation"]; + NSNumber *rotation = data[@"rotation"]; if (rotation != nil) { [sink setRotation:ToDouble(rotation)]; } - NSNumber* visible = data[@"visible"]; + NSNumber *visible = data[@"visible"]; if (visible != nil) { [sink setVisible:ToBool(visible)]; } - NSNumber* zIndex = data[@"zIndex"]; + NSNumber *zIndex = data[@"zIndex"]; if (zIndex != nil) { [sink setZIndex:ToInt(zIndex)]; } } -static void InterpretInfoWindow(id sink, NSDictionary* data) { - NSDictionary* infoWindow = data[@"infoWindow"]; +static void InterpretInfoWindow(id sink, NSDictionary *data) { + NSDictionary *infoWindow = data[@"infoWindow"]; if (infoWindow) { - NSString* title = infoWindow[@"title"]; - NSString* snippet = infoWindow[@"snippet"]; + NSString *title = infoWindow[@"title"]; + NSString *snippet = infoWindow[@"snippet"]; if (title) { [sink setInfoWindowTitle:title snippet:snippet]; } - NSArray* infoWindowAnchor = infoWindow[@"infoWindowAnchor"]; + NSArray *infoWindowAnchor = infoWindow[@"infoWindowAnchor"]; if (infoWindowAnchor) { [sink setInfoWindowAnchor:ToPoint(infoWindowAnchor)]; } } } -static UIImage* scaleImage(UIImage* image, NSNumber* scaleParam) { +static UIImage *scaleImage(UIImage *image, NSNumber *scaleParam) { double scale = 1.0; if ([scaleParam isKindOfClass:[NSNumber class]]) { scale = scaleParam.doubleValue; @@ -177,8 +177,8 @@ static void InterpretInfoWindow(id sink, NSDictio return image; } -static UIImage* ExtractIcon(NSObject* registrar, NSArray* iconData) { - UIImage* image; +static UIImage *ExtractIcon(NSObject *registrar, NSArray *iconData) { + UIImage *image; if ([iconData.firstObject isEqualToString:@"defaultMarker"]) { CGFloat hue = (iconData.count == 1) ? 0.0f : ToDouble(iconData[1]); image = [GMSMarker markerImageWithColor:[UIColor colorWithHue:hue / 360.0 @@ -195,13 +195,13 @@ static void InterpretInfoWindow(id sink, NSDictio } else if ([iconData.firstObject isEqualToString:@"fromAssetImage"]) { if (iconData.count == 3) { image = [UIImage imageNamed:[registrar lookupKeyForAsset:iconData[1]]]; - NSNumber* scaleParam = iconData[2]; + NSNumber *scaleParam = iconData[2]; image = scaleImage(image, scaleParam); } else { - NSString* error = + NSString *error = [NSString stringWithFormat:@"'fromAssetImage' should have exactly 3 arguments. Got: %lu", (unsigned long)iconData.count]; - NSException* exception = [NSException exceptionWithName:@"InvalidBitmapDescriptor" + NSException *exception = [NSException exceptionWithName:@"InvalidBitmapDescriptor" reason:error userInfo:nil]; @throw exception; @@ -209,19 +209,19 @@ static void InterpretInfoWindow(id sink, NSDictio } else if ([iconData[0] isEqualToString:@"fromBytes"]) { if (iconData.count == 2) { @try { - FlutterStandardTypedData* byteData = iconData[1]; + FlutterStandardTypedData *byteData = iconData[1]; CGFloat screenScale = [[UIScreen mainScreen] scale]; image = [UIImage imageWithData:[byteData data] scale:screenScale]; - } @catch (NSException* exception) { + } @catch (NSException *exception) { @throw [NSException exceptionWithName:@"InvalidByteDescriptor" reason:@"Unable to interpret bytes as a valid image." userInfo:nil]; } } else { - NSString* error = [NSString + NSString *error = [NSString stringWithFormat:@"fromBytes should have exactly one argument, the bytes. Got: %lu", (unsigned long)iconData.count]; - NSException* exception = [NSException exceptionWithName:@"InvalidByteDescriptor" + NSException *exception = [NSException exceptionWithName:@"InvalidByteDescriptor" reason:error userInfo:nil]; @throw exception; @@ -232,14 +232,14 @@ static void InterpretInfoWindow(id sink, NSDictio } @implementation FLTMarkersController { - NSMutableDictionary* _markerIdToController; - FlutterMethodChannel* _methodChannel; - NSObject* _registrar; - GMSMapView* _mapView; -} -- (instancetype)init:(FlutterMethodChannel*)methodChannel - mapView:(GMSMapView*)mapView - registrar:(NSObject*)registrar { + NSMutableDictionary *_markerIdToController; + FlutterMethodChannel *_methodChannel; + NSObject *_registrar; + GMSMapView *_mapView; +} +- (instancetype)init:(FlutterMethodChannel *)methodChannel + mapView:(GMSMapView *)mapView + registrar:(NSObject *)registrar { self = [super init]; if (self) { _methodChannel = methodChannel; @@ -249,11 +249,11 @@ - (instancetype)init:(FlutterMethodChannel*)methodChannel } return self; } -- (void)addMarkers:(NSArray*)markersToAdd { - for (NSDictionary* marker in markersToAdd) { +- (void)addMarkers:(NSArray *)markersToAdd { + for (NSDictionary *marker in markersToAdd) { CLLocationCoordinate2D position = [FLTMarkersController getPosition:marker]; - NSString* markerId = [FLTMarkersController getMarkerId:marker]; - FLTGoogleMapMarkerController* controller = + NSString *markerId = [FLTMarkersController getMarkerId:marker]; + FLTGoogleMapMarkerController *controller = [[FLTGoogleMapMarkerController alloc] initMarkerWithPosition:position markerId:markerId mapView:_mapView]; @@ -261,22 +261,22 @@ - (void)addMarkers:(NSArray*)markersToAdd { _markerIdToController[markerId] = controller; } } -- (void)changeMarkers:(NSArray*)markersToChange { - for (NSDictionary* marker in markersToChange) { - NSString* markerId = [FLTMarkersController getMarkerId:marker]; - FLTGoogleMapMarkerController* controller = _markerIdToController[markerId]; +- (void)changeMarkers:(NSArray *)markersToChange { + for (NSDictionary *marker in markersToChange) { + NSString *markerId = [FLTMarkersController getMarkerId:marker]; + FLTGoogleMapMarkerController *controller = _markerIdToController[markerId]; if (!controller) { continue; } InterpretMarkerOptions(marker, controller, _registrar); } } -- (void)removeMarkerIds:(NSArray*)markerIdsToRemove { - for (NSString* markerId in markerIdsToRemove) { +- (void)removeMarkerIds:(NSArray *)markerIdsToRemove { + for (NSString *markerId in markerIdsToRemove) { if (!markerId) { continue; } - FLTGoogleMapMarkerController* controller = _markerIdToController[markerId]; + FLTGoogleMapMarkerController *controller = _markerIdToController[markerId]; if (!controller) { continue; } @@ -284,57 +284,57 @@ - (void)removeMarkerIds:(NSArray*)markerIdsToRemove { [_markerIdToController removeObjectForKey:markerId]; } } -- (BOOL)onMarkerTap:(NSString*)markerId { +- (BOOL)onMarkerTap:(NSString *)markerId { if (!markerId) { return NO; } - FLTGoogleMapMarkerController* controller = _markerIdToController[markerId]; + FLTGoogleMapMarkerController *controller = _markerIdToController[markerId]; if (!controller) { return NO; } [_methodChannel invokeMethod:@"marker#onTap" arguments:@{@"markerId" : markerId}]; return controller.consumeTapEvents; } -- (void)onMarkerDragStart:(NSString*)markerId coordinate:(CLLocationCoordinate2D)coordinate { +- (void)onMarkerDragStart:(NSString *)markerId coordinate:(CLLocationCoordinate2D)coordinate { if (!markerId) { return; } - FLTGoogleMapMarkerController* controller = _markerIdToController[markerId]; + FLTGoogleMapMarkerController *controller = _markerIdToController[markerId]; if (!controller) { return; } [_methodChannel invokeMethod:@"marker#onDragStart" arguments:@{@"markerId" : markerId, @"position" : PositionToJson(coordinate)}]; } -- (void)onMarkerDrag:(NSString*)markerId coordinate:(CLLocationCoordinate2D)coordinate { +- (void)onMarkerDrag:(NSString *)markerId coordinate:(CLLocationCoordinate2D)coordinate { if (!markerId) { return; } - FLTGoogleMapMarkerController* controller = _markerIdToController[markerId]; + FLTGoogleMapMarkerController *controller = _markerIdToController[markerId]; if (!controller) { return; } [_methodChannel invokeMethod:@"marker#onDrag" arguments:@{@"markerId" : markerId, @"position" : PositionToJson(coordinate)}]; } -- (void)onMarkerDragEnd:(NSString*)markerId coordinate:(CLLocationCoordinate2D)coordinate { +- (void)onMarkerDragEnd:(NSString *)markerId coordinate:(CLLocationCoordinate2D)coordinate { if (!markerId) { return; } - FLTGoogleMapMarkerController* controller = _markerIdToController[markerId]; + FLTGoogleMapMarkerController *controller = _markerIdToController[markerId]; if (!controller) { return; } [_methodChannel invokeMethod:@"marker#onDragEnd" arguments:@{@"markerId" : markerId, @"position" : PositionToJson(coordinate)}]; } -- (void)onInfoWindowTap:(NSString*)markerId { +- (void)onInfoWindowTap:(NSString *)markerId { if (markerId && _markerIdToController[markerId]) { [_methodChannel invokeMethod:@"infoWindow#onTap" arguments:@{@"markerId" : markerId}]; } } -- (void)showMarkerInfoWindow:(NSString*)markerId result:(FlutterResult)result { - FLTGoogleMapMarkerController* controller = _markerIdToController[markerId]; +- (void)showMarkerInfoWindow:(NSString *)markerId result:(FlutterResult)result { + FLTGoogleMapMarkerController *controller = _markerIdToController[markerId]; if (controller) { [controller showInfoWindow]; result(nil); @@ -344,8 +344,8 @@ - (void)showMarkerInfoWindow:(NSString*)markerId result:(FlutterResult)result { details:nil]); } } -- (void)hideMarkerInfoWindow:(NSString*)markerId result:(FlutterResult)result { - FLTGoogleMapMarkerController* controller = _markerIdToController[markerId]; +- (void)hideMarkerInfoWindow:(NSString *)markerId result:(FlutterResult)result { + FLTGoogleMapMarkerController *controller = _markerIdToController[markerId]; if (controller) { [controller hideInfoWindow]; result(nil); @@ -355,8 +355,8 @@ - (void)hideMarkerInfoWindow:(NSString*)markerId result:(FlutterResult)result { details:nil]); } } -- (void)isMarkerInfoWindowShown:(NSString*)markerId result:(FlutterResult)result { - FLTGoogleMapMarkerController* controller = _markerIdToController[markerId]; +- (void)isMarkerInfoWindowShown:(NSString *)markerId result:(FlutterResult)result { + FLTGoogleMapMarkerController *controller = _markerIdToController[markerId]; if (controller) { result(@([controller isInfoWindowShown])); } else { @@ -366,11 +366,11 @@ - (void)isMarkerInfoWindowShown:(NSString*)markerId result:(FlutterResult)result } } -+ (CLLocationCoordinate2D)getPosition:(NSDictionary*)marker { - NSArray* position = marker[@"position"]; ++ (CLLocationCoordinate2D)getPosition:(NSDictionary *)marker { + NSArray *position = marker[@"position"]; return ToLocation(position); } -+ (NSString*)getMarkerId:(NSDictionary*)marker { ++ (NSString *)getMarkerId:(NSDictionary *)marker { return marker[@"markerId"]; } @end diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolygonController.h b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolygonController.h index b123ac0a3d68..bdc5dd4bf850 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolygonController.h +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolygonController.h @@ -9,30 +9,30 @@ @protocol FLTGoogleMapPolygonOptionsSink - (void)setConsumeTapEvents:(BOOL)consume; - (void)setVisible:(BOOL)visible; -- (void)setFillColor:(UIColor*)color; -- (void)setStrokeColor:(UIColor*)color; +- (void)setFillColor:(UIColor *)color; +- (void)setStrokeColor:(UIColor *)color; - (void)setStrokeWidth:(CGFloat)width; -- (void)setPoints:(NSArray*)points; -- (void)setHoles:(NSArray*>*)holes; +- (void)setPoints:(NSArray *)points; +- (void)setHoles:(NSArray *> *)holes; - (void)setZIndex:(int)zIndex; @end // Defines polygon controllable by Flutter. @interface FLTGoogleMapPolygonController : NSObject -@property(atomic, readonly) NSString* polygonId; -- (instancetype)initPolygonWithPath:(GMSMutablePath*)path - polygonId:(NSString*)polygonId - mapView:(GMSMapView*)mapView; +@property(atomic, readonly) NSString *polygonId; +- (instancetype)initPolygonWithPath:(GMSMutablePath *)path + polygonId:(NSString *)polygonId + mapView:(GMSMapView *)mapView; - (void)removePolygon; @end @interface FLTPolygonsController : NSObject -- (instancetype)init:(FlutterMethodChannel*)methodChannel - mapView:(GMSMapView*)mapView - registrar:(NSObject*)registrar; -- (void)addPolygons:(NSArray*)polygonsToAdd; -- (void)changePolygons:(NSArray*)polygonsToChange; -- (void)removePolygonIds:(NSArray*)polygonIdsToRemove; -- (void)onPolygonTap:(NSString*)polygonId; -- (bool)hasPolygonWithId:(NSString*)polygonId; +- (instancetype)init:(FlutterMethodChannel *)methodChannel + mapView:(GMSMapView *)mapView + registrar:(NSObject *)registrar; +- (void)addPolygons:(NSArray *)polygonsToAdd; +- (void)changePolygons:(NSArray *)polygonsToChange; +- (void)removePolygonIds:(NSArray *)polygonIdsToRemove; +- (void)onPolygonTap:(NSString *)polygonId; +- (bool)hasPolygonWithId:(NSString *)polygonId; @end diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolygonController.m b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolygonController.m index 5ad8d4d3bc0e..649ba98bca13 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolygonController.m +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolygonController.m @@ -6,12 +6,12 @@ #import "JsonConversions.h" @implementation FLTGoogleMapPolygonController { - GMSPolygon* _polygon; - GMSMapView* _mapView; + GMSPolygon *_polygon; + GMSMapView *_mapView; } -- (instancetype)initPolygonWithPath:(GMSMutablePath*)path - polygonId:(NSString*)polygonId - mapView:(GMSMapView*)mapView { +- (instancetype)initPolygonWithPath:(GMSMutablePath *)path + polygonId:(NSString *)polygonId + mapView:(GMSMapView *)mapView { self = [super init]; if (self) { _polygon = [GMSPolygon polygonWithPath:path]; @@ -37,20 +37,20 @@ - (void)setVisible:(BOOL)visible { - (void)setZIndex:(int)zIndex { _polygon.zIndex = zIndex; } -- (void)setPoints:(NSArray*)points { - GMSMutablePath* path = [GMSMutablePath path]; +- (void)setPoints:(NSArray *)points { + GMSMutablePath *path = [GMSMutablePath path]; - for (CLLocation* location in points) { + for (CLLocation *location in points) { [path addCoordinate:location.coordinate]; } _polygon.path = path; } -- (void)setHoles:(NSArray*>*)rawHoles { - NSMutableArray* holes = [[NSMutableArray alloc] init]; +- (void)setHoles:(NSArray *> *)rawHoles { + NSMutableArray *holes = [[NSMutableArray alloc] init]; - for (NSArray* points in rawHoles) { - GMSMutablePath* path = [GMSMutablePath path]; - for (CLLocation* location in points) { + for (NSArray *points in rawHoles) { + GMSMutablePath *path = [GMSMutablePath path]; + for (CLLocation *location in points) { [path addCoordinate:location.coordinate]; } [holes addObject:path]; @@ -59,10 +59,10 @@ - (void)setHoles:(NSArray*>*)rawHoles { _polygon.holes = holes; } -- (void)setFillColor:(UIColor*)color { +- (void)setFillColor:(UIColor *)color { _polygon.fillColor = color; } -- (void)setStrokeColor:(UIColor*)color { +- (void)setStrokeColor:(UIColor *)color { _polygon.strokeColor = color; } - (void)setStrokeWidth:(CGFloat)width { @@ -70,72 +70,72 @@ - (void)setStrokeWidth:(CGFloat)width { } @end -static int ToInt(NSNumber* data) { return [FLTGoogleMapJsonConversions toInt:data]; } +static int ToInt(NSNumber *data) { return [FLTGoogleMapJsonConversions toInt:data]; } -static BOOL ToBool(NSNumber* data) { return [FLTGoogleMapJsonConversions toBool:data]; } +static BOOL ToBool(NSNumber *data) { return [FLTGoogleMapJsonConversions toBool:data]; } -static NSArray* ToPoints(NSArray* data) { +static NSArray *ToPoints(NSArray *data) { return [FLTGoogleMapJsonConversions toPoints:data]; } -static NSArray*>* ToHoles(NSArray* data) { +static NSArray *> *ToHoles(NSArray *data) { return [FLTGoogleMapJsonConversions toHoles:data]; } -static UIColor* ToColor(NSNumber* data) { return [FLTGoogleMapJsonConversions toColor:data]; } +static UIColor *ToColor(NSNumber *data) { return [FLTGoogleMapJsonConversions toColor:data]; } -static void InterpretPolygonOptions(NSDictionary* data, id sink, - NSObject* registrar) { - NSNumber* consumeTapEvents = data[@"consumeTapEvents"]; +static void InterpretPolygonOptions(NSDictionary *data, id sink, + NSObject *registrar) { + NSNumber *consumeTapEvents = data[@"consumeTapEvents"]; if (consumeTapEvents != nil) { [sink setConsumeTapEvents:ToBool(consumeTapEvents)]; } - NSNumber* visible = data[@"visible"]; + NSNumber *visible = data[@"visible"]; if (visible != nil) { [sink setVisible:ToBool(visible)]; } - NSNumber* zIndex = data[@"zIndex"]; + NSNumber *zIndex = data[@"zIndex"]; if (zIndex != nil) { [sink setZIndex:ToInt(zIndex)]; } - NSArray* points = data[@"points"]; + NSArray *points = data[@"points"]; if (points) { [sink setPoints:ToPoints(points)]; } - NSArray* holes = data[@"holes"]; + NSArray *holes = data[@"holes"]; if (holes) { [sink setHoles:ToHoles(holes)]; } - NSNumber* fillColor = data[@"fillColor"]; + NSNumber *fillColor = data[@"fillColor"]; if (fillColor != nil) { [sink setFillColor:ToColor(fillColor)]; } - NSNumber* strokeColor = data[@"strokeColor"]; + NSNumber *strokeColor = data[@"strokeColor"]; if (strokeColor != nil) { [sink setStrokeColor:ToColor(strokeColor)]; } - NSNumber* strokeWidth = data[@"strokeWidth"]; + NSNumber *strokeWidth = data[@"strokeWidth"]; if (strokeWidth != nil) { [sink setStrokeWidth:ToInt(strokeWidth)]; } } @implementation FLTPolygonsController { - NSMutableDictionary* _polygonIdToController; - FlutterMethodChannel* _methodChannel; - NSObject* _registrar; - GMSMapView* _mapView; -} -- (instancetype)init:(FlutterMethodChannel*)methodChannel - mapView:(GMSMapView*)mapView - registrar:(NSObject*)registrar { + NSMutableDictionary *_polygonIdToController; + FlutterMethodChannel *_methodChannel; + NSObject *_registrar; + GMSMapView *_mapView; +} +- (instancetype)init:(FlutterMethodChannel *)methodChannel + mapView:(GMSMapView *)mapView + registrar:(NSObject *)registrar { self = [super init]; if (self) { _methodChannel = methodChannel; @@ -145,11 +145,11 @@ - (instancetype)init:(FlutterMethodChannel*)methodChannel } return self; } -- (void)addPolygons:(NSArray*)polygonsToAdd { - for (NSDictionary* polygon in polygonsToAdd) { - GMSMutablePath* path = [FLTPolygonsController getPath:polygon]; - NSString* polygonId = [FLTPolygonsController getPolygonId:polygon]; - FLTGoogleMapPolygonController* controller = +- (void)addPolygons:(NSArray *)polygonsToAdd { + for (NSDictionary *polygon in polygonsToAdd) { + GMSMutablePath *path = [FLTPolygonsController getPath:polygon]; + NSString *polygonId = [FLTPolygonsController getPolygonId:polygon]; + FLTGoogleMapPolygonController *controller = [[FLTGoogleMapPolygonController alloc] initPolygonWithPath:path polygonId:polygonId mapView:_mapView]; @@ -157,22 +157,22 @@ - (void)addPolygons:(NSArray*)polygonsToAdd { _polygonIdToController[polygonId] = controller; } } -- (void)changePolygons:(NSArray*)polygonsToChange { - for (NSDictionary* polygon in polygonsToChange) { - NSString* polygonId = [FLTPolygonsController getPolygonId:polygon]; - FLTGoogleMapPolygonController* controller = _polygonIdToController[polygonId]; +- (void)changePolygons:(NSArray *)polygonsToChange { + for (NSDictionary *polygon in polygonsToChange) { + NSString *polygonId = [FLTPolygonsController getPolygonId:polygon]; + FLTGoogleMapPolygonController *controller = _polygonIdToController[polygonId]; if (!controller) { continue; } InterpretPolygonOptions(polygon, controller, _registrar); } } -- (void)removePolygonIds:(NSArray*)polygonIdsToRemove { - for (NSString* polygonId in polygonIdsToRemove) { +- (void)removePolygonIds:(NSArray *)polygonIdsToRemove { + for (NSString *polygonId in polygonIdsToRemove) { if (!polygonId) { continue; } - FLTGoogleMapPolygonController* controller = _polygonIdToController[polygonId]; + FLTGoogleMapPolygonController *controller = _polygonIdToController[polygonId]; if (!controller) { continue; } @@ -180,32 +180,32 @@ - (void)removePolygonIds:(NSArray*)polygonIdsToRemove { [_polygonIdToController removeObjectForKey:polygonId]; } } -- (void)onPolygonTap:(NSString*)polygonId { +- (void)onPolygonTap:(NSString *)polygonId { if (!polygonId) { return; } - FLTGoogleMapPolygonController* controller = _polygonIdToController[polygonId]; + FLTGoogleMapPolygonController *controller = _polygonIdToController[polygonId]; if (!controller) { return; } [_methodChannel invokeMethod:@"polygon#onTap" arguments:@{@"polygonId" : polygonId}]; } -- (bool)hasPolygonWithId:(NSString*)polygonId { +- (bool)hasPolygonWithId:(NSString *)polygonId { if (!polygonId) { return false; } return _polygonIdToController[polygonId] != nil; } -+ (GMSMutablePath*)getPath:(NSDictionary*)polygon { - NSArray* pointArray = polygon[@"points"]; - NSArray* points = ToPoints(pointArray); - GMSMutablePath* path = [GMSMutablePath path]; - for (CLLocation* location in points) { ++ (GMSMutablePath *)getPath:(NSDictionary *)polygon { + NSArray *pointArray = polygon[@"points"]; + NSArray *points = ToPoints(pointArray); + GMSMutablePath *path = [GMSMutablePath path]; + for (CLLocation *location in points) { [path addCoordinate:location.coordinate]; } return path; } -+ (NSString*)getPolygonId:(NSDictionary*)polygon { ++ (NSString *)getPolygonId:(NSDictionary *)polygon { return polygon[@"polygonId"]; } @end diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolylineController.h b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolylineController.h index f7fafc2f065f..0e614eeb62ab 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolylineController.h +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolylineController.h @@ -9,29 +9,29 @@ @protocol FLTGoogleMapPolylineOptionsSink - (void)setConsumeTapEvents:(BOOL)consume; - (void)setVisible:(BOOL)visible; -- (void)setColor:(UIColor*)color; +- (void)setColor:(UIColor *)color; - (void)setStrokeWidth:(CGFloat)width; -- (void)setPoints:(NSArray*)points; +- (void)setPoints:(NSArray *)points; - (void)setZIndex:(int)zIndex; - (void)setGeodesic:(BOOL)isGeodesic; @end // Defines polyline controllable by Flutter. @interface FLTGoogleMapPolylineController : NSObject -@property(atomic, readonly) NSString* polylineId; -- (instancetype)initPolylineWithPath:(GMSMutablePath*)path - polylineId:(NSString*)polylineId - mapView:(GMSMapView*)mapView; +@property(atomic, readonly) NSString *polylineId; +- (instancetype)initPolylineWithPath:(GMSMutablePath *)path + polylineId:(NSString *)polylineId + mapView:(GMSMapView *)mapView; - (void)removePolyline; @end @interface FLTPolylinesController : NSObject -- (instancetype)init:(FlutterMethodChannel*)methodChannel - mapView:(GMSMapView*)mapView - registrar:(NSObject*)registrar; -- (void)addPolylines:(NSArray*)polylinesToAdd; -- (void)changePolylines:(NSArray*)polylinesToChange; -- (void)removePolylineIds:(NSArray*)polylineIdsToRemove; -- (void)onPolylineTap:(NSString*)polylineId; -- (bool)hasPolylineWithId:(NSString*)polylineId; +- (instancetype)init:(FlutterMethodChannel *)methodChannel + mapView:(GMSMapView *)mapView + registrar:(NSObject *)registrar; +- (void)addPolylines:(NSArray *)polylinesToAdd; +- (void)changePolylines:(NSArray *)polylinesToChange; +- (void)removePolylineIds:(NSArray *)polylineIdsToRemove; +- (void)onPolylineTap:(NSString *)polylineId; +- (bool)hasPolylineWithId:(NSString *)polylineId; @end diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolylineController.m b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolylineController.m index 8c70d2c161ba..f366051b4af2 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolylineController.m +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolylineController.m @@ -6,12 +6,12 @@ #import "JsonConversions.h" @implementation FLTGoogleMapPolylineController { - GMSPolyline* _polyline; - GMSMapView* _mapView; + GMSPolyline *_polyline; + GMSMapView *_mapView; } -- (instancetype)initPolylineWithPath:(GMSMutablePath*)path - polylineId:(NSString*)polylineId - mapView:(GMSMapView*)mapView { +- (instancetype)initPolylineWithPath:(GMSMutablePath *)path + polylineId:(NSString *)polylineId + mapView:(GMSMapView *)mapView { self = [super init]; if (self) { _polyline = [GMSPolyline polylineWithPath:path]; @@ -37,16 +37,16 @@ - (void)setVisible:(BOOL)visible { - (void)setZIndex:(int)zIndex { _polyline.zIndex = zIndex; } -- (void)setPoints:(NSArray*)points { - GMSMutablePath* path = [GMSMutablePath path]; +- (void)setPoints:(NSArray *)points { + GMSMutablePath *path = [GMSMutablePath path]; - for (CLLocation* location in points) { + for (CLLocation *location in points) { [path addCoordinate:location.coordinate]; } _polyline.path = path; } -- (void)setColor:(UIColor*)color { +- (void)setColor:(UIColor *)color { _polyline.strokeColor = color; } - (void)setStrokeWidth:(CGFloat)width { @@ -58,63 +58,63 @@ - (void)setGeodesic:(BOOL)isGeodesic { } @end -static int ToInt(NSNumber* data) { return [FLTGoogleMapJsonConversions toInt:data]; } +static int ToInt(NSNumber *data) { return [FLTGoogleMapJsonConversions toInt:data]; } -static BOOL ToBool(NSNumber* data) { return [FLTGoogleMapJsonConversions toBool:data]; } +static BOOL ToBool(NSNumber *data) { return [FLTGoogleMapJsonConversions toBool:data]; } -static NSArray* ToPoints(NSArray* data) { +static NSArray *ToPoints(NSArray *data) { return [FLTGoogleMapJsonConversions toPoints:data]; } -static UIColor* ToColor(NSNumber* data) { return [FLTGoogleMapJsonConversions toColor:data]; } +static UIColor *ToColor(NSNumber *data) { return [FLTGoogleMapJsonConversions toColor:data]; } -static void InterpretPolylineOptions(NSDictionary* data, id sink, - NSObject* registrar) { - NSNumber* consumeTapEvents = data[@"consumeTapEvents"]; +static void InterpretPolylineOptions(NSDictionary *data, id sink, + NSObject *registrar) { + NSNumber *consumeTapEvents = data[@"consumeTapEvents"]; if (consumeTapEvents != nil) { [sink setConsumeTapEvents:ToBool(consumeTapEvents)]; } - NSNumber* visible = data[@"visible"]; + NSNumber *visible = data[@"visible"]; if (visible != nil) { [sink setVisible:ToBool(visible)]; } - NSNumber* zIndex = data[@"zIndex"]; + NSNumber *zIndex = data[@"zIndex"]; if (zIndex != nil) { [sink setZIndex:ToInt(zIndex)]; } - NSArray* points = data[@"points"]; + NSArray *points = data[@"points"]; if (points) { [sink setPoints:ToPoints(points)]; } - NSNumber* strokeColor = data[@"color"]; + NSNumber *strokeColor = data[@"color"]; if (strokeColor != nil) { [sink setColor:ToColor(strokeColor)]; } - NSNumber* strokeWidth = data[@"width"]; + NSNumber *strokeWidth = data[@"width"]; if (strokeWidth != nil) { [sink setStrokeWidth:ToInt(strokeWidth)]; } - NSNumber* geodesic = data[@"geodesic"]; + NSNumber *geodesic = data[@"geodesic"]; if (geodesic != nil) { [sink setGeodesic:geodesic.boolValue]; } } @implementation FLTPolylinesController { - NSMutableDictionary* _polylineIdToController; - FlutterMethodChannel* _methodChannel; - NSObject* _registrar; - GMSMapView* _mapView; -} -- (instancetype)init:(FlutterMethodChannel*)methodChannel - mapView:(GMSMapView*)mapView - registrar:(NSObject*)registrar { + NSMutableDictionary *_polylineIdToController; + FlutterMethodChannel *_methodChannel; + NSObject *_registrar; + GMSMapView *_mapView; +} +- (instancetype)init:(FlutterMethodChannel *)methodChannel + mapView:(GMSMapView *)mapView + registrar:(NSObject *)registrar { self = [super init]; if (self) { _methodChannel = methodChannel; @@ -124,11 +124,11 @@ - (instancetype)init:(FlutterMethodChannel*)methodChannel } return self; } -- (void)addPolylines:(NSArray*)polylinesToAdd { - for (NSDictionary* polyline in polylinesToAdd) { - GMSMutablePath* path = [FLTPolylinesController getPath:polyline]; - NSString* polylineId = [FLTPolylinesController getPolylineId:polyline]; - FLTGoogleMapPolylineController* controller = +- (void)addPolylines:(NSArray *)polylinesToAdd { + for (NSDictionary *polyline in polylinesToAdd) { + GMSMutablePath *path = [FLTPolylinesController getPath:polyline]; + NSString *polylineId = [FLTPolylinesController getPolylineId:polyline]; + FLTGoogleMapPolylineController *controller = [[FLTGoogleMapPolylineController alloc] initPolylineWithPath:path polylineId:polylineId mapView:_mapView]; @@ -136,22 +136,22 @@ - (void)addPolylines:(NSArray*)polylinesToAdd { _polylineIdToController[polylineId] = controller; } } -- (void)changePolylines:(NSArray*)polylinesToChange { - for (NSDictionary* polyline in polylinesToChange) { - NSString* polylineId = [FLTPolylinesController getPolylineId:polyline]; - FLTGoogleMapPolylineController* controller = _polylineIdToController[polylineId]; +- (void)changePolylines:(NSArray *)polylinesToChange { + for (NSDictionary *polyline in polylinesToChange) { + NSString *polylineId = [FLTPolylinesController getPolylineId:polyline]; + FLTGoogleMapPolylineController *controller = _polylineIdToController[polylineId]; if (!controller) { continue; } InterpretPolylineOptions(polyline, controller, _registrar); } } -- (void)removePolylineIds:(NSArray*)polylineIdsToRemove { - for (NSString* polylineId in polylineIdsToRemove) { +- (void)removePolylineIds:(NSArray *)polylineIdsToRemove { + for (NSString *polylineId in polylineIdsToRemove) { if (!polylineId) { continue; } - FLTGoogleMapPolylineController* controller = _polylineIdToController[polylineId]; + FLTGoogleMapPolylineController *controller = _polylineIdToController[polylineId]; if (!controller) { continue; } @@ -159,32 +159,32 @@ - (void)removePolylineIds:(NSArray*)polylineIdsToRemove { [_polylineIdToController removeObjectForKey:polylineId]; } } -- (void)onPolylineTap:(NSString*)polylineId { +- (void)onPolylineTap:(NSString *)polylineId { if (!polylineId) { return; } - FLTGoogleMapPolylineController* controller = _polylineIdToController[polylineId]; + FLTGoogleMapPolylineController *controller = _polylineIdToController[polylineId]; if (!controller) { return; } [_methodChannel invokeMethod:@"polyline#onTap" arguments:@{@"polylineId" : polylineId}]; } -- (bool)hasPolylineWithId:(NSString*)polylineId { +- (bool)hasPolylineWithId:(NSString *)polylineId { if (!polylineId) { return false; } return _polylineIdToController[polylineId] != nil; } -+ (GMSMutablePath*)getPath:(NSDictionary*)polyline { - NSArray* pointArray = polyline[@"points"]; - NSArray* points = ToPoints(pointArray); - GMSMutablePath* path = [GMSMutablePath path]; - for (CLLocation* location in points) { ++ (GMSMutablePath *)getPath:(NSDictionary *)polyline { + NSArray *pointArray = polyline[@"points"]; + NSArray *points = ToPoints(pointArray); + GMSMutablePath *path = [GMSMutablePath path]; + for (CLLocation *location in points) { [path addCoordinate:location.coordinate]; } return path; } -+ (NSString*)getPolylineId:(NSDictionary*)polyline { ++ (NSString *)getPolylineId:(NSDictionary *)polyline { return polyline[@"polylineId"]; } @end diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/JsonConversions.h b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/JsonConversions.h index 6ede4dbaf8b4..c0f673ecd025 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/JsonConversions.h +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/JsonConversions.h @@ -6,14 +6,14 @@ #import @interface FLTGoogleMapJsonConversions : NSObject -+ (bool)toBool:(NSNumber*)data; -+ (int)toInt:(NSNumber*)data; -+ (double)toDouble:(NSNumber*)data; -+ (float)toFloat:(NSNumber*)data; -+ (CLLocationCoordinate2D)toLocation:(NSArray*)data; -+ (CGPoint)toPoint:(NSArray*)data; -+ (NSArray*)positionToJson:(CLLocationCoordinate2D)position; -+ (UIColor*)toColor:(NSNumber*)data; -+ (NSArray*)toPoints:(NSArray*)data; -+ (NSArray*>*)toHoles:(NSArray*)data; ++ (bool)toBool:(NSNumber *)data; ++ (int)toInt:(NSNumber *)data; ++ (double)toDouble:(NSNumber *)data; ++ (float)toFloat:(NSNumber *)data; ++ (CLLocationCoordinate2D)toLocation:(NSArray *)data; ++ (CGPoint)toPoint:(NSArray *)data; ++ (NSArray *)positionToJson:(CLLocationCoordinate2D)position; ++ (UIColor *)toColor:(NSNumber *)data; ++ (NSArray *)toPoints:(NSArray *)data; ++ (NSArray *> *)toHoles:(NSArray *)data; @end diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/JsonConversions.m b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/JsonConversions.m index 592d7e825b38..0e88d4707489 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/JsonConversions.m +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/JsonConversions.m @@ -6,37 +6,37 @@ @implementation FLTGoogleMapJsonConversions -+ (bool)toBool:(NSNumber*)data { ++ (bool)toBool:(NSNumber *)data { return data.boolValue; } -+ (int)toInt:(NSNumber*)data { ++ (int)toInt:(NSNumber *)data { return data.intValue; } -+ (double)toDouble:(NSNumber*)data { ++ (double)toDouble:(NSNumber *)data { return data.doubleValue; } -+ (float)toFloat:(NSNumber*)data { ++ (float)toFloat:(NSNumber *)data { return data.floatValue; } -+ (CLLocationCoordinate2D)toLocation:(NSArray*)data { ++ (CLLocationCoordinate2D)toLocation:(NSArray *)data { return CLLocationCoordinate2DMake([FLTGoogleMapJsonConversions toDouble:data[0]], [FLTGoogleMapJsonConversions toDouble:data[1]]); } -+ (CGPoint)toPoint:(NSArray*)data { ++ (CGPoint)toPoint:(NSArray *)data { return CGPointMake([FLTGoogleMapJsonConversions toDouble:data[0]], [FLTGoogleMapJsonConversions toDouble:data[1]]); } -+ (NSArray*)positionToJson:(CLLocationCoordinate2D)position { ++ (NSArray *)positionToJson:(CLLocationCoordinate2D)position { return @[ @(position.latitude), @(position.longitude) ]; } -+ (UIColor*)toColor:(NSNumber*)numberColor { ++ (UIColor *)toColor:(NSNumber *)numberColor { unsigned long value = [numberColor unsignedLongValue]; return [UIColor colorWithRed:((float)((value & 0xFF0000) >> 16)) / 255.0 green:((float)((value & 0xFF00) >> 8)) / 255.0 @@ -44,12 +44,12 @@ + (UIColor*)toColor:(NSNumber*)numberColor { alpha:((float)((value & 0xFF000000) >> 24)) / 255.0]; } -+ (NSArray*)toPoints:(NSArray*)data { - NSMutableArray* points = [[NSMutableArray alloc] init]; ++ (NSArray *)toPoints:(NSArray *)data { + NSMutableArray *points = [[NSMutableArray alloc] init]; for (unsigned i = 0; i < [data count]; i++) { - NSNumber* latitude = data[i][0]; - NSNumber* longitude = data[i][1]; - CLLocation* point = + NSNumber *latitude = data[i][0]; + NSNumber *longitude = data[i][1]; + CLLocation *point = [[CLLocation alloc] initWithLatitude:[FLTGoogleMapJsonConversions toDouble:latitude] longitude:[FLTGoogleMapJsonConversions toDouble:longitude]]; [points addObject:point]; @@ -58,10 +58,10 @@ + (UIColor*)toColor:(NSNumber*)numberColor { return points; } -+ (NSArray*>*)toHoles:(NSArray*)data { - NSMutableArray*>* holes = [[[NSMutableArray alloc] init] init]; ++ (NSArray *> *)toHoles:(NSArray *)data { + NSMutableArray *> *holes = [[[NSMutableArray alloc] init] init]; for (unsigned i = 0; i < [data count]; i++) { - NSArray* points = [FLTGoogleMapJsonConversions toPoints:data[i]]; + NSArray *points = [FLTGoogleMapJsonConversions toPoints:data[i]]; [holes addObject:points]; } diff --git a/packages/google_sign_in/google_sign_in/example/ios/Runner/main.m b/packages/google_sign_in/google_sign_in/example/ios/Runner/main.m index f97b9ef5c8a1..f143297b30d6 100644 --- a/packages/google_sign_in/google_sign_in/example/ios/Runner/main.m +++ b/packages/google_sign_in/google_sign_in/example/ios/Runner/main.m @@ -6,7 +6,7 @@ #import #import "AppDelegate.h" -int main(int argc, char* argv[]) { +int main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } diff --git a/packages/google_sign_in/google_sign_in/example/ios/RunnerUITests/GoogleSignInUITests.m b/packages/google_sign_in/google_sign_in/example/ios/RunnerUITests/GoogleSignInUITests.m index 52d8da1b5964..c8fa27864b43 100644 --- a/packages/google_sign_in/google_sign_in/example/ios/RunnerUITests/GoogleSignInUITests.m +++ b/packages/google_sign_in/google_sign_in/example/ios/RunnerUITests/GoogleSignInUITests.m @@ -6,7 +6,7 @@ @import XCTest; @interface GoogleSignInUITests : XCTestCase -@property(nonatomic, strong) XCUIApplication* app; +@property(nonatomic, strong) XCUIApplication *app; @end @implementation GoogleSignInUITests @@ -19,9 +19,9 @@ - (void)setUp { } - (void)testSignInPopUp { - XCUIApplication* app = self.app; + XCUIApplication *app = self.app; - XCUIElement* signInButton = app.buttons[@"SIGN IN"]; + XCUIElement *signInButton = app.buttons[@"SIGN IN"]; if (![signInButton waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); XCTFail(@"Failed due to not able to find Sign In button"); @@ -34,9 +34,9 @@ - (void)testSignInPopUp { - (void)allowSignInPermissions { // The "Sign In" system permissions pop up isn't caught by // addUIInterruptionMonitorWithDescription. - XCUIApplication* springboard = + XCUIApplication *springboard = [[XCUIApplication alloc] initWithBundleIdentifier:@"com.apple.springboard"]; - XCUIElement* permissionAlert = springboard.alerts.firstMatch; + XCUIElement *permissionAlert = springboard.alerts.firstMatch; if ([permissionAlert waitForExistenceWithTimeout:5.0]) { [permissionAlert.buttons[@"Continue"] tap]; } else { diff --git a/packages/image_picker/image_picker/example/ios/Runner/main.m b/packages/image_picker/image_picker/example/ios/Runner/main.m index f97b9ef5c8a1..f143297b30d6 100644 --- a/packages/image_picker/image_picker/example/ios/Runner/main.m +++ b/packages/image_picker/image_picker/example/ios/Runner/main.m @@ -6,7 +6,7 @@ #import #import "AppDelegate.h" -int main(int argc, char* argv[]) { +int main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } diff --git a/packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerTestImages.m b/packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerTestImages.m index a0bae7b8f91c..64843f75d05b 100644 --- a/packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerTestImages.m +++ b/packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerTestImages.m @@ -6,10 +6,10 @@ @implementation ImagePickerTestImages -+ (NSData*)JPGTestData { - NSBundle* bundle = [NSBundle bundleForClass:self]; - NSURL* url = [bundle URLForResource:@"jpgImage" withExtension:@"jpg"]; - NSData* data = [NSData dataWithContentsOfURL:url]; ++ (NSData *)JPGTestData { + NSBundle *bundle = [NSBundle bundleForClass:self]; + NSURL *url = [bundle URLForResource:@"jpgImage" withExtension:@"jpg"]; + NSData *data = [NSData dataWithContentsOfURL:url]; if (!data.length) { // When the tests are run outside the example project (podspec lint) the image may not be // embedded in the test bundle. Fall back to the base64 string representation of the jpg. @@ -73,10 +73,10 @@ + (NSData*)JPGTestData { return data; } -+ (NSData*)PNGTestData { - NSBundle* bundle = [NSBundle bundleForClass:self]; - NSURL* url = [bundle URLForResource:@"pngImage" withExtension:@"png"]; - NSData* data = [NSData dataWithContentsOfURL:url]; ++ (NSData *)PNGTestData { + NSBundle *bundle = [NSBundle bundleForClass:self]; + NSURL *url = [bundle URLForResource:@"pngImage" withExtension:@"png"]; + NSData *data = [NSData dataWithContentsOfURL:url]; if (!data.length) { // When the tests are run outside the example project (podspec lint) the image may not be // embedded in the test bundle. Fall back to the base64 string representation of the png. @@ -91,10 +91,10 @@ + (NSData*)PNGTestData { return data; } -+ (NSData*)GIFTestData { - NSBundle* bundle = [NSBundle bundleForClass:self]; - NSURL* url = [bundle URLForResource:@"gifImage" withExtension:@"gif"]; - NSData* data = [NSData dataWithContentsOfURL:url]; ++ (NSData *)GIFTestData { + NSBundle *bundle = [NSBundle bundleForClass:self]; + NSURL *url = [bundle URLForResource:@"gifImage" withExtension:@"gif"]; + NSData *data = [NSData dataWithContentsOfURL:url]; if (!data.length) { // When the tests are run outside the example project (podspec lint) the image may not be // embedded in the test bundle. Fall back to the base64 string representation of the gif. diff --git a/packages/image_picker/image_picker/example/ios/RunnerUITests/ImagePickerFromGalleryUITests.m b/packages/image_picker/image_picker/example/ios/RunnerUITests/ImagePickerFromGalleryUITests.m index 4b2163d00577..e081cee9cce4 100644 --- a/packages/image_picker/image_picker/example/ios/RunnerUITests/ImagePickerFromGalleryUITests.m +++ b/packages/image_picker/image_picker/example/ios/RunnerUITests/ImagePickerFromGalleryUITests.m @@ -9,7 +9,7 @@ @interface ImagePickerFromGalleryUITests : XCTestCase -@property(nonatomic, strong) XCUIApplication* app; +@property(nonatomic, strong) XCUIApplication *app; @end @@ -24,9 +24,9 @@ - (void)setUp { [self.app launch]; __weak typeof(self) weakSelf = self; [self addUIInterruptionMonitorWithDescription:@"Permission popups" - handler:^BOOL(XCUIElement* _Nonnull interruptingElement) { + handler:^BOOL(XCUIElement *_Nonnull interruptingElement) { if (@available(iOS 14, *)) { - XCUIElement* allPhotoPermission = + XCUIElement *allPhotoPermission = interruptingElement .buttons[@"Allow Access to All Photos"]; if (![allPhotoPermission waitForExistenceWithTimeout: @@ -39,7 +39,7 @@ - (void)setUp { } [allPhotoPermission tap]; } else { - XCUIElement* ok = interruptingElement.buttons[@"OK"]; + XCUIElement *ok = interruptingElement.buttons[@"OK"]; if (![ok waitForExistenceWithTimeout: kElementWaitingTime]) { os_log_error(OS_LOG_DEFAULT, "%@", @@ -69,10 +69,10 @@ - (void)testCancel { - (void)launchPickerAndCancel { // Find and tap on the pick from gallery button. - NSPredicate* predicateToFindImageFromGalleryButton = + NSPredicate *predicateToFindImageFromGalleryButton = [NSPredicate predicateWithFormat:@"label == %@", @"image_picker_example_from_gallery"]; - XCUIElement* imageFromGalleryButton = + XCUIElement *imageFromGalleryButton = [self.app.otherElements elementMatchingPredicate:predicateToFindImageFromGalleryButton]; if (![imageFromGalleryButton waitForExistenceWithTimeout:kElementWaitingTime]) { os_log_error(OS_LOG_DEFAULT, "%@", self.app.debugDescription); @@ -84,10 +84,10 @@ - (void)launchPickerAndCancel { [imageFromGalleryButton tap]; // Find and tap on the `pick` button. - NSPredicate* predicateToFindPickButton = + NSPredicate *predicateToFindPickButton = [NSPredicate predicateWithFormat:@"label == %@", @"PICK"]; - XCUIElement* pickButton = [self.app.buttons elementMatchingPredicate:predicateToFindPickButton]; + XCUIElement *pickButton = [self.app.buttons elementMatchingPredicate:predicateToFindPickButton]; if (![pickButton waitForExistenceWithTimeout:kElementWaitingTime]) { os_log_error(OS_LOG_DEFAULT, "%@", self.app.debugDescription); XCTFail(@"Failed due to not able to find pick button with %@ seconds", @(kElementWaitingTime)); @@ -101,10 +101,10 @@ - (void)launchPickerAndCancel { [self.app tap]; // Find and tap on the `Cancel` button. - NSPredicate* predicateToFindCancelButton = + NSPredicate *predicateToFindCancelButton = [NSPredicate predicateWithFormat:@"label == %@", @"Cancel"]; - XCUIElement* cancelButton = + XCUIElement *cancelButton = [self.app.buttons elementMatchingPredicate:predicateToFindCancelButton]; if (![cancelButton waitForExistenceWithTimeout:kElementWaitingTime]) { os_log_error(OS_LOG_DEFAULT, "%@", self.app.debugDescription); @@ -116,7 +116,7 @@ - (void)launchPickerAndCancel { [cancelButton tap]; // Find the "not picked image text". - XCUIElement* imageNotPickedText = [self.app.staticTexts + XCUIElement *imageNotPickedText = [self.app.staticTexts elementMatchingPredicate:[NSPredicate predicateWithFormat:@"label == %@", @"You have not yet picked an image."]]; @@ -131,10 +131,10 @@ - (void)launchPickerAndCancel { - (void)launchPickerAndPick { // Find and tap on the pick from gallery button. - NSPredicate* predicateToFindImageFromGalleryButton = + NSPredicate *predicateToFindImageFromGalleryButton = [NSPredicate predicateWithFormat:@"label == %@", @"image_picker_example_from_gallery"]; - XCUIElement* imageFromGalleryButton = + XCUIElement *imageFromGalleryButton = [self.app.otherElements elementMatchingPredicate:predicateToFindImageFromGalleryButton]; if (![imageFromGalleryButton waitForExistenceWithTimeout:kElementWaitingTime]) { os_log_error(OS_LOG_DEFAULT, "%@", self.app.debugDescription); @@ -146,10 +146,10 @@ - (void)launchPickerAndPick { [imageFromGalleryButton tap]; // Find and tap on the `pick` button. - NSPredicate* predicateToFindPickButton = + NSPredicate *predicateToFindPickButton = [NSPredicate predicateWithFormat:@"label == %@", @"PICK"]; - XCUIElement* pickButton = [self.app.buttons elementMatchingPredicate:predicateToFindPickButton]; + XCUIElement *pickButton = [self.app.buttons elementMatchingPredicate:predicateToFindPickButton]; if (![pickButton waitForExistenceWithTimeout:kElementWaitingTime]) { os_log_error(OS_LOG_DEFAULT, "%@", self.app.debugDescription); XCTFail(@"Failed due to not able to find pick button with %@ seconds", @(kElementWaitingTime)); @@ -163,11 +163,11 @@ - (void)launchPickerAndPick { [self.app tap]; // Find an image and tap on it. (IOS 14 UI, images are showing directly) - XCUIElement* aImage; + XCUIElement *aImage; if (@available(iOS 14, *)) { aImage = [self.app.scrollViews.firstMatch.images elementBoundByIndex:1]; } else { - XCUIElement* allPhotosCell = [self.app.cells + XCUIElement *allPhotosCell = [self.app.cells elementMatchingPredicate:[NSPredicate predicateWithFormat:@"label == %@", @"All Photos"]]; if (![allPhotosCell waitForExistenceWithTimeout:kElementWaitingTime]) { os_log_error(OS_LOG_DEFAULT, "%@", self.app.debugDescription); @@ -188,10 +188,10 @@ - (void)launchPickerAndPick { [aImage tap]; // Find the picked image. - NSPredicate* predicateToFindPickedImage = + NSPredicate *predicateToFindPickedImage = [NSPredicate predicateWithFormat:@"label == %@", @"image_picker_example_picked_image"]; - XCUIElement* pickedImage = [self.app.images elementMatchingPredicate:predicateToFindPickedImage]; + XCUIElement *pickedImage = [self.app.images elementMatchingPredicate:predicateToFindPickedImage]; if (![pickedImage waitForExistenceWithTimeout:kElementWaitingTime]) { os_log_error(OS_LOG_DEFAULT, "%@", self.app.debugDescription); XCTFail(@"Failed due to not able to find pickedImage with %@ seconds", @(kElementWaitingTime)); diff --git a/packages/image_picker/image_picker/example/ios/RunnerUITests/ImagePickerFromLimitedGalleryUITests.m b/packages/image_picker/image_picker/example/ios/RunnerUITests/ImagePickerFromLimitedGalleryUITests.m index 802a494b0f5e..455fd6269d4b 100644 --- a/packages/image_picker/image_picker/example/ios/RunnerUITests/ImagePickerFromLimitedGalleryUITests.m +++ b/packages/image_picker/image_picker/example/ios/RunnerUITests/ImagePickerFromLimitedGalleryUITests.m @@ -9,7 +9,7 @@ @interface ImagePickerFromLimitedGalleryUITests : XCTestCase -@property(nonatomic, strong) XCUIApplication* app; +@property(nonatomic, strong) XCUIApplication *app; @end @@ -24,8 +24,8 @@ - (void)setUp { [self.app launch]; __weak typeof(self) weakSelf = self; [self addUIInterruptionMonitorWithDescription:@"Permission popups" - handler:^BOOL(XCUIElement* _Nonnull interruptingElement) { - XCUIElement* limitedPhotoPermission = + handler:^BOOL(XCUIElement *_Nonnull interruptingElement) { + XCUIElement *limitedPhotoPermission = [interruptingElement.buttons elementBoundByIndex:0]; if (![limitedPhotoPermission waitForExistenceWithTimeout: @@ -57,10 +57,10 @@ - (void)testSelectingFromGallery { - (void)launchPickerAndSelect { // Find and tap on the pick from gallery button. - NSPredicate* predicateToFindImageFromGalleryButton = + NSPredicate *predicateToFindImageFromGalleryButton = [NSPredicate predicateWithFormat:@"label == %@", @"image_picker_example_from_gallery"]; - XCUIElement* imageFromGalleryButton = + XCUIElement *imageFromGalleryButton = [self.app.otherElements elementMatchingPredicate:predicateToFindImageFromGalleryButton]; if (![imageFromGalleryButton waitForExistenceWithTimeout:kLimitedElementWaitingTime]) { os_log_error(OS_LOG_DEFAULT, "%@", self.app.debugDescription); @@ -72,10 +72,10 @@ - (void)launchPickerAndSelect { [imageFromGalleryButton tap]; // Find and tap on the `pick` button. - NSPredicate* predicateToFindPickButton = + NSPredicate *predicateToFindPickButton = [NSPredicate predicateWithFormat:@"label == %@", @"PICK"]; - XCUIElement* pickButton = [self.app.buttons elementMatchingPredicate:predicateToFindPickButton]; + XCUIElement *pickButton = [self.app.buttons elementMatchingPredicate:predicateToFindPickButton]; if (![pickButton waitForExistenceWithTimeout:kLimitedElementWaitingTime]) { os_log_error(OS_LOG_DEFAULT, "%@", self.app.debugDescription); XCTSkip(@"Pick button isn't found so the test is skipped..."); @@ -89,11 +89,11 @@ - (void)launchPickerAndSelect { [self.app tap]; // Find an image and tap on it. (IOS 14 UI, images are showing directly) - XCUIElement* aImage; + XCUIElement *aImage; if (@available(iOS 14, *)) { aImage = [self.app.scrollViews.firstMatch.images elementBoundByIndex:1]; } else { - XCUIElement* selectedPhotosCell = [self.app.cells + XCUIElement *selectedPhotosCell = [self.app.cells elementMatchingPredicate:[NSPredicate predicateWithFormat:@"label == %@", @"Selected Photos"]]; if (![selectedPhotosCell waitForExistenceWithTimeout:kLimitedElementWaitingTime]) { @@ -116,10 +116,10 @@ - (void)launchPickerAndSelect { [aImage tap]; // Find and tap on the `Done` button. - NSPredicate* predicateToFindDoneButton = + NSPredicate *predicateToFindDoneButton = [NSPredicate predicateWithFormat:@"label == %@", @"Done"]; - XCUIElement* doneButton = [self.app.buttons elementMatchingPredicate:predicateToFindDoneButton]; + XCUIElement *doneButton = [self.app.buttons elementMatchingPredicate:predicateToFindDoneButton]; if (![doneButton waitForExistenceWithTimeout:kLimitedElementWaitingTime]) { os_log_error(OS_LOG_DEFAULT, "%@", self.app.debugDescription); XCTSkip(@"Permissions popup could not fired so the test is skipped..."); @@ -132,7 +132,7 @@ - (void)launchPickerAndSelect { if (@available(iOS 14, *)) { aImage = [self.app.scrollViews.firstMatch.images elementBoundByIndex:1]; } else { - XCUIElement* selectedPhotosCell = [self.app.cells + XCUIElement *selectedPhotosCell = [self.app.cells elementMatchingPredicate:[NSPredicate predicateWithFormat:@"label == %@", @"Selected Photos"]]; if (![selectedPhotosCell waitForExistenceWithTimeout:kLimitedElementWaitingTime]) { @@ -155,10 +155,10 @@ - (void)launchPickerAndSelect { [aImage tap]; // Find the picked image. - NSPredicate* predicateToFindPickedImage = + NSPredicate *predicateToFindPickedImage = [NSPredicate predicateWithFormat:@"label == %@", @"image_picker_example_picked_image"]; - XCUIElement* pickedImage = [self.app.images elementMatchingPredicate:predicateToFindPickedImage]; + XCUIElement *pickedImage = [self.app.images elementMatchingPredicate:predicateToFindPickedImage]; if (![pickedImage waitForExistenceWithTimeout:kLimitedElementWaitingTime]) { os_log_error(OS_LOG_DEFAULT, "%@", self.app.debugDescription); XCTFail(@"Failed due to not able to find pickedImage with %@ seconds", diff --git a/packages/in_app_purchase/in_app_purchase/example/ios/Runner/main.m b/packages/in_app_purchase/in_app_purchase/example/ios/Runner/main.m index f97b9ef5c8a1..f143297b30d6 100644 --- a/packages/in_app_purchase/in_app_purchase/example/ios/Runner/main.m +++ b/packages/in_app_purchase/in_app_purchase/example/ios/Runner/main.m @@ -6,7 +6,7 @@ #import #import "AppDelegate.h" -int main(int argc, char* argv[]) { +int main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner/main.m b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner/main.m index f97b9ef5c8a1..f143297b30d6 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner/main.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner/main.m @@ -6,7 +6,7 @@ #import #import "AppDelegate.h" -int main(int argc, char* argv[]) { +int main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/InAppPurchasePluginTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/InAppPurchasePluginTests.m index 14ed06c78c35..fcb2c9425d69 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/InAppPurchasePluginTests.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/InAppPurchasePluginTests.m @@ -11,8 +11,8 @@ @interface InAppPurchasePluginTest : XCTestCase -@property(strong, nonatomic) FIAPReceiptManagerStub* receiptManagerStub; -@property(strong, nonatomic) InAppPurchasePlugin* plugin; +@property(strong, nonatomic) FIAPReceiptManagerStub *receiptManagerStub; +@property(strong, nonatomic) InAppPurchasePlugin *plugin; @end @@ -27,9 +27,9 @@ - (void)tearDown { } - (void)testInvalidMethodCall { - XCTestExpectation* expectation = + XCTestExpectation *expectation = [self expectationWithDescription:@"expect result to be not implemented"]; - FlutterMethodCall* call = [FlutterMethodCall methodCallWithMethodName:@"invalid" arguments:NULL]; + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"invalid" arguments:NULL]; __block id result; [self.plugin handleMethodCall:call result:^(id r) { @@ -41,8 +41,8 @@ - (void)testInvalidMethodCall { } - (void)testCanMakePayments { - XCTestExpectation* expectation = [self expectationWithDescription:@"expect result to be YES"]; - FlutterMethodCall* call = + XCTestExpectation *expectation = [self expectationWithDescription:@"expect result to be YES"]; + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"-[SKPaymentQueue canMakePayments:]" arguments:NULL]; __block id result; @@ -56,9 +56,9 @@ - (void)testCanMakePayments { } - (void)testGetProductResponse { - XCTestExpectation* expectation = + XCTestExpectation *expectation = [self expectationWithDescription:@"expect response contains 1 item"]; - FlutterMethodCall* call = [FlutterMethodCall + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin startProductRequest:result:]" arguments:@[ @"123" ]]; __block id result; @@ -69,27 +69,27 @@ - (void)testGetProductResponse { }]; [self waitForExpectations:@[ expectation ] timeout:5]; XCTAssert([result isKindOfClass:[NSDictionary class]]); - NSArray* resultArray = [result objectForKey:@"products"]; + NSArray *resultArray = [result objectForKey:@"products"]; XCTAssertEqual(resultArray.count, 1); XCTAssertTrue([resultArray.firstObject[@"productIdentifier"] isEqualToString:@"123"]); } - (void)testAddPaymentFailure { - XCTestExpectation* expectation = + XCTestExpectation *expectation = [self expectationWithDescription:@"result should return failed state"]; - FlutterMethodCall* call = + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin addPayment:result:]" arguments:@{ @"productIdentifier" : @"123", @"quantity" : @(1), @"simulatesAskToBuyInSandbox" : @YES, }]; - SKPaymentQueueStub* queue = [SKPaymentQueueStub new]; + SKPaymentQueueStub *queue = [SKPaymentQueueStub new]; queue.testState = SKPaymentTransactionStateFailed; - __block SKPaymentTransaction* transactionForUpdateBlock; + __block SKPaymentTransaction *transactionForUpdateBlock; self.plugin.paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue - transactionsUpdated:^(NSArray* _Nonnull transactions) { - SKPaymentTransaction* transaction = transactions[0]; + transactionsUpdated:^(NSArray *_Nonnull transactions) { + SKPaymentTransaction *transaction = transactions[0]; if (transaction.transactionState == SKPaymentTransactionStateFailed) { transactionForUpdateBlock = transaction; [expectation fulfill]; @@ -98,7 +98,7 @@ - (void)testAddPaymentFailure { transactionRemoved:nil restoreTransactionFailed:nil restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:^BOOL(SKPayment* _Nonnull payment, SKProduct* _Nonnull product) { + shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { return YES; } updatedDownloads:nil]; @@ -112,21 +112,21 @@ - (void)testAddPaymentFailure { } - (void)testAddPaymentSuccessWithoutPaymentDiscount { - XCTestExpectation* expectation = + XCTestExpectation *expectation = [self expectationWithDescription:@"result should return success state"]; - FlutterMethodCall* call = + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin addPayment:result:]" arguments:@{ @"productIdentifier" : @"123", @"quantity" : @(1), @"simulatesAskToBuyInSandbox" : @YES, }]; - SKPaymentQueueStub* queue = [SKPaymentQueueStub new]; + SKPaymentQueueStub *queue = [SKPaymentQueueStub new]; queue.testState = SKPaymentTransactionStatePurchased; - __block SKPaymentTransaction* transactionForUpdateBlock; + __block SKPaymentTransaction *transactionForUpdateBlock; self.plugin.paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue - transactionsUpdated:^(NSArray* _Nonnull transactions) { - SKPaymentTransaction* transaction = transactions[0]; + transactionsUpdated:^(NSArray *_Nonnull transactions) { + SKPaymentTransaction *transaction = transactions[0]; if (transaction.transactionState == SKPaymentTransactionStatePurchased) { transactionForUpdateBlock = transaction; if (@available(iOS 12.2, *)) { @@ -138,7 +138,7 @@ - (void)testAddPaymentSuccessWithoutPaymentDiscount { transactionRemoved:nil restoreTransactionFailed:nil restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:^BOOL(SKPayment* _Nonnull payment, SKProduct* _Nonnull product) { + shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { return YES; } updatedDownloads:nil]; @@ -151,9 +151,9 @@ - (void)testAddPaymentSuccessWithoutPaymentDiscount { } - (void)testAddPaymentSuccessWithPaymentDiscount { - XCTestExpectation* expectation = + XCTestExpectation *expectation = [self expectationWithDescription:@"result should return success state"]; - FlutterMethodCall* call = + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin addPayment:result:]" arguments:@{ @"productIdentifier" : @"123", @@ -167,16 +167,16 @@ - (void)testAddPaymentSuccessWithPaymentDiscount { @"timestamp" : @(1635847102), } }]; - SKPaymentQueueStub* queue = [SKPaymentQueueStub new]; + SKPaymentQueueStub *queue = [SKPaymentQueueStub new]; queue.testState = SKPaymentTransactionStatePurchased; - __block SKPaymentTransaction* transactionForUpdateBlock; + __block SKPaymentTransaction *transactionForUpdateBlock; self.plugin.paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue - transactionsUpdated:^(NSArray* _Nonnull transactions) { - SKPaymentTransaction* transaction = transactions[0]; + transactionsUpdated:^(NSArray *_Nonnull transactions) { + SKPaymentTransaction *transaction = transactions[0]; if (transaction.transactionState == SKPaymentTransactionStatePurchased) { transactionForUpdateBlock = transaction; if (@available(iOS 12.2, *)) { - SKPaymentDiscount* paymentDiscount = transaction.payment.paymentDiscount; + SKPaymentDiscount *paymentDiscount = transaction.payment.paymentDiscount; XCTAssertEqual(paymentDiscount.identifier, @"test_identifier"); XCTAssertEqual(paymentDiscount.keyIdentifier, @"test_key_identifier"); XCTAssertEqualObjects( @@ -191,7 +191,7 @@ - (void)testAddPaymentSuccessWithPaymentDiscount { transactionRemoved:nil restoreTransactionFailed:nil restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:^BOOL(SKPayment* _Nonnull payment, SKProduct* _Nonnull product) { + shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { return YES; } updatedDownloads:nil]; @@ -204,9 +204,9 @@ - (void)testAddPaymentSuccessWithPaymentDiscount { } - (void)testAddPaymentFailureWithInvalidPaymentDiscount { - XCTestExpectation* expectation = + XCTestExpectation *expectation = [self expectationWithDescription:@"result should return success state"]; - FlutterMethodCall* call = + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin addPayment:result:]" arguments:@{ @"productIdentifier" : @"123", @@ -224,7 +224,7 @@ - (void)testAddPaymentFailureWithInvalidPaymentDiscount { handleMethodCall:call result:^(id r) { XCTAssertTrue([r isKindOfClass:FlutterError.class]); - FlutterError* result = r; + FlutterError *result = r; XCTAssertEqualObjects(result.code, @"storekit_invalid_payment_discount_object"); XCTAssertEqualObjects(result.message, @"You have requested a payment and specified a payment " @@ -238,23 +238,23 @@ - (void)testAddPaymentFailureWithInvalidPaymentDiscount { } - (void)testAddPaymentWithNullSandboxArgument { - XCTestExpectation* expectation = + XCTestExpectation *expectation = [self expectationWithDescription:@"result should return success state"]; - XCTestExpectation* simulatesAskToBuyInSandboxExpectation = + XCTestExpectation *simulatesAskToBuyInSandboxExpectation = [self expectationWithDescription:@"payment isn't simulatesAskToBuyInSandbox"]; - FlutterMethodCall* call = + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin addPayment:result:]" arguments:@{ @"productIdentifier" : @"123", @"quantity" : @(1), @"simulatesAskToBuyInSandbox" : [NSNull null], }]; - SKPaymentQueueStub* queue = [SKPaymentQueueStub new]; + SKPaymentQueueStub *queue = [SKPaymentQueueStub new]; queue.testState = SKPaymentTransactionStatePurchased; - __block SKPaymentTransaction* transactionForUpdateBlock; + __block SKPaymentTransaction *transactionForUpdateBlock; self.plugin.paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue - transactionsUpdated:^(NSArray* _Nonnull transactions) { - SKPaymentTransaction* transaction = transactions[0]; + transactionsUpdated:^(NSArray *_Nonnull transactions) { + SKPaymentTransaction *transaction = transactions[0]; if (transaction.transactionState == SKPaymentTransactionStatePurchased) { transactionForUpdateBlock = transaction; [expectation fulfill]; @@ -266,7 +266,7 @@ - (void)testAddPaymentWithNullSandboxArgument { transactionRemoved:nil restoreTransactionFailed:nil restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:^BOOL(SKPayment* _Nonnull payment, SKProduct* _Nonnull product) { + shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { return YES; } updatedDownloads:nil]; @@ -279,16 +279,16 @@ - (void)testAddPaymentWithNullSandboxArgument { } - (void)testRestoreTransactions { - XCTestExpectation* expectation = + XCTestExpectation *expectation = [self expectationWithDescription:@"result successfully restore transactions"]; - FlutterMethodCall* call = [FlutterMethodCall + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin restoreTransactions:result:]" arguments:nil]; - SKPaymentQueueStub* queue = [SKPaymentQueueStub new]; + SKPaymentQueueStub *queue = [SKPaymentQueueStub new]; queue.testState = SKPaymentTransactionStatePurchased; __block BOOL callbackInvoked = NO; self.plugin.paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue - transactionsUpdated:^(NSArray* _Nonnull transactions) { + transactionsUpdated:^(NSArray *_Nonnull transactions) { } transactionRemoved:nil restoreTransactionFailed:nil @@ -307,11 +307,11 @@ - (void)testRestoreTransactions { } - (void)testRetrieveReceiptDataSuccess { - XCTestExpectation* expectation = [self expectationWithDescription:@"receipt data retrieved"]; - FlutterMethodCall* call = [FlutterMethodCall + XCTestExpectation *expectation = [self expectationWithDescription:@"receipt data retrieved"]; + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin retrieveReceiptData:result:]" arguments:nil]; - __block NSDictionary* result; + __block NSDictionary *result; [self.plugin handleMethodCall:call result:^(id r) { result = r; @@ -323,11 +323,11 @@ - (void)testRetrieveReceiptDataSuccess { } - (void)testRetrieveReceiptDataError { - XCTestExpectation* expectation = [self expectationWithDescription:@"receipt data retrieved"]; - FlutterMethodCall* call = [FlutterMethodCall + XCTestExpectation *expectation = [self expectationWithDescription:@"receipt data retrieved"]; + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin retrieveReceiptData:result:]" arguments:nil]; - __block NSDictionary* result; + __block NSDictionary *result; self.receiptManagerStub.returnError = YES; [self.plugin handleMethodCall:call result:^(id r) { @@ -337,15 +337,15 @@ - (void)testRetrieveReceiptDataError { [self waitForExpectations:@[ expectation ] timeout:5]; XCTAssertNotNil(result); XCTAssert([result isKindOfClass:[FlutterError class]]); - NSDictionary* details = ((FlutterError*)result).details; + NSDictionary *details = ((FlutterError *)result).details; XCTAssertNotNil(details[@"error"]); - NSNumber* errorCode = (NSNumber*)details[@"error"][@"code"]; + NSNumber *errorCode = (NSNumber *)details[@"error"][@"code"]; XCTAssertEqual(errorCode, [NSNumber numberWithInteger:99]); } - (void)testRefreshReceiptRequest { - XCTestExpectation* expectation = [self expectationWithDescription:@"expect success"]; - FlutterMethodCall* call = + XCTestExpectation *expectation = [self expectationWithDescription:@"expect success"]; + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin refreshReceipt:result:]" arguments:nil]; __block BOOL result = NO; @@ -359,9 +359,9 @@ - (void)testRefreshReceiptRequest { } - (void)testPresentCodeRedemptionSheet { - XCTestExpectation* expectation = + XCTestExpectation *expectation = [self expectationWithDescription:@"expect successfully present Code Redemption Sheet"]; - FlutterMethodCall* call = [FlutterMethodCall + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin presentCodeRedemptionSheet:result:]" arguments:nil]; __block BOOL callbackInvoked = NO; @@ -375,11 +375,11 @@ - (void)testPresentCodeRedemptionSheet { } - (void)testGetPendingTransactions { - XCTestExpectation* expectation = [self expectationWithDescription:@"expect success"]; - FlutterMethodCall* call = + XCTestExpectation *expectation = [self expectationWithDescription:@"expect success"]; + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"-[SKPaymentQueue transactions]" arguments:nil]; - SKPaymentQueue* mockQueue = OCMClassMock(SKPaymentQueue.class); - NSDictionary* transactionMap = @{ + SKPaymentQueue *mockQueue = OCMClassMock(SKPaymentQueue.class); + NSDictionary *transactionMap = @{ @"transactionIdentifier" : [NSNull null], @"transactionState" : @(SKPaymentTransactionStatePurchasing), @"payment" : [NSNull null], @@ -392,7 +392,7 @@ - (void)testGetPendingTransactions { OCMStub(mockQueue.transactions).andReturn(@[ [[SKPaymentTransactionStub alloc] initWithMap:transactionMap] ]); - __block NSArray* resultArray; + __block NSArray *resultArray; self.plugin.paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue transactionsUpdated:nil transactionRemoved:nil @@ -410,14 +410,14 @@ - (void)testGetPendingTransactions { } - (void)testStartAndStopObservingPaymentQueue { - FlutterMethodCall* startCall = [FlutterMethodCall + FlutterMethodCall *startCall = [FlutterMethodCall methodCallWithMethodName:@"-[SKPaymentQueue startObservingTransactionQueue]" arguments:nil]; - FlutterMethodCall* stopCall = + FlutterMethodCall *stopCall = [FlutterMethodCall methodCallWithMethodName:@"-[SKPaymentQueue stopObservingTransactionQueue]" arguments:nil]; - SKPaymentQueueStub* queue = [SKPaymentQueueStub new]; + SKPaymentQueueStub *queue = [SKPaymentQueueStub new]; self.plugin.paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue @@ -425,8 +425,8 @@ - (void)testStartAndStopObservingPaymentQueue { transactionRemoved:nil restoreTransactionFailed:nil restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:^BOOL(SKPayment* _Nonnull payment, - SKProduct* _Nonnull product) { + shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, + SKProduct *_Nonnull product) { return YES; } updatedDownloads:nil]; @@ -453,7 +453,7 @@ - (void)testStartAndStopObservingPaymentQueue { - (void)testRegisterPaymentQueueDelegate { if (@available(iOS 13, *)) { - FlutterMethodCall* call = + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"-[SKPaymentQueue registerDelegate]" arguments:nil]; @@ -480,7 +480,7 @@ - (void)testRegisterPaymentQueueDelegate { - (void)testRemovePaymentQueueDelegate { if (@available(iOS 13, *)) { - FlutterMethodCall* call = + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"-[SKPaymentQueue removeDelegate]" arguments:nil]; @@ -507,11 +507,11 @@ - (void)testRemovePaymentQueueDelegate { } - (void)testShowPriceConsentIfNeeded { - FlutterMethodCall* call = + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"-[SKPaymentQueue showPriceConsentIfNeeded]" arguments:nil]; - FIAPaymentQueueHandler* mockQueueHandler = OCMClassMock(FIAPaymentQueueHandler.class); + FIAPaymentQueueHandler *mockQueueHandler = OCMClassMock(FIAPaymentQueueHandler.class); self.plugin.paymentQueueHandler = mockQueueHandler; [self.plugin handleMethodCall:call diff --git a/packages/ios_platform_images/example/ios/RunnerTests/IosPlatformImagesTests.m b/packages/ios_platform_images/example/ios/RunnerTests/IosPlatformImagesTests.m index 747719d30276..c95c6ad5730d 100644 --- a/packages/ios_platform_images/example/ios/RunnerTests/IosPlatformImagesTests.m +++ b/packages/ios_platform_images/example/ios/RunnerTests/IosPlatformImagesTests.m @@ -11,7 +11,7 @@ @interface IosPlatformImagesTests : XCTestCase @implementation IosPlatformImagesTests - (void)testPlugin { - IosPlatformImagesPlugin* plugin = [[IosPlatformImagesPlugin alloc] init]; + IosPlatformImagesPlugin *plugin = [[IosPlatformImagesPlugin alloc] init]; XCTAssertNotNil(plugin); } diff --git a/packages/ios_platform_images/ios/Classes/IosPlatformImagesPlugin.m b/packages/ios_platform_images/ios/Classes/IosPlatformImagesPlugin.m index abd331e5d0cb..5f7debc3fe07 100644 --- a/packages/ios_platform_images/ios/Classes/IosPlatformImagesPlugin.m +++ b/packages/ios_platform_images/ios/Classes/IosPlatformImagesPlugin.m @@ -13,16 +13,16 @@ @interface IosPlatformImagesPlugin () @implementation IosPlatformImagesPlugin -+ (void)registerWithRegistrar:(NSObject*)registrar { - FlutterMethodChannel* channel = ++ (void)registerWithRegistrar:(NSObject *)registrar { + FlutterMethodChannel *channel = [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/ios_platform_images" binaryMessenger:[registrar messenger]]; - [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { + [channel setMethodCallHandler:^(FlutterMethodCall *call, FlutterResult result) { if ([@"loadImage" isEqualToString:call.method]) { - NSString* name = call.arguments; - UIImage* image = [UIImage imageNamed:name]; - NSData* data = UIImagePNGRepresentation(image); + NSString *name = call.arguments; + UIImage *image = [UIImage imageNamed:name]; + NSData *data = UIImagePNGRepresentation(image); if (data) { result(@{ @"scale" : @(image.scale), @@ -33,11 +33,11 @@ + (void)registerWithRegistrar:(NSObject*)registrar { } return; } else if ([@"resolveURL" isEqualToString:call.method]) { - NSArray* args = call.arguments; - NSString* name = args[0]; - NSString* extension = (args[1] == (id)NSNull.null) ? nil : args[1]; + NSArray *args = call.arguments; + NSString *name = args[0]; + NSString *extension = (args[1] == (id)NSNull.null) ? nil : args[1]; - NSURL* url = [[NSBundle mainBundle] URLForResource:name withExtension:extension]; + NSURL *url = [[NSBundle mainBundle] URLForResource:name withExtension:extension]; result(url.absoluteString); return; } diff --git a/packages/ios_platform_images/ios/Classes/UIImage+ios_platform_images.h b/packages/ios_platform_images/ios/Classes/UIImage+ios_platform_images.h index b98a423fa915..356a5f1cfe3e 100644 --- a/packages/ios_platform_images/ios/Classes/UIImage+ios_platform_images.h +++ b/packages/ios_platform_images/ios/Classes/UIImage+ios_platform_images.h @@ -22,6 +22,6 @@ /// /// Note: We don't yet support images from package dependencies (ex. /// `AssetImage('icons/heart.png', package: 'my_icons')`). -+ (UIImage*)flutterImageWithName:(NSString*)name; ++ (UIImage *)flutterImageWithName:(NSString *)name; @end diff --git a/packages/ios_platform_images/ios/Classes/UIImage+ios_platform_images.m b/packages/ios_platform_images/ios/Classes/UIImage+ios_platform_images.m index c6b8d8e91d1b..f20bbcd08c9b 100644 --- a/packages/ios_platform_images/ios/Classes/UIImage+ios_platform_images.m +++ b/packages/ios_platform_images/ios/Classes/UIImage+ios_platform_images.m @@ -6,20 +6,20 @@ #import "UIImage+ios_platform_images.h" @implementation UIImage (ios_platform_images) -+ (UIImage*)flutterImageWithName:(NSString*)name { - NSString* filename = [name lastPathComponent]; - NSString* path = [name stringByDeletingLastPathComponent]; ++ (UIImage *)flutterImageWithName:(NSString *)name { + NSString *filename = [name lastPathComponent]; + NSString *path = [name stringByDeletingLastPathComponent]; for (int screenScale = [UIScreen mainScreen].scale; screenScale > 1; --screenScale) { - NSString* key = [FlutterDartProject + NSString *key = [FlutterDartProject lookupKeyForAsset:[NSString stringWithFormat:@"%@/%d.0x/%@", path, screenScale, filename]]; - UIImage* image = [UIImage imageNamed:key + UIImage *image = [UIImage imageNamed:key inBundle:[NSBundle mainBundle] compatibleWithTraitCollection:nil]; if (image) { return image; } } - NSString* key = [FlutterDartProject lookupKeyForAsset:name]; + NSString *key = [FlutterDartProject lookupKeyForAsset:name]; return [UIImage imageNamed:key inBundle:[NSBundle mainBundle] compatibleWithTraitCollection:nil]; } @end diff --git a/packages/local_auth/example/ios/Runner/main.m b/packages/local_auth/example/ios/Runner/main.m index f97b9ef5c8a1..f143297b30d6 100644 --- a/packages/local_auth/example/ios/Runner/main.m +++ b/packages/local_auth/example/ios/Runner/main.m @@ -6,7 +6,7 @@ #import #import "AppDelegate.h" -int main(int argc, char* argv[]) { +int main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } diff --git a/packages/local_auth/example/ios/RunnerTests/FLTLocalAuthPluginTests.m b/packages/local_auth/example/ios/RunnerTests/FLTLocalAuthPluginTests.m index 97e78e2f624b..dc409da9f57c 100644 --- a/packages/local_auth/example/ios/RunnerTests/FLTLocalAuthPluginTests.m +++ b/packages/local_auth/example/ios/RunnerTests/FLTLocalAuthPluginTests.m @@ -15,7 +15,7 @@ // Private API needed for tests. @interface FLTLocalAuthPlugin (Test) -- (void)setAuthContextOverrides:(NSArray*)authContexts; +- (void)setAuthContextOverrides:(NSArray *)authContexts; @end // Set a long timeout to avoid flake due to slow CI. @@ -31,19 +31,19 @@ - (void)setUp { } - (void)testSuccessfullAuthWithBiometrics { - FLTLocalAuthPlugin* plugin = [[FLTLocalAuthPlugin alloc] init]; + FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc] init]; id mockAuthContext = OCMClassMock([LAContext class]); plugin.authContextOverrides = @[ mockAuthContext ]; const LAPolicy policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics; - NSString* reason = @"a reason"; + NSString *reason = @"a reason"; OCMStub([mockAuthContext canEvaluatePolicy:policy error:[OCMArg setTo:nil]]).andReturn(YES); // evaluatePolicy:localizedReason:reply: calls back on an internal queue, which is not // guaranteed to be on the main thread. Ensure that's handled correctly by calling back on // a background thread. - void (^backgroundThreadReplyCaller)(NSInvocation*) = ^(NSInvocation* invocation) { - void (^reply)(BOOL, NSError*); + void (^backgroundThreadReplyCaller)(NSInvocation *) = ^(NSInvocation *invocation) { + void (^reply)(BOOL, NSError *); [invocation getArgument:&reply atIndex:4]; dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{ reply(YES, nil); @@ -52,13 +52,13 @@ - (void)testSuccessfullAuthWithBiometrics { OCMStub([mockAuthContext evaluatePolicy:policy localizedReason:reason reply:[OCMArg any]]) .andDo(backgroundThreadReplyCaller); - FlutterMethodCall* call = [FlutterMethodCall methodCallWithMethodName:@"authenticate" + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"authenticate" arguments:@{ @"biometricOnly" : @(YES), @"localizedReason" : reason, }]; - XCTestExpectation* expectation = [self expectationWithDescription:@"Result is called"]; + XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"]; [plugin handleMethodCall:call result:^(id _Nullable result) { XCTAssertTrue([NSThread isMainThread]); @@ -70,19 +70,19 @@ - (void)testSuccessfullAuthWithBiometrics { } - (void)testSuccessfullAuthWithoutBiometrics { - FLTLocalAuthPlugin* plugin = [[FLTLocalAuthPlugin alloc] init]; + FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc] init]; id mockAuthContext = OCMClassMock([LAContext class]); plugin.authContextOverrides = @[ mockAuthContext ]; const LAPolicy policy = LAPolicyDeviceOwnerAuthentication; - NSString* reason = @"a reason"; + NSString *reason = @"a reason"; OCMStub([mockAuthContext canEvaluatePolicy:policy error:[OCMArg setTo:nil]]).andReturn(YES); // evaluatePolicy:localizedReason:reply: calls back on an internal queue, which is not // guaranteed to be on the main thread. Ensure that's handled correctly by calling back on // a background thread. - void (^backgroundThreadReplyCaller)(NSInvocation*) = ^(NSInvocation* invocation) { - void (^reply)(BOOL, NSError*); + void (^backgroundThreadReplyCaller)(NSInvocation *) = ^(NSInvocation *invocation) { + void (^reply)(BOOL, NSError *); [invocation getArgument:&reply atIndex:4]; dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{ reply(YES, nil); @@ -91,13 +91,13 @@ - (void)testSuccessfullAuthWithoutBiometrics { OCMStub([mockAuthContext evaluatePolicy:policy localizedReason:reason reply:[OCMArg any]]) .andDo(backgroundThreadReplyCaller); - FlutterMethodCall* call = [FlutterMethodCall methodCallWithMethodName:@"authenticate" + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"authenticate" arguments:@{ @"biometricOnly" : @(NO), @"localizedReason" : reason, }]; - XCTestExpectation* expectation = [self expectationWithDescription:@"Result is called"]; + XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"]; [plugin handleMethodCall:call result:^(id _Nullable result) { XCTAssertTrue([NSThread isMainThread]); @@ -109,19 +109,19 @@ - (void)testSuccessfullAuthWithoutBiometrics { } - (void)testFailedAuthWithBiometrics { - FLTLocalAuthPlugin* plugin = [[FLTLocalAuthPlugin alloc] init]; + FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc] init]; id mockAuthContext = OCMClassMock([LAContext class]); plugin.authContextOverrides = @[ mockAuthContext ]; const LAPolicy policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics; - NSString* reason = @"a reason"; + NSString *reason = @"a reason"; OCMStub([mockAuthContext canEvaluatePolicy:policy error:[OCMArg setTo:nil]]).andReturn(YES); // evaluatePolicy:localizedReason:reply: calls back on an internal queue, which is not // guaranteed to be on the main thread. Ensure that's handled correctly by calling back on // a background thread. - void (^backgroundThreadReplyCaller)(NSInvocation*) = ^(NSInvocation* invocation) { - void (^reply)(BOOL, NSError*); + void (^backgroundThreadReplyCaller)(NSInvocation *) = ^(NSInvocation *invocation) { + void (^reply)(BOOL, NSError *); [invocation getArgument:&reply atIndex:4]; dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{ reply(NO, [NSError errorWithDomain:@"error" code:99 userInfo:nil]); @@ -130,13 +130,13 @@ - (void)testFailedAuthWithBiometrics { OCMStub([mockAuthContext evaluatePolicy:policy localizedReason:reason reply:[OCMArg any]]) .andDo(backgroundThreadReplyCaller); - FlutterMethodCall* call = [FlutterMethodCall methodCallWithMethodName:@"authenticate" + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"authenticate" arguments:@{ @"biometricOnly" : @(YES), @"localizedReason" : reason, }]; - XCTestExpectation* expectation = [self expectationWithDescription:@"Result is called"]; + XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"]; [plugin handleMethodCall:call result:^(id _Nullable result) { XCTAssertTrue([NSThread isMainThread]); @@ -148,19 +148,19 @@ - (void)testFailedAuthWithBiometrics { } - (void)testFailedAuthWithoutBiometrics { - FLTLocalAuthPlugin* plugin = [[FLTLocalAuthPlugin alloc] init]; + FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc] init]; id mockAuthContext = OCMClassMock([LAContext class]); plugin.authContextOverrides = @[ mockAuthContext ]; const LAPolicy policy = LAPolicyDeviceOwnerAuthentication; - NSString* reason = @"a reason"; + NSString *reason = @"a reason"; OCMStub([mockAuthContext canEvaluatePolicy:policy error:[OCMArg setTo:nil]]).andReturn(YES); // evaluatePolicy:localizedReason:reply: calls back on an internal queue, which is not // guaranteed to be on the main thread. Ensure that's handled correctly by calling back on // a background thread. - void (^backgroundThreadReplyCaller)(NSInvocation*) = ^(NSInvocation* invocation) { - void (^reply)(BOOL, NSError*); + void (^backgroundThreadReplyCaller)(NSInvocation *) = ^(NSInvocation *invocation) { + void (^reply)(BOOL, NSError *); [invocation getArgument:&reply atIndex:4]; dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{ reply(NO, [NSError errorWithDomain:@"error" code:99 userInfo:nil]); @@ -169,13 +169,13 @@ - (void)testFailedAuthWithoutBiometrics { OCMStub([mockAuthContext evaluatePolicy:policy localizedReason:reason reply:[OCMArg any]]) .andDo(backgroundThreadReplyCaller); - FlutterMethodCall* call = [FlutterMethodCall methodCallWithMethodName:@"authenticate" + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"authenticate" arguments:@{ @"biometricOnly" : @(NO), @"localizedReason" : reason, }]; - XCTestExpectation* expectation = [self expectationWithDescription:@"Result is called"]; + XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"]; [plugin handleMethodCall:call result:^(id _Nullable result) { XCTAssertTrue([NSThread isMainThread]); diff --git a/packages/path_provider/path_provider/example/ios/Runner/main.m b/packages/path_provider/path_provider/example/ios/Runner/main.m index f97b9ef5c8a1..f143297b30d6 100644 --- a/packages/path_provider/path_provider/example/ios/Runner/main.m +++ b/packages/path_provider/path_provider/example/ios/Runner/main.m @@ -6,7 +6,7 @@ #import #import "AppDelegate.h" -int main(int argc, char* argv[]) { +int main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } diff --git a/packages/path_provider/path_provider/example/ios/RunnerTests/PathProviderTests.m b/packages/path_provider/path_provider/example/ios/RunnerTests/PathProviderTests.m index be48ea6b7ddf..0fcc05043c0a 100644 --- a/packages/path_provider/path_provider/example/ios/RunnerTests/PathProviderTests.m +++ b/packages/path_provider/path_provider/example/ios/RunnerTests/PathProviderTests.m @@ -11,7 +11,7 @@ @interface PathProviderTests : XCTestCase @implementation PathProviderTests - (void)testPlugin { - FLTPathProviderPlugin* plugin = [[FLTPathProviderPlugin alloc] init]; + FLTPathProviderPlugin *plugin = [[FLTPathProviderPlugin alloc] init]; XCTAssertNotNil(plugin); } diff --git a/packages/path_provider/path_provider/example/windows/runner/main.cpp b/packages/path_provider/path_provider/example/windows/runner/main.cpp index 126302b0be18..c7dbde1c7123 100644 --- a/packages/path_provider/path_provider/example/windows/runner/main.cpp +++ b/packages/path_provider/path_provider/example/windows/runner/main.cpp @@ -11,7 +11,7 @@ #include "utils.h" int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, - _In_ wchar_t *command_line, _In_ int show_command) { + _In_ wchar_t* command_line, _In_ int show_command) { // Attach to console when present (e.g., 'flutter run') or create a // new console when running with a debugger. if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { diff --git a/packages/path_provider/path_provider/example/windows/runner/utils.cpp b/packages/path_provider/path_provider/example/windows/runner/utils.cpp index 537728149601..e875ce8b05a9 100644 --- a/packages/path_provider/path_provider/example/windows/runner/utils.cpp +++ b/packages/path_provider/path_provider/example/windows/runner/utils.cpp @@ -13,7 +13,7 @@ void CreateAndAttachConsole() { if (::AllocConsole()) { - FILE *unused; + FILE* unused; if (freopen_s(&unused, "CONOUT$", "w", stdout)) { _dup2(_fileno(stdout), 1); } diff --git a/packages/path_provider/path_provider_ios/example/ios/Runner/main.m b/packages/path_provider/path_provider_ios/example/ios/Runner/main.m index f97b9ef5c8a1..f143297b30d6 100644 --- a/packages/path_provider/path_provider_ios/example/ios/Runner/main.m +++ b/packages/path_provider/path_provider_ios/example/ios/Runner/main.m @@ -6,7 +6,7 @@ #import #import "AppDelegate.h" -int main(int argc, char* argv[]) { +int main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } diff --git a/packages/path_provider/path_provider_ios/example/ios/RunnerTests/PathProviderTests.m b/packages/path_provider/path_provider_ios/example/ios/RunnerTests/PathProviderTests.m index 0f746cc81b93..87d227795614 100644 --- a/packages/path_provider/path_provider_ios/example/ios/RunnerTests/PathProviderTests.m +++ b/packages/path_provider/path_provider_ios/example/ios/RunnerTests/PathProviderTests.m @@ -11,7 +11,7 @@ @interface PathProviderTests : XCTestCase @implementation PathProviderTests - (void)testPlugin { - FLTPathProviderPlugin* plugin = [[FLTPathProviderPlugin alloc] init]; + FLTPathProviderPlugin *plugin = [[FLTPathProviderPlugin alloc] init]; XCTAssertNotNil(plugin); } diff --git a/packages/path_provider/path_provider_ios/ios/Classes/FLTPathProviderPlugin.m b/packages/path_provider/path_provider_ios/ios/Classes/FLTPathProviderPlugin.m index 4d3dfeb6e6e6..063e028f4d3c 100644 --- a/packages/path_provider/path_provider_ios/ios/Classes/FLTPathProviderPlugin.m +++ b/packages/path_provider/path_provider_ios/ios/Classes/FLTPathProviderPlugin.m @@ -4,12 +4,12 @@ #import "FLTPathProviderPlugin.h" -NSString* GetDirectoryOfType(NSSearchPathDirectory dir) { - NSArray* paths = NSSearchPathForDirectoriesInDomains(dir, NSUserDomainMask, YES); +NSString *GetDirectoryOfType(NSSearchPathDirectory dir) { + NSArray *paths = NSSearchPathForDirectoriesInDomains(dir, NSUserDomainMask, YES); return paths.firstObject; } -static FlutterError* getFlutterError(NSError* error) { +static FlutterError *getFlutterError(NSError *error) { if (error == nil) return nil; return [FlutterError errorWithCode:[NSString stringWithFormat:@"Error %ld", (long)error.code] message:error.domain @@ -18,21 +18,21 @@ @implementation FLTPathProviderPlugin -+ (void)registerWithRegistrar:(NSObject*)registrar { - FlutterMethodChannel* channel = ++ (void)registerWithRegistrar:(NSObject *)registrar { + FlutterMethodChannel *channel = [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/path_provider" binaryMessenger:registrar.messenger]; - [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { + [channel setMethodCallHandler:^(FlutterMethodCall *call, FlutterResult result) { if ([@"getTemporaryDirectory" isEqualToString:call.method]) { result([self getTemporaryDirectory]); } else if ([@"getApplicationDocumentsDirectory" isEqualToString:call.method]) { result([self getApplicationDocumentsDirectory]); } else if ([@"getApplicationSupportDirectory" isEqualToString:call.method]) { - NSString* path = [self getApplicationSupportDirectory]; + NSString *path = [self getApplicationSupportDirectory]; // Create the path if it doesn't exist - NSError* error; - NSFileManager* fileManager = [NSFileManager defaultManager]; + NSError *error; + NSFileManager *fileManager = [NSFileManager defaultManager]; BOOL success = [fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil @@ -50,19 +50,19 @@ + (void)registerWithRegistrar:(NSObject*)registrar { }]; } -+ (NSString*)getTemporaryDirectory { ++ (NSString *)getTemporaryDirectory { return GetDirectoryOfType(NSCachesDirectory); } -+ (NSString*)getApplicationDocumentsDirectory { ++ (NSString *)getApplicationDocumentsDirectory { return GetDirectoryOfType(NSDocumentDirectory); } -+ (NSString*)getApplicationSupportDirectory { ++ (NSString *)getApplicationSupportDirectory { return GetDirectoryOfType(NSApplicationSupportDirectory); } -+ (NSString*)getLibraryDirectory { ++ (NSString *)getLibraryDirectory { return GetDirectoryOfType(NSLibraryDirectory); } diff --git a/packages/path_provider/path_provider_windows/example/windows/runner/main.cpp b/packages/path_provider/path_provider_windows/example/windows/runner/main.cpp index 126302b0be18..c7dbde1c7123 100644 --- a/packages/path_provider/path_provider_windows/example/windows/runner/main.cpp +++ b/packages/path_provider/path_provider_windows/example/windows/runner/main.cpp @@ -11,7 +11,7 @@ #include "utils.h" int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, - _In_ wchar_t *command_line, _In_ int show_command) { + _In_ wchar_t* command_line, _In_ int show_command) { // Attach to console when present (e.g., 'flutter run') or create a // new console when running with a debugger. if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { diff --git a/packages/path_provider/path_provider_windows/example/windows/runner/utils.cpp b/packages/path_provider/path_provider_windows/example/windows/runner/utils.cpp index 537728149601..e875ce8b05a9 100644 --- a/packages/path_provider/path_provider_windows/example/windows/runner/utils.cpp +++ b/packages/path_provider/path_provider_windows/example/windows/runner/utils.cpp @@ -13,7 +13,7 @@ void CreateAndAttachConsole() { if (::AllocConsole()) { - FILE *unused; + FILE* unused; if (freopen_s(&unused, "CONOUT$", "w", stdout)) { _dup2(_fileno(stdout), 1); } diff --git a/packages/quick_actions/quick_actions/example/ios/Runner/main.m b/packages/quick_actions/quick_actions/example/ios/Runner/main.m index f97b9ef5c8a1..f143297b30d6 100644 --- a/packages/quick_actions/quick_actions/example/ios/Runner/main.m +++ b/packages/quick_actions/quick_actions/example/ios/Runner/main.m @@ -6,7 +6,7 @@ #import #import "AppDelegate.h" -int main(int argc, char* argv[]) { +int main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } diff --git a/packages/quick_actions/quick_actions/example/ios/RunnerTests/RunnerTests.m b/packages/quick_actions/quick_actions/example/ios/RunnerTests/RunnerTests.m index 64e0f7e1d8b2..ddcdc6a8defc 100644 --- a/packages/quick_actions/quick_actions/example/ios/RunnerTests/RunnerTests.m +++ b/packages/quick_actions/quick_actions/example/ios/RunnerTests/RunnerTests.m @@ -11,7 +11,7 @@ @interface QuickActionsTests : XCTestCase @implementation QuickActionsTests - (void)testPlugin { - FLTQuickActionsPlugin* plugin = [[FLTQuickActionsPlugin alloc] init]; + FLTQuickActionsPlugin *plugin = [[FLTQuickActionsPlugin alloc] init]; XCTAssertNotNil(plugin); } diff --git a/packages/shared_preferences/shared_preferences/example/ios/Runner/main.m b/packages/shared_preferences/shared_preferences/example/ios/Runner/main.m index f97b9ef5c8a1..f143297b30d6 100644 --- a/packages/shared_preferences/shared_preferences/example/ios/Runner/main.m +++ b/packages/shared_preferences/shared_preferences/example/ios/Runner/main.m @@ -6,7 +6,7 @@ #import #import "AppDelegate.h" -int main(int argc, char* argv[]) { +int main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } diff --git a/packages/shared_preferences/shared_preferences/example/windows/runner/main.cpp b/packages/shared_preferences/shared_preferences/example/windows/runner/main.cpp index 126302b0be18..c7dbde1c7123 100644 --- a/packages/shared_preferences/shared_preferences/example/windows/runner/main.cpp +++ b/packages/shared_preferences/shared_preferences/example/windows/runner/main.cpp @@ -11,7 +11,7 @@ #include "utils.h" int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, - _In_ wchar_t *command_line, _In_ int show_command) { + _In_ wchar_t* command_line, _In_ int show_command) { // Attach to console when present (e.g., 'flutter run') or create a // new console when running with a debugger. if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { diff --git a/packages/shared_preferences/shared_preferences/example/windows/runner/utils.cpp b/packages/shared_preferences/shared_preferences/example/windows/runner/utils.cpp index 537728149601..e875ce8b05a9 100644 --- a/packages/shared_preferences/shared_preferences/example/windows/runner/utils.cpp +++ b/packages/shared_preferences/shared_preferences/example/windows/runner/utils.cpp @@ -13,7 +13,7 @@ void CreateAndAttachConsole() { if (::AllocConsole()) { - FILE *unused; + FILE* unused; if (freopen_s(&unused, "CONOUT$", "w", stdout)) { _dup2(_fileno(stdout), 1); } diff --git a/packages/shared_preferences/shared_preferences_ios/example/ios/Runner/main.m b/packages/shared_preferences/shared_preferences_ios/example/ios/Runner/main.m index f97b9ef5c8a1..f143297b30d6 100644 --- a/packages/shared_preferences/shared_preferences_ios/example/ios/Runner/main.m +++ b/packages/shared_preferences/shared_preferences_ios/example/ios/Runner/main.m @@ -6,7 +6,7 @@ #import #import "AppDelegate.h" -int main(int argc, char* argv[]) { +int main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } diff --git a/packages/shared_preferences/shared_preferences_ios/example/ios/RunnerTests/SharedPreferencesTests.m b/packages/shared_preferences/shared_preferences_ios/example/ios/RunnerTests/SharedPreferencesTests.m index 94b3f9f0f517..792f6c111b82 100644 --- a/packages/shared_preferences/shared_preferences_ios/example/ios/RunnerTests/SharedPreferencesTests.m +++ b/packages/shared_preferences/shared_preferences_ios/example/ios/RunnerTests/SharedPreferencesTests.m @@ -11,7 +11,7 @@ @interface SharedPreferencesTests : XCTestCase @implementation SharedPreferencesTests - (void)testPlugin { - FLTSharedPreferencesPlugin* plugin = [[FLTSharedPreferencesPlugin alloc] init]; + FLTSharedPreferencesPlugin *plugin = [[FLTSharedPreferencesPlugin alloc] init]; XCTAssertNotNil(plugin); } diff --git a/packages/shared_preferences/shared_preferences_windows/example/windows/runner/main.cpp b/packages/shared_preferences/shared_preferences_windows/example/windows/runner/main.cpp index 126302b0be18..c7dbde1c7123 100644 --- a/packages/shared_preferences/shared_preferences_windows/example/windows/runner/main.cpp +++ b/packages/shared_preferences/shared_preferences_windows/example/windows/runner/main.cpp @@ -11,7 +11,7 @@ #include "utils.h" int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, - _In_ wchar_t *command_line, _In_ int show_command) { + _In_ wchar_t* command_line, _In_ int show_command) { // Attach to console when present (e.g., 'flutter run') or create a // new console when running with a debugger. if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { diff --git a/packages/shared_preferences/shared_preferences_windows/example/windows/runner/utils.cpp b/packages/shared_preferences/shared_preferences_windows/example/windows/runner/utils.cpp index 537728149601..e875ce8b05a9 100644 --- a/packages/shared_preferences/shared_preferences_windows/example/windows/runner/utils.cpp +++ b/packages/shared_preferences/shared_preferences_windows/example/windows/runner/utils.cpp @@ -13,7 +13,7 @@ void CreateAndAttachConsole() { if (::AllocConsole()) { - FILE *unused; + FILE* unused; if (freopen_s(&unused, "CONOUT$", "w", stdout)) { _dup2(_fileno(stdout), 1); } diff --git a/packages/url_launcher/url_launcher/example/ios/Runner/main.m b/packages/url_launcher/url_launcher/example/ios/Runner/main.m index f97b9ef5c8a1..f143297b30d6 100644 --- a/packages/url_launcher/url_launcher/example/ios/Runner/main.m +++ b/packages/url_launcher/url_launcher/example/ios/Runner/main.m @@ -6,7 +6,7 @@ #import #import "AppDelegate.h" -int main(int argc, char* argv[]) { +int main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } diff --git a/packages/url_launcher/url_launcher/example/windows/runner/main.cpp b/packages/url_launcher/url_launcher/example/windows/runner/main.cpp index 126302b0be18..c7dbde1c7123 100644 --- a/packages/url_launcher/url_launcher/example/windows/runner/main.cpp +++ b/packages/url_launcher/url_launcher/example/windows/runner/main.cpp @@ -11,7 +11,7 @@ #include "utils.h" int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, - _In_ wchar_t *command_line, _In_ int show_command) { + _In_ wchar_t* command_line, _In_ int show_command) { // Attach to console when present (e.g., 'flutter run') or create a // new console when running with a debugger. if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { diff --git a/packages/url_launcher/url_launcher/example/windows/runner/utils.cpp b/packages/url_launcher/url_launcher/example/windows/runner/utils.cpp index 537728149601..e875ce8b05a9 100644 --- a/packages/url_launcher/url_launcher/example/windows/runner/utils.cpp +++ b/packages/url_launcher/url_launcher/example/windows/runner/utils.cpp @@ -13,7 +13,7 @@ void CreateAndAttachConsole() { if (::AllocConsole()) { - FILE *unused; + FILE* unused; if (freopen_s(&unused, "CONOUT$", "w", stdout)) { _dup2(_fileno(stdout), 1); } diff --git a/packages/url_launcher/url_launcher_ios/example/ios/Runner/main.m b/packages/url_launcher/url_launcher_ios/example/ios/Runner/main.m index f97b9ef5c8a1..f143297b30d6 100644 --- a/packages/url_launcher/url_launcher_ios/example/ios/Runner/main.m +++ b/packages/url_launcher/url_launcher_ios/example/ios/Runner/main.m @@ -6,7 +6,7 @@ #import #import "AppDelegate.h" -int main(int argc, char* argv[]) { +int main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } diff --git a/packages/url_launcher/url_launcher_ios/example/ios/RunnerTests/URLLauncherTests.m b/packages/url_launcher/url_launcher_ios/example/ios/RunnerTests/URLLauncherTests.m index a94bd88f24fe..6507a95a9d07 100644 --- a/packages/url_launcher/url_launcher_ios/example/ios/RunnerTests/URLLauncherTests.m +++ b/packages/url_launcher/url_launcher_ios/example/ios/RunnerTests/URLLauncherTests.m @@ -11,7 +11,7 @@ @interface URLLauncherTests : XCTestCase @implementation URLLauncherTests - (void)testPlugin { - FLTURLLauncherPlugin* plugin = [[FLTURLLauncherPlugin alloc] init]; + FLTURLLauncherPlugin *plugin = [[FLTURLLauncherPlugin alloc] init]; XCTAssertNotNil(plugin); } diff --git a/packages/url_launcher/url_launcher_ios/example/ios/RunnerUITests/URLLauncherUITests.m b/packages/url_launcher/url_launcher_ios/example/ios/RunnerUITests/URLLauncherUITests.m index 18af3be9a1e5..b6d3bceff039 100644 --- a/packages/url_launcher/url_launcher_ios/example/ios/RunnerUITests/URLLauncherUITests.m +++ b/packages/url_launcher/url_launcher_ios/example/ios/RunnerUITests/URLLauncherUITests.m @@ -6,7 +6,7 @@ @import os.log; @interface URLLauncherUITests : XCTestCase -@property(nonatomic, strong) XCUIApplication* app; +@property(nonatomic, strong) XCUIApplication *app; @end @implementation URLLauncherUITests @@ -19,18 +19,18 @@ - (void)setUp { } - (void)testLaunch { - XCUIApplication* app = self.app; + XCUIApplication *app = self.app; - NSArray* buttonNames = @[ + NSArray *buttonNames = @[ @"Launch in app", @"Launch in app(JavaScript ON)", @"Launch in app(DOM storage ON)", @"Launch a universal link in a native app, fallback to Safari.(Youtube)" ]; - for (NSString* buttonName in buttonNames) { - XCUIElement* button = app.buttons[buttonName]; + for (NSString *buttonName in buttonNames) { + XCUIElement *button = app.buttons[buttonName]; XCTAssertTrue([button waitForExistenceWithTimeout:30.0]); XCTAssertEqual(app.webViews.count, 0); [button tap]; - XCUIElement* webView = app.webViews.firstMatch; + XCUIElement *webView = app.webViews.firstMatch; XCTAssertTrue([webView waitForExistenceWithTimeout:30.0]); XCTAssertTrue([app.buttons[@"ForwardButton"] waitForExistenceWithTimeout:30.0]); XCTAssertTrue(app.buttons[@"Share"].exists); diff --git a/packages/url_launcher/url_launcher_windows/example/windows/runner/main.cpp b/packages/url_launcher/url_launcher_windows/example/windows/runner/main.cpp index 126302b0be18..c7dbde1c7123 100644 --- a/packages/url_launcher/url_launcher_windows/example/windows/runner/main.cpp +++ b/packages/url_launcher/url_launcher_windows/example/windows/runner/main.cpp @@ -11,7 +11,7 @@ #include "utils.h" int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, - _In_ wchar_t *command_line, _In_ int show_command) { + _In_ wchar_t* command_line, _In_ int show_command) { // Attach to console when present (e.g., 'flutter run') or create a // new console when running with a debugger. if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { diff --git a/packages/url_launcher/url_launcher_windows/example/windows/runner/utils.cpp b/packages/url_launcher/url_launcher_windows/example/windows/runner/utils.cpp index 537728149601..e875ce8b05a9 100644 --- a/packages/url_launcher/url_launcher_windows/example/windows/runner/utils.cpp +++ b/packages/url_launcher/url_launcher_windows/example/windows/runner/utils.cpp @@ -13,7 +13,7 @@ void CreateAndAttachConsole() { if (::AllocConsole()) { - FILE *unused; + FILE* unused; if (freopen_s(&unused, "CONOUT$", "w", stdout)) { _dup2(_fileno(stdout), 1); } diff --git a/packages/video_player/video_player/example/ios/Runner/main.m b/packages/video_player/video_player/example/ios/Runner/main.m index f97b9ef5c8a1..f143297b30d6 100644 --- a/packages/video_player/video_player/example/ios/Runner/main.m +++ b/packages/video_player/video_player/example/ios/Runner/main.m @@ -6,7 +6,7 @@ #import #import "AppDelegate.h" -int main(int argc, char* argv[]) { +int main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } diff --git a/packages/video_player/video_player/example/ios/RunnerUITests/VideoPlayerUITests.m b/packages/video_player/video_player/example/ios/RunnerUITests/VideoPlayerUITests.m index 5452c11cbf86..7c92493ee17a 100644 --- a/packages/video_player/video_player/example/ios/RunnerUITests/VideoPlayerUITests.m +++ b/packages/video_player/video_player/example/ios/RunnerUITests/VideoPlayerUITests.m @@ -6,7 +6,7 @@ @import XCTest; @interface VideoPlayerUITests : XCTestCase -@property(nonatomic, strong) XCUIApplication* app; +@property(nonatomic, strong) XCUIApplication *app; @end @implementation VideoPlayerUITests @@ -19,43 +19,43 @@ - (void)setUp { } - (void)testPlayVideo { - XCUIApplication* app = self.app; + XCUIApplication *app = self.app; - XCUIElement* remoteTab = [app.otherElements + XCUIElement *remoteTab = [app.otherElements elementMatchingPredicate:[NSPredicate predicateWithFormat:@"selected == YES"]]; XCTAssertTrue([remoteTab waitForExistenceWithTimeout:30.0]); XCTAssertTrue([remoteTab.label containsString:@"Remote"]); - XCUIElement* playButton = app.staticTexts[@"Play"]; + XCUIElement *playButton = app.staticTexts[@"Play"]; XCTAssertTrue([playButton waitForExistenceWithTimeout:30.0]); [playButton tap]; - XCUIElement* chirpClosedCaption = app.staticTexts[@"[ Birds chirping ]"]; + XCUIElement *chirpClosedCaption = app.staticTexts[@"[ Birds chirping ]"]; XCTAssertTrue([chirpClosedCaption waitForExistenceWithTimeout:30.0]); - XCUIElement* buzzClosedCaption = app.staticTexts[@"[ Buzzing ]"]; + XCUIElement *buzzClosedCaption = app.staticTexts[@"[ Buzzing ]"]; XCTAssertTrue([buzzClosedCaption waitForExistenceWithTimeout:30.0]); - XCUIElement* playbackSpeed1x = app.staticTexts[@"Playback speed\n1.0x"]; + XCUIElement *playbackSpeed1x = app.staticTexts[@"Playback speed\n1.0x"]; XCTAssertTrue([playbackSpeed1x waitForExistenceWithTimeout:30.0]); [playbackSpeed1x tap]; - XCUIElement* playbackSpeed5xButton = app.buttons[@"5.0x"]; + XCUIElement *playbackSpeed5xButton = app.buttons[@"5.0x"]; XCTAssertTrue([playbackSpeed5xButton waitForExistenceWithTimeout:30.0]); [playbackSpeed5xButton tap]; - XCUIElement* playbackSpeed5x = app.staticTexts[@"Playback speed\n5.0x"]; + XCUIElement *playbackSpeed5x = app.staticTexts[@"Playback speed\n5.0x"]; XCTAssertTrue([playbackSpeed5x waitForExistenceWithTimeout:30.0]); // Cycle through tabs. - for (NSString* tabName in @[ @"Asset", @"List example" ]) { - NSPredicate* predicate = [NSPredicate predicateWithFormat:@"label BEGINSWITH %@", tabName]; - XCUIElement* unselectedTab = [app.staticTexts elementMatchingPredicate:predicate]; + for (NSString *tabName in @[ @"Asset", @"List example" ]) { + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"label BEGINSWITH %@", tabName]; + XCUIElement *unselectedTab = [app.staticTexts elementMatchingPredicate:predicate]; XCTAssertTrue([unselectedTab waitForExistenceWithTimeout:30.0]); XCTAssertFalse(unselectedTab.isSelected); [unselectedTab tap]; - XCUIElement* selectedTab = [app.otherElements + XCUIElement *selectedTab = [app.otherElements elementMatchingPredicate:[NSPredicate predicateWithFormat:@"label BEGINSWITH %@", tabName]]; XCTAssertTrue([selectedTab waitForExistenceWithTimeout:30.0]); XCTAssertTrue(selectedTab.isSelected); diff --git a/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.h b/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.h index 2514aee71f10..a737d05628f8 100644 --- a/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.h +++ b/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.h @@ -5,5 +5,5 @@ #import @interface FLTVideoPlayerPlugin : NSObject -- (instancetype)initWithRegistrar:(NSObject*)registrar; +- (instancetype)initWithRegistrar:(NSObject *)registrar; @end diff --git a/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m b/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m index a67e37138b9a..696fba21f661 100644 --- a/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m +++ b/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m @@ -13,54 +13,54 @@ @interface FLTFrameUpdater : NSObject @property(nonatomic) int64_t textureId; -@property(nonatomic, weak, readonly) NSObject* registry; -- (void)onDisplayLink:(CADisplayLink*)link; +@property(nonatomic, weak, readonly) NSObject *registry; +- (void)onDisplayLink:(CADisplayLink *)link; @end @implementation FLTFrameUpdater -- (FLTFrameUpdater*)initWithRegistry:(NSObject*)registry { +- (FLTFrameUpdater *)initWithRegistry:(NSObject *)registry { NSAssert(self, @"super init cannot be nil"); if (self == nil) return nil; _registry = registry; return self; } -- (void)onDisplayLink:(CADisplayLink*)link { +- (void)onDisplayLink:(CADisplayLink *)link { [_registry textureFrameAvailable:_textureId]; } @end @interface FLTVideoPlayer : NSObject -@property(readonly, nonatomic) AVPlayer* player; -@property(readonly, nonatomic) AVPlayerItemVideoOutput* videoOutput; -@property(readonly, nonatomic) CADisplayLink* displayLink; -@property(nonatomic) FlutterEventChannel* eventChannel; +@property(readonly, nonatomic) AVPlayer *player; +@property(readonly, nonatomic) AVPlayerItemVideoOutput *videoOutput; +@property(readonly, nonatomic) CADisplayLink *displayLink; +@property(nonatomic) FlutterEventChannel *eventChannel; @property(nonatomic) FlutterEventSink eventSink; @property(nonatomic) CGAffineTransform preferredTransform; @property(nonatomic, readonly) BOOL disposed; @property(nonatomic, readonly) BOOL isPlaying; @property(nonatomic) BOOL isLooping; @property(nonatomic, readonly) BOOL isInitialized; -- (instancetype)initWithURL:(NSURL*)url - frameUpdater:(FLTFrameUpdater*)frameUpdater - httpHeaders:(NSDictionary*)headers; +- (instancetype)initWithURL:(NSURL *)url + frameUpdater:(FLTFrameUpdater *)frameUpdater + httpHeaders:(NSDictionary *)headers; @end -static void* timeRangeContext = &timeRangeContext; -static void* statusContext = &statusContext; -static void* presentationSizeContext = &presentationSizeContext; -static void* durationContext = &durationContext; -static void* playbackLikelyToKeepUpContext = &playbackLikelyToKeepUpContext; -static void* playbackBufferEmptyContext = &playbackBufferEmptyContext; -static void* playbackBufferFullContext = &playbackBufferFullContext; +static void *timeRangeContext = &timeRangeContext; +static void *statusContext = &statusContext; +static void *presentationSizeContext = &presentationSizeContext; +static void *durationContext = &durationContext; +static void *playbackLikelyToKeepUpContext = &playbackLikelyToKeepUpContext; +static void *playbackBufferEmptyContext = &playbackBufferEmptyContext; +static void *playbackBufferFullContext = &playbackBufferFullContext; @implementation FLTVideoPlayer -- (instancetype)initWithAsset:(NSString*)asset frameUpdater:(FLTFrameUpdater*)frameUpdater { - NSString* path = [[NSBundle mainBundle] pathForResource:asset ofType:nil]; +- (instancetype)initWithAsset:(NSString *)asset frameUpdater:(FLTFrameUpdater *)frameUpdater { + NSString *path = [[NSBundle mainBundle] pathForResource:asset ofType:nil]; return [self initWithURL:[NSURL fileURLWithPath:path] frameUpdater:frameUpdater httpHeaders:nil]; } -- (void)addObservers:(AVPlayerItem*)item { +- (void)addObservers:(AVPlayerItem *)item { [item addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew @@ -97,9 +97,9 @@ - (void)addObservers:(AVPlayerItem*)item { object:item]; } -- (void)itemDidPlayToEndTime:(NSNotification*)notification { +- (void)itemDidPlayToEndTime:(NSNotification *)notification { if (_isLooping) { - AVPlayerItem* p = [notification object]; + AVPlayerItem *p = [notification object]; [p seekToTime:kCMTimeZero completionHandler:nil]; } else { if (_eventSink) { @@ -129,18 +129,18 @@ NS_INLINE CGFloat radiansToDegrees(CGFloat radians) { return degrees; }; -- (AVMutableVideoComposition*)getVideoCompositionWithTransform:(CGAffineTransform)transform - withAsset:(AVAsset*)asset - withVideoTrack:(AVAssetTrack*)videoTrack { - AVMutableVideoCompositionInstruction* instruction = +- (AVMutableVideoComposition *)getVideoCompositionWithTransform:(CGAffineTransform)transform + withAsset:(AVAsset *)asset + withVideoTrack:(AVAssetTrack *)videoTrack { + AVMutableVideoCompositionInstruction *instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; instruction.timeRange = CMTimeRangeMake(kCMTimeZero, [asset duration]); - AVMutableVideoCompositionLayerInstruction* layerInstruction = + AVMutableVideoCompositionLayerInstruction *layerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack]; [layerInstruction setTransform:_preferredTransform atTime:kCMTimeZero]; - AVMutableVideoComposition* videoComposition = [AVMutableVideoComposition videoComposition]; + AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition]; instruction.layerInstructions = @[ layerInstruction ]; videoComposition.instructions = @[ instruction ]; @@ -162,8 +162,8 @@ - (AVMutableVideoComposition*)getVideoCompositionWithTransform:(CGAffineTransfor return videoComposition; } -- (void)createVideoOutputAndDisplayLink:(FLTFrameUpdater*)frameUpdater { - NSDictionary* pixBuffAttributes = @{ +- (void)createVideoOutputAndDisplayLink:(FLTFrameUpdater *)frameUpdater { + NSDictionary *pixBuffAttributes = @{ (id)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA), (id)kCVPixelBufferIOSurfacePropertiesKey : @{} }; @@ -175,19 +175,19 @@ - (void)createVideoOutputAndDisplayLink:(FLTFrameUpdater*)frameUpdater { _displayLink.paused = YES; } -- (instancetype)initWithURL:(NSURL*)url - frameUpdater:(FLTFrameUpdater*)frameUpdater - httpHeaders:(NSDictionary*)headers { - NSDictionary* options = nil; +- (instancetype)initWithURL:(NSURL *)url + frameUpdater:(FLTFrameUpdater *)frameUpdater + httpHeaders:(NSDictionary *)headers { + NSDictionary *options = nil; if (headers != nil && [headers count] != 0) { options = @{@"AVURLAssetHTTPHeaderFieldsKey" : headers}; } - AVURLAsset* urlAsset = [AVURLAsset URLAssetWithURL:url options:options]; - AVPlayerItem* item = [AVPlayerItem playerItemWithAsset:urlAsset]; + AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:url options:options]; + AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:urlAsset]; return [self initWithPlayerItem:item frameUpdater:frameUpdater]; } -- (CGAffineTransform)fixTransform:(AVAssetTrack*)videoTrack { +- (CGAffineTransform)fixTransform:(AVAssetTrack *)videoTrack { CGAffineTransform transform = videoTrack.preferredTransform; // TODO(@recastrodiaz): why do we need to do this? Why is the preferredTransform incorrect? // At least 2 user videos show a black screen when in portrait mode if we directly use the @@ -210,16 +210,17 @@ - (CGAffineTransform)fixTransform:(AVAssetTrack*)videoTrack { return transform; } -- (instancetype)initWithPlayerItem:(AVPlayerItem*)item frameUpdater:(FLTFrameUpdater*)frameUpdater { +- (instancetype)initWithPlayerItem:(AVPlayerItem *)item + frameUpdater:(FLTFrameUpdater *)frameUpdater { self = [super init]; NSAssert(self, @"super init cannot be nil"); - AVAsset* asset = [item asset]; + AVAsset *asset = [item asset]; void (^assetCompletionHandler)(void) = ^{ if ([asset statusOfValueForKey:@"tracks" error:nil] == AVKeyValueStatusLoaded) { - NSArray* tracks = [asset tracksWithMediaType:AVMediaTypeVideo]; + NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo]; if ([tracks count] > 0) { - AVAssetTrack* videoTrack = tracks[0]; + AVAssetTrack *videoTrack = tracks[0]; void (^trackCompletionHandler)(void) = ^{ if (self->_disposed) return; if ([videoTrack statusOfValueForKey:@"preferredTransform" @@ -230,7 +231,7 @@ - (instancetype)initWithPlayerItem:(AVPlayerItem*)item frameUpdater:(FLTFrameUpd // https://developer.apple.com/documentation/avfoundation/avplayeritem/1388818-videocomposition // Video composition can only be used with file-based media and is not supported for // use with media served using HTTP Live Streaming. - AVMutableVideoComposition* videoComposition = + AVMutableVideoComposition *videoComposition = [self getVideoCompositionWithTransform:self->_preferredTransform withAsset:asset withVideoTrack:videoTrack]; @@ -255,14 +256,14 @@ - (instancetype)initWithPlayerItem:(AVPlayerItem*)item frameUpdater:(FLTFrameUpd return self; } -- (void)observeValueForKeyPath:(NSString*)path +- (void)observeValueForKeyPath:(NSString *)path ofObject:(id)object - change:(NSDictionary*)change - context:(void*)context { + change:(NSDictionary *)change + context:(void *)context { if (context == timeRangeContext) { if (_eventSink != nil) { - NSMutableArray*>* values = [[NSMutableArray alloc] init]; - for (NSValue* rangeValue in [object loadedTimeRanges]) { + NSMutableArray *> *values = [[NSMutableArray alloc] init]; + for (NSValue *rangeValue in [object loadedTimeRanges]) { CMTimeRange range = [rangeValue CMTimeRangeValue]; int64_t start = FLTCMTimeToMillis(range.start); [values addObject:@[ @(start), @(start + FLTCMTimeToMillis(range.duration)) ]]; @@ -270,7 +271,7 @@ - (void)observeValueForKeyPath:(NSString*)path _eventSink(@{@"event" : @"bufferingUpdate", @"values" : values}); } } else if (context == statusContext) { - AVPlayerItem* item = (AVPlayerItem*)object; + AVPlayerItem *item = (AVPlayerItem *)object; switch (item.status) { case AVPlayerItemStatusFailed: if (_eventSink != nil) { @@ -290,7 +291,7 @@ - (void)observeValueForKeyPath:(NSString*)path break; } } else if (context == presentationSizeContext || context == durationContext) { - AVPlayerItem* item = (AVPlayerItem*)object; + AVPlayerItem *item = (AVPlayerItem *)object; if (item.status == AVPlayerItemStatusReadyToPlay) { // Due to an apparent bug, when the player item is ready, it still may not have determined // its presentation size or duration. When these properties are finally set, re-check if @@ -423,18 +424,18 @@ - (CVPixelBufferRef)copyPixelBuffer { } } -- (void)onTextureUnregistered:(NSObject*)texture { +- (void)onTextureUnregistered:(NSObject *)texture { dispatch_async(dispatch_get_main_queue(), ^{ [self dispose]; }); } -- (FlutterError* _Nullable)onCancelWithArguments:(id _Nullable)arguments { +- (FlutterError *_Nullable)onCancelWithArguments:(id _Nullable)arguments { _eventSink = nil; return nil; } -- (FlutterError* _Nullable)onListenWithArguments:(id _Nullable)arguments +- (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments eventSink:(nonnull FlutterEventSink)events { _eventSink = events; // TODO(@recastrodiaz): remove the line below when the race condition is resolved: @@ -452,7 +453,7 @@ - (FlutterError* _Nullable)onListenWithArguments:(id _Nullable)arguments - (void)disposeSansEventChannel { _disposed = YES; [_displayLink invalidate]; - AVPlayerItem* currentItem = self.player.currentItem; + AVPlayerItem *currentItem = self.player.currentItem; [currentItem removeObserver:self forKeyPath:@"status"]; [currentItem removeObserver:self forKeyPath:@"loadedTimeRanges"]; [currentItem removeObserver:self forKeyPath:@"presentationSize"]; @@ -473,21 +474,21 @@ - (void)dispose { @end @interface FLTVideoPlayerPlugin () -@property(readonly, weak, nonatomic) NSObject* registry; -@property(readonly, weak, nonatomic) NSObject* messenger; +@property(readonly, weak, nonatomic) NSObject *registry; +@property(readonly, weak, nonatomic) NSObject *messenger; @property(readonly, strong, nonatomic) - NSMutableDictionary* playersByTextureId; -@property(readonly, strong, nonatomic) NSObject* registrar; + NSMutableDictionary *playersByTextureId; +@property(readonly, strong, nonatomic) NSObject *registrar; @end @implementation FLTVideoPlayerPlugin -+ (void)registerWithRegistrar:(NSObject*)registrar { - FLTVideoPlayerPlugin* instance = [[FLTVideoPlayerPlugin alloc] initWithRegistrar:registrar]; ++ (void)registerWithRegistrar:(NSObject *)registrar { + FLTVideoPlayerPlugin *instance = [[FLTVideoPlayerPlugin alloc] initWithRegistrar:registrar]; [registrar publish:instance]; FLTVideoPlayerApiSetup(registrar.messenger, instance); } -- (instancetype)initWithRegistrar:(NSObject*)registrar { +- (instancetype)initWithRegistrar:(NSObject *)registrar { self = [super init]; NSAssert(self, @"super init cannot be nil"); _registry = [registrar textures]; @@ -497,7 +498,7 @@ - (instancetype)initWithRegistrar:(NSObject*)registrar { return self; } -- (void)detachFromEngineForRegistrar:(NSObject*)registrar { +- (void)detachFromEngineForRegistrar:(NSObject *)registrar { [self.playersByTextureId.allValues makeObjectsPerformSelector:@selector(disposeSansEventChannel)]; [self.playersByTextureId removeAllObjects]; // TODO(57151): This should be commented out when 57151's fix lands on stable. @@ -506,39 +507,39 @@ - (void)detachFromEngineForRegistrar:(NSObject*)registra // FLTVideoPlayerApiSetup(registrar.messenger, nil); } -- (FLTTextureMessage*)onPlayerSetup:(FLTVideoPlayer*)player - frameUpdater:(FLTFrameUpdater*)frameUpdater { +- (FLTTextureMessage *)onPlayerSetup:(FLTVideoPlayer *)player + frameUpdater:(FLTFrameUpdater *)frameUpdater { int64_t textureId = [self.registry registerTexture:player]; frameUpdater.textureId = textureId; - FlutterEventChannel* eventChannel = [FlutterEventChannel + FlutterEventChannel *eventChannel = [FlutterEventChannel eventChannelWithName:[NSString stringWithFormat:@"flutter.io/videoPlayer/videoEvents%lld", textureId] binaryMessenger:_messenger]; [eventChannel setStreamHandler:player]; player.eventChannel = eventChannel; self.playersByTextureId[@(textureId)] = player; - FLTTextureMessage* result = [[FLTTextureMessage alloc] init]; + FLTTextureMessage *result = [[FLTTextureMessage alloc] init]; result.textureId = @(textureId); return result; } -- (void)initialize:(FlutterError* __autoreleasing*)error { +- (void)initialize:(FlutterError *__autoreleasing *)error { // Allow audio playback when the Ring/Silent switch is set to silent [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil]; [self.playersByTextureId - enumerateKeysAndObjectsUsingBlock:^(NSNumber* textureId, FLTVideoPlayer* player, BOOL* stop) { + enumerateKeysAndObjectsUsingBlock:^(NSNumber *textureId, FLTVideoPlayer *player, BOOL *stop) { [self.registry unregisterTexture:textureId.unsignedIntegerValue]; [player dispose]; }]; [self.playersByTextureId removeAllObjects]; } -- (FLTTextureMessage*)create:(FLTCreateMessage*)input error:(FlutterError**)error { - FLTFrameUpdater* frameUpdater = [[FLTFrameUpdater alloc] initWithRegistry:_registry]; - FLTVideoPlayer* player; +- (FLTTextureMessage *)create:(FLTCreateMessage *)input error:(FlutterError **)error { + FLTFrameUpdater *frameUpdater = [[FLTFrameUpdater alloc] initWithRegistry:_registry]; + FLTVideoPlayer *player; if (input.asset) { - NSString* assetPath; + NSString *assetPath; if (input.packageName) { assetPath = [_registrar lookupKeyForAsset:input.asset fromPackage:input.packageName]; } else { @@ -557,8 +558,8 @@ - (FLTTextureMessage*)create:(FLTCreateMessage*)input error:(FlutterError**)erro } } -- (void)dispose:(FLTTextureMessage*)input error:(FlutterError**)error { - FLTVideoPlayer* player = self.playersByTextureId[input.textureId]; +- (void)dispose:(FLTTextureMessage *)input error:(FlutterError **)error { + FLTVideoPlayer *player = self.playersByTextureId[input.textureId]; [self.registry unregisterTexture:input.textureId.intValue]; [self.playersByTextureId removeObjectForKey:input.textureId]; // If the Flutter contains https://github.com/flutter/engine/pull/12695, @@ -579,46 +580,46 @@ - (void)dispose:(FLTTextureMessage*)input error:(FlutterError**)error { }); } -- (void)setLooping:(FLTLoopingMessage*)input error:(FlutterError**)error { - FLTVideoPlayer* player = self.playersByTextureId[input.textureId]; +- (void)setLooping:(FLTLoopingMessage *)input error:(FlutterError **)error { + FLTVideoPlayer *player = self.playersByTextureId[input.textureId]; player.isLooping = input.isLooping.boolValue; } -- (void)setVolume:(FLTVolumeMessage*)input error:(FlutterError**)error { - FLTVideoPlayer* player = self.playersByTextureId[input.textureId]; +- (void)setVolume:(FLTVolumeMessage *)input error:(FlutterError **)error { + FLTVideoPlayer *player = self.playersByTextureId[input.textureId]; [player setVolume:input.volume.doubleValue]; } -- (void)setPlaybackSpeed:(FLTPlaybackSpeedMessage*)input error:(FlutterError**)error { - FLTVideoPlayer* player = self.playersByTextureId[input.textureId]; +- (void)setPlaybackSpeed:(FLTPlaybackSpeedMessage *)input error:(FlutterError **)error { + FLTVideoPlayer *player = self.playersByTextureId[input.textureId]; [player setPlaybackSpeed:input.speed.doubleValue]; } -- (void)play:(FLTTextureMessage*)input error:(FlutterError**)error { - FLTVideoPlayer* player = self.playersByTextureId[input.textureId]; +- (void)play:(FLTTextureMessage *)input error:(FlutterError **)error { + FLTVideoPlayer *player = self.playersByTextureId[input.textureId]; [player play]; } -- (FLTPositionMessage*)position:(FLTTextureMessage*)input error:(FlutterError**)error { - FLTVideoPlayer* player = self.playersByTextureId[input.textureId]; - FLTPositionMessage* result = [[FLTPositionMessage alloc] init]; +- (FLTPositionMessage *)position:(FLTTextureMessage *)input error:(FlutterError **)error { + FLTVideoPlayer *player = self.playersByTextureId[input.textureId]; + FLTPositionMessage *result = [[FLTPositionMessage alloc] init]; result.position = @([player position]); return result; } -- (void)seekTo:(FLTPositionMessage*)input error:(FlutterError**)error { - FLTVideoPlayer* player = self.playersByTextureId[input.textureId]; +- (void)seekTo:(FLTPositionMessage *)input error:(FlutterError **)error { + FLTVideoPlayer *player = self.playersByTextureId[input.textureId]; [player seekTo:input.position.intValue]; [self.registry textureFrameAvailable:input.textureId.intValue]; } -- (void)pause:(FLTTextureMessage*)input error:(FlutterError**)error { - FLTVideoPlayer* player = self.playersByTextureId[input.textureId]; +- (void)pause:(FLTTextureMessage *)input error:(FlutterError **)error { + FLTVideoPlayer *player = self.playersByTextureId[input.textureId]; [player pause]; } -- (void)setMixWithOthers:(FLTMixWithOthersMessage*)input - error:(FlutterError* _Nullable __autoreleasing*)error { +- (void)setMixWithOthers:(FLTMixWithOthersMessage *)input + error:(FlutterError *_Nullable __autoreleasing *)error { if (input.mixWithOthers.boolValue) { [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers diff --git a/packages/webview_flutter/webview_flutter/example/ios/Runner/main.m b/packages/webview_flutter/webview_flutter/example/ios/Runner/main.m index f97b9ef5c8a1..f143297b30d6 100644 --- a/packages/webview_flutter/webview_flutter/example/ios/Runner/main.m +++ b/packages/webview_flutter/webview_flutter/example/ios/Runner/main.m @@ -6,7 +6,7 @@ #import #import "AppDelegate.h" -int main(int argc, char* argv[]) { +int main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } diff --git a/packages/webview_flutter/webview_flutter/example/ios/RunnerUITests/FLTWebViewUITests.m b/packages/webview_flutter/webview_flutter/example/ios/RunnerUITests/FLTWebViewUITests.m index d193be745972..d6870dc9a29c 100644 --- a/packages/webview_flutter/webview_flutter/example/ios/RunnerUITests/FLTWebViewUITests.m +++ b/packages/webview_flutter/webview_flutter/example/ios/RunnerUITests/FLTWebViewUITests.m @@ -6,7 +6,7 @@ @import os.log; @interface FLTWebViewUITests : XCTestCase -@property(nonatomic, strong) XCUIApplication* app; +@property(nonatomic, strong) XCUIApplication *app; @end @implementation FLTWebViewUITests @@ -19,22 +19,22 @@ - (void)setUp { } - (void)testUserAgent { - XCUIApplication* app = self.app; - XCUIElement* menu = app.buttons[@"Show menu"]; + XCUIApplication *app = self.app; + XCUIElement *menu = app.buttons[@"Show menu"]; if (![menu waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); XCTFail(@"Failed due to not able to find menu"); } [menu tap]; - XCUIElement* userAgent = app.buttons[@"Show user agent"]; + XCUIElement *userAgent = app.buttons[@"Show user agent"]; if (![userAgent waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); XCTFail(@"Failed due to not able to find Show user agent"); } - NSPredicate* userAgentPredicate = + NSPredicate *userAgentPredicate = [NSPredicate predicateWithFormat:@"label BEGINSWITH 'User Agent: Mozilla/5.0 (iPhone; '"]; - XCUIElement* userAgentPopUp = [app.otherElements elementMatchingPredicate:userAgentPredicate]; + XCUIElement *userAgentPopUp = [app.otherElements elementMatchingPredicate:userAgentPredicate]; XCTAssertFalse(userAgentPopUp.exists); [userAgent tap]; if (![userAgentPopUp waitForExistenceWithTimeout:30.0]) { @@ -44,15 +44,15 @@ - (void)testUserAgent { } - (void)testCache { - XCUIApplication* app = self.app; - XCUIElement* menu = app.buttons[@"Show menu"]; + XCUIApplication *app = self.app; + XCUIElement *menu = app.buttons[@"Show menu"]; if (![menu waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); XCTFail(@"Failed due to not able to find menu"); } [menu tap]; - XCUIElement* clearCache = app.buttons[@"Clear cache"]; + XCUIElement *clearCache = app.buttons[@"Clear cache"]; if (![clearCache waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); XCTFail(@"Failed due to not able to find Clear cache"); @@ -61,21 +61,21 @@ - (void)testCache { [menu tap]; - XCUIElement* listCache = app.buttons[@"List cache"]; + XCUIElement *listCache = app.buttons[@"List cache"]; if (![listCache waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); XCTFail(@"Failed due to not able to find List cache"); } [listCache tap]; - XCUIElement* emptyCachePopup = app.otherElements[@"{\"cacheKeys\":[],\"localStorage\":{}}"]; + XCUIElement *emptyCachePopup = app.otherElements[@"{\"cacheKeys\":[],\"localStorage\":{}}"]; if (![emptyCachePopup waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); XCTFail(@"Failed due to not able to find empty cache pop up"); } [menu tap]; - XCUIElement* addCache = app.buttons[@"Add to cache"]; + XCUIElement *addCache = app.buttons[@"Add to cache"]; if (![addCache waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); XCTFail(@"Failed due to not able to find Add to cache"); @@ -89,7 +89,7 @@ - (void)testCache { } [listCache tap]; - XCUIElement* cachePopup = + XCUIElement *cachePopup = app.otherElements[@"{\"cacheKeys\":[\"test_caches_entry\"],\"localStorage\":{\"test_" @"localStorage\":\"dummy_entry\"}}"]; if (![cachePopup waitForExistenceWithTimeout:30.0]) { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner/main.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner/main.m index f97b9ef5c8a1..f143297b30d6 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner/main.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner/main.m @@ -6,7 +6,7 @@ #import #import "AppDelegate.h" -int main(int argc, char* argv[]) { +int main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerUITests/FLTWebViewUITests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerUITests/FLTWebViewUITests.m index e43fb97d41ef..689a601d7bdc 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerUITests/FLTWebViewUITests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerUITests/FLTWebViewUITests.m @@ -5,9 +5,9 @@ @import XCTest; @import os.log; -static UIColor* getPixelColorInImage(CGImageRef image, size_t x, size_t y) { +static UIColor *getPixelColorInImage(CGImageRef image, size_t x, size_t y) { CFDataRef pixelData = CGDataProviderCopyData(CGImageGetDataProvider(image)); - const UInt8* data = CFDataGetBytePtr(pixelData); + const UInt8 *data = CFDataGetBytePtr(pixelData); size_t bytesPerRow = CGImageGetBytesPerRow(image); size_t pixelInfo = (bytesPerRow * y) + (x * 4); // 4 bytes per pixel @@ -25,7 +25,7 @@ } @interface FLTWebViewUITests : XCTestCase -@property(nonatomic, strong) XCUIApplication* app; +@property(nonatomic, strong) XCUIApplication *app; @end @implementation FLTWebViewUITests @@ -38,35 +38,35 @@ - (void)setUp { } - (void)testTransparentBackground { - XCUIApplication* app = self.app; - XCUIElement* menu = app.buttons[@"Show menu"]; + XCUIApplication *app = self.app; + XCUIElement *menu = app.buttons[@"Show menu"]; if (![menu waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); XCTFail(@"Failed due to not able to find menu"); } [menu tap]; - XCUIElement* transparentBackground = app.buttons[@"Transparent background example"]; + XCUIElement *transparentBackground = app.buttons[@"Transparent background example"]; if (![transparentBackground waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); XCTFail(@"Failed due to not able to find Transparent background example"); } [transparentBackground tap]; - XCUIElement* transparentBackgroundLoaded = + XCUIElement *transparentBackgroundLoaded = app.webViews.staticTexts[@"Transparent background test"]; if (![transparentBackgroundLoaded waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); XCTFail(@"Failed due to not able to find Transparent background test"); } - XCUIScreenshot* screenshot = [[XCUIScreen mainScreen] screenshot]; + XCUIScreenshot *screenshot = [[XCUIScreen mainScreen] screenshot]; - UIImage* screenshotImage = screenshot.image; + UIImage *screenshotImage = screenshot.image; CGImageRef screenshotCGImage = screenshotImage.CGImage; - UIColor* centerLeftColor = + UIColor *centerLeftColor = getPixelColorInImage(screenshotCGImage, 0, CGImageGetHeight(screenshotCGImage) / 2); - UIColor* centerColor = + UIColor *centerColor = getPixelColorInImage(screenshotCGImage, CGImageGetWidth(screenshotCGImage) / 2, CGImageGetHeight(screenshotCGImage) / 2); @@ -86,22 +86,22 @@ - (void)testTransparentBackground { } - (void)testUserAgent { - XCUIApplication* app = self.app; - XCUIElement* menu = app.buttons[@"Show menu"]; + XCUIApplication *app = self.app; + XCUIElement *menu = app.buttons[@"Show menu"]; if (![menu waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); XCTFail(@"Failed due to not able to find menu"); } [menu tap]; - XCUIElement* userAgent = app.buttons[@"Show user agent"]; + XCUIElement *userAgent = app.buttons[@"Show user agent"]; if (![userAgent waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); XCTFail(@"Failed due to not able to find Show user agent"); } - NSPredicate* userAgentPredicate = + NSPredicate *userAgentPredicate = [NSPredicate predicateWithFormat:@"label BEGINSWITH 'User Agent: Mozilla/5.0 (iPhone; '"]; - XCUIElement* userAgentPopUp = [app.otherElements elementMatchingPredicate:userAgentPredicate]; + XCUIElement *userAgentPopUp = [app.otherElements elementMatchingPredicate:userAgentPredicate]; XCTAssertFalse(userAgentPopUp.exists); [userAgent tap]; if (![userAgentPopUp waitForExistenceWithTimeout:30.0]) { @@ -111,15 +111,15 @@ - (void)testUserAgent { } - (void)testCache { - XCUIApplication* app = self.app; - XCUIElement* menu = app.buttons[@"Show menu"]; + XCUIApplication *app = self.app; + XCUIElement *menu = app.buttons[@"Show menu"]; if (![menu waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); XCTFail(@"Failed due to not able to find menu"); } [menu tap]; - XCUIElement* clearCache = app.buttons[@"Clear cache"]; + XCUIElement *clearCache = app.buttons[@"Clear cache"]; if (![clearCache waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); XCTFail(@"Failed due to not able to find Clear cache"); @@ -128,21 +128,21 @@ - (void)testCache { [menu tap]; - XCUIElement* listCache = app.buttons[@"List cache"]; + XCUIElement *listCache = app.buttons[@"List cache"]; if (![listCache waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); XCTFail(@"Failed due to not able to find List cache"); } [listCache tap]; - XCUIElement* emptyCachePopup = app.otherElements[@"{\"cacheKeys\":[],\"localStorage\":{}}"]; + XCUIElement *emptyCachePopup = app.otherElements[@"{\"cacheKeys\":[],\"localStorage\":{}}"]; if (![emptyCachePopup waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); XCTFail(@"Failed due to not able to find empty cache pop up"); } [menu tap]; - XCUIElement* addCache = app.buttons[@"Add to cache"]; + XCUIElement *addCache = app.buttons[@"Add to cache"]; if (![addCache waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); XCTFail(@"Failed due to not able to find Add to cache"); @@ -156,7 +156,7 @@ - (void)testCache { } [listCache tap]; - XCUIElement* cachePopup = + XCUIElement *cachePopup = app.otherElements[@"{\"cacheKeys\":[\"test_caches_entry\"],\"localStorage\":{\"test_" @"localStorage\":\"dummy_entry\"}}"]; if (![cachePopup waitForExistenceWithTimeout:30.0]) { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager.h index dc5b8f589ade..b18e58b57cb3 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager.h @@ -9,11 +9,11 @@ NS_ASSUME_NONNULL_BEGIN @interface FLTCookieManager : NSObject -+ (FLTCookieManager*)instance; ++ (FLTCookieManager *)instance; -- (void)setCookiesForData:(NSArray*)cookies; +- (void)setCookiesForData:(NSArray *)cookies; -- (void)setCookieForData:(NSDictionary*)cookie; +- (void)setCookieForData:(NSDictionary *)cookie; @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWebViewFlutterPlugin.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWebViewFlutterPlugin.m index 0d04d6c3a9ee..a4e87ba5177a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWebViewFlutterPlugin.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWebViewFlutterPlugin.m @@ -8,9 +8,9 @@ @implementation FLTWebViewFlutterPlugin -+ (void)registerWithRegistrar:(NSObject*)registrar { ++ (void)registerWithRegistrar:(NSObject *)registrar { [FLTCookieManager registerWithRegistrar:registrar]; - FLTWebViewFactory* webviewFactory = + FLTWebViewFactory *webviewFactory = [[FLTWebViewFactory alloc] initWithMessenger:registrar.messenger cookieManager:[FLTCookieManager instance]]; [registrar registerViewFactory:webviewFactory withId:@"plugins.flutter.io/webview"]; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.h index 0dae94290a0d..69a15fc063c8 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.h @@ -19,22 +19,22 @@ NS_ASSUME_NONNULL_BEGIN @interface FLTWebViewController : NSObject -@property(nonatomic) FLTWKWebView* webView; +@property(nonatomic) FLTWKWebView *webView; - (instancetype)initWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args - binaryMessenger:(NSObject*)messenger; + binaryMessenger:(NSObject *)messenger; -- (UIView*)view; +- (UIView *)view; -- (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result; +- (void)onMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result; @end @interface FLTWebViewFactory : NSObject -- (instancetype)initWithMessenger:(NSObject*)messenger - cookieManager:(FLTCookieManager*)cookieManager; +- (instancetype)initWithMessenger:(NSObject *)messenger + cookieManager:(FLTCookieManager *)cookieManager; @end NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m index 3f3b9d917783..61f112019686 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m @@ -9,12 +9,12 @@ #import "JavaScriptChannelHandler.h" @implementation FLTWebViewFactory { - NSObject* _messenger; - FLTCookieManager* _cookieManager; + NSObject *_messenger; + FLTCookieManager *_cookieManager; } -- (instancetype)initWithMessenger:(NSObject*)messenger - cookieManager:(FLTCookieManager*)cookieManager { +- (instancetype)initWithMessenger:(NSObject *)messenger + cookieManager:(FLTCookieManager *)cookieManager { self = [super init]; if (self) { _messenger = messenger; @@ -23,18 +23,18 @@ - (instancetype)initWithMessenger:(NSObject*)messenger return self; } -- (NSObject*)createArgsCodec { +- (NSObject *)createArgsCodec { return [FlutterStandardMessageCodec sharedInstance]; } -- (NSObject*)createWithFrame:(CGRect)frame - viewIdentifier:(int64_t)viewId - arguments:(id _Nullable)args { +- (NSObject *)createWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args { if (@available(iOS 11.0, *)) { [_cookieManager setCookiesForData:args[@"cookies"]]; } - FLTWebViewController* webviewController = [[FLTWebViewController alloc] initWithFrame:frame + FLTWebViewController *webviewController = [[FLTWebViewController alloc] initWithFrame:frame viewIdentifier:viewId arguments:args binaryMessenger:_messenger]; @@ -66,37 +66,37 @@ - (void)setFrame:(CGRect)frame { @end @implementation FLTWebViewController { - FLTWKWebView* _webView; + FLTWKWebView *_webView; int64_t _viewId; - FlutterMethodChannel* _channel; - NSString* _currentUrl; + FlutterMethodChannel *_channel; + NSString *_currentUrl; // The set of registered JavaScript channel names. - NSMutableSet* _javaScriptChannelNames; - FLTWKNavigationDelegate* _navigationDelegate; - FLTWKProgressionDelegate* _progressionDelegate; + NSMutableSet *_javaScriptChannelNames; + FLTWKNavigationDelegate *_navigationDelegate; + FLTWKProgressionDelegate *_progressionDelegate; } - (instancetype)initWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args - binaryMessenger:(NSObject*)messenger { + binaryMessenger:(NSObject *)messenger { if (self = [super init]) { _viewId = viewId; - NSString* channelName = [NSString stringWithFormat:@"plugins.flutter.io/webview_%lld", viewId]; + NSString *channelName = [NSString stringWithFormat:@"plugins.flutter.io/webview_%lld", viewId]; _channel = [FlutterMethodChannel methodChannelWithName:channelName binaryMessenger:messenger]; _javaScriptChannelNames = [[NSMutableSet alloc] init]; - WKUserContentController* userContentController = [[WKUserContentController alloc] init]; + WKUserContentController *userContentController = [[WKUserContentController alloc] init]; if ([args[@"javascriptChannelNames"] isKindOfClass:[NSArray class]]) { - NSArray* javaScriptChannelNames = args[@"javascriptChannelNames"]; + NSArray *javaScriptChannelNames = args[@"javascriptChannelNames"]; [_javaScriptChannelNames addObjectsFromArray:javaScriptChannelNames]; [self registerJavaScriptChannels:_javaScriptChannelNames controller:userContentController]; } - NSDictionary* settings = args[@"settings"]; + NSDictionary *settings = args[@"settings"]; - WKWebViewConfiguration* configuration = [[WKWebViewConfiguration alloc] init]; + WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init]; [self applyConfigurationSettings:settings toConfiguration:configuration]; configuration.userContentController = userContentController; [self updateAutoMediaPlaybackPolicy:args[@"autoMediaPlaybackPolicy"] @@ -105,10 +105,10 @@ - (instancetype)initWithFrame:(CGRect)frame _webView = [[FLTWKWebView alloc] initWithFrame:frame configuration:configuration]; // Background color - NSNumber* backgroundColorNSNumber = args[@"backgroundColor"]; + NSNumber *backgroundColorNSNumber = args[@"backgroundColor"]; if ([backgroundColorNSNumber isKindOfClass:[NSNumber class]]) { int backgroundColorInt = [backgroundColorNSNumber intValue]; - UIColor* backgroundColor = [UIColor colorWithRed:(backgroundColorInt >> 16 & 0xff) / 255.0 + UIColor *backgroundColor = [UIColor colorWithRed:(backgroundColorInt >> 16 & 0xff) / 255.0 green:(backgroundColorInt >> 8 & 0xff) / 255.0 blue:(backgroundColorInt & 0xff) / 255.0 alpha:(backgroundColorInt >> 24 & 0xff) / 255.0]; @@ -121,7 +121,7 @@ - (instancetype)initWithFrame:(CGRect)frame _webView.UIDelegate = self; _webView.navigationDelegate = _navigationDelegate; __weak __typeof__(self) weakSelf = self; - [_channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { + [_channel setMethodCallHandler:^(FlutterMethodCall *call, FlutterResult result) { [weakSelf onMethodCall:call result:result]; }]; @@ -136,11 +136,11 @@ - (instancetype)initWithFrame:(CGRect)frame // TODO(amirh): return an error if apply settings failed once it's possible to do so. // https://github.com/flutter/flutter/issues/36228 - NSString* initialUrl = args[@"initialUrl"]; + NSString *initialUrl = args[@"initialUrl"]; if ([initialUrl isKindOfClass:[NSString class]]) { - NSURL* url = [NSURL URLWithString:initialUrl]; + NSURL *url = [NSURL URLWithString:initialUrl]; if (url) { - NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; [_webView loadRequest:request]; } } @@ -154,11 +154,11 @@ - (void)dealloc { } } -- (UIView*)view { +- (UIView *)view { return _webView; } -- (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { +- (void)onMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { if ([[call method] isEqualToString:@"updateSettings"]) { [self onUpdateSettings:call result:result]; } else if ([[call method] isEqualToString:@"loadFile"]) { @@ -210,8 +210,8 @@ - (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { } } -- (void)onUpdateSettings:(FlutterMethodCall*)call result:(FlutterResult)result { - NSString* error = [self applySettings:[call arguments]]; +- (void)onUpdateSettings:(FlutterMethodCall *)call result:(FlutterResult)result { + NSString *error = [self applySettings:[call arguments]]; if (error == nil) { result(nil); return; @@ -219,8 +219,8 @@ - (void)onUpdateSettings:(FlutterMethodCall*)call result:(FlutterResult)result { result([FlutterError errorWithCode:@"updateSettings_failed" message:error details:nil]); } -- (void)onLoadFile:(FlutterMethodCall*)call result:(FlutterResult)result { - NSString* error = nil; +- (void)onLoadFile:(FlutterMethodCall *)call result:(FlutterResult)result { + NSString *error = nil; if (![FLTWebViewController isValidStringArgument:[call arguments] withErrorMessage:&error]) { result([FlutterError errorWithCode:@"loadFile_failed" message:@"Failed parsing file path." @@ -228,10 +228,10 @@ - (void)onLoadFile:(FlutterMethodCall*)call result:(FlutterResult)result { return; } - NSURL* url = [NSURL fileURLWithPath:[call arguments] isDirectory:NO]; + NSURL *url = [NSURL fileURLWithPath:[call arguments] isDirectory:NO]; if (!url) { - NSString* errorDetails = [NSString stringWithFormat:@"Initializing NSURL with the supplied " + NSString *errorDetails = [NSString stringWithFormat:@"Initializing NSURL with the supplied " @"'%@' path resulted in a nil value.", [call arguments]]; result([FlutterError errorWithCode:@"loadFile_failed" @@ -240,14 +240,14 @@ - (void)onLoadFile:(FlutterMethodCall*)call result:(FlutterResult)result { return; } - NSURL* baseUrl = [url URLByDeletingLastPathComponent]; + NSURL *baseUrl = [url URLByDeletingLastPathComponent]; [_webView loadFileURL:url allowingReadAccessToURL:baseUrl]; result(nil); } -- (void)onLoadFlutterAsset:(FlutterMethodCall*)call result:(FlutterResult)result { - NSString* error = nil; +- (void)onLoadFlutterAsset:(FlutterMethodCall *)call result:(FlutterResult)result { + NSString *error = nil; if (![FLTWebViewController isValidStringArgument:[call arguments] withErrorMessage:&error]) { result([FlutterError errorWithCode:@"loadFlutterAsset_invalidKey" message:@"Supplied asset key is not valid." @@ -255,9 +255,9 @@ - (void)onLoadFlutterAsset:(FlutterMethodCall*)call result:(FlutterResult)result return; } - NSString* assetKey = [call arguments]; - NSString* assetFilePath = [FlutterDartProject lookupKeyForAsset:assetKey]; - NSURL* url = [[NSBundle mainBundle] URLForResource:[assetFilePath stringByDeletingPathExtension] + NSString *assetKey = [call arguments]; + NSString *assetFilePath = [FlutterDartProject lookupKeyForAsset:assetKey]; + NSURL *url = [[NSBundle mainBundle] URLForResource:[assetFilePath stringByDeletingPathExtension] withExtension:assetFilePath.pathExtension]; if (!url) { @@ -274,8 +274,8 @@ - (void)onLoadFlutterAsset:(FlutterMethodCall*)call result:(FlutterResult)result result(nil); } -- (void)onLoadHtmlString:(FlutterMethodCall*)call result:(FlutterResult)result { - NSDictionary* arguments = [call arguments]; +- (void)onLoadHtmlString:(FlutterMethodCall *)call result:(FlutterResult)result { + NSDictionary *arguments = [call arguments]; if (![arguments isKindOfClass:NSDictionary.class]) { result([FlutterError errorWithCode:@"loadHtmlString_failed" @@ -286,10 +286,10 @@ - (void)onLoadHtmlString:(FlutterMethodCall*)call result:(FlutterResult)result { return; } - NSString* htmlString = [call arguments][@"html"]; - NSString* baseUrl = + NSString *htmlString = [call arguments][@"html"]; + NSString *baseUrl = [call arguments][@"baseUrl"] == [NSNull null] ? nil : [call arguments][@"baseUrl"]; - NSString* error = nil; + NSString *error = nil; if (![FLTWebViewController isValidStringArgument:htmlString withErrorMessage:&error]) { result([FlutterError errorWithCode:@"loadHtmlString_failed" message:@"Failed parsing HTML string argument." @@ -301,15 +301,15 @@ - (void)onLoadHtmlString:(FlutterMethodCall*)call result:(FlutterResult)result { result(nil); } -- (void)onLoadUrl:(FlutterMethodCall*)call result:(FlutterResult)result { - NSMutableDictionary* requestData = [[NSMutableDictionary alloc] init]; +- (void)onLoadUrl:(FlutterMethodCall *)call result:(FlutterResult)result { + NSMutableDictionary *requestData = [[NSMutableDictionary alloc] init]; if (call.arguments[@"url"]) { requestData[@"uri"] = call.arguments[@"url"]; } if (call.arguments[@"headers"]) { requestData[@"headers"] = call.arguments[@"headers"]; } - NSURLRequest* request = [self buildNSURLRequest:@{@"request" : requestData}]; + NSURLRequest *request = [self buildNSURLRequest:@{@"request" : requestData}]; if (!request) { result([FlutterError errorWithCode:@"loadUrl_failed" @@ -321,8 +321,8 @@ - (void)onLoadUrl:(FlutterMethodCall*)call result:(FlutterResult)result { } } -- (void)onLoadRequest:(FlutterMethodCall*)call result:(FlutterResult)result { - NSURLRequest* request = [self buildNSURLRequest:[call arguments]]; +- (void)onLoadRequest:(FlutterMethodCall *)call result:(FlutterResult)result { + NSURLRequest *request = [self buildNSURLRequest:[call arguments]]; if (!request) { result([FlutterError errorWithCode:@"loadRequest_failed" @@ -334,38 +334,38 @@ - (void)onLoadRequest:(FlutterMethodCall*)call result:(FlutterResult)result { } } -- (void)onCanGoBack:(FlutterMethodCall*)call result:(FlutterResult)result { +- (void)onCanGoBack:(FlutterMethodCall *)call result:(FlutterResult)result { BOOL canGoBack = [_webView canGoBack]; result(@(canGoBack)); } -- (void)onCanGoForward:(FlutterMethodCall*)call result:(FlutterResult)result { +- (void)onCanGoForward:(FlutterMethodCall *)call result:(FlutterResult)result { BOOL canGoForward = [_webView canGoForward]; result(@(canGoForward)); } -- (void)onGoBack:(FlutterMethodCall*)call result:(FlutterResult)result { +- (void)onGoBack:(FlutterMethodCall *)call result:(FlutterResult)result { [_webView goBack]; result(nil); } -- (void)onGoForward:(FlutterMethodCall*)call result:(FlutterResult)result { +- (void)onGoForward:(FlutterMethodCall *)call result:(FlutterResult)result { [_webView goForward]; result(nil); } -- (void)onReload:(FlutterMethodCall*)call result:(FlutterResult)result { +- (void)onReload:(FlutterMethodCall *)call result:(FlutterResult)result { [_webView reload]; result(nil); } -- (void)onCurrentUrl:(FlutterMethodCall*)call result:(FlutterResult)result { +- (void)onCurrentUrl:(FlutterMethodCall *)call result:(FlutterResult)result { _currentUrl = [[_webView URL] absoluteString]; result(_currentUrl); } -- (void)onEvaluateJavaScript:(FlutterMethodCall*)call result:(FlutterResult)result { - NSString* jsString = [call arguments]; +- (void)onEvaluateJavaScript:(FlutterMethodCall *)call result:(FlutterResult)result { + NSString *jsString = [call arguments]; if (!jsString) { result([FlutterError errorWithCode:@"evaluateJavaScript_failed" message:@"JavaScript String cannot be null" @@ -373,7 +373,7 @@ - (void)onEvaluateJavaScript:(FlutterMethodCall*)call result:(FlutterResult)resu return; } [_webView evaluateJavaScript:jsString - completionHandler:^(_Nullable id evaluateResult, NSError* _Nullable error) { + completionHandler:^(_Nullable id evaluateResult, NSError *_Nullable error) { if (error) { result([FlutterError errorWithCode:@"evaluateJavaScript_failed" @@ -386,10 +386,10 @@ - (void)onEvaluateJavaScript:(FlutterMethodCall*)call result:(FlutterResult)resu }]; } -- (void)onRunJavaScript:(FlutterMethodCall*)call +- (void)onRunJavaScript:(FlutterMethodCall *)call result:(FlutterResult)result sendReturnValue:(BOOL)sendReturnValue { - NSString* jsString = [call arguments]; + NSString *jsString = [call arguments]; if (!jsString) { result([FlutterError errorWithCode:@"runJavascript_failed" message:@"JavaScript String cannot be null" @@ -398,7 +398,7 @@ - (void)onRunJavaScript:(FlutterMethodCall*)call } [_webView evaluateJavaScript:jsString - completionHandler:^(_Nullable id evaluateResult, NSError* _Nullable error) { + completionHandler:^(_Nullable id evaluateResult, NSError *_Nullable error) { if (error) { // WebKit will throw an error (WKErrorJavaScriptResultTypeIsUnsupported) when the // type of the evaluated value is unsupported. This also goes for @@ -421,25 +421,25 @@ - (void)onRunJavaScript:(FlutterMethodCall*)call }]; } -- (void)onAddJavaScriptChannels:(FlutterMethodCall*)call result:(FlutterResult)result { - NSArray* channelNames = [call arguments]; - NSSet* channelNamesSet = [[NSSet alloc] initWithArray:channelNames]; +- (void)onAddJavaScriptChannels:(FlutterMethodCall *)call result:(FlutterResult)result { + NSArray *channelNames = [call arguments]; + NSSet *channelNamesSet = [[NSSet alloc] initWithArray:channelNames]; [_javaScriptChannelNames addObjectsFromArray:channelNames]; [self registerJavaScriptChannels:channelNamesSet controller:_webView.configuration.userContentController]; result(nil); } -- (void)onRemoveJavaScriptChannels:(FlutterMethodCall*)call result:(FlutterResult)result { +- (void)onRemoveJavaScriptChannels:(FlutterMethodCall *)call result:(FlutterResult)result { // WkWebView does not support removing a single user script, so instead we remove all // user scripts, all message handlers. And re-register channels that shouldn't be removed. [_webView.configuration.userContentController removeAllUserScripts]; - for (NSString* channelName in _javaScriptChannelNames) { + for (NSString *channelName in _javaScriptChannelNames) { [_webView.configuration.userContentController removeScriptMessageHandlerForName:channelName]; } - NSArray* channelNamesToRemove = [call arguments]; - for (NSString* channelName in channelNamesToRemove) { + NSArray *channelNamesToRemove = [call arguments]; + for (NSString *channelName in channelNamesToRemove) { [_javaScriptChannelNames removeObject:channelName]; } @@ -450,9 +450,9 @@ - (void)onRemoveJavaScriptChannels:(FlutterMethodCall*)call result:(FlutterResul - (void)clearCache:(FlutterResult)result { if (@available(iOS 9.0, *)) { - NSSet* cacheDataTypes = [WKWebsiteDataStore allWebsiteDataTypes]; - WKWebsiteDataStore* dataStore = [WKWebsiteDataStore defaultDataStore]; - NSDate* dateFrom = [NSDate dateWithTimeIntervalSince1970:0]; + NSSet *cacheDataTypes = [WKWebsiteDataStore allWebsiteDataTypes]; + WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore]; + NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0]; [dataStore removeDataOfTypes:cacheDataTypes modifiedSince:dateFrom completionHandler:^{ @@ -465,12 +465,12 @@ - (void)clearCache:(FlutterResult)result { } - (void)onGetTitle:(FlutterResult)result { - NSString* title = _webView.title; + NSString *title = _webView.title; result(title); } -- (void)onScrollTo:(FlutterMethodCall*)call result:(FlutterResult)result { - NSDictionary* arguments = [call arguments]; +- (void)onScrollTo:(FlutterMethodCall *)call result:(FlutterResult)result { + NSDictionary *arguments = [call arguments]; int x = [arguments[@"x"] intValue]; int y = [arguments[@"y"] intValue]; @@ -478,10 +478,10 @@ - (void)onScrollTo:(FlutterMethodCall*)call result:(FlutterResult)result { result(nil); } -- (void)onScrollBy:(FlutterMethodCall*)call result:(FlutterResult)result { +- (void)onScrollBy:(FlutterMethodCall *)call result:(FlutterResult)result { CGPoint contentOffset = _webView.scrollView.contentOffset; - NSDictionary* arguments = [call arguments]; + NSDictionary *arguments = [call arguments]; int x = [arguments[@"x"] intValue] + contentOffset.x; int y = [arguments[@"y"] intValue] + contentOffset.y; @@ -489,28 +489,28 @@ - (void)onScrollBy:(FlutterMethodCall*)call result:(FlutterResult)result { result(nil); } -- (void)getScrollX:(FlutterMethodCall*)call result:(FlutterResult)result { +- (void)getScrollX:(FlutterMethodCall *)call result:(FlutterResult)result { int offsetX = _webView.scrollView.contentOffset.x; result(@(offsetX)); } -- (void)getScrollY:(FlutterMethodCall*)call result:(FlutterResult)result { +- (void)getScrollY:(FlutterMethodCall *)call result:(FlutterResult)result { int offsetY = _webView.scrollView.contentOffset.y; result(@(offsetY)); } // Returns nil when successful, or an error message when one or more keys are unknown. -- (NSString*)applySettings:(NSDictionary*)settings { - NSMutableArray* unknownKeys = [[NSMutableArray alloc] init]; - for (NSString* key in settings) { +- (NSString *)applySettings:(NSDictionary *)settings { + NSMutableArray *unknownKeys = [[NSMutableArray alloc] init]; + for (NSString *key in settings) { if ([key isEqualToString:@"jsMode"]) { - NSNumber* mode = settings[key]; + NSNumber *mode = settings[key]; [self updateJsMode:mode]; } else if ([key isEqualToString:@"hasNavigationDelegate"]) { - NSNumber* hasDartNavigationDelegate = settings[key]; + NSNumber *hasDartNavigationDelegate = settings[key]; _navigationDelegate.hasDartNavigationDelegate = [hasDartNavigationDelegate boolValue]; } else if ([key isEqualToString:@"hasProgressTracking"]) { - NSNumber* hasProgressTrackingValue = settings[key]; + NSNumber *hasProgressTrackingValue = settings[key]; bool hasProgressTracking = [hasProgressTrackingValue boolValue]; if (hasProgressTracking) { _progressionDelegate = [[FLTWKProgressionDelegate alloc] initWithWebView:_webView @@ -519,14 +519,14 @@ - (NSString*)applySettings:(NSDictionary*)settings { } else if ([key isEqualToString:@"debuggingEnabled"]) { // no-op debugging is always enabled on iOS. } else if ([key isEqualToString:@"gestureNavigationEnabled"]) { - NSNumber* allowsBackForwardNavigationGestures = settings[key]; + NSNumber *allowsBackForwardNavigationGestures = settings[key]; _webView.allowsBackForwardNavigationGestures = [allowsBackForwardNavigationGestures boolValue]; } else if ([key isEqualToString:@"userAgent"]) { - NSString* userAgent = settings[key]; + NSString *userAgent = settings[key]; [self updateUserAgent:[userAgent isEqual:[NSNull null]] ? nil : userAgent]; } else if ([key isEqualToString:@"zoomEnabled"]) { - NSNumber* zoomEnabled = settings[key]; + NSNumber *zoomEnabled = settings[key]; _navigationDelegate.shouldEnableZoom = [zoomEnabled boolValue]; } else { [unknownKeys addObject:key]; @@ -539,20 +539,20 @@ - (NSString*)applySettings:(NSDictionary*)settings { [unknownKeys componentsJoinedByString:@", "]]; } -- (void)applyConfigurationSettings:(NSDictionary*)settings - toConfiguration:(WKWebViewConfiguration*)configuration { +- (void)applyConfigurationSettings:(NSDictionary *)settings + toConfiguration:(WKWebViewConfiguration *)configuration { NSAssert(configuration != _webView.configuration, @"configuration needs to be updated before webView.configuration."); - for (NSString* key in settings) { + for (NSString *key in settings) { if ([key isEqualToString:@"allowsInlineMediaPlayback"]) { - NSNumber* allowsInlineMediaPlayback = settings[key]; + NSNumber *allowsInlineMediaPlayback = settings[key]; configuration.allowsInlineMediaPlayback = [allowsInlineMediaPlayback boolValue]; } } } -- (void)updateJsMode:(NSNumber*)mode { - WKPreferences* preferences = [[_webView configuration] preferences]; +- (void)updateJsMode:(NSNumber *)mode { + WKPreferences *preferences = [[_webView configuration] preferences]; switch ([mode integerValue]) { case 0: // disabled [preferences setJavaScriptEnabled:NO]; @@ -565,8 +565,8 @@ - (void)updateJsMode:(NSNumber*)mode { } } -- (void)updateAutoMediaPlaybackPolicy:(NSNumber*)policy - inConfiguration:(WKWebViewConfiguration*)configuration { +- (void)updateAutoMediaPlaybackPolicy:(NSNumber *)policy + inConfiguration:(WKWebViewConfiguration *)configuration { switch ([policy integerValue]) { case 0: // require_user_action_for_all_media_types if (@available(iOS 10.0, *)) { @@ -604,25 +604,25 @@ - (void)updateAutoMediaPlaybackPolicy:(NSNumber*)policy * * @return NSURLRequest object. */ -- (NSURLRequest*)buildNSURLRequest:(NSDictionary*)arguments { +- (NSURLRequest *)buildNSURLRequest:(NSDictionary *)arguments { id requestParameters = arguments[@"request"]; if (![requestParameters isKindOfClass:[NSDictionary class]]) { return nil; } - NSString* urlString = requestParameters[@"uri"]; + NSString *urlString = requestParameters[@"uri"]; if (!urlString) { return nil; } - NSURL* url = [NSURL URLWithString:urlString]; + NSURL *url = [NSURL URLWithString:urlString]; if (!url) { return nil; } - NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; - NSString* httpMethod = requestParameters[@"method"]; + NSString *httpMethod = requestParameters[@"method"]; if (httpMethod) { [request setHTTPMethod:httpMethod]; } @@ -640,16 +640,16 @@ - (NSURLRequest*)buildNSURLRequest:(NSDictionary*)arguments { return request; } -- (void)registerJavaScriptChannels:(NSSet*)channelNames - controller:(WKUserContentController*)userContentController { - for (NSString* channelName in channelNames) { - FLTJavaScriptChannel* channel = +- (void)registerJavaScriptChannels:(NSSet *)channelNames + controller:(WKUserContentController *)userContentController { + for (NSString *channelName in channelNames) { + FLTJavaScriptChannel *channel = [[FLTJavaScriptChannel alloc] initWithMethodChannel:_channel javaScriptChannelName:channelName]; [userContentController addScriptMessageHandler:channel name:channelName]; - NSString* wrapperSource = [NSString + NSString *wrapperSource = [NSString stringWithFormat:@"window.%@ = webkit.messageHandlers.%@;", channelName, channelName]; - WKUserScript* wrapperScript = + WKUserScript *wrapperScript = [[WKUserScript alloc] initWithSource:wrapperSource injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO]; @@ -657,7 +657,7 @@ - (void)registerJavaScriptChannels:(NSSet*)channelNames } } -- (void)updateUserAgent:(NSString*)userAgent { +- (void)updateUserAgent:(NSString *)userAgent { if (@available(iOS 9.0, *)) { [_webView setCustomUserAgent:userAgent]; } else { @@ -673,7 +673,7 @@ - (void)updateUserAgent:(NSString*)userAgent { * case the supplied argument is not valid. * @return `YES` if the given `argument` is a valid non-null, non-empty string; otherwise `NO`. */ -+ (BOOL)isValidStringArgument:(id)argument withErrorMessage:(NSString**)errorDetails { ++ (BOOL)isValidStringArgument:(id)argument withErrorMessage:(NSString **)errorDetails { if (!argument) { if (errorDetails) { *errorDetails = @"Argument is nil."; @@ -698,10 +698,10 @@ + (BOOL)isValidStringArgument:(id)argument withErrorMessage:(NSString**)errorDet #pragma mark WKUIDelegate -- (WKWebView*)webView:(WKWebView*)webView - createWebViewWithConfiguration:(WKWebViewConfiguration*)configuration - forNavigationAction:(WKNavigationAction*)navigationAction - windowFeatures:(WKWindowFeatures*)windowFeatures { +- (WKWebView *)webView:(WKWebView *)webView + createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration + forNavigationAction:(WKNavigationAction *)navigationAction + windowFeatures:(WKWindowFeatures *)windowFeatures { if (!navigationAction.targetFrame.isMainFrame) { [webView loadRequest:navigationAction.request]; } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView_Test.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView_Test.h index 13973f8c5c6e..a84954e17c97 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView_Test.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView_Test.h @@ -10,11 +10,11 @@ NS_ASSUME_NONNULL_BEGIN @interface FLTWebViewController () -- (NSURLRequest*)buildNSURLRequest:(NSDictionary*)arguments; +- (NSURLRequest *)buildNSURLRequest:(NSDictionary *)arguments; -- (void)onLoadUrl:(FlutterMethodCall*)call result:(FlutterResult)result; +- (void)onLoadUrl:(FlutterMethodCall *)call result:(FlutterResult)result; -- (void)onLoadRequest:(FlutterMethodCall*)call result:(FlutterResult)result; +- (void)onLoadRequest:(FlutterMethodCall *)call result:(FlutterResult)result; @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/JavaScriptChannelHandler.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/JavaScriptChannelHandler.h index a0a5ec657295..f442d7af8d87 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/JavaScriptChannelHandler.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/JavaScriptChannelHandler.h @@ -9,8 +9,8 @@ NS_ASSUME_NONNULL_BEGIN @interface FLTJavaScriptChannel : NSObject -- (instancetype)initWithMethodChannel:(FlutterMethodChannel*)methodChannel - javaScriptChannelName:(NSString*)javaScriptChannelName; +- (instancetype)initWithMethodChannel:(FlutterMethodChannel *)methodChannel + javaScriptChannelName:(NSString *)javaScriptChannelName; @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/JavaScriptChannelHandler.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/JavaScriptChannelHandler.m index ec9a363a4b2e..1aed25f1b7d9 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/JavaScriptChannelHandler.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/JavaScriptChannelHandler.m @@ -5,12 +5,12 @@ #import "JavaScriptChannelHandler.h" @implementation FLTJavaScriptChannel { - FlutterMethodChannel* _methodChannel; - NSString* _javaScriptChannelName; + FlutterMethodChannel *_methodChannel; + NSString *_javaScriptChannelName; } -- (instancetype)initWithMethodChannel:(FlutterMethodChannel*)methodChannel - javaScriptChannelName:(NSString*)javaScriptChannelName { +- (instancetype)initWithMethodChannel:(FlutterMethodChannel *)methodChannel + javaScriptChannelName:(NSString *)javaScriptChannelName { self = [super init]; NSAssert(methodChannel != nil, @"methodChannel must not be null."); NSAssert(javaScriptChannelName != nil, @"javaScriptChannelName must not be null."); @@ -21,12 +21,12 @@ - (instancetype)initWithMethodChannel:(FlutterMethodChannel*)methodChannel return self; } -- (void)userContentController:(WKUserContentController*)userContentController - didReceiveScriptMessage:(WKScriptMessage*)message { +- (void)userContentController:(WKUserContentController *)userContentController + didReceiveScriptMessage:(WKScriptMessage *)message { NSAssert(_methodChannel != nil, @"Can't send a message to an unitialized JavaScript channel."); NSAssert(_javaScriptChannelName != nil, @"Can't send a message to an unitialized JavaScript channel."); - NSDictionary* arguments = @{ + NSDictionary *arguments = @{ @"channel" : _javaScriptChannelName, @"message" : [NSString stringWithFormat:@"%@", message.body] }; diff --git a/script/tool/lib/src/format_command.dart b/script/tool/lib/src/format_command.dart index f24a99436c87..10c0779de927 100644 --- a/script/tool/lib/src/format_command.dart +++ b/script/tool/lib/src/format_command.dart @@ -138,7 +138,7 @@ class FormatCommand extends PluginCommand { print('Formatting .cc, .cpp, .h, .m, and .mm files...'); final int exitCode = await _runBatched( - getStringArg('clang-format'), ['-i', '--style=Google'], + getStringArg('clang-format'), ['-i', '--style=file'], files: clangFiles); if (exitCode != 0) { printError( diff --git a/script/tool/test/format_command_test.dart b/script/tool/test/format_command_test.dart index d278bb2940b8..2890c528e4c1 100644 --- a/script/tool/test/format_command_test.dart +++ b/script/tool/test/format_command_test.dart @@ -301,7 +301,7 @@ void main() { 'clang-format', [ '-i', - '--style=Google', + '--style=file', ..._getPackagesDirRelativePaths(pluginDir, files) ], packagesDir.path), @@ -357,7 +357,7 @@ void main() { '/path/to/clang-format', [ '-i', - '--style=Google', + '--style=file', ..._getPackagesDirRelativePaths(pluginDir, files) ], packagesDir.path), @@ -425,7 +425,7 @@ void main() { 'clang-format', [ '-i', - '--style=Google', + '--style=file', ..._getPackagesDirRelativePaths(pluginDir, clangFiles) ], packagesDir.path), From eb6e912329af4aea97de2183f885483ecc605e44 Mon Sep 17 00:00:00 2001 From: Koen Van Looveren Date: Thu, 27 Jan 2022 00:30:23 +0100 Subject: [PATCH 119/600] [video_player] Added the setCaptionOffset (#3275) --- packages/video_player/video_player/AUTHORS | 1 + .../video_player/video_player/CHANGELOG.md | 4 + .../video_player/example/lib/main.dart | 40 +++++++++ .../video_player/lib/video_player.dart | 34 ++++++- .../video_player/video_player/pubspec.yaml | 2 +- .../video_player/test/video_player_test.dart | 89 +++++++++++++++++++ 6 files changed, 166 insertions(+), 4 deletions(-) diff --git a/packages/video_player/video_player/AUTHORS b/packages/video_player/video_player/AUTHORS index 493a0b4ef9c2..02a9c690f330 100644 --- a/packages/video_player/video_player/AUTHORS +++ b/packages/video_player/video_player/AUTHORS @@ -64,3 +64,4 @@ Aleksandr Yurkovskiy Anton Borries Alex Li Rahul Raj <64.rahulraj@gmail.com> +Koen Van Looveren diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index b7225a8603ab..e28ef83ca720 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.2.16 + +* Introduces `setCaptionOffset` to offset the caption display based on a Duration. + ## 2.2.15 * Updates README discussion of permissions. diff --git a/packages/video_player/video_player/example/lib/main.dart b/packages/video_player/video_player/example/lib/main.dart index 042982798d54..9297cc69470c 100644 --- a/packages/video_player/video_player/example/lib/main.dart +++ b/packages/video_player/video_player/example/lib/main.dart @@ -269,6 +269,17 @@ class _ControlsOverlay extends StatelessWidget { const _ControlsOverlay({Key? key, required this.controller}) : super(key: key); + static const _exampleCaptionOffsets = [ + Duration(seconds: -10), + Duration(seconds: -3), + Duration(seconds: -1, milliseconds: -500), + Duration(milliseconds: -250), + Duration(milliseconds: 0), + Duration(milliseconds: 250), + Duration(seconds: 1, milliseconds: 500), + Duration(seconds: 3), + Duration(seconds: 10), + ]; static const _examplePlaybackRates = [ 0.25, 0.5, @@ -308,6 +319,35 @@ class _ControlsOverlay extends StatelessWidget { controller.value.isPlaying ? controller.pause() : controller.play(); }, ), + Align( + alignment: Alignment.topLeft, + child: PopupMenuButton( + initialValue: controller.value.captionOffset, + tooltip: 'Caption Offset', + onSelected: (delay) { + controller.setCaptionOffset(delay); + }, + itemBuilder: (context) { + return [ + for (final offsetDuration in _exampleCaptionOffsets) + PopupMenuItem( + value: offsetDuration, + child: Text('${offsetDuration.inMilliseconds}ms'), + ) + ]; + }, + child: Padding( + padding: const EdgeInsets.symmetric( + // Using less vertical padding as the text is also longer + // horizontally, so it feels like it would need more spacing + // horizontally (matching the aspect ratio of the video). + vertical: 12, + horizontal: 16, + ), + child: Text('${controller.value.captionOffset.inMilliseconds}ms'), + ), + ), + ), Align( alignment: Alignment.topRight, child: PopupMenuButton( diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index 3d4e1c6aa599..8fd216286fca 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -43,6 +43,7 @@ class VideoPlayerValue { this.size = Size.zero, this.position = Duration.zero, this.caption = Caption.none, + this.captionOffset = Duration.zero, this.buffered = const [], this.isInitialized = false, this.isPlaying = false, @@ -78,6 +79,11 @@ class VideoPlayerValue { /// [position], this will be a [Caption.none] object. final Caption caption; + /// The [Duration] that should be used to offset the current [position] to get the correct [Caption]. + /// + /// Defaults to Duration.zero. + final Duration captionOffset; + /// The currently buffered ranges. final List buffered; @@ -135,6 +141,7 @@ class VideoPlayerValue { Size? size, Duration? position, Caption? caption, + Duration? captionOffset, List? buffered, bool? isInitialized, bool? isPlaying, @@ -149,6 +156,7 @@ class VideoPlayerValue { size: size ?? this.size, position: position ?? this.position, caption: caption ?? this.caption, + captionOffset: captionOffset ?? this.captionOffset, buffered: buffered ?? this.buffered, isInitialized: isInitialized ?? this.isInitialized, isPlaying: isPlaying ?? this.isPlaying, @@ -169,6 +177,7 @@ class VideoPlayerValue { 'size: $size, ' 'position: $position, ' 'caption: $caption, ' + 'captionOffset: $captionOffset, ' 'buffered: [${buffered.join(', ')}], ' 'isInitialized: $isInitialized, ' 'isPlaying: $isPlaying, ' @@ -581,6 +590,22 @@ class VideoPlayerController extends ValueNotifier { await _applyPlaybackSpeed(); } + /// Sets the caption offset. + /// + /// The [offset] will be used when getting the correct caption for a specific position. + /// The [offset] can be positive or negative. + /// + /// The values will be handled as follows: + /// * 0: This is the default behaviour. No offset will be applied. + /// * >0: The caption will have a negative offset. So you will get caption text from the past. + /// * <0: The caption will have a positive offset. So you will get caption text from the future. + void setCaptionOffset(Duration offset) { + value = value.copyWith( + captionOffset: offset, + caption: _getCaptionAt(value.position), + ); + } + /// The closed caption based on the current [position] in the video. /// /// If there are no closed captions at the current [position], this will @@ -593,9 +618,10 @@ class VideoPlayerController extends ValueNotifier { return Caption.none; } + final delayedPosition = position + value.captionOffset; // TODO: This would be more efficient as a binary search. for (final caption in _closedCaptionFile!.captions) { - if (caption.start <= position && caption.end >= position) { + if (caption.start <= delayedPosition && caption.end >= delayedPosition) { return caption; } } @@ -604,8 +630,10 @@ class VideoPlayerController extends ValueNotifier { } void _updatePosition(Duration position) { - value = value.copyWith(position: position); - value = value.copyWith(caption: _getCaptionAt(position)); + value = value.copyWith( + position: position, + caption: _getCaptionAt(position), + ); } @override diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 858a7cedd5c2..63520f30db4b 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for displaying inline video with other Flutter widgets on Android, iOS, and web. repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.2.15 +version: 2.2.16 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player/test/video_player_test.dart b/packages/video_player/video_player/test/video_player_test.dart index 08eecb618f41..3bce821e8fe3 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -69,6 +69,9 @@ class FakeController extends ValueNotifier @override VideoPlayerOptions? get videoPlayerOptions => null; + + @override + void setCaptionOffset(Duration delay) {} } Future _loadClosedCaption() async => @@ -557,11 +560,92 @@ void main() { await controller.seekTo(const Duration(milliseconds: 300)); expect(controller.value.caption.text, 'two'); + await controller.seekTo(const Duration(milliseconds: 301)); + expect(controller.value.caption.text, 'two'); + + await controller.seekTo(const Duration(milliseconds: 500)); + expect(controller.value.caption.text, ''); + + await controller.seekTo(const Duration(milliseconds: 300)); + expect(controller.value.caption.text, 'two'); + + await controller.seekTo(const Duration(milliseconds: 301)); + expect(controller.value.caption.text, 'two'); + }); + + test('works when seeking with captionOffset positive', () async { + final VideoPlayerController controller = VideoPlayerController.network( + 'https://127.0.0.1', + closedCaptionFile: _loadClosedCaption(), + ); + + await controller.initialize(); + controller.setCaptionOffset(Duration(milliseconds: 100)); + expect(controller.value.position, const Duration()); + expect(controller.value.caption.text, ''); + + await controller.seekTo(const Duration(milliseconds: 100)); + expect(controller.value.caption.text, 'one'); + + await controller.seekTo(const Duration(milliseconds: 101)); + expect(controller.value.caption.text, ''); + + await controller.seekTo(const Duration(milliseconds: 250)); + expect(controller.value.caption.text, 'two'); + + await controller.seekTo(const Duration(milliseconds: 300)); + expect(controller.value.caption.text, 'two'); + + await controller.seekTo(const Duration(milliseconds: 301)); + expect(controller.value.caption.text, ''); + await controller.seekTo(const Duration(milliseconds: 500)); expect(controller.value.caption.text, ''); await controller.seekTo(const Duration(milliseconds: 300)); expect(controller.value.caption.text, 'two'); + + await controller.seekTo(const Duration(milliseconds: 301)); + expect(controller.value.caption.text, ''); + }); + + test('works when seeking with captionOffset negative', () async { + final VideoPlayerController controller = VideoPlayerController.network( + 'https://127.0.0.1', + closedCaptionFile: _loadClosedCaption(), + ); + + await controller.initialize(); + controller.setCaptionOffset(Duration(milliseconds: -100)); + expect(controller.value.position, const Duration()); + expect(controller.value.caption.text, ''); + + await controller.seekTo(const Duration(milliseconds: 100)); + expect(controller.value.caption.text, ''); + + await controller.seekTo(const Duration(milliseconds: 200)); + expect(controller.value.caption.text, 'one'); + + await controller.seekTo(const Duration(milliseconds: 250)); + expect(controller.value.caption.text, 'one'); + + await controller.seekTo(const Duration(milliseconds: 300)); + expect(controller.value.caption.text, 'one'); + + await controller.seekTo(const Duration(milliseconds: 301)); + expect(controller.value.caption.text, ''); + + await controller.seekTo(const Duration(milliseconds: 400)); + expect(controller.value.caption.text, 'two'); + + await controller.seekTo(const Duration(milliseconds: 500)); + expect(controller.value.caption.text, 'two'); + + await controller.seekTo(const Duration(milliseconds: 600)); + expect(controller.value.caption.text, ''); + + await controller.seekTo(const Duration(milliseconds: 300)); + expect(controller.value.caption.text, 'one'); }); }); @@ -655,6 +739,7 @@ void main() { expect(uninitialized.duration, equals(Duration.zero)); expect(uninitialized.position, equals(Duration.zero)); expect(uninitialized.caption, equals(Caption.none)); + expect(uninitialized.captionOffset, equals(Duration.zero)); expect(uninitialized.buffered, isEmpty); expect(uninitialized.isPlaying, isFalse); expect(uninitialized.isLooping, isFalse); @@ -675,6 +760,7 @@ void main() { expect(error.duration, equals(Duration.zero)); expect(error.position, equals(Duration.zero)); expect(error.caption, equals(Caption.none)); + expect(error.captionOffset, equals(Duration.zero)); expect(error.buffered, isEmpty); expect(error.isPlaying, isFalse); expect(error.isLooping, isFalse); @@ -694,6 +780,7 @@ void main() { const Duration position = Duration(seconds: 1); const Caption caption = Caption( text: 'foo', number: 0, start: Duration.zero, end: Duration.zero); + const Duration captionOffset = Duration(milliseconds: 250); final List buffered = [ DurationRange(const Duration(seconds: 0), const Duration(seconds: 4)) ]; @@ -709,6 +796,7 @@ void main() { size: size, position: position, caption: caption, + captionOffset: captionOffset, buffered: buffered, isInitialized: isInitialized, isPlaying: isPlaying, @@ -724,6 +812,7 @@ void main() { 'size: Size(400.0, 300.0), ' 'position: 0:00:01.000000, ' 'caption: Caption(number: 0, start: 0:00:00.000000, end: 0:00:00.000000, text: foo), ' + 'captionOffset: 0:00:00.250000, ' 'buffered: [DurationRange(start: 0:00:00.000000, end: 0:00:04.000000)], ' 'isInitialized: true, ' 'isPlaying: true, ' From e282dfbebcf4e84ab31073029f528fe9ee31f075 Mon Sep 17 00:00:00 2001 From: zuvola Date: Thu, 27 Jan 2022 09:05:23 +0900 Subject: [PATCH 120/600] [camera] Fix ImageStream ImageFormatGroup is Ignored in iOS (#4519) --- packages/camera/camera/CHANGELOG.md | 4 ++ .../example/integration_test/camera_test.dart | 52 +++++++++++++++++++ .../camera/camera/ios/Classes/CameraPlugin.m | 19 ++++--- packages/camera/camera/pubspec.yaml | 2 +- 4 files changed, 70 insertions(+), 7 deletions(-) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index cb560f5a34f5..1ec29577175a 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.4+8 + +* Fixes a bug where ImageFormatGroup was ignored in `startImageStream` on iOS. + ## 0.9.4+7 * Fixes a crash in iOS when passing null queue pointer into AVFoundation API due to race condition. diff --git a/packages/camera/camera/example/integration_test/camera_test.dart b/packages/camera/camera/example/integration_test/camera_test.dart index d09300a3f906..3af291afe63b 100644 --- a/packages/camera/camera/example/integration_test/camera_test.dart +++ b/packages/camera/camera/example/integration_test/camera_test.dart @@ -240,4 +240,56 @@ void main() { }, skip: !Platform.isAndroid, ); + + /// Start streaming with specifying the ImageFormatGroup. + Future startStreaming(List cameras, + ImageFormatGroup? imageFormatGroup) async { + final CameraController controller = CameraController( + cameras.first, + ResolutionPreset.low, + enableAudio: false, + imageFormatGroup: imageFormatGroup, + ); + + await controller.initialize(); + final _completer = Completer(); + + await controller.startImageStream((CameraImage image) { + if (!_completer.isCompleted) { + Future(() async { + await controller.stopImageStream(); + await controller.dispose(); + }).then((value) { + _completer.complete(image); + }); + } + }); + return _completer.future; + } + + testWidgets( + 'iOS image streaming with imageFormatGroup', + (WidgetTester tester) async { + final List cameras = await availableCameras(); + if (cameras.isEmpty) { + return; + } + + var _image = await startStreaming(cameras, null); + expect(_image, isNotNull); + expect(_image.format.group, ImageFormatGroup.bgra8888); + expect(_image.planes.length, 1); + + _image = await startStreaming(cameras, ImageFormatGroup.yuv420); + expect(_image, isNotNull); + expect(_image.format.group, ImageFormatGroup.yuv420); + expect(_image.planes.length, 2); + + _image = await startStreaming(cameras, ImageFormatGroup.bgra8888); + expect(_image, isNotNull); + expect(_image.format.group, ImageFormatGroup.bgra8888); + expect(_image.planes.length, 1); + }, + skip: !Platform.isIOS, + ); } diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.m b/packages/camera/camera/ios/Classes/CameraPlugin.m index 5d706520cc5b..e79a78348712 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin.m +++ b/packages/camera/camera/ios/Classes/CameraPlugin.m @@ -355,6 +355,8 @@ @interface FLTCam : NSObject =2.14.0 <3.0.0" From 3cc18d916eeadc2e1d322d3f7ba8257b316e1af9 Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Wed, 26 Jan 2022 17:50:22 -0800 Subject: [PATCH 121/600] [camera]refactor flash mode and add unit test (#4695) --- packages/camera/camera/CHANGELOG.md | 6 +- .../ios/Runner.xcodeproj/project.pbxproj | 4 ++ .../ios/RunnerTests/FLTFlashModeTests.m | 30 ++++++++++ .../camera/camera/ios/Classes/CameraPlugin.m | 59 +++---------------- .../camera/ios/Classes/CameraPlugin.modulemap | 1 + .../camera/camera/ios/Classes/FlashMode.h | 32 ++++++++++ .../camera/camera/ios/Classes/FlashMode.m | 39 ++++++++++++ 7 files changed, 120 insertions(+), 51 deletions(-) create mode 100644 packages/camera/camera/example/ios/RunnerTests/FLTFlashModeTests.m create mode 100644 packages/camera/camera/ios/Classes/FlashMode.h create mode 100644 packages/camera/camera/ios/Classes/FlashMode.m diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 1ec29577175a..64b7cfd03f81 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Minor iOS internal code cleanup related to flash mode. + ## 0.9.4+8 * Fixes a bug where ImageFormatGroup was ignored in `startImageStream` on iOS. @@ -5,7 +9,7 @@ ## 0.9.4+7 * Fixes a crash in iOS when passing null queue pointer into AVFoundation API due to race condition. -* Minor iOS internal code cleanup. +* Minor iOS internal code cleanup related to dispatch queue. ## 0.9.4+6 diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj index 80f672b5b9cf..87bcf2e666cd 100644 --- a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj @@ -22,6 +22,7 @@ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; E01EE4A82799F3A5008C1950 /* QueueHelperTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E01EE4A72799F3A5008C1950 /* QueueHelperTests.m */; }; E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */; }; + E032F2A727A0BF2D009E9028 /* FLTFlashModeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E032F2A627A0BF2D009E9028 /* FLTFlashModeTests.m */; }; E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */; }; E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */; }; E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */; }; @@ -81,6 +82,7 @@ A24F9E418BA48BCC7409B117 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; E01EE4A72799F3A5008C1950 /* QueueHelperTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QueueHelperTests.m; sourceTree = ""; }; E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CameraCaptureSessionQueueRaceConditionTests.m; sourceTree = ""; }; + E032F2A627A0BF2D009E9028 /* FLTFlashModeTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTFlashModeTests.m; sourceTree = ""; }; E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeMethodChannelTests.m; sourceTree = ""; }; E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeTextureRegistryTests.m; sourceTree = ""; }; E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeEventChannelTests.m; sourceTree = ""; }; @@ -125,6 +127,7 @@ F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */, F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */, E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */, + E032F2A627A0BF2D009E9028 /* FLTFlashModeTests.m */, ); path = RunnerTests; sourceTree = ""; @@ -388,6 +391,7 @@ buildActionMask = 2147483647; files = ( 03F6F8B226CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m in Sources */, + E032F2A727A0BF2D009E9028 /* FLTFlashModeTests.m in Sources */, 033B94BE269C40A200B4DF97 /* CameraMethodChannelTests.m in Sources */, 03BB766B2665316900CE5A93 /* CameraFocusTests.m in Sources */, E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */, diff --git a/packages/camera/camera/example/ios/RunnerTests/FLTFlashModeTests.m b/packages/camera/camera/example/ios/RunnerTests/FLTFlashModeTests.m new file mode 100644 index 000000000000..b38b7eba8304 --- /dev/null +++ b/packages/camera/camera/example/ios/RunnerTests/FLTFlashModeTests.m @@ -0,0 +1,30 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import camera.Test; +@import AVFoundation; +#import + +@interface FLTFlashModeTests : XCTestCase + +@end + +@implementation FLTFlashModeTests + +- (void)testFLTGetFLTFlashModeForString { + XCTAssertEqual(FLTFlashModeOff, FLTGetFLTFlashModeForString(@"off")); + XCTAssertEqual(FLTFlashModeAuto, FLTGetFLTFlashModeForString(@"auto")); + XCTAssertEqual(FLTFlashModeAlways, FLTGetFLTFlashModeForString(@"always")); + XCTAssertEqual(FLTFlashModeTorch, FLTGetFLTFlashModeForString(@"torch")); + XCTAssertThrows(FLTGetFLTFlashModeForString(@"unkwown")); +} + +- (void)testFLTGetAVCaptureFlashModeForFLTFlashMode { + XCTAssertEqual(AVCaptureFlashModeOff, FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashModeOff)); + XCTAssertEqual(AVCaptureFlashModeAuto, FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashModeAuto)); + XCTAssertEqual(AVCaptureFlashModeOn, FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashModeAlways)); + XCTAssertEqual(-1, FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashModeTorch)); +} + +@end diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.m b/packages/camera/camera/ios/Classes/CameraPlugin.m index e79a78348712..be5ba80923c2 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin.m +++ b/packages/camera/camera/ios/Classes/CameraPlugin.m @@ -14,6 +14,7 @@ #import "FLTThreadSafeFlutterResult.h" #import "FLTThreadSafeMethodChannel.h" #import "FLTThreadSafeTextureRegistry.h" +#import "FlashMode.h" @interface FLTSavePhotoDelegate : NSObject @property(readonly, nonatomic) NSString *path; @@ -113,34 +114,6 @@ - (void)captureOutput:(AVCapturePhotoOutput *)output } @end -// Mirrors FlashMode in flash_mode.dart -typedef enum { - FlashModeOff, - FlashModeAuto, - FlashModeAlways, - FlashModeTorch, -} FlashMode; - -static FlashMode getFlashModeForString(NSString *mode) { - if ([mode isEqualToString:@"off"]) { - return FlashModeOff; - } else if ([mode isEqualToString:@"auto"]) { - return FlashModeAuto; - } else if ([mode isEqualToString:@"always"]) { - return FlashModeAlways; - } else if ([mode isEqualToString:@"torch"]) { - return FlashModeTorch; - } else { - NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain - code:NSURLErrorUnknown - userInfo:@{ - NSLocalizedDescriptionKey : [NSString - stringWithFormat:@"Unknown flash mode %@", mode] - }]; - @throw error; - } -} - static OSType getVideoFormatFromString(NSString *videoFormatString) { if ([videoFormatString isEqualToString:@"bgra8888"]) { return kCVPixelFormatType_32BGRA; @@ -152,20 +125,6 @@ static OSType getVideoFormatFromString(NSString *videoFormatString) { } } -static AVCaptureFlashMode getAVCaptureFlashModeForFlashMode(FlashMode mode) { - switch (mode) { - case FlashModeOff: - return AVCaptureFlashModeOff; - case FlashModeAuto: - return AVCaptureFlashModeAuto; - case FlashModeAlways: - return AVCaptureFlashModeOn; - case FlashModeTorch: - default: - return -1; - } -} - // Mirrors ExposureMode in camera.dart typedef enum { ExposureModeAuto, @@ -349,7 +308,7 @@ @interface FLTCam : NSObject +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * Represents camera's flash mode. Mirrors `FlashMode` enum in flash_mode.dart. + */ +typedef NS_ENUM(NSInteger, FLTFlashMode) { + FLTFlashModeOff, + FLTFlashModeAuto, + FLTFlashModeAlways, + FLTFlashModeTorch, +}; + +/** + * Gets FLTFlashMode from its string representation. + * @param mode a string representation of the FLTFlashMode. + */ +extern FLTFlashMode FLTGetFLTFlashModeForString(NSString *mode); + +/** + * Gets AVCaptureFlashMode from FLTFlashMode. + * @param mode flash mode. + */ +extern AVCaptureFlashMode FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashMode mode); + +NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera/ios/Classes/FlashMode.m b/packages/camera/camera/ios/Classes/FlashMode.m new file mode 100644 index 000000000000..b9d4a4afd6bd --- /dev/null +++ b/packages/camera/camera/ios/Classes/FlashMode.m @@ -0,0 +1,39 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FlashMode.h" + +FLTFlashMode FLTGetFLTFlashModeForString(NSString *mode) { + if ([mode isEqualToString:@"off"]) { + return FLTFlashModeOff; + } else if ([mode isEqualToString:@"auto"]) { + return FLTFlashModeAuto; + } else if ([mode isEqualToString:@"always"]) { + return FLTFlashModeAlways; + } else if ([mode isEqualToString:@"torch"]) { + return FLTFlashModeTorch; + } else { + NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain + code:NSURLErrorUnknown + userInfo:@{ + NSLocalizedDescriptionKey : [NSString + stringWithFormat:@"Unknown flash mode %@", mode] + }]; + @throw error; + } +} + +AVCaptureFlashMode FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashMode mode) { + switch (mode) { + case FLTFlashModeOff: + return AVCaptureFlashModeOff; + case FLTFlashModeAuto: + return AVCaptureFlashModeAuto; + case FLTFlashModeAlways: + return AVCaptureFlashModeOn; + case FLTFlashModeTorch: + default: + return -1; + } +} From 26f6438390116e769fdcb6244cfe697251bcc9c8 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 26 Jan 2022 22:00:46 -0500 Subject: [PATCH 122/600] [ci] Pin master for LUCI tests (#4681) Update LUCI to match recent Cirrus change, using a pinned version of master instead of latest master. Part of https://github.com/flutter/flutter/issues/93811 --- .ci.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.ci.yaml b/.ci.yaml index d935fb6f0e34..061e42092ad8 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -41,6 +41,8 @@ targets: properties: add_recipes_cq: "true" target_file: windows_build_and_platform_tests.yaml + channel: master + version_file: flutter_master.version dependencies: > [ {"dependency": "vs_build"} @@ -66,6 +68,8 @@ targets: properties: add_recipes_cq: "true" target_file: build_all_plugins.yaml + channel: master + version_file: flutter_master.version dependencies: > [ {"dependency": "vs_build"} @@ -91,6 +95,8 @@ targets: properties: add_recipes_cq: "true" target_file: uwp_build_and_platform_tests.yaml + channel: master + version_file: flutter_master.version dependencies: > [ {"dependency": "vs_build"} @@ -103,6 +109,8 @@ targets: properties: add_recipes_cq: "true" target_file: plugin_tools_tests.yaml + channel: master + version_file: flutter_master.version scheduler: luci - name: Linux ci_yaml plugins roller From c27b257dd3e22f8645cf5e26fd3dfe472b208e56 Mon Sep 17 00:00:00 2001 From: BeMacized Date: Thu, 27 Jan 2022 10:47:09 +0100 Subject: [PATCH 123/600] Move local_auth plugin into a subfolder in preparation for federation migration (#4705) --- packages/local_auth/{ => local_auth}/AUTHORS | 0 packages/local_auth/{ => local_auth}/CHANGELOG.md | 0 packages/local_auth/{ => local_auth}/LICENSE | 0 packages/local_auth/{ => local_auth}/README.md | 0 .../{ => local_auth}/android/build.gradle | 0 .../{ => local_auth}/android/lint-baseline.xml | 0 .../{ => local_auth}/android/settings.gradle | 0 .../android/src/main/AndroidManifest.xml | 0 .../plugins/localauth/AuthenticationHelper.java | 0 .../flutter/plugins/localauth/LocalAuthPlugin.java | 0 .../main/res/drawable/fingerprint_initial_icon.xml | 0 .../main/res/drawable/fingerprint_success_icon.xml | 0 .../main/res/drawable/fingerprint_warning_icon.xml | 0 .../src/main/res/drawable/ic_done_white_24dp.xml | 0 .../main/res/drawable/ic_fingerprint_white_24dp.xml | 0 .../res/drawable/ic_priority_high_white_24dp.xml | 0 .../android/src/main/res/layout/go_to_setting.xml | 0 .../android/src/main/res/layout/scan_fp.xml | 0 .../android/src/main/res/values/colors.xml | 0 .../android/src/main/res/values/dimens.xml | 0 .../android/src/main/res/values/styles.xml | 0 .../io/flutter/plugins/localauth/LocalAuthTest.java | 0 .../local_auth/{ => local_auth}/example/README.md | 0 .../example/android/app/build.gradle | 0 .../app/gradle/wrapper/gradle-wrapper.properties | 0 .../io/flutter/plugins/DartIntegrationTest.java | 0 .../localauth/FlutterFragmentActivityTest.java | 0 .../android/app/src/main/AndroidManifest.xml | 0 .../app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin .../app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin .../app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin .../app/src/main/res/mipmap-xxhdpi/ic_launcher.png | Bin .../app/src/main/res/mipmap-xxxhdpi/ic_launcher.png | Bin .../{ => local_auth}/example/android/build.gradle | 0 .../example/android/gradle.properties | 0 .../gradle/wrapper/gradle-wrapper.properties | 0 .../example/android/settings.gradle | 0 .../example/android/settings_aar.gradle | 0 .../example/integration_test/local_auth_test.dart | 0 .../example/ios/Flutter/AppFrameworkInfo.plist | 0 .../example/ios/Flutter/Debug.xcconfig | 0 .../example/ios/Flutter/Release.xcconfig | 0 .../local_auth/{ => local_auth}/example/ios/Podfile | 0 .../example/ios/Runner.xcodeproj/project.pbxproj | 0 .../project.xcworkspace/contents.xcworkspacedata | 0 .../xcshareddata/xcschemes/Runner.xcscheme | 0 .../ios/Runner.xcworkspace/contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../example/ios/Runner/AppDelegate.h | 0 .../example/ios/Runner/AppDelegate.m | 0 .../AppIcon.appiconset/Contents.json | 0 .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin .../AppIcon.appiconset/Icon-App-83.5x83.5@2x.png | Bin .../ios/Runner/Base.lproj/LaunchScreen.storyboard | 0 .../example/ios/Runner/Base.lproj/Main.storyboard | 0 .../{ => local_auth}/example/ios/Runner/Info.plist | 0 .../{ => local_auth}/example/ios/Runner/main.m | 0 .../ios/RunnerTests/FLTLocalAuthPluginTests.m | 0 .../example/ios/RunnerTests/Info.plist | 0 .../{ => local_auth}/example/lib/main.dart | 0 .../{ => local_auth}/example/pubspec.yaml | 0 .../example/test_driver/integration_test.dart | 0 .../local_auth/{ => local_auth}/ios/Assets/.gitkeep | 0 .../ios/Classes/FLTLocalAuthPlugin.h | 0 .../ios/Classes/FLTLocalAuthPlugin.m | 0 .../{ => local_auth}/ios/local_auth.podspec | 0 .../{ => local_auth}/lib/auth_strings.dart | 0 .../{ => local_auth}/lib/error_codes.dart | 0 .../local_auth/{ => local_auth}/lib/local_auth.dart | 0 packages/local_auth/{ => local_auth}/pubspec.yaml | 2 +- .../{ => local_auth}/test/local_auth_test.dart | 0 83 files changed, 1 insertion(+), 1 deletion(-) rename packages/local_auth/{ => local_auth}/AUTHORS (100%) rename packages/local_auth/{ => local_auth}/CHANGELOG.md (100%) rename packages/local_auth/{ => local_auth}/LICENSE (100%) rename packages/local_auth/{ => local_auth}/README.md (100%) rename packages/local_auth/{ => local_auth}/android/build.gradle (100%) rename packages/local_auth/{ => local_auth}/android/lint-baseline.xml (100%) rename packages/local_auth/{ => local_auth}/android/settings.gradle (100%) rename packages/local_auth/{ => local_auth}/android/src/main/AndroidManifest.xml (100%) rename packages/local_auth/{ => local_auth}/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java (100%) rename packages/local_auth/{ => local_auth}/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java (100%) rename packages/local_auth/{ => local_auth}/android/src/main/res/drawable/fingerprint_initial_icon.xml (100%) rename packages/local_auth/{ => local_auth}/android/src/main/res/drawable/fingerprint_success_icon.xml (100%) rename packages/local_auth/{ => local_auth}/android/src/main/res/drawable/fingerprint_warning_icon.xml (100%) rename packages/local_auth/{ => local_auth}/android/src/main/res/drawable/ic_done_white_24dp.xml (100%) rename packages/local_auth/{ => local_auth}/android/src/main/res/drawable/ic_fingerprint_white_24dp.xml (100%) rename packages/local_auth/{ => local_auth}/android/src/main/res/drawable/ic_priority_high_white_24dp.xml (100%) rename packages/local_auth/{ => local_auth}/android/src/main/res/layout/go_to_setting.xml (100%) rename packages/local_auth/{ => local_auth}/android/src/main/res/layout/scan_fp.xml (100%) rename packages/local_auth/{ => local_auth}/android/src/main/res/values/colors.xml (100%) rename packages/local_auth/{ => local_auth}/android/src/main/res/values/dimens.xml (100%) rename packages/local_auth/{ => local_auth}/android/src/main/res/values/styles.xml (100%) rename packages/local_auth/{ => local_auth}/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java (100%) rename packages/local_auth/{ => local_auth}/example/README.md (100%) rename packages/local_auth/{ => local_auth}/example/android/app/build.gradle (100%) rename packages/local_auth/{ => local_auth}/example/android/app/gradle/wrapper/gradle-wrapper.properties (100%) rename packages/local_auth/{ => local_auth}/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java (100%) rename packages/local_auth/{ => local_auth}/example/android/app/src/androidTest/java/io/flutter/plugins/localauth/FlutterFragmentActivityTest.java (100%) rename packages/local_auth/{ => local_auth}/example/android/app/src/main/AndroidManifest.xml (100%) rename packages/local_auth/{ => local_auth}/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png (100%) rename packages/local_auth/{ => local_auth}/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png (100%) rename packages/local_auth/{ => local_auth}/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png (100%) rename packages/local_auth/{ => local_auth}/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png (100%) rename packages/local_auth/{ => local_auth}/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png (100%) rename packages/local_auth/{ => local_auth}/example/android/build.gradle (100%) rename packages/local_auth/{ => local_auth}/example/android/gradle.properties (100%) rename packages/local_auth/{ => local_auth}/example/android/gradle/wrapper/gradle-wrapper.properties (100%) rename packages/local_auth/{ => local_auth}/example/android/settings.gradle (100%) rename packages/local_auth/{ => local_auth}/example/android/settings_aar.gradle (100%) rename packages/local_auth/{ => local_auth}/example/integration_test/local_auth_test.dart (100%) rename packages/local_auth/{ => local_auth}/example/ios/Flutter/AppFrameworkInfo.plist (100%) rename packages/local_auth/{ => local_auth}/example/ios/Flutter/Debug.xcconfig (100%) rename packages/local_auth/{ => local_auth}/example/ios/Flutter/Release.xcconfig (100%) rename packages/local_auth/{ => local_auth}/example/ios/Podfile (100%) rename packages/local_auth/{ => local_auth}/example/ios/Runner.xcodeproj/project.pbxproj (100%) rename packages/local_auth/{ => local_auth}/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata (100%) rename packages/local_auth/{ => local_auth}/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme (100%) rename packages/local_auth/{ => local_auth}/example/ios/Runner.xcworkspace/contents.xcworkspacedata (100%) rename packages/local_auth/{ => local_auth}/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename packages/local_auth/{ => local_auth}/example/ios/Runner/AppDelegate.h (100%) rename packages/local_auth/{ => local_auth}/example/ios/Runner/AppDelegate.m (100%) rename packages/local_auth/{ => local_auth}/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename packages/local_auth/{ => local_auth}/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png (100%) rename packages/local_auth/{ => local_auth}/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png (100%) rename packages/local_auth/{ => local_auth}/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png (100%) rename packages/local_auth/{ => local_auth}/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png (100%) rename packages/local_auth/{ => local_auth}/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png (100%) rename packages/local_auth/{ => local_auth}/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png (100%) rename packages/local_auth/{ => local_auth}/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png (100%) rename packages/local_auth/{ => local_auth}/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png (100%) rename packages/local_auth/{ => local_auth}/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png (100%) rename packages/local_auth/{ => local_auth}/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png (100%) rename packages/local_auth/{ => local_auth}/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png (100%) rename packages/local_auth/{ => local_auth}/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png (100%) rename packages/local_auth/{ => local_auth}/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png (100%) rename packages/local_auth/{ => local_auth}/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png (100%) rename packages/local_auth/{ => local_auth}/example/ios/Runner/Base.lproj/LaunchScreen.storyboard (100%) rename packages/local_auth/{ => local_auth}/example/ios/Runner/Base.lproj/Main.storyboard (100%) rename packages/local_auth/{ => local_auth}/example/ios/Runner/Info.plist (100%) rename packages/local_auth/{ => local_auth}/example/ios/Runner/main.m (100%) rename packages/local_auth/{ => local_auth}/example/ios/RunnerTests/FLTLocalAuthPluginTests.m (100%) rename packages/local_auth/{ => local_auth}/example/ios/RunnerTests/Info.plist (100%) rename packages/local_auth/{ => local_auth}/example/lib/main.dart (100%) rename packages/local_auth/{ => local_auth}/example/pubspec.yaml (100%) rename packages/local_auth/{ => local_auth}/example/test_driver/integration_test.dart (100%) rename packages/local_auth/{ => local_auth}/ios/Assets/.gitkeep (100%) rename packages/local_auth/{ => local_auth}/ios/Classes/FLTLocalAuthPlugin.h (100%) rename packages/local_auth/{ => local_auth}/ios/Classes/FLTLocalAuthPlugin.m (100%) rename packages/local_auth/{ => local_auth}/ios/local_auth.podspec (100%) rename packages/local_auth/{ => local_auth}/lib/auth_strings.dart (100%) rename packages/local_auth/{ => local_auth}/lib/error_codes.dart (100%) rename packages/local_auth/{ => local_auth}/lib/local_auth.dart (100%) rename packages/local_auth/{ => local_auth}/pubspec.yaml (97%) rename packages/local_auth/{ => local_auth}/test/local_auth_test.dart (100%) diff --git a/packages/local_auth/AUTHORS b/packages/local_auth/local_auth/AUTHORS similarity index 100% rename from packages/local_auth/AUTHORS rename to packages/local_auth/local_auth/AUTHORS diff --git a/packages/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md similarity index 100% rename from packages/local_auth/CHANGELOG.md rename to packages/local_auth/local_auth/CHANGELOG.md diff --git a/packages/local_auth/LICENSE b/packages/local_auth/local_auth/LICENSE similarity index 100% rename from packages/local_auth/LICENSE rename to packages/local_auth/local_auth/LICENSE diff --git a/packages/local_auth/README.md b/packages/local_auth/local_auth/README.md similarity index 100% rename from packages/local_auth/README.md rename to packages/local_auth/local_auth/README.md diff --git a/packages/local_auth/android/build.gradle b/packages/local_auth/local_auth/android/build.gradle similarity index 100% rename from packages/local_auth/android/build.gradle rename to packages/local_auth/local_auth/android/build.gradle diff --git a/packages/local_auth/android/lint-baseline.xml b/packages/local_auth/local_auth/android/lint-baseline.xml similarity index 100% rename from packages/local_auth/android/lint-baseline.xml rename to packages/local_auth/local_auth/android/lint-baseline.xml diff --git a/packages/local_auth/android/settings.gradle b/packages/local_auth/local_auth/android/settings.gradle similarity index 100% rename from packages/local_auth/android/settings.gradle rename to packages/local_auth/local_auth/android/settings.gradle diff --git a/packages/local_auth/android/src/main/AndroidManifest.xml b/packages/local_auth/local_auth/android/src/main/AndroidManifest.xml similarity index 100% rename from packages/local_auth/android/src/main/AndroidManifest.xml rename to packages/local_auth/local_auth/android/src/main/AndroidManifest.xml diff --git a/packages/local_auth/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java b/packages/local_auth/local_auth/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java similarity index 100% rename from packages/local_auth/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java rename to packages/local_auth/local_auth/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java diff --git a/packages/local_auth/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java b/packages/local_auth/local_auth/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java similarity index 100% rename from packages/local_auth/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java rename to packages/local_auth/local_auth/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java diff --git a/packages/local_auth/android/src/main/res/drawable/fingerprint_initial_icon.xml b/packages/local_auth/local_auth/android/src/main/res/drawable/fingerprint_initial_icon.xml similarity index 100% rename from packages/local_auth/android/src/main/res/drawable/fingerprint_initial_icon.xml rename to packages/local_auth/local_auth/android/src/main/res/drawable/fingerprint_initial_icon.xml diff --git a/packages/local_auth/android/src/main/res/drawable/fingerprint_success_icon.xml b/packages/local_auth/local_auth/android/src/main/res/drawable/fingerprint_success_icon.xml similarity index 100% rename from packages/local_auth/android/src/main/res/drawable/fingerprint_success_icon.xml rename to packages/local_auth/local_auth/android/src/main/res/drawable/fingerprint_success_icon.xml diff --git a/packages/local_auth/android/src/main/res/drawable/fingerprint_warning_icon.xml b/packages/local_auth/local_auth/android/src/main/res/drawable/fingerprint_warning_icon.xml similarity index 100% rename from packages/local_auth/android/src/main/res/drawable/fingerprint_warning_icon.xml rename to packages/local_auth/local_auth/android/src/main/res/drawable/fingerprint_warning_icon.xml diff --git a/packages/local_auth/android/src/main/res/drawable/ic_done_white_24dp.xml b/packages/local_auth/local_auth/android/src/main/res/drawable/ic_done_white_24dp.xml similarity index 100% rename from packages/local_auth/android/src/main/res/drawable/ic_done_white_24dp.xml rename to packages/local_auth/local_auth/android/src/main/res/drawable/ic_done_white_24dp.xml diff --git a/packages/local_auth/android/src/main/res/drawable/ic_fingerprint_white_24dp.xml b/packages/local_auth/local_auth/android/src/main/res/drawable/ic_fingerprint_white_24dp.xml similarity index 100% rename from packages/local_auth/android/src/main/res/drawable/ic_fingerprint_white_24dp.xml rename to packages/local_auth/local_auth/android/src/main/res/drawable/ic_fingerprint_white_24dp.xml diff --git a/packages/local_auth/android/src/main/res/drawable/ic_priority_high_white_24dp.xml b/packages/local_auth/local_auth/android/src/main/res/drawable/ic_priority_high_white_24dp.xml similarity index 100% rename from packages/local_auth/android/src/main/res/drawable/ic_priority_high_white_24dp.xml rename to packages/local_auth/local_auth/android/src/main/res/drawable/ic_priority_high_white_24dp.xml diff --git a/packages/local_auth/android/src/main/res/layout/go_to_setting.xml b/packages/local_auth/local_auth/android/src/main/res/layout/go_to_setting.xml similarity index 100% rename from packages/local_auth/android/src/main/res/layout/go_to_setting.xml rename to packages/local_auth/local_auth/android/src/main/res/layout/go_to_setting.xml diff --git a/packages/local_auth/android/src/main/res/layout/scan_fp.xml b/packages/local_auth/local_auth/android/src/main/res/layout/scan_fp.xml similarity index 100% rename from packages/local_auth/android/src/main/res/layout/scan_fp.xml rename to packages/local_auth/local_auth/android/src/main/res/layout/scan_fp.xml diff --git a/packages/local_auth/android/src/main/res/values/colors.xml b/packages/local_auth/local_auth/android/src/main/res/values/colors.xml similarity index 100% rename from packages/local_auth/android/src/main/res/values/colors.xml rename to packages/local_auth/local_auth/android/src/main/res/values/colors.xml diff --git a/packages/local_auth/android/src/main/res/values/dimens.xml b/packages/local_auth/local_auth/android/src/main/res/values/dimens.xml similarity index 100% rename from packages/local_auth/android/src/main/res/values/dimens.xml rename to packages/local_auth/local_auth/android/src/main/res/values/dimens.xml diff --git a/packages/local_auth/android/src/main/res/values/styles.xml b/packages/local_auth/local_auth/android/src/main/res/values/styles.xml similarity index 100% rename from packages/local_auth/android/src/main/res/values/styles.xml rename to packages/local_auth/local_auth/android/src/main/res/values/styles.xml diff --git a/packages/local_auth/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java b/packages/local_auth/local_auth/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java similarity index 100% rename from packages/local_auth/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java rename to packages/local_auth/local_auth/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java diff --git a/packages/local_auth/example/README.md b/packages/local_auth/local_auth/example/README.md similarity index 100% rename from packages/local_auth/example/README.md rename to packages/local_auth/local_auth/example/README.md diff --git a/packages/local_auth/example/android/app/build.gradle b/packages/local_auth/local_auth/example/android/app/build.gradle similarity index 100% rename from packages/local_auth/example/android/app/build.gradle rename to packages/local_auth/local_auth/example/android/app/build.gradle diff --git a/packages/local_auth/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/local_auth/local_auth/example/android/app/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from packages/local_auth/example/android/app/gradle/wrapper/gradle-wrapper.properties rename to packages/local_auth/local_auth/example/android/app/gradle/wrapper/gradle-wrapper.properties diff --git a/packages/local_auth/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java b/packages/local_auth/local_auth/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java similarity index 100% rename from packages/local_auth/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java rename to packages/local_auth/local_auth/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java diff --git a/packages/local_auth/example/android/app/src/androidTest/java/io/flutter/plugins/localauth/FlutterFragmentActivityTest.java b/packages/local_auth/local_auth/example/android/app/src/androidTest/java/io/flutter/plugins/localauth/FlutterFragmentActivityTest.java similarity index 100% rename from packages/local_auth/example/android/app/src/androidTest/java/io/flutter/plugins/localauth/FlutterFragmentActivityTest.java rename to packages/local_auth/local_auth/example/android/app/src/androidTest/java/io/flutter/plugins/localauth/FlutterFragmentActivityTest.java diff --git a/packages/local_auth/example/android/app/src/main/AndroidManifest.xml b/packages/local_auth/local_auth/example/android/app/src/main/AndroidManifest.xml similarity index 100% rename from packages/local_auth/example/android/app/src/main/AndroidManifest.xml rename to packages/local_auth/local_auth/example/android/app/src/main/AndroidManifest.xml diff --git a/packages/local_auth/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/local_auth/local_auth/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from packages/local_auth/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png rename to packages/local_auth/local_auth/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/packages/local_auth/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/local_auth/local_auth/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from packages/local_auth/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png rename to packages/local_auth/local_auth/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/packages/local_auth/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/packages/local_auth/local_auth/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from packages/local_auth/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to packages/local_auth/local_auth/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/packages/local_auth/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/local_auth/local_auth/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from packages/local_auth/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to packages/local_auth/local_auth/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/packages/local_auth/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/local_auth/local_auth/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from packages/local_auth/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to packages/local_auth/local_auth/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/packages/local_auth/example/android/build.gradle b/packages/local_auth/local_auth/example/android/build.gradle similarity index 100% rename from packages/local_auth/example/android/build.gradle rename to packages/local_auth/local_auth/example/android/build.gradle diff --git a/packages/local_auth/example/android/gradle.properties b/packages/local_auth/local_auth/example/android/gradle.properties similarity index 100% rename from packages/local_auth/example/android/gradle.properties rename to packages/local_auth/local_auth/example/android/gradle.properties diff --git a/packages/local_auth/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/local_auth/local_auth/example/android/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from packages/local_auth/example/android/gradle/wrapper/gradle-wrapper.properties rename to packages/local_auth/local_auth/example/android/gradle/wrapper/gradle-wrapper.properties diff --git a/packages/local_auth/example/android/settings.gradle b/packages/local_auth/local_auth/example/android/settings.gradle similarity index 100% rename from packages/local_auth/example/android/settings.gradle rename to packages/local_auth/local_auth/example/android/settings.gradle diff --git a/packages/local_auth/example/android/settings_aar.gradle b/packages/local_auth/local_auth/example/android/settings_aar.gradle similarity index 100% rename from packages/local_auth/example/android/settings_aar.gradle rename to packages/local_auth/local_auth/example/android/settings_aar.gradle diff --git a/packages/local_auth/example/integration_test/local_auth_test.dart b/packages/local_auth/local_auth/example/integration_test/local_auth_test.dart similarity index 100% rename from packages/local_auth/example/integration_test/local_auth_test.dart rename to packages/local_auth/local_auth/example/integration_test/local_auth_test.dart diff --git a/packages/local_auth/example/ios/Flutter/AppFrameworkInfo.plist b/packages/local_auth/local_auth/example/ios/Flutter/AppFrameworkInfo.plist similarity index 100% rename from packages/local_auth/example/ios/Flutter/AppFrameworkInfo.plist rename to packages/local_auth/local_auth/example/ios/Flutter/AppFrameworkInfo.plist diff --git a/packages/local_auth/example/ios/Flutter/Debug.xcconfig b/packages/local_auth/local_auth/example/ios/Flutter/Debug.xcconfig similarity index 100% rename from packages/local_auth/example/ios/Flutter/Debug.xcconfig rename to packages/local_auth/local_auth/example/ios/Flutter/Debug.xcconfig diff --git a/packages/local_auth/example/ios/Flutter/Release.xcconfig b/packages/local_auth/local_auth/example/ios/Flutter/Release.xcconfig similarity index 100% rename from packages/local_auth/example/ios/Flutter/Release.xcconfig rename to packages/local_auth/local_auth/example/ios/Flutter/Release.xcconfig diff --git a/packages/local_auth/example/ios/Podfile b/packages/local_auth/local_auth/example/ios/Podfile similarity index 100% rename from packages/local_auth/example/ios/Podfile rename to packages/local_auth/local_auth/example/ios/Podfile diff --git a/packages/local_auth/example/ios/Runner.xcodeproj/project.pbxproj b/packages/local_auth/local_auth/example/ios/Runner.xcodeproj/project.pbxproj similarity index 100% rename from packages/local_auth/example/ios/Runner.xcodeproj/project.pbxproj rename to packages/local_auth/local_auth/example/ios/Runner.xcodeproj/project.pbxproj diff --git a/packages/local_auth/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/local_auth/local_auth/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from packages/local_auth/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to packages/local_auth/local_auth/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/packages/local_auth/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/local_auth/local_auth/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme similarity index 100% rename from packages/local_auth/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to packages/local_auth/local_auth/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme diff --git a/packages/local_auth/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/local_auth/local_auth/example/ios/Runner.xcworkspace/contents.xcworkspacedata similarity index 100% rename from packages/local_auth/example/ios/Runner.xcworkspace/contents.xcworkspacedata rename to packages/local_auth/local_auth/example/ios/Runner.xcworkspace/contents.xcworkspacedata diff --git a/packages/local_auth/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/local_auth/local_auth/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from packages/local_auth/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to packages/local_auth/local_auth/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/packages/local_auth/example/ios/Runner/AppDelegate.h b/packages/local_auth/local_auth/example/ios/Runner/AppDelegate.h similarity index 100% rename from packages/local_auth/example/ios/Runner/AppDelegate.h rename to packages/local_auth/local_auth/example/ios/Runner/AppDelegate.h diff --git a/packages/local_auth/example/ios/Runner/AppDelegate.m b/packages/local_auth/local_auth/example/ios/Runner/AppDelegate.m similarity index 100% rename from packages/local_auth/example/ios/Runner/AppDelegate.m rename to packages/local_auth/local_auth/example/ios/Runner/AppDelegate.m diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json rename to packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png similarity index 100% rename from packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png rename to packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png similarity index 100% rename from packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png rename to packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png similarity index 100% rename from packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png rename to packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png similarity index 100% rename from packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png rename to packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png similarity index 100% rename from packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png rename to packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png similarity index 100% rename from packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png rename to packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png similarity index 100% rename from packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png rename to packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png similarity index 100% rename from packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png rename to packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png similarity index 100% rename from packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png rename to packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png similarity index 100% rename from packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png rename to packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png similarity index 100% rename from packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png rename to packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png similarity index 100% rename from packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png rename to packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png similarity index 100% rename from packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png rename to packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png diff --git a/packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png similarity index 100% rename from packages/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png rename to packages/local_auth/local_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png diff --git a/packages/local_auth/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/local_auth/local_auth/example/ios/Runner/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from packages/local_auth/example/ios/Runner/Base.lproj/LaunchScreen.storyboard rename to packages/local_auth/local_auth/example/ios/Runner/Base.lproj/LaunchScreen.storyboard diff --git a/packages/local_auth/example/ios/Runner/Base.lproj/Main.storyboard b/packages/local_auth/local_auth/example/ios/Runner/Base.lproj/Main.storyboard similarity index 100% rename from packages/local_auth/example/ios/Runner/Base.lproj/Main.storyboard rename to packages/local_auth/local_auth/example/ios/Runner/Base.lproj/Main.storyboard diff --git a/packages/local_auth/example/ios/Runner/Info.plist b/packages/local_auth/local_auth/example/ios/Runner/Info.plist similarity index 100% rename from packages/local_auth/example/ios/Runner/Info.plist rename to packages/local_auth/local_auth/example/ios/Runner/Info.plist diff --git a/packages/local_auth/example/ios/Runner/main.m b/packages/local_auth/local_auth/example/ios/Runner/main.m similarity index 100% rename from packages/local_auth/example/ios/Runner/main.m rename to packages/local_auth/local_auth/example/ios/Runner/main.m diff --git a/packages/local_auth/example/ios/RunnerTests/FLTLocalAuthPluginTests.m b/packages/local_auth/local_auth/example/ios/RunnerTests/FLTLocalAuthPluginTests.m similarity index 100% rename from packages/local_auth/example/ios/RunnerTests/FLTLocalAuthPluginTests.m rename to packages/local_auth/local_auth/example/ios/RunnerTests/FLTLocalAuthPluginTests.m diff --git a/packages/local_auth/example/ios/RunnerTests/Info.plist b/packages/local_auth/local_auth/example/ios/RunnerTests/Info.plist similarity index 100% rename from packages/local_auth/example/ios/RunnerTests/Info.plist rename to packages/local_auth/local_auth/example/ios/RunnerTests/Info.plist diff --git a/packages/local_auth/example/lib/main.dart b/packages/local_auth/local_auth/example/lib/main.dart similarity index 100% rename from packages/local_auth/example/lib/main.dart rename to packages/local_auth/local_auth/example/lib/main.dart diff --git a/packages/local_auth/example/pubspec.yaml b/packages/local_auth/local_auth/example/pubspec.yaml similarity index 100% rename from packages/local_auth/example/pubspec.yaml rename to packages/local_auth/local_auth/example/pubspec.yaml diff --git a/packages/local_auth/example/test_driver/integration_test.dart b/packages/local_auth/local_auth/example/test_driver/integration_test.dart similarity index 100% rename from packages/local_auth/example/test_driver/integration_test.dart rename to packages/local_auth/local_auth/example/test_driver/integration_test.dart diff --git a/packages/local_auth/ios/Assets/.gitkeep b/packages/local_auth/local_auth/ios/Assets/.gitkeep similarity index 100% rename from packages/local_auth/ios/Assets/.gitkeep rename to packages/local_auth/local_auth/ios/Assets/.gitkeep diff --git a/packages/local_auth/ios/Classes/FLTLocalAuthPlugin.h b/packages/local_auth/local_auth/ios/Classes/FLTLocalAuthPlugin.h similarity index 100% rename from packages/local_auth/ios/Classes/FLTLocalAuthPlugin.h rename to packages/local_auth/local_auth/ios/Classes/FLTLocalAuthPlugin.h diff --git a/packages/local_auth/ios/Classes/FLTLocalAuthPlugin.m b/packages/local_auth/local_auth/ios/Classes/FLTLocalAuthPlugin.m similarity index 100% rename from packages/local_auth/ios/Classes/FLTLocalAuthPlugin.m rename to packages/local_auth/local_auth/ios/Classes/FLTLocalAuthPlugin.m diff --git a/packages/local_auth/ios/local_auth.podspec b/packages/local_auth/local_auth/ios/local_auth.podspec similarity index 100% rename from packages/local_auth/ios/local_auth.podspec rename to packages/local_auth/local_auth/ios/local_auth.podspec diff --git a/packages/local_auth/lib/auth_strings.dart b/packages/local_auth/local_auth/lib/auth_strings.dart similarity index 100% rename from packages/local_auth/lib/auth_strings.dart rename to packages/local_auth/local_auth/lib/auth_strings.dart diff --git a/packages/local_auth/lib/error_codes.dart b/packages/local_auth/local_auth/lib/error_codes.dart similarity index 100% rename from packages/local_auth/lib/error_codes.dart rename to packages/local_auth/local_auth/lib/error_codes.dart diff --git a/packages/local_auth/lib/local_auth.dart b/packages/local_auth/local_auth/lib/local_auth.dart similarity index 100% rename from packages/local_auth/lib/local_auth.dart rename to packages/local_auth/local_auth/lib/local_auth.dart diff --git a/packages/local_auth/pubspec.yaml b/packages/local_auth/local_auth/pubspec.yaml similarity index 97% rename from packages/local_auth/pubspec.yaml rename to packages/local_auth/local_auth/pubspec.yaml index 08215c566040..cd9d0d9760f4 100644 --- a/packages/local_auth/pubspec.yaml +++ b/packages/local_auth/local_auth/pubspec.yaml @@ -1,7 +1,7 @@ name: local_auth description: Flutter plugin for Android and iOS devices to allow local authentication via fingerprint, touch ID, face ID, passcode, pin, or pattern. -repository: https://github.com/flutter/plugins/tree/main/packages/local_auth +repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 version: 1.1.10 diff --git a/packages/local_auth/test/local_auth_test.dart b/packages/local_auth/local_auth/test/local_auth_test.dart similarity index 100% rename from packages/local_auth/test/local_auth_test.dart rename to packages/local_auth/local_auth/test/local_auth_test.dart From df0ed4ca9862aa4be2aa97283a7f3ed9812689c1 Mon Sep 17 00:00:00 2001 From: BeMacized Date: Thu, 27 Jan 2022 16:33:17 +0100 Subject: [PATCH 124/600] Remove dependency on flutter.dev and google.com in native packages for Android and iOS (#4707) --- .../webview_flutter_test.dart | 56 ++++++++++++------- .../webview_flutter_test.dart | 36 ++++++++---- 2 files changed, 62 insertions(+), 30 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart index 544089f1a7ab..58f2f369bcf5 100644 --- a/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart @@ -2,8 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// This test is run using `flutter drive` by the CI (see /script/tool/README.md +// in this repository for details on driving that tooling manually), but can +// also be run using `flutter test` directly during development. + import 'dart:async'; import 'dart:convert'; +import 'dart:io'; import 'dart:typed_data'; import 'package:flutter/foundation.dart'; @@ -19,19 +24,31 @@ import 'package:webview_flutter_android_example/navigation_request.dart'; import 'package:webview_flutter_android_example/web_view.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; -void main() { +Future main() async { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - // URLs to navigate to in tests. These need to be URLs that we are confident will - // always be accessible, and won't do redirection. (E.g., just - // 'https://www.google.com/' will sometimes redirect traffic that looks - // like it's coming from a bot, which is true of these tests). - const String primaryUrl = 'https://flutter.dev/'; - const String secondaryUrl = 'https://www.google.com/robots.txt'; - const bool _skipDueToIssue86757 = true; - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757. + final HttpServer server = await HttpServer.bind(InternetAddress.anyIPv4, 0); + server.forEach((HttpRequest request) { + if (request.uri.path == '/hello.txt') { + request.response.writeln('Hello, world.'); + } else if (request.uri.path == '/secondary.txt') { + request.response.writeln('How are you today?'); + } else if (request.uri.path == '/headers') { + request.response.writeln('${request.headers}'); + } else if (request.uri.path == '/favicon.ico') { + request.response.statusCode = HttpStatus.notFound; + } else { + fail('unexpected request: ${request.method} ${request.uri}'); + } + request.response.close(); + }); + final String prefixUrl = 'http://${server.address.address}:${server.port}'; + final String primaryUrl = '$prefixUrl/hello.txt'; + final String secondaryUrl = '$prefixUrl/secondary.txt'; + final String headersUrl = '$prefixUrl/headers'; + testWidgets('initialUrl', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); @@ -54,7 +71,7 @@ void main() { expect(currentUrl, primaryUrl); }, skip: _skipDueToIssue86757); - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757. + // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets('loadUrl', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); @@ -97,7 +114,7 @@ void main() { expect(result, equals('2')); }); - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757. + // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets('loadUrl with headers', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); @@ -126,10 +143,9 @@ void main() { final Map headers = { 'test_header': 'flutter_test_header' }; - await controller.loadUrl('https://flutter-header-echo.herokuapp.com/', - headers: headers); + await controller.loadUrl(headersUrl, headers: headers); final String? currentUrl = await controller.currentUrl(); - expect(currentUrl, 'https://flutter-header-echo.herokuapp.com/'); + expect(currentUrl, headersUrl); await pageStarts.stream.firstWhere((String url) => url == currentUrl); await pageLoads.stream.firstWhere((String url) => url == currentUrl); @@ -139,7 +155,7 @@ void main() { expect(content.contains('flutter_test_header'), isTrue); }, skip: _skipDueToIssue86757); - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757. + // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets('JavascriptChannel', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); @@ -251,7 +267,7 @@ void main() { expect(customUserAgent2, 'Custom_User_Agent2'); }); - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757. + // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets('use default platform userAgent after webView is rebuilt', (WidgetTester tester) async { final Completer controllerCompleter = @@ -727,7 +743,7 @@ void main() { }); group('Programmatic Scroll', () { - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757. + // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets('setAndGetScrollPosition', (WidgetTester tester) async { const String scrollTestPage = ''' @@ -814,7 +830,7 @@ void main() { WebView.platform = AndroidWebView(); }); - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757. + // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets('setAndGetScrollPosition', (WidgetTester tester) async { const String scrollTestPage = ''' @@ -883,7 +899,7 @@ void main() { expect(Y_SCROLL * 2, scrollPosY); }, skip: _skipDueToIssue86757); - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757. + // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets('inputs are scrolled into view when focused', (WidgetTester tester) async { const String scrollTestPage = ''' @@ -1253,7 +1269,7 @@ void main() { // Flaky on Android: https://github.com/flutter/flutter/issues/86757 skip: _skipDueToIssue86757); - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757. + // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets( 'can open new window and go back', (WidgetTester tester) async { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart index 5824d5d32d0b..924a6caa0f85 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart @@ -2,6 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// This test is run using `flutter drive` by the CI (see /script/tool/README.md +// in this repository for details on driving that tooling manually), but can +// also be run using `flutter test` directly during development. + import 'dart:async'; import 'dart:convert'; import 'dart:io'; @@ -18,15 +22,28 @@ import 'package:webview_flutter_wkwebview_example/navigation_decision.dart'; import 'package:webview_flutter_wkwebview_example/navigation_request.dart'; import 'package:webview_flutter_wkwebview_example/web_view.dart'; -void main() { +Future main() async { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - // URLs to navigate to in tests. These need to be URLs that we are confident will - // always be accessible, and won't do redirection. (E.g., just - // 'https://www.google.com/' will sometimes redirect traffic that looks - // like it's coming from a bot, which is true of these tests). - const String primaryUrl = 'https://flutter.dev/'; - const String secondaryUrl = 'https://www.google.com/robots.txt'; + final HttpServer server = await HttpServer.bind(InternetAddress.anyIPv4, 0); + server.forEach((HttpRequest request) { + if (request.uri.path == '/hello.txt') { + request.response.writeln('Hello, world.'); + } else if (request.uri.path == '/secondary.txt') { + request.response.writeln('How are you today?'); + } else if (request.uri.path == '/headers') { + request.response.writeln('${request.headers}'); + } else if (request.uri.path == '/favicon.ico') { + request.response.statusCode = HttpStatus.notFound; + } else { + fail('unexpected request: ${request.method} ${request.uri}'); + } + request.response.close(); + }); + final String prefixUrl = 'http://${server.address.address}:${server.port}'; + final String primaryUrl = '$prefixUrl/hello.txt'; + final String secondaryUrl = '$prefixUrl/secondary.txt'; + final String headersUrl = '$prefixUrl/headers'; testWidgets('initialUrl', (WidgetTester tester) async { final Completer controllerCompleter = @@ -118,10 +135,9 @@ void main() { final Map headers = { 'test_header': 'flutter_test_header' }; - await controller.loadUrl('https://flutter-header-echo.herokuapp.com/', - headers: headers); + await controller.loadUrl(headersUrl, headers: headers); final String? currentUrl = await controller.currentUrl(); - expect(currentUrl, 'https://flutter-header-echo.herokuapp.com/'); + expect(currentUrl, headersUrl); await pageStarts.stream.firstWhere((String url) => url == currentUrl); await pageLoads.stream.firstWhere((String url) => url == currentUrl); From 609d64345cb791cbf35784ecc67e0511df79fef4 Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Thu, 27 Jan 2022 13:35:23 -0800 Subject: [PATCH 125/600] [camera]refactor more types into separate files, and add unit tests (#4704) --- packages/camera/camera/CHANGELOG.md | 1 + .../ios/Runner.xcodeproj/project.pbxproj | 8 +- .../ios/RunnerTests/CameraPropertiesTests.m | 107 +++++++++ .../ios/RunnerTests/FLTFlashModeTests.m | 30 --- .../camera/camera/ios/Classes/CameraPlugin.m | 219 +++--------------- .../camera/ios/Classes/CameraPlugin.modulemap | 2 +- .../camera/ios/Classes/CameraProperties.h | 118 ++++++++++ .../camera/ios/Classes/CameraProperties.m | 187 +++++++++++++++ .../camera/camera/ios/Classes/FlashMode.h | 32 --- .../camera/camera/ios/Classes/FlashMode.m | 39 ---- 10 files changed, 447 insertions(+), 296 deletions(-) create mode 100644 packages/camera/camera/example/ios/RunnerTests/CameraPropertiesTests.m delete mode 100644 packages/camera/camera/example/ios/RunnerTests/FLTFlashModeTests.m create mode 100644 packages/camera/camera/ios/Classes/CameraProperties.h create mode 100644 packages/camera/camera/ios/Classes/CameraProperties.m delete mode 100644 packages/camera/camera/ios/Classes/FlashMode.h delete mode 100644 packages/camera/camera/ios/Classes/FlashMode.m diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 64b7cfd03f81..dd4042ebdb59 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Minor iOS internal code cleanup related to resolution preset, video format, focus mode, exposure mode and device orientation. * Minor iOS internal code cleanup related to flash mode. ## 0.9.4+8 diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj index 87bcf2e666cd..0be5b59bb6b4 100644 --- a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj @@ -22,10 +22,10 @@ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; E01EE4A82799F3A5008C1950 /* QueueHelperTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E01EE4A72799F3A5008C1950 /* QueueHelperTests.m */; }; E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */; }; - E032F2A727A0BF2D009E9028 /* FLTFlashModeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E032F2A627A0BF2D009E9028 /* FLTFlashModeTests.m */; }; E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */; }; E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */; }; E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */; }; + E0F95E3D27A32AB900699390 /* CameraPropertiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */; }; E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */; }; F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */ = {isa = PBXBuildFile; fileRef = F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */; }; /* End PBXBuildFile section */ @@ -82,10 +82,10 @@ A24F9E418BA48BCC7409B117 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; E01EE4A72799F3A5008C1950 /* QueueHelperTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QueueHelperTests.m; sourceTree = ""; }; E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CameraCaptureSessionQueueRaceConditionTests.m; sourceTree = ""; }; - E032F2A627A0BF2D009E9028 /* FLTFlashModeTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTFlashModeTests.m; sourceTree = ""; }; E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeMethodChannelTests.m; sourceTree = ""; }; E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeTextureRegistryTests.m; sourceTree = ""; }; E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeEventChannelTests.m; sourceTree = ""; }; + E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPropertiesTests.m; sourceTree = ""; }; E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPreviewPauseTests.m; sourceTree = ""; }; F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockFLTThreadSafeFlutterResult.h; sourceTree = ""; }; F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockFLTThreadSafeFlutterResult.m; sourceTree = ""; }; @@ -127,7 +127,7 @@ F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */, F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */, E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */, - E032F2A627A0BF2D009E9028 /* FLTFlashModeTests.m */, + E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */, ); path = RunnerTests; sourceTree = ""; @@ -391,8 +391,8 @@ buildActionMask = 2147483647; files = ( 03F6F8B226CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m in Sources */, - E032F2A727A0BF2D009E9028 /* FLTFlashModeTests.m in Sources */, 033B94BE269C40A200B4DF97 /* CameraMethodChannelTests.m in Sources */, + E0F95E3D27A32AB900699390 /* CameraPropertiesTests.m in Sources */, 03BB766B2665316900CE5A93 /* CameraFocusTests.m in Sources */, E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */, F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */, diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraPropertiesTests.m b/packages/camera/camera/example/ios/RunnerTests/CameraPropertiesTests.m new file mode 100644 index 000000000000..865791586382 --- /dev/null +++ b/packages/camera/camera/example/ios/RunnerTests/CameraPropertiesTests.m @@ -0,0 +1,107 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import camera.Test; +@import AVFoundation; +@import XCTest; + +@interface CameraPropertiesTests : XCTestCase + +@end + +@implementation CameraPropertiesTests + +#pragma mark - flash mode tests + +- (void)testFLTGetFLTFlashModeForString { + XCTAssertEqual(FLTFlashModeOff, FLTGetFLTFlashModeForString(@"off")); + XCTAssertEqual(FLTFlashModeAuto, FLTGetFLTFlashModeForString(@"auto")); + XCTAssertEqual(FLTFlashModeAlways, FLTGetFLTFlashModeForString(@"always")); + XCTAssertEqual(FLTFlashModeTorch, FLTGetFLTFlashModeForString(@"torch")); + XCTAssertThrows(FLTGetFLTFlashModeForString(@"unkwown")); +} + +- (void)testFLTGetAVCaptureFlashModeForFLTFlashMode { + XCTAssertEqual(AVCaptureFlashModeOff, FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashModeOff)); + XCTAssertEqual(AVCaptureFlashModeAuto, FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashModeAuto)); + XCTAssertEqual(AVCaptureFlashModeOn, FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashModeAlways)); + XCTAssertEqual(-1, FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashModeTorch)); +} + +#pragma mark - exposure mode tests + +- (void)testFLTGetStringForFLTExposureMode { + XCTAssertEqualObjects(@"auto", FLTGetStringForFLTExposureMode(FLTExposureModeAuto)); + XCTAssertEqualObjects(@"locked", FLTGetStringForFLTExposureMode(FLTExposureModeLocked)); + XCTAssertThrows(FLTGetStringForFLTExposureMode(-1)); +} + +- (void)testFLTGetFLTExposureModeForString { + XCTAssertEqual(FLTExposureModeAuto, FLTGetFLTExposureModeForString(@"auto")); + XCTAssertEqual(FLTExposureModeLocked, FLTGetFLTExposureModeForString(@"locked")); + XCTAssertThrows(FLTGetFLTExposureModeForString(@"unknown")); +} + +#pragma mark - focus mode tests + +- (void)testFLTGetStringForFLTFocusMode { + XCTAssertEqualObjects(@"auto", FLTGetStringForFLTFocusMode(FLTFocusModeAuto)); + XCTAssertEqualObjects(@"locked", FLTGetStringForFLTFocusMode(FLTFocusModeLocked)); + XCTAssertThrows(FLTGetStringForFLTFocusMode(-1)); +} + +- (void)testFLTGetFLTFocusModeForString { + XCTAssertEqual(FLTFocusModeAuto, FLTGetFLTFocusModeForString(@"auto")); + XCTAssertEqual(FLTFocusModeLocked, FLTGetFLTFocusModeForString(@"locked")); + XCTAssertThrows(FLTGetFLTFocusModeForString(@"unknown")); +} + +#pragma mark - resolution preset tests + +- (void)testFLTGetFLTResolutionPresetForString { + XCTAssertEqual(FLTResolutionPresetVeryLow, FLTGetFLTResolutionPresetForString(@"veryLow")); + XCTAssertEqual(FLTResolutionPresetLow, FLTGetFLTResolutionPresetForString(@"low")); + XCTAssertEqual(FLTResolutionPresetMedium, FLTGetFLTResolutionPresetForString(@"medium")); + XCTAssertEqual(FLTResolutionPresetHigh, FLTGetFLTResolutionPresetForString(@"high")); + XCTAssertEqual(FLTResolutionPresetVeryHigh, FLTGetFLTResolutionPresetForString(@"veryHigh")); + XCTAssertEqual(FLTResolutionPresetUltraHigh, FLTGetFLTResolutionPresetForString(@"ultraHigh")); + XCTAssertEqual(FLTResolutionPresetMax, FLTGetFLTResolutionPresetForString(@"max")); + XCTAssertThrows(FLTGetFLTFlashModeForString(@"unknown")); +} + +#pragma mark - video format tests + +- (void)testFLTGetVideoFormatFromString { + XCTAssertEqual(kCVPixelFormatType_32BGRA, FLTGetVideoFormatFromString(@"bgra8888")); + XCTAssertEqual(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, + FLTGetVideoFormatFromString(@"yuv420")); + XCTAssertEqual(kCVPixelFormatType_32BGRA, FLTGetVideoFormatFromString(@"unknown")); +} + +#pragma mark - device orientation tests + +- (void)testFLTGetUIDeviceOrientationForString { + XCTAssertEqual(UIDeviceOrientationPortraitUpsideDown, + FLTGetUIDeviceOrientationForString(@"portraitDown")); + XCTAssertEqual(UIDeviceOrientationLandscapeRight, + FLTGetUIDeviceOrientationForString(@"landscapeLeft")); + XCTAssertEqual(UIDeviceOrientationLandscapeLeft, + FLTGetUIDeviceOrientationForString(@"landscapeRight")); + XCTAssertEqual(UIDeviceOrientationPortrait, FLTGetUIDeviceOrientationForString(@"portraitUp")); + XCTAssertThrows(FLTGetUIDeviceOrientationForString(@"unknown")); +} + +- (void)testFLTGetStringForUIDeviceOrientation { + XCTAssertEqualObjects(@"portraitDown", + FLTGetStringForUIDeviceOrientation(UIDeviceOrientationPortraitUpsideDown)); + XCTAssertEqualObjects(@"landscapeLeft", + FLTGetStringForUIDeviceOrientation(UIDeviceOrientationLandscapeRight)); + XCTAssertEqualObjects(@"landscapeRight", + FLTGetStringForUIDeviceOrientation(UIDeviceOrientationLandscapeLeft)); + XCTAssertEqualObjects(@"portraitUp", + FLTGetStringForUIDeviceOrientation(UIDeviceOrientationPortrait)); + XCTAssertEqualObjects(@"portraitUp", FLTGetStringForUIDeviceOrientation(-1)); +} + +@end diff --git a/packages/camera/camera/example/ios/RunnerTests/FLTFlashModeTests.m b/packages/camera/camera/example/ios/RunnerTests/FLTFlashModeTests.m deleted file mode 100644 index b38b7eba8304..000000000000 --- a/packages/camera/camera/example/ios/RunnerTests/FLTFlashModeTests.m +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -@import camera.Test; -@import AVFoundation; -#import - -@interface FLTFlashModeTests : XCTestCase - -@end - -@implementation FLTFlashModeTests - -- (void)testFLTGetFLTFlashModeForString { - XCTAssertEqual(FLTFlashModeOff, FLTGetFLTFlashModeForString(@"off")); - XCTAssertEqual(FLTFlashModeAuto, FLTGetFLTFlashModeForString(@"auto")); - XCTAssertEqual(FLTFlashModeAlways, FLTGetFLTFlashModeForString(@"always")); - XCTAssertEqual(FLTFlashModeTorch, FLTGetFLTFlashModeForString(@"torch")); - XCTAssertThrows(FLTGetFLTFlashModeForString(@"unkwown")); -} - -- (void)testFLTGetAVCaptureFlashModeForFLTFlashMode { - XCTAssertEqual(AVCaptureFlashModeOff, FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashModeOff)); - XCTAssertEqual(AVCaptureFlashModeAuto, FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashModeAuto)); - XCTAssertEqual(AVCaptureFlashModeOn, FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashModeAlways)); - XCTAssertEqual(-1, FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashModeTorch)); -} - -@end diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.m b/packages/camera/camera/ios/Classes/CameraPlugin.m index be5ba80923c2..ae551887f7f2 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin.m +++ b/packages/camera/camera/ios/Classes/CameraPlugin.m @@ -10,11 +10,11 @@ #import #import #import +#import "CameraProperties.h" #import "FLTThreadSafeEventChannel.h" #import "FLTThreadSafeFlutterResult.h" #import "FLTThreadSafeMethodChannel.h" #import "FLTThreadSafeTextureRegistry.h" -#import "FlashMode.h" @interface FLTSavePhotoDelegate : NSObject @property(readonly, nonatomic) NSString *path; @@ -114,167 +114,6 @@ - (void)captureOutput:(AVCapturePhotoOutput *)output } @end -static OSType getVideoFormatFromString(NSString *videoFormatString) { - if ([videoFormatString isEqualToString:@"bgra8888"]) { - return kCVPixelFormatType_32BGRA; - } else if ([videoFormatString isEqualToString:@"yuv420"]) { - return kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange; - } else { - NSLog(@"The selected imageFormatGroup is not supported by iOS. Defaulting to brga8888"); - return kCVPixelFormatType_32BGRA; - } -} - -// Mirrors ExposureMode in camera.dart -typedef enum { - ExposureModeAuto, - ExposureModeLocked, - -} ExposureMode; - -static NSString *getStringForExposureMode(ExposureMode mode) { - switch (mode) { - case ExposureModeAuto: - return @"auto"; - case ExposureModeLocked: - return @"locked"; - } - NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain - code:NSURLErrorUnknown - userInfo:@{ - NSLocalizedDescriptionKey : [NSString - stringWithFormat:@"Unknown string for exposure mode"] - }]; - @throw error; -} - -static ExposureMode getExposureModeForString(NSString *mode) { - if ([mode isEqualToString:@"auto"]) { - return ExposureModeAuto; - } else if ([mode isEqualToString:@"locked"]) { - return ExposureModeLocked; - } else { - NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain - code:NSURLErrorUnknown - userInfo:@{ - NSLocalizedDescriptionKey : [NSString - stringWithFormat:@"Unknown exposure mode %@", mode] - }]; - @throw error; - } -} - -static UIDeviceOrientation getUIDeviceOrientationForString(NSString *orientation) { - if ([orientation isEqualToString:@"portraitDown"]) { - return UIDeviceOrientationPortraitUpsideDown; - } else if ([orientation isEqualToString:@"landscapeLeft"]) { - return UIDeviceOrientationLandscapeRight; - } else if ([orientation isEqualToString:@"landscapeRight"]) { - return UIDeviceOrientationLandscapeLeft; - } else if ([orientation isEqualToString:@"portraitUp"]) { - return UIDeviceOrientationPortrait; - } else { - NSError *error = [NSError - errorWithDomain:NSCocoaErrorDomain - code:NSURLErrorUnknown - userInfo:@{ - NSLocalizedDescriptionKey : - [NSString stringWithFormat:@"Unknown device orientation %@", orientation] - }]; - @throw error; - } -} - -static NSString *getStringForUIDeviceOrientation(UIDeviceOrientation orientation) { - switch (orientation) { - case UIDeviceOrientationPortraitUpsideDown: - return @"portraitDown"; - case UIDeviceOrientationLandscapeRight: - return @"landscapeLeft"; - case UIDeviceOrientationLandscapeLeft: - return @"landscapeRight"; - case UIDeviceOrientationPortrait: - default: - return @"portraitUp"; - break; - }; -} - -// Mirrors FocusMode in camera.dart -typedef enum { - FocusModeAuto, - FocusModeLocked, -} FocusMode; - -static NSString *getStringForFocusMode(FocusMode mode) { - switch (mode) { - case FocusModeAuto: - return @"auto"; - case FocusModeLocked: - return @"locked"; - } - NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain - code:NSURLErrorUnknown - userInfo:@{ - NSLocalizedDescriptionKey : [NSString - stringWithFormat:@"Unknown string for focus mode"] - }]; - @throw error; -} - -static FocusMode getFocusModeForString(NSString *mode) { - if ([mode isEqualToString:@"auto"]) { - return FocusModeAuto; - } else if ([mode isEqualToString:@"locked"]) { - return FocusModeLocked; - } else { - NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain - code:NSURLErrorUnknown - userInfo:@{ - NSLocalizedDescriptionKey : [NSString - stringWithFormat:@"Unknown focus mode %@", mode] - }]; - @throw error; - } -} - -// Mirrors ResolutionPreset in camera.dart -typedef enum { - veryLow, - low, - medium, - high, - veryHigh, - ultraHigh, - max, -} ResolutionPreset; - -static ResolutionPreset getResolutionPresetForString(NSString *preset) { - if ([preset isEqualToString:@"veryLow"]) { - return veryLow; - } else if ([preset isEqualToString:@"low"]) { - return low; - } else if ([preset isEqualToString:@"medium"]) { - return medium; - } else if ([preset isEqualToString:@"high"]) { - return high; - } else if ([preset isEqualToString:@"veryHigh"]) { - return veryHigh; - } else if ([preset isEqualToString:@"ultraHigh"]) { - return ultraHigh; - } else if ([preset isEqualToString:@"max"]) { - return max; - } else { - NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain - code:NSURLErrorUnknown - userInfo:@{ - NSLocalizedDescriptionKey : [NSString - stringWithFormat:@"Unknown resolution preset %@", preset] - }]; - @throw error; - } -} - @interface FLTCam : NSObject @@ -305,9 +144,9 @@ @interface FLTCam : NSObject -#import - -NS_ASSUME_NONNULL_BEGIN - -/** - * Represents camera's flash mode. Mirrors `FlashMode` enum in flash_mode.dart. - */ -typedef NS_ENUM(NSInteger, FLTFlashMode) { - FLTFlashModeOff, - FLTFlashModeAuto, - FLTFlashModeAlways, - FLTFlashModeTorch, -}; - -/** - * Gets FLTFlashMode from its string representation. - * @param mode a string representation of the FLTFlashMode. - */ -extern FLTFlashMode FLTGetFLTFlashModeForString(NSString *mode); - -/** - * Gets AVCaptureFlashMode from FLTFlashMode. - * @param mode flash mode. - */ -extern AVCaptureFlashMode FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashMode mode); - -NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera/ios/Classes/FlashMode.m b/packages/camera/camera/ios/Classes/FlashMode.m deleted file mode 100644 index b9d4a4afd6bd..000000000000 --- a/packages/camera/camera/ios/Classes/FlashMode.m +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "FlashMode.h" - -FLTFlashMode FLTGetFLTFlashModeForString(NSString *mode) { - if ([mode isEqualToString:@"off"]) { - return FLTFlashModeOff; - } else if ([mode isEqualToString:@"auto"]) { - return FLTFlashModeAuto; - } else if ([mode isEqualToString:@"always"]) { - return FLTFlashModeAlways; - } else if ([mode isEqualToString:@"torch"]) { - return FLTFlashModeTorch; - } else { - NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain - code:NSURLErrorUnknown - userInfo:@{ - NSLocalizedDescriptionKey : [NSString - stringWithFormat:@"Unknown flash mode %@", mode] - }]; - @throw error; - } -} - -AVCaptureFlashMode FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashMode mode) { - switch (mode) { - case FLTFlashModeOff: - return AVCaptureFlashModeOff; - case FLTFlashModeAuto: - return AVCaptureFlashModeAuto; - case FLTFlashModeAlways: - return AVCaptureFlashModeOn; - case FLTFlashModeTorch: - default: - return -1; - } -} From 60982c335b756b7ff096d17633b4a6377e64c59b Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Thu, 27 Jan 2022 16:00:23 -0800 Subject: [PATCH 126/600] [camera]refactor FLTCam outside of CameraPlugin (#4708) --- packages/camera/camera/CHANGELOG.md | 1 + .../ios/RunnerTests/CameraFocusTests.m | 26 +- .../ios/RunnerTests/CameraPreviewPauseTests.m | 11 +- .../camera/camera/ios/Classes/CameraPlugin.m | 1112 +---------------- .../camera/ios/Classes/CameraPlugin.modulemap | 1 + packages/camera/camera/ios/Classes/FLTCam.h | 86 ++ packages/camera/camera/ios/Classes/FLTCam.m | 1087 ++++++++++++++++ 7 files changed, 1185 insertions(+), 1139 deletions(-) create mode 100644 packages/camera/camera/ios/Classes/FLTCam.h create mode 100644 packages/camera/camera/ios/Classes/FLTCam.m diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index dd4042ebdb59..ea27720c94f8 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Minor iOS internal code cleanup related to camera class and its delegate. * Minor iOS internal code cleanup related to resolution preset, video format, focus mode, exposure mode and device orientation. * Minor iOS internal code cleanup related to flash mode. diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraFocusTests.m b/packages/camera/camera/example/ios/RunnerTests/CameraFocusTests.m index fdc2be9901a4..e0f5fdaa3c97 100644 --- a/packages/camera/camera/example/ios/RunnerTests/CameraFocusTests.m +++ b/packages/camera/camera/example/ios/RunnerTests/CameraFocusTests.m @@ -3,25 +3,11 @@ // found in the LICENSE file. @import camera; +@import camera.Test; @import XCTest; @import AVFoundation; #import -// Mirrors FocusMode in camera.dart -typedef enum { - FocusModeAuto, - FocusModeLocked, -} FocusMode; - -@interface FLTCam : NSObject - -- (void)applyFocusMode; -- (void)applyFocusMode:(FocusMode)focusMode onDevice:(AVCaptureDevice *)captureDevice; -- (void)setFocusPointWithResult:(FLTThreadSafeFlutterResult *)result x:(double)x y:(double)y; -@end - @interface CameraFocusTests : XCTestCase @property(readonly, nonatomic) FLTCam *camera; @property(readonly, nonatomic) id mockDevice; @@ -51,7 +37,7 @@ - (void)testAutoFocusWithContinuousModeSupported_ShouldSetContinuousAutoFocus { [[_mockDevice reject] setFocusMode:AVCaptureFocusModeAutoFocus]; // Run test - [_camera applyFocusMode:FocusModeAuto onDevice:_mockDevice]; + [_camera applyFocusMode:FLTFocusModeAuto onDevice:_mockDevice]; // Expect setFocusMode:AVCaptureFocusModeContinuousAutoFocus OCMVerify([_mockDevice setFocusMode:AVCaptureFocusModeContinuousAutoFocus]); @@ -68,7 +54,7 @@ - (void)testAutoFocusWithContinuousModeNotSupported_ShouldSetAutoFocus { [[_mockDevice reject] setFocusMode:AVCaptureFocusModeContinuousAutoFocus]; // Run test - [_camera applyFocusMode:FocusModeAuto onDevice:_mockDevice]; + [_camera applyFocusMode:FLTFocusModeAuto onDevice:_mockDevice]; // Expect setFocusMode:AVCaptureFocusModeAutoFocus OCMVerify([_mockDevice setFocusMode:AVCaptureFocusModeAutoFocus]); @@ -86,7 +72,7 @@ - (void)testAutoFocusWithNoModeSupported_ShouldSetNothing { [[_mockDevice reject] setFocusMode:AVCaptureFocusModeAutoFocus]; // Run test - [_camera applyFocusMode:FocusModeAuto onDevice:_mockDevice]; + [_camera applyFocusMode:FLTFocusModeAuto onDevice:_mockDevice]; } - (void)testLockedFocusWithModeSupported_ShouldSetModeAutoFocus { @@ -99,7 +85,7 @@ - (void)testLockedFocusWithModeSupported_ShouldSetModeAutoFocus { [[_mockDevice reject] setFocusMode:AVCaptureFocusModeContinuousAutoFocus]; // Run test - [_camera applyFocusMode:FocusModeLocked onDevice:_mockDevice]; + [_camera applyFocusMode:FLTFocusModeLocked onDevice:_mockDevice]; // Expect setFocusMode:AVCaptureFocusModeAutoFocus OCMVerify([_mockDevice setFocusMode:AVCaptureFocusModeAutoFocus]); @@ -116,7 +102,7 @@ - (void)testLockedFocusWithModeNotSupported_ShouldSetNothing { [[_mockDevice reject] setFocusMode:AVCaptureFocusModeAutoFocus]; // Run test - [_camera applyFocusMode:FocusModeLocked onDevice:_mockDevice]; + [_camera applyFocusMode:FLTFocusModeLocked onDevice:_mockDevice]; } - (void)testSetFocusPointWithResult_SetsFocusPointOfInterest { diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraPreviewPauseTests.m b/packages/camera/camera/example/ios/RunnerTests/CameraPreviewPauseTests.m index eb6c0079322c..2c1adbef468b 100644 --- a/packages/camera/camera/example/ios/RunnerTests/CameraPreviewPauseTests.m +++ b/packages/camera/camera/example/ios/RunnerTests/CameraPreviewPauseTests.m @@ -3,21 +3,12 @@ // found in the LICENSE file. @import camera; +@import camera.Test; @import XCTest; @import AVFoundation; #import #import "MockFLTThreadSafeFlutterResult.h" -@interface FLTCam : NSObject -@property(assign, nonatomic) BOOL isPreviewPaused; - -- (void)pausePreviewWithResult:(FLTThreadSafeFlutterResult *)result; - -- (void)resumePreviewWithResult:(FLTThreadSafeFlutterResult *)result; -@end - @interface CameraPreviewPauseTests : XCTestCase @end diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.m b/packages/camera/camera/ios/Classes/CameraPlugin.m index ae551887f7f2..a0adb70ad704 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin.m +++ b/packages/camera/camera/ios/Classes/CameraPlugin.m @@ -5,1121 +5,15 @@ #import "CameraPlugin.h" #import "CameraPlugin_Test.h" -#import -#import -#import -#import -#import +@import AVFoundation; + #import "CameraProperties.h" +#import "FLTCam.h" #import "FLTThreadSafeEventChannel.h" #import "FLTThreadSafeFlutterResult.h" #import "FLTThreadSafeMethodChannel.h" #import "FLTThreadSafeTextureRegistry.h" -@interface FLTSavePhotoDelegate : NSObject -@property(readonly, nonatomic) NSString *path; -@property(readonly, nonatomic) FLTThreadSafeFlutterResult *result; -@end - -@interface FLTImageStreamHandler : NSObject -// The queue on which `eventSink` property should be accessed -@property(nonatomic, strong) dispatch_queue_t captureSessionQueue; -// `eventSink` property should be accessed on `captureSessionQueue`. -// The block itself should be invoked on the main queue. -@property FlutterEventSink eventSink; -@end - -@implementation FLTImageStreamHandler - -- (instancetype)initWithCaptureSessionQueue:(dispatch_queue_t)captureSessionQueue { - self = [super init]; - NSAssert(self, @"super init cannot be nil"); - _captureSessionQueue = captureSessionQueue; - return self; -} - -- (FlutterError *_Nullable)onCancelWithArguments:(id _Nullable)arguments { - dispatch_async(self.captureSessionQueue, ^{ - self.eventSink = nil; - }); - return nil; -} - -- (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments - eventSink:(nonnull FlutterEventSink)events { - dispatch_async(self.captureSessionQueue, ^{ - self.eventSink = events; - }); - return nil; -} -@end - -@implementation FLTSavePhotoDelegate { - /// Used to keep the delegate alive until didFinishProcessingPhotoSampleBuffer. - FLTSavePhotoDelegate *selfReference; -} - -- initWithPath:(NSString *)path result:(FLTThreadSafeFlutterResult *)result { - self = [super init]; - NSAssert(self, @"super init cannot be nil"); - _path = path; - selfReference = self; - _result = result; - return self; -} - -- (void)captureOutput:(AVCapturePhotoOutput *)output - didFinishProcessingPhotoSampleBuffer:(CMSampleBufferRef)photoSampleBuffer - previewPhotoSampleBuffer:(CMSampleBufferRef)previewPhotoSampleBuffer - resolvedSettings:(AVCaptureResolvedPhotoSettings *)resolvedSettings - bracketSettings:(AVCaptureBracketedStillImageSettings *)bracketSettings - error:(NSError *)error API_AVAILABLE(ios(10)) { - selfReference = nil; - if (error) { - [_result sendError:error]; - return; - } - - NSData *data = [AVCapturePhotoOutput - JPEGPhotoDataRepresentationForJPEGSampleBuffer:photoSampleBuffer - previewPhotoSampleBuffer:previewPhotoSampleBuffer]; - - // TODO(sigurdm): Consider writing file asynchronously. - bool success = [data writeToFile:_path atomically:YES]; - - if (!success) { - [_result sendErrorWithCode:@"IOError" message:@"Unable to write file" details:nil]; - return; - } - [_result sendSuccessWithData:_path]; -} - -- (void)captureOutput:(AVCapturePhotoOutput *)output - didFinishProcessingPhoto:(AVCapturePhoto *)photo - error:(NSError *)error API_AVAILABLE(ios(11.0)) { - selfReference = nil; - if (error) { - [_result sendError:error]; - return; - } - - NSData *photoData = [photo fileDataRepresentation]; - - bool success = [photoData writeToFile:_path atomically:YES]; - if (!success) { - [_result sendErrorWithCode:@"IOError" message:@"Unable to write file" details:nil]; - return; - } - [_result sendSuccessWithData:_path]; -} -@end - -@interface FLTCam : NSObject -@property(readonly, nonatomic) int64_t textureId; -@property(nonatomic, copy) void (^onFrameAvailable)(void); -@property BOOL enableAudio; -@property(nonatomic) FLTImageStreamHandler *imageStreamHandler; -@property(nonatomic) FLTThreadSafeMethodChannel *methodChannel; -@property(readonly, nonatomic) AVCaptureSession *captureSession; -@property(readonly, nonatomic) AVCaptureDevice *captureDevice; -@property(readonly, nonatomic) AVCapturePhotoOutput *capturePhotoOutput API_AVAILABLE(ios(10)); -@property(readonly, nonatomic) AVCaptureVideoDataOutput *captureVideoOutput; -@property(readonly, nonatomic) AVCaptureInput *captureVideoInput; -@property(readonly) CVPixelBufferRef volatile latestPixelBuffer; -@property(readonly, nonatomic) CGSize previewSize; -@property(readonly, nonatomic) CGSize captureSize; -@property(strong, nonatomic) AVAssetWriter *videoWriter; -@property(strong, nonatomic) AVAssetWriterInput *videoWriterInput; -@property(strong, nonatomic) AVAssetWriterInput *audioWriterInput; -@property(strong, nonatomic) AVAssetWriterInputPixelBufferAdaptor *assetWriterPixelBufferAdaptor; -@property(strong, nonatomic) AVCaptureVideoDataOutput *videoOutput; -@property(strong, nonatomic) AVCaptureAudioDataOutput *audioOutput; -@property(strong, nonatomic) NSString *videoRecordingPath; -@property(assign, nonatomic) BOOL isRecording; -@property(assign, nonatomic) BOOL isRecordingPaused; -@property(assign, nonatomic) BOOL videoIsDisconnected; -@property(assign, nonatomic) BOOL audioIsDisconnected; -@property(assign, nonatomic) BOOL isAudioSetup; -@property(assign, nonatomic) BOOL isStreamingImages; -@property(assign, nonatomic) BOOL isPreviewPaused; -@property(assign, nonatomic) FLTResolutionPreset resolutionPreset; -@property(assign, nonatomic) FLTExposureMode exposureMode; -@property(assign, nonatomic) FLTFocusMode focusMode; -@property(assign, nonatomic) FLTFlashMode flashMode; -@property(assign, nonatomic) UIDeviceOrientation lockedCaptureOrientation; -@property(assign, nonatomic) CMTime lastVideoSampleTime; -@property(assign, nonatomic) CMTime lastAudioSampleTime; -@property(assign, nonatomic) CMTime videoTimeOffset; -@property(assign, nonatomic) CMTime audioTimeOffset; -// Format used for video and image streaming. -@property(assign, nonatomic) FourCharCode videoFormat; -@property(nonatomic) CMMotionManager *motionManager; -@property AVAssetWriterInputPixelBufferAdaptor *videoAdaptor; -@end - -@implementation FLTCam { - // All FLTCam's state access and capture session related operations should be on run on this - // queue. - dispatch_queue_t _captureSessionQueue; - UIDeviceOrientation _deviceOrientation; -} -NSString *const errorMethod = @"error"; - -- (instancetype)initWithCameraName:(NSString *)cameraName - resolutionPreset:(NSString *)resolutionPreset - enableAudio:(BOOL)enableAudio - orientation:(UIDeviceOrientation)orientation - captureSessionQueue:(dispatch_queue_t)captureSessionQueue - error:(NSError **)error { - self = [super init]; - NSAssert(self, @"super init cannot be nil"); - @try { - _resolutionPreset = FLTGetFLTResolutionPresetForString(resolutionPreset); - } @catch (NSError *e) { - *error = e; - } - _enableAudio = enableAudio; - _captureSessionQueue = captureSessionQueue; - _captureSession = [[AVCaptureSession alloc] init]; - _captureDevice = [AVCaptureDevice deviceWithUniqueID:cameraName]; - _flashMode = _captureDevice.hasFlash ? FLTFlashModeAuto : FLTFlashModeOff; - _exposureMode = FLTExposureModeAuto; - _focusMode = FLTFocusModeAuto; - _lockedCaptureOrientation = UIDeviceOrientationUnknown; - _deviceOrientation = orientation; - _videoFormat = kCVPixelFormatType_32BGRA; - - NSError *localError = nil; - _captureVideoInput = [AVCaptureDeviceInput deviceInputWithDevice:_captureDevice - error:&localError]; - - if (localError) { - *error = localError; - return nil; - } - - _captureVideoOutput = [AVCaptureVideoDataOutput new]; - _captureVideoOutput.videoSettings = - @{(NSString *)kCVPixelBufferPixelFormatTypeKey : @(_videoFormat)}; - [_captureVideoOutput setAlwaysDiscardsLateVideoFrames:YES]; - [_captureVideoOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()]; - - AVCaptureConnection *connection = - [AVCaptureConnection connectionWithInputPorts:_captureVideoInput.ports - output:_captureVideoOutput]; - - if ([_captureDevice position] == AVCaptureDevicePositionFront) { - connection.videoMirrored = YES; - } - - [_captureSession addInputWithNoConnections:_captureVideoInput]; - [_captureSession addOutputWithNoConnections:_captureVideoOutput]; - [_captureSession addConnection:connection]; - - if (@available(iOS 10.0, *)) { - _capturePhotoOutput = [AVCapturePhotoOutput new]; - [_capturePhotoOutput setHighResolutionCaptureEnabled:YES]; - [_captureSession addOutput:_capturePhotoOutput]; - } - _motionManager = [[CMMotionManager alloc] init]; - [_motionManager startAccelerometerUpdates]; - - [self setCaptureSessionPreset:_resolutionPreset]; - [self updateOrientation]; - - return self; -} - -- (void)start { - [_captureSession startRunning]; -} - -- (void)stop { - [_captureSession stopRunning]; -} - -- (void)setVideoFormat:(OSType)videoFormat { - _videoFormat = videoFormat; - _captureVideoOutput.videoSettings = - @{(NSString *)kCVPixelBufferPixelFormatTypeKey : @(videoFormat)}; -} - -- (void)setDeviceOrientation:(UIDeviceOrientation)orientation { - if (_deviceOrientation == orientation) { - return; - } - - _deviceOrientation = orientation; - [self updateOrientation]; -} - -- (void)updateOrientation { - if (_isRecording) { - return; - } - - UIDeviceOrientation orientation = (_lockedCaptureOrientation != UIDeviceOrientationUnknown) - ? _lockedCaptureOrientation - : _deviceOrientation; - - [self updateOrientation:orientation forCaptureOutput:_capturePhotoOutput]; - [self updateOrientation:orientation forCaptureOutput:_captureVideoOutput]; -} - -- (void)updateOrientation:(UIDeviceOrientation)orientation - forCaptureOutput:(AVCaptureOutput *)captureOutput { - if (!captureOutput) { - return; - } - - AVCaptureConnection *connection = [captureOutput connectionWithMediaType:AVMediaTypeVideo]; - if (connection && connection.isVideoOrientationSupported) { - connection.videoOrientation = [self getVideoOrientationForDeviceOrientation:orientation]; - } -} - -- (void)captureToFile:(FLTThreadSafeFlutterResult *)result API_AVAILABLE(ios(10)) { - AVCapturePhotoSettings *settings = [AVCapturePhotoSettings photoSettings]; - if (_resolutionPreset == FLTResolutionPresetMax) { - [settings setHighResolutionPhotoEnabled:YES]; - } - - AVCaptureFlashMode avFlashMode = FLTGetAVCaptureFlashModeForFLTFlashMode(_flashMode); - if (avFlashMode != -1) { - [settings setFlashMode:avFlashMode]; - } - NSError *error; - NSString *path = [self getTemporaryFilePathWithExtension:@"jpg" - subfolder:@"pictures" - prefix:@"CAP_" - error:error]; - if (error) { - [result sendError:error]; - return; - } - - [_capturePhotoOutput capturePhotoWithSettings:settings - delegate:[[FLTSavePhotoDelegate alloc] initWithPath:path - result:result]]; -} - -- (AVCaptureVideoOrientation)getVideoOrientationForDeviceOrientation: - (UIDeviceOrientation)deviceOrientation { - if (deviceOrientation == UIDeviceOrientationPortrait) { - return AVCaptureVideoOrientationPortrait; - } else if (deviceOrientation == UIDeviceOrientationLandscapeLeft) { - // Note: device orientation is flipped compared to video orientation. When UIDeviceOrientation - // is landscape left the video orientation should be landscape right. - return AVCaptureVideoOrientationLandscapeRight; - } else if (deviceOrientation == UIDeviceOrientationLandscapeRight) { - // Note: device orientation is flipped compared to video orientation. When UIDeviceOrientation - // is landscape right the video orientation should be landscape left. - return AVCaptureVideoOrientationLandscapeLeft; - } else if (deviceOrientation == UIDeviceOrientationPortraitUpsideDown) { - return AVCaptureVideoOrientationPortraitUpsideDown; - } else { - return AVCaptureVideoOrientationPortrait; - } -} - -- (NSString *)getTemporaryFilePathWithExtension:(NSString *)extension - subfolder:(NSString *)subfolder - prefix:(NSString *)prefix - error:(NSError *)error { - NSString *docDir = - NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; - NSString *fileDir = - [[docDir stringByAppendingPathComponent:@"camera"] stringByAppendingPathComponent:subfolder]; - NSString *fileName = [prefix stringByAppendingString:[[NSUUID UUID] UUIDString]]; - NSString *file = - [[fileDir stringByAppendingPathComponent:fileName] stringByAppendingPathExtension:extension]; - - NSFileManager *fm = [NSFileManager defaultManager]; - if (![fm fileExistsAtPath:fileDir]) { - [[NSFileManager defaultManager] createDirectoryAtPath:fileDir - withIntermediateDirectories:true - attributes:nil - error:&error]; - if (error) { - return nil; - } - } - - return file; -} - -- (void)setCaptureSessionPreset:(FLTResolutionPreset)resolutionPreset { - switch (resolutionPreset) { - case FLTResolutionPresetMax: - case FLTResolutionPresetUltraHigh: - if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset3840x2160]) { - _captureSession.sessionPreset = AVCaptureSessionPreset3840x2160; - _previewSize = CGSizeMake(3840, 2160); - break; - } - if ([_captureSession canSetSessionPreset:AVCaptureSessionPresetHigh]) { - _captureSession.sessionPreset = AVCaptureSessionPresetHigh; - _previewSize = - CGSizeMake(_captureDevice.activeFormat.highResolutionStillImageDimensions.width, - _captureDevice.activeFormat.highResolutionStillImageDimensions.height); - break; - } - case FLTResolutionPresetVeryHigh: - if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1920x1080]) { - _captureSession.sessionPreset = AVCaptureSessionPreset1920x1080; - _previewSize = CGSizeMake(1920, 1080); - break; - } - case FLTResolutionPresetHigh: - if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) { - _captureSession.sessionPreset = AVCaptureSessionPreset1280x720; - _previewSize = CGSizeMake(1280, 720); - break; - } - case FLTResolutionPresetMedium: - if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset640x480]) { - _captureSession.sessionPreset = AVCaptureSessionPreset640x480; - _previewSize = CGSizeMake(640, 480); - break; - } - case FLTResolutionPresetLow: - if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset352x288]) { - _captureSession.sessionPreset = AVCaptureSessionPreset352x288; - _previewSize = CGSizeMake(352, 288); - break; - } - default: - if ([_captureSession canSetSessionPreset:AVCaptureSessionPresetLow]) { - _captureSession.sessionPreset = AVCaptureSessionPresetLow; - _previewSize = CGSizeMake(352, 288); - } else { - NSError *error = - [NSError errorWithDomain:NSCocoaErrorDomain - code:NSURLErrorUnknown - userInfo:@{ - NSLocalizedDescriptionKey : - @"No capture session available for current capture session." - }]; - @throw error; - } - } -} - -- (void)captureOutput:(AVCaptureOutput *)output - didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer - fromConnection:(AVCaptureConnection *)connection { - if (output == _captureVideoOutput) { - CVPixelBufferRef newBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); - CFRetain(newBuffer); - CVPixelBufferRef old = _latestPixelBuffer; - while (!OSAtomicCompareAndSwapPtrBarrier(old, newBuffer, (void **)&_latestPixelBuffer)) { - old = _latestPixelBuffer; - } - if (old != nil) { - CFRelease(old); - } - if (_onFrameAvailable) { - _onFrameAvailable(); - } - } - if (!CMSampleBufferDataIsReady(sampleBuffer)) { - [_methodChannel invokeMethod:errorMethod - arguments:@"sample buffer is not ready. Skipping sample"]; - return; - } - if (_isStreamingImages) { - FlutterEventSink eventSink = _imageStreamHandler.eventSink; - if (eventSink) { - CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); - // Must lock base address before accessing the pixel data - CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly); - - size_t imageWidth = CVPixelBufferGetWidth(pixelBuffer); - size_t imageHeight = CVPixelBufferGetHeight(pixelBuffer); - - NSMutableArray *planes = [NSMutableArray array]; - - const Boolean isPlanar = CVPixelBufferIsPlanar(pixelBuffer); - size_t planeCount; - if (isPlanar) { - planeCount = CVPixelBufferGetPlaneCount(pixelBuffer); - } else { - planeCount = 1; - } - - for (int i = 0; i < planeCount; i++) { - void *planeAddress; - size_t bytesPerRow; - size_t height; - size_t width; - - if (isPlanar) { - planeAddress = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, i); - bytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, i); - height = CVPixelBufferGetHeightOfPlane(pixelBuffer, i); - width = CVPixelBufferGetWidthOfPlane(pixelBuffer, i); - } else { - planeAddress = CVPixelBufferGetBaseAddress(pixelBuffer); - bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer); - height = CVPixelBufferGetHeight(pixelBuffer); - width = CVPixelBufferGetWidth(pixelBuffer); - } - - NSNumber *length = @(bytesPerRow * height); - NSData *bytes = [NSData dataWithBytes:planeAddress length:length.unsignedIntegerValue]; - - NSMutableDictionary *planeBuffer = [NSMutableDictionary dictionary]; - planeBuffer[@"bytesPerRow"] = @(bytesPerRow); - planeBuffer[@"width"] = @(width); - planeBuffer[@"height"] = @(height); - planeBuffer[@"bytes"] = [FlutterStandardTypedData typedDataWithBytes:bytes]; - - [planes addObject:planeBuffer]; - } - // Before accessing pixel data, we should lock the base address, and unlock it afterwards. - // Done accessing the `pixelBuffer` at this point. - CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly); - - NSMutableDictionary *imageBuffer = [NSMutableDictionary dictionary]; - imageBuffer[@"width"] = [NSNumber numberWithUnsignedLong:imageWidth]; - imageBuffer[@"height"] = [NSNumber numberWithUnsignedLong:imageHeight]; - imageBuffer[@"format"] = @(_videoFormat); - imageBuffer[@"planes"] = planes; - imageBuffer[@"lensAperture"] = [NSNumber numberWithFloat:[_captureDevice lensAperture]]; - Float64 exposureDuration = CMTimeGetSeconds([_captureDevice exposureDuration]); - Float64 nsExposureDuration = 1000000000 * exposureDuration; - imageBuffer[@"sensorExposureTime"] = [NSNumber numberWithInt:nsExposureDuration]; - imageBuffer[@"sensorSensitivity"] = [NSNumber numberWithFloat:[_captureDevice ISO]]; - - dispatch_async(dispatch_get_main_queue(), ^{ - eventSink(imageBuffer); - }); - } - } - if (_isRecording && !_isRecordingPaused) { - if (_videoWriter.status == AVAssetWriterStatusFailed) { - [_methodChannel invokeMethod:errorMethod - arguments:[NSString stringWithFormat:@"%@", _videoWriter.error]]; - return; - } - - CFRetain(sampleBuffer); - CMTime currentSampleTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer); - - if (_videoWriter.status != AVAssetWriterStatusWriting) { - [_videoWriter startWriting]; - [_videoWriter startSessionAtSourceTime:currentSampleTime]; - } - - if (output == _captureVideoOutput) { - if (_videoIsDisconnected) { - _videoIsDisconnected = NO; - - if (_videoTimeOffset.value == 0) { - _videoTimeOffset = CMTimeSubtract(currentSampleTime, _lastVideoSampleTime); - } else { - CMTime offset = CMTimeSubtract(currentSampleTime, _lastVideoSampleTime); - _videoTimeOffset = CMTimeAdd(_videoTimeOffset, offset); - } - - return; - } - - _lastVideoSampleTime = currentSampleTime; - - CVPixelBufferRef nextBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); - CMTime nextSampleTime = CMTimeSubtract(_lastVideoSampleTime, _videoTimeOffset); - [_videoAdaptor appendPixelBuffer:nextBuffer withPresentationTime:nextSampleTime]; - } else { - CMTime dur = CMSampleBufferGetDuration(sampleBuffer); - - if (dur.value > 0) { - currentSampleTime = CMTimeAdd(currentSampleTime, dur); - } - - if (_audioIsDisconnected) { - _audioIsDisconnected = NO; - - if (_audioTimeOffset.value == 0) { - _audioTimeOffset = CMTimeSubtract(currentSampleTime, _lastAudioSampleTime); - } else { - CMTime offset = CMTimeSubtract(currentSampleTime, _lastAudioSampleTime); - _audioTimeOffset = CMTimeAdd(_audioTimeOffset, offset); - } - - return; - } - - _lastAudioSampleTime = currentSampleTime; - - if (_audioTimeOffset.value != 0) { - CFRelease(sampleBuffer); - sampleBuffer = [self adjustTime:sampleBuffer by:_audioTimeOffset]; - } - - [self newAudioSample:sampleBuffer]; - } - - CFRelease(sampleBuffer); - } -} - -- (CMSampleBufferRef)adjustTime:(CMSampleBufferRef)sample by:(CMTime)offset CF_RETURNS_RETAINED { - CMItemCount count; - CMSampleBufferGetSampleTimingInfoArray(sample, 0, nil, &count); - CMSampleTimingInfo *pInfo = malloc(sizeof(CMSampleTimingInfo) * count); - CMSampleBufferGetSampleTimingInfoArray(sample, count, pInfo, &count); - for (CMItemCount i = 0; i < count; i++) { - pInfo[i].decodeTimeStamp = CMTimeSubtract(pInfo[i].decodeTimeStamp, offset); - pInfo[i].presentationTimeStamp = CMTimeSubtract(pInfo[i].presentationTimeStamp, offset); - } - CMSampleBufferRef sout; - CMSampleBufferCreateCopyWithNewTiming(nil, sample, count, pInfo, &sout); - free(pInfo); - return sout; -} - -- (void)newVideoSample:(CMSampleBufferRef)sampleBuffer { - if (_videoWriter.status != AVAssetWriterStatusWriting) { - if (_videoWriter.status == AVAssetWriterStatusFailed) { - [_methodChannel invokeMethod:errorMethod - arguments:[NSString stringWithFormat:@"%@", _videoWriter.error]]; - } - return; - } - if (_videoWriterInput.readyForMoreMediaData) { - if (![_videoWriterInput appendSampleBuffer:sampleBuffer]) { - [_methodChannel - invokeMethod:errorMethod - arguments:[NSString stringWithFormat:@"%@", @"Unable to write to video input"]]; - } - } -} - -- (void)newAudioSample:(CMSampleBufferRef)sampleBuffer { - if (_videoWriter.status != AVAssetWriterStatusWriting) { - if (_videoWriter.status == AVAssetWriterStatusFailed) { - [_methodChannel invokeMethod:errorMethod - arguments:[NSString stringWithFormat:@"%@", _videoWriter.error]]; - } - return; - } - if (_audioWriterInput.readyForMoreMediaData) { - if (![_audioWriterInput appendSampleBuffer:sampleBuffer]) { - [_methodChannel - invokeMethod:errorMethod - arguments:[NSString stringWithFormat:@"%@", @"Unable to write to audio input"]]; - } - } -} - -- (void)close { - [_captureSession stopRunning]; - for (AVCaptureInput *input in [_captureSession inputs]) { - [_captureSession removeInput:input]; - } - for (AVCaptureOutput *output in [_captureSession outputs]) { - [_captureSession removeOutput:output]; - } -} - -- (void)dealloc { - if (_latestPixelBuffer) { - CFRelease(_latestPixelBuffer); - } - [_motionManager stopAccelerometerUpdates]; -} - -- (CVPixelBufferRef)copyPixelBuffer { - CVPixelBufferRef pixelBuffer = _latestPixelBuffer; - while (!OSAtomicCompareAndSwapPtrBarrier(pixelBuffer, nil, (void **)&_latestPixelBuffer)) { - pixelBuffer = _latestPixelBuffer; - } - - return pixelBuffer; -} - -- (void)startVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result { - if (!_isRecording) { - NSError *error; - _videoRecordingPath = [self getTemporaryFilePathWithExtension:@"mp4" - subfolder:@"videos" - prefix:@"REC_" - error:error]; - if (error) { - [result sendError:error]; - return; - } - if (![self setupWriterForPath:_videoRecordingPath]) { - [result sendErrorWithCode:@"IOError" message:@"Setup Writer Failed" details:nil]; - return; - } - _isRecording = YES; - _isRecordingPaused = NO; - _videoTimeOffset = CMTimeMake(0, 1); - _audioTimeOffset = CMTimeMake(0, 1); - _videoIsDisconnected = NO; - _audioIsDisconnected = NO; - [result sendSuccess]; - } else { - [result sendErrorWithCode:@"Error" message:@"Video is already recording" details:nil]; - } -} - -- (void)stopVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result { - if (_isRecording) { - _isRecording = NO; - - if (_videoWriter.status != AVAssetWriterStatusUnknown) { - [_videoWriter finishWritingWithCompletionHandler:^{ - if (self->_videoWriter.status == AVAssetWriterStatusCompleted) { - [self updateOrientation]; - [result sendSuccessWithData:self->_videoRecordingPath]; - self->_videoRecordingPath = nil; - } else { - [result sendErrorWithCode:@"IOError" - message:@"AVAssetWriter could not finish writing!" - details:nil]; - } - }]; - } - } else { - NSError *error = - [NSError errorWithDomain:NSCocoaErrorDomain - code:NSURLErrorResourceUnavailable - userInfo:@{NSLocalizedDescriptionKey : @"Video is not recording!"}]; - [result sendError:error]; - } -} - -- (void)pauseVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result { - _isRecordingPaused = YES; - _videoIsDisconnected = YES; - _audioIsDisconnected = YES; - [result sendSuccess]; -} - -- (void)resumeVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result { - _isRecordingPaused = NO; - [result sendSuccess]; -} - -- (void)lockCaptureOrientationWithResult:(FLTThreadSafeFlutterResult *)result - orientation:(NSString *)orientationStr { - UIDeviceOrientation orientation; - @try { - orientation = FLTGetUIDeviceOrientationForString(orientationStr); - } @catch (NSError *e) { - [result sendError:e]; - return; - } - - if (_lockedCaptureOrientation != orientation) { - _lockedCaptureOrientation = orientation; - [self updateOrientation]; - } - - [result sendSuccess]; -} - -- (void)unlockCaptureOrientationWithResult:(FLTThreadSafeFlutterResult *)result { - _lockedCaptureOrientation = UIDeviceOrientationUnknown; - [self updateOrientation]; - [result sendSuccess]; -} - -- (void)setFlashModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSString *)modeStr { - FLTFlashMode mode; - @try { - mode = FLTGetFLTFlashModeForString(modeStr); - } @catch (NSError *e) { - [result sendError:e]; - return; - } - if (mode == FLTFlashModeTorch) { - if (!_captureDevice.hasTorch) { - [result sendErrorWithCode:@"setFlashModeFailed" - message:@"Device does not support torch mode" - details:nil]; - return; - } - if (!_captureDevice.isTorchAvailable) { - [result sendErrorWithCode:@"setFlashModeFailed" - message:@"Torch mode is currently not available" - details:nil]; - return; - } - if (_captureDevice.torchMode != AVCaptureTorchModeOn) { - [_captureDevice lockForConfiguration:nil]; - [_captureDevice setTorchMode:AVCaptureTorchModeOn]; - [_captureDevice unlockForConfiguration]; - } - } else { - if (!_captureDevice.hasFlash) { - [result sendErrorWithCode:@"setFlashModeFailed" - message:@"Device does not have flash capabilities" - details:nil]; - return; - } - AVCaptureFlashMode avFlashMode = FLTGetAVCaptureFlashModeForFLTFlashMode(mode); - if (![_capturePhotoOutput.supportedFlashModes - containsObject:[NSNumber numberWithInt:((int)avFlashMode)]]) { - [result sendErrorWithCode:@"setFlashModeFailed" - message:@"Device does not support this specific flash mode" - details:nil]; - return; - } - if (_captureDevice.torchMode != AVCaptureTorchModeOff) { - [_captureDevice lockForConfiguration:nil]; - [_captureDevice setTorchMode:AVCaptureTorchModeOff]; - [_captureDevice unlockForConfiguration]; - } - } - _flashMode = mode; - [result sendSuccess]; -} - -- (void)setExposureModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSString *)modeStr { - FLTExposureMode mode; - @try { - mode = FLTGetFLTExposureModeForString(modeStr); - } @catch (NSError *e) { - [result sendError:e]; - return; - } - _exposureMode = mode; - [self applyExposureMode]; - [result sendSuccess]; -} - -- (void)applyExposureMode { - [_captureDevice lockForConfiguration:nil]; - switch (_exposureMode) { - case FLTExposureModeLocked: - [_captureDevice setExposureMode:AVCaptureExposureModeAutoExpose]; - break; - case FLTExposureModeAuto: - if ([_captureDevice isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) { - [_captureDevice setExposureMode:AVCaptureExposureModeContinuousAutoExposure]; - } else { - [_captureDevice setExposureMode:AVCaptureExposureModeAutoExpose]; - } - break; - } - [_captureDevice unlockForConfiguration]; -} - -- (void)setFocusModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSString *)modeStr { - FLTFocusMode mode; - @try { - mode = FLTGetFLTFocusModeForString(modeStr); - } @catch (NSError *e) { - [result sendError:e]; - return; - } - _focusMode = mode; - [self applyFocusMode]; - [result sendSuccess]; -} - -- (void)applyFocusMode { - [self applyFocusMode:_focusMode onDevice:_captureDevice]; -} - -/** - * Applies FocusMode on the AVCaptureDevice. - * - * If the @c focusMode is set to FocusModeAuto the AVCaptureDevice is configured to use - * AVCaptureFocusModeContinuousModeAutoFocus when supported, otherwise it is set to - * AVCaptureFocusModeAutoFocus. If neither AVCaptureFocusModeContinuousModeAutoFocus nor - * AVCaptureFocusModeAutoFocus are supported focus mode will not be set. - * If @c focusMode is set to FocusModeLocked the AVCaptureDevice is configured to use - * AVCaptureFocusModeAutoFocus. If AVCaptureFocusModeAutoFocus is not supported focus mode will not - * be set. - * - * @param focusMode The focus mode that should be applied to the @captureDevice instance. - * @param captureDevice The AVCaptureDevice to which the @focusMode will be applied. - */ -- (void)applyFocusMode:(FLTFocusMode)focusMode onDevice:(AVCaptureDevice *)captureDevice { - [captureDevice lockForConfiguration:nil]; - switch (focusMode) { - case FLTFocusModeLocked: - if ([captureDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]) { - [captureDevice setFocusMode:AVCaptureFocusModeAutoFocus]; - } - break; - case FLTFocusModeAuto: - if ([captureDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) { - [captureDevice setFocusMode:AVCaptureFocusModeContinuousAutoFocus]; - } else if ([captureDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]) { - [captureDevice setFocusMode:AVCaptureFocusModeAutoFocus]; - } - break; - } - [captureDevice unlockForConfiguration]; -} - -- (void)pausePreviewWithResult:(FLTThreadSafeFlutterResult *)result { - _isPreviewPaused = true; - [result sendSuccess]; -} - -- (void)resumePreviewWithResult:(FLTThreadSafeFlutterResult *)result { - _isPreviewPaused = false; - [result sendSuccess]; -} - -- (CGPoint)getCGPointForCoordsWithOrientation:(UIDeviceOrientation)orientation - x:(double)x - y:(double)y { - double oldX = x, oldY = y; - switch (orientation) { - case UIDeviceOrientationPortrait: // 90 ccw - y = 1 - oldX; - x = oldY; - break; - case UIDeviceOrientationPortraitUpsideDown: // 90 cw - x = 1 - oldY; - y = oldX; - break; - case UIDeviceOrientationLandscapeRight: // 180 - x = 1 - x; - y = 1 - y; - break; - case UIDeviceOrientationLandscapeLeft: - default: - // No rotation required - break; - } - return CGPointMake(x, y); -} - -- (void)setExposurePointWithResult:(FLTThreadSafeFlutterResult *)result x:(double)x y:(double)y { - if (!_captureDevice.isExposurePointOfInterestSupported) { - [result sendErrorWithCode:@"setExposurePointFailed" - message:@"Device does not have exposure point capabilities" - details:nil]; - return; - } - UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation]; - [_captureDevice lockForConfiguration:nil]; - [_captureDevice setExposurePointOfInterest:[self getCGPointForCoordsWithOrientation:orientation - x:x - y:y]]; - [_captureDevice unlockForConfiguration]; - // Retrigger auto exposure - [self applyExposureMode]; - [result sendSuccess]; -} - -- (void)setFocusPointWithResult:(FLTThreadSafeFlutterResult *)result x:(double)x y:(double)y { - if (!_captureDevice.isFocusPointOfInterestSupported) { - [result sendErrorWithCode:@"setFocusPointFailed" - message:@"Device does not have focus point capabilities" - details:nil]; - return; - } - UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation]; - [_captureDevice lockForConfiguration:nil]; - - [_captureDevice setFocusPointOfInterest:[self getCGPointForCoordsWithOrientation:orientation - x:x - y:y]]; - [_captureDevice unlockForConfiguration]; - // Retrigger auto focus - [self applyFocusMode]; - [result sendSuccess]; -} - -- (void)setExposureOffsetWithResult:(FLTThreadSafeFlutterResult *)result offset:(double)offset { - [_captureDevice lockForConfiguration:nil]; - [_captureDevice setExposureTargetBias:offset completionHandler:nil]; - [_captureDevice unlockForConfiguration]; - [result sendSuccessWithData:@(offset)]; -} - -- (void)startImageStreamWithMessenger:(NSObject *)messenger { - if (!_isStreamingImages) { - FlutterEventChannel *eventChannel = - [FlutterEventChannel eventChannelWithName:@"plugins.flutter.io/camera/imageStream" - binaryMessenger:messenger]; - FLTThreadSafeEventChannel *threadSafeEventChannel = - [[FLTThreadSafeEventChannel alloc] initWithEventChannel:eventChannel]; - - _imageStreamHandler = - [[FLTImageStreamHandler alloc] initWithCaptureSessionQueue:_captureSessionQueue]; - [threadSafeEventChannel setStreamHandler:_imageStreamHandler - completion:^{ - dispatch_async(self->_captureSessionQueue, ^{ - self.isStreamingImages = YES; - }); - }]; - } else { - [_methodChannel invokeMethod:errorMethod - arguments:@"Images from camera are already streaming!"]; - } -} - -- (void)stopImageStream { - if (_isStreamingImages) { - _isStreamingImages = NO; - _imageStreamHandler = nil; - } else { - [_methodChannel invokeMethod:errorMethod arguments:@"Images from camera are not streaming!"]; - } -} - -- (void)getMaxZoomLevelWithResult:(FLTThreadSafeFlutterResult *)result { - CGFloat maxZoomFactor = [self getMaxAvailableZoomFactor]; - - [result sendSuccessWithData:[NSNumber numberWithFloat:maxZoomFactor]]; -} - -- (void)getMinZoomLevelWithResult:(FLTThreadSafeFlutterResult *)result { - CGFloat minZoomFactor = [self getMinAvailableZoomFactor]; - [result sendSuccessWithData:[NSNumber numberWithFloat:minZoomFactor]]; -} - -- (void)setZoomLevel:(CGFloat)zoom Result:(FLTThreadSafeFlutterResult *)result { - CGFloat maxAvailableZoomFactor = [self getMaxAvailableZoomFactor]; - CGFloat minAvailableZoomFactor = [self getMinAvailableZoomFactor]; - - if (maxAvailableZoomFactor < zoom || minAvailableZoomFactor > zoom) { - NSString *errorMessage = [NSString - stringWithFormat:@"Zoom level out of bounds (zoom level should be between %f and %f).", - minAvailableZoomFactor, maxAvailableZoomFactor]; - - [result sendErrorWithCode:@"ZOOM_ERROR" message:errorMessage details:nil]; - return; - } - - NSError *error = nil; - if (![_captureDevice lockForConfiguration:&error]) { - [result sendError:error]; - return; - } - _captureDevice.videoZoomFactor = zoom; - [_captureDevice unlockForConfiguration]; - - [result sendSuccess]; -} - -- (CGFloat)getMinAvailableZoomFactor { - if (@available(iOS 11.0, *)) { - return _captureDevice.minAvailableVideoZoomFactor; - } else { - return 1.0; - } -} - -- (CGFloat)getMaxAvailableZoomFactor { - if (@available(iOS 11.0, *)) { - return _captureDevice.maxAvailableVideoZoomFactor; - } else { - return _captureDevice.activeFormat.videoMaxZoomFactor; - } -} - -- (BOOL)setupWriterForPath:(NSString *)path { - NSError *error = nil; - NSURL *outputURL; - if (path != nil) { - outputURL = [NSURL fileURLWithPath:path]; - } else { - return NO; - } - if (_enableAudio && !_isAudioSetup) { - [self setUpCaptureSessionForAudio]; - } - - _videoWriter = [[AVAssetWriter alloc] initWithURL:outputURL - fileType:AVFileTypeMPEG4 - error:&error]; - NSParameterAssert(_videoWriter); - if (error) { - [_methodChannel invokeMethod:errorMethod arguments:error.description]; - return NO; - } - - NSDictionary *videoSettings = [_captureVideoOutput - recommendedVideoSettingsForAssetWriterWithOutputFileType:AVFileTypeMPEG4]; - _videoWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo - outputSettings:videoSettings]; - - _videoAdaptor = [AVAssetWriterInputPixelBufferAdaptor - assetWriterInputPixelBufferAdaptorWithAssetWriterInput:_videoWriterInput - sourcePixelBufferAttributes:@{ - (NSString *)kCVPixelBufferPixelFormatTypeKey : @(_videoFormat) - }]; - - NSParameterAssert(_videoWriterInput); - - _videoWriterInput.expectsMediaDataInRealTime = YES; - - // Add the audio input - if (_enableAudio) { - AudioChannelLayout acl; - bzero(&acl, sizeof(acl)); - acl.mChannelLayoutTag = kAudioChannelLayoutTag_Mono; - NSDictionary *audioOutputSettings = nil; - // Both type of audio inputs causes output video file to be corrupted. - audioOutputSettings = @{ - AVFormatIDKey : [NSNumber numberWithInt:kAudioFormatMPEG4AAC], - AVSampleRateKey : [NSNumber numberWithFloat:44100.0], - AVNumberOfChannelsKey : [NSNumber numberWithInt:1], - AVChannelLayoutKey : [NSData dataWithBytes:&acl length:sizeof(acl)], - }; - _audioWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio - outputSettings:audioOutputSettings]; - _audioWriterInput.expectsMediaDataInRealTime = YES; - - [_videoWriter addInput:_audioWriterInput]; - [_audioOutput setSampleBufferDelegate:self queue:_captureSessionQueue]; - } - - if (_flashMode == FLTFlashModeTorch) { - [self.captureDevice lockForConfiguration:nil]; - [self.captureDevice setTorchMode:AVCaptureTorchModeOn]; - [self.captureDevice unlockForConfiguration]; - } - - [_videoWriter addInput:_videoWriterInput]; - - [_captureVideoOutput setSampleBufferDelegate:self queue:_captureSessionQueue]; - - return YES; -} - -- (void)setUpCaptureSessionForAudio { - NSError *error = nil; - // Create a device input with the device and add it to the session. - // Setup the audio input. - AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio]; - AVCaptureDeviceInput *audioInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice - error:&error]; - if (error) { - [_methodChannel invokeMethod:errorMethod arguments:error.description]; - } - // Setup the audio output. - _audioOutput = [[AVCaptureAudioDataOutput alloc] init]; - - if ([_captureSession canAddInput:audioInput]) { - [_captureSession addInput:audioInput]; - - if ([_captureSession canAddOutput:_audioOutput]) { - [_captureSession addOutput:_audioOutput]; - _isAudioSetup = YES; - } else { - [_methodChannel invokeMethod:errorMethod - arguments:@"Unable to add Audio input/output to session capture"]; - _isAudioSetup = NO; - } - } -} -@end - @interface CameraPlugin () @property(readonly, nonatomic) FLTThreadSafeTextureRegistry *registry; @property(readonly, nonatomic) NSObject *messenger; diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.modulemap b/packages/camera/camera/ios/Classes/CameraPlugin.modulemap index 059374186076..1ad34373fbdb 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin.modulemap +++ b/packages/camera/camera/ios/Classes/CameraPlugin.modulemap @@ -7,5 +7,6 @@ framework module camera { explicit module Test { header "CameraPlugin_Test.h" header "CameraProperties.h" + header "FLTCam.h" } } diff --git a/packages/camera/camera/ios/Classes/FLTCam.h b/packages/camera/camera/ios/Classes/FLTCam.h new file mode 100644 index 000000000000..417a1d74db21 --- /dev/null +++ b/packages/camera/camera/ios/Classes/FLTCam.h @@ -0,0 +1,86 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import AVFoundation; +@import Foundation; +@import Flutter; + +#import "CameraProperties.h" +#import "FLTThreadSafeEventChannel.h" +#import "FLTThreadSafeFlutterResult.h" +#import "FLTThreadSafeMethodChannel.h" +#import "FLTThreadSafeTextureRegistry.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * A class that manages camera's state and performs camera operations. + */ +@interface FLTCam : NSObject + +@property(readonly, nonatomic) AVCaptureDevice *captureDevice; +@property(readonly, nonatomic) CGSize previewSize; +@property(assign, nonatomic) BOOL isPreviewPaused; +@property(nonatomic, copy) void (^onFrameAvailable)(void); +@property(nonatomic) FLTThreadSafeMethodChannel *methodChannel; +@property(assign, nonatomic) FLTResolutionPreset resolutionPreset; +@property(assign, nonatomic) FLTExposureMode exposureMode; +@property(assign, nonatomic) FLTFocusMode focusMode; +@property(assign, nonatomic) FLTFlashMode flashMode; +// Format used for video and image streaming. +@property(assign, nonatomic) FourCharCode videoFormat; + +- (instancetype)initWithCameraName:(NSString *)cameraName + resolutionPreset:(NSString *)resolutionPreset + enableAudio:(BOOL)enableAudio + orientation:(UIDeviceOrientation)orientation + captureSessionQueue:(dispatch_queue_t)captureSessionQueue + error:(NSError **)error; +- (void)start; +- (void)stop; +- (void)setDeviceOrientation:(UIDeviceOrientation)orientation; +- (void)captureToFile:(FLTThreadSafeFlutterResult *)result API_AVAILABLE(ios(10)); +- (void)close; +- (void)startVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result; +- (void)stopVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result; +- (void)pauseVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result; +- (void)resumeVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result; +- (void)lockCaptureOrientationWithResult:(FLTThreadSafeFlutterResult *)result + orientation:(NSString *)orientationStr; +- (void)unlockCaptureOrientationWithResult:(FLTThreadSafeFlutterResult *)result; +- (void)setFlashModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSString *)modeStr; +- (void)setExposureModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSString *)modeStr; +- (void)setFocusModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSString *)modeStr; +- (void)applyFocusMode; + +/** + * Applies FocusMode on the AVCaptureDevice. + * + * If the @c focusMode is set to FocusModeAuto the AVCaptureDevice is configured to use + * AVCaptureFocusModeContinuousModeAutoFocus when supported, otherwise it is set to + * AVCaptureFocusModeAutoFocus. If neither AVCaptureFocusModeContinuousModeAutoFocus nor + * AVCaptureFocusModeAutoFocus are supported focus mode will not be set. + * If @c focusMode is set to FocusModeLocked the AVCaptureDevice is configured to use + * AVCaptureFocusModeAutoFocus. If AVCaptureFocusModeAutoFocus is not supported focus mode will not + * be set. + * + * @param focusMode The focus mode that should be applied to the @captureDevice instance. + * @param captureDevice The AVCaptureDevice to which the @focusMode will be applied. + */ +- (void)applyFocusMode:(FLTFocusMode)focusMode onDevice:(AVCaptureDevice *)captureDevice; +- (void)pausePreviewWithResult:(FLTThreadSafeFlutterResult *)result; +- (void)resumePreviewWithResult:(FLTThreadSafeFlutterResult *)result; +- (void)setExposurePointWithResult:(FLTThreadSafeFlutterResult *)result x:(double)x y:(double)y; +- (void)setFocusPointWithResult:(FLTThreadSafeFlutterResult *)result x:(double)x y:(double)y; +- (void)setExposureOffsetWithResult:(FLTThreadSafeFlutterResult *)result offset:(double)offset; +- (void)startImageStreamWithMessenger:(NSObject *)messenger; +- (void)stopImageStream; +- (void)getMaxZoomLevelWithResult:(FLTThreadSafeFlutterResult *)result; +- (void)getMinZoomLevelWithResult:(FLTThreadSafeFlutterResult *)result; +- (void)setZoomLevel:(CGFloat)zoom Result:(FLTThreadSafeFlutterResult *)result; +- (void)setUpCaptureSessionForAudio; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera/ios/Classes/FLTCam.m b/packages/camera/camera/ios/Classes/FLTCam.m new file mode 100644 index 000000000000..11be4ad5a486 --- /dev/null +++ b/packages/camera/camera/ios/Classes/FLTCam.m @@ -0,0 +1,1087 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FLTCam.h" + +@import CoreMotion; +#import + +@interface FLTImageStreamHandler : NSObject +// The queue on which `eventSink` property should be accessed +@property(nonatomic, strong) dispatch_queue_t captureSessionQueue; +// `eventSink` property should be accessed on `captureSessionQueue`. +// The block itself should be invoked on the main queue. +@property FlutterEventSink eventSink; +@end + +@implementation FLTImageStreamHandler + +- (instancetype)initWithCaptureSessionQueue:(dispatch_queue_t)captureSessionQueue { + self = [super init]; + NSAssert(self, @"super init cannot be nil"); + _captureSessionQueue = captureSessionQueue; + return self; +} + +- (FlutterError *_Nullable)onCancelWithArguments:(id _Nullable)arguments { + dispatch_async(self.captureSessionQueue, ^{ + self.eventSink = nil; + }); + return nil; +} + +- (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments + eventSink:(nonnull FlutterEventSink)events { + dispatch_async(self.captureSessionQueue, ^{ + self.eventSink = events; + }); + return nil; +} +@end + +@interface FLTSavePhotoDelegate : NSObject +@property(readonly, nonatomic) NSString *path; +@property(readonly, nonatomic) FLTThreadSafeFlutterResult *result; +@end + +@implementation FLTSavePhotoDelegate { + /// Used to keep the delegate alive until didFinishProcessingPhotoSampleBuffer. + FLTSavePhotoDelegate *selfReference; +} + +- initWithPath:(NSString *)path result:(FLTThreadSafeFlutterResult *)result { + self = [super init]; + NSAssert(self, @"super init cannot be nil"); + _path = path; + selfReference = self; + _result = result; + return self; +} + +- (void)captureOutput:(AVCapturePhotoOutput *)output + didFinishProcessingPhotoSampleBuffer:(CMSampleBufferRef)photoSampleBuffer + previewPhotoSampleBuffer:(CMSampleBufferRef)previewPhotoSampleBuffer + resolvedSettings:(AVCaptureResolvedPhotoSettings *)resolvedSettings + bracketSettings:(AVCaptureBracketedStillImageSettings *)bracketSettings + error:(NSError *)error API_AVAILABLE(ios(10)) { + selfReference = nil; + if (error) { + [_result sendError:error]; + return; + } + + NSData *data = [AVCapturePhotoOutput + JPEGPhotoDataRepresentationForJPEGSampleBuffer:photoSampleBuffer + previewPhotoSampleBuffer:previewPhotoSampleBuffer]; + + // TODO(sigurdm): Consider writing file asynchronously. + bool success = [data writeToFile:_path atomically:YES]; + + if (!success) { + [_result sendErrorWithCode:@"IOError" message:@"Unable to write file" details:nil]; + return; + } + [_result sendSuccessWithData:_path]; +} + +- (void)captureOutput:(AVCapturePhotoOutput *)output + didFinishProcessingPhoto:(AVCapturePhoto *)photo + error:(NSError *)error API_AVAILABLE(ios(11.0)) { + selfReference = nil; + if (error) { + [_result sendError:error]; + return; + } + + NSData *photoData = [photo fileDataRepresentation]; + + bool success = [photoData writeToFile:_path atomically:YES]; + if (!success) { + [_result sendErrorWithCode:@"IOError" message:@"Unable to write file" details:nil]; + return; + } + [_result sendSuccessWithData:_path]; +} +@end + +@interface FLTCam () + +@property(readonly, nonatomic) int64_t textureId; +@property BOOL enableAudio; +@property(nonatomic) FLTImageStreamHandler *imageStreamHandler; +@property(readonly, nonatomic) AVCaptureSession *captureSession; + +@property(readonly, nonatomic) AVCapturePhotoOutput *capturePhotoOutput API_AVAILABLE(ios(10)); +@property(readonly, nonatomic) AVCaptureVideoDataOutput *captureVideoOutput; +@property(readonly, nonatomic) AVCaptureInput *captureVideoInput; +@property(readonly) CVPixelBufferRef volatile latestPixelBuffer; +@property(readonly, nonatomic) CGSize captureSize; +@property(strong, nonatomic) AVAssetWriter *videoWriter; +@property(strong, nonatomic) AVAssetWriterInput *videoWriterInput; +@property(strong, nonatomic) AVAssetWriterInput *audioWriterInput; +@property(strong, nonatomic) AVAssetWriterInputPixelBufferAdaptor *assetWriterPixelBufferAdaptor; +@property(strong, nonatomic) AVCaptureVideoDataOutput *videoOutput; +@property(strong, nonatomic) AVCaptureAudioDataOutput *audioOutput; +@property(strong, nonatomic) NSString *videoRecordingPath; +@property(assign, nonatomic) BOOL isRecording; +@property(assign, nonatomic) BOOL isRecordingPaused; +@property(assign, nonatomic) BOOL videoIsDisconnected; +@property(assign, nonatomic) BOOL audioIsDisconnected; +@property(assign, nonatomic) BOOL isAudioSetup; +@property(assign, nonatomic) BOOL isStreamingImages; +@property(assign, nonatomic) UIDeviceOrientation lockedCaptureOrientation; +@property(assign, nonatomic) CMTime lastVideoSampleTime; +@property(assign, nonatomic) CMTime lastAudioSampleTime; +@property(assign, nonatomic) CMTime videoTimeOffset; +@property(assign, nonatomic) CMTime audioTimeOffset; +@property(nonatomic) CMMotionManager *motionManager; +@property AVAssetWriterInputPixelBufferAdaptor *videoAdaptor; +// All FLTCam's state access and capture session related operations should be on run on this queue. +@property(strong, nonatomic) dispatch_queue_t captureSessionQueue; +@property(assign, nonatomic) UIDeviceOrientation deviceOrientation; +@end + +@implementation FLTCam + +NSString *const errorMethod = @"error"; + +- (instancetype)initWithCameraName:(NSString *)cameraName + resolutionPreset:(NSString *)resolutionPreset + enableAudio:(BOOL)enableAudio + orientation:(UIDeviceOrientation)orientation + captureSessionQueue:(dispatch_queue_t)captureSessionQueue + error:(NSError **)error { + self = [super init]; + NSAssert(self, @"super init cannot be nil"); + @try { + _resolutionPreset = FLTGetFLTResolutionPresetForString(resolutionPreset); + } @catch (NSError *e) { + *error = e; + } + _enableAudio = enableAudio; + _captureSessionQueue = captureSessionQueue; + _captureSession = [[AVCaptureSession alloc] init]; + _captureDevice = [AVCaptureDevice deviceWithUniqueID:cameraName]; + _flashMode = _captureDevice.hasFlash ? FLTFlashModeAuto : FLTFlashModeOff; + _exposureMode = FLTExposureModeAuto; + _focusMode = FLTFocusModeAuto; + _lockedCaptureOrientation = UIDeviceOrientationUnknown; + _deviceOrientation = orientation; + _videoFormat = kCVPixelFormatType_32BGRA; + + NSError *localError = nil; + _captureVideoInput = [AVCaptureDeviceInput deviceInputWithDevice:_captureDevice + error:&localError]; + + if (localError) { + *error = localError; + return nil; + } + + _captureVideoOutput = [AVCaptureVideoDataOutput new]; + _captureVideoOutput.videoSettings = + @{(NSString *)kCVPixelBufferPixelFormatTypeKey : @(_videoFormat)}; + [_captureVideoOutput setAlwaysDiscardsLateVideoFrames:YES]; + [_captureVideoOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()]; + + AVCaptureConnection *connection = + [AVCaptureConnection connectionWithInputPorts:_captureVideoInput.ports + output:_captureVideoOutput]; + + if ([_captureDevice position] == AVCaptureDevicePositionFront) { + connection.videoMirrored = YES; + } + + [_captureSession addInputWithNoConnections:_captureVideoInput]; + [_captureSession addOutputWithNoConnections:_captureVideoOutput]; + [_captureSession addConnection:connection]; + + if (@available(iOS 10.0, *)) { + _capturePhotoOutput = [AVCapturePhotoOutput new]; + [_capturePhotoOutput setHighResolutionCaptureEnabled:YES]; + [_captureSession addOutput:_capturePhotoOutput]; + } + _motionManager = [[CMMotionManager alloc] init]; + [_motionManager startAccelerometerUpdates]; + + [self setCaptureSessionPreset:_resolutionPreset]; + [self updateOrientation]; + + return self; +} + +- (void)start { + [_captureSession startRunning]; +} + +- (void)stop { + [_captureSession stopRunning]; +} + +- (void)setVideoFormat:(OSType)videoFormat { + _videoFormat = videoFormat; + _captureVideoOutput.videoSettings = + @{(NSString *)kCVPixelBufferPixelFormatTypeKey : @(videoFormat)}; +} + +- (void)setDeviceOrientation:(UIDeviceOrientation)orientation { + if (_deviceOrientation == orientation) { + return; + } + + _deviceOrientation = orientation; + [self updateOrientation]; +} + +- (void)updateOrientation { + if (_isRecording) { + return; + } + + UIDeviceOrientation orientation = (_lockedCaptureOrientation != UIDeviceOrientationUnknown) + ? _lockedCaptureOrientation + : _deviceOrientation; + + [self updateOrientation:orientation forCaptureOutput:_capturePhotoOutput]; + [self updateOrientation:orientation forCaptureOutput:_captureVideoOutput]; +} + +- (void)updateOrientation:(UIDeviceOrientation)orientation + forCaptureOutput:(AVCaptureOutput *)captureOutput { + if (!captureOutput) { + return; + } + + AVCaptureConnection *connection = [captureOutput connectionWithMediaType:AVMediaTypeVideo]; + if (connection && connection.isVideoOrientationSupported) { + connection.videoOrientation = [self getVideoOrientationForDeviceOrientation:orientation]; + } +} + +- (void)captureToFile:(FLTThreadSafeFlutterResult *)result API_AVAILABLE(ios(10)) { + AVCapturePhotoSettings *settings = [AVCapturePhotoSettings photoSettings]; + if (_resolutionPreset == FLTResolutionPresetMax) { + [settings setHighResolutionPhotoEnabled:YES]; + } + + AVCaptureFlashMode avFlashMode = FLTGetAVCaptureFlashModeForFLTFlashMode(_flashMode); + if (avFlashMode != -1) { + [settings setFlashMode:avFlashMode]; + } + NSError *error; + NSString *path = [self getTemporaryFilePathWithExtension:@"jpg" + subfolder:@"pictures" + prefix:@"CAP_" + error:error]; + if (error) { + [result sendError:error]; + return; + } + + [_capturePhotoOutput capturePhotoWithSettings:settings + delegate:[[FLTSavePhotoDelegate alloc] initWithPath:path + result:result]]; +} + +- (AVCaptureVideoOrientation)getVideoOrientationForDeviceOrientation: + (UIDeviceOrientation)deviceOrientation { + if (deviceOrientation == UIDeviceOrientationPortrait) { + return AVCaptureVideoOrientationPortrait; + } else if (deviceOrientation == UIDeviceOrientationLandscapeLeft) { + // Note: device orientation is flipped compared to video orientation. When UIDeviceOrientation + // is landscape left the video orientation should be landscape right. + return AVCaptureVideoOrientationLandscapeRight; + } else if (deviceOrientation == UIDeviceOrientationLandscapeRight) { + // Note: device orientation is flipped compared to video orientation. When UIDeviceOrientation + // is landscape right the video orientation should be landscape left. + return AVCaptureVideoOrientationLandscapeLeft; + } else if (deviceOrientation == UIDeviceOrientationPortraitUpsideDown) { + return AVCaptureVideoOrientationPortraitUpsideDown; + } else { + return AVCaptureVideoOrientationPortrait; + } +} + +- (NSString *)getTemporaryFilePathWithExtension:(NSString *)extension + subfolder:(NSString *)subfolder + prefix:(NSString *)prefix + error:(NSError *)error { + NSString *docDir = + NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; + NSString *fileDir = + [[docDir stringByAppendingPathComponent:@"camera"] stringByAppendingPathComponent:subfolder]; + NSString *fileName = [prefix stringByAppendingString:[[NSUUID UUID] UUIDString]]; + NSString *file = + [[fileDir stringByAppendingPathComponent:fileName] stringByAppendingPathExtension:extension]; + + NSFileManager *fm = [NSFileManager defaultManager]; + if (![fm fileExistsAtPath:fileDir]) { + [[NSFileManager defaultManager] createDirectoryAtPath:fileDir + withIntermediateDirectories:true + attributes:nil + error:&error]; + if (error) { + return nil; + } + } + + return file; +} + +- (void)setCaptureSessionPreset:(FLTResolutionPreset)resolutionPreset { + switch (resolutionPreset) { + case FLTResolutionPresetMax: + case FLTResolutionPresetUltraHigh: + if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset3840x2160]) { + _captureSession.sessionPreset = AVCaptureSessionPreset3840x2160; + _previewSize = CGSizeMake(3840, 2160); + break; + } + if ([_captureSession canSetSessionPreset:AVCaptureSessionPresetHigh]) { + _captureSession.sessionPreset = AVCaptureSessionPresetHigh; + _previewSize = + CGSizeMake(_captureDevice.activeFormat.highResolutionStillImageDimensions.width, + _captureDevice.activeFormat.highResolutionStillImageDimensions.height); + break; + } + case FLTResolutionPresetVeryHigh: + if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1920x1080]) { + _captureSession.sessionPreset = AVCaptureSessionPreset1920x1080; + _previewSize = CGSizeMake(1920, 1080); + break; + } + case FLTResolutionPresetHigh: + if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) { + _captureSession.sessionPreset = AVCaptureSessionPreset1280x720; + _previewSize = CGSizeMake(1280, 720); + break; + } + case FLTResolutionPresetMedium: + if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset640x480]) { + _captureSession.sessionPreset = AVCaptureSessionPreset640x480; + _previewSize = CGSizeMake(640, 480); + break; + } + case FLTResolutionPresetLow: + if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset352x288]) { + _captureSession.sessionPreset = AVCaptureSessionPreset352x288; + _previewSize = CGSizeMake(352, 288); + break; + } + default: + if ([_captureSession canSetSessionPreset:AVCaptureSessionPresetLow]) { + _captureSession.sessionPreset = AVCaptureSessionPresetLow; + _previewSize = CGSizeMake(352, 288); + } else { + NSError *error = + [NSError errorWithDomain:NSCocoaErrorDomain + code:NSURLErrorUnknown + userInfo:@{ + NSLocalizedDescriptionKey : + @"No capture session available for current capture session." + }]; + @throw error; + } + } +} + +- (void)captureOutput:(AVCaptureOutput *)output + didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer + fromConnection:(AVCaptureConnection *)connection { + if (output == _captureVideoOutput) { + CVPixelBufferRef newBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); + CFRetain(newBuffer); + CVPixelBufferRef old = _latestPixelBuffer; + while (!OSAtomicCompareAndSwapPtrBarrier(old, newBuffer, (void **)&_latestPixelBuffer)) { + old = _latestPixelBuffer; + } + if (old != nil) { + CFRelease(old); + } + if (_onFrameAvailable) { + _onFrameAvailable(); + } + } + if (!CMSampleBufferDataIsReady(sampleBuffer)) { + [_methodChannel invokeMethod:errorMethod + arguments:@"sample buffer is not ready. Skipping sample"]; + return; + } + if (_isStreamingImages) { + FlutterEventSink eventSink = _imageStreamHandler.eventSink; + if (eventSink) { + CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); + // Must lock base address before accessing the pixel data + CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly); + + size_t imageWidth = CVPixelBufferGetWidth(pixelBuffer); + size_t imageHeight = CVPixelBufferGetHeight(pixelBuffer); + + NSMutableArray *planes = [NSMutableArray array]; + + const Boolean isPlanar = CVPixelBufferIsPlanar(pixelBuffer); + size_t planeCount; + if (isPlanar) { + planeCount = CVPixelBufferGetPlaneCount(pixelBuffer); + } else { + planeCount = 1; + } + + for (int i = 0; i < planeCount; i++) { + void *planeAddress; + size_t bytesPerRow; + size_t height; + size_t width; + + if (isPlanar) { + planeAddress = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, i); + bytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, i); + height = CVPixelBufferGetHeightOfPlane(pixelBuffer, i); + width = CVPixelBufferGetWidthOfPlane(pixelBuffer, i); + } else { + planeAddress = CVPixelBufferGetBaseAddress(pixelBuffer); + bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer); + height = CVPixelBufferGetHeight(pixelBuffer); + width = CVPixelBufferGetWidth(pixelBuffer); + } + + NSNumber *length = @(bytesPerRow * height); + NSData *bytes = [NSData dataWithBytes:planeAddress length:length.unsignedIntegerValue]; + + NSMutableDictionary *planeBuffer = [NSMutableDictionary dictionary]; + planeBuffer[@"bytesPerRow"] = @(bytesPerRow); + planeBuffer[@"width"] = @(width); + planeBuffer[@"height"] = @(height); + planeBuffer[@"bytes"] = [FlutterStandardTypedData typedDataWithBytes:bytes]; + + [planes addObject:planeBuffer]; + } + // Before accessing pixel data, we should lock the base address, and unlock it afterwards. + // Done accessing the `pixelBuffer` at this point. + CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly); + + NSMutableDictionary *imageBuffer = [NSMutableDictionary dictionary]; + imageBuffer[@"width"] = [NSNumber numberWithUnsignedLong:imageWidth]; + imageBuffer[@"height"] = [NSNumber numberWithUnsignedLong:imageHeight]; + imageBuffer[@"format"] = @(_videoFormat); + imageBuffer[@"planes"] = planes; + imageBuffer[@"lensAperture"] = [NSNumber numberWithFloat:[_captureDevice lensAperture]]; + Float64 exposureDuration = CMTimeGetSeconds([_captureDevice exposureDuration]); + Float64 nsExposureDuration = 1000000000 * exposureDuration; + imageBuffer[@"sensorExposureTime"] = [NSNumber numberWithInt:nsExposureDuration]; + imageBuffer[@"sensorSensitivity"] = [NSNumber numberWithFloat:[_captureDevice ISO]]; + + dispatch_async(dispatch_get_main_queue(), ^{ + eventSink(imageBuffer); + }); + } + } + if (_isRecording && !_isRecordingPaused) { + if (_videoWriter.status == AVAssetWriterStatusFailed) { + [_methodChannel invokeMethod:errorMethod + arguments:[NSString stringWithFormat:@"%@", _videoWriter.error]]; + return; + } + + CFRetain(sampleBuffer); + CMTime currentSampleTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer); + + if (_videoWriter.status != AVAssetWriterStatusWriting) { + [_videoWriter startWriting]; + [_videoWriter startSessionAtSourceTime:currentSampleTime]; + } + + if (output == _captureVideoOutput) { + if (_videoIsDisconnected) { + _videoIsDisconnected = NO; + + if (_videoTimeOffset.value == 0) { + _videoTimeOffset = CMTimeSubtract(currentSampleTime, _lastVideoSampleTime); + } else { + CMTime offset = CMTimeSubtract(currentSampleTime, _lastVideoSampleTime); + _videoTimeOffset = CMTimeAdd(_videoTimeOffset, offset); + } + + return; + } + + _lastVideoSampleTime = currentSampleTime; + + CVPixelBufferRef nextBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); + CMTime nextSampleTime = CMTimeSubtract(_lastVideoSampleTime, _videoTimeOffset); + [_videoAdaptor appendPixelBuffer:nextBuffer withPresentationTime:nextSampleTime]; + } else { + CMTime dur = CMSampleBufferGetDuration(sampleBuffer); + + if (dur.value > 0) { + currentSampleTime = CMTimeAdd(currentSampleTime, dur); + } + + if (_audioIsDisconnected) { + _audioIsDisconnected = NO; + + if (_audioTimeOffset.value == 0) { + _audioTimeOffset = CMTimeSubtract(currentSampleTime, _lastAudioSampleTime); + } else { + CMTime offset = CMTimeSubtract(currentSampleTime, _lastAudioSampleTime); + _audioTimeOffset = CMTimeAdd(_audioTimeOffset, offset); + } + + return; + } + + _lastAudioSampleTime = currentSampleTime; + + if (_audioTimeOffset.value != 0) { + CFRelease(sampleBuffer); + sampleBuffer = [self adjustTime:sampleBuffer by:_audioTimeOffset]; + } + + [self newAudioSample:sampleBuffer]; + } + + CFRelease(sampleBuffer); + } +} + +- (CMSampleBufferRef)adjustTime:(CMSampleBufferRef)sample by:(CMTime)offset CF_RETURNS_RETAINED { + CMItemCount count; + CMSampleBufferGetSampleTimingInfoArray(sample, 0, nil, &count); + CMSampleTimingInfo *pInfo = malloc(sizeof(CMSampleTimingInfo) * count); + CMSampleBufferGetSampleTimingInfoArray(sample, count, pInfo, &count); + for (CMItemCount i = 0; i < count; i++) { + pInfo[i].decodeTimeStamp = CMTimeSubtract(pInfo[i].decodeTimeStamp, offset); + pInfo[i].presentationTimeStamp = CMTimeSubtract(pInfo[i].presentationTimeStamp, offset); + } + CMSampleBufferRef sout; + CMSampleBufferCreateCopyWithNewTiming(nil, sample, count, pInfo, &sout); + free(pInfo); + return sout; +} + +- (void)newVideoSample:(CMSampleBufferRef)sampleBuffer { + if (_videoWriter.status != AVAssetWriterStatusWriting) { + if (_videoWriter.status == AVAssetWriterStatusFailed) { + [_methodChannel invokeMethod:errorMethod + arguments:[NSString stringWithFormat:@"%@", _videoWriter.error]]; + } + return; + } + if (_videoWriterInput.readyForMoreMediaData) { + if (![_videoWriterInput appendSampleBuffer:sampleBuffer]) { + [_methodChannel + invokeMethod:errorMethod + arguments:[NSString stringWithFormat:@"%@", @"Unable to write to video input"]]; + } + } +} + +- (void)newAudioSample:(CMSampleBufferRef)sampleBuffer { + if (_videoWriter.status != AVAssetWriterStatusWriting) { + if (_videoWriter.status == AVAssetWriterStatusFailed) { + [_methodChannel invokeMethod:errorMethod + arguments:[NSString stringWithFormat:@"%@", _videoWriter.error]]; + } + return; + } + if (_audioWriterInput.readyForMoreMediaData) { + if (![_audioWriterInput appendSampleBuffer:sampleBuffer]) { + [_methodChannel + invokeMethod:errorMethod + arguments:[NSString stringWithFormat:@"%@", @"Unable to write to audio input"]]; + } + } +} + +- (void)close { + [_captureSession stopRunning]; + for (AVCaptureInput *input in [_captureSession inputs]) { + [_captureSession removeInput:input]; + } + for (AVCaptureOutput *output in [_captureSession outputs]) { + [_captureSession removeOutput:output]; + } +} + +- (void)dealloc { + if (_latestPixelBuffer) { + CFRelease(_latestPixelBuffer); + } + [_motionManager stopAccelerometerUpdates]; +} + +- (CVPixelBufferRef)copyPixelBuffer { + CVPixelBufferRef pixelBuffer = _latestPixelBuffer; + while (!OSAtomicCompareAndSwapPtrBarrier(pixelBuffer, nil, (void **)&_latestPixelBuffer)) { + pixelBuffer = _latestPixelBuffer; + } + + return pixelBuffer; +} + +- (void)startVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result { + if (!_isRecording) { + NSError *error; + _videoRecordingPath = [self getTemporaryFilePathWithExtension:@"mp4" + subfolder:@"videos" + prefix:@"REC_" + error:error]; + if (error) { + [result sendError:error]; + return; + } + if (![self setupWriterForPath:_videoRecordingPath]) { + [result sendErrorWithCode:@"IOError" message:@"Setup Writer Failed" details:nil]; + return; + } + _isRecording = YES; + _isRecordingPaused = NO; + _videoTimeOffset = CMTimeMake(0, 1); + _audioTimeOffset = CMTimeMake(0, 1); + _videoIsDisconnected = NO; + _audioIsDisconnected = NO; + [result sendSuccess]; + } else { + [result sendErrorWithCode:@"Error" message:@"Video is already recording" details:nil]; + } +} + +- (void)stopVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result { + if (_isRecording) { + _isRecording = NO; + + if (_videoWriter.status != AVAssetWriterStatusUnknown) { + [_videoWriter finishWritingWithCompletionHandler:^{ + if (self->_videoWriter.status == AVAssetWriterStatusCompleted) { + [self updateOrientation]; + [result sendSuccessWithData:self->_videoRecordingPath]; + self->_videoRecordingPath = nil; + } else { + [result sendErrorWithCode:@"IOError" + message:@"AVAssetWriter could not finish writing!" + details:nil]; + } + }]; + } + } else { + NSError *error = + [NSError errorWithDomain:NSCocoaErrorDomain + code:NSURLErrorResourceUnavailable + userInfo:@{NSLocalizedDescriptionKey : @"Video is not recording!"}]; + [result sendError:error]; + } +} + +- (void)pauseVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result { + _isRecordingPaused = YES; + _videoIsDisconnected = YES; + _audioIsDisconnected = YES; + [result sendSuccess]; +} + +- (void)resumeVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result { + _isRecordingPaused = NO; + [result sendSuccess]; +} + +- (void)lockCaptureOrientationWithResult:(FLTThreadSafeFlutterResult *)result + orientation:(NSString *)orientationStr { + UIDeviceOrientation orientation; + @try { + orientation = FLTGetUIDeviceOrientationForString(orientationStr); + } @catch (NSError *e) { + [result sendError:e]; + return; + } + + if (_lockedCaptureOrientation != orientation) { + _lockedCaptureOrientation = orientation; + [self updateOrientation]; + } + + [result sendSuccess]; +} + +- (void)unlockCaptureOrientationWithResult:(FLTThreadSafeFlutterResult *)result { + _lockedCaptureOrientation = UIDeviceOrientationUnknown; + [self updateOrientation]; + [result sendSuccess]; +} + +- (void)setFlashModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSString *)modeStr { + FLTFlashMode mode; + @try { + mode = FLTGetFLTFlashModeForString(modeStr); + } @catch (NSError *e) { + [result sendError:e]; + return; + } + if (mode == FLTFlashModeTorch) { + if (!_captureDevice.hasTorch) { + [result sendErrorWithCode:@"setFlashModeFailed" + message:@"Device does not support torch mode" + details:nil]; + return; + } + if (!_captureDevice.isTorchAvailable) { + [result sendErrorWithCode:@"setFlashModeFailed" + message:@"Torch mode is currently not available" + details:nil]; + return; + } + if (_captureDevice.torchMode != AVCaptureTorchModeOn) { + [_captureDevice lockForConfiguration:nil]; + [_captureDevice setTorchMode:AVCaptureTorchModeOn]; + [_captureDevice unlockForConfiguration]; + } + } else { + if (!_captureDevice.hasFlash) { + [result sendErrorWithCode:@"setFlashModeFailed" + message:@"Device does not have flash capabilities" + details:nil]; + return; + } + AVCaptureFlashMode avFlashMode = FLTGetAVCaptureFlashModeForFLTFlashMode(mode); + if (![_capturePhotoOutput.supportedFlashModes + containsObject:[NSNumber numberWithInt:((int)avFlashMode)]]) { + [result sendErrorWithCode:@"setFlashModeFailed" + message:@"Device does not support this specific flash mode" + details:nil]; + return; + } + if (_captureDevice.torchMode != AVCaptureTorchModeOff) { + [_captureDevice lockForConfiguration:nil]; + [_captureDevice setTorchMode:AVCaptureTorchModeOff]; + [_captureDevice unlockForConfiguration]; + } + } + _flashMode = mode; + [result sendSuccess]; +} + +- (void)setExposureModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSString *)modeStr { + FLTExposureMode mode; + @try { + mode = FLTGetFLTExposureModeForString(modeStr); + } @catch (NSError *e) { + [result sendError:e]; + return; + } + _exposureMode = mode; + [self applyExposureMode]; + [result sendSuccess]; +} + +- (void)applyExposureMode { + [_captureDevice lockForConfiguration:nil]; + switch (_exposureMode) { + case FLTExposureModeLocked: + [_captureDevice setExposureMode:AVCaptureExposureModeAutoExpose]; + break; + case FLTExposureModeAuto: + if ([_captureDevice isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) { + [_captureDevice setExposureMode:AVCaptureExposureModeContinuousAutoExposure]; + } else { + [_captureDevice setExposureMode:AVCaptureExposureModeAutoExpose]; + } + break; + } + [_captureDevice unlockForConfiguration]; +} + +- (void)setFocusModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSString *)modeStr { + FLTFocusMode mode; + @try { + mode = FLTGetFLTFocusModeForString(modeStr); + } @catch (NSError *e) { + [result sendError:e]; + return; + } + _focusMode = mode; + [self applyFocusMode]; + [result sendSuccess]; +} + +- (void)applyFocusMode { + [self applyFocusMode:_focusMode onDevice:_captureDevice]; +} + +- (void)applyFocusMode:(FLTFocusMode)focusMode onDevice:(AVCaptureDevice *)captureDevice { + [captureDevice lockForConfiguration:nil]; + switch (focusMode) { + case FLTFocusModeLocked: + if ([captureDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]) { + [captureDevice setFocusMode:AVCaptureFocusModeAutoFocus]; + } + break; + case FLTFocusModeAuto: + if ([captureDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) { + [captureDevice setFocusMode:AVCaptureFocusModeContinuousAutoFocus]; + } else if ([captureDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]) { + [captureDevice setFocusMode:AVCaptureFocusModeAutoFocus]; + } + break; + } + [captureDevice unlockForConfiguration]; +} + +- (void)pausePreviewWithResult:(FLTThreadSafeFlutterResult *)result { + _isPreviewPaused = true; + [result sendSuccess]; +} + +- (void)resumePreviewWithResult:(FLTThreadSafeFlutterResult *)result { + _isPreviewPaused = false; + [result sendSuccess]; +} + +- (CGPoint)getCGPointForCoordsWithOrientation:(UIDeviceOrientation)orientation + x:(double)x + y:(double)y { + double oldX = x, oldY = y; + switch (orientation) { + case UIDeviceOrientationPortrait: // 90 ccw + y = 1 - oldX; + x = oldY; + break; + case UIDeviceOrientationPortraitUpsideDown: // 90 cw + x = 1 - oldY; + y = oldX; + break; + case UIDeviceOrientationLandscapeRight: // 180 + x = 1 - x; + y = 1 - y; + break; + case UIDeviceOrientationLandscapeLeft: + default: + // No rotation required + break; + } + return CGPointMake(x, y); +} + +- (void)setExposurePointWithResult:(FLTThreadSafeFlutterResult *)result x:(double)x y:(double)y { + if (!_captureDevice.isExposurePointOfInterestSupported) { + [result sendErrorWithCode:@"setExposurePointFailed" + message:@"Device does not have exposure point capabilities" + details:nil]; + return; + } + UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation]; + [_captureDevice lockForConfiguration:nil]; + [_captureDevice setExposurePointOfInterest:[self getCGPointForCoordsWithOrientation:orientation + x:x + y:y]]; + [_captureDevice unlockForConfiguration]; + // Retrigger auto exposure + [self applyExposureMode]; + [result sendSuccess]; +} + +- (void)setFocusPointWithResult:(FLTThreadSafeFlutterResult *)result x:(double)x y:(double)y { + if (!_captureDevice.isFocusPointOfInterestSupported) { + [result sendErrorWithCode:@"setFocusPointFailed" + message:@"Device does not have focus point capabilities" + details:nil]; + return; + } + UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation]; + [_captureDevice lockForConfiguration:nil]; + + [_captureDevice setFocusPointOfInterest:[self getCGPointForCoordsWithOrientation:orientation + x:x + y:y]]; + [_captureDevice unlockForConfiguration]; + // Retrigger auto focus + [self applyFocusMode]; + [result sendSuccess]; +} + +- (void)setExposureOffsetWithResult:(FLTThreadSafeFlutterResult *)result offset:(double)offset { + [_captureDevice lockForConfiguration:nil]; + [_captureDevice setExposureTargetBias:offset completionHandler:nil]; + [_captureDevice unlockForConfiguration]; + [result sendSuccessWithData:@(offset)]; +} + +- (void)startImageStreamWithMessenger:(NSObject *)messenger { + if (!_isStreamingImages) { + FlutterEventChannel *eventChannel = + [FlutterEventChannel eventChannelWithName:@"plugins.flutter.io/camera/imageStream" + binaryMessenger:messenger]; + FLTThreadSafeEventChannel *threadSafeEventChannel = + [[FLTThreadSafeEventChannel alloc] initWithEventChannel:eventChannel]; + + _imageStreamHandler = + [[FLTImageStreamHandler alloc] initWithCaptureSessionQueue:_captureSessionQueue]; + [threadSafeEventChannel setStreamHandler:_imageStreamHandler + completion:^{ + dispatch_async(self->_captureSessionQueue, ^{ + self.isStreamingImages = YES; + }); + }]; + } else { + [_methodChannel invokeMethod:errorMethod + arguments:@"Images from camera are already streaming!"]; + } +} + +- (void)stopImageStream { + if (_isStreamingImages) { + _isStreamingImages = NO; + _imageStreamHandler = nil; + } else { + [_methodChannel invokeMethod:errorMethod arguments:@"Images from camera are not streaming!"]; + } +} + +- (void)getMaxZoomLevelWithResult:(FLTThreadSafeFlutterResult *)result { + CGFloat maxZoomFactor = [self getMaxAvailableZoomFactor]; + + [result sendSuccessWithData:[NSNumber numberWithFloat:maxZoomFactor]]; +} + +- (void)getMinZoomLevelWithResult:(FLTThreadSafeFlutterResult *)result { + CGFloat minZoomFactor = [self getMinAvailableZoomFactor]; + [result sendSuccessWithData:[NSNumber numberWithFloat:minZoomFactor]]; +} + +- (void)setZoomLevel:(CGFloat)zoom Result:(FLTThreadSafeFlutterResult *)result { + CGFloat maxAvailableZoomFactor = [self getMaxAvailableZoomFactor]; + CGFloat minAvailableZoomFactor = [self getMinAvailableZoomFactor]; + + if (maxAvailableZoomFactor < zoom || minAvailableZoomFactor > zoom) { + NSString *errorMessage = [NSString + stringWithFormat:@"Zoom level out of bounds (zoom level should be between %f and %f).", + minAvailableZoomFactor, maxAvailableZoomFactor]; + + [result sendErrorWithCode:@"ZOOM_ERROR" message:errorMessage details:nil]; + return; + } + + NSError *error = nil; + if (![_captureDevice lockForConfiguration:&error]) { + [result sendError:error]; + return; + } + _captureDevice.videoZoomFactor = zoom; + [_captureDevice unlockForConfiguration]; + + [result sendSuccess]; +} + +- (CGFloat)getMinAvailableZoomFactor { + if (@available(iOS 11.0, *)) { + return _captureDevice.minAvailableVideoZoomFactor; + } else { + return 1.0; + } +} + +- (CGFloat)getMaxAvailableZoomFactor { + if (@available(iOS 11.0, *)) { + return _captureDevice.maxAvailableVideoZoomFactor; + } else { + return _captureDevice.activeFormat.videoMaxZoomFactor; + } +} + +- (BOOL)setupWriterForPath:(NSString *)path { + NSError *error = nil; + NSURL *outputURL; + if (path != nil) { + outputURL = [NSURL fileURLWithPath:path]; + } else { + return NO; + } + if (_enableAudio && !_isAudioSetup) { + [self setUpCaptureSessionForAudio]; + } + + _videoWriter = [[AVAssetWriter alloc] initWithURL:outputURL + fileType:AVFileTypeMPEG4 + error:&error]; + NSParameterAssert(_videoWriter); + if (error) { + [_methodChannel invokeMethod:errorMethod arguments:error.description]; + return NO; + } + + NSDictionary *videoSettings = [_captureVideoOutput + recommendedVideoSettingsForAssetWriterWithOutputFileType:AVFileTypeMPEG4]; + _videoWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo + outputSettings:videoSettings]; + + _videoAdaptor = [AVAssetWriterInputPixelBufferAdaptor + assetWriterInputPixelBufferAdaptorWithAssetWriterInput:_videoWriterInput + sourcePixelBufferAttributes:@{ + (NSString *)kCVPixelBufferPixelFormatTypeKey : @(_videoFormat) + }]; + + NSParameterAssert(_videoWriterInput); + + _videoWriterInput.expectsMediaDataInRealTime = YES; + + // Add the audio input + if (_enableAudio) { + AudioChannelLayout acl; + bzero(&acl, sizeof(acl)); + acl.mChannelLayoutTag = kAudioChannelLayoutTag_Mono; + NSDictionary *audioOutputSettings = nil; + // Both type of audio inputs causes output video file to be corrupted. + audioOutputSettings = @{ + AVFormatIDKey : [NSNumber numberWithInt:kAudioFormatMPEG4AAC], + AVSampleRateKey : [NSNumber numberWithFloat:44100.0], + AVNumberOfChannelsKey : [NSNumber numberWithInt:1], + AVChannelLayoutKey : [NSData dataWithBytes:&acl length:sizeof(acl)], + }; + _audioWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio + outputSettings:audioOutputSettings]; + _audioWriterInput.expectsMediaDataInRealTime = YES; + + [_videoWriter addInput:_audioWriterInput]; + [_audioOutput setSampleBufferDelegate:self queue:_captureSessionQueue]; + } + + if (_flashMode == FLTFlashModeTorch) { + [self.captureDevice lockForConfiguration:nil]; + [self.captureDevice setTorchMode:AVCaptureTorchModeOn]; + [self.captureDevice unlockForConfiguration]; + } + + [_videoWriter addInput:_videoWriterInput]; + + [_captureVideoOutput setSampleBufferDelegate:self queue:_captureSessionQueue]; + + return YES; +} + +- (void)setUpCaptureSessionForAudio { + NSError *error = nil; + // Create a device input with the device and add it to the session. + // Setup the audio input. + AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio]; + AVCaptureDeviceInput *audioInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice + error:&error]; + if (error) { + [_methodChannel invokeMethod:errorMethod arguments:error.description]; + } + // Setup the audio output. + _audioOutput = [[AVCaptureAudioDataOutput alloc] init]; + + if ([_captureSession canAddInput:audioInput]) { + [_captureSession addInput:audioInput]; + + if ([_captureSession canAddOutput:_audioOutput]) { + [_captureSession addOutput:_audioOutput]; + _isAudioSetup = YES; + } else { + [_methodChannel invokeMethod:errorMethod + arguments:@"Unable to add Audio input/output to session capture"]; + _isAudioSetup = NO; + } + } +} +@end From 72dff1b4342676f3b4b6fc9a7fb810140dcf7477 Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Fri, 28 Jan 2022 15:00:22 -0800 Subject: [PATCH 127/600] [camera] Sample buffer handling on session queue (#4709) --- packages/camera/camera/CHANGELOG.md | 3 +- .../ios/Runner.xcodeproj/project.pbxproj | 4 ++ .../ios/RunnerTests/SampleBufferQueueTests.m | 37 +++++++++++++++++++ .../camera/ios/Classes/CameraPlugin.modulemap | 1 + packages/camera/camera/ios/Classes/FLTCam.m | 4 +- .../camera/camera/ios/Classes/FLTCam_Test.h | 12 ++++++ packages/camera/camera/pubspec.yaml | 2 +- 7 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 packages/camera/camera/example/ios/RunnerTests/SampleBufferQueueTests.m create mode 100644 packages/camera/camera/ios/Classes/FLTCam_Test.h diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index ea27720c94f8..9b32af241032 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.9.4+9 +* iOS performance improvement by moving sample buffer handling from the main queue to a background session queue. * Minor iOS internal code cleanup related to camera class and its delegate. * Minor iOS internal code cleanup related to resolution preset, video format, focus mode, exposure mode and device orientation. * Minor iOS internal code cleanup related to flash mode. diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj index 0be5b59bb6b4..65a6bbda3261 100644 --- a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj @@ -26,6 +26,7 @@ E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */; }; E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */; }; E0F95E3D27A32AB900699390 /* CameraPropertiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */; }; + E0F95E4427A36B9200699390 /* SampleBufferQueueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0F95E4327A36B9200699390 /* SampleBufferQueueTests.m */; }; E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */; }; F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */ = {isa = PBXBuildFile; fileRef = F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */; }; /* End PBXBuildFile section */ @@ -86,6 +87,7 @@ E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeTextureRegistryTests.m; sourceTree = ""; }; E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeEventChannelTests.m; sourceTree = ""; }; E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPropertiesTests.m; sourceTree = ""; }; + E0F95E4327A36B9200699390 /* SampleBufferQueueTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SampleBufferQueueTests.m; sourceTree = ""; }; E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPreviewPauseTests.m; sourceTree = ""; }; F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockFLTThreadSafeFlutterResult.h; sourceTree = ""; }; F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockFLTThreadSafeFlutterResult.m; sourceTree = ""; }; @@ -122,6 +124,7 @@ E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */, E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */, E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */, + E0F95E4327A36B9200699390 /* SampleBufferQueueTests.m */, E01EE4A72799F3A5008C1950 /* QueueHelperTests.m */, E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */, F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */, @@ -390,6 +393,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + E0F95E4427A36B9200699390 /* SampleBufferQueueTests.m in Sources */, 03F6F8B226CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m in Sources */, 033B94BE269C40A200B4DF97 /* CameraMethodChannelTests.m in Sources */, E0F95E3D27A32AB900699390 /* CameraPropertiesTests.m in Sources */, diff --git a/packages/camera/camera/example/ios/RunnerTests/SampleBufferQueueTests.m b/packages/camera/camera/example/ios/RunnerTests/SampleBufferQueueTests.m new file mode 100644 index 000000000000..19cead905f61 --- /dev/null +++ b/packages/camera/camera/example/ios/RunnerTests/SampleBufferQueueTests.m @@ -0,0 +1,37 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import camera; +@import camera.Test; +@import AVFoundation; +@import XCTest; +#import + +@interface SampleBufferQueueTests : XCTestCase + +@end + +@implementation SampleBufferQueueTests + +- (void)testSampleBufferCallbackQueueMustBeCaptureSessionQueue { + id inputMock = OCMClassMock([AVCaptureDeviceInput class]); + OCMStub([inputMock deviceInputWithDevice:[OCMArg any] error:[OCMArg setTo:nil]]) + .andReturn(inputMock); + + id sessionMock = OCMClassMock([AVCaptureSession class]); + OCMStub([sessionMock alloc]).andReturn(sessionMock); + OCMStub([sessionMock addInputWithNoConnections:[OCMArg any]]); // no-op + OCMStub([sessionMock canSetSessionPreset:[OCMArg any]]).andReturn(YES); + + dispatch_queue_t captureSessionQueue = dispatch_queue_create("testing", NULL); + FLTCam *cam = [[FLTCam alloc] initWithCameraName:@"camera" + resolutionPreset:@"medium" + enableAudio:true + orientation:UIDeviceOrientationPortrait + captureSessionQueue:captureSessionQueue + error:nil]; + XCTAssertEqual(captureSessionQueue, cam.captureVideoOutput.sampleBufferCallbackQueue); +} + +@end diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.modulemap b/packages/camera/camera/ios/Classes/CameraPlugin.modulemap index 1ad34373fbdb..a695728fc87a 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin.modulemap +++ b/packages/camera/camera/ios/Classes/CameraPlugin.modulemap @@ -8,5 +8,6 @@ framework module camera { header "CameraPlugin_Test.h" header "CameraProperties.h" header "FLTCam.h" + header "FLTCam_Test.h" } } diff --git a/packages/camera/camera/ios/Classes/FLTCam.m b/packages/camera/camera/ios/Classes/FLTCam.m index 11be4ad5a486..82ac6714d689 100644 --- a/packages/camera/camera/ios/Classes/FLTCam.m +++ b/packages/camera/camera/ios/Classes/FLTCam.m @@ -3,6 +3,7 @@ // found in the LICENSE file. #import "FLTCam.h" +#import "FLTCam_Test.h" @import CoreMotion; #import @@ -114,7 +115,6 @@ @interface FLTCam () =2.14.0 <3.0.0" From a5b97ea9b37283167968755387e6ee4cc5f268d1 Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Tue, 1 Feb 2022 10:10:22 -0800 Subject: [PATCH 128/600] [video_player] Avoid blocking the main thread loading video count (#4714) --- .../video_player/video_player/CHANGELOG.md | 4 + .../ios/RunnerTests/VideoPlayerTests.m | 89 ++++++++++++++++++- .../ios/Classes/FLTVideoPlayerPlugin.m | 29 ++++-- .../video_player/video_player/pubspec.yaml | 2 +- 4 files changed, 117 insertions(+), 7 deletions(-) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index e28ef83ca720..fc6e28d4495d 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.2.17 + +* Avoid blocking the main thread loading video count on iOS. + ## 2.2.16 * Introduces `setCaptionOffset` to offset the caption display based on a Duration. diff --git a/packages/video_player/video_player/example/ios/RunnerTests/VideoPlayerTests.m b/packages/video_player/video_player/example/ios/RunnerTests/VideoPlayerTests.m index 90c7dc2ee95d..c57f16672f9d 100644 --- a/packages/video_player/video_player/example/ios/RunnerTests/VideoPlayerTests.m +++ b/packages/video_player/video_player/example/ios/RunnerTests/VideoPlayerTests.m @@ -8,7 +8,7 @@ #import -@interface FLTVideoPlayer : NSObject +@interface FLTVideoPlayer : NSObject @property(readonly, nonatomic) AVPlayer *player; @end @@ -70,4 +70,91 @@ - (void)testDeregistersFromPlayer { [self waitForExpectationsWithTimeout:1 handler:nil]; } +- (void)testVideoControls { + NSObject *registry = + (NSObject *)[[UIApplication sharedApplication] delegate]; + NSObject *registrar = [registry registrarForPlugin:@"TestVideoControls"]; + + FLTVideoPlayerPlugin *videoPlayerPlugin = + (FLTVideoPlayerPlugin *)[[FLTVideoPlayerPlugin alloc] initWithRegistrar:registrar]; + + NSDictionary *videoInitialization = + [self testPlugin:videoPlayerPlugin + uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4"]; + XCTAssertEqualObjects(videoInitialization[@"height"], @720); + XCTAssertEqualObjects(videoInitialization[@"width"], @1280); + XCTAssertEqualWithAccuracy([videoInitialization[@"duration"] intValue], 4000, 200); +} + +- (void)testAudioControls { + NSObject *registry = + (NSObject *)[[UIApplication sharedApplication] delegate]; + NSObject *registrar = [registry registrarForPlugin:@"TestAudioControls"]; + + FLTVideoPlayerPlugin *videoPlayerPlugin = + (FLTVideoPlayerPlugin *)[[FLTVideoPlayerPlugin alloc] initWithRegistrar:registrar]; + + NSDictionary *audioInitialization = + [self testPlugin:videoPlayerPlugin + uri:@"https://cdn.pixabay.com/audio/2021/09/06/audio_bacd4d6020.mp3"]; + XCTAssertEqualObjects(audioInitialization[@"height"], @0); + XCTAssertEqualObjects(audioInitialization[@"width"], @0); + // Perfect precision not guaranteed. + XCTAssertEqualWithAccuracy([audioInitialization[@"duration"] intValue], 68500, 200); +} + +- (NSDictionary *)testPlugin:(FLTVideoPlayerPlugin *)videoPlayerPlugin + uri:(NSString *)uri { + FlutterError *error; + [videoPlayerPlugin initialize:&error]; + XCTAssertNil(error); + + FLTCreateMessage *create = [[FLTCreateMessage alloc] init]; + create.uri = uri; + FLTTextureMessage *textureMessage = [videoPlayerPlugin create:create error:&error]; + + NSNumber *textureId = textureMessage.textureId; + FLTVideoPlayer *player = videoPlayerPlugin.playersByTextureId[textureId]; + XCTAssertNotNil(player); + + XCTestExpectation *initializedExpectation = [self expectationWithDescription:@"initialized"]; + __block NSDictionary *initializationEvent; + [player onListenWithArguments:nil + eventSink:^(NSDictionary *event) { + if ([event[@"event"] isEqualToString:@"initialized"]) { + initializationEvent = event; + XCTAssertEqual(event.count, 4); + [initializedExpectation fulfill]; + } + }]; + [self waitForExpectationsWithTimeout:1.0 handler:nil]; + + // Starts paused. + AVPlayer *avPlayer = player.player; + XCTAssertEqual(avPlayer.rate, 0); + XCTAssertEqual(avPlayer.volume, 1); + XCTAssertEqual(avPlayer.timeControlStatus, AVPlayerTimeControlStatusPaused); + + // Change playback speed. + FLTPlaybackSpeedMessage *playback = [[FLTPlaybackSpeedMessage alloc] init]; + playback.textureId = textureId; + playback.speed = @2; + [videoPlayerPlugin setPlaybackSpeed:playback error:&error]; + XCTAssertNil(error); + XCTAssertEqual(avPlayer.rate, 2); + XCTAssertEqual(avPlayer.timeControlStatus, AVPlayerTimeControlStatusWaitingToPlayAtSpecifiedRate); + + // Volume + FLTVolumeMessage *volume = [[FLTVolumeMessage alloc] init]; + volume.textureId = textureId; + volume.volume = @(0.1); + [videoPlayerPlugin setVolume:volume error:&error]; + XCTAssertNil(error); + XCTAssertEqual(avPlayer.volume, 0.1f); + + [player onCancelWithArguments:nil]; + + return initializationEvent; +} + @end diff --git a/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m b/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m index 696fba21f661..5d09cfed61d2 100644 --- a/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m +++ b/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m @@ -331,25 +331,44 @@ - (void)updatePlayingState { - (void)setupEventSinkIfReadyToPlay { if (_eventSink && !_isInitialized) { - BOOL hasVideoTracks = - [[self.player.currentItem.asset tracksWithMediaType:AVMediaTypeVideo] count] != 0; - CGSize size = [self.player currentItem].presentationSize; + AVPlayerItem *currentItem = self.player.currentItem; + CGSize size = currentItem.presentationSize; CGFloat width = size.width; CGFloat height = size.height; + // Wait until tracks are loaded to check duration or if there are any videos. + AVAsset *asset = currentItem.asset; + if ([asset statusOfValueForKey:@"tracks" error:nil] != AVKeyValueStatusLoaded) { + void (^trackCompletionHandler)(void) = ^{ + if ([asset statusOfValueForKey:@"tracks" error:nil] != AVKeyValueStatusLoaded) { + // Cancelled, or something failed. + return; + } + // This completion block will run on an AVFoundation background queue. + // Hop back to the main thread to set up event sink. + [self performSelector:_cmd onThread:NSThread.mainThread withObject:self waitUntilDone:NO]; + }; + [asset loadValuesAsynchronouslyForKeys:@[ @"tracks" ] + completionHandler:trackCompletionHandler]; + return; + } + + BOOL hasVideoTracks = [asset tracksWithMediaType:AVMediaTypeVideo].count != 0; + // The player has not yet initialized when it contains video tracks. if (hasVideoTracks && height == CGSizeZero.height && width == CGSizeZero.width) { return; } // The player may be initialized but still needs to determine the duration. - if ([self duration] == 0) { + int64_t duration = [self duration]; + if (duration == 0) { return; } _isInitialized = YES; _eventSink(@{ @"event" : @"initialized", - @"duration" : @([self duration]), + @"duration" : @(duration), @"width" : @(width), @"height" : @(height) }); diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 63520f30db4b..9a3697b45b98 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for displaying inline video with other Flutter widgets on Android, iOS, and web. repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.2.16 +version: 2.2.17 environment: sdk: ">=2.14.0 <3.0.0" From 4450606ac080442fc67f413941541ba9e0d7bbe8 Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Wed, 2 Feb 2022 08:10:24 -0800 Subject: [PATCH 129/600] [video_player] Update audio URL in iOS tests (#4720) --- packages/video_player/video_player/CHANGELOG.md | 4 ++++ .../video_player/example/ios/RunnerTests/VideoPlayerTests.m | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index fc6e28d4495d..8209cc9abf1c 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Update audio URL in iOS tests. + ## 2.2.17 * Avoid blocking the main thread loading video count on iOS. diff --git a/packages/video_player/video_player/example/ios/RunnerTests/VideoPlayerTests.m b/packages/video_player/video_player/example/ios/RunnerTests/VideoPlayerTests.m index c57f16672f9d..1e717fa67846 100644 --- a/packages/video_player/video_player/example/ios/RunnerTests/VideoPlayerTests.m +++ b/packages/video_player/video_player/example/ios/RunnerTests/VideoPlayerTests.m @@ -96,11 +96,11 @@ - (void)testAudioControls { NSDictionary *audioInitialization = [self testPlugin:videoPlayerPlugin - uri:@"https://cdn.pixabay.com/audio/2021/09/06/audio_bacd4d6020.mp3"]; + uri:@"https://flutter.github.io/assets-for-api-docs/assets/audio/rooster.mp3"]; XCTAssertEqualObjects(audioInitialization[@"height"], @0); XCTAssertEqualObjects(audioInitialization[@"width"], @0); // Perfect precision not guaranteed. - XCTAssertEqualWithAccuracy([audioInitialization[@"duration"] intValue], 68500, 200); + XCTAssertEqualWithAccuracy([audioInitialization[@"duration"] intValue], 5400, 200); } - (NSDictionary *)testPlugin:(FLTVideoPlayerPlugin *)videoPlayerPlugin From 4ebfcb53b93144cbe4a7e848c992fb394a697641 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 2 Feb 2022 11:51:52 -0500 Subject: [PATCH 130/600] [video_player] Federate mobile implementations (#4712) Moves the iOS and Android implementations to their own federated packages. The iOS implementation package uses the name `avfoundation` rather than `ios` since it is likely that a macOS implementation could share a significant amount of code. As part of the federation, migrates the mobile implementations to using in-package method channels rather than the shared method channel. Currently these are just duplicates of the existing method channels; updating them to new version of Pigeon will be done in a separate PR later. Temporarily marks `video_player` as unpublishable to allow the implementations to be moved, rather than copied and deleted, in order to better preserve git history. A follow-up PR will restore it to publishable form. No version change: The changes to `video_player_platform_interface` only affect development of the plugin, not use of the plugin. Part of https://github.com/flutter/flutter/issues/68498 Part of https://github.com/flutter/flutter/issues/94224 --- .../video_player/video_player/CHANGELOG.md | 1 + .../video_player/android/settings.gradle | 1 - .../video_player/example/ios/Podfile | 4 - .../ios/Runner.xcodeproj/project.pbxproj | 253 +----- .../video_player/video_player/pubspec.yaml | 14 +- .../video_player/video_player_android/AUTHORS | 66 ++ .../video_player_android/CHANGELOG.md | 3 + .../CONTRIBUTING.md | 0 .../video_player/video_player_android/LICENSE | 25 + .../video_player_android/README.md | 11 + .../android/build.gradle | 0 .../gradle/wrapper/gradle-wrapper.properties | 0 .../android/settings.gradle | 1 + .../android/src/main/AndroidManifest.xml | 0 .../videoplayer/CustomSSLSocketFactory.java | 0 .../flutter/plugins/videoplayer/Messages.java | 0 .../plugins/videoplayer/QueuingEventSink.java | 0 .../plugins/videoplayer/VideoPlayer.java | 0 .../videoplayer/VideoPlayerOptions.java | 0 .../videoplayer/VideoPlayerPlugin.java | 0 .../plugins/videoplayer/VideoPlayerTest.java | 0 .../video_player_android/example/.gitignore | 1 + .../video_player_android/example/README.md | 8 + .../example/android/app/build.gradle | 66 ++ .../gradle/wrapper/gradle-wrapper.properties | 5 + .../flutter/plugins/DartIntegrationTest.java | 14 + .../FlutterActivityTest.java | 19 + .../android/app/src/main/AndroidManifest.xml | 23 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values/styles.xml | 6 + .../main/res/xml/network_security_config.xml | 7 + .../FlutterActivityTest.java | 50 ++ .../example/android/build.gradle | 29 + .../example/android/gradle.properties | 4 + .../gradle/wrapper/gradle-wrapper.properties | 6 + .../example/android/settings.gradle | 15 + .../example/assets/Butterfly-209.mp4 | Bin 0 -> 2429896 bytes .../example/assets/flutter-mark-square-64.png | Bin 0 -> 482 bytes .../integration_test/video_player_test.dart | 169 +++++ .../example/lib/main.dart | 235 ++++++ .../example/lib/mini_controller.dart | 538 +++++++++++++ .../video_player_android/example/pubspec.yaml | 35 + .../example/test_driver/integration_test.dart | 7 + .../example/test_driver/video_player.dart | 11 + .../lib/src/android_video_player.dart | 173 +++++ .../lib/src/messages.dart | 425 +++++++++++ .../lib/video_player_android.dart | 5 + .../pigeons/messages.dart | 4 +- .../video_player_android/pubspec.yaml | 28 + .../test/android_video_player_test.dart | 341 +++++++++ .../video_player_android/test/test_api.dart | 200 +++++ .../video_player_avfoundation/AUTHORS | 66 ++ .../video_player_avfoundation/CHANGELOG.md | 3 + .../video_player_avfoundation/CONTRIBUTING.md | 33 + .../video_player_avfoundation/LICENSE | 25 + .../video_player_avfoundation/README.md | 11 + .../example/.gitignore | 1 + .../example/README.md | 8 + .../example/assets/Butterfly-209.mp4 | Bin 0 -> 2429896 bytes .../example/assets/flutter-mark-square-64.png | Bin 0 -> 482 bytes .../integration_test/video_player_test.dart | 181 +++++ .../ios/Flutter/AppFrameworkInfo.plist | 30 + .../example/ios/Flutter/Debug.xcconfig | 2 + .../example/ios/Flutter/Release.xcconfig | 2 + .../example/ios/Podfile | 42 + .../ios/Runner.xcodeproj/project.pbxproj | 717 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/xcschemes/Runner.xcscheme | 107 +++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/ios/Runner/AppDelegate.h | 10 + .../example/ios/Runner/AppDelegate.m | 17 + .../AppIcon.appiconset/Contents.json | 116 +++ .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 564 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 1588 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 1025 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 1716 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 1920 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 1895 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 3831 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 1888 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 3294 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 3612 bytes .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/README.md | 5 + .../Runner/Base.lproj/LaunchScreen.storyboard | 37 + .../ios/Runner/Base.lproj/Main.storyboard | 26 + .../example/ios/Runner/Info.plist | 54 ++ .../example/ios/Runner/main.m | 13 + .../example/ios/RunnerTests/Info.plist | 0 .../ios/RunnerTests/VideoPlayerTests.m | 2 +- .../example/ios/RunnerUITests/Info.plist | 0 .../ios/RunnerUITests/VideoPlayerUITests.m | 8 +- .../example/lib/main.dart | 235 ++++++ .../example/lib/mini_controller.dart | 538 +++++++++++++ .../example/pubspec.yaml | 35 + .../example/test_driver/integration_test.dart | 7 + .../example/test_driver/video_player.dart | 11 + .../ios/Assets/.gitkeep | 0 .../ios/Classes/FLTVideoPlayerPlugin.h | 0 .../ios/Classes/FLTVideoPlayerPlugin.m | 3 + .../ios/Classes/messages.h | 0 .../ios/Classes/messages.m | 0 .../ios/video_player_avfoundation.podspec} | 7 +- .../lib/src/avfoundation_video_player.dart | 173 +++++ .../lib/src/messages.dart | 425 +++++++++++ .../lib/video_player_avfoundation.dart | 5 + .../pigeons/messages.dart | 69 ++ .../video_player_avfoundation/pubspec.yaml | 27 + .../test/avfoundation_video_player_test.dart | 341 +++++++++ .../test/test_api.dart | 199 +++++ .../CHANGELOG.md | 4 + .../CONTRIBUTING.md | 46 ++ .../lib/method_channel_video_player.dart | 4 + .../pigeons/messages.dart | 63 ++ .../pubspec.yaml | 1 + 128 files changed, 6297 insertions(+), 275 deletions(-) delete mode 100644 packages/video_player/video_player/android/settings.gradle create mode 100644 packages/video_player/video_player_android/AUTHORS create mode 100644 packages/video_player/video_player_android/CHANGELOG.md rename packages/video_player/{video_player => video_player_android}/CONTRIBUTING.md (100%) create mode 100644 packages/video_player/video_player_android/LICENSE create mode 100644 packages/video_player/video_player_android/README.md rename packages/video_player/{video_player => video_player_android}/android/build.gradle (100%) rename packages/video_player/{video_player => video_player_android}/android/gradle/wrapper/gradle-wrapper.properties (100%) create mode 100644 packages/video_player/video_player_android/android/settings.gradle rename packages/video_player/{video_player => video_player_android}/android/src/main/AndroidManifest.xml (100%) rename packages/video_player/{video_player => video_player_android}/android/src/main/java/io/flutter/plugins/videoplayer/CustomSSLSocketFactory.java (100%) rename packages/video_player/{video_player => video_player_android}/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java (100%) rename packages/video_player/{video_player => video_player_android}/android/src/main/java/io/flutter/plugins/videoplayer/QueuingEventSink.java (100%) rename packages/video_player/{video_player => video_player_android}/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java (100%) rename packages/video_player/{video_player => video_player_android}/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerOptions.java (100%) rename packages/video_player/{video_player => video_player_android}/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java (100%) rename packages/video_player/{video_player => video_player_android}/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java (100%) create mode 100644 packages/video_player/video_player_android/example/.gitignore create mode 100644 packages/video_player/video_player_android/example/README.md create mode 100644 packages/video_player/video_player_android/example/android/app/build.gradle create mode 100644 packages/video_player/video_player_android/example/android/app/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/video_player/video_player_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java create mode 100644 packages/video_player/video_player_android/example/android/app/src/androidTest/java/io/flutter/plugins/videoplayerexample/FlutterActivityTest.java create mode 100644 packages/video_player/video_player_android/example/android/app/src/main/AndroidManifest.xml create mode 100644 packages/video_player/video_player_android/example/android/app/src/main/res/drawable/launch_background.xml create mode 100644 packages/video_player/video_player_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 packages/video_player/video_player_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 packages/video_player/video_player_android/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 packages/video_player/video_player_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 packages/video_player/video_player_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 packages/video_player/video_player_android/example/android/app/src/main/res/values/styles.xml create mode 100644 packages/video_player/video_player_android/example/android/app/src/main/res/xml/network_security_config.xml create mode 100644 packages/video_player/video_player_android/example/android/app/src/test/java/io/flutter/plugins/videoplayerexample/FlutterActivityTest.java create mode 100644 packages/video_player/video_player_android/example/android/build.gradle create mode 100644 packages/video_player/video_player_android/example/android/gradle.properties create mode 100644 packages/video_player/video_player_android/example/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/video_player/video_player_android/example/android/settings.gradle create mode 100644 packages/video_player/video_player_android/example/assets/Butterfly-209.mp4 create mode 100644 packages/video_player/video_player_android/example/assets/flutter-mark-square-64.png create mode 100644 packages/video_player/video_player_android/example/integration_test/video_player_test.dart create mode 100644 packages/video_player/video_player_android/example/lib/main.dart create mode 100644 packages/video_player/video_player_android/example/lib/mini_controller.dart create mode 100644 packages/video_player/video_player_android/example/pubspec.yaml create mode 100644 packages/video_player/video_player_android/example/test_driver/integration_test.dart create mode 100644 packages/video_player/video_player_android/example/test_driver/video_player.dart create mode 100644 packages/video_player/video_player_android/lib/src/android_video_player.dart create mode 100644 packages/video_player/video_player_android/lib/src/messages.dart create mode 100644 packages/video_player/video_player_android/lib/video_player_android.dart rename packages/video_player/{video_player => video_player_android}/pigeons/messages.dart (91%) create mode 100644 packages/video_player/video_player_android/pubspec.yaml create mode 100644 packages/video_player/video_player_android/test/android_video_player_test.dart create mode 100644 packages/video_player/video_player_android/test/test_api.dart create mode 100644 packages/video_player/video_player_avfoundation/AUTHORS create mode 100644 packages/video_player/video_player_avfoundation/CHANGELOG.md create mode 100644 packages/video_player/video_player_avfoundation/CONTRIBUTING.md create mode 100644 packages/video_player/video_player_avfoundation/LICENSE create mode 100644 packages/video_player/video_player_avfoundation/README.md create mode 100644 packages/video_player/video_player_avfoundation/example/.gitignore create mode 100644 packages/video_player/video_player_avfoundation/example/README.md create mode 100644 packages/video_player/video_player_avfoundation/example/assets/Butterfly-209.mp4 create mode 100644 packages/video_player/video_player_avfoundation/example/assets/flutter-mark-square-64.png create mode 100644 packages/video_player/video_player_avfoundation/example/integration_test/video_player_test.dart create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Flutter/AppFrameworkInfo.plist create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Flutter/Debug.xcconfig create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Flutter/Release.xcconfig create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Podfile create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner/AppDelegate.h create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner/AppDelegate.m create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner/Base.lproj/Main.storyboard create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner/Info.plist create mode 100644 packages/video_player/video_player_avfoundation/example/ios/Runner/main.m rename packages/video_player/{video_player => video_player_avfoundation}/example/ios/RunnerTests/Info.plist (100%) rename packages/video_player/{video_player => video_player_avfoundation}/example/ios/RunnerTests/VideoPlayerTests.m (99%) rename packages/video_player/{video_player => video_player_avfoundation}/example/ios/RunnerUITests/Info.plist (100%) rename packages/video_player/{video_player => video_player_avfoundation}/example/ios/RunnerUITests/VideoPlayerUITests.m (85%) create mode 100644 packages/video_player/video_player_avfoundation/example/lib/main.dart create mode 100644 packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart create mode 100644 packages/video_player/video_player_avfoundation/example/pubspec.yaml create mode 100644 packages/video_player/video_player_avfoundation/example/test_driver/integration_test.dart create mode 100644 packages/video_player/video_player_avfoundation/example/test_driver/video_player.dart rename packages/video_player/{video_player => video_player_avfoundation}/ios/Assets/.gitkeep (100%) rename packages/video_player/{video_player => video_player_avfoundation}/ios/Classes/FLTVideoPlayerPlugin.h (100%) rename packages/video_player/{video_player => video_player_avfoundation}/ios/Classes/FLTVideoPlayerPlugin.m (99%) rename packages/video_player/{video_player => video_player_avfoundation}/ios/Classes/messages.h (100%) rename packages/video_player/{video_player => video_player_avfoundation}/ios/Classes/messages.m (100%) rename packages/video_player/{video_player/ios/video_player.podspec => video_player_avfoundation/ios/video_player_avfoundation.podspec} (87%) create mode 100644 packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart create mode 100644 packages/video_player/video_player_avfoundation/lib/src/messages.dart create mode 100644 packages/video_player/video_player_avfoundation/lib/video_player_avfoundation.dart create mode 100644 packages/video_player/video_player_avfoundation/pigeons/messages.dart create mode 100644 packages/video_player/video_player_avfoundation/pubspec.yaml create mode 100644 packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart create mode 100644 packages/video_player/video_player_avfoundation/test/test_api.dart create mode 100644 packages/video_player/video_player_platform_interface/CONTRIBUTING.md create mode 100644 packages/video_player/video_player_platform_interface/pigeons/messages.dart diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 8209cc9abf1c..76c0a4a9ab9d 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Moves Android and iOS implementations to federated packages. * Update audio URL in iOS tests. ## 2.2.17 diff --git a/packages/video_player/video_player/android/settings.gradle b/packages/video_player/video_player/android/settings.gradle deleted file mode 100644 index bbc9b9dd21d8..000000000000 --- a/packages/video_player/video_player/android/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'video_player' diff --git a/packages/video_player/video_player/example/ios/Podfile b/packages/video_player/video_player/example/ios/Podfile index fe37427f8a74..f7d6a5e68c3a 100644 --- a/packages/video_player/video_player/example/ios/Podfile +++ b/packages/video_player/video_player/example/ios/Podfile @@ -29,10 +29,6 @@ flutter_ios_podfile_setup target 'Runner' do flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) - target 'RunnerTests' do - inherit! :search_paths - pod 'OCMock', '3.5' - end end post_install do |installer| diff --git a/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj b/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj index d19d578b6fb4..2596398e0ff4 100644 --- a/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 46; objects = { /* Begin PBXBuildFile section */ @@ -15,28 +15,8 @@ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; B0F5C77B94E32FB72444AE9F /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 20721C28387E1F78689EC502 /* libPods-Runner.a */; }; - D182ECB59C06DBC7E2D5D913 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7BD232FD3BD3343A5F52AF50 /* libPods-RunnerTests.a */; }; - F7151F2F26603EBD0028CB91 /* VideoPlayerUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = F7151F2E26603EBD0028CB91 /* VideoPlayerUITests.m */; }; - F7151F3D26603ECA0028CB91 /* VideoPlayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F7151F3C26603ECA0028CB91 /* VideoPlayerTests.m */; }; /* End PBXBuildFile section */ -/* Begin PBXContainerItemProxy section */ - F7151F3126603EBD0028CB91 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 97C146E61CF9000F007C117D /* Project object */; - proxyType = 1; - remoteGlobalIDString = 97C146ED1CF9000F007C117D; - remoteInfo = Runner; - }; - F7151F3F26603ECA0028CB91 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 97C146E61CF9000F007C117D /* Project object */; - proxyType = 1; - remoteGlobalIDString = 97C146ED1CF9000F007C117D; - remoteInfo = Runner; - }; -/* End PBXContainerItemProxy section */ - /* Begin PBXCopyFilesBuildPhase section */ 9705A1C41CF9048500538489 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; @@ -71,12 +51,6 @@ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; B15EC39F4617FE1082B18834 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; C18C242FF01156F58C0DAF1C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - F7151F2C26603EBD0028CB91 /* RunnerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - F7151F2E26603EBD0028CB91 /* VideoPlayerUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VideoPlayerUITests.m; sourceTree = ""; }; - F7151F3026603EBD0028CB91 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - F7151F3A26603ECA0028CB91 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - F7151F3C26603ECA0028CB91 /* VideoPlayerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VideoPlayerTests.m; sourceTree = ""; }; - F7151F3E26603ECA0028CB91 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -88,21 +62,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - F7151F2926603EBD0028CB91 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F7151F3726603ECA0028CB91 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - D182ECB59C06DBC7E2D5D913 /* libPods-RunnerTests.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -142,8 +101,6 @@ children = ( 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, - F7151F3B26603ECA0028CB91 /* RunnerTests */, - F7151F2D26603EBD0028CB91 /* RunnerUITests */, 97C146EF1CF9000F007C117D /* Products */, 05E898481BC29A7FA83AA441 /* Pods */, 23104BB9DCF267F65AD246F9 /* Frameworks */, @@ -154,8 +111,6 @@ isa = PBXGroup; children = ( 97C146EE1CF9000F007C117D /* Runner.app */, - F7151F2C26603EBD0028CB91 /* RunnerUITests.xctest */, - F7151F3A26603ECA0028CB91 /* RunnerTests.xctest */, ); name = Products; sourceTree = ""; @@ -184,24 +139,6 @@ name = "Supporting Files"; sourceTree = ""; }; - F7151F2D26603EBD0028CB91 /* RunnerUITests */ = { - isa = PBXGroup; - children = ( - F7151F2E26603EBD0028CB91 /* VideoPlayerUITests.m */, - F7151F3026603EBD0028CB91 /* Info.plist */, - ); - path = RunnerUITests; - sourceTree = ""; - }; - F7151F3B26603ECA0028CB91 /* RunnerTests */ = { - isa = PBXGroup; - children = ( - F7151F3C26603ECA0028CB91 /* VideoPlayerTests.m */, - F7151F3E26603ECA0028CB91 /* Info.plist */, - ); - path = RunnerTests; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -226,43 +163,6 @@ productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; }; - F7151F2B26603EBD0028CB91 /* RunnerUITests */ = { - isa = PBXNativeTarget; - buildConfigurationList = F7151F3526603EBD0028CB91 /* Build configuration list for PBXNativeTarget "RunnerUITests" */; - buildPhases = ( - F7151F2826603EBD0028CB91 /* Sources */, - F7151F2926603EBD0028CB91 /* Frameworks */, - F7151F2A26603EBD0028CB91 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - F7151F3226603EBD0028CB91 /* PBXTargetDependency */, - ); - name = RunnerUITests; - productName = RunnerUITests; - productReference = F7151F2C26603EBD0028CB91 /* RunnerUITests.xctest */; - productType = "com.apple.product-type.bundle.ui-testing"; - }; - F7151F3926603ECA0028CB91 /* RunnerTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = F7151F4126603ECB0028CB91 /* Build configuration list for PBXNativeTarget "RunnerTests" */; - buildPhases = ( - E9F7B01F913C69934A6629F6 /* [CP] Check Pods Manifest.lock */, - F7151F3626603ECA0028CB91 /* Sources */, - F7151F3726603ECA0028CB91 /* Frameworks */, - F7151F3826603ECA0028CB91 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - F7151F4026603ECA0028CB91 /* PBXTargetDependency */, - ); - name = RunnerTests; - productName = RunnerTests; - productReference = F7151F3A26603ECA0028CB91 /* RunnerTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -275,16 +175,6 @@ 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; }; - F7151F2B26603EBD0028CB91 = { - CreatedOnToolsVersion = 12.5; - ProvisioningStyle = Automatic; - TestTargetID = 97C146ED1CF9000F007C117D; - }; - F7151F3926603ECA0028CB91 = { - CreatedOnToolsVersion = 12.5; - ProvisioningStyle = Automatic; - TestTargetID = 97C146ED1CF9000F007C117D; - }; }; }; buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; @@ -301,8 +191,6 @@ projectRoot = ""; targets = ( 97C146ED1CF9000F007C117D /* Runner */, - F7151F3926603ECA0028CB91 /* RunnerTests */, - F7151F2B26603EBD0028CB91 /* RunnerUITests */, ); }; /* End PBXProject section */ @@ -319,20 +207,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - F7151F2A26603EBD0028CB91 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F7151F3826603ECA0028CB91 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -364,28 +238,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - E9F7B01F913C69934A6629F6 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; F31A669BD45D5A7C940BF077 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -417,37 +269,8 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - F7151F2826603EBD0028CB91 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - F7151F2F26603EBD0028CB91 /* VideoPlayerUITests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F7151F3626603ECA0028CB91 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - F7151F3D26603ECA0028CB91 /* VideoPlayerTests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXTargetDependency section */ - F7151F3226603EBD0028CB91 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 97C146ED1CF9000F007C117D /* Runner */; - targetProxy = F7151F3126603EBD0028CB91 /* PBXContainerItemProxy */; - }; - F7151F4026603ECA0028CB91 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 97C146ED1CF9000F007C117D /* Runner */; - targetProxy = F7151F3F26603ECA0028CB91 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - /* Begin PBXVariantGroup section */ 97C146FA1CF9000F007C117D /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -616,62 +439,6 @@ }; name = Release; }; - F7151F3326603EBD0028CB91 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = RunnerUITests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_TARGET_NAME = Runner; - }; - name = Debug; - }; - F7151F3426603EBD0028CB91 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = RunnerUITests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_TARGET_NAME = Runner; - }; - name = Release; - }; - F7151F4226603ECB0028CB91 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 6CDC4DA5940705A6E7671616 /* Pods-RunnerTests.debug.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = RunnerTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; - }; - name = Debug; - }; - F7151F4326603ECB0028CB91 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 2A2EA522BDC492279A91AB75 /* Pods-RunnerTests.release.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = RunnerTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; - }; - name = Release; - }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -693,24 +460,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - F7151F3526603EBD0028CB91 /* Build configuration list for PBXNativeTarget "RunnerUITests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F7151F3326603EBD0028CB91 /* Debug */, - F7151F3426603EBD0028CB91 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - F7151F4126603ECB0028CB91 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F7151F4226603ECB0028CB91 /* Debug */, - F7151F4326603ECB0028CB91 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; /* End XCConfigurationList section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 9a3697b45b98..02739bb7c502 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -4,6 +4,9 @@ description: Flutter plugin for displaying inline video with other Flutter repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 version: 2.2.17 +# Temporarily disable publishing to allow moving Android and iOS +# implementations. +publish_to: none environment: sdk: ">=2.14.0 <3.0.0" @@ -13,16 +16,20 @@ flutter: plugin: platforms: android: - package: io.flutter.plugins.videoplayer - pluginClass: VideoPlayerPlugin + default_package: video_player_android ios: - pluginClass: FLTVideoPlayerPlugin + default_package: video_player_avfoundation web: default_package: video_player_web dependencies: flutter: sdk: flutter + # Temporary path dependencies to allow moving Android and iOS implementations. + video_player_android: + path: ../video_player_android + video_player_avfoundation: + path: ../video_player_avfoundation video_player_platform_interface: ">=4.2.0 <6.0.0" video_player_web: ^2.0.0 html: ^0.15.0 @@ -31,4 +38,3 @@ dev_dependencies: flutter_test: sdk: flutter pedantic: ^1.10.0 - pigeon: ^0.1.21 diff --git a/packages/video_player/video_player_android/AUTHORS b/packages/video_player/video_player_android/AUTHORS new file mode 100644 index 000000000000..493a0b4ef9c2 --- /dev/null +++ b/packages/video_player/video_player_android/AUTHORS @@ -0,0 +1,66 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md new file mode 100644 index 000000000000..626b9ef887cf --- /dev/null +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -0,0 +1,3 @@ +## 2.2.17 + +* Splits from `video_player` as a federated implementation. diff --git a/packages/video_player/video_player/CONTRIBUTING.md b/packages/video_player/video_player_android/CONTRIBUTING.md similarity index 100% rename from packages/video_player/video_player/CONTRIBUTING.md rename to packages/video_player/video_player_android/CONTRIBUTING.md diff --git a/packages/video_player/video_player_android/LICENSE b/packages/video_player/video_player_android/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/video_player/video_player_android/LICENSE @@ -0,0 +1,25 @@ +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/video_player/video_player_android/README.md b/packages/video_player/video_player_android/README.md new file mode 100644 index 000000000000..28bd66f89c64 --- /dev/null +++ b/packages/video_player/video_player_android/README.md @@ -0,0 +1,11 @@ +# video\_player\_android + +The Android implementation of [`video_player`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use `video_player` +normally. This package will be automatically included in your app when you do. + +[1]: https://pub.dev/packages/video_player +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin diff --git a/packages/video_player/video_player/android/build.gradle b/packages/video_player/video_player_android/android/build.gradle similarity index 100% rename from packages/video_player/video_player/android/build.gradle rename to packages/video_player/video_player_android/android/build.gradle diff --git a/packages/video_player/video_player/android/gradle/wrapper/gradle-wrapper.properties b/packages/video_player/video_player_android/android/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from packages/video_player/video_player/android/gradle/wrapper/gradle-wrapper.properties rename to packages/video_player/video_player_android/android/gradle/wrapper/gradle-wrapper.properties diff --git a/packages/video_player/video_player_android/android/settings.gradle b/packages/video_player/video_player_android/android/settings.gradle new file mode 100644 index 000000000000..00681714f7d8 --- /dev/null +++ b/packages/video_player/video_player_android/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'video_player_android' diff --git a/packages/video_player/video_player/android/src/main/AndroidManifest.xml b/packages/video_player/video_player_android/android/src/main/AndroidManifest.xml similarity index 100% rename from packages/video_player/video_player/android/src/main/AndroidManifest.xml rename to packages/video_player/video_player_android/android/src/main/AndroidManifest.xml diff --git a/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/CustomSSLSocketFactory.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/CustomSSLSocketFactory.java similarity index 100% rename from packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/CustomSSLSocketFactory.java rename to packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/CustomSSLSocketFactory.java diff --git a/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java similarity index 100% rename from packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java rename to packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java diff --git a/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/QueuingEventSink.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/QueuingEventSink.java similarity index 100% rename from packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/QueuingEventSink.java rename to packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/QueuingEventSink.java diff --git a/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java similarity index 100% rename from packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java rename to packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java diff --git a/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerOptions.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerOptions.java similarity index 100% rename from packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerOptions.java rename to packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerOptions.java diff --git a/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java similarity index 100% rename from packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java rename to packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java diff --git a/packages/video_player/video_player/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java similarity index 100% rename from packages/video_player/video_player/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java rename to packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java diff --git a/packages/video_player/video_player_android/example/.gitignore b/packages/video_player/video_player_android/example/.gitignore new file mode 100644 index 000000000000..d3e68fd01e5d --- /dev/null +++ b/packages/video_player/video_player_android/example/.gitignore @@ -0,0 +1 @@ +lib/generated_plugin_registrant.dart diff --git a/packages/video_player/video_player_android/example/README.md b/packages/video_player/video_player_android/example/README.md new file mode 100644 index 000000000000..8ceb0ff485fa --- /dev/null +++ b/packages/video_player/video_player_android/example/README.md @@ -0,0 +1,8 @@ +# video_player_example + +Demonstrates how to use the video_player plugin. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](https://flutter.dev/). diff --git a/packages/video_player/video_player_android/example/android/app/build.gradle b/packages/video_player/video_player_android/example/android/app/build.gradle new file mode 100644 index 000000000000..7b3c7db80c7e --- /dev/null +++ b/packages/video_player/video_player_android/example/android/app/build.gradle @@ -0,0 +1,66 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 31 + + lintOptions { + disable 'InvalidPackage' + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + defaultConfig { + applicationId "io.flutter.plugins.videoplayerexample" + minSdkVersion 21 + targetSdkVersion 29 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + testImplementation 'junit:junit:4.13' + testImplementation 'org.robolectric:robolectric:4.4' + testImplementation 'org.mockito:mockito-core:3.5.13' + androidTestImplementation 'androidx.test:runner:1.1.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' +} diff --git a/packages/video_player/video_player_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/video_player/video_player_android/example/android/app/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..9a4163a4f5ee --- /dev/null +++ b/packages/video_player/video_player_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/packages/video_player/video_player_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java b/packages/video_player/video_player_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java new file mode 100644 index 000000000000..0f4298dca155 --- /dev/null +++ b/packages/video_player/video_player_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java @@ -0,0 +1,14 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface DartIntegrationTest {} diff --git a/packages/video_player/video_player_android/example/android/app/src/androidTest/java/io/flutter/plugins/videoplayerexample/FlutterActivityTest.java b/packages/video_player/video_player_android/example/android/app/src/androidTest/java/io/flutter/plugins/videoplayerexample/FlutterActivityTest.java new file mode 100644 index 000000000000..45cf5c6e9903 --- /dev/null +++ b/packages/video_player/video_player_android/example/android/app/src/androidTest/java/io/flutter/plugins/videoplayerexample/FlutterActivityTest.java @@ -0,0 +1,19 @@ +// 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.videoplayerexample; + +import androidx.test.rule.ActivityTestRule; +import dev.flutter.plugins.integration_test.FlutterTestRunner; +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.plugins.DartIntegrationTest; +import org.junit.Rule; +import org.junit.runner.RunWith; + +@DartIntegrationTest +@RunWith(FlutterTestRunner.class) +public class FlutterActivityTest { + @Rule + public ActivityTestRule rule = new ActivityTestRule<>(FlutterActivity.class); +} diff --git a/packages/video_player/video_player_android/example/android/app/src/main/AndroidManifest.xml b/packages/video_player/video_player_android/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..a2574c90d7d9 --- /dev/null +++ b/packages/video_player/video_player_android/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + diff --git a/packages/video_player/video_player_android/example/android/app/src/main/res/drawable/launch_background.xml b/packages/video_player/video_player_android/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 000000000000..304732f88420 --- /dev/null +++ b/packages/video_player/video_player_android/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/packages/video_player/video_player_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/video_player/video_player_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/packages/video_player/video_player_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/video_player/video_player_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/packages/video_player/video_player_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/video_player/video_player_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/packages/video_player/video_player_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/video_player/video_player_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/packages/video_player/video_player_android/example/android/app/src/main/res/values/styles.xml b/packages/video_player/video_player_android/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 000000000000..5691c756a6bc --- /dev/null +++ b/packages/video_player/video_player_android/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,6 @@ + + + + diff --git a/packages/video_player/video_player_android/example/android/app/src/main/res/xml/network_security_config.xml b/packages/video_player/video_player_android/example/android/app/src/main/res/xml/network_security_config.xml new file mode 100644 index 000000000000..043e5ce55a2b --- /dev/null +++ b/packages/video_player/video_player_android/example/android/app/src/main/res/xml/network_security_config.xml @@ -0,0 +1,7 @@ + + + + www.sample-videos.com + 184.72.239.149 + + \ No newline at end of file diff --git a/packages/video_player/video_player_android/example/android/app/src/test/java/io/flutter/plugins/videoplayerexample/FlutterActivityTest.java b/packages/video_player/video_player_android/example/android/app/src/test/java/io/flutter/plugins/videoplayerexample/FlutterActivityTest.java new file mode 100644 index 000000000000..434861f4b754 --- /dev/null +++ b/packages/video_player/video_player_android/example/android/app/src/test/java/io/flutter/plugins/videoplayerexample/FlutterActivityTest.java @@ -0,0 +1,50 @@ +// 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.videoplayerexample; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import io.flutter.embedding.engine.FlutterEngine; +import io.flutter.embedding.engine.FlutterEngineCache; +import io.flutter.embedding.engine.FlutterJNI; +import io.flutter.embedding.engine.loader.FlutterLoader; +import io.flutter.embedding.engine.plugins.FlutterPlugin; +import io.flutter.plugins.videoplayer.VideoPlayerPlugin; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@RunWith(RobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class FlutterActivityTest { + + @Test + public void disposeAllPlayers() { + VideoPlayerPlugin videoPlayerPlugin = spy(new VideoPlayerPlugin()); + FlutterLoader flutterLoader = mock(FlutterLoader.class); + FlutterJNI flutterJNI = mock(FlutterJNI.class); + ArgumentCaptor pluginBindingCaptor = + ArgumentCaptor.forClass(FlutterPlugin.FlutterPluginBinding.class); + + when(flutterJNI.isAttached()).thenReturn(true); + FlutterEngine engine = + spy(new FlutterEngine(RuntimeEnvironment.application, flutterLoader, flutterJNI)); + FlutterEngineCache.getInstance().put("my_flutter_engine", engine); + + engine.getPlugins().add(videoPlayerPlugin); + verify(videoPlayerPlugin, times(1)).onAttachedToEngine(pluginBindingCaptor.capture()); + + engine.destroy(); + verify(videoPlayerPlugin, times(1)).onDetachedFromEngine(pluginBindingCaptor.capture()); + verify(videoPlayerPlugin, times(1)).initialize(); + } +} diff --git a/packages/video_player/video_player_android/example/android/build.gradle b/packages/video_player/video_player_android/example/android/build.gradle new file mode 100644 index 000000000000..c21bff8e0a2f --- /dev/null +++ b/packages/video_player/video_player_android/example/android/build.gradle @@ -0,0 +1,29 @@ +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.0.1' + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/packages/video_player/video_player_android/example/android/gradle.properties b/packages/video_player/video_player_android/example/android/gradle.properties new file mode 100644 index 000000000000..a6738207fd15 --- /dev/null +++ b/packages/video_player/video_player_android/example/android/gradle.properties @@ -0,0 +1,4 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true +android.enableR8=true diff --git a/packages/video_player/video_player_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/video_player/video_player_android/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..b8793d3c0d69 --- /dev/null +++ b/packages/video_player/video_player_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 23 08:50:38 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip diff --git a/packages/video_player/video_player_android/example/android/settings.gradle b/packages/video_player/video_player_android/example/android/settings.gradle new file mode 100644 index 000000000000..115da6cb4f4d --- /dev/null +++ b/packages/video_player/video_player_android/example/android/settings.gradle @@ -0,0 +1,15 @@ +include ':app' + +def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + +def plugins = new Properties() +def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') +if (pluginsFile.exists()) { + pluginsFile.withInputStream { stream -> plugins.load(stream) } +} + +plugins.each { name, path -> + def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() + include ":$name" + project(":$name").projectDir = pluginDirectory +} diff --git a/packages/video_player/video_player_android/example/assets/Butterfly-209.mp4 b/packages/video_player/video_player_android/example/assets/Butterfly-209.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..c8489799f549457b017664d6eb7ccbd82ae5bff4 GIT binary patch literal 2429896 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

B*|L2|Ac!qBB* zo#SXfy8SEoD;*@Xum9UtwF{TzXH&6DlsSpaq*wka;&tNf#TnAhbt$~A4flT991u7{ z$pDCU+_%=5;n)y+^t7YIqe?<~3JD?;TXlKAn?k^wtRFKrX2%^`%P@u-vd2DXbXZH} z67l$v;8yu?-|xcqKRtECGb7f&Q?qtmxkR_-vf6m%vD1M+;MKJJEzE#K^2vZh9>vTa zHce_z+o}QBdlTI;*>*O-A=KES@(uD$9)*-hWt-bTWmPe4duoGzwdY>#Xzz_kzPaUN zA}3ph&+b89f8Q@x8!fS9%-BKx}c~&Z#`inivfQ+fX$dl*pc1l!MJ>7C# zlP&gNFM7<583Lv@B+k8+cP{Ksl{#WT+r>YRM-B5*xh>W_8kJ%& z zB^cDXpJ%t*hg3Yw8wMow(E!w$tZ?~JWMTH@7(I(*$!fxy2kUEjJ94LXCPZ(;D7wk! zr+w{~FxKB*Lf${?;xSGu|G|_OW2$5}3@NjqywC-7)2=_cA&5YyQ2ZyUufpB@}D6m$(5v*RUXog1i6xc9>u~1C9$`bIzJWC)rR_#Tx6@ z$lz_#BaXQ>)}SkMuX9?H2%+vP)3aUX9cT5q&u~2TCPe+!PCJ&O zSm;2?Y05gaKR9Q=_)lvcr_1iO3#&;@YS{PizwvFA#&{v$V8{7s!H`AsEacj_9LqWt zPwCL@qjV2gPpELpJi6I+mE`43p+z|s;M-}}^vY4VM-67=#h>K;(>fL(AT-_n2-;*M z$(Cx-_#llr-rnQ)XGLBH*=N9^KNV*niHQ(E6l< zdKRw&X@yykXxO#c7D{foUVH9s*FjP|j2Km@r9Xt#TZU)0te`JKPBG!qG6Dkk>DCvi z*s2-gReo!~d}S;>32V$9M2faU@^m48cF7Hup`jK8veNByo$gIQ61`--%LWk)muXpt zIZSzn_F~7q$w(Pi#?QkxWVswF4#)bF^!|g$&8jjnInFrg8tsKTPPSH}*UkNJ|*~#j# zWG)IMTpJAFwuX*k1MXlw=Y|mEF`>ab-&M&t-hK07ZKykm#uMKyM&!i9GY>!T!7V6^ zGCnZREgax^32i9e0K>CCcrav_*8%(p53Y;s$_Muw=^?(bq^Ma)!`)%Asp3;>vAL6n z{5tLP<(YmPLpAFRD$fO`75gcp?j-MezXY+A^7T-`IN<8{HJ+9T(oRI=ih}53+sbJ( z5hFnJpBddlB7yS(UF49)a|-ly>k&k^Uo3BzX}7wabC8YvhtqX$>%!4zXjC;R0iR(! z+pog@Zn7^2WA~`fv9ruepapMP%dyl`oqMQsL)yp&?1e9nj|fhAso^Bg^clS&Gwv-i zQY}PB?y{o071)>NTO`eX4dMMx>$9zkj~7>$g*RBC!rI$@CMkTqO_;KZj!%Mu6}3(- zPP)I|tiY+O(|x4ESP33DOR<4=o<9sAg<8#6oaiZ_hc#XS|J~~tf3l(lL`>aubKF-DSz0EO zuQp0{g#2TY#QLSaZ{?PDTc6Zz;1Pb~25i+kVh$8?EiE6_Gk-vP#|9T%{}Gr2mSREX zYp2f251}-Oy34P_mTzc!b|f;NI9R&@A{@xH$NqM5J2@5X-iyw+sv&FEf$PV|?qcX4 zASm;mu70bP_a%;puqYu{Ul($qx^T9QUPFeceOdEpH;4ZJ(nf6tu2RQ

mQCCR=mr zdJV+qF)VXII!-+H65lLWE{PaC^3x=&!kd7y90R`_CD$Q-VTaH~s zOi$(F5&o3SaetH#_@6^hfNP&#cSz`dMog8@g^Slw22BghQ=lA7FPK%ZaZt_vIt50R zOujunxF;;DGa+xrMWN$(BtutCNlg~B+->@~zI7gnxnxAfEavDhKd)*I-+F{Dt~L#fYUXz*?A$k^RuVu7e`BM%{)NTQUZ8q zsIAa<{{OU99b;pQn9VS{gp-M&1|xip|6N_owq#*i{nMy|IGH{C+3R1;6VF#KRIuc9 zb$0pAS=sx>HE<9Q`|KqP$$aA*U0^D7IONc!o`HZ!4`v#*bX&VrG;nC)3c;P=WY)6Z zlH`c0GtJTk2$kLwQj!M(2|Y_qRBR1bVR8 zbjr-!{8V?<2~cStyJtisJO>`>{ifZVY1v&0#BGLnez!t9S0!Q#BLNcOs94Jt8&+0_ zeG8Q7k*=*h&vFu%>E+Qr($z*U!2JX6sgvYJT#yCXuY0VVj*m10+a1AzF2mG%655)B zQ7V+$hD3XSM(z_Y_<&%84_%@0LwE4@nny4p?j)rSO-0x*FmsB2%Ot97Pm?QV(%aYX z1Z0A`G{eV36dUvYEHvJvC+7#BU3S~T{{km1U4TX(=(XeDhNIR;V}dLA+TP_RXBTBU z$;ZQzw}>%XwcH(=a@LG0b!RkYHmk$BcvEzc;hIuM44cEurm1%3z6zNyvr%P|PfCS| zm3j+#M{gm~`~F=2=qNGNhI^}-Tppz^Wq(y!dX+6o9*N+}Jp_|^v0X<5BRds{E=b@X z^Mh7NCHi9Z14wiIuWGl>OLK3B>FW?237b~IyJN8q$$Y^}+(Jm0X&zhE@!s_+$cvgT zIL8Y1>3cP^p0_&L#TQM5rqAUJ2zj+UG;3x`X?Kio=bTX?e4Xq{6W-e6_=S#bN%~R; zo`q~AyraMnc*<73n&iQj*#j_5Bb$Z!pIftvLUb{?bmUlYW0A9h|JRYmOo_dch_G$shBeI4r@Bg>LE+?TjK&2R5Vv5d@z^@qpOLx%^y!qr)GIDV6 z+quu|do+YL#ax0@!2yYFm^LA9_KQr88oEU5M)EE4xA9xm=nd)0Hhe`r@5jr9l1S-+ zEy+|vB2FNB!3h#YQMN>+ZPZ?4!zK^;=M*Eukyefs z}T%7o~RhE^?wUP+2k&@|d#i)cmr^#>EQVlYPVi*{z!_ByF%FY+XFkL1{i z%cJYwe|AqO6^A+4N+7R{`#{I+8El`FjHNs1j9nvd{1dbAt8Tb^{>}RXf@Jba^W$jT zKyykhonOOHBym9IdzMP?LUy4D>obV+RUTo&KF`&B0f!`>*2T{~^{=c;>oF(rD{%$u zv4?!ZC8@q&&+tQ(Fo!nrBo%?2aRge9A0sFMmx}?{oiW01)OPD| z6~JNwlgc(`!yo@R~U{+QJH_yrh4;IWU;94 zK{xreK7TrUeuKlj;LBME?S*cl&QY5kzPMh)_4B+jOF2??LP}m{LyiJHE8=F*=y0W! zw{4qufqH-5JhD_`_GIiQ(;liLfWN(`tbV36TPl$U?l_6r6snOps1t<$;NV$pZ>Er%M8L{2)F$a}v1IL};p zdhiTsFO@*K@OkLNBsv4)M{Uid-B%|_x_Aw$SzJ|$31m7PgYAXdd;H<+`)%v+o)OG) zswVsMnceE)LF)8KG-#(kl5iR^Qrt^kO&g`EQGaL>d}^&`AblsW)q36w^IO-7nuC}u z_^duA9VyW{(rl{kd29D=d70qPK-*X%X(Dvm=>&7Q8yJ3wNe38rX{`y(O`e_weDH>N z%GIFrLn7(m`Q;2)vfo>i_L~dXx9MO$90cpt8GA(DSXy`VS{mcc7oKEDmRA* z;b@~f=N#$+Y$4HnTeum=H6UrSz1(*N9FYDNCJ*iWWsN1xKIo~1%hs!bcgUr z=~GP)gePMvPpEPZlW;!5K?vdSA*7pv;N#Uz5qV66i`-Hif2DIqg!0KRYEN&K!n|Z5 zW;wDE|Fhj^OAwmXt?X14)+kt>xDvZ1Pa{KH$kM z)PyuXw|-L3W}`CLQI-9dQ}{*CbJkgHzoFY7I5vk44Fj#C*9%6bBLdQgEjtchgN~2X z+OR>p39^JaAHFa=WnAhOI&}5F7yx}_V5eqlxbGuw3AE^`9q%kGfCrC+Ljlh4zZTZI zi`!9Cj5p+HP*WX8?c2V(V_QLHe$K=Akb%ILQ}z8m^1z#NwxgGLn|dlu(W?#Rb~S%@ z@ZM@zAdu{`LQ$#LKe6`NLk!zm^pC;w&<;pMi2vr^6{p89z-qyr!2X^SrP2FV7*N|u zL5(yWGPsXQ85$;DDlDj3$Y{kz-fYJx=ji-3l5yhLq!W8>-KMW&NJ4~3(RiO zvd8O#b4IB5ze2^P(IVh-pDD3LZ3c$rVWI4D`*+q+sSOn-v6I(l-F08TZ`z@tCU)xs0>DtnzfK8b>E$ zY9HMzDQgvrYxwAM8XoZG6GB%;5uG&%o-3rDNdm*PnvC)le90)1?$h%eY@VWz} zB5c1c{^_e4+v=`Cs|eSAe?nbdNv9@@Ncvgsw8n_N`twd`gLq0kg3P#lySDV4HY0I^ zmkXAWnZScd-Yd2>)$Ihs!3ml=jPObPx2tQM#ncGwP@%fI(@6W6$fRm2$ND)H;0~Un z20%dguK zHOLh$3h0mB0-=YNZyD0!eUHf=l|BL83`@jm&=dFilwr)2(k)(4k}E6!IlSqCEeSCs z8Ayyt)GQdx%M@^I(K_Qp%{&qOmQYjhiyAD3_g5RkQQK)lG zKD^FFP5H(Rp6JkK-q2gJyso?!#uycJ7)E-qkUg&C4m@cKT#U%$`U>~E$-Ur9%{c?Z z>zkxv5>Q)vCk$8DuI_hUq83o8AFa)01kAoR+GZ10s+l z%l{Ka$co}-hKg=#yeQxBX{80IN|PN7HiW`vHkeZX`~R`7N=Pz{bqa1g?|F`|DiUSY z9d(*%PjzIx>)VM+(q$`IA-33;V%0Kk%g%0r~*O(^leCX zK;YpF{QQa8KOKWO#o#BBzhrpBC^&KDbrN%qOC3qKfbKf;+zizs!-VDMs@#nve+u7?DfuQsv*?pMZH1U*=mp2gV==8y&v*v1My^~l|9o} z1*CW!>~)Lg;yfP?FbIRDV`o7i*ascbVD>3)ea+JK%y&HK`q#!riZnQWjDyLI`8HvP zqCm|?lJSFE!lup9dyTer7NI2rKpj@qj;kx6(H_|WB#ly>=N(n;7-Ur0z_)+Yb|Q*> zvcuj*qoBNY#_%aXZi6=D?kiSjYG5}Vf?Fg`({%!%F28pI%X^Y-@$H260UO!_Z|^iV3WR z!ht=&$LJVPqOUx^X`2lb*m%BLW8cALbq1` z-UEA%#h{jkk!v5)FjPE>l<^+BN5wQ{L&MTKmrjuX;+=Za@Q4HXI|b2uQTNq#Hokl| zp26A5FZDI)ow51NKdy9%)+`8pW;Fjb>TRgKq8T;i4&8WRqvX3jTp_FR9!dIN&6AYc zC9sLO#pFOFvZ4~jeAVo{(RSR~cOyAHV=^)DH@%2r$@Ok$V80@{%qDNJ5$ zlX+M->J()>sO;TbJ$SPPgzUbcP}MpPTjZZdYK=tzM+NsJr1m7^cV1-JwH{Kge(jpwv|Q4W*QJ2FnDYNX zK}AtVCML&CReti6QGdi98y@PHm0iNwY6D7t^VGI^H;tVeM-adZ;8X?xL1!Dp+{5B1 zp|n}f{_Jo90gF)uTGR8PrtT!156(t@@*N2bio3W@u`ofT)$n=}07a)IH;sQ$pKRf8 z>bQNu9r&cxQ=pk{Zee6qgOd@dv6uFP=53(QUhDoVD`C}%wcJN`-^Bo(8ZK&?3)WfC zyM7c`b`JJ+7~k7bYU=4Qr^7=?^2*Vk3+9ixa3s}yfnssxfx4z(nyX)7ZVB)rr@l~5R_rp2J2t~EZZ)ijKE?5 zcBL)yy6$&$cTd}ztny07ZKjsL55k#$92}DDW+;7M?=ZS(aCY#>w#Rg=L96Wx<(9nx zCs+M&r_>2>@Kcn#!Dz|$iW_&cv>=aoe^HbJ#YgPg{-0f;8V6iWwHO%UKDEzSNbp6b zx*(wDz!EBL&tiSa5qp=~Uapw0#{6c7RPIb{63Ym`pq+M$cRIMDWF6ZDvFbA(4Q@<* zOISUoO9nG8+0Hu9u)K^>yvxql3^8vAEXg4bMIDcr!X}N_7kne^Aqe6bf_=&P!<3f; z9wwhBfzu5aGv7!odvr@f9xa}v&5K9z5NK)ZG2!u zj_LG0C0(X@bT2@%30Ct`JOV|H;(Ds*nPgU?_ZfuQ%Zev%0?0=x2;O7^tu#~MC8dou zFwjPom`Y!Pd)~=*m4dnH0AK*Ni9U1yD#d;k=qNoqHt#Fcr8CmRB8%4*e1Y+GKZGnY ze$veN6kM9VV=gVm7#<}8X%yQwTM&}|tnE$0h8C16d}(x4o~)k^<86#~Z*T+R(C-;U z-fC?IQ3&=5h1QiZKyHQ}m6rf|Pe!og6uJ0|D(9m30JxOVi8y=`3nFu`{$K!aT*!w-e` z59RoQ*V0(OcXfLf^ogzgGSbKAJc;fqImtdLl3lx0y_r^Fps?86qD@+#F;`3F-w8N0 zq`YH{7Cy@##}EJB*lMhf1By;x-uccmV?L#kFMqntwN7@r{bII5#ik&ipx3P1TTKzn z$0BebxTrG~AMci>dK)PlO}+htyP;$XXgJiQ4ge`rVGe_9QkufTM-ncaDtkLqt#f5X(8UQhy_z~^O{|&cS4*L=w&58b)^i=i^vS^LN zumNmu?x?Bf7*`G&bV&Q{gQMa}t{r>07_oA6`3y9pbx`+12|9m{3-3nxVNd8y=TviK zZt``qznghh0xBgx^KbnNUIUDv&D8vIP-LQ3N%8?|@ML;l0%#GqlEY9fz4+R}`@yzl9%mUNpz^nky`|fNPLz8Z{Lkb@ zxE_t?1D}6;8>i}wb51E|P$X^t#{YdROrGS``Vj89Pb|qQgYIJZrXtBy0&HyTby*;b!D=59wQvE;FO)*O{eU-U8$&I!cqatQ$Uboq(ZTkl(YItdX;gNag%EfC zp(XlkNQffUDiqOgj0cxIX+-k>2$7t>eDQbD7iRFRo?^K(m5Dx&W-WHy7!)McL9_I| zBAJa3YCH+H@FS-4l=$&7NSX1%Eo0H;!fK>PqM@2~Qe3;#pH#IVNp&%Pb;LWCm{dwN zo_#j617kTrPfw}^1!ujwC)o`Pe-oDB`_Oh#t|)jC_<{CVVm2O6BApv279Z#8{i6;}tr z8pgVpAR3C42m+i`99AD6!7%PmJ5_kDeb}{|f(Fc6gy_-lm-6=HrsFDW#NsOiYM!CR zRCLSt)9MK0_~78b`VUco-P_^ob{tidXEbSp8<^44e~>pKly6B%QGJjJW*3f zpGE%WbW>yv!Oyd=y5M$&{L?HzwM`mOfhTVmPQx34E4vV11 zk&@*J_}Rres1`z(C5Ey`<2QPwD?7W!nrs?;mj9PM4+7?3X;kwyKDH7r2@&MRN1va0 zZr(M+ULq=OudBR3YHyGzwetF=WRuwjp5YG&QdW~e4IH2J`^Eppl(=vojhz3aX_ z900TzL`38_b~|;f!RqcUzq$(cO_AYn5%r<@*Il6RjadyXq(KNRs>4IAe^=~3uzb*i z8Oe`~+A$pW%RSzUfVn1daVzKW0^jk0%EGtPF9t8DQA2z{z?RAOD24*hh$_NT5DHn~ z_12p(UIS!XnZBZ9sK`8;lpV%H^9P!NFrqWz^$4-|j~d^Qq}W@1GK=N@1`jLQG#v71 zIvOS#Z~#iyB#*DaMgvi)C!WUUtl@-LQ!|e;J#_Q{)^v~A%q&fYsIP9soEj9h7aA#` z%}fSUzhY6fme}qv8r3I@Q{=aqC{s#j$Lv=?dlzJMS>O{tRq80(d967rKsWuzHoxXV zO(Ypj$a-!aS<5K2mJvbGRfv^^Oj9pmM}h$4hI?NMJ0I7Gua751^&-lrh^{y;w*7k1 zSnT&ZRMA56mT2D_H!yEgpOLCiA1KmwMagn+Q+DBBdmto=U8K~#KhU;vN5tdpGR)IZ zrF}m=U&cB9Dt7V5iwq&;{(pyL?m~SxWI79 zvQ@5tBlh6PblIkZ-@@nFA==4lpcM#7sz{HkDSF{wBF|ToV^mhi;$1R}7wDko8E1FM zI*BQ>z+pq=%B(2&7ff{FKj}AqN=cu_n&D_Pqq?*0rKBdTbGO}LargGIrmiJVt8K@N zn8am;Od?F!ZKAg1?o9&B^ZNf3;@4bx;u!dQyvfl*GG!!Cr1pnbgLv_dcUGWSZdi&%9=(z4hYE^$?#XbhA z_s4&&A@~!Q8E84W24Fl1k6yu(?B?F)4Zrg9zFBcWwS0+S001uLL7rr4B>(L;Rd%*( z35>Fa9!1-A2_J2Gj@7U;EYM~NjTJN)%*m5iGAdP2K_s~%YLoE*Dcsv-_Ihfp0qTA4ht?D!pjmc;ZiJ?#Y^;zY;ff zn-X+Ccj9Y%?n=_Zh*tNOTe3PBuD)yVeq#8d1!s>OvA^Z}xye%v7zgx2PQ*t7RfZw@ zJAtx|Ig^UGS%wtifwX1%p z4Um9nvC5-b4e660#eM^H#_?_p30b6zgv|ebbB9n$C4-Htp66o_NJCDjV2}Vl%4DV5 z^g#@nHp>NvQh`zSn)Hwm6JTi;O>Kta9Y%q3{BO4mCey#41c8W?^fmPEKh{lSlYV&! zf>>fp3S)C zqeS(|{wd^y+I>7M*nMj)HPIVLAkZC|MnW?%S^n%PV|X z+>PTwr19guTeK|U@X~RWx8;atrahBvLiobGZE`xRdNYeib1IF`_K6$8<;44^+Y!m@ zjp{q%#J*F`pwHoDzhqqTA@}OywFXi!`LJdwBsXhL4G0-sr)y&g_29# zN6}D5^aMl8#~qW?z_1L>utcBtY1($g0E+N}imBuGx_!(y+JToVwFFx2y-v-u^felq z?>1lg(C6c{_qEkX7M7GAop)!IV&9h1i8CHf!`D-*-s-El>4w7JEo=`E7;??TU z5J`MqvQnEr^@>O|?q^cQnpigm&xF?xgfn!9so0rukt4B@QRJK^LUJ*HV*n-L)SE4iQ&GK z!YI*1Kv9Dq0@AsTFfUrtIWXXcbROl|2GbCr#}rEOpgd%VqM`%o>anD#(OmX<=-t;Wzpq%S1v?u#-_!k?Z;9n-&G&KRm&cj-rgOekg@|l@CX~&5y%fRIhw1f2?Ta#7yrgc)W1 z!CziK^P7m>8OTM#xBd!(^F6rT=((s{t`T1{TCO8+n|LNEfh@f>yE%^QY>*`vA3nA5 zL*5f{5|8D@@?w6bd%+<=#r~gT#Uc5}E2&Y?|Ey((&`~d2UQm7I2{ea zD?ny1Hr0;Rb0H9cVq>enUtwLzQ$Q}cj6uvpH}EO*6xbZ$nbJlu{w2Er>4HI zdY-4%^&u_NWLl!a^wJk3%ubEXvqbjmifFa0I92qsTDh?pCm-#7%{h1UP??H zH6wn_q8cXBFTxfPeQnRw`Lx-R0DAWC6lu%KX<8mO&x<(%?f@0EE6$%hnOKnB8I4z(k- z_{Kg}LI$NS#qy3lEy@w%-d_TTnMD6bfc5s1iBeS{@EwA4v{nfXRJ|$hQ26n57ex5$be%uO!P?u?=Y67rWkh(RqK#HD-&)RZ^5gAz(=nma4z5~ z3(Kot%cxB3e6rDBVSo1}b#^2EbGkh+!cVxn6fe`(e5yUAQ@D#66Ktb0>lp!GKO0v# zi^%F51`z?P2SI6r86=)!AzMb{ z5g^EbcJ&g;M5tCY?s<=RGSwyIR^ev?l>HiU_ocVi+;-V~4~u@FOC3Si1l3;7!|5`! zXlzp6MsVQR@k0tus0=)SlCpMyTN6sVyGl8xex9S8yj(Lj^^gh9T<2E|@?u#fotur< zpqg;NdXM9&*O5$_BT;02ICg>z;j$Etp`gjlk6zk<@N|3Rovx299{(@IoqCY*x#xL3k9&*6Y?j zsP4>LAtvU#eO-pa*<=-7%Ynv7Kdt*VXd#6&<^j&=ryG(nEc7YOLf(%xXWw?aNdAL= zMKeDlfZ<+9*O*?{LG0?hdLGVtPJ*mi=G-RExzpI+BEL@sqb|i{Kph>m8!I2*2rPUR zVHbCMrd&o?p#BUL!;iYgck3_O<3sBsa&as_0~`<*w;N6(sEMU*e-hysr1bqV#Aq2` zE3gcZV-s4~?$q(rT8;RP#7@KeW&D}=fAHI>H3k` zU`kE~7hFWZsH_+t=yE_iBAVDXrh5vxrgKSaIMDJ0>`JH%4{Pg#Fd&o&7R#Z$S=U+( zG^4cNT(9}O^W2VF>$wO*Ub@s{T1-DHn4ji05}smXT2U7w23VdxDlNtmq}<70IeV$eVyky!^sYdmblG|k`rH*m`=yEIK*?}U?`R2JZ#-khO{Q2w`>ue+ z@rT7eF&2or_igW@1b@FJ?zdRp*rQM;4@AZm`KK!sUw0oI&tO;mB!~DIXr<6aG?MF} zq$A-2scRyKa4RU?@=B$jKK83O!MB{uJiO;Mbir_9JNX&{@nD~Q`SJrK<}cT)k#kI% z(d`Bqjc7SK$D;r%K-9nC?hIQvB5F34mz@A@Ni}ZxdECLvdBFhBFhHPt79IhKpu7}- zs^y)HYR41UrP+vK)VsaMMJwaC=%W~#K zAsDJC^)sp&)9wLjA3x4tMS5^)ax9p0#N8wO=vq(J=WWfjt*T zU9PU(9c+_Ufn1lG_c2@>$Zhd(rkbcG~i5M|c6 zF-wBP&{`>|i&KhH9UVO*U|ECRQk>FwW};m+v$1 zqD+iTF|?ETlJ)m3xF7Nbt$Dtyg(q33m1*jm+V7Kls255;+$%c0jCrq3sPl20DEec{ zib9c;mklIVM+iP{N>JI=8XVg-E#-mnD1Bg2x_Tx`l&%uKA12#2brK6!%_4jM=#GVD zWe3Z3&iV^3nj}jPs=7KCv?6*qY?O|lPZ(aAN$ z$b1rbn1@y1lho&p&E3R}t##Ezq`HR-keV*^E&yLp!B&c!!Y#)dqATWP)tM zdhm{oO6@Q(Sp{0aN{f~~*2WmPLp_n|&H^QhMGY|tbN+HFTFjVZzL=6K7Zgqju!cm? zo49a3isk;J5W+EQid!%$vo@2R&$xpH=VdQqtsH8+rFfrEQCcbp#-2Brz2i65l-M~o zDS8&$MFIqjU=p$`%?P5sI%nz$&fznDPS_>IAvCY~oRftxiZ8B<48Z8L!K1ZMy%F+Y z2aqbvLzoI(9lU%FNS@9x(m$)lpw;r**eX@^hgZcI96;SODHz)Ywbfu4Xe^o*%JOzP zi9U5h>5!vASwO~iY^X_ds>%n=!r2Vp+Kcw$#(XP=mOMY7@}4lQUz(6+%xd5-IG9i< zFTQj>6&y~hk09ORP_0}7&OrH_J-Qu8eE4sgqm9|lTsSBI`Lrhu8T=LNQG+5puDpH1 zsM@fkIol)yF~!T9qDe)1dy#M>n#E-;JsAa1R-?gCvA@Xm4M6^T`!LQY#Im1;bUE5GtQR==}~v*&nSDUW7WLgV9XIlOcCBV-%oMTssY>=?hLR7OV9%y$gBU@ zW@oK^mq^5F2h`j-mrxU`WQP>#L#^NYG}$56nf}K%mZCPQQD&$!JztNPK2js;`d@du zZoE)fwRCZR{~!e*1j_Xh}fN?<6=L(|$A3D?%>Qx|1mq&JJi=_dHdbVyR${jY5+(Isx$NNbWaO$vgnW zO~^|pR0o~%6 zxF>tni2NGNAje$5U-mE5G1)%HR&uwPrA6zW^!r{>V_b^anAKOvp|B|D`^VQD4Rin0 zAx?gXX&+@nM%R>(3hnDNJl@1Z>&Ix>3I%=`E!-NsnFis-OEMfHyiTrGm;d1c3#}#! znD)gczoc#chQ~Iw554OdZ>nalivU$4ml&6{M$#c@nEGO1#3Tk`A+lY#d|N>aU*b`_^Q_n9u- z;9F3tf74axDfYDs1)~qteasxDnO*z@b8_TP*j5Acnx)W?ek%$O4x@TyE>Fj`G#=?q zbY!1z0_+7Ac~WO3hf46i2GZ0yz3*XJiLJE}I83JWycZ(U8ZGMqqiJsFE*1J(31UzV z?G)i|`1^j2< ztPdT_)~Ri|rQUYPnl`Qa=bEG&+=FKQeE$Tj@@n@2Hez<+5@8#!)d29Yf7?e<`^WbV zalW8P1(tDV)B!=a^im~O2CXXN+UMP|gF0%_n2vFn;19Ph~iRJDuP|_C0a`M%E{0w z1YNOK)V8yY9>sT$R$;>^@l=x)DR>I=GFSB{&RLIs?QsiG1p!jSHbE1b|9V90!+pVB+`0x(#7w5$=(Y{>JCwX z{|NNUWsND$O^RPFj(_l%z?gktC@&l$pkc=VO^K2x}$V1FLQwMltF(9MjF z)?7cagA`pusLip-!{go;0#(ms4J{0Imw9x)ZCvUAZ&7Rc1ufPnNv5x9K|KJ(Vtk+5 z0dZ#$h^Ct1+ZEWWIf1PJX6uHqm%P13e1>XfAEZpp){4AA2Cvhy1uHUs=V9>gG~tCl z0{*W@7paKbHusFrtfQ?kQMztw+9*G#>RK|#J;S|qI(a6(aCK@W9>M#gg*H1o>cQYa zJ6#K@S77yRfuU>S!1Jb4CzL*5$dUb&_#ykwNdZf4dSvP=*})Q({)iyN$@tNV;Px*a zrjt9=u^{x$U`}D)ppG;?VLjayl}=Br{#1bZ8zE*SLf~Ulsr-(H`(NZ4h$8epy1Onf zmxExv(du}(HhYdCN;ShjqNo|zK*nM1R?OkO5t=>xp}>B9qbU#~ru9`(4K?P(tqjQT;iayD@Z7s=mSmOOKw7WIobAxM?S= zT|Lvn(ZAs;i1*1RS-#aLLa{iwvH$lzV}A`UQt|RVhJ&tb8!o6B)}cYZY|S;x`tFDL zG1PT-vISOLj!IIVW^N6vgzHm%p89+Mvw!50N8?8j#Pg``SpQsIE{&A>p5v&VBay9+ z@1lAdvx}^e@NEIQY4{8Sn6%E_IJ~3Ke1+lP2Q+NdM8ncNmy3>lpK-4O5m6YhqwtGB z0d(l-6*T>v%7QqYVlIdC>P@-$oIqln_veRS!Nc~m+ApG+84Yab;Xmt|@p*(pOLQK^ z4&y&R9nj`Yl-w(J@79J{_|yXx^ZZ9fm*67xAXI;DP59P?00puOb8a6GnNr20%smI$ zZktO}bB0ur4qr|bA6`6bt4Qa}xyn&~JapoP6X3Cv8^0>_VFwA`MST*GQ}&IWjgua0 z&0|IF`Gb2#=O}bJC%*BKH^|+vi+A5bQM;z;eI@aFBeBT#Z4jAT_x!^TBqqtE-*wEQ z&{(QK+v^m?uGBQ8BoJruL<0j=jsGu+sf>}TLw&z>Sd?7jm6HOd0_dzHf8@bZj$NA1 zP74bD&3sX;cz`H@w6hiKsSMMi!7w4+jcW6X#!B-MW}2D_w)mxKW)uaZ)1KWD$bK@HM)=@@cG4<~@y zYKF^eSj;~PaxNJ8s09}lHSgbI=of--iyKzZ(7>Yz&Zpr*@QLFIA~|xaP(YL~%&X#+ zxXGEPA&^F8dN!mp%!FD!!1A94LZn&ZmbPg$>d!sxDP(LnS)8l| z{&|6Z@4A+@{u6?x`AjdSA4&4)+=2E4(UR&@*oKkQJeUiK3V3rm&OTP{JnGj{LEbg)@`>m{G%EF?z zV;yQ!%W$)fliI;j98ghMy&lDUa#lKenBXDt>Yk|?E8xu})rvmG-+ zjgx*5A%F!dI>Su@rjDek`5Pj8_P|%b$42LNG3N|>e9F#!Aj4E1pOaugDM{VU4Fq2| zjqG-;-Hi7#T<FyfGRYAYof}nK>^kVSA5EH&l^JzVwr#nZmu3#k%yemk66W z^lttjTHxI4`)smt^miA#gBtzBZzOXKTT9%l=CHI<*2)k~_7~yx#j7?cwzQ5i9 zm{4m0McSg^*VzMv(`)D)W$LJxbNGvmt`b&uR}MVs!f;jv(KGKF_vG+hBsU*)m$PY% z2f;0m_GiCri#^s!ZIo(Ev;MC4MiCA$tI z@y~UBv1hegCdG{B-G(^{xRsEx(z}&y>DT7ey7&o=8rybuusCbUI-CCxV3vBp5?>C7 zIt;%4)FOC}A6&U9C;y7Zfby)FNAJL23rb=~CG*(w-|jIkVy4oQHblZd(5-R)k9=5f z1cJ1S0t1(M7lV;c((G!30FrAvz)f}Ir-uZsCH$mmhXWz~vRa8D^|8$U-4Ri_AjGV) z5=n=dz@bud@~?(U8S<0uhGDb2-rJGLS!biYU#$l}$I*z26`7@M_1O|m)FYg@m5RLvWN1f|W;Z~R`(QeJ)OzKS@GEb<%)j}P2#za&dpk6Vx$Ry)qX@W8fPlhuoxLeo=P+~$w4IinLltY1rQrARdwtL zq?ZOjO2~pO*<(o5bjsFUqD#%eg9~vWH8pve_w1kPJK~PnRCQm?4B}hwu5l$?w{579 zuLBF%Ig}wtpe)O_H0J}_B~VZx6%UJe@{Yp@q8S2a)!S$md7nA&PhD^oq`G@QsJ;x6{v#x?3W7T9$_9%ZvgJ#L+PieMBK0QW2B=@SM*e(ihi z5qtXCYQ{m^(PM6gnF7JM$T}hJvJIJ-fMn&K?iGY;6GCC}iJ(l|k2~+{{Xt_Bb-Xr2 zhZ)fwi#UH_HWHl51ZB7G^2fuUj&?3re!o{l%}SF&TuD?1crQHrI3!SFlr;m#&8jsx zidhyF3W8=;NWM%>2{Bl=7=01s5KT&^LDH@z<~&;AoXq0h&)4qOf;*ru(O~>kNZqbi zTA#Y(1x8-c$PX!!`%XeJ$$O|P1cJTeS*3g+Pc&jU8Z;&gG`dxvh1TK~r_P%KS@ruK zaZ69A?N0J%Dg=;(cFJCUV`>5>m;no86(bY=9{zKz&Z|0}r_u_;OT^dDrxrcoxwAgJ zeB>}$D)1xa%ldNtL6ppX`z~nc*1n98Dq7#|buvGyO`5iRk1F2%Ein^dJ(8M95m`dA z@Uv!9EN(a_0T9;y`Z=pE|@Ui&(V)ngz@xy+p8;*J4aW~js%TJqCCA?$s zAu#pAVej43hGhQ5`!gFH+E_SCKZJ(<6#8w%a~tD7=2R)lY^8WwAFji{>_{S!4>v`& zIzM=+Mm>Q2U2}Iw38mvA8r*H4htVu``@Pv4{w?H{tFsR zf<;`rx7jLg8l#SmB8HS0VLdlOS#*@_`<8cC4v!rjvloCAwD#}CyzJ3pLwDVAyK({bKd?9RyZMFjlLYfg+r+r4C~GB*zz3FOU9pr)0Hvl#nw z$Im~@oMg!WN|ckx!D~)aek0(04Ek%@S!Sv@tqlJ3=;Dv7$uCwRjMMp%v{RMJ4vLE* z+#W*)*|dWw!N$&Ng4otS^K?BGP?JC!sA2I>SlDg|e+1^Y6pNfEQH;8m^l*hFWUyi< zaBzle{^u6Ssnf%m#5jnd@lzeuCr3Z8eE+A(2YD7o3-Wtuid9qjD`nL?|HV%XUnqsD zVO&dJIM7bSjlT>yWM3g-WT-l-zdw#MoJf*IH5*%r4Ex88*#)!Jm(}NoizX)NER2q| z3Q{PTT{%%SP8U-={Sr1yiwehKkl%-lNa8>N^BeP)N@-o>s=R{UoEWd7u$Y)5utR@*4bI5aHRNm-LK|~ zp9fL@xz2=&Q(qx%jLvSvTg*hI=%I&V6OOr$& zDSf`|gXxJ(*|f-gf|^5-MY58sU#sFH*vNUT@4(5MS_4faI=RTVO-SL~h{rfs<_SaD zB)-Nj6UY`=F{TgNRWe10J@vI*OfAj3h4I*yx1R#)uiR3fe9JXB1Xlt7DHTqk>kZF) zJ!Xxdext$?8ry+YDXYTW{mHRoJ=GTt!2YB&sheE?gr7BCN?@P<-)~1Qrqj!OKfz9D z7R{WQPk6VSvia4$?!<_@pdqo4(A2Nzum5PNNCR)t{Jja8wL4gbu;&tVr z3&I`S48}7WN4N2ofcE(tRO`(#xx4o~)~sy37wU{cTjvBAE1R1#cIz-cy6bNBOFe`x z0Vgz7J3mj^hPQe{hc!eJQYCF3VUuxweH6M1-PWa4J+@8ZxbukU9kDZ)%}AJ)AcA39 zsEgC@oErFGCZSR#)3FFx#2u@iw@vJa1Brg3j}voZS_UQ+q}I&n2{Wq0`XZO#^ny~! zIQb&zh2zX2EiKiY8Yfy}&>lN8uEMXK=4#-4OV7rs30!(Vg;1;M$h?_W<*S}ditqpx z8Q_8G`;#AD%bg(9;lK&DB@fdDI!4gpQ5P7PBP|D{Of^%rENnh7HKgs;-~ocw_^cTE zdJ^_PT(n^fdep#DO%ABfVKa0L724U_@O&xQa%@~zotxG$=Ojm3=@1iK)UVJZT+_WFvL4s#Yp{fut>O}&4bi5^eJb|L7PCwOro|A^M2j$tB zq>A7QdC2BE7A)blM+c-g%(xmL|5JLjk_5YAr@>=;lWyvuen0_~I=xC9tm6;=whi?WGAUSp&M4JH!Mx9fo+HsZ6-EFRD6`ol#F|1Cv+Fou>`u{UpYFYILb_(r4|!r z%3(h1eN{lPc3T>-TC&0zEh#pT%X7zHTlf7?B|whp2w4k6O01IB3r~n}eYrB1g#Fq= z560fe+bMucd0`%K9&cYt(XT--TRUP8-SJ7PE*M^JJ#R^>-2$e*kL~j;=IQD6%5Gnk zQeE|L@{%59-L2&h%*j=u=q}C)S50%C2~FM$^>@MzN~zr1-jl0njEYntn`tsSlx>;= zO;;S`$`;8Cfp%Hq%An-C?MTja8ZBwM*ZYZvf5Oo0Yb@x=)V)bZCnRHYAgDllh8N$$ z^M;~yUc5^e?6U5Sim1harB7VJL}TVhfe>gn`w=xFzf~DH_FJJ2>3Hewdh#0Pj0oMV zf=LbR69jVRGC9F2et@>k#h@V|K%Y|q0#U)36VyW~6W(h?%k+A=`8-^$?6sjb&wiN= zo@41cyitbH23_{Uj$Ds4-L3kZCqV-<(c7=mSrzy}0GWCRJ*TQ!o@?DSrx#*ftZ~cZgpW3bCFBAtV?mx1` z9N|Kg{FIoa;{NgCgpk4M?Vh#-+sXi zFZ8h*8Ox|`?&4Fc(RxiQ<;|9#!iNiji<&Q)K#g|xgB5GZ&`TW0&jpd3r#wY0ceyA? zYkY${Y}|6cTAHrFWsZd=TNv3PT@_0LiDoz#4SC^&9Tkbc$;vEEAfkOsMGfWIfewbf zp?E2R7GDf?9;d;e#XGQah+Dn4yxOpi35~al9osRj*3N+Hl5*kFblFe zCK~_%Mh({9WJ@exQHuhc0gEYw1fH#$N zh1Uck(9HJ#Z$aCYPW4770*rW*zLv3ART@G<_T0onX!Lz>y}jH`o#)=yLf-75Y9e*| z=@e(zHg~2Mjdq^5^m*uYalvup_f3ZqZ}&BrhESjC9pM&OgzERp^;--W{@mBtGDKAg zvi~f|>{(#qIpYyj1V)|G zmTLjY4;@~$e}8Uj$8W$_2Y>3{3FN~#HLL|Zi(hj$!Y^_?>n!AGb zw)oc*j5+*yUg2UG=CU4c8*i$ZS-f=mXB~tP8Ae1kIXRu02N&Uo`Y!;f4>#6 zdwh2Zn_MDPNXeE!hk<*6O#s*7&(Z*U8KZ>v9?k6DDHPcx@?M8LT70-?BEUro_TF=& zL4ZE!O|E~IGh2wj7dh}@HN5^afwEyK9Mjbw1jB9As4~*J$5Z4I-)PSny5qNhPNX z?jK?~oOkwvBFR>kzNzwQ62(@EUjaLXu)Lx`S?R$+&o&%;SaRKYsDPkJDfPtUy@x3-Ck;wu_g(IkTJLO zUju?pgy(&p?^F-QDx^k&716N^&0GYlELh+k!(O|Gl{-wqcweeo*iPCs)BwSd1?j&N zO|i&VzDT@ol!|O$tY2iM1VEbAfjxb|kM|*?6(}kOhjN-95@hri`Omc)YXG>i#yVWw zjfZCDT{d(blxef#bU>pejkM)H4Shq00&R?2cI7cC3hPZlvuZ8gy^|jhUmUoYuc@jw zDxhUdcyM0Kv-_#>1Ym?g-QNLw_LG0mIzB5O8jQ6| z7|G)(CkO@u#mW30)vel*Gv`_7MPm4A3$)D=yt7XmF{m%&W9UZ>jSGuWd3A8b-C)Fk zt!DgT2heib-jMm4TTT~ariJ`(G=E4W7^tZu+jTUOYcZTZd2uF?``M_d(O5N(82S77 z?uj*fY0{WG+@=!2uvjcv?3&_=O_cA4+ZT|C7a`uj5{oc1kkiU|mL| zl(Tv=xWl2&D~-nc9k&OFaTS)o-AGKhvl?yBE}pQr5Ke5%jmvT}7a66v9(R zxq;LQhq&YSTj}sZtrK&|H`fEK|Iy5usJnx<<|DU&nm!RbgHR(9rAQdgk$1LK;<%`+ zh_7KKD$^Qj(7U!jFN^h7EU3r;-reSxd^b^t70mQ%gc7XRGKC{6i9?K<=3OI78X_h% z6gCTBWuNL>vq+{Xp^Qx;m3`L;8C7I-+h_{OIg>|^Tupy?1%mo5xD%e>-l_SSX({z^ z)Et}M$7pTqCVwwMpI5E&moXt)GvFBa9=phzx@Zdh`=dsLwM9x=!XTO!N{#&w+7@zW z3MRa19+cDgeFgywq|s8$)cvI)`?!j4PMrJKTE5@%cqb;+P3P{Avg9HG+^~~Ad?J%H ztQ%RdrugE8Wf~-}0EIOEAjR(CIJm>K^os@GA>7_y`pFWLI7myy{P9pOQ?|#FR<(0G zij@!pDGgkp>&H7$eWHU{L~AS0JblMJ_7pAFUKQ!{Kzs%mFjq9f`_;%!zRB(zG_bi0 zB7!HYG})hM&BvK;-(5GXUnB_d`v|@8&oM2MCL8c4{R&g=fC^^f6>`q5mML z4RO09Da-=ux7Imp*I+9*rKV2pZF$)OWYIp`WGvkd^XeY;BlbvDCwVpPfW~YVf>~H3 zf(hwaUv-Lf2QI8m+(-TjxSRL-YEHW-%b}H2A2dZ z1^HTJgUbu?L#bttr#{O=U_Hlk*GJkfABa>NNZj%**~+5#J{6SJCLuq zDo6|y&@k2hu|b;`CW{;$q|>2bv>k7+DgJ!;Sa2!UXka2Las46bJaMR%oLX+fJlmnT z;yjk6?v}9#$7`Oj@JIkz6!s54=07GD#-lC>8Y>9Df#etD+s4EIoKul_qFWg`{KpC+ z39caabxHx(YqZgG+=19$y!=Zx3Q~pNhD>L6LS*Ygk;Rti!E4&zDxR^qFW_*P27%;0}j;e`6EPG6si9k{70x$Ya8-||7>=jqzB|F9)W zl0|CVf#ml9)HkLqPCWBH{PQTlzGn_JDIflRLlX9GED_3S(f4@nn~=Tp9mg-h!rweo z&q#+5VI((3OQ?SXl;9%U9*x^^ZCAd&>(<251fW3h5g#=a=1i0UM}?z_3<15Z;kN5n zJQyx{(nj+or=~6P&qH|35ZF4bJXP&;3!5GSoKucfd8Tb*Ix_7|pE+<1q1saO5Y+9a z;W^)%D3IsI%L&6lkn3ecj>?f6RK2)j-+<=sY#!&ouAg~x_bej`5cxsaVd&NO7Zs`K zO+|A0BMl(mQ5@$T%I#t#z$wW@Sgl>EyTqqF3EL5rW`a(?T*PbST;(OiPv$rEnvue> zcx>wz*r{j*TBZ%YvUC+M#p~v;GNI z`&h%pM1+T%ktQ^|7f5$8UhbI@XfJmMjB`RU8b$$9+vVO#`zTA1;=w6Pc9 z);MhJMee*IY6WEyGE9K;E31@n5$WPA56ge4jH3oa$8?gaYHrN4Q%ON$rh;c}(C9Rl z;qTI28=9Eznw*ktVYr}-(}t;fKC{GW{;8hcDN$i`rxjhBco)9>4fE!x zF3O-ODQw}pKrR*^2Y>X2L!}e-M*P!o`UCbU1DoL0eh_OgdJ`GSO~!bQB)KA4u%VCGh$v`sJA!r1Z2gk4iwz^pJ$us<*DemAH7KQ6 zvC=fQIO==epH7Xbi-*+GL8a}#;>_ugH6Z6CUrQqj5XA$UKNBs8xf6Tnag4EqU8=6G zZrg!}Fldx0T3C5^;tWm)&%6^qyKdTims^%hsQ~JkyFW;s(UXwtl*2(U<_S>I_Wk2F zU(iBX6U~M~=LqZTKDYci`&>J50&CivAZym8acvX#+)%SFpccHs8#aIfPAoTEOW@dW zKG8$Fb*cMBCt1F+CXULy;{*Ns-vB3O0C8Nzql`nfWx~ehFKSWM5}e#)j6%VtnHvw{s5%Ivv6Ct)4K34|!K@d6R0^c@NHfb#bGS?pi=zgRc zFon_w9s}|K@u?;?2TvE07vx&?=svn0`=S@??(9o{@xN?dh9CN7JSM_U)+D~<2u(X}x;vxs*^Bpgxv^Nd}FhGYA-pCD|4_lYO^Tmi$2ZTzl1xWsvPjgq0RZH#z z6&IBX{7236&Jr3!K#qp9wN_v;ZjW;n$6$sqow zD7Ln$td9J2xM%Y$exgLZ;64Kxb990d*Nz&zLeDOiH0Q6B@ZVI6J$9w$Vm*@)8^=}y zyjHU}7W~=+@QQklH!!Ugf-Wi~iQb>J*B>*MDd%gB=H^#?mx!mO6P!~Zv`|-3omu6g zF85jw?ZL*bNAwEy!CFlb6pFu$ML+$d2&sqTXJGqJT10u5uR_8rwYRqLaheXt1`NyE zSnomcCXEpb%r+jQd}BNlnhtv&*0}3(3|t=gX>EA#-JAYdignzOfQ<4fjDPA)w_GZu z4~)cRG~R1PFow2keHq~4G?iZQCQsQe@)SOVULonUUyA^VD>7R(q>$?VJ1(WEP2TK> zGau=FE$h`^FM_u9nlYiD{Z7sr#1vxdxO2X)d$q2s(smi6Hkq2bOv{C2rP)9XUZqwI zW;V}Dt+U9rYF02Vlhk3ka*P>aWu&UhZdYn(3T}1~b-0QH3G6a{sV!Z^WYg}B&!%-| zi|yPGtZCj`hjn&%!YL3gol~ZMqmlUW4fyQve_1JuVGCkcL(3#rq(p=Sy|j~cNkX{S z9JdGt<B-!Rt=_MM+kwbMCL*kEt)i{PU4HI?>+TV4cu2w+%`!o>3j8|kG&Fz@O> zYOd`$;VH)=uW)x>#y4rGrtsV_Abe@Px~hZcbRFvZ{3dZ45IF4{vU-EJEYLaR;~Kev z1~;kH^EECS{P{U@&RrdPvQvfF&BMeT3n%jdep!{EZ)K_&4=!_I=>v1_vo$5qw;6E; zJ92_$|H{?3pq8sNhl@K@{lWU`K%p^&yMQB&FNp;(K8;=#2vLr|lIL^20^FC~@f$Oj zlQQh_&-`(}%1r#Ydx}JJ7kqGc+wnQ9Gs91QLu6xe$Wl7uiwK;!-lgl|V7#mD(KTY` zZ;EoAM*4_C6d9+@nr>*B?3C+^V0;jr{1lMI6iN5`Lm7ogJ8_vS?R{GEfbbHuh!SwM zLd;D@n?S0I8^F5-ZSvq*=)W3(i%FYD-{Lpzc(u@ilxh1iDmT%e3GxzHX{T#ngRt;o z&OPV?O_ie}U*|Qjdl+(x(`8lMs0P;F4&!7A7%p+MPT9MxVlj0gJNa#mh8tgFIY_Ff ziq-4T1_5u-O1VMlhXz(^Juy`LEs5*bK9{JW(yp99lbSJ~7egalJ;dZ)CA!Id!7Udg zH5gZ66?P4aANCO+qrW#0LN)XCm8&l5*0xIUPPm@}8#WkB|Jbc-Znu<+`Xa{CK{+F0 zb2ZeOoRsQG4~<)9AcdmP?=6;PEW71s`c)_Gh8z(vTDr1b0o1s1#1O*A<+BW(HUGP% zNd!ikvOtet?B>NCDLcRbvn!qW*iLYm(F(7`xeT?-Bqp}h98SPSi!kCu*7elT-TR+t zvg2Iq)!e0_Q(xM!Cw}~GS9DaZ!)HfZaBuI2o-|BgY}UyjmK|A5rLo~zTB(Nu`*mll zsranZ1cT%CZoPGVul=?pir?ZrI15$RM$d#8yw#ih=O#8k zly7`r!||=EVg+Y3^Z{5rGh9H<=WD=&?;Tx2GPq2bJRZ>i`k*+E@FEG#tYpLLP1YX* zx~<>}uKbOOi!1(Phq_8m6q)~?TCjq$f3bj(JyEkdcQUr>goJ)wk{3oM`5xQnn7r%o z&gdbq20j^Uc9__RRsd=%aa#b76>4WA_C!&^vA# zcTdTC z_g^6JC|fuCD-KxZ1UdAf+m!yDTf0z|>LwNX^e@Y;jb`40cyY=+p@yXDhx+73Ohsy- zyGxjx@$KfH7t~B@W~0{$q55*#L0Mw)I%Cs5N=EYazoG(Dq6U1~NT0+^v(v@N^s6d? z^Ru<3w_AAqqIkMX%1 zJGbsvny2+9h&miCAGr~=uZj#*tGrTL#WHDK#z(i}XdgAgI${6z)FhcBlRaw-Gsf$= zW0+zSO@oK-{r^m}eH398^UX38X*O(JPc#EIlN9(Jfz@0;N+%a`x(oKt)@OoZpZ{s7 zEAXAJZ(oSrBoj5X8O?;75lB5N`DuT&Z9-iGb1$SEH9rfV;!ydC-iygZ@2m*nAhO3) zy)N|bYOYmscZ56`w3(sdLA}2*;@Q{!*QoIvdqD6mSFl`CwG14+%-OjSu(7t6ioDY@ zqoL?3)!@qy(rqMrTKEAoL{q5?9J|RQO=#nzp<@SmP3!8Q<^LJtqh`G$Wv`yuj<(Lt z*U&1>q^teE@9~E#W_dZ#mDc9CIVH-UEYEEJUH?Y8Ec3_j|MP%6l4e^f#RwMj$QJ6&=CVj~vKx=n91@8wto-W!TgKH3Y z2*t^KtErtY@;Z+0QLlJ(mog}&q#~}Qee@(*B0;Zt5+&IF2IyMz&{Y>k$8u)(Nh8#% zLb>cvR$C3*=L8Jh*sEtY zj>q-V24*pOwb$aW#l71jAI3SCgIE!FsLyUOWM186SofDYE80kx&7H_ z!LXi=URtfUU$~&e!msfi=B)pkVtSlA{`4My6{pv{A{FO=>o|eaf2ud{Fu-0j ziaRe8?ZaxDBQ87WUh?he(um^?))@bFi%yXWTj_cja~yf>gKYQH8ddmQIvLlbeWe%y zP8G4baG}n(re&p>>nZh=;9%ZR^qjUWW=(2{jRC8ojR?nbx!oG*#PVDIOH;=s-Bxk< z!IjF@qd7xv#xLDwBd(6n-Dvxzp9R%Reo~4iy%ityYbXRqDG@(5a|a`e z0(RNwy1QI=7fxjnJ^MzT;^}TNW)#E`5*1q+@8-Avv)V*qcF_Fo97?PwE4Ar-oHR(> zGYUJCMD2v`G_+P7To6&M&A}BO9G{IEMKDX19_RWcbZ@YXpCL`yW7pA(ITeMhs6aTv z37=j@>C+yf`@D`>BKbUjUz=VnHon`vo#Ij=9IGY%!Vx=SdMZtj;QVp1{LJIoDzno7 zbssVol%kq$HDX6@6#Vi?tcz2xz)Vq50)aO3*Q6jrOGN0p;BQhB%YeouTED}osLGIW z;X0AId`g|_xx6>H7^%uL=vwZ>binATWuTcc*j#n#&;mlj=oCW)F8<3BTa1*Kw)5Q} z&Y+PTa=cRc6S8Vm7M~=iB-0re7}XAM%3BP> zgpI#0l(?ee871we1&$M}p_swN5>UxPNKPc$aV7r2I#>6xHu-~A^IPuUkgOfwepyjN zkIOLLNpWZr1xxR}pMqNST({0?$k>elaFjiRuQnvF`;G}Dtxb`1lnLtQ%Kzaj2yWL) z=LP;MFHBuq=aK+TK(fDnWclIZ!k?X(2{4;`sic$|U7s>8NMr+rvA98e-+Jm>7W5w&HP6d>1Zpvr?2M@mnX%smOE3#yGge zJNd!YWj-h2Gi+ufTY!K?YS(W1`Qd$@Qga!gMtK>7U%I3Ry3vGCLb<_A-3{1iE(#pE zGPF}!T9e%!RW}s@Xb>KZaK5U(KZLzdmtTy5bSF_^14>3oH0>+Oq()+Yc#|Gn>H<=q zlB4nrT)C8TWEK!QjSj}yZWy`CqJ`GgXBqRCrN6lLwfAZ-YoVFEeRR)-*6U>C`WVQi zSU?Q>QyN8Eu}NRX(L^6mG8Gl!fsPw0eft9GfUMemZA~+w#C&~zxj4kb_N&B(4k z4Rsr|@jttyP*CsM=m@pnE+R7z*c0PeE%?eb666y+n}%X zjd#6%t|U=upV-)jJ%#24@Ahj_tal`G8-d9`f|DR!95Ktf%6fnc3N!SUXI)y!^&@vG zRkt>~U4GbneO<;;kGN#R0^f?9Ce|6SlYh ztQAoVeyo-7Bjq-mI}Oj=L%}U4jk$Hg;nS}&wtSDhsw|WO?j?hwDz&(wBF|g_)Wa`k zX$S;S8{{dGfwQ5dM?UHGGZT+x zt$U^yy}&6Zt%^02uU>{Sa)^Jea4MZ3FX&E_@)sU&JJK3#Q}aG~la(knmV?5ot=Irp z&3wzV5nPIyHJZ~>6K1LuFd;`GCwjGrzH7EbuFifzar+qD`A}mv6Wf&d<@tp7I2KTp z?l?Id1eH#*+XQtBhk!X;QAkiul!78WIX*9d#GkA%A#EMeC$6)F@UZiEA+wk>Dr?>-21C?Mo$WQ2nud4 zk!xc0h4X43i2K~Kz{Rb_kJ*5Pd_Fr5#a;2$2JLp#imPraf2%6oMpZor$sdA3gC~HJ z9KQ*)Fv_?;<+nj^+2h$M8Z2S*N4>MZ_PZbzvf{HrsYSKd}{n2e>=UJZo?xKX~(drPTQ$XPCY zi0{fAzv-m(P%w0%#M7S8VvBU#FTA}Gtl71v0xT;7hoGE+m!>{EYM@qz89kfASy`R1 z3*(4*5@tMwol0`FkoF=_fGA4dvk(Y? z>oA;W)z~68c6HP}<~;APqCAwnWqZ3(y;$Bl{>gwRWFa+CC2X(M*^trbvQLE zYV{5fYH;(x={nd-bpi;5#^SjJ7G2EXxtL8^N1DFi^W-+;W$;e-vlGzw&-l+Z+g)Ad z`}B{5x5Ye{4}xr*!o~+cBOue4v z8a7`ddRW+l;&X3SH=Cn~jKcnJfk+S03`h3+=UhQZw(@(JP`16CW3%Rzr^vN%{}{i~ z9_b@INNvBaIKy`EynYrizMZkdI8n%um%7EU4U+gj}F zldLy;_K0Ct^vVD7X{#l587JiJ9oS+@m&$F^^QF@8N)B zE#bF}hXZW!DPd=NvVMLVMm#lJI}ZZ2k;u1vSXJZ0H%?tBtnU=w5wiYZ&x9g{n+OFG zn?O2IZQC8F->dDr<5hJEdS2p*Zb*f}f~1^|5EcQqZcY?&XiMLp2<;_8TA;O(S(gM;0yG##9^7M4bZ0nfC4?Y(6RYOTC+(}j7}_|%ngjIh z=)5h(=w?X-(VdUT7eR39zQs-Dm@5fu%I3G5ye^VBwflYuzbox}zE;#LcmI`j54z0z zZjZjhkgaTR6z zDAOYva2TSb0E8xz2Yz#E>9+AwXu44klgZi6PB=X8|J~fNm@zj)4)y2(GVJl0Tb{X zcF31bFz!J(|6eO5DmGp7T?IMI!?lE|L2Cev%7k~x#AF6HR!NbP%fc933#;WM$ZA%bCB)b@d0nG z=x2zGmS3|w(Z%sqGpVU(%{f~$IIB?0Ic;N23eU>=_v}%q(8O`?Q5GcM0&=(%4%LCG zV#7;=kgy#pq5=$l-6}~>lDMxf+oVez-EjDNq?2P$X8*-p7#y1#%J4fv4*v}hzR0SY ziU-60+&hVFE(HO+RrkkGQpY=k^%Gs&x>=kQQ}sH{Jgaoh2Jr>|R%B9Pq4}hL z_qh!HrfS`W>tsJ&xR zFlwl=*k$>7NxQgKXaoz1lMw*yPkWJIn@XzIrJ=-P;lcx|46+o`wt@n_);#2!zCP_< zN4cwGv>k8Q`XMu?7p;~vIh^3nu|33WldUzo2LJ7YirkcuD~p|FlQ8eIcRGT%aL}qS zEn<=K9*yLZAOv`AP^SJ@atH)-=1um6od+u?CfGS@?SL|poh*zdfgRAJ*_BsaAz8|S&R)R#^(b<(iXaE_7GXVvm!5aN&h7U1b-l{@HLWF6hve5W^v*5)IKF(0HgRCK+f zqQj(7PCsi8P10rfCJ>%2lrsv$z)@3MHDeL7Ipu-gthb-)toCcO7S{jVd*8-}f!SX~ zuM&Gd=pm5MT1efn&OHRcup(2Sh59erqB=oICNfQfjurIGYx7)2&ht4|=5c@49)a~i ziG){UVihy&kY%z~+l&eW_4#`gXV3Q9kcZUoaU_A->`69}$B3IK*AAMpraVEarrCk` zpxIP+FU{yKd_3x@VEH`)jp(j*I~ImlS*{Mah(GMFs`v^qQgH7!WQ8_eiixuC44_>e z`qC75`jEt{f740m&tK~=z~Uim8LHsqLMIx7VmuY^_(q$u{o zh?nF3ArQeM=~nfnY)m_c9M5&k$F0#QI>@6*5s|XPF zM>jljE{MdQ8#QBi(;pyb9^zP9p~hC*6xOBxz*G#+uOfm%V#)`Q^zbc zzj9BRbJ5r#Z&9X@w;<=uYIW%Q3715V_lecAv39V?NZqU)-v8$GL8ZS0%FCR01{kU1 z4bX9iNS*yLdNd{qUK$YfNp-S2o(Qe!RQs&a-iox*%9`9?ob;;+kqs%a`m-i`C?{g zE;?P*AT&0T7F$(^Cf}9$nhw#G_3ls%^8<^_=i(vT^1Ze;_wkuE?yQT;mXH5@x#3mM z32JL*KzlK@&2Vn3Ekfl%nsw*lF8e;a{>C%~E61TlCj`L9l|>!W0Q_eStVqEIcM(A5 zV2FyIO@=pRy}EHZ#God?@!6D~h;z*FoAxzZBVLCrK%ZlCg3rqQ!egf4-F|3;+JNce zl1DbY;vZglql0y;Z#e?IvW^W-C~;$h)guU2FgNVEaDH%X@DTOu0ac3Ao;}%AKKY-m zPZL^p6>KK>ApxCYpTkXm_ibVQVb}a#ZVOI(4%+i`C!^JiPEYm`nqa$?+;aiCi@05! z|DMCFf;fcxi+Zt6H3;598Gm*%easP0|18 z=I+jq2(bw?()w7H6&{oK8XB9TDj^}CMwH%l6w^aQiBou+Sm^Z1!-Gg$vyD2)Cd(~c zqPEe!+CnTrAv7DiGH1lH>y&{{Ng?KvXJB{iZI6;ke7ij}8j|xhFFr(iE3reFcwa%l z=?-<89+EKQM4lUOt5O%mU$0llEQd0ngo^E+=)+ZO#lu=5d!LV147nv1`H?T58{~uE zn@B`zdb%}X5{3-l$CfVKlC^8r=@0JZjsYyhY=>`bAuj585mI;G4b>iauic-}iYTzL z`oJYCnjIKs4Kq9@$(J`> zX;~ZFLo$}4vH@QC_jU76n|7@EI)HnWD6^T1jfHH^!9O%=0`^nBWB64zx!h%#q0ALM zK1{!&2q|nfffnbBO-PtA4u{Tex%AoDI}YVpjV#Q2;#FA}HxuFB1)luUI4P2VPeMYB z%fA6|52_d2&({_CM09q>J|2N{6Ux>(N~U3>A*(eU@<>#A<8Fu^=4a+U;-1L!+QKI6 zM)6Z!LM_S{Lp|YM{X~Jj)5B^G-n~yECvldo3H#9l>=t);g#Q$QB@lYgGaqj6j%g*n zIZ5y^tD_2PF&(}@GGOfC>p@INK^%#Q?~#-Y0L&s32AH^v)LkEz^Q&r?+kM^|b@`I= z^KY2uc-4I*o>l;%*>~C?MNnOt{*6C;o#?|P!vD!Bczl~_PD1yw`#<_G52+V9dJ)bE zA~Z^V6te_1-~A7!S{m~dOnf77&2(c2#PtPHBj%KVs5Bt1uV%(E zrgQGpAKYf~KZx_8G~28oh1w6QUxM#RJCAPAJ1JV1*qp)6N^^j+XCyd=K`XSbkQ=NE z_TxfrWymeR1jd z1~+snY(eZdP$ah?s`F_ke`ty?O*+>bUr)`qt(NUXT3u)`72xQ=HHvbbVIg+U&IRll zMXiqEeY8r~58q*xc@56#Ss8cD9^&!qsT+h6v;6h!TTDP`D@H~1zm5tm?pY^L{_J`yH_bH6!jyW{cQkH znr!Pn+&NI#eYrGzT3#j($AHR_r;99Hi$QZ*8jGOX>O| z{J^Kf3(J6(%k8RNM)Ms6)L~)_VE8&A)9st?!hldHw@qS`fS#)RF|T}eL6b(toWkQb z58x^BaFSO{mSwYo5F~aM%R4pIaD_Zor?=R5^@=4U45J3yMmsp$Tx)Ni&mNt;uh6{( z2LZNZcvAG>U{4^wf95#pTh@WP$dw4H{llcMW}T$Q>9S!wObvpo5|m^T{pCrm2b)tfBip2>^#XHk<<&qlkRam_1R6%_TRd z7iU5W0~Ze9gv_=x$759MF(nAL`lf`l%fp~rWA&2Q8if5DQ=?Xu6_bv6GTh*&DG!hA z_>JlrrwU0wg_EZGmPdz&IFT{IXR%NgKEc-EDx#Fy;$_`}(WcTZaEk#tQ)?6)yjD|9 zop@Jx2tjrCHQ7xQfW92$PNR19Z~$~Grs3~_Bf@5uc$Dqf#glAHCdQ)Ax4v}9wbp;r zxOhA9gn4TvSUR&BSZWvvLa%*EP%R!Lt(S*7N|HfR=?e^OrK(I5Y z^Z>3}exHkDYRmf>C9I{-NGZdWZwmI6hdtEUYv95Gh&t6Rm?N@_>vNTB{J~hXVVaXA zGlBu&obtKuxUnT3)s1Nt9?HPfM@;zhKF5Q89u(V)q+5gGp?#>IG;1b96#k3dC$8<1 z_onq*3ZF?15DMS(#-J>FQRu)q!+$1rZ6e`M8tNr+e5&~ky3R*?z>k@ z2TyKmr{n5$umzOGU=;aj071IZ5}p!8FNBFwPc7;2x^O+MmN4>@i94C92w>roX@&{2 zmk9T0VBbBBO_iriz_&n<*#Pm0p2-|Rm5x4VecAtJZHZVJ+ISQ3&V+(}PC?1p4{urq z4;PI^BeSkAcIQc6JZhWe;KzuDlyxJs?Pidj0fy=K*t=Kd_lW)^hxFl` z!_q<5GN`5+kkUy$|iZqR~%$uz;^ z7lXH#i&nL*Zr0XQIRFS0HsHa+oIz+VZ*X^UO0tQj$+kWC8pc3#UVy(w0_R~XUtSnu?BAzKftxTMwij~cw z^1qVKnY$|Fw^9NSU0#3D1dkVQyK}E5bCuV?d8b&KELEjHvQ!u5zGM?AU~{7-YiQE# z9i1|z=-2!^Z3ix=10Wtg4a|%hXFm(#xNr(n2^Wu5y2I;zyUJ~(lWzx_UU9*9JYFlH-R7aR3WEI9hAB;AgKb9*7qOBf327M0kNZ0Nr=A&R%PYAy%TEfqX}sZTrQln zF>)1WVm3IY#NUtvUF#xcog99PnCk)eI^CI#f3IB7qP&6}kwdsK!%a00GSD}Fl`)ni zn!K9<(NsyXXA_)jx;=TH?d#vef(q+E6gJiCtdxDK>HITFoL{xAGf;dNfEThNOn2Q) zf;FFvpWFRs&!bdS!m$RNv{?lm=m0hVgIpoRv@m@lT-alXv8CQ zpg-2}D-y-fg6d7j>u004Gk7(qSNTV^)p5H5x)*L2pprM-_j%Q8<=IT@Y{^Wo;rb1> zkD2tmqSVbL- zxVuBV^BU;RTUM|pfKfo5{=HNCr=zedY$_@Lt&n2aV+8%`Hv`{V?&+4dIcRZ!t&tdc zz^&kBi}KA-)+-cDqBhHvAdF4mvdxD;KobI-r3%YVoW{-~V$If9#c937_nO(beUVk# zL)#?d3K6Kj`M%qxLjErKG*pio7$GxfSNqd^f|!*EgUiVeg;Ue^pJ3Z2P?ml1P32Xa z1bJ$K{ozEuq8zb9?|H#)Fddb)1Cx7gCE0NIONUu#!>A=f3LM2eVDV6O?EoocD|`LX z?xdLaoVOU_w@u~Ww(hi41c!QlkY&}eLw+Ap(wiq={)6nd`7u;YO1krn7zRx?h!5qA z&Vy#Bh$IknJ|Y5MwB(nux~SiH_VSfnY;>EkyJSFUorao+q%xs|zCrc9ej0tG{t#hr zv(47;B0FPRp}|Lo{1vC+iNHd`k{RrLTofo)$l?gzSm2((%m?GJoJZQ zB|R`grQn&kF7kvgK|wb8TK@zYp6ArSJq-?poe7~1*we%xnN&vijWAWorv z&^O``{}{Azj;^Fqm#yt5O2sceJaT&?<*&I4z(rYL;A)wym>>Tl4mhdMSZq>SF4MBn zIwwfhXx4y%Pqt;-d8XILs&lIW8vFu;M=;2B&SnXiQ_$BS(zr&rfDySfb$^BcG4qU5LdgF$Wpqp z8iJWhW|_xJI_HU_y&qxZ5Vam_#Ysl1uyixBsbkWRPG^|4H>P_@{4BVp#d{9HW6Itp z98(fon~TWP_6Qcr>DmQ}#GZAapMbLzkTbkhpEl#4@6{>8Ij2zE5Os>z z&9n9?jy#ix$-A$O6ECr|c33*P;O?_k!W<5Nh{-KHyRob^CpNatwCdv&xG&1%$(__+qNA+wk;tFv%FiUjzjL!-wv#@`9q~?zLR1AIFyJkJecZ`Z<7E?-_#>cMMNjzzl z;&t(4EY#2p*;b1=30(rwfHby#o-;Rtj&UqHKnynmB`eXb1BSE;5YIXHk=IMP`D#0S%_Qad_;csZz)JBtSK4$A?}!keYPWDcenuUfy)_Y$#bv-r`MIt@9VanS;aS?%)~i=E~vK-@fL z@8U9<+AYo!f=!2NFU;4JIVGIif0V;b4EK07G=<=;cC^75lT0<{OBSdP4 zyGmQS{Hu(_*=66M@i|x>S)4Zp)we5RJ6JIY#ZRoAsx|!#?Gz&wcG=yv+wY zSu(6()o+lF1kPu`8dIG)u&{%bUU*uexfBW04``K$1h3jjBCOL;zID%!Q|*aYNO$;Btu8;|3Dy6hW$%5ky3FLiA|kSRHnGcbrS-h#!0&HBzIu=T7Bncjk9*T}$Z*otHTZh?z;{3$a(_PgQC;fR&Z*<9`;xUfzo7>HSrzYB0V4G&dcaz2l zn)T_G_x=XTt{n7Y&^(dDxg50Vg5E}9_g&SCYdb<2suelEO45@_Y0bA`^K-udhHCQG zZbR#Y>3g{4Xhb_HN@@sTm>VtbB1S@Y{DijnZy?cpqxo_M0O-pbHahC*16KE-V)U*H zRt~FE*3GLM{+%# z873%^ks8Vb`ry&}b?_$(R6&Oo=Ov3clBn360hbM8G(gJtElvFJw6q{g=|lik2C&XC zfkyaCs}cW&c7Zsslvjc-#fpf=SXfa;qC{FI)_;^szt#%~!(^By92$p$+)i&;FMmND7@o<)1ce;d zDwS_LF{A$J#NSdy1n$Ko_3@A@>?~#B4|l5GU;6IkcnU8S8>nwU9DdQ*UoL`%{0o(e z35c5IUkH4o2qqTN>YtkN^&dfxNfWAA@#hlxwM=IbuZgiL8!69(x@pq2SrE;qLPaSz z;uC*a?%V&BNhG9VkSGJWwRX9pin@+PiTBUC0~>y<;u=$)uh5+;j5+p?hf~x!2v9)2 zv5&pCGk|~1Rkrl?)mp#8EvL*Y-D$!2RL3h7_*^>!gXW`}C=lKL8|yMta39pW7Iw<3 ze3u9S)s@^J|Lz?eZWETInagM{bj4rYVO!ukqKtUlxwuoqc4;@wg16EKMZ5Zz2kO65 zWIuFD6E!>{8yX^x7s8)w@}`ApKFW0q!C;#IRLtdCl2l#BX`qC(j>`9G!gb8$!O2(l zJln8gXMWITH4f~9g5r4wfrfSqeIUY%s~GPkoZ!%1h{O6)1c(0sE7(=9{8T-f#S<~7 zl#L1k?2FooMtBZl5$|>#2Bz*$MrZtsZC1FtOB&IHksveXMKuIM;FN8WkfkZ`{N}vj zo#_5BXLYP;L>t5p1Sx~xy49VU=c;(0r&3k@2RAh?bebH+WzeWv=u8?<(T1rl+>BrO zzPEfqaK74S0Avb@CF^xQO5Wy(1!q+KE1zJ=tEY=(5)y<`{g3eHM$J9r(YxlG<~UL- z(!P-yX*6t4(!{aYx3|vgRgj8CcRo)0^0gFxAkv=griV-s--`k^Ig>X+am1b zD^M4v9x0$1!{#XGzl0;0$Cws&-n$nr4?mSbk98vOmWpIyig>nB*vmpVaBIkglUq9&WTib4sXK2Ok zVsn;3w}0m`hMXG50jnc>`||qlR~pNV41#Tg{FCNfT3m#(<aq-i5|sNWV31x9k!DXuYF(+G^-j(TF3e3t(id3e(aMs8vNBKu`A~v z^J)(74w}J*NeG^llG32p=6IJtAq12L$2ecO*AG?aP>RVO=@g6afWT2xkFcL4G z>0{Qpt>hvFV0erfbg(MGj$C}0sxS`IVmiiBy-sT)=dU4~Faw<@T&Wx>H{BTRx4zh{ zYB_(hkYO?~=5cuT)tW?^ZuWG%pB}l#1uJJ7nL%qTO(6E6Ix>|F!ry2UFR$a<`fQv{ zyt6PT(;1X#2EU!CU%JsW{;IiIGIG?w8}0d$+*~84A>sQ8oThMn)V<~7*J((+$CW-e zu&Zs&^4~>9ZNpR@PpS!Xm{09U{E_^lRY8OGT;?AME5?csD2o|jFEJZ5O2f?LXm^>5 zv^aoaf@#xE~$m36@5hU}JFpL5FU7F6awE8kg-K*nm-F6>HKY-4J_Qvgf9 zP=c!_i%K24vvnx4MlBz0PXqmSd!)_dUXB8(8@B-XPVpyL0yLb7K5z1+^Msu@p4YtW zWGcaK1FpqRsNQ!@uiJ1HxtxFe;fr|RjzL6=V~t)cJ>;@0 zNH*%Cn;g_rb^Hs9Jsy7040#(|Yd%gY$Xi&)EiXgpld^?)9ruJwKoOFEeji}?t2#37 z_gMa0)gc=Zo$IrbiWij#&a_h+)A~bv*juIcvSMvqFY4*?!x-l}F8`!utySp4dLfy4 z$uQ!8wbI(ipmO5Iojc7+cR_HV=c@$(#{}WI4?t8;p z)1v~KV?^*gQR4u3qkAQ~k>WuPM4G-4^)}#>t|{c)z4ZE`MISQIr*f@+r%SC9Z3mmR zeO^jgAp-wO{X?rI5_5 zRmBE|n(u4Ua^nHnJ*e|&*waq_AS*p~wE+%v(}~PaPelB-fAW<+#~u` zNH+u{jHrq=6qc!UxeES(0%XgQXP0rDus?M6RES?rbaiZXukBi-JZ%h^i{kGXS6ZXm zBMEy(T}tPUoD9Ub)kjK*Z@kX|;lrC0xWw$l>@3DU(e}F%JJ#hBkUSto6?y7lFT~F} zDh@Orb+Xa}VWZ~YTgjo%BNm<)0X50k3(pt^|CH7Ids8!D3fJmy~wtw&ss>5h+9D7FW1(>O5t|PnX2xv&f{agpTPhC zFy29$s7>is@?cW}zrT+z+Y1JZO+dX>#xZG2-QeRC(3sf+qU+Y=(8D5w?MjJd7h`- z@>mBDO}cVlv|Qr`KMQD@Z1+3ipEo%Gdzagp&<0!J(aRO*I}}<(j{N@cVvo4lo3!Z6 zgrJMnBXg|UUP#;X+zWD#nDh;5)8AEhidi+dy2-|iEDqg<>z&gY?MJ5SM_{Xxo!F1e z2F58~8PvxR?`rMzqG5nt0@|aPzL3^_lu7aabfoY#QW^_Qk6%WBvtuMja&_9L+fk#st%pD#Nw6;uVDu$ zhBQjZbN%c~(0sOF5`~dix?M>9ah|VvNNYMS8FGRxO&3Ds0wnA6oF&5_07csuQ;E~` zN#_TBfb&Vr{h^qGWa1gf|Aiekb*-Jl!)88_LJ8X;%t_esW?a%h#tD181OE zWgV>tR!tcia;_4>li-rrhim{YzY_!99q0+b7abF_G1kD}rGAo=^`sx+jh6nFV9nSA z64H4Q<}E6IC%)a61bgJ%eLBs{POTRjpsXv#FhGcrH~#<+cO+K-T6uZ*QA%|D5bHgS zJ5V=ay`A3tbLdU!=jiG|1){QrxK*HP8aUB#8WG<2CI4qT z;W0El1o`hF4zLkiDoxb&CpYwixQj0lcn?MCb*BRiqtl7gFXh`{u2_ z?n1mPO5V~10lT7GtujHF6?=?;biZm(yl*$K4#_q#by`7+B^)Ks0B~#V6+(* zF$Gw;tAV8b4TDA@(_O79Nvxs<{QV2>01;e~|IzJSmOyV|%xR<>@$S@bLIz(+{efY<_EG)Vwb3xx!{@BFa zRN@V)1GQ+b?!iKbWay&MQJGq7ppUKr#jK9p?B&3R186&1O-E2GBvDpI*K`R+<(BEy zK2zZ$7t3q2CdA9`xA z`YDt-mJsrx46vgCz+A^Z54AyaWq>zfI0GcKw6Lv){gM>Y#PrnCvCfL@dcV@ULBr0$k!#>;ANFUH;?r$I^J68h#TQ5{9Rm%qW#$kW=DWMUwhG}y|fv)`cR+@5iD5kNu;zh z?X&dPXX%&d0jfKvqNrja@V5YH_dBm1oB*QKmT5tw*XUb*Jp@{jj({-tIOrZ=uJ>)! z*ti`#lEyiala9+K?k-)*MLDMNs_@`%PD+u)S>;G~^;nG0V)o0B44Yl=|N5BRUx~#X zhF^U)miZmbR^~HN806pAA8Tuiob#?~=rSycrD@>q0omhI zv83}oax#fuXWM!M{oeDxa793}_rF2oWEh^@Fz(8?IF^+vBbK{3Nzy%`#t`@pA5K}Z z1g1mtj(Mi25YB{y*w6UV2np8FCG9IP6Uc!d=6^K`z*n>I2YI3LsS6podBXmVY zdk0_>IK{nc%_Uk*#Got=zrV$yyW)ky0zSpBa!;gpF4I0$2CJvS%!_RwW-F0aeIB8(q8 z+&GM8yI}hNbV#&4O2`6m?ssLclRZ2;m??Qq_CiKM-;lxsDv_6F*jBRwazyLU^yx)` z$Sh_HBz-hP3 z#QFH#_=V00WFR3ap^5ch3b?p>>jjmzv|dmMOqzf|(0)$dir-|^Kg`tfR@0}w!B6D` z1}IuW`>T6E?C$sik0HpcCxnsiExd^Xp1p0A~~?A)kN@mA17R^ zpR-{e-u^H$6qeD))35qqwpY~KnlWrDeAY);XaS77PGAl?l(FfZy_VnG^l>%;DorM)l+mXFJO-%{1!NYhLP-Z>XqXIH%(7H=nKFhT4zcJnFl4SB z`If6^3dLN7H{{uq;Em`3+Wa-J)CDs0j7{ce-r*b647X{l^?6flUkQ>BNI%aw0?a)~ z80}ZJg-v})QM=doc51?5@DEJV%sUiAHz0K5PS7BT;o6B(@VMcVbn(d?!5Nkcjm13e=3(cJ9z^{q{;YtW~Bjk~6q3J1Nos-4< zta#u+MxFA~X ziMa$dHrf*{n^lsBOIAj1o)0e}Ur^NOQ)7u?0VYo0O#;I&*_1k%f-slTyQO0_7iSle zo6>P^v67q$ee{gJp0xn>9sB+Iq|9b)^YB%$3qV#n2~vzGac8O@p2G2NrVn5r;PRD@ z<2(*m6OAx+CN1S%hH(iZ?EiS7Tk04P94rms8ACi5eaQ|5>7n(WTr}&yQ;T2ym-HoH#nlf{iaFhdY19;g4HSyV8lvqRuMv2ImkPL#|Jv>Iyv&t4W{D1}JVR@kTV((_SbLS2$!2Y#`N zg!hiIYuWBY9?U&g_C!zctE>RP$VhIA$eXGSFjhNFWw(uQ^Iu7E_E@84Eig6}&rKb-G_WRTsldWv~BL9ZF+hqn~A8pQiw+F9A=T4 z8UYU;$O<#v1HuV&2G!>?{G(|Y$^FKyXQZ-mJp2mtkmHM3?F&aexIEn{R3p?s;B04K zXj4sVC-0HyLjyrL?>!O07N_G5A_{7ii?ouRAE*+gL~3(57GUmqkS?kw_%2OOZ96!1 zR2%ZiB3x)G<^M-Z3|k*raU`&_8N`nVW4&B)0+oO`=HOODBzTTTWeauWq%ZzWqMUlTB%(9; zRl@1B+&Prg;-b=&S7N!1sRwCG@TH}5)QrzPH5RzoEvne@yLCNZ0x-yH;rk6W(pf8( z!k2_?ZJ5Vkd=z#X(NzE4?t8Z6BG=gojsL(4*Slp^pl>in%u-SH{;XZtp)T`yCO&q) z16pRxKduOA11LY51p6mHG!>;t{9gktnCVhEN?8GSkCv~Dbfc)V1}hA821TohlSKgG zwPDW9ZJ@eb4(09$hBj=p)&9KubcoLIG=2o-s+Usl8QOB8OafS@wLRRXHqrzM1nsKA~9770k)Xr`#Le{wAp4@=Im6ugpKOG(yY_80N3-&<;h*lkPs z`N??HnAEpbyMu=a<4cYbl~vqBIrdTRQz^lZd5qt-+c8eIh)x-d`5CY)NJ!VT95mwp zC*keN?}dTbFjj}at(l;YVUTM39@w1G@{jy@z|f<@bH-m!M>2=&IC1Nt5JN*mQkYgyyX#jEm_Gi%P+7S_K~sYgqFuH9cL_qgjTAO-H45>F&V)5Ekd44w=jK36pWAp>;$YAKu(*S%zAd3rUliVSX;CAQLa{? z9)nO!sI8IdCXxX%4&nM>$5fhPGn|ADPhTOycFTM7I+=AhM?0!AN*7s2eq4Ih;b2XQ zn}aMjE_wY-360{uc%i%(=D;6XXOcN3idZNGFY*BS6&~lMqllIqk&`pRrCE!b`y1;R z*{l|?pN#3JtYsmn?cSeNe1ph?0Q2zMUdj;gfSi~6rx*m8*Grqpn_gkXR<<*TEw*Hw8rXT_otT^W7i0fr8d(hp@nf?8FE$(!_ok z?42k(c$u>Ix96n}E^b0|T0UJ37Y* zPGd}N&czs|5|7AT5HFv$6mEe{V2CMLg0W*>AOWMo87C7OmeP$Q;qaZ@syr0#oE9gZEi5pP$+(p{CIVT(9{i5)NT8wO`{OZAWFFq(`=zl^C_#+z!o2 z)cPF`7C~3|b@vWxZV}|%NiXNH%D5C$CP914oG^^aIZ@$ojnrczjNI*@)06-xIEyi_cpI8d>-*$%=09*pA-hdTzzqM`nHff44r*k zgdkIRn+vWCSgNSfw|rwaKi9`1F-YP#3wtpL3U5B49RHpnSZq5)Ok(%>-_KD0D3izh;ItJ-{AyCHwmYP;y~bv(p%wsxUbCWK(>@_4e2FM#rL zY1E5emD>n;weF=ZyS2k5d);C*BI_Q`lX^+;=R5jVmEjqY$R@Gblb9sm><;CnmPZi>b%Astz=p|ciHn2)FCP}ed z);>_?%)71|PENNX)BY(q@+&~?$8@ND+2cOL3O5}dY1-n=HxuZFH#V#)miFr-v%;kh zRN+gBa(!6e86MupBin^o)?&m|!%1#|~_1JA` z?SUTQ2sLEz=rLdbvWS+|n40KpdvbF(<3<1an44(vOdZ~s%zW5uo`|ZK&)8pQ`uus;e!7r=lCp^#^lFmu zGBMYZE%Nu}Ze(E|kO!8Y(jUT;q*GHUl&W=cdIfk^Dq%N%nUpm_#}>6ZIThUmjJq@I z>^3xWUgYhWJ(C{uo}k6AZ;10i^3EE|rSh#Yh@Y!Xq@GtF_hgIk^ug`7Iz)KKKI=N% ziH;{H7R7Plzfr>J6<9`N^7nK1U*KxgBC7cSw8T2hVPYt~X`2c!`c=rdy{Rd73oFsZ9BbH274Ra3$Y!bF`#E6Aq3h zL~vW&4WFYtahza$xrD9?G1bgB^dL^THg$C19W=}xwKL;-j@T82I{OdRnsr)Sc9~$8 z3R3>UMCjTx3ID^kWXBr z-%TZ;##uK4SfFV7Wvcr1WTK?1pvGQYtp)d7*4SV25UI-Ux;+%3cA%jcR+SS@6VWVG zDkWJMgA5bXxBLgl$jC4(5ZJ0l|7-IU(Zu`2CiQSa} zGTMHPAMo+Ew2Nu(24jf!=0k146Nh|&@DSds2F^t8x#Y@}a7U42|M00iM)XmVq_yp>s7J(N(R4=w62BJ9=%l(u|BcQMq zJI~w!eSy(9c)>ai9YMedyB&bJ#uGwh%D-w8Qj_O9m-ZHdfvQ8+>C}8M=_I~5X66~x zg96?dPn*FBy>AMC1HA&~aKNTza7KR{?Dq6^c*cIG=gTzQeyr-1O52^wxNI*6Y=ko zD)fP|SAYd~fj95TQ3{IN?-GHmo@wj(mFx%Pbe?2xlhW&M70r-E_a7O(^ zd!#n<*W31L$Xn3LAp`UBDT`D0A0F~>Es>_JHl7052s9E!OHtcRSrVH0=pZyCVT@z|IPOB|gWKf%%nPTEVDa2u3r$K(^Mztq{&P)-xa5v0h+U%>)ExlaV@B@ol^X#}? zzFN3SjHc(6>siL*8nq1dju#1fD{#QmExOQYPedsW1|NM&_yL{c!P0=4rgg<~iDQiC z{3Ku^bCIK+GHa#=1++VM?Bs=0&@o$<4d+>q58H#s?0W6Ydyf15wzh+XR;55Dru~0) z^Qm}gCZgK5K))D@pXX)udCAL0n54wxtZ~iEh1jG$r8dQ0L9B}FGIGi{+NaZ`vJIT( zNQl*`m<0of^P}b)Uyu`Xac`%#bK2v#y9U-T>F4Z~?l>)oYFWHey+kdX$Jzggefk(YQ=%;MMKh6cYhcF)c!BtyT6l?@X z^lKiJ9fCte`E1)*5;qAYq944qXvmO*MYGrcJ!UGoJOiHq`l4pK*v3jl{1OYdTu5wPu` zssr<+S+4{=MICF8P+pTUW;hJpyv}$Tgmt=v68`;ITJ62}vaeK}U4opRcSt3P+Lnm? znT37cfoQx`2LjUl0Rv{QSz@fvHG9SKc*zRM;cPHLw z0>;@aKlF7FX{>5`_%C?YyLyIwMLXit993VN5PIAm)OTAOvlCl*Jm^3uGRgnoFQ`8d z#OtrM5m2tTF4Q>Z;U+)Rzl<$&IL!eCjJ0z`;2b!0)0wgrV@z+w`|Y4=AC}qAgq@6) zdp>`uVWZfPZ%w)-AujWv%x#=LhTxMauz;rjeX(8X-Bx-q&0M?FS^906g&}vOGT02G zi&nVzTq#CFrCu&4l@_VV^F~904&=!(xv7NS7#HxC+9 zzv}<*xuiz$sg+S<&&e2ECjF0X;v;NtL)Ij5cc5->3@5scB<+9&{2ZB z#GHltww%+)0W6PjTMu^zt0JLlFeM}li+5sUTG{y}SIXhK4Nas#N4I>uWz4Oc>}mAhwCK`wy5<`~@yBh3fs0NKu!mON_WTVL;p+=1 zVs~crs23d)EL|6#FiOqvDYgA`LeWLUXio)UdJULrWgXyeV*^gyF`M`6!Cr~)?Kt`{ z=#U@mBDE}0d8#DbiHp(ot_y?>q|3i*ZO=pZp0TMIQ;QT4R@^%>c$q;-y4mV7DHCgx z8%yLDu=Pv??t1CyQ@RbzQj-_I+?iFs5oa^z^F5j60xw0$>%G};1*4I=H-|5^Co@m- zH#TsZ2c$%jf)+9UNsaqm&wWmAgBF9?`R6l`-2$BpI1~(`udx#)m6+!}?XG1wM*|O& zd+;&&FjW^4dqGTc>lOw@=;3))Fwx)id=9;+fmM8J&j1;P+RD53@>0(0t6m{zFrCWgF7v!%SyNb)SuyjFfZwe;yF z;Lr!zt&c!MKWltQcCP~Q`aE#hWTYtl4fb65#t)63Pe463Ff*0jv3v&|5vWv%sxh+m z$926?iGoXp|77$GkT1TOlHcAtiOkyWk=1ZoohT5p7BS}#5PFh!v`Oa{55k6Z)jMUc ztB2f8{hU@Y7#jd84ntQ#N9@_6%B~#3OdSrmU+y;QP~-|yuuL0*66JU-eQ4rtlcuX7 zl*dZR1ADLPw73Fn>_696%oka6+M(0@*iu) z@{bvu73NTT`-D2N^d0M{%unmMbo7Ys`qik^s_MX8O_|t==3c^S0mCTw$Eh?SS|ZK%Udl6D}osrUNCr{SgFr}q}J!IDwT7` z(EdcN5>9Q@;(DB;yj-`i-obQ)&~dEi12+8bWZv9(P+nvAS=84xghAcs^C5erDjfL} zx)E#gYRDTG#7>Xj6=6uvw*vgFOM(i=9i7jD1f}f(>CN~Q)>{?65|+U z;%NwqDu5b!jIm-U!1@}m^^n0YhYBLE$FI8~_8qxobNv7UgA8gVOW-e*L^$a8se?dO zlOwsVy~e$y{}FaJK*^6Y)9O;YYFx>u7!k5nK8U5y*ue_zh%zcu%M80YRGIVugMF@# z&ZA)3wTvAz?{6udS6hv6{b}zsm}4;MjNlG;+ScULSN$V(eNl&TBCf{Gj~5 z^fwApxG%KZVSfwFkJ|2<0iMIG?|lJ^*u7QB)fR)tOC^*ftJQglYYv;BOu0bvBK>UD zW8ezkzrt+56qN{u2XZVz73%!Y@QtcG1vB&-`kBH&DJfB@0j;eFf>x8d~z#bF{F9aaF>+3k@n1qKtjp90G9;km3SIgbPn;30ssOwR6@ zVN<0CXkbJGB4cGw$0r6PdDWIm2(*yIWjCYj+RF*lOX_OMUMv~ zDjJSI+VkpAtf-|-byLkg8Vx2w0&l0%7V$%Njk~*eFwl09F`7u~Md~tR;Ks?dZ|CB| z`-={Y#dSz%U3h|h>5lb7CBXd@QY&M(f6h`c*tDgR#{sK*va@^penkb0nyJ*WaZS$* zd{BQ%v+AVEL*Oco-=cV!adNYU5hYV%>MeT#S$P7%Z4WF>EjYmV*XrWUl(%8m^030j z|A2g#QS$1MaTk>r=Exzq2wh(ReeSS_9eB8E1+*m=Z;HHgWk8cn7xpUYajaPCjZ1ep zp{QbjnHv|lucl9;R>TSDZe?v<_u`lFw3FBmQ-~K7fZSl$1U5EUC?v>t{Ah3t4w^(I@8Ih1Ie|g+h-( zua!4P$P(n&*v;geYFSvI;z9E1al!aP*sn11a#Wafh9}5p%j}DK$9V7ufgL>DRfy6K z3aKcCzVe*|Y1joi}H?rh0GW2)aqj*u_8&Dsfo3bt|*k7Gnhpn^CKNMoAliq}d{*ej(Q z&Q#5m`*?I@Qn>BrkGAgzY9T#OPt6HgyT~4lADg+zT?uLzcxSCMoKzpPMksTc6o445 zhvYQ3N8l3p6q?;9z&7uy=4ufcK9hH_1Yp!qa=CK5rDpqPz^`6qRdH0aga?HNf)kS! znrcYqFKtDf7Dj-Ks&S{^PQtN$X+okRDiOBCFC!d>yo$FdRWDo`;9jNHw|CDdkK|?Z z@I4125(`pu#j*d}*`xNND#Ia*9X-v@e*DvZ5<<5uk5=XCR)*xSjMd_}){_sKxkvPZ zo`=BWj=INvLzVH~3;U`jPrgBC{8P{>#);0@Dif>BacE|XSxJ3XxF4q?J{fX0?3xvn zv$cF8+LZfv<$fR=As+7rE5h_O#%wTD$rrO>k4yBT*7lB|5DL17QDQFqLe8^&R`$9@ ziEnha(bV@|+44i*#EYDwV2EUxX)rcHH%rP9q$pL5(=1ZnmHw9%DN9sH)~v#V;M@

6YyB?IruOYrH1pAf0Ec_2x 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

m^q5xLb>Hq$y&{mj< zUB%Iu=A?{fK1E`yOXqfpc%Gv|!o39#|3VR5>4X)>V;9eV2L>3-CJMf@9FksW6E0FH z2aZzJ4?C2iIz*G@sP(O?#18pE)N&E+IWY};yKIytfh+#aqj|bbj&dDccj=T1YDMvoo4=LEwv4cx(br(&cN$?d-J`@ zSL@azP|}NbWdc?SgsMH0ai0Be8`p;BH6n^$ZU(nQ6#@^9nS%&ueSV>SR>ejA$r5(i zBKIM$FMjSrs^koX;1SOK#T}wJvu5G1&T!wYbF}|A)~|pJq(0gRjkW(AOW3$%P-dI`n$gwwS33xg znPhDT9p-{jqb~RgN(v+j^c*HL(rF=?HS0RcRMGqzwVW%MB+d z%9L9@x1IpiLXRVbp6*)PGp>E`Kt=Mq3EV;3nj)^%l-@RQy3C=z_-$;REqd(YBLgf` zMh$0!rKM~t%5fDIk{nl2xR~O4?lkBY<;E9BGOc>|3@x2cK&I{om`H7ibbzV!H2!t) z7yfd^xh~7FqN4ez7%j09)eUMU_WS6=>qJ@;yc;jeQM{|@a)LKQI+`8B4B6?@l8V+E()QYMA8jWaWbhIpthvR8dy|_8Zhj;= zTF{!a931njY$0?nKF6#9jx2tBKIlT+(=`rm0y71vibTdey!^~E?#!>r(VFhgPQ-gtY_ntOEvE7&^nY({uuHl-rW423}!AGW8GMIRnUu0 zn&5nqD$6o77AGlTl!(O4EIRLW8PlfOD%E{#v`%K5dc=%@j&U~> z=K>FFlqFs+&Vl#1FHw?*Jw(GIJWc&Vd_xwLNZu>mYF(@+cC|m0#GM6*y;>^C;6RYsL9O0?D@b8_&yo@QPycL|rIKZ{S3MiUtwgV> z;a25XSbhHzV%Ah%?MR^>fEfqk;?=CtP?;kXceFKX{tUuSRZiy~SfiiOihf`{CECZE z{JB;htaO+cM=@_bG)WR~i7Sa#F1syCd41+K{d0kA*3CDqRupB72}DRU6k~uCR&)Hy z9I1$k$ZI-5@~Vy+4Q%Fr3)~JcI@J9=~5MPGX62OpTn*mZ{HapMz4j-US#9KeM?69m0NQBBE{$?snnJrUz3yWrb6};cZH$2=0d96q1bE2BA=aE|P z{s~9*`u1r(EWo%q1w7sqJJh-bG5>ELm0AM)#ez)xkQef@+XF4)?MS!j8jFuUI|~fl zReZ?w^kpN&QnOb|d@BEswFj+lB+W4kki=~V)+9g;qAX=w$gVj$#Sn+A*5h*2Ipt(nj`o_j0bx<>@j@UH#&4FDkOfbEFg>={~R2RhIG zi(r_|ovgJ7j=_!MM*UHoR?0l@Kc|Zd4`JCJ?<~am)J6)e}*Kk#_UYK=L`! z2Oe8iOg!1D@TH76PUy6%;%=w}FcqC0w~MeaHeK(pdS&*<$@7yUei^ZKpUa~{{bMKW z9K~W=#rRlFlqHNXyTG~sY=8cgQF)OYuDQUVYDV(0<5DB%iaswn4R}bBV#UVGNH=

=*{1YvYROi!gXBjT}H0a+!M4 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

@8dpw4Zk}4* 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%0AmO?%1XBKEawzu2hG^kW|Yc2g# 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

Sc`q>UsG>XmWfI0kq%llYo90ff` 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�Qsqb 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

mhB9twI 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Ηs%QYf z-QDqfqC1&jnIEw@BZl@AG~GP8(eMypEKtdp2l zT6C0=d~h}IpfIGd|5Xqy03AAvRA2+Ym$jyzmqB-)r~)KQHC`m=Hu~9*lSd&6ArFV%CLo=*sJ=4UQ-?ePfbay6e)MIM(hR_3Ybg#ZmmP;b~OWaXcG=e8#i)Uvods zqk_5{j%-IMppXsO!C_eh1yW-g;EbQ%8OPM;cbV3mclFGmG|p#o=s6ST=g$eJJAG?3jI*~FA&bH8 z7%9?x`@(s;rd*Ckp&)5{-hs{4=r1kOr8nn!@>T<^-Kk~?aqHWz6XF5e_4pFuaF5+| zkHB`7qP2ua@sr34uB_P>D4K}$ ziJ+6Vx1s+L*_y|oXz8m@qw(FSO*2;-$z={K z4u)}>Z=U$0`4pMyn)OC4NOhy9EYqsST8l#~wv!^RFrmB}OCG2BzuE-aA7BD2`NgDCr(UHoQoMAA2)or&AQShBjbkHM^zYZ22fd#%pDRC zvjxkqC`UuZ{Jk% z2YB!a&pHub?6EP_3Y<#7~?Hjd`)tkP+VSda?wRFXJ7P}7GB-z z(&eGOGr6sqUg-7%kIY`(M!7Q+1=Q(j_CE2zWR00#t)`e5VzaCEP)K07AnATNq2~+witQgZa~M`veTL|^9*5fL=?_CA z7j|BfR$=>9E)gc9r$YX}6gdtWEx`)&69}S50g`SK{i8NhE#Yph>F(TO!vaX#|Ek1t zWxiw*S4zfRjzoiJ>a)skC<1Guqx=P+zWBGFyK0ExgA@FV*e4?@!s(>s5ww6?Bp&y8 zxX||cU^S$_D(U;(6KGemIaP~vyEz8zCr=e{bE`!F@)H}Z(pONk8FIb1LNfLTIUYJr zr)*txP2@M3bG%o#{LuuJwsXn|B8J5fwS#+>%_x@4YtXHPUF?TDj&TcswZ?1CMI!FB zW9tXMk7lZk4*upTg1%Zdk>x8=-nI|Z^nem1sK*d}tI&<{(G#u=cPNz7oq-;AXrx$M z1>%!(x=jw=k_ThJ>qF2LyNkElS2+@?=RD2lf-^pms4yVynSwW`7h4oiP3VLNyah}f zwfmYUxlrv34M}NN`4x)`ob})fzgQ}!L+O$%m`2W(#h2v_qA60Ypve*w78w41RgZJA zDy7x~5a)KnI6rwA$Wtkx*;i#53Cky^gnc%p5whn#& zi)t*#oD0E2#=$!tKC_B)Bt0B^s@w?RoMNUyD)n*Bni)aY>HPdm9?bzD9Vs+UjE(~r z%Gp$r4TWZfG?Vq03OG!PFyO}y6Oq2Hkh-Al+j*+^n3XCIn@r|6;3XZ_fsj45Z(7R& zIk%K5SJXm&lH6JT`y(5XIAy}fJ;`>L47~GIim>umRLBtOaCgUiXn9tp4RT}WH#f;& zn(p@Z5jq33<>3l-uDqz10=lpZ~4U4EUG2`?mw9M-OiIE;ZB%) z@pI)sob4`2=d6RbN`HJ}{%Six%P6vlgPsWqPcWo596CK;t)qt~SVDJftgrD0ytsaJ z+|&bf!hT=AaasDDgV0znUkZyL5c(?kG}R8Q4k#$wTKlETBeO}c7jNoo1u&QYt7SCg zh;$+DU<$tn*}Vyu`>|A2kp)B3((uv1`k!w*cpt2Ww?jY{%+No<2X(hFzs8{}7VpK^^*k?D z83Houee#@qE3b271S&OQVU2)G@?;o%)~Yxl13}zBxRL3qe^AwibMmDjO>|HyHT)dm zm0%%V&*qpC6M8P@QOah9@zqKEoXfublR57!Sa?T`MOxY*fo<)0ROfbOh%5qtc}>3+><<8r)h zyXJ`M2z~;xhi07&VgQdVB@u5KkY#88aVY<74wm%)=q|W4blUmO*Y3yzn^SzmIj(V+<%0ZE7hIIs$#>;PMgIVNYTdWOS1!`vG^p0jn=w+ritGLGIv6mS6s);6&d~ zvEP?*3N&maO9>3dvx+Jg1vk_3o@Ih7o!=kH)D1Uw}rfvraq+ zsk*%)kwJLG$y9o)XM_PDHfo(}+R+W@wIvU6yT~VJ{!9qFc~k54qi_w_| zZb-H>eRuXv#?HLaF)Z#9oiRd#Iazbho`#A|u8>%4QaU|5F9MB&X_&AZ&93o6v<6?mkq|F;!!Lk`#l2Qj)z0G-m3TK;IEr?tVBUq37w2Fw!N{B z@DK~WRnuZ0+o_=v2T$Mn22NuptFFBeh@Qfpu8uVco{E^n*t#AZXHnUgCd&_Z zgl5!3_C+>pbn&kq(&%5g^MbdF0G|+>_v*M|A51F(lNFv~+TVDTUAH?UI{>x>Zi7-6 zlnmehJfh!mL0eo=*X}}T=BnCh3iy)rLUKDYr!mHIq1K^(L+H-MDyeJhn<}Z-XmFX9 zn|5ymo$Yg?*hzGF9xvOCDcn)|F~Ogzet7#zJP7I<`wwV0@DGyF6YbRMPp>l0RGrI# zAJU$fiL*jgAhP2Wt=7K|+RZtc9vI>4SuwtSpnrbL5A+!Ooiq!z60duZf{o|8oAqgy z7-j5oYb#oGHXgJ%yr~OG)3zun zmUUAB0esYBWJvim3Z9^A-_E!UW=I_O>a<#P&AU)Lnan1750+_DUOob*?V3EU3rgez z5bX7|2X#Vc%zkPjPA`6hOQeYxzEM~~Fgm+58Lc4KrMy)%n>&Wkk!8_MeQgi!ZGS`8 z4P~tL?eF*jb;E~yFZ5<5MSj@_WZp0n z%^&|RwJDuQRung{7Dfn-CsFiWK!<)quO5EGEZy8UUy$H$O>m=hFaN4YHnU>jKDscL zdnXlzCV?)5LU_G8_U>xiQ*kLQ_>f2{i#A9}TF+k4QcCtE=x>19V4|vtq35CPO`kqA zJYjp##?fKT{pwlf=)d7;0f2UaHAgKifi7p`H zMENE7U+)I$!C+)n=qE|JnFUmoP_)YQ-bp09&7)NkMsRF1qKCZ5MhG^5SUK)?)?m{nc8nL%<3+l!Dv>c$F$=JfYq?tL#xK`X z%CIVUU4#`-pTz;h_=heIPI3rsS2Ua|!H{AODTJ1=OJQ3Va4~6Uw%Uzh+MpZWrdc1k zFRo7xVOE@aqgO!J30uRs(U}V|#fc>!OrmMHqO1bxqL{q$O%BCyt8_(qH5t>NH6W?sWkYYnMQ03*iK zu#AmqS>=9movGHam=lm`Pg4C3>B)T;gxvQN*)DzAqzxgy zNnqxKRn6Wzz7RCkQLQx9RX9+abD7u*BEmHa>vM`E7-E>3T!Y@bfYwG6aCwMnicxHn zq9agJ{Jrt^@ZTUq6{#Ek$ z^_Q;i|3_%F{<_6BrLfO8^%<0PPBHqqc~F%1J4*I9%E^O7V^q5D)Iu7r{!~mB+ersW z=6dpcZ|!;Q8L$T#NMlEp3kLR!+g}xg3;`9Wg{mi+ZI8&$t`OC@11LIo{vqknH3*pg zMcd`hZQ4QUHskQosuW8VY>*7){cwb+PE=tZ)7gvC2*X$1`G`%>EdJ1mx2@s9b=;o; z_dI@rE|Zox@Qq;N((&=Fz@NjGGhIWv7Xo4crMkW)F&X?-W0S)6j-Hy@mBVB~w77f^ zKwBx$CNlCernfqZ1v>BjpW;UI-=4E*9G64^msihkMj{oDg+|lQ-k)5%Z}3^DgsZ9RePMEZ&F?!I)1~8hkY5cdwXAPqxc`eyKTE4Qtg5A zc{dt};Tma{qxkYjL3_=ARx?y}u0_%>PXzP^P$S^xaM4$VKvLd6cL4oXpebBe-S6g} z%tt`sbP{A7Q3IqA5?bZl)iC5+uXqj?*`1T~n+bI334EofiTM$+r%q&S3OEnUa|;>B`Ei$h)~z|^j%eb=36!_LHjF24Wx&ENfLvmO&HfSfsB2RsFkZi zI~zmDG^k{#m%RuR1X-T&%0ADYq)4=$${~yvK1Nq~@3=&ql}@P1OV_3%H$|pistP-9 zpd^f=!jWH$b0H#o`RGv3u;n`4pAj|0m9>Fwrb!m|L8GZv6z7uDbn2&va#0)qhkL59 zXP8(+&k2!eB8#sDJjk_kv}&gx==WlrCDi9iFg%);Ic6i~bZBkv;1e5Z7AaRxgPyTT0*hPQkRmU$ zjIVe9we?NYx7hQbrPEvpG0eh;LG-iMpSuY?Jvaiehyj~^JkvvY`mdIqPJ$G+oDnbw zwN5NIvXA7=u~?H>{?5NE)Rsr4E4de5l^NS!L!Tb8Yyk{uye1T^Ae_O#q`R(q8=C8S zVNHVgj6{uSc8{G|jBg|4da_)QCvWg!{j9${)3$iv2a-SiUINM}O02X%n5p4wQTN>BR;+fR0bkX#q^2>~p=q8xe_~N!= zM=|(bY`o>6o4RJ3Xj-r9b9%$`S7mu6nmd7#w?Y3P3=y#{lx^U$VXppB4`vMfrzv`W zEL&?g4muoh9Ng;WrcX5<4m#@*65`NT#~VMYjYZqv+#_^5N{M3u$vDe5);lTuz@tyw zKyOdB1#}>}n=~|+x@S}7KHy(@$f-P53IM9JgLrW)_`n2NBT4IhJGox%t4s?=xOd=5 z&|zJfaTyFQtM`&`fEyM}tjoyB;_Bv)>+3h!rw-tKO`+FhI!WmdcOMkdh_GO9am5PG zQ2mac;QYlY_g~f}`^al?1vpHQrwcVco9b8FcanpuvR8i9JvGEW`*VsNf;spSIxCdp z!jNMA*hPC7(jTLQDr`Fcz`dBQi|Q%18oFjtiNYfn3A^v!Y8|?R=(_K8o$q>4@}9Qw zVr`Il@MG213#(Z+(a+$Dndp<(br`>{qK`+BOHgc^Q$ddK2w zz+#%|QRA~*TnE@OHL4D*)gIgc<{pj3&t2X|%4Fd~sM#C+1O|Z_8&;%mjN(n+-B=^V z2ml`t@HSjymvZ?p?=IT91Sj6uZ?_GaW&VNUUz;Xt|4)e;*me0(zlxC6C4&sJYoBH*>kyfzWiiJya?-82?L%J10+v=y;O-V{gRqhmCEI z@)rHF3sN;8(#-4{Y)vAPeF!yS#X!*vvwa>g+lxyAGdCKOrmh0!Z50KxS;q-;`aDb( zR>Qit5~tg|8&KsRaz-naQ^2?8k{I@?VRCcF(mHL>T%J=Y8ra4Hm6G7Z4}T3@e>j%< z1~%GwOZ%v?iVf$l@S`U>bw&Ft*rj*d%1lA%sJ=;R5sYPF<~rK+v+(Uq!g$VI_SuC) zr2hxEGAaSjQsMhP-OR(GcPsQvW*pM2YLeYWJ)#%=L${ogwNAg*_<^w-(^Hg#xdns* z=|B3`D*i?WaB}>hHcOjeA5sn-jH-nwssv>50!>y#(W{&K6XhHp3^@wP2mYwKS;ZUH zC`l8pexPr^$p9l)>}BMyqJk>hJM@S4kTTX3P@b{5`wSpHzvYOJG~aHA5-0o)t0&_h zYRIv&$IDZ&(M->Q$HGp@N5Z{{6`uWS3WaeRUMS$xcJBxXGPA-XWSm1NP=XB)He2~4 zBUCdXuh8Tsp>54$?svx3ZNVu}6uG^Hi@*6H#{}XID3i49jZbm$9Hj&Sqn}Pa_-Ja+ z@`=`dGb%O{sZm#55W&lg`(#0?0^(rBgu*P4=w2@R50<)?u>4%vk+pH4FD-hkRJnhV zZ;R$c(HH2->(usqwNu^=Pvv(i1?a3f-S?`FS=uV+dosxgxxj)N7eQ9TV~gqFatKDI zEN3Cqz)L%k0wZi20+Z=y2D7RTurH@OvOf}=cdxv9Z}2pP)sXMit>QnrS&yta_#@YE zB_Uz_&_C*3Z9Ch{<$P39?((shVvx8iaLYj`dk7;54hJ4D^3uIFb7X6~tvCBLL|{YpOX zZXhDIc-VD84D%L2If^F!F-%=ln3xnC^%^5$tsKos4|64!n}9@tR(mc19yW$Q%ZhUW zIHS%FSM?yTZ?rcoAjRzI_23GGWtdwpj18y6(&&fi_O21L87v1~Zmc1|{+toZF7JI+ z7pphTK`|&=*?q9vyRHiW;fR+tY~T0l6`V`O5|v8Ye+G|2zyZUpuspcmqDX zT@jm(-4I{(C?m8i**m~$&&=6TD?|$m2nBks*&+;X{6!i#=q14L;L^#_m^zUP+E=PI z6Hpl!OE6!_!@Mzxes7CruT2{?4*jd$>7JplI`*Yxgk#qw;`yb>XEkNG?uux#P6~AH z+o>y=KqD%7-BwewR1=)Ct=O##u87gBxgBlAnp8+{65D8g?8q!xe?0&_K*GP~&?Zg{SX|Q9tmes%OLz3Wya$N} z@Ax0!bZ(3{zSVA9o`*{@(A8T)h8E*G)p5J-Cw9vN=>>*FZkFp64_1ASy&(;*X7^~| z#Tqbciosc%ViBqTg%~p@!KCe+CZ>}k1PZE*(5)e(rZj0^Xv1^DD-4eG_CguE-lW(R zTe&NJMQ2>@9lnQcABg6u_ZL(-yK*_tI8jEmrrH>=siHduV6+zE3Wi%#xd6Dju55$H z;RK?%3MY)Z%{)fEZ;(|D)>E89)lH?}abV}?1kr2guIyR0iTyZ+n&YQowkf<*+6jNr zO-Y3W+RXZg1_|_BA$=OWddsbEnU?N^E>na7m-#BS)o=S!kIP+h#SGXe=WfA1`khSK zve#Pr=b&?~9V6I;c?oF-7n5G0|8mw{n3x_}s|Z(g`7KMDC5^k7WUbZ{Be2^_ z6{EdMR&~Sk+jV7Rc+TQ51N4^WIUrEUtfBdpv>^SK6@g|U#tOO*fTGU)&@+~oXszB{ zmx%jMqqr$dEegQEz%*aqB}z1dW~#Z$xCxm`N}?i8H6n_C%c$!bADN{+j4}Lz_ z@y$5`a|(EjGv0?T5LhT>46Q(OKge-oC^Hc8MiGZvY3JF-T93AaqFVs+kJQ}2A~o0n zC`Mo*$GhD<;Z(`I^qm9V^U^9mofXZzu#m)Nmp%EP_BO8AlrPL8EPBlSxpYAhab$yI++?> zi-14!U-&L#_C_}b8=LMT`sAE><41>E)==n}+uU0~B@|2Zw5%tp3&Nb7KUVMgJj?9e zFX0|~lCKbT4Xo2H_N}SQg^KXALz-ZscR>J)cPt+9nUt9<54Ul)dCzxJIuWnFJ(z=J zNl~Q4C)gK~a>pfu6#KH!!`fOZmk-qLZpEFgw3-xtblua6jhN`#2DvTGm*!Cubi#k8 zD0xxPi3yO1ffXzv7jGMJ4-bZe;g3>pEY?+5^>TTH+yGk3Z{#Acm$e)* z?Cv{*Ux5=Nloe`hX81e{c49d4nU@0Y7^aQc`a?}fQ+K%*U}f`T(P1&w%j4e}20!`L zALOQ)9ReRXLG4_4)6>FN_$rcB(H|<#_hGi}1fS#qM}3Z3+>Ep6_tTz~%}6O%Z3@1o z$j4{0De>Kj0bUIlm#_0^6qSwQORn1R(F90B0lCEpUzC)&VC$o^!=I)15psnSkcp2V zgnxYC1tuO)_Xyga)2d%RwF!6=-5u&V(h8DWcvCRPve^kvgdNWTLDdr*At8B1o=-)0 zG25LuI@=^+Fd1Sb~iZ(DTzI{|d6_$dCl$wG^IAONF;-j{i zTHNMk9_zDB_T>nrDaoP!%Ja$PAn6*WmPu~zonfbVZJ@;NbN`AJ2lSxJD13-w(Q}R< zJ&{5jRMlRG|Cg=2c-ej*3OALWxZWq3B>#w>B*59vUD9N3;5@xaJPFhm*n2~LJc);) zP+HzQaeUb9d&Un8=);Uf7osE$q&ihKwV2McbgEX5I=sOR<2ov*{ozc^n;-@-@?XBF3g= zp;nm9y{w_dXGUUN7|R0Bs0s<2nDQxb@Gr;T-qkZN@$Zq5Iw0sARE*GJ)24EE2#Isv zNSprI0Y@@&5KCncDURL9m+$4vTNJIw2f=PMN)2+8$9z7xALiVgpmU6T=;ctU4QbhB zX#A7^4DHkw1tXbV8w2h4$~3c*-)ea31=btOzTOjJq9Qv9z(;p>sj ziAGR>YX-wFBoqqpB{)7)PpS+TJxmcvaNnNge5YTVgLXXVF8{uZ|z8*|3A(+*b64@ zwI-@3icY0&#jLkn^eb}sp_fLQ7=1Tq2EB_GWRz<>ODgf=r>K30z4{N z249Q|9%#%jx~jAAmm1rHu!#C%W>rW|E4)5;S4-ZLti9u9tNw>T*1_^{L)c)^! z9(8twz~rmO${75QnyA37Nwvk4h>49oE5olqxNWt8wgJO9OW7kP!k*G)#EMH&3iM6ytp zQ&4_4s7w5IzypFv^+EChK=@i9s*ccP$Ej1A)fp)OrVMsnnOrZzAZLL#ExUCJ#yNz@ z8QHzd%W1(h{BK8;CF3X-vDkKbH8p|I7bP~yUG&g8C#=;A2B=UI}1AQbd$ZMo_ z{Um>ajd4w`EA(&%`^UAln3!P&d-*Uq$L&>ss4yO*B6+5NxR3w1zca;HfY0eMCSm12 z#tg1(s``fg3L+SWbj+CEWkN)1=O~~5OfPH*X~t2_-kFQSe2w`8(ChU?MYCvqDKV4` z7knou@4Mdz>C(-ZP-OC&)_+#iA0YX*@>@DJ-S9l~r5lgU0PrNxF`h}2%WWcPbw^Jr z?cX`%KhE%RS`!dQXTTCNolg;%@1qCgw(|FS-#_{J`Umv3NViO@MaZ z+3Br-kRP{Qny1v#I!%&ZokXjrEph{HLXTzqS28BasmweI67vVvUh8)+y+fk$6aDtb z<90PWF{zc<=Q-uotDURzdVC`HEBxRT6^6mClm1df7RvBot{FJ2#~4m~0^WZNwuNY2 zgJ;vdcfB>n#QKfhrY3lb042pel^)HM-IN3^vgs>xCn+ad-x)(ooNk0S$L< zk5}XV?2Rx2x9~AP}o7t8=S9ao?_iAtx3=>d~Lf$eAh!?;;`|B9RiJ2rdI}WR)OiK&%tQV$wg+J)qnNL3H_1<#GMGu)T59?~^7`8F_zn1UtykY(?qyvn=lS@&QW_ zR}RJaUJ%XiaYHE}ohc2M>*GGT+C&<`Q#kBIS(!ARB6|KEnk@H?!U09OHb3`?6N=j# zS*79pS5p_4foK3h6reVBeyTSE`Wo0#&8aWw0kU^CtRSPF#1aGZNV?=gvyI2mh8dut z86Z+9i205HtVH=0$EHdM9Fd;{h&7_yw}x7WX_@=*5SRI{qiI}_RqJPrea1>v9 zmIjOUx=`nx#CqBicPjLy6oMh|#o4=8RiE;7((f13eIME}WKM7r4H7lc zkGG7rr@^{(HAc_Q{TlOr6EM6#P8N|S3(PGV!!HOoKg(E0qH5Dih*Qs2jm4h_}{3xt&T72{4=IK{D= z!jaP+FE&*T>eQE^9{aQ|&VZC0k5r(-ULDQ>TWs08`jv-+zzi3tFsj+LEtq73TJbtO zZnQ3?GY~6se2!-?WxlX*PxDn(LxKi@EvOg?P~MKkW{-_xmhmWn^p}AyJ+p^t5IwRP zp~B4oOo+NJlIb>bD8kwXR^b!9a2UGga)vioA%EKW(RC3tB=l1UQs;WMS(Vz7DCkSA zMUei{9Rh$hG_eA7YvlFs!1P1AG{lNgFfE6Y5(~>qp3;o9LTy7nf9CelxAG zU0v|1YG&YDkyriOIFG9@7OYvm?Pmv_{{>^2VTn)Q8s*)`RYs{g?^>y{4Ng7Rf zJ;n`9G@4pcrgZ1GD}cvGLQj^XG}I*9^aB)cl*2iqbNrUgs4yWNXGSV$wkrJM=U`n= zoH0v>V(?f*c8+HeIeiK!p_v>%!P=vch)>oE34qHD4DXgVu9Q;L>z^;)Y%S3RF3kXO ziIeZ$kI*OUsN0FPUT1cS+U99qx3g;{2<_aybVz|M?M*d!qG zlg`1;){zCK#eon>UKPSsEDC!?7-)WJi&rl`aT(8{Nv+}!Wy+&ku~j`NK*up79JEt=8^)y{QkpP6j5 z9PRQ&L_DTGOT`_BK4|7R0kuH5$LDQsx zy$2=OLgtxh0FzItnN>-`h9kaHKQp+2e^i}_{e32F4Py}`B-`^lcu%NRi~L$ndMbnI zC&E%f3@f5D6(n=`(<;A^TDOe-`a>F@@!B<}e5KD?)hb#dG+%Q^BVw?miyo(yA_dcbu{i!vr)#jxM)beoqFj zsS~Q++JlQguwH9d<3gUbISTF-&mccyGHBmRS@njCxdxdiQb9oM_S#)P(GII{_tyKsOZQM=yNZz zn92ERoc!Ya>zb;$LStO`8g^#Yhul;swo~0C@_v<3`S~Sm)DuQ$wuvpj!e512q%t8r zlR0;1;;nn01Um)1q6byZU79Ti@yBz@j!{8{?(eg2D!*z$c9zD(9fD_UuJMMvGT<#N zI&~I^2<>POYZ<|Aew{(}s_~T7dfY`7Bu%Jid9omAMdCrbxj2u1^qa-2mnLIEoG2q% zswX`9=$G-C+nsKi`hVR2@+uon-o1aWr1Koc-B>UR9C?*ivzU!i)3Wu)hlbXy6?XiL z)Ic6Ph}r_m;;h)1l|pTBN7+!Cs4oLnO(>54eX5}ggT z!hApi;6>A*04zeD$B{(wsh6(T{Y!b4wVf-|iiLtr<30KB4tUHl{=(CETC2Qn^jG|8-iC z_+m7HheFWKfi$?^$D7pVU(*+yE`~l@y%8BGf;WD;qZC`2>x_)}iQKjXhm>?)PiBQy%0P`q7El z-b6UD%(ZBZo9om3N5dgBbv!}}2+g^@G5nDnw3S|3qRx`f%EMhvRiwJ+k?(bUloO(Q z<6x@suNK{Co6f{3E@Z2iNmd`F*Zc=cr`s+DRDqnOuX~ba8VRV)P6p?k;#1!sOI2b4 zWuH>eJFWYO+`RjG6Pv)I~^EMF!Hpu#6 zDLBpmE4@&spW#=1MGIGd3myMA6kZc7IC)>%Z*KR9p5_@UT^2eWrsV#wwd{a}VkaEQ zXXw;AH_bpm&Df&}+2E$At@W!?@56NObNbjI>#NPym=MQjtI*%{Ss90*8pkW}+A5k2 zkbQ}u-(cD&U?bEP%n?gh#q+pX31mbwsOOI(&&j^v(lqta5H)rU#2UbBS1fy;*g7HT zD$W2z*}{Ft_&Q1L7LX+KM;RqqI;T8#9GPsK=IKS$mR7Mw?d1e1asrGsMfB&LlS#)2 z+HBsTRiwb2@j{!GPol;cY6Z$Q)ErX+Gf=h)SZS_!CNYPDn9JPM@SoUvQYEB z6k3_Ho}U-IE>`DsaX)rn6>we6s>k%U>Ge@&(f+D~YKwq&cQ=gmk(xrXT&uyX*J4fxcSXu))!pEJrxXB;nZ{Oi%gtBa; z5Eje~_p+e{!x#enWPs3TEAl=kTB+vcqtjdJ7~#(ugVx$Ord*~+6TH{4yN#HPM}Q`} zOpZU-PBu$ z@8(C&#P++UEth&W2#Pq$cgPgR@gX|AhG|Tr)b_4YUT)b)Vwr<#%lM}l$6%R)ewsdt zBVysR0#m-{yjOmRMAeUV?D(iNk}X=RE(}WDB1k<6rE{4x)6*?q4CQA3DIb5bn=OOfH>tKL;z05nII*&>nJ*79$@!wfb$ijMA`leuNS1r zJnu)|o@}3(g!tUHpCiB#Isi>xS|~9)aq#L|e)g@V;gMRhp5F4LIm|ecPK)we1NU~M z8hM1-FviCKGqRUNm+M;;ZYk}f?i8z(b&VwEMtEccUR&8tNCP^xrR_&eWmG_V5N*uKa#AW&ykZ-I- zN(U(a!-EBfkv?}|G|r~k22aUfN`n-lNSYiZ? zN9J+`I=qbLc2Dn!(e9ik*@_?@6h3WB53)}wqhygWBEO5e1y67toY=LDX%Zp- zOqRR)SkX|DtOJxD7%L;9Ke0f}$Swt_01BYZH!LXP zE})GEJghQ)j(%E(_9i0jeKm0dDi7$&PNDc!H@U!5>2E3Gq-cga^Vo!4nCVU{$(odRLPkGqE^6 z>=9wPoTP5My`ni=+jg_WZs>nC9^C>z7A4zR^oyxQIUEDXAk`rp{4ZlJY;T)yM@Uu! zKB8`UfO|9v-JEV!J~*JBbK;l0vVRndrXlOzWncdRAx6N&P?#8bG;-W*9sB@fin!$I zocr>n$tgkCwcDMTljhS|1D%121(SJSpZCp%SQ}xEVIvZq@?E)w3ONQL$uu1}6uysX z#~32JpMi2y*5r#~M1gfJWHF{6JCQvGg5&)ZYXsQz(};&DjAH!$sEGLPXQ!)^%(Z9o zF@5<_9{oLyJ}3h#xN+0$)gJUD&8OW_BFHm8-5cPvbnn0RosOUoz-4tUWBo_(Y|9js z=SWAe;d!pd!_k)rWoyGK7R}DduQ@qsyzopWB@!9F5*w5u2gmm%n$B`tFS}X;6CbNm zhmqWCYni5v;Fi1feeN9XvT`Z^1|O+Cz;nU|psZ3E0N4Wnb~0axM(DCUTA+9mnYg07 z+CX5%`~eF!P9@J2--iyu2M0qemkYf_`ey64bMjd3e$hqU?~P&Wq9s*m3B1z?%APu_AS)*$ z(T8*Iga;MOg1P#-4F+U$!@#Etacxmg>ZguSCqy zYb(Yz5Mpy!=d{zur z^HPQ1)f4swd(-4rTg~=*TVzLYDuQELbXM+64|~Zv2Y{RLj(a_=(kZg@O`;s!!wJ_T zXl*(&33^b9(Cu6-@^XMsQ;$I>`K0}=kSd4aBYPbZ6DK$i0T#ZMeVO%X5Gh;#s*L7G zXqt|dn^?$_k%K+CB#(*y9bCoIhWFAzh-b&4cA0hR=AjEjzMK7H$8nb(=)^5ximsjG zal^qL<4YH!4|kmO?vWv6Z+qa&#R!lI>#%#*T#pTMR>9d<4SXpnBfC{G>8zC~fbQrX z54?V_?S=O_~ewU3pydbc*j2F~eV9354Rbp*QHnYW(x{FWEWq zDrc2xQ8V(^iJ!Q?iOc0uwU0L!CN{3|2WM`2m>Z%0roj$rtzV@)>=E_(c=j@T?Cxa) zhpkx2AX+%=?n{X_3pUf){- zj^fI8V`7wqA?3IT(lan`AnvPqxu)0B{F!G<_-o^n#dHA4**VQNexw!2897wF0QA8_ z3hwekokle#KG=aM8*n|Yn6dXn{2_McJ_o4_8decl;eQdO5qf0fvG@|lVrJdhW2n+=iLuswAN~fZ8uII+>m=`7 z3nS9dsx`r93vO%ODL_b5S-DDPx?`M!-xHY=k$_khRT^C4)r;zOWN`(2SW#Ta9(iu! zuM&aM|Eo*NN8mQsW&1i~hh*Ax$i*x?zvzsGR+;Mz-Mg^#!L-D?#1{Ze5J0uag~6q0 zPC?Y+9`6_#=pcYp)6la7WY-ZQ;q>s;;ht)As57RnVhX;ft288U*fJs?KYp364`P&4 zRW{#brlr%aXS`5;nf#p?CRG4$3lF9#JLZlUT9%9a(YSqd#Av%yLr{wA7(QI+mb1W& zdM2+2k5etc8C`dyduunS$m0hFJMME(SNk=dqV}45icb(?E?glIItJydrKHI5)Oh8; z?KQ+>?Z4*t+U4gITAM2LCr^`YJeY1(3(sre=fVal6&e=SO&jd%yAJq2tL5nPoF}Sa}X0-@R4fAy~LL?$BCheUSB5@XAuA zvbO8i#{tDO=I3tgaVpKnZD_eR5MrV9iy-{y#p<4bD!DqXN$7cQqbtOY-mvE?r;Iwd zhzz@=o5^W}fwaKxS)-MJk`{-u97CJhI@<%{BJ0I&IHSUjr5Fl4$MF&YLO7Ym8kuT6 z*}z~dO4p?#)o0rsGsX9V>&1hOcPHT-nDt^ql!rav&?cgCNvk32e zV^=(Td3A+|CE7*i_&*8JwPza2Om*%&bQF@V1w2?vS)C&#pnY{nzZpPa;0zo<#2%zf zGh<4FDRE6lYJgSft?Et_9b+WRF3Y>Am{Duj&>PGBH_Ss{OZmx!a3~e`$DxVNZf;1u zv?$VujXH7=wAbJn9+yh7#iZUr3Mdu;75HzY!wo= zUn4y!pHQuj<;y$qf6ed6L ztSpsLIaaP~p^uVEO}a)9|H`sK=ps(SrON<9guH>EJ4$5RbKj7Y@y;a$D2=wT4Hn*U}dkqqE015m(Rgx1M$(lICH#T`E~ptP4V6$C9U{g^nd$ z&|t2B_3jx)$1`U$JiU}wJ(dvWl=Pt6FIgvnKai~tb!E5uOw-qRsd=QP+(e7TPIs6_ zM~6YEj%pk9szCy^hJ8~o^yKAyUGeH@W7XE3{!&SmuWRAG2~oszah7CZi@R z8n>`2&=X{2TwsS}o-J!Zcg4+YhqBxQNm1lNJdXRF*L@EE&jMqV?`?M!+k9L=b8uke z)~aGAv?0$&4!hEhFcQ3bk*5gfQM%Dy-dKA6M`q1^tb{;Xj+GPs&r$K@-%!%JPgKIc z3Rw-JDAtJx-)q=fz|Z8A8@eDzyD*q)xP7{4VdYtDq3n8{(%5^jE|)D23W|4A<~fMJ z>JkJ%%m(0IYzSbIkaT|QVUS*$ayFzSIye7HAG^67^4Zuhe0A0JkXi@gv5?=F7?c^GFy+75`-{($iW>v%GgApIvR`s zM3aE+8)x3{C*b;jUau@#VN&U_Karxa<)C`TKTdfbK}`0C2l<{K!dlAOoJ7cj>fZ~j%d7~2D-MEtNMJhn;F6=AW=DR=G3Mwxj^1=@*l z(6MU>5!un7fdDsm6zgziP)U|Zm4qAPaYgTT}bE?xaB0rU~x1s}L1v z3in+5bTFTeO_My&*o|tTPnx?{Pk}P!X;YePnX|-nEaALN#?R5Y)8Bmf_EFM3U@B?T z7c2G(@J&Iiv_??mcKKWh1g3zZ)Xx|Jr2MmLd}Wz<1PmYZg?`z)4>R(g?tJJ-$_93CbYI zfma<>L@f+R2$#Ar<0OI1ID(-Cv~!y2H5>MnMWn6SXOg^xgoLa)08$xvp>6J;q$)jn zZ>~VDlN8iS>mz6!OjedJ<}6k(rKq{+J~@~Y+&O=@PU7I45&Y}d`Edq#I)Nvv8#Ll% zjxV1OFG&wV>K1;x2hOu#>B%7*T+V=SF7e%#YSC6zX35h)e2O$$;Rew@awGnc3Kjwt zVk=nm_)^J#en_jyE|OduAhNbc2HVdW@5z#$Nm4eTkY_Q^p5lxplNjo+kKo(U@NZ=T z_%%Fo#^h9B9B|~Gt=@8rLPJgUQSS}%?ud=U&`Z0E$R@W4?x=YWbB$h~jf6x2QFZ5B z3X4F5v_O-m{GvGOmBH!Zuq^dxSRd0q!LR7q_wgw<1xdk8BQEa!RDn#z;z_9yoe&V% ztb>yt7UXvZdY_Bnf(y`#@ur|qQvzchiRAj@^$I|RV5GqelC@BJ1Ng0!dTyL)28s3} z+AH^(EtTc@-%6s%>}1Az#|9tnH_#xDnWV7_r4=ZWid@DkmGgIuz>ewNe0C%X3~$-j zEm_(F*gvc6TbYmDqFS=a`1WXE*9c|VlXfrbhqQ?+#(sFr=!S!ILZEk)@vO8072fdH zn0`F+RR?hPuzMv&%weCvIn0ubhwCkTCTE*MD2SS|*Bh%KI=YmH<~1sP<5_S7T@v!k zdNUPuKv+CCCowPW#?h5unU^kz%xA;dbr>G>ul>vWvE>+pW%mrNJ!N-GxpzcBm{fwJ zSsybD>%C-X%-6*vD(*x!x#GNZhQ7zkE8a|Jjnmi2Q%$H=Re_<5lK^_$U?Ovg-pS;h#n zkqs_EqXb8f2Jvyme3jyegK)#50fhceeK57%&~zz9)}k)6)%PJevWJk z#Sf~uG}q${>9q)l7xxIB%Bv!rm)w&zXMjxY2-c$7di! zn*{~8rUR56)-r=xH6Bo%6s!vsl+TaEZo?dX09>RsT7aE|X3W>%UgD@)yvMiiK7(jG zz^AtA#gGOAc#_?CA&KzStoI)7dUJ~z%L7V@~-(pQ;^AH}hTY!wXj4#4P3 z#Kjk-;XuJ2qVr63CF4S zsXL|^>9xs4pu*h@{R-t3o{&7JqCIs2BO}6t_u%}+(VBQru1ImB}{|cWq3KbFjwy?(I#fGVXVNG z{+71TN(?6UBl{2{>t&j(Rabb}L4Fxls@E=C2xTey&CpkhS1Q7y z#Jr3hy-+la0cJA7gRxyBaEH7`3|&i#wK0RVKpW=fzOwA;%md0T2A_ZZG{;wP&-RPg z2Pd;L>PwE-}W zg;Kt`%*+~Wr`n;I_?-TE*;1{!un(dMqWcSdrh~#~9NTV6Yoa-ezAfhVCx}(-xPL4c zK{Qg{Z-^ju=|Ov;MPc9qm^GGdNqO!&V-#F915x_+%lvz49gMd$K_L2p+}5x-G2HmD zvq3oQY9;_{a^_3lCnzwwiQnNDQ~{}>3e!J;-g{pMcgo4+o}05`%W6bUUgCaEjy4Cp zr?r&Tsxt?Ea;`P~j#+Pg08rZmgfLbg!IuGo>^WabI@716SZ${q*)*A6e6y)9PNV0s z)V~REdCkH#sMm%hjk||VML}kWt;0*DJmcmZCQHpB3U^@es)=P~?s1J^FTi1$|G68y z;Sh~{&{@%@^V>2g?<(O0v%_rG{I*0Nk7N$eI+fX%#~)4M%?+NsA|yrHrB&~Dee%WL zhT)nxkwoLal>~(j88JE=e4=h?7ha*0QgFVZfzw*j|hpYnwofm$`nr)Tiu8Y19bp$T9NIziJgh+H!7eQYa}AN!37C zLpDfVECHt(PonYDA+PYXxrra6eQD@P#;z=!-ND90yH0Dp!ON<7cm;L2hp(?n$3K^3 z$Nc;xePZInX$IchJLU{E>W){NL&^Sa>CM%pRHfF0g8|*8#%f!4bEhH+>1n$t_;a}e&)jaeR^`7xS8(M zY%Oc26Pl>WQR)Llrg<7D=)#A!>-J=FrOoICekhOUGA-&whZ|9bB0=AUIfwS&353>y z-0|^4NmthMk6DP4FntUiVgxV1Dw=VW{pHXNcs*=~`s0_X<8u$F26%3c=`xyY6(2mq zgv6ulMtRF%1{U=V6qx4^iDp~K(-=w8qll=5gs)J7xi-v)bS6y$+6j0TtkXOcjfEiN z6mdfb$n_c&`SAC0ZdZGoBi(3-gPgcXwzF!247g6>ohXX{1_(yV`4jD#e)^hv>p~g- z{aE7T;KlW2@M!=s(y|k!86o@4@CJsa35nglp;NeMb&+{!3E76frk_iZf*%(JU|+K6 zs$VKP1iFFgjIHdRb5JKSZO^f0XW2&=IEjDOJ_OVBLqYVSO(BOgp_{5z5Nl*as$?9U zS-fOb6bj&}HJNO*B>~0}*_?>g52pFG6tZhC^)^=9wLxOPTi>WX(rF`5)+5o%i5Mx8 ziaVlv7iK$0WiDH~2jq0e2EH#%-Zh)iunRI+kO$l3dBW^74*ExIuT1&lXIx(IJ|S(u zu$svcodKcd+5Fyf?>4^-3^PBYm8G;^b=%r*Ds*%UKgKfuTz!lVvG=zJ!)Za0n8s&# z{-m4~veSX)dn~-6@Dwzhvf@ia$>(CY;eEn+xF~Gv z!Dn%D{xP~|YS>3FvmZ;aF@6*wCv0HOqlWU?gG<<$A-~*68PxNSd=&7J>(IBNH^3xN zZOAy{UsSB3uN^_Kdqy;AZGD>O{b@5uEo zTkBMSV|fqcnKKI(dk{CQiPuXLEeD*DwZyKK`UUrui$xc<8Fwc zYbcc*y5PJjtmX}%b-zlXFP225VH_2Jp>RfaEqnwGxw?E=&-9h6#6oMxYRZMvHr#M?zm79 z0XR8f!eBK*!Fsq{Cr^igtAn%VW@hkvmSTX!YA85fsp!XOZQ$o1oOU*;_s@Gdj-g%z6+m8r^ zAvWMh@;?bb{u5e*b@sL*Cz-X{c`l12JQ@3pLOs}6RQ+OBD-;r=r8rg3Z!SU$(fv(c zfQjWpJ?5se`HStwW7<<5yiF(}uG;NnHvtRbP9P~w`7?=Tzc`nNApCOhvcuaaM5wlczSW`Of zz|Q33X`{k{bt>D+x}q@I8hab!;?}*?-~v)0K_gSq%{%{{dZwWZ>TrFSZ@R8q6J_rm z(}`X9(Q)eb@QJ*CLi}TB+`sAA}9qOj#L+i%Om z3iI4~8!W?S?H;BHUiU(p6BV%D%{i~NXh4x}b-6dY@h}*Jv(`|Puvx&X?n~L1Pte(Y z&2$5ZX%}}qFUv$BAG;j2Wk8AkjY%QZ>-!%YsGWZ*IN>=pHwBvg8>S~axva}Bi2mbR zali*}nU^$X6xnGg*axkX;##eA_AiT|r$Se-v62;ZlnnhZ7AKg^n-Xpjns_O>R$8~W zovCAnxHWlMg{NZpP?8EYId?bW6u;jYZoZ>_jWzn5nlkEYBdkhN)x)oul&DYkdTOp(~G-+g=YD09m-K@N7#jSYL_O%;3 zrj9jGbPrBP^UT>N#EeoL_?BkAR)TmuzyApL>%7wVLuR>YNlB^l~PbYUP zfmx1yss8c9vO4hc;>@B3+oE%A9z_%&Gz>Y?^&k{aK#=<7g)0p`_M3xs@dY+WZ|a_t zPFC5??et{#j8I(C(+4xR=06b4!1^F`;&5Y;nfVRS1)lBE-c>T}!0V`WC=M3;HjgWs z%Yp~fY*0hB93&JOsV}t<&;_aGnec1P*uERg8j?vN8R&O>V8wDw;tb>k$V%wr8+`ng*h;*ftjeoL`suP^$IGcLQvl~)&u8Sxk zUYV2``kZ@dTx%y*#}0^=Td6yddkVDWXUoe?(nG5O<#}s^!9ctuFrvmtUSx%_WCYWY zmqeKu0L?fP5Dn;sVOxXPYmAb?Z@Sd1bWy$mPu23Zu7d&8f|~1>cuOcH8ox2JdZ<@I zt(9%I@O4(Ey-#wu&}WoZ#_+*)J3c1uR7p&W7wZG45lb+O2hHpNz08IDk!Zrijm@?9__43tVbhVy<>h_0lbyu=X4i|fp(sq;{4NEz2E#qYQh zy$(@-WnN{c-T)M-En!iSCPCwx-lFGe+Nwqfws?0tFGq~xa`H|Ku#ie6P74>@@#xT5tNh!xI_cjx1EK=Up(! zliAw+V5=N%5<-8SyfV8&YNE?gl2M-+z+%#6KVqi#_BXQ;lB_@p?S@EH069R$zXty- z>x>B{HNIk`uU>O8c=Km%dp_wmK7I67*?50Uegr*tc8W}O932;nq!pc4PsID(EXE16 znquuiV(3E`@y?(*=u>ya>wQ3>D1!@agw*2%n3O61r$jLheOj%~E+!ei0D)bD^)2U8 zy8KQ&>E28xK4Je@M*AvIKzEeN4o<xb@vw6 zxLsGMH(lT)Hjh&z#TIFG`c1rx^}S4sAGSx;o0#}1``qS-y5EUXYN)r2vseoge3#Z7 zI4;^d0y7+$#pBEoxr@hvh52`Dt0SoJJ|WZ7h54$Q2_!yKtI2G+2CIN!rD)?A$+c}^ zE^in&D_@M6^2v9?r*EnpW>KQB?k}b$C_It$JcSJoAodN9TpH)1yaa-b<$LsY9jghKTQ@T%XN*P2HiaC(OB{cmx};ERrTa9-4R1on@M>dg5X86uh`Zh-+#XhMR?@;EY{5-Vx-*4W!bmq zpoVBTQ6*tCh1Inmy3gzjk37U!Wshwq`BztYDdQBa5SgAR+^^dTwLA=$!}x2uxa6lS zGWwg#ToZ6fyxDElT-UQfIgi)AbY^sst2sTwTcvZEd+7WT==<8p257(V7WwSsO2=!L9LPVdu+r!NB_{rC>uyiO}+B5kCTEs!P59kaJ)Ek4Cw% zr0}{;{V;Gel~-Feeck+1y8I{sRKI%w%bF^iM3?E;9n-th%U;HC197Hh)U9~MepW=#f=wz5pRM)EF#4vZ=1g#;_{Ij}H|G~&SFA%P`#zRhl4$5NJsd9KKFj~D&M;A^}IS~y@ z-Q3qng}(Q_O)=`zaNW4*hIm)-UyXdDguzx%^Gw zw851T_oWxNe;BAdqHdf}MOWS$f*8;ff9Q9as#1%pqC>ljayo#kcly2v(h*qzSc*|% zOzXzzOBA>0TvlwEN>b@o@aLP& zd2gYW|NcXM%?EkOy`WfTx~s}6zWG(bsEOJ0&fRhja`}J-Tm`7(dEw@R!PssYaT93w zNfkd|@FAXVA48fEFHYM>KAACGq}?l3b^g$8x)W7Mu}+6W z!!r;L-FG7nYRLDVTkyYaK?)vb;m#&JY}c0e->N!_4kRy3qIL3-090o^?34@GkzG0j z)^1z!rtGDha#Dylqkf(9JlTzHo0{*7Qj2T?rAP`>OTrOpe%?#5dDtjIt>iX|V)?u> zMjKPbJHl`Ot;AUp>`p=$K)Cgn=fW?NM;YLqZyR44U{RBJMfV?_()RgdzfHfKrtCJW z-?;}jrkD*8tEo-GTR6cCj}Az^4MKe`n0({r=^*I8sTj*K;f+jX)GJ#%}S{Kw2c8THuNkYyNF4951vkLYI-mOyi5* zNeSx#jWAbO0KDYTYKcC#a*p*;o!A+>`BEDq5S=+RPDiL7&!xh|ewwA4-`h*jDQd|< z<*t2bfwBfwon-6i>t=w^!0f?B#}?^Ga;5Vd|kGB$n3E<1~0C%;2Qpn;6EaCL3XgQvo3sAoa8MfR5`Ai^BftuzR z&C}Ng*Og1}!vNknqdNy0lORDG9Xy`gI*YMJKEp56k4UallV{f5C*n>iWE8 z`qSw=5lckaM*(^kbwTj>E70!3R1!+6{!*vU(q^b_&GlLtk@AyB@sSK1!nFt&iA+{Y z-69;4xYF#J6@*>N`LMGx6`riH&PcGGDX8byg%)LIFfVzs9!=Sbq4tynl=5<$)DNfQXbuMTEMk3P52?3 z8TvdM^r6oBVjL(d+o(@t1-w~8=+OoYT{`4F+<->A6ew^Sf}QY?;Q1`*xCDThf|L_2 zcAqeHsGf?|YXS`SSV36fnir&s=$E)Sa7q|~1<{|9gpS!=nL|%~-?BE%o2osma7)m{ zqc;BnA5UZ3gAuK_!Ia~cVvg!L`ve%PX%51Oh}&q1G;Szk#~o>^8|Y(kHU}YIw>9vc zCfl5UsC?6QK*1w|9ZUj&%7tQiwAO!TjZUT1RlaO*UU_vx))-HDhgB?tMd}j(tL})8 zaq5hWv8=nl3ek$a(BG)~7leVQN)h14F&6?HBMChuC7lFbz`8YPTH%#XNuIkefp2in5?*gl z=!Tx}G(clP|F_fk5@u7()NZrfe3?B+eDW>8fLqFYy1T((5KPWn`JsE!uFFMtS!=*i z{l-U)EjEOj3D0gym%z=nraOkEiSdYbVoQ7Us)!2sL*P8tDHT~?^YPR)-yUZoaj|CC z-IEV-%sKQFjLhaL=C$o zcAHf?X4WH>-@kv-+jgKgBdW;?@Q2puq27Qg>5$jU>5XbT{6!kZ=gYL2xn^2zgTY~n z62CX6g}2t5Gx)2P=*m)xOkRq1$Ctv=Q=dR3dpc<%_)~9t(x@GX`ig0^xjV}p9qPo@!WmhSV4ubSsvv!|tnUkwg8S-v5^b=TVg@2?mGMf^asQib%Gn}fhRq{NrJZkX_M8wI)qXu4^}{%&>eJz7$JP4X zC-t*7ESHIWU@ZY!QAHrDWa6tN84@$RR?pD0w+==`>TL6eh?o21GxMazg43CM$@RR) zf~yjahi0d@1Q~|21kfD72^5!2$5L}gPwd(_t6&qd9Mq&Il1xO~F}c=E?eKop)tO*2MPV; z-9%V(ZBFb+wzNDfpaB4h15-8ZAdUS!jm%6`dO0QFjT{sJ6GgL9e+X(iQjRU2>(XQh z`#7BF$Q%0rV$vkR}{ghS2+#vOCB`_$l3>Q|(rk zn)5UKG6udsaZCIMNc0v{w2nK7>v`w*CIgu)MiOx%0{DK zT`}=uYh%haqJ@*nWX*`bMn1yEU8+tx@JXXM$Xl?P^&rL4EY90(RgqMnOhrvcI=k`{Y zyIBiAGSVwDpK6jH2_+*%W;ofl1Odvx-ZA1ylr&pkAlP>ars`Jlmc4D1tIbv3yR%fO zlWj=7NtGKJvMd^iD<@8m^IH!rNiS1KGptelz*LZ@3%lM7~^$8>MKd3R+3ok0`473^j1$MJKh==u!clNuSWKYw89bOA7w;DJh78HbXq;khxj&G^s;)_C!7#;KN; z!nsxjLS);`B#R>sLgUg~4fYUsr+AW>t(Y?KM542fbRAF%0BqoP(aK@yD^sfWkS2z% zV1qoGJjGzJDOlexH@QNf4&a+6TaTQb#@J|N02yS0I2Zz!Z7#LwpqX_4d*fWI2k8bs z!C+jvt3e^pbj_Um3O=q@?G}`@0BmF9hm*-gxi>bo0 zpt)GP1NPg*_|Dqx%?YbLio1h@ukSrNjr(jLpxI%`i|HA!LRX_kWm-2>{`rWXUuj{^ z(k+BwmjT$tKHQ}a;IzWy%s}qOi;!{d_N=dmo^NZD;+p26! z`<8U6+Axmpip%sLMcDgJeWK$2*iI*}*Ui4;)rKLF>}%s{#rxJh0@fV8!nQlv7ZQf)NW#SjT;ejzsHCSZY@O^(!^ zNz3!2k57uYCKsCwOO@^En8ok@#fG(Y!T3v6vr!1#&Ta9|Sca(xe$&g1WCa`FPn>EE z+tVQ6q4ldiOZUcF2~%;{!YSn+Z+V?aqKTb{PF-UwGkiSt!On={6)DrYV;d+*osI7I zYt}Co!-?d>*GW|7h|o(>3x=XN6puPKL;YuP$x&{cq>(ly{>A(m=c6 zH|lziUy{k28E_{*NnzW7qu{6rO4saxvm{bKM`Wfu?~*-BU{jz)W=vQ1O!~RbJXM4nBCD^>Qz(QO zv6pe!+=g5k+g{Yb5S_`l3hf|q_C1&vNQH1o(=c#5U{@>h9bn^{_$jKIeV*4xDM`hy zKA{M_k_aoW@3Axwr1GVCYqhoE2`t$waDIpt#mJ8CYLi+Gz#_zD{Hm!0V6}+^DfxK6 z_%E&BccSq;FCQ46b5v1zV#SzMZSJ^5|7$lW?xdb6km}$)QoA5`DR5gZxw>(YX2jDp z<7>ux$L+@-Qer5cWR=(HXkbbjv#iIDpQU|`7u6DXMz%|8F-@sXV?J}m}@evLo(+n|q zTvqY>W`5D<_Y&RmGXr@GUEDC10}W1Y45fbOgBwFk5zp)rD0riMja}8K*$7P zx((we6P&zZer@vQbBBa}8sKLmG^P3s5iRfy6Ja2bT+9$tuQC5hFsmtt1pHhZ+w1>yc^GXTJl#!r%rHWnNa@lFg zGvlGMpH8Ur&|>R^s7Q-#Xa!dv493V9@*f@LmdLS4z#!n>;dn}|0Rz$6*WR=mjfEI@ zz$G}!&3?(?c(zEKVkhXIgeYw#J_fr{rR=iO|qX(C|K& zAbznpd-G89P=dfU>&SzTi3(&QAX?MvAf{m&An2BdgKqYFP<@O?hRXJ_>1}2Ja65_;A_=>=PZ-bBe^xyLsl_z?7|2lgq;-C@o+!)34_~mgB~fO{nu;^a|p{9Jyuv} zfsw8nq1%IP5xnS>){6}D2(Y36`lQ32>+O$xBrZI zC;a-Jfw@UEo_2Gx6c z_KwZ~pyky)-uKR3x1i)dBq9a$8w9Rejz^CVY- z_!7AIw=64dXp-h}f$?L*Ux0IjVXd6owZ|W2^XYr!J|AR-MyXLF>U``+!*s$2(`CZh zJs)(`m7@~!(ZN@J5pWH6&CTXd-7;gHH)FIe=z~O5!D@pWvJ_WrTA#fSYX_tN*C+A6 z@%%c`z~uya%m+wW9DAC|TbN^Zf=55RC_CgsZKZ`&go7TNosZ+t27+C~d`cOluaocx zGn=O>Q!TTuwo7mhlf`m}{NlQ{T}guj%#nekDQS=|B8YX4+Ul$aaD)3pC{l}FQoVG? zjl)6R$x8DRT^~JAF^Z(q44m#Hr+n!@90N&EUUw0_g-nTL*K3DpU(${G!juIz<3x}$ ze13#SVR$y|y|K(#51Jh`MIU2OX!BdsjoOThjXrZr&KSd({4FIiqpOT3nKHo~zzuX@ zXjAg(Mlnm&!;g^A^5*!0mE}udAmI-$E-m{24RO2n**2p-6}}GS9A{B}d%8IO+T;J4 z%JW@9z7{@E;)qGBV^E5T&+4bv9v5AkIFRRyH`lGqnH8k7iJ6ClpeyWtq~FxjVn+UV zKF1vw;?xTQwf1$1n9zFW)mmlHM7`~Z9CYZE9#d>idD2)gD?i&OT$^$9QrfNt-QNp* z91Fo6^FY5L02fL<$Sv31(6SdYe@mW@&HXm|T-!uZSf1!Qo5+m9= z4u}^1kahwPz}RCc3ApFU;%ov{q+_i*C7l0z34rerj$_h*QDp_ISb$zULN*epHi~&= z-bUVMdBi>m-vMda7gKa}l)Eq}U}qhoa>7V~?cBz*vU7k4J+d%IGNVy34F zqz4|*Ul?hQ${%2`wa4yNb?>d3C(vav)0_d+fp028$6W7_6O^8n-uT9LQhEohd_$ac2vse`nP}Sb=cbLs zSQEl85G>A#Zj-rWmNz{(UJFzb*I%rTo1v3cF$(5FV$LfX}$#yzIwyv7OHuB;)_FsJ|qF&}5-{bBmHNDJ0)&Tkqc%_|`;T78IbG zUXlJ%ygSeep|Z^9G10-|GH+z5QMF85x1_$M?3n)X5^s;j;7?oevr< z(BK2G)cEUazC5dYt}qsmaQ%T zcE~sxy&f6`!4|ve4IZXA69Is3>f578jeLG)DWNFGSX|Y^DOv#T8z>ZmXVg5PuT>r3 zSeCP{<`Z3Ig-USSq!1z|R^s{Nx`Fv4K*iFjPkZx|f^@574|8W_ zK1-}7LXhOjjKkifh;gYNPzV^z z_*OuRw!DB|oV^kI`6vPHs5%-iWp0^s%~qI+pclE2UHNh$m^%0BbI{(uzT_ib;$##} zUT=J2XV#U)F`+uR(m-hZce=wWM8a6sI8v-bU3=|+78$$StHiiKd9lPg%!HnUbt)?F z@rQxKhuxvig7L8OX++GIghkq=6%DdkOe{ti1jxUVTpr03put73)(gz5shwcYjk+MY z$5qSoyj0d4jRzpybE?CL4a2Z-$q+E70a-sE!PSla#*)wm?V3TEmYl%UrY3OI`m$eX zdNzMh1cP%e^efBp3$^u~KM^fFXnVsWQ$s))z-uGJ8#6GwgzZ%vdBuyG`m4hyg=Z^7 z4kW+sp0%}@*&Mc2I92!Bq)=_uo%`M9$(-^?ZwM$v(=AL|k&enB2JUauKP` za*f!(K?(LuQ9g*K{wCVe0@@lnddEbCuuAs(bS7eGektC(FW?sHj4ri#J8DNGQ0rx3!hUmqd!SSlK3fQ3K=PzRcaYrMt zI0mIQVmdY5KX~E+fvMPgM#xo2vT$i`OZZWF(*ikg=!P|&FKkP$ON@5patssN z6GyTYN4!_C!Da@}hykAW{B43S<51aOP>494=pz^46|s`m1q`HJ`lDk;yo&$`!z${1 zgkH`TX+b>ZTH{8qu~u*cdzYs3(o__4|rLxvkf4Q z(q}#jT@(h=u}XlUlBCpJ;`yv};h47o#uD~LHQ_cM=COg=z}piV>PlG?U38W4+84eM zJMeqf>_^LK^}~tZDzA+*VTez(vJe>wnsvD%Hqe_{X$wyMrbDk}53=9yg2vv{qXI_F7^$5f%!XMST^ICLB@C6Vxm42O1(W#b1RBOuR)0!~c(9|8$`|Wlqr{;M6|UgQMpWDnHHIFqLHh7Z zUm}Zk2`oVQ3miAE7#55LAbk|$mv$*+SzOLeGGHk!2ajNV;vJ=wHjX#&OUcukb)t;K zOlW^iMFSh3RW7rCGIFkdsVPS6gu&TvplLkPW~NtuV{3V8oBvdO@q6SC61l>j9Q(XE zHeZ(A_s_ePXEZu{OMM>%09C0a&Qko*VPk5zpuy;R-@F8C8VNnR?MzNn(Mr%2*FF0*`;Str*C#x8l;m-1$HEmL&pz&wLne8Wf!Z6@ z+WC?XSS8>2{&$YVgl<_#%HC##F{Y-6yl5G!x8$g(%ur>R6jWrn&$|4%_C_F6JzV#A zoe0`)#3+@412g}DS#*^3sjmM}b|B^%WzNQjPz*cCdil1;{l82bK_CDCN}@rUeNFmQ zq$UAOC;yZsM`AUv?ME*pAm`e7K}&fh2S;d-w$hw5>E(BIqAE0+ya16nL<8exME6&b zZjb>RoJ$*;txy6a&z63mC#nLctI~y>xf9>pkvs_wS(T5mC$R}S={hfdn%GIZD$c)- zW*2EKOh!^(kK*ga-|6D7^w;Vf)wi8H41<}#Vu203c($cAap&kpb1;OjRAnAYt+hmM(;X1V{=)LN7+yvD@m7PNJBqS`VB0L6V9lH*@% zcNtWFuTESL9cF0|*7eYx(PbA=GJoKMkgIu`_9gK9ORMG{vd9B{`TRiOx-Ndhm$Wc8 zr=P8(`UE(iVu&lMWfyZpMsgXqgeT<)l%d;%>5x3lfZ?VDm3ev zEZJ_@2~KQK$K@l8AgO>iuyIF#6>cREo@;P>vFf5(J_y(JRS^^9d|#P596_W+_M(~0 zb{NND1R51GM1xJ6eyw%9aBjKJ!aZrf2TXP~RX||<$(wenXuFI7mk40=eK%eIuSmuM zkJ}~6B<~DesxR&UqHXK;YMn$W%=4(>WT>*m(nJe%zhY6??WZrt1(Lr3wM>B`_jE4R zgdA)0D(zkil5?3=h_e|u@&m&tNEbN)cCg>Q(d|eo9u@1YUYZ?@iq*-1QY!CUi4Y}! z-khElJMBQ`9N>0@77~I}L9i{Rr_EsI9&}zyNlu9o1t%`Ly+&s=!Syqkw2Lh*x_1AF z%RZC>NTQPTK+Rj$=%~TC!wssV_OWXr%-ZL7@|e7^O$fQiB!>ACOyb|(5fpYA%-ohb zdmFNImaN{~NW+4e2FRBZ)B;)Z>Aa0JgB1#0So)?Lr-%NZ>6=#{5C5szuJwjQ`+RL6 ze#RSEF}T^=gVO!RD5}VvM+^b7%Ph@2J|IYB`(`}V`6Y48VM^uz$`ikRmwfsBp=jq; zYBv$?0Dt*OYLC^Gx-iPNLw}XDXqfyb1@-e~oE$ZO`~zECH8M-zrbk>&4QMuX`EsX8kfyF|V z+C8XJi4Iffp|t~YAW+#y=?SI@CKCIi*|m(Id|>6G3z}$aI!&ZLMWte)(?1TXN4*$X zWRpGPx=WFpAYPB<4A1SPWGLIk_<;^e$wQKeSS5%w(6(Y_=|G^z zKB1b}#hF!oKCY)U(dX%$*GR$_a&h5>^k?iwy7*7=8*gng8lk5rB#-+y^mBf@qlz>0 zt(o({YJEKkB8d66JkhYq=@j8}fZKYQw_pB{d1BzbaU73g`BQaqEAg~sW2bY|^E)?J za%JZ-8rK~CResFJu^UpV^=kKS2!g9~)|#)7nJOH}ICNaBvn zrqG=p!-E8faQsBueHW3ETL_xnZ!fA0&+78)o`SnVFY0)(Z2r!s%gjut1UzwqZ)OqEHum;onygCypu6X) zLN`FI35w9^!mZ6myNJ%uZX)g2?U}A{5pwT}10B0t+R5w<|Aa7fP#-#*S~ir#FuWm? zue+0sO+YO~1AEkK1(V;_z1?UM2eqi(Z(GN3-}(x4!oi1}WbwGWAquCuyI9Q(pkAI? zpHZ=|$4Wl6kL-+cx?XpEsL4*Gc34ZBw}x=+hgL={uADQ&_)v|itNHQHkMjGyuw3Mt z_tD?$e?puP3NRY!NMn-V43LJ?O(20NdU!A^wk_7r>qUX!WQ9y2vz)`fD8k~2BZ(RE zo=$p#&r5H}a4yP9fFgLg`{IxlNCmK2G8rSW(FK@C_-S=e?Yy5L9@*#&wA1&A)V;6D zlZwp<1Wg-E;y4Dsbu^;mFdhy~y;jO7(%_HRKi(4e+R93Uc>-D z-V&`^I}A$#aAjk?-9KjU<3X{H_glQGSz=18t(0Cj{+tbrRU4`8%A4>Sr`qYhjT>}E96T0Zw=UZ_B zS4GM@iz+l9G%H)w4mz#2=5L>lk-;OtSmI?j2X)_p8i7Wuxfb}3! z0X>m3VxUUGBxuzkdEC#onCy2;g#}s3$&VZti7aQJLao`8heB@Sk8i{W$Q+Wqilx2? z)+I0S1~Z()HA)qQ-hTGSd6cC7pV;XHG`nb1IEt?>!C~353}GO zqtB%AAKh8_@D;<86ozU|9a?27Nfvjces!7E(XLkk?h=YsMFH?pM(-n+Ax32+&Y&lftGs-R9Ib6F7V=o>C z@b9Ff-WDrAiliUeDzRvPcOJTm0w`8VB#GnRFX0MPwZ4ZoK1yHGZR%#H`)8xSlXrRQ zXe~G;uuQmb^m(%7XQZDwmjOTuTiktpFU#L_Mws`$BC2YjntMZTHbteSM!G(xZU!iY zKd%=a7qC<#SzgoS&{pXOY)pc0*CN>rz$rtlDwg(OqKIG5tN&p2RuzfdyDD4u8n_BN zRC_AQoVvPL*KfV*s*1G}VO&^@Xs;!m@^J%Q&g_*O!t=KGZdOTsx_&W+C_f|mAke++ zbzPoOX#1BrgjT{phmH8-h#E?pY!ZR9>OEgNW=ilF(cF_Iz6+2`s({v=cqq(eGDsL+ob7}e%B@t z`bl(v0Li{mw@sFB168vw#t>#QaS)&MG_Bxnl8i0Eb)U`weAEK1Z$Fm8Or|KD&kneJ z^dyjAURDh7rZ1~eO94@XP`IrJ%$! zYe{Z6s(M52aMXb{b#(w=m56QSiDKhqNpXuU6@pXJLn?Qj*$-2p1>9L&=OON zJ+7WCP=|qe< zB1C8pCsI#zve>@h#TV0h`86Ae7}Lu&l(U7p%2=wszWF}_m#rJh$4IPP*8x}>`bvSs zBw+G>O!Yl?9r7`6g_LWZ?dGT`;f6CKi(}|XEL(oYe*&IC-H~xdeX~u;n!18warLle zsJ}tfKUXgOhLss$)G*1EuMF)6FAJO9muF+*hy0L) zzqXXO40f?>j15E9$C!}^GJ~?-3N6J|JWbGp_PHf;CqX``U0``T$l=Sv9U$mHI4~JR z%&!JpoJCZ1_Exs;88R>OU3=aaEodq)&!`{iZ2GD;lYz3P!6lNDkNC?eHx=bsFh}Jjy4N_$K3_Ig z6GIX4m9Ft4d6M^C(Ua3$Q}}0J^8|9YA@w8Mqd8 zgEN}JJFY;i+g%32y+I6CerVho%7f#@3^?DrcE zex+8;SHm-egP2v^sJ^@-2%vKcrj9(OC7xUn>E_}w)6VZtAKE8DYEylr(w$)Z_khGv zt)rFkPeLe7PGp%sju%>&u%Eh8rPm`iP5N-_rfOq76a>p4S!P?kd{u12WgVszfp*6? z_^tiVjTR?wE*-W|zsG31slzHIYAP#2Qz8p(11?E@(Y@tDraWnPJ0Um8F>I2J+Hk?) zcXt*`2|u4N;zYk+o29^M{Q2WXbUH`2gEz5mzOv?A!B>$HQEUKDY@ivi#lq29m!h>~ z0Axl&+HF#zAuz&H4AqsZ{}fet{`I>PNe7 z04+e%!LcsBBmiIGP%}vS=9bitBX^>_OHCf}KByjAgDWwxiwzksIL1M5tjav7#-QX` zyp3jq0(ZKw+AxXIcZRZVUi~(TU3o;DefW6y5&J7z-GyAr%IGC|0T#=;q|xe%-Qc71 zJRXzftbpjKr9S+E8fXSja7#oX!0YkshsOuw3~|T*#l8l?2(y+d!#gyUCHmxzoU?$d zmADn4M^+vf5hV$=Ac|%Ok(P^QqD|UwO~|LcVr?K7n4t=VH~sAd+Oi0gykkN%NgyVx zb9Y6++&|keq@m8YYv$@VE#R+gC%z1oE>NyMEHhxgX}=2}wq>*b_Li63>@^)qR$5NT ziUVPgiNJ&Sp79-hb-Ko`xoX7_uc7RTDnUt){0cLccijKs)AlDf&%x6iu3mIFi2r<~ zzRu~VELi}b&LP+nBNcZ=e>7R3uBbNrVP-jR!i1aP`F~jaSPAPA#4KFsS%7db77>ay!O&NTZs=V` z;5*_usV?E08y${7*^>bRobT+;9RKu0FXJJ%)%pvuHBR?W66B}M{iIb&{o#YlATh4Asea9;vST2RYBeLb zMTx787!rb@Afh}lvp5VcoO>~SwT7YEF=G1i&xPB;0tV)JE)v3eCA~KcpakZ6t}SIs z7zQb7gW%ZLe2RTY-(f53_I~ghC#YTtlc#TDNW{dL9x7Sj22f8VCV*0|<5|!?q9GyG zYd2^478k9^AvSe9dnJq=jMBnH(qtfse;;bPy+I{f5ue7pSED8+VCKvGWJxc6b%6(b zadWDpBE8A+8P$W3eP4|n)`3lU13$4Tk9;S7HXzfAjVW+@Uo`bK$^0n5B z`r>K`_ISE(ylHP#M_EiA?}$M*cKl2^=6FXsA;&LNlS3^ZBSjyPhM zM=`n;{uS(TdY`etzYK%4J|YC?(C%5gOMp6DmHd)+xh$%8WJAwslmT*(PQClJYo|H= zR2+wlRaq)CTkGYA*N#9=dh!v;%hWZE@t~fq-Z2@upP%6psPn=krYk2E&h3Ir&8`aQ zV;4>>V2C%B-92e6uvDEFC;d|G_MZ8@ygmb^uUe}&L9kD#yhX6WoAV?o@=UkUf3)*R zmLfr@DoA!%5=%M^bI?KC>#%FBiC}9zx@E~p9`~(S-508J#Ha(?INZ*@-r);qffLMw zEu)$)PV92zmF9?KI|7(fEiBgzkjO+))LOZj88@|{&3FIEkuEU zP=slGaqGhb7}`*O9q0VSD;hmiR8?8B48)VBDSU*;yo;A|(e+Zt2M?Cj5RXR-EHQAO zX}#ME@~a6(BEfddehcF;iWgYl$Gn;bqV4ZFOMQ?XBZE~bpsLnOheEOR-wkO_U2<0& zjatp!yBr`^tw07Q=LPqP9t0E8m23}>Sy&{In44Qp4 z_hdrtPo$;3IwHHGd5FSzblB3&zN|RYC5a0D93&!8tyQL5_dDVLy$__E)b;9PflF-F zq$JS6_SxH#6FgLWw zF(;Cq`a)+!HQ@5ie&h&ho{_bn`6JUg13>3lY(P$Lmr?9wI(9XM?_$`_l_~b~o7iaA zG~b!Q*&%SWdRo?pNd{?)IjTQbm+C=d=fzWcZoP=PIt=@_jDE^E>=FyTHI2SrzcgzK z*$G4uB4MMM1ohj@6t@Tu4T(LH0c#-qb~7e$5N)HTnFR2)23Rkmlbjl1n7DqGOm)VL%D`l;me^8-VgjAON{Q0|HTZ(|Dr&dS0Z1lTbBfaI%!aPvsu$5vHkI>s*Jz&O!WXYB4WT;k9b`c={ zG>w%Ko^T9_j((?GRc6m#XX65}mnDYc1-D;)obTy9s&Ra_)MX84Lyk|7)t4kqT#`Lq zTI-G2bxW6+AO0XQdqS!w#c9zPkZtq~A4NZmRzt*X=B3_@hS&mA@cLX!W@?Qhi;PQX z8P>P9E7|ck6eU1JJI=wmU%1j@TGpfd(wuK*VirbevGm~Lts?2TtOMpZ_BczQB}$NS zzi_(@uIkQSOB$T19h{Mn!I>>H2y{<4Gd-(doeEAH4fM(Fol+hY5jP`LkbH;0ATkWl z*<}ClqT|~EVldpyEI_15>(EMNhOU}wy}gN!PZAEWW&joHI%~+W-9jPfe=MXdpt}D= zvS~v$yiE$*HD2B&ljDRMPpBa=LBE|`;h*|CDftoS znR%)h9MCeplgse{H$iTje5bef0Yl-`*{fuXlYIQv>@3Pl$%@6oo&?~eWUnMCP4V|Y z(N1E%6(|?2I;aFAtT1#DJ8`s!m6SyLfnl?!?4rIy3VU|aq!hY|IiTU7+DyC=T#m9d z_Duqk<)ww$gKtN+J>hY8op&gJcsove>{Npsn+5{Rkg zf2ns$3;rTfBhZ~6i}AxIZLfB#y}Yf)srAK>)Sk2#j+#xb)6z*kVn&oE=$G1uk_^&2 z2gqinlU?XHysW1i=i|-Y|4!WEj&kz*^ySa7H}+U=l*Z(%L44Y~W@!AQDAdNDou75L z{db?nwvBS6Bbji_LIQ)olt;c>D%|mJhOf9 z?e~ZNf*fW7h;nm^+Dn%27-zlI8n)s~78jF)C@9%!-UZ}x#lh@?kS>2Uv`WK1UFjD( zoE92o_0PfVuwX|P%I|I&qZ>|_6i^d2b}%T~HKKA81jHod#<+JZy%q4?RYI6PW8N@E zTj#yYN50-2-;QeJcNnN0*Sl}_0A@3)lQz?8pmwPBYhOq}__K~aHp!B52h=~?_|q}< zONn$06dF02YLCfYN7uHkdr7&o{hL201xDa6m9*1li!%%v9e(U9&I)MzCCPq$^osyHn6 z&1o#~4!E3?)Y0|BjqliJb^9-&({nz8((#xg*l1yuXn28kUvx<>QLIxtv)DwU9k(Qd zKJ0_pR$DAZ@~}ArxKc6VT(D z!I5BhtRY;49{F~;=KyT<{OsY}|LL|hXZB}IZh z={zn?KjW6aIJ$jdp9NdR%zqH27uNfuw8CN*8J<1!R(uh8)vi9Gn48&gUr#?A=?tj} z2zx_VsmBJoX+V3oczx*KMyLVpc6aeGZl3ZKUt|9C4qG^;EQR&<@~I^foQ#h-ab%-_ z1e#J2>VKAvO#T;;1Lf!VN8Neqcr0?#09Em?H-%!QLKZk&Hb$dRXDawo=|nt3(u8Jx zRT{A@9jJebKO5A7_RB#G8#mUFwfLT=eUnLDkKBqrCa;K?v3hf;*4Jwjw@S4N-qFmrXY4biYe|w1Gmaq3?bf%$TCf*C!QWP+GX>4;?NZ0veCNid(_hZ5+4nABi72y`A~>p~7_ zv8_~jRzUK&_?tByB@l{;RdL!V8+Ts!lwGb!5+253bLNSN7z`>85kM7`bG_V_R1g*cl2cw*|GM4mCX96a3O|9J(mCFpKX)vx=b@I5Uk;IL6^^sX%tZ#1h zy8Nh3mX-S+2`xYnpre{1H0QsICCt_A)n^kT6pcY&0*Eq~%R6T{xa8Y1g)-sNyltJS z=JdRBwK6c_p=JmEo0u}7H=&l~lZ~IcS)L+HxvxKU47bqfpvY#(ip+wzz0{rXyHjk( z`%|j;!NSP`7`=`1_vxXPsi1tK#~owaq6eNH;_Cj8g|N3j#Szp6#t^=0+!5@5K+(=A z-!GTi5`sE>a+b|*J`81thCmt?e>V6NYw>>M8%qJO)Py6(9U_JoizTqB6<0MH3?Vwb z^}GW6slr75Tfo$U;e8s;kxN7nF1DtQOvOp;?QF_va?gRrT|U%w_W&XTP!$xED@@eA za?e71LVT0_+)Vq!QY=X%1~)%66ntid^$5#sC@2x7$f8C9^Mm5TSPsyN{h2aP7sS0) zN17hDT2G?YwQe9^7ErYqh)1}rYA}3qW_g$9QIs`>T=)I62q3utm*MNc%_9A!jRuf4 z%sGrcbTfObR6k~LJVDvwGauI<72fa|RQ9c5z{WlvXt{ZbIoq=DFh)Z`I{ZI68KG48 zS^2lH{a;>!wbJ;8Tvsy52WQjA%|kZqc=2Gp!!4&z-%%_Y?SUDDO~RnXH%iM;Slp9bz8DZajS(Z8AH+N?i@1(D;ca;B6KwA=i<`wj;&Nx zD;%oQ(H;iFyqI*6t?Z$H$m?7KOp*sH#>DxV9 zX{|h4ivU_T7FtUivPXAEu*5`NT#U?`M8Heg8&f|9XkEYD4LFKxNA|iN%XKy^#@PH3=i6F`9%C>w7>Lt7(u*HtV#24~jv$KWFQ8yDM?R%YT~)V*Nsu;Rz)QgU?q0 zf9IxgNil_fZ~wZde~)*VMT~RJ`p9)nVy1ZJsEb|& z#@RWJVyh}AWRN|1%{U)(@Kec7%g+&G$e)bBuE-|gX~o?Br{j^i*))yrN~G8wB`bFA zROBFIsDtDmvYVSkxXcFv*&1d_o}R|?z}2i!wBGqqQO;Zsmqi$|ssz!>R~$;d-PTaj z=>c8Ri6mBQdLN{(zS@Yc`NqYR?1uMosGW2t+MZ5G#dTPkF^df#S#Yc8fZXC4-|hI6 z+-4%2x;)qKYu^i#eoe$i6kJLQW86tc1SXIr&bS_d|!u-@V4gw2cCme9N`^&!WJ_Jh4SRG4kMB+ zV-NX)!>p0*s;k@B31Pt-9uPP3M0Zr;x;e899Eqn*xEAj%tj5#s8wuUe#TGK39x&xJ zDK(Tt<;m?K#W=t;{4^_fWv2mUsLIu9qaf!iku+qT*x?+$r+U_**fXlSi~=jkHD|gq zrKqBFd`R^{n*gjKO5pZEpTh5BnbB{G%qB5JF^r5(Ah>;$cmvF#+4Z4svS+nC6?e&x znApqAck?|lHSrh*dWt;0>5$Xx(W0i~K6J=U48 zkZQt(JaJHSvV)mCt%C51Kf_DikM&pY!Bkz1l+Cbn#G|ER@8|-WVsv?L?5JaL=0&%LS7!w*Tq6JXNPN5E+=z z5dRaoW<4zG$;_e|0;_bHk5K2nVAL;6Fp@%~z+qv#;OgJP3S4|uR)(RYTVqAh!bwDR zd`H7o?R`*~)4p&knWejdN{fnSDBaWg-FIx%=a0I-g5{$kv&4Z*_6J0h`FEPnPLjV)FlOdR^)HE;v;wWFom?)#2baFy_l+1~+$geK@E?8{yB zWUUxyt<@G`Ed|RL6i{*2=&=Z#5QQE?Oqhl?kB}s0b`<-72Sl%=FYaj8u;e~o%6ZFc8k0y+)rb4V-i%g&`3sxOnM9vJ z6OZk#flz;fBFV)`imj|TeXBgtimqJDt$Mf805hV8PBDqNntKtlDuMnLDio4@tcIGu z0`-(`P##D?9dCoK;hOI)*z7JDoY?cw4R(17^{=J9OiJDbTSJC%EZr5mDlP*y&^AvI z0Bz#_j{x?Ufps8>S>HpQB;Nf!AE*%W+Hxehbef-BJRnPe@vD!?sq5IQd`stROc?Ts z?3lij;AfjEg6I0If4eJ)R`%WSKvz!;RD1N1T^-MNWUjsp)Z)%t@;a5$T(DYmhLc;x z@9v3Yl#E_mgSdrG!~;P|3nac#!09{Y$2}kLvp*_1bqcztMVng-QE8xw8Fi!m_{f=0 zYOe9wT+W)^U=gXNbtzRPwHF|uC*@Twnn-7Z6Fp%vhRZUNKE~b)!e5*=Z1TjuJurNQ zN|h271p{IkusQqI4s`Da&y+n?&bznqwpsq@KmB25|*)4cc`+@d67 z1dm3oC~?cL`ZY#(o2+6K9^v%!{yFgHcEr1H^vTkks*k0I%x=1Nw%rw4315b;j~JN^ zo0_bjO3iwXb{0OJYZdCP(nw{sT?GG<$EM+e3CJE=MwS(P8Tq^z5h)Ta> zv!mt;w{E#zPEDBDnNMR!DZNXg0Jm5Oi?0B(P!Pp$_`?$QL5>kTv7Cb000^dSLQy{< zajf-A!`BSS%a4e>oC9#6e83oFtc>Ci>ybS_ao}AoXfp4D71Yo*K3@27yVO?E4DCI~xPlvpVh=5HT7(~GoqGjgw5);+kiA-E` zJ%C8V#7~HjPz{4^woYoXsnA3dyO0z_>T37B2`X1}q#(ke($K6cVUYtUPkYK_k>h@Vr!_@N zr~SC2k~UBl9jd)^N1R)MY3y7Goc%}Ony`%57pSgR9%>k#?(>r<7PF&PeeVuXu^QaN zSlT#F_(3$=fn?*t6j_cNX%U+IUpUvN)xW>;jL@&S)+g7`Dw<~pad@ViW|@YcwQyYt zY+!i;0WI*lp|sDZE^NJ~ebf4VMR4lxRaH1W)V4jYdoH%_f8uisL}mSLVtR1MX5SGZ zam;xzN!hkL)73{&D%VvGMAIXd{z@o@gdB&BMtMD}*Ba zbN9O~ZB-Z{D)7&qhD%Le)eM&<6fifh{nLx<>T}g5f)|Y9pZYg+y+Re(J+mNCGq_k8 zth*(33bZn>hc9wwO0zA#DL98mejul)TdKOcjK>`MfUX(&}De~;jPl|j7Doo-!*NN11{QOPn zkAZ=7@Yx38FU&wOS2dZBkw)+Cnk>`_9TtFcnU~}u(g^mr>t>z@aayETDEMd+_b~H` z*U_B@uP|LMLaTPq zlZSOMjoz<59^y4G7JeH22jF05zPNnf)t$!iw3$PH%~s4H=vK-=f3&RGhyLzFOd5(G zQyvy1Mv>@K-Av|Qqz$P3@@-FtAij;Oxbz12lA_<;mxFw`HQdwV)t8 z2>^+L^eBac)4aAMZN-hsEp~!}pBeKLTx1GJgFwYq&~3#WGiAFS((jWC@`YxbM?YZx z(OD3Ea*o-M{rde;8YZh~xb&LeSFfDyq>6wDL2QjIeZjAH5Q`t*mXCfm(p5Htg4-AtWviMzBLl4n%MQ&8Lh!J_rE(;-_g?do_LCS z$6yM*6n+$$Y|Q0?d8}l(UItyBwsN(%!g-d2ULl0pe_O0uX)ojZZkc}fEnPc=c8@8Z ziZKt&1zEfW;}iDoe6=47nG7$?qO<8gn;c8Pt|zZyj18GxQ?1$~1Y0J1x;_Yg&0>yj zhvF)y^y%vj6^NQk=rRfC7EU*p+Qf0qD}6ea!QeoKp*#!MwXXPv9Scz(q&2u-wr&kTS|8XO_+f&`(Wk`|=zSc|G*o&_GetIplO8dh zGCYe{kH4$eeT&#Po@#Cuz=CjgjhbkbviVQncasIPelJBrA#0KL7C|I6edU7}3QsF{ ztZrT~P2s9{IfpEhIfwzfgwAF!zHSp+JJmgaJe{*HW2)y_(0$>dJ;QL)m&f6ms6}3z zRrnU{XA|iuo$v^!KRF&zhPO(6 z5MHN83n6HBqWGO!NQcUb2921LL8;$)QN4z*4AEJ(5*~WsM%&a!G4+_w9$wjclz4}| zGB9=xWF=*_759M|0}feX>3T@kt<2h7P(dr#7a~!j_o?3uFzyzUGYN=|1BCs+q6AN1#wd5xE~clpoBN!JhAmPq0s!)*X`%TVHC+ zKXlR_T%=EG7d{`z=AG^wHBnQv@8ob%_L%`w#GBQX-~b;lb$`LSE;5Wz`H5~s@Kw$b zFM)bUF@#lCm2Qw)b4y^=DW2FAY%=@ov%g=q!OQ-8X~=X)k^;>mm)h*mnNUO2nP|vZv4`- z6(REq(3aS=9hndpvZ87~B_@~Imf*SnLHN<;!c2S~?+&|*Pel-`(z+04RehAmIy*0o zc_||>%Eef$#izv6#+v}|L};+9SBrw=2gmCPhWfM1A4|AhHd^xImr-Pjr+y3CsTGWM zC6-ranXp}u0()(Te0(@D$nW-E(qTUu9u1xz^_U}aRP>bFCcC|R1b^*(vLr8rhooK` zTuKqRE%IJ{IaxE|%HJjlAc0Mxo$650u8h-`QVk@1D2^d#_wS(Luf`F)rXfG6YN3!O zS2EQ-VzRMKxLlIO38nJR5PdRw_ys&ZWPe4`O8HI?i^f|5j+KGZZMOVx?T_+hg8lOC#ab3e-S~MKV1jLKxU51ki+20DH|UAXAE?)5^xtivH!(Q2OW&pazqmP z=l-5by9dn-tOTJ0{2SxxncAgD>Z9#7`t-$e0Gf`SH#C+~zUjv9TB0pEl=f@))LEwD z1+kSlihXjH&I!$-la75pg#f4Az!aqhOV+?4B)wn~0F+(?ng>z>zxCH8GF}^>cuD6% z1KN3aXTqh8q|}Opxao@?6uECh;Ml6ZLt7TLcLcJk3AK^m5o@5o<;*uXm@*CzOfL&a zt-{orr^lsiITzjYnk&HU8-~{ZDgt0Y+Cw%bmB)q1HtZa^IUvKjvhY>NV$Sd-*vG5h zsaLP$%!mJ7yBk>@oF%%ZeFG@?=>Up6VEGlB*m%mTiS+@x|zr>UmEz4x(iBdnw*O-94t_7oZdZI8^GMX9`uJ#5)=ug}aW%tXMt1#qoF zU#g;-jz+UmqthtL%w2r@^=rm%fK#6VOEsNsR`*&V;^8@XKyyflqHu{2p^gL#vm?!& za}*yOdvIy*E2$?F1?AE20KUuR-esx%E{NnT2=4pzm>1pAPupJ7m6Gd~6B_JY9gL+6`^MlbfWVaXXFFJu_|#88~+wB zKzya@-pwJdhH85v9nih!^QIw@5gJo{4sB((+SKGkoEROos@7BL`ec^s2cs1VD^MFr zcA4KLRexm31ux0I*+)8FyTf2z(qaZ#S*H8DDocSpiHqEWnbwojo{*dy^{X^BzqVcf zV1Qw-XwFeVEk#oCYy*|>SM)yqChj^)fct7>lDV8Tt2A?rU{HMy0Jp|0yiY{ zlbHeHa=c5OPqjbNl*fZ*m|sbUrv#URbmn>SdP_$R6_HchGEDjeE_#-Y(TT8OcG#XT z_aJFV*RD*j)yc{1sjv0x@ZXV9V1KDsP3{^56A8w7Ti0o&2SGi(=QCuX-oOhn8 z_ER@{tv<1cdovqW*AIJQidMoO2T1_=c#46!6RAdB7y~j9lsZ;k(j97(o>TwBU?ARh zD3tPYU z&XFNf%TGD<07I2hT4G?=_49e?_9v@rw&?+50ZA@1jyjr~K%~~cH9F=rH3dN7G}mD$ zl@xQsv_BO()~2V0y_5jc+|l$;b9_bpuQ=9;@CI2--J{PiIF*+GQ+I#na*unCabD$u z5+*ozxi^AL%teDUL*h6Bpl>3`CpuCl$1P}l7tc)Enbff~pJ)Z{)T{Wz`pc|+Q>!8x zJ>|{YNTsi3&Y`*cABhU6pP)?|CN&pTbP(!^cmDKAVaes-rZ+p9>}89U7KR*)NrA3; zhsU^bwS67%^ST34`NV9nJDv&$Ww;Cm9_-Nfv*^!cvXYCJ9r4JcxP$x`+M(x-gWz&D zbV_h^bu9%KnrE!RPq^af(krz_GcGca8H~ERD%k%17fGmLXSSB2ZdM4MEq;T@hsQxJ zL-4S)=}mbH3x_2|)Z7!O3ovB<=65hiL2QQ7+FWU(?lML#y`d$nn{RWbcMf!@{hoi- zNzi}P!SK>di&z4vw5J?VX7-o1ad< z$vMfjvM0P%h+D7gg~o-qF9;1ffO|C&?-?N<$B7}_Eb2oE>?y}zmVK%SIkwI_fo2PC zxhGffG2^?=&&pqIo{OV(4e7y~_E>~&F};0gERZ^$5dZbA8?(@Ir`XJt-D3N{IHvu| z8f$QX_G^e%HbEiM`HqIv4N=pr+$L^c`G7Y-Nh6f6pseuOwA-TY=H)M8W=vtz>~QFV zc0d|+5U6}UtSTJ#YX)_&iFvrkpow=QRTYD2*A_0V=qmMho9obn2?T)x-uTcz3r=;q z0#j^sEPMhkBHwT)FmK(HI7Ztzc@xr2Oe$IkI0}KbksA@G$R-OAD#1nkkr$tBV5ElQjkCYJXoZFX)@Df^Ir-3 zj3_d4@j_sfoTOiJyq~MwT0DT`QoPS+fsAh@qc2=L0N&9PEXk}RjUl! z)XF_%AT3p7T%k+e0-y!6m}LkGQW=s2q#J$ke#&9Vl#CGeZ(!Bq%qtq%0eMWi!s(3P za)55qk2X;KUxahJjC)&<0EQR>twE|#Lb!!v_gtM6be0j1az1c=3;`EHFHU+&>4c0I z+gzAWW9gT14IEZ|?hS2zP`BmhbNm4L3ap*Pg{Tprb8sdpIgR2~c|;?9{6Ba+iG62q zg=y#Oh=pUe;;lxw>70X9NXTV%SJ`9!yeYT(0AL;#%wWKEJVUH^BSkqlUGhZS1@y3G zCBJ~vep8`7?>SOjU^Zf{IfCl^fb0mFPs1HiY64^55zPXl^rNwHoJ!vIsVZ%*h<(#z#NrQa8)iXmdV? zHyKPGn!@MqT=Mxskuw=U4lBj&Kk^GEbePh%&BI zWgni95m;=+k-~Q#l?pg!?8HvILJnz}@ZzKqF~h zDc42ILgR{n$J<>TR7;>q<3h?f{18WV7Ro)}gO>EgD^!01>xODj`>nzNY93IsT~uk^v*`eiMnvSC7FqGwPoSe)(N;65TPtG_OcC$OqKIsqUc*};C7IkZy+mp#;Gvv z;FXM(KaIiqsX>Xx0E-_0C8&yHvlJ(>mb@f6EmLL5;^&rGr+Gvn`+Qi547+~mrRn-d z{Wv;+P#2tz_Pb?yn4=9U>(M!1i$7{&Q=2O*9^7ySvMu5faKRuSeq|EqU38x!XP{y8PL1L>ZhiMt-}&517Fx* zo#6lhgv+U#lPJf`n}f&Yo0+F|05~~y6hlMn{>&=vrF=-sk6#UyAZyJZtaOG~e+T!b zf+)H2^ho8bugtDVPdhtsB9LBl>6hM(07QUu%Vz9{2l1T7V8pBpKRuO7c+n-6xznbAj>hi;~jId&JyEWfaOijjKU= zJL_;NoaKz;V7r*e!3-$XH!yYfJU1 zZA1!6yF<>o{S=M23jUabKTm>1+aU02l(!>eLT9ZphGK=UUiF-BbJk4n1Ev)?dNV45 zwg#H`gc5ZE7MnqW3F_9mzCyhHZx;^-1WP)v9Cis-3wKZ@Y^uo6b? zU;MA{DNU&GfuXAs-W-ORIr2O|br*PC_r||4i`j30KRg+79DnSF{Lr@Q2Cx@f3s+Nk zO6<9j0h@nkj27huRnQCenn8mtruwQ^5)bZn4hJTkxJdSZ%~NT^yxvhINg&ZW)P;J#+~OVDjo1k!ZP`E`op0yJt>c7Q`1h=jdSO#PF~V|dMP)JF zOWm1v-}|3rJZ@E!1G~8srZC|LeC{ilD;5sFjVzaB7C|fe>Djqx*?zPT_UPWwuyfjJ zUqt2Td3tmN71@-_{=?d)sliR^e%G@J~&lyEtW!93HKI0Kx|-N<6s?{m2%77m3`F2}+< z(anbyK>Pq5i-{~bhOO`hz#K4JQ^g>s-=T=Yi_Gj8v@sOR>6%vUT zs-X9^+zi)Tb{=|SixqdLL9ZcJ8LDA?sR%Tjb+uO%HBE=m`u4??B~?3kZ6az{!_dM- z?#z#s3|_KPfK4W9oyvKXpuSm#o@$o>lGQ`D!(zVfR$HiWtY^K;#QI*BOk)Kw;`aM; zf!WhaRvQ>?2<(C0(ae-PR`&*DE*H)hD~iPFxX*#h*NAH#Bo7INBw6U3qU>4hmxXoh z+uhL#{hanm1D*=@M{*HS>>b?7ThTv+spNU@9V5QkjM-_<%Pif~vg4K-3@VnC(RPzX zwjEqrFYR1+c?t3M*>FC|3yvb7rF}*VFe;0xHH6EU9D^YUX07OQ`+w1TQIEh3YbhkA z6ieG^yRYJ5^M!)@RJmbg&1$MddKUEKWfJUlTxK2xLYo~%`OF9ybAc--3Hl3aWTp5M zWkj4Z?*Rb0EOU?(GW6J#c20b=dyfj?WlRmNypcm;Xd*qh(uipL=t^p6)UHR)^MFmtWTOFv%8F|r?elx?QX#g=l3tOS$7^ukc z_`AkGO2ErLe7L?n$l+JOl+2E*wCY*Ruffy_Jk_$jC@w^#je0WpflkPND`~s^c#M-UxKj`a*=uF#}yO^Q#L$ z0TGbHh9HE-kE|(wmXCieb@kmVg-KcO@p-kx_8ceUW_bxUtpq>&g~pEteT42)2m zJ2UT<~-;R?up8nvDi~bXVrj zB9+@(QKZ%tjtC4ym)9M9M+d-2{xKc}0vP@iYhtp-H=_>452a?8l59OVY9ABU1N`=T z1VsNaRW{^)VQaeLTCvLm2wkE${Qv+Sr$L^aWQl*VPu>SJuQwt>@&0xcl>5Dhgava~ zW*b3*)7~2WKrmzlybc(rcV2nFh>gGlEN$2=X`e(2Ml@|7#aXv>HQYe;CQD+!#1ce={t7_sx$YIv~`m^77Q^zoHhNL-TEx zWHqOSrW>oOA0-Fbhl1TeXTt-qZVGbxkWH-BZM0e%tLu(s>LleJ9U(L=6R_2yGkBI4 zbDAs-6rY^Ih#6?0WJn1bp+@vVut16*`crLbK+)w$yz+0iQ;)j-er4P`b=E6I3#|J% zl`%6MJIylPtYOI1ZPR+12Y!CnUUWXw?i8lRS$E@*=L+Yv!QEUKOF-mB@2W_%s&Kp> z_h`Z+HOM7f*Ko$+>h(V|0~qHPZhjqQ9UHml3@zj`$peQMyc#zC=l7viUg~%{OwI#SUA04BS{Uw69IBbPuBo= z--BCI+OJrzAag1)fQ0Y*?~p~LB)6?0x#M<)9jH};^R^U|$|u$&9E|TEtqxxKE`Lca z(hQ!<)7f}@ehX!koW#!YD4S+YG~0-J^xKA(?GKF_4W_>JISsuDN9tm}*lR+WHBn5{ zvH(jnLr|1%0aC2X&?CqSbGM(C!O92iZDZj3OWx7~wWftz0R@dIxz7TCN&AAgax2vm z05J-$=zpz!?Sh4&Ie?KHAI9xOHK#v83*E($0u8z|(4cEoW{Cz$U(Zgc=u+AA!ZA4( z=Vzc*R5p`C&9VZ?G}I3FL7v;9l)Su7^x`iYqC}Uo9DAyBRp^6|{G`a0y@g&R7f?X= zL2%yb9V3F4h_m!Je%|rjo+z>G)i(++AY%bnm(`-HV-tB0d}sgd0D!;YVtl6>`XnDi zZ66EN{Q36=i!zydcRKz>#Fs&|yt-S#J;p}R#sSbt$jq`W5ZP;Kzl!!lYg7t{}!WTWa07EBrrl>iFLIq3vF{>5;<^Xab~(DB<7f@0ykz>{=$bl+#lX^ z=$)XItop_;jA@*CRYa=mwTOQC=K0>{cJZg#)%Qx{wxc{WMxs!{7CN%VNj|=~BV(dceitqlEd995M2?qxZi;$i-AfM~`1fjKdowYyg>&OhZadio= zE%~q>F(=YDImT1M*T~U_v4vJsbp?%9vnwz4~cPI>^1BUpw=KZB9 zQV989K*O*hKS?*M^(S4k{D@?_bC+wC@1VBPo5_%_PEomCTrjF~6FuqNfqsi8)GDl_ z-q#gazHa=>Wmz1!1P2x2*h|$JZvMG5Z?{4t^o{1(4VM%zZb=fj#mDS1?d5h?{?JtN zuklJ9aj{*i>5*YrgaPf;&`P_;%_(+1U)@ANn~^q69B-6+dhFmN5^?BC)8DC@rHY&6 z4ca=5X7X0I1#P>5FM4mDM*bAQHf^t{qP44@Pq}2PV&(h-hUoa5o&dZj;Na3i8*%2< z26t1vHp(jnZ76|4-Az=5;>f~D{uAXhaiDNl`bwo--UwiCsKky!L~N!0_c(Yc(sJ(6 zRuQH1NGpf);hCsUOVHV&j_jKdvQu9Ov%mO}QthME!#pPz@?T3qFn~bER*t-)@4KbN zW0fbd=xXL1w*IR60&$$VlVQLt23}?lroFxM4f?_K%SrpZE<<^A=Tr~C7b=Jk`PmE{ zIbUj<0XpcVrMrgb{sQPOq&2-vDB^29H&P33z8gtY(UpqLK`#Ik-~yaXB=0u}8}kP$ zt&jeN^Fwx5dKy%U&-@*9yNOf<&cPqSTX{Ln8VVk7`dyDKPWd(qP!}4gPnD7@fc=(? zHygT3Y;5CBwv81xF{)Hk$vYCyavcu}_D|6}8WaBFoQyfRi_?I{8mb|TfL_X_cjTBW z)!)|`$3o&xy`?V8+u)COJie5j&ec-uQ3{_=gsrH>I*FGNG!;fN4BM5`@Aj~_FlhCH z!F?~IB0;I&3LO;lW-m-ke6&MzIJ)R|KCfgt`cVfkjP8k^;ViQQCtL`CImJ@VEfS1?s925KB*RNxWPDq)E=t;QQ_cpjs&Ha^y#CKzac>u z|C?1GUwZdJnR9*g<`obLZBU9BPkAbJ^t^_l+gW5>nqu0vq9(siW3^M^XNQI8%<(jS zB`ZS8mY4F9zMz$5TSH;S>B**c;o&fh(&9W~E881-NH42HEkUy4x?C9dUhf$H5Txld zd72l6ej*|(L$TF0wQQ)rFbRPPr;D`Z!B{jv2h_<&_Duj&p2#-O>pNdflnmNpg|Z8Z z{wJiNe+$M{6xF9I4AV|LhnU>Tv{#%UK@QV%18d#F0vPeEEb{-{EnrDaSb52Le*O@M zTIH4ys#@{4Na0{Tx!81SYGSs_-FC&px^ti=O{c*~1pifXjRzN@@>H`-ZOnDQw{1@F ziMS&FKx4h006xTS5gJAdKd(&_1p6ZRZS`%%NS#acL z4}$2R2Of2b+fS}3u`@rrlYqVJNVwfrasRL^XXGuZ@JsT?u*R-T>z;EA7DsrFj2Q|N z%Fq9g(md3a^k#!Qvms{e<&MR?4*OIXd|fuER|WMBFhAM*9AwmY*OX{yUI+|O^6zg~)Mow1Cyfv5JpU>+c^*;$%dsN|VH zglHA=;nf%j;%bc<2ULa4Ub)m_YwXZ z+JBmmO&yz+*;872nOffAs4iY(Nmz-Qg`*=c9xk^MnDJ$#8n z^4~qsza;=F+g%P@YHD*V(fA&|f4a6bcSi;IO79A4Yn314p=3#LAFVaH9MrWYje=hp zLWzp9<;|%a1e#sI)UNtQ4ZQC1ztmc=k#EZR*R{^1d49IaJ#wgCyn=@`O=0!^L~T29 zsx^0T6h+)KafD5bu|fW&Ib>Ki{zlacyS9lmqC-lZBIb+xGP!~ zOgv zIAXH(R}Q9$Pw5hGF70)~g7~o7E+5=MgI#07J0;k7P)LKB#ty7=c18rlSyxxc7UOk+ zlYBzn(T3+7L=d4pMp9c7N`Fl<3EN=ipdaiAeUdN|>V$QH;kBMkh1>SZH_A36#qbJE zuEU2EujDmKR>_6%=CfSj0t=9`s2fi+oEI_8mnB!vH`F=|bWX-K99ZbGa;GF&q%$K; z(P)3MXeHrIFMK7^58wvEsb0b7A%MfRjp1NUCO$TuBrF4PF5}v+3OS3rt*%kM)mlpK z9x-Na4I7p;*NchhQTEIB0F7wup9B^5e)dF&)q|UWJ>iU?q(gp$+|k&i9i><85U_M; zx$wIdCP2p-!Nmn)-6F-QKeropf?oSp0WmQ6E?bDx<})1tkn%J?J{^n zi!3Y8h|l=>=-%CHr8l=}QvKT(LIeG80w71&Zxj&nxh+UUwjp)}^wQk0Cer;@O?2W! zX>!aTnGcufN5u#2#LQJf3jQ>lf(yu}e7>MhXln(N7%*hWQlqnmM5z{WYN4n+P#gRD z%0k%Gj+^(5-#rVx-y}|Ez=vo8jM>sX6mff*1dh?KbT&yGe;xck`$NgqD7bytHn+;M z{wqXDGw}h0yKejS7}`eOJUXTq!CQit!H(dKXCSy1yWd6uFVD==(fuaGT(3MHyp-f; zhp1-f3Nr@BFzWPZ4=jg#Xpb72m_ff`RX=wy4USY_ulD~dlRty8u! zx^qNq{aC#K;^grQz__Jr%@3Gqo{6TV1x20*?goyjYxDg)lchq|xjsiH2;9soL-W1X zEi2k)Mu%v#<{!Io)bA+*8iGoerhw|5smHA01km|f;cF;MRW{2xq<>ogIOtep;X6l) zgpx=_%-kLS0^WPhl{8)GkXiy1p3=VHj#ME$_K~E3O{}2YR6=ieRE;YhNw$!XZB-u@ z*Sq|Pz?6go0{sjC{FcmQUyoM2?cHEW4@1ipeXkBCaJcj1bi|0FMo22}b=f65@zgP{CM^Tq`x^C#J4pqAG=fF; z$LZ*X)}R}cv1I48JWimUin`7B^bH9>g)%_1ipP>471`P^5wwwjhX%R~6Vp*)?pa(< zNG}h$F?-h0+vd4w1K?w5Mz$b5NX|`Tt1MtIqV#S?RQcXwWM+7mi!NH2Cr9SKgO1&v z|37dJv})>Pf}$+IKIJ%L%{GS9SqHSnQR}(}G5Mvgi1`7(e6+)NbkIyRpG9DW^3DET zeroFi0De8`i?FilUO~iCF}c_0bBRl>)QH3>nR~-X9wf3Pd!sa<#)lX|Xv!tjjSQ2Jr^GJuu}}*O*-nZyLVJv-HM&IVu>zo8$xW8p?KK zwsFwuncWtDuuL)6_oXcV%MS1Khr4%(O1-iAvH#A!+K!>Qh?$StgLum3ha=egrE!lq z#np>wv!X09`<$zbT;`P9=jgiE(z?!<=FB;b=2>Aaicc&P2WACbnGY z?L^_98)sXn?TB=>Pp4#Rb9Z}^BO=s(^v94IwYK|o}1u5Q3z$0(EX8ja}mxPlJ zpE+s_@-u+($~X0OV}O$|7NOU(XdprhyX#%~j@v>lL&H5u?Mrbo5g|061;?F} zvdVJh>NpyDiovxW3>cQhPuv|`<`8+N#RnTE6B;Pf2bwf8Uq{;qLiXDq1U|AhGhFFd zqWJfFk4B%`g#IHY=XfKzL#q*FUJiLvLmT0DZl4=3cVzQfETQ2m{`1;AP&wbj!__l- zlFbk-&f&jCXO@?cu<@Hdo}-DcUkUlV;op@{J==oKn3VE85My>G{8LnTFv1@rL8U}l zs-Osa;I;R!|FwXLsN7LgZ{PW!b`mwG?yxmINDd+zbFwO|joglIeH5_CDchzOuIlPKD?@cQlqXm*fiIHQ+5FwjRM~7@aQ$^DsN)hBuuXyR17c+%Dz~tXSN)Ozb}p zTrRh+H9trkXRUUfv>xQgw62$jst_JH5ufI1r&ndV=2pmJF%6v`fJw6f82y?;;qyVq zwL{8%JdiPia|8<7vy=0Y6wdHlx91g0xQ~!NKJBaoB9a38N1EuV*(_WYIB*($7`W2M zN=mS@=XlKl2{!on^~yAkL}LKY)xkOBXV1`#m$@}xE1Xc>NEhK?Tkn_9yt1SVa3h$x zvJG4$?EF$FBwMk76<)=sSv89jGW~3mlOCuauZOyzp(brO3+A`*_8D@+>tvr=pQ1>d zmFW#Sc?ygOw*-(?sb#ap8vi!g-$mXH?9OyWP(@j8P%qvK&nuV1uJvi#6b=wj z$u$si$^5Y|$zTjGroES(KHMCu4s|`yFxN$rnOFkWfUNn%4Nzga0a9I@>A$~eqk>|E z^}7^jTmHODRt1f8$V7C8%cgdyvBuiu?$laAhl3~!{%0MWN}dB-f!tePjlqP($!SIc zkebm-y}90js%5Y>kAf(YTF-2RD9{`xb2s+0j<;YIci60x7V0(64Ok&y8&f_K{$3W2Sf#+fsq{oJic;mNzo^KOp1~)|J zI0tl5-VgTNaWV%>*ZHjoCZ5;ybS+uz@t*B2IHznPga9$qz)?6M7RTT4)xN)dbEW*1a5C2a$B2jKT24h4Z2`xU4;fP z{z-RecccMmV=?U>V?-)SPeM@{bqvZaiWE0Bh`9(luaY#3cpg_5nk>Cf6dVIA1#S5^dBlh_GUbEB!X`D@JL62}h&=K=%~M zftN&|tRa+VC%>tK8NK*>R=Z%Y(7{5&p(Iq$PY^d%#kfr(Whf2WxN#bN4VB=zh5ghB z2(M8Bn7~abgHyPi-`ZK54q>1S=zY0va1zr$DeX$dqUr!LSyEa#;g}{cS~u3=_~}Py zloo8Oz9HZU&fV)W$RGWU1f9|lc}H79bxrgWtij7{JyEQQy!>b2Vx;nbR1wIKppC%D-jgIW7>u>SakY;{X`1a}9ki zp+ra$LpysgI*tUh&5_llC<7kkZ*SSTR>~6R@7kMo(VlT=tkE(9tSx{+mdf(o_I+T#jYQhb2C6(0R;c;^fh->7iotaF zs>eMwH5);m06VWwt|tb{gPuJ5xv&FZ=p3Aw;YuQ6{37g3yyE&aAC^{7ygp za#W$W({%6>{7L6uz(=st`p~qsYb1dH{$3RffX%fGEwg~Ua@*7?fmAx z5f?ok|%8teVYHq~j$q^7T3shk#o@-Tztn0%sc zaF@@qcBIBF1b{TPGhiugyUal3T+J)!0EQ+FPmOU5R#0nWaAF5^C8p@{(m_lW-+)Ha`xMG?U(1^06fGe-!OF@?2r_)?9`27$Iwox1R_>9p{;}OMo|H6*|WN zv)e@4Xgi)n{itzvujEZ^S=`+LKWqF;Uu)Mxg)~9gHFpjryBDe;$L*fF z0A9&MpCzn%{Fbda@-(KnA9Cl9)=C2F(jC(lwM^aN!qIJ=xR`)xptBPYcLEI0jfxKq zKkGmL+$8H{edLV$<+A}B%_$MiYOT;v4@5**znx2WMZblSkkb*VhaI*+UK1RW7k>)N zxZ|CitbMXD!#2jUoND37>=y;XSXj&}L^bo<(JR3XBqZZOcPgHv%Wt4j8$0a(%4XhU z-(dHNde^;qr3&%D=we4F%%^-eA}o6jAR}W_g?6;`IaM@_uaIQ|uq%lzv3V*QbsQ%o zUr++Ns*q;hA*qlKJpde2)Nm6ff+Jp-e^rPDZ+W%_*-f{)cf{5`Zz=Ig#GGLNB?~bM{|jZ z?sp70^4t89dL#VDJVz&3!V|7_4XDCw+(t+i@n`Kb<9eI|@)|2B-aobkC;lk4!Rjz7 zb`ER4f6(MkL|FR_NPP=5&Y%?#X2e8KLpG#LL6PRDQ!LC2n4o?}sw)gfpsO(XG83B3*#TqG74B0)mP?vIW_ESLy{nnVsn^ruHSquB+ zX147~C)rCoUr>-w2O_Y>`}WfW!qfRDR)t=gI4`iWhu%)jp2`x3V+uv`5hap2?%$!H z{F@ACAgUv~x!XyqVO>EIO0C;hAU^B0U|hAR(W$S)X|t0p2R&Ocg2o5b2GgZESylLP zVxHN)WGwQW1RFLctA(aVxkWvt(Co}9idJ>uA@gcmBX0T|=KhM(9n;r%qDzz8JSIp3_&y=$ z8F_Q6WQYVFa)|uiFVjB6jCr(T7BCg4h((_VL0;oQgo;eCy6#u{z$ZQ69`~1N4Gz_~ zeadb_K_44#3iLW()HB4G+(q}dVaEceLu-akM|cHb(RW9bICBmK91>f4Z(f%_3od8p zp={gm=z-J%D@M>b6y-Abku9{&s80Tk?*uKw(|>w#S18hySk)W`dxX${sN%O32phOW zoE=1)y%6!Nmc77I<%bbD;&OU6u|eAFSx`ti~UMthl9KLnORWn z%07Eh^XSfH91>`*wFmgoJE(=~?3I6pLS?@Crk$$eBZ7KoZB!DjRY!DCNB>^6YduWS z{9eB(5Wr6e%hdN(#^ca>`Nhn2v&BZrwVWIimw|vQ zbwns;zr9C)#4dLsTu4G%Rp@UBMGM(FF6kGuPY2dmHD^85Cg%tyxqXjMJYh8Fw;+6d z|MYpT-BhbmJyc)J5(s#GdnF5kbayTEPZUW@qL>7$lakGB}x%Fm`%~_oMH$!Xc9N8d^d3>kGXZUeJiA!2WZ@L-3G) z%2VOzQ`84{mK%c%BejTLEoR$2PMsfDUpv;i1jercIsS})D1}eH@wwo5j zOyH~Q=GWa>(rQW}WL_ceF%7tib|KyXNK81LG-g`zoI6I zHuDn1O=n7^a*!MAyd_r7V1S#d9uq77kj^F28qWy-phwdp0c;YLEdo*Yj_2=aVDvQT ztjn@Wbsk+BKlWX*%$s0SBHo^SC?wP+1)AfBiG9POfgpg@c6Q7sv73x{lfgr6hAlzW zU-`%eaHg(~3?1=ftz;H48N@-RrW^zN3hR8q z9bS>XnB8+SfuzD_I0n{>C4GMU7%1)|diIb-_rk{6w33$=_J!lOzF{@=H2V4oC@`5+ zB(?$7j9|zn?B-3>dW2rdBRJ zWCruRe?)ll%rz_C%o*Gt2;-yc6tMWT>}NYINtBy;H$lk7^~-&@tB*qoL$!jkjqH&g zLK0ebNa!wW2K?Nzqmbrw+kC06>(=-~hqKz5le|sID)cio>L5nfopf7+Fx=l?Qy6K! z7#@j}&^gK1o`{KGNbwmcrss1CkuY34@s86aOch#T-B0M9*B5)cB!^p3Sjt}^ZspZ8 zZ9TSZ3#aZxKsnLZy;O-jc!e@=K5=A=cF@v79m5mKl_g0W_fO2D{0(XmhwiwA8u{l} z=BkuP>H!EyU)~2daRRqt_|PbBR4>&e1v)jO8Ebr5se^Z&fpEAOKqNiCJL}FQor-Q+ z+_fOp1>+*dhh|V_giD03kA-0r4du{d99Pjf%$MPI-c z1(-Jab{Ud1eG-Syc=JXEMJGS9FK{^$2iU)TT2=Ccu_%+j>;1C=2{IgQ-X*-hz%P|a zsR^(yZYv*j&O3Y#=B64lZ(T9=PHC~rX4QswTSlbHeNV=vnLs}`>$O^b6)a-sr}Pn? zyr+5-stNTTC{2HSj;F_2yLmn28+RPI;=u+i#Na98%;B! z7#m!WxEd6oh~jwryFQ=E^2trHj=uC&um%l!Q^RW!+dWy|=f);OEC*uNmlVZx{y5@F z*GlW8c(Uc2f2%*z)N$;IBePt@1M{gr!0XK zx2%2fqJg%VITf!EZC?DIL)1eiC3LA0QC`I29y!HPIBY45h5yB?CUy$yG$GP>F5c1P zOBr`tGpuz}vu`fBqB-5W3NEwSvdU9>PksI2EQixySE)OHe{%VBjX4)M=IEX>;>)SM2L7gO9v)DFY$nTAlmE4~ zfit@(DCy*5(*=&P$2}n($9x z8Lv0_Z6i0FU_pg1KVbYqyD1#F%NVfQ+&voo6W59tUh4k8ony3o35uJGOh5o70IWCP zNxG_piAhZqU>@WmOV^(xuZQktAFhR49FdaK{{N}|SCdnaK+fZkG?=_*@LxVIs;gTM zl@%?!c_Tru6X&mlZO2(#eJI5xapJvS7hmw~$(hC)C7x1bUfS{PpGd6JH2v2&u(~CK zGQ*kwp2OxFL)a(^+7ijwS-;S~!(5?~z<2z*uwm z8HJvIhdjTZYeI~K=`&heZ&FDEbIq}(eJ3jICqzGkbwGlqF)r+9gaL0pS&$+Q#I_kH zDXN;Hw#?1#_dkA0?~V%h`6YKTjQ^z_qdB7)i%vsXRsZKB&A7qA z-NjS1$FX3A(QZxUBxq=F|<{H&e{+ zf-D5J^liN|zo|zf&f{ptAyiRiB8fC;3WKV<^wXAan@CraXO~;h}lM4ouURQ4G zb8VHtvAYbE7+9I!eb%Wzig5_uJ#wswY6h0MbgBkWO#pR`J=+vxzlG|WV;tn|O2=RL zXegntc`!*)>XHd@5Bq>uyBLW(5kqrfbbM99Pt5}y7_#g+yJovW*U*+CF@AkTp#h*s zpR5p3$26P%N8^VX`pADk(`U0a?NYCAZ7KK{un*p$n2Q!kbf=ny7H6%p4+d0$Zodsnue<_2 zH-w!m!$f=s2{Rwo`+l&h49uWGDkoEj5kHo&r|m{`*y47A5~Hk8%r%H8CB%X}hpPQoIv zKq(+8tWLF$yTQ0Qeco`HF4#>`B6gvwn0d{!tHe>&NO8Yt%dA z0zc%ztPC4y)`AXzjc2&qs9?Gup5z}Jij_hU^XCw#=F_Ja_O2R=Ci>o-5bC@;0CsZwO7dxUF!r}#6gk&LS$2{Jr9x_BBA44E zEn3$XeoCls4t289aH(@5wgBmuo@6*NosYXwgmyi}#0VCF1{IprrKv``C(XDN81q%5gzk5`OYcqX=3&UTO zraifvpv0|<0jwq)_2(0P?H>$b(2Fc`nkf_R`zWvyst&ij> zBdHL=x-JLULMuW}C7@Fft5mCMEaO?HFcQG`F)m>h=`JP8dB{hQkg@LRoCa=l0ixX{ zn(&)O)oa%z$|5UzVAT`^`9i6p{afy`d|=S5v($kS>Kp`&5OiMwI(9)ri|4$fT3~Ds zIonf!_6`K|0Fn((z|T}zUs05rqO60!xiS`fP0QC002hu+ZdB~rqhBcS0|lZzY6d9$ z<~AKk;bS{dB6wRVGAUaEhHp+H&y1$31w-H`KTFPgbWO@t0|h&yG%T24?;*$hPcPs+ z-CNi`S?}NcyfqGtMk!^A?z3dM8uVf%V{v(4y_sMzE~z-4V5wBjRB4!C1X@?O zkAAl}+IpmXT0d07+OqFvvADaITw?yIljN)GGW=D>gcQlZYVELhL;#b++5cnUK&TP@ z*5K8_eMQN066lWa0-?g_ioGXCh!vbv7dNrjo}yLnD5BaL&-NT(QDV; z6=VDey3xp{c5-cU%)DNuDmU`%uIkg0n##_*j*EgYWZ4QbRQ|xzsek1aP#bUOP7JpmA$J7vBJ}br4Jn<%BQ#Rcf~qP^lb?n|Ciy>pdT)DY zd(C;Xem<380PO@Zolk>CeT+kCfiXCYlMaJ&Z>t-9CrCD#xeWM)$|o=;omZ9-)7S zdc1WmKAKr_P2*-uB4zS)(*DfjSro9QaQZL(vcS(I5IxBp_pP0Q5%BYV!MHwnwN~(R z>7z}=9ej+r2W7>-?(u!N1uQs~q}93P%-phocAa+CGuZ66e^-h(g0Af}Eqa&io|euD zMITBbV}vyLq=|W1yXOq)Hw0c->W327Cgq3whMMhb^AtO;ic49oB$c`h+QBZ*UY*JcQ5tY~&kP<7%P4aiJp{A5l?To@0FnPf$$iF#0drt(|Zl5fxP&~A;t zPgAQv1&m&4JYYA7k_#c?c-S5G)Yx?;t!#D%?bY=evtdv6II9=^McgV;rsZAABG;A8 zACM%W2SFBd@RI&-HtEMO{4N`I|E&HP(rwK?XHwiuH+h4(E_C3YFc;8gJ0an z$a0f%-MgfUFH%NoYwdKA2b-Ur{(J(iT#r~gZ+dzAy!=Pl*3`T4N4Ditzc>)>mt|Kh zj3M?3P|L&-f_ZUCXybm$OwxG?l=WZ@Uwhw&XY>1_Q_5N%`D4aZ>{UUY%)HlEJP{jv;d+jMn^^>?&}l>sy^qR`Lsd~J%fRc8%K&m{6&!;8 z*qOoRRymK8F+JQG8|n$#<>_7;_EtGMEcGCTVBASEpXH9^CEUdpVui`o(Z$?~;zptP zT+jwLg`rpAU4krP2qh&;E0g1^48!wH(I!4@aSIJ>*q5hsonbv8o8Pu?8%z;wADhQI z7gi7){A$#{gsvEr$ws=^rg=YO@1kl?JT zlG;vKW>IuAQ{G`of3PjNd`K1;Y?l4Yzv~Cbk;e7z!@Me!J85&b3c;`=Utmaw)iOS*c@d*+sAI*Pz}Xt6@GJm zwd3p+aDFF1561WdkTtGPiXlO5Hoe5(vOqBj!SpuCFmJ)f?A*+ohLKPRu8!1S&`O9* z=Kg2%| zHBGwA!rBUS;nirOM_U=06|PWQI`xJ0`==DFUcnZ%+ak4HY`) zT$Q~f+Px~0Mkg)T7b|Os$I8?IemsKg!mHwe<4=w?a3_8e3B3b)ot?~|U3~c3M#Vq= znQ&>;mvcv8Y+Fd)kA3>qM3xHGh&;@!j)rksm)@g; zYHkB`!D&3ZXB6Aq4NgYTS%7gert$H%b=-mDXcckdu5(@1g-XPr3KNZoT;6jd$b~y5 z2Ir&6EN`N{1%%m`6Y*9X?NXElBB~Afu9_%Qk)B_yX>qi}Dmcyfu;ZIr09R|crV6Zq zK)PpwfV;Q;#J#4?ew%aaNI|m0vJBHRIp0mepRr+xGL9fEcs}$nWGc@I%7L}Ir#GQf zbZHFcIE(fEcXjMCwXlajLO2u!R}Rm|n>n96U`^Q(O;XQKCKju?7+2}{{8BSK+gEYC z)}k*~<}WEt&~A>}&5|_e9BORm<#4LdLNyV@t-ss>iQ#CA^Kpsm07dEUlH<-@ICN8*X)v!FVRoi3+`Fs#iQEf3_H>Lv|WbU%|D$Rwk=VeuvMizJ__N}`aK%js#t8St_%q2NQOHksZSNF}l zAKrkW_0%N^qPcp(`l5Vx%?sYFP9w(AE+>pZv#(2<%%}h5mWGRxA_q$r>p; z#9-5WH_(6(PI0ajfSP*a!G_$X^cKaN-{AkOa-cCO>jBujeQ)1cB*D&?(w~l6HTFWd zQ_w~ow?<|t=Yf&1AW&i`m)V?~TFe7(f+&4)qKlo({(e+w*=Lhz#u3EMd$uFG(2Q}u z4UmfY-Z&U?IR7obeZt&Y%s6MV8YW1`BkW;)`sPYLJQ!p2o{1#{Nt*&x1>r0ijnZnq zpS5TVMo|0{GQ)xBekq!4o*tDS1?^}z40CsSMZA+frD$(&bf$ENUWO#a15$VTbiJcvSFchh%M%l1*jy4+{LhkiHOY&6quv2CLBCKRJLM(k{)RV`zJ z&>))zB|~7`uYcA)caG8pcVKUs+nj}v&_BE0oIixY;cjm~ne_>?OC$Qs`PGVK92w}M z#hsRRpFrRJm~@=CK)!8ZGEhD8+ECBjHE8=aozxcDy9}Sa*0$LU${-zEO6p{+`Ty%Z zRkLV{8tWB2@LH)VtVneBtqOpWA7QP@jqkso5HA#u9ynOoudOXelJ;(j?HI>goAg&D z(nRwg1!+VGw_bWLt{L%Fpwk)f7;L}@sNxEqEr8;1 zyB&a>AI+}Nl2%W0%b3lbmA4U_!GD&}&$3baUPE^?M+71NNH#CP;qdYNwA0(vsYb1U zSKOH3ZsY!Uk=Wj=?%O+b0QQJ#$D-d0 zzaauiUA62kBR+i9?!Wvh3WxHiw?#YC+aj>Pi>1Amp#EcK=7&>f9$(~z&B9#w5T~-O z>!VLxH`w<}RuSCmYb#3-ZTeGI^!@6XkJ~jJx8As7?m3LRt{ZpQGYestA7<8>20HOsMM5j0$QU}l8s4dwaj~zQmKYem*yr8K9zIeOouWG2&^e{uqgR_CCv4| z1xArz5yV$GqjEAlf*43yMOTp$3M%FbmGJBYPS*vypqa`XKz#RlgT{_nt02VXaZYOn7N1ejihZE0C#Q2iD5+OMb@et1a}P=9lEIK0 zW`l8`v$W?RI*M~M7_}7*N@ML*0kN(pVF1VTuvpDgRyL(@4U;5!JtIMDeecw++4)t$ zPZW-3A5Js2yFtqeYCcgSZ&<3I#S*%1t|A`FXTIqhOu|XPl^a_YLEI{q^{+)8*#V|q z$<6Eg@gLWOzrHI4SI}g40-6{?(Em+dCdeX?E!@)&pL&A1Vdk-t{M$0;Mw5@3J zCtWh`|DD$b@W5YTu5iKm-=p|kg}F`AX$jS2#?$d|LA@YTm(?sl_FOJ4Z?^uguDaod zoA64BM0E{Qj&Z~$OcLx=^8ch}E)GVSg};)3VqGcc>#_r&XC|nQQB+%i$&B)$>B0on+)r0tV zE(Sas79`H&mi4IDL1$-h*CpK3PilHJP(||2gi*A-A*Cu0*2~0|F2rThd_-~c#!=dU zvtp-+tD;FzdF*4)oo&-2}T&<5bKGP-0% z4n$M^6S-UKRWtZg(U#YI^|Fd9hAaG761G%LqOGd@C{gzz8kB9G5Y0mgfJ`B`JTL$z zUT6RVvF!Y__dldh<6^I?ux~9zr0KtN#ookkU*zzyeeI`4)-KK^oq2dQLAjSKPKj0B z(NP4i2rzi}W|Zx=c9oqeJSocczgK&GG{#(D=~-Q1il#KsWHA^Bt*eIFH{3)ankv8m zdx++H23t@{(qb;MS)OODNI%0!JsPa1uQz~D81`FqJiwOGQW{w1EmekjV5B+SWp=(n zAQJMqW$k#f@ETsdH1|q3lnwElnd4il96B())l=7Qs&q}{E;a*5m$h{sjo2L{bzrxw zn3}_c!*CtY#&iP&^eey_FdElU)49X0eo`G3LaWq*dI4Uv0j+62_jb%Gb6v@+>AQLl zk@mSv`Vz}nag;3E;p$l>IXFdmC^GvLDLIEB8kBvakcDHRNWeFJBstoOwpu|1qyRVD zbIvo7{@Kv3EzOj@j-Jpui<@2pt3M>h^QRg5w>%a>%zy5-sULfF-xg5mVk3wd|%e=G2PU zCG^TsozrddYSnh&blUr=zLO`bre2;VU%I3q3GLXas=-hlJ4b+4DvAldU4ZaLI4-}C z1YLkgu4EE`Vv{T3^1dpP%0jVF;y@CsBBR78sHDvh2ow)|8#?_I3)V*>`<$vPK;*U# zGf;-d+eLHwW$k@KqUfu%zf>T1<&8B1NtjM~U1MWdvH2*y-Xq*v58>_MD*?ac@c#LJ z%5+XO2YTdyY*ya%je~R2U|}*yl!;QDYjLjQT1;(4iv&L0H5sZi#{dnVL&rY___wG9t&eI>cB-@{_ zpd`Ya87Cy4rWIl4b-^&>WmnOAyoPBVc3Q?JZCX$>*H!%+4EFz$8|G-0uWy5(s6_Wr zo5pc*=-fAf`qz|%%Sx7BpTG?1u}BhqtIl*HGZ~L26~c2<2n<-?o0cHJQSi9Bd7ZU1 z7&YFnLg#rQlzoF5Hd&aPYC}twI>QfP>g!PEndRUBKn11nuHRNWV|qrYQZD9MH~x&P*ye@A*7?vuLjxC?;V8P3t=IviA$uQrC`#hQ zhe<6nfYNH`W3jU7JI4y{WE<_#N7#W};e%fveC?n#Csenc-U;74ELB_XtZliw;C%C` zD_R+{{=QTM3KQy<(Ix8yfui7diEV&5AsUorv5<@;0Gr9esCEL7im@oL5giLmpR%Zs zoapF;TAr#S&!|&+U!=8Vv5Ze4=jWl$!iV%974JC@(s}=#{YM=l5O4iC z>R3o?t%X98UL&lZDA~3|P*6X}smJIe23TKzYWK+}dM{4XoZ%zAzQW>lwPytO{=Bjz zEyW=IVSnXbrGuE)`T!y+?4&^uMglggRdaogT~I|;!T=1#zGkO<-t#4`OXMzSdR05AHO`2O%1i zZLNxnV4{RTCpEw-yA0Jz(=G%62IKn3S@ zuidX4bjO;tcv`NC9?uJRz3`d4_N#t)sXY7%KB7uRdVNwIe0QcqORvx$PdBTy#*y_i z#D9BS>bO#RH>*%&WH2I-pR6#)&vj*Lt3RVGvXcVJbTlGiZ^z5s4%-Ivw2)4~SS%)T zNy!W$wJx;IRmx(OlHvtoYAekp>iAVwB8d_px1DwnDF(=hMs=eG&NodVFqPy-BXrb=a74nZu0@VfX7#W>lF)4a~_Dve1#MZC)c;1yH@D(WfW@%}a8 zo#)d3;&vbLM$-DzKI!R8Uc<8DAQ4wcC&k9D^L13WMY#>(XgemATDnj+QqjJ2Vfo>! z;dAb7F&Fa_M!;_1Z#Zjw8bFiAuBi?r?qq130l*;|lx3}uiBO=4ASEeRlWx`^5mpNT zC!78MB>IzjG4lINnJVYrd!ERfbL@+oWT*`_!K7?fjPbLOo<43kaqZ4-fpEoi4rQbu zlLVjvL-uXsP;L#NJN2%b?yA*$$!U^dZ^wMRi)wV$JMHJ=8mpiM*F*r7vK=ZI7Yw*f zwLX}=YM$dIXvlT|Ed&j4^}urQZ0~5@TZI!;yuSnum#~%2?a}nPY$>o~=)4auz^{on zbDD-_OeT^z@MZpMJx?z?BYc1nl%|-42qpmxXszP~GlD855M_`dG9Bwqg}-1kT;x2d z;QSWAI*N|Gc;~P2@ZY*P-(6JYed%~=*O!|yubf$mYfJmI^_0s zget)uFSs}%8kB90l+Hqu07{@y%?SXBjRXKn{NI&Ra$j2vr?z~heLlC$d#7KK#lE}H z+y9H!5~nHUf@QNf(J7WTOFGnZ-XmvA-dg}Hs!JZ5>OCY3^*spc1le4bJTbEjBm%|i zeh$Lo(*?pPaV4Zyy!8cH+68)A`UEzxDl%hvU0mXXK^~`mBs+K~Kz6PoI=+;)pAiCG zfcmAxT{$ zl#Nm4cQ7a=@HwtXNoSIb9=LlZ{MmRCwDYJ?G6k^@Dzt0_8UT*T9#ZhtV3LU{nfKY%DSYUo{iSz?P0K>9p8;;)_mX#7V z(#gXFbruP9^ExHr>YdA7i&&9nU|Uug^qwSwmyzqW~-0!AHAB^+nvtieCn(mB${Jx`llo6$&B& zu(OJHn=pZ45kLe^4AmBseDqz0-FkRWz4Ie~(iJE3B1V?O|7^K9?Sf=*T6kcv(> zP-_EfIH_QUSX~ndWUhu|c-xFoVN)oMBKK-xn55&{wsGMlt+J`iIdFj+KNhBM!>mx) ztz8M4Up}gE>&v$H(~a3z_51q0|HOQC`MG*&lfm%cp`^q*7XJ*|=#ZuT=)<<14JU25 zwXr`5rY@x0hQ3g;-fuZ76ehpbB!_act=-32QYgew}@WT64(y=*h-qLI(u z$voZ{2oX7gr-ehy?<&=>1+=lnnh7X1=azz8!tbqsiKa7=B2W>s4+C_HT~&#TSOA=* z^^8`HuFnbTSBd*yx6g0(fBRFUQl6ZOsSO^uWskz#<*Q>$aj{t;gzG6a){)CtyVhtS zJJhyQWlwZChy`&#Jb5d#6nCKBsgUId6&2=LiQvo*6V7tvZ0qAh!Svcs%s^rsCMg$8 z_bLPWfqehN5=`ILsn*dPPaW1K4)o#@kw5+Fd4#1T1zljc8>Qsdr8O1J9vgA`H|m=D z@#Di-L8+20Mwak85CGWSQc1%VZxz$PcCQO zgUQ)zHYaoDJJim9ok7@WI=ln}qeV%USl3>!%7#V3Bx;k;xYOHEp&e6lVn=C>=i>hW z*uI>6Bey4EE=dR6%JpjHk}Nl^@?PsXoUn;fv}Kk64A-zhArE}#$y~1`YrWMsYVLh(?APOd z-fQRweJb`pw8DT-(dA1%i-Oc zxMr_hped9R%7Ao{Fd7mFw9XVvGxK01`P*uv%(%Y1vr1`wv~8;y_E^ax@==t~Oeu;0KcKAXIEG{TP(aiV>vB`z8ABq&zaD0|y}* zlx?aA$3d`BVn7gx&C-=Yz5rbz1E8NZ=^Z23e8X0K&f&0mxj)+aIvEax#B8cF-Lb8m zdM!krRo-(p#0KcGs^r-$BUW#7&>&{DEy8gi4OR$vOHmEf$O)MjU*XjUeLA8H#=Aqv zg;?=57Oy?LUGzA^MZ3hKFPgB|?fOt*9}fG-C@6v?Q_*T9_)9KJaDPsq$|YvJB5N<& z?`oD0sx9kr%E?Lu;}%KWvf8>5X2|U zKp|nF#;(~Y!7XY!fX6oLb&TfevTP-iYbctwxNBjjRI%}Wb=fxJ8ZKL#0`f?^KQXh^ zIDqm(Q2Y)7Jo{X3{eUQaeXBkh-tj>%nt3`ZWPFaSun>|{EIIBs!mHCV8_N9FsQM$e zuI+mefmSAd$sTEPvRu50ZQx9q9Sp63TKJs`U^B@i zs_^EZObrsdrLtFOO?{fCN|oLmioMg8wb_=h?!aa`Bnlj~I4|$6|I__lFI+&=W6P(6 zBCT0$3M>Q;2o8~+EqorK+jg^R7AdOSd6bS3QjMX2Wt^*vNSQm=1*VeoYVVUwSI{@r zrD>@Nq6AGXpobFxj!6fG(u6AV%CG=#c1lPGI$UMr38IB747bf8p|e@`PjXD1#!}3j%owm;5H&IJmzWjdAOFuzbtWzn4VOlP_-wnpDIkd+Sf;PW3Q>E~wX%p+|tkSJwa3LC$WwD}yA)rWr zGsh5$?*d}NmjM6~KA>~(Cd1VLGe>#k08a7r{~n`TtvB zcGFFFqK%7u^;2sb8vs>jOY4jTr;=oGim!1?=|f6hC$MuTaS9To3I%}+U=)l2E^(>_ zZBs!2breUQX)7B<#B0Jiwn(R4M3;cj-U8<6b3=Y^JM4YEKeGah8`L6_@*AX$FSQ@~ z4SrPIl`ZTpoS&FIa#G`>tPd;xHP#YBLUCtzgUbm+A*qbyP>=-gDRms*bpcdbtc9oo zhO<*tD=YgXp(sMw>$18gXYr6qV1$Ih2}Lmuc5TNb7QFJhF4V`F#o<1SE4_w#s+OlS zDT?Hx+`cml;I>|LRgD0vKvchhYA=oz0#Dmy^H*!4;>pieiYI3TF~E z8E!kYamdC}mFbqoI{vn%QHw;;hDSVjYB(Vplx3cb#6qzV1VIo82PGMm8D*;3paQ}R zC*-@Y=^iiMUbyYGLu-nzt2uyp?h6X@z+4}6P(Uk(zh!39irc16IS5L0N(P5-_zJjN zzwG$094Hj!RjB1|!nT(bxnf`o5?6OKXnLCzq8`M!@s?mVSN7=lK8k)IBH4}ZweKW3 z&NNRs(rAJa4?=;%x5Y(?bZBfb7=5>`jq6AXN00Io?KIwjCjU@S|n-}aSF-JXX zeBn;WA4$dEu`Z`M4{h_AI_Ox_b*mg+Swa*+>sj3?Ti{RD%V%&snZ@4M^l>GQXL;ehD@|{SJt=`ol!$-GMd?+ny^|l4j~$p zZLN}{VARJjiFu{!OR9GR@z#GwQ zkbVxe#9`KenL`}J+#oXKI*trC5FFpAbo0*!MXabzX$OB=&q-CZ|% ziLJJ8Ji3dO5~@Z*N3HD{vJ;rvxan-u*jcUf8yybSaVqAfTZrbXHecajQ8hYzzLGOP z2lT7*(4$suNe=;kW6z>`5cB1+kB|DW_8Y> zuyjCA9WiR4n&p~4@F+DpBj93NMF<10e-);16ty*PYU6N8)mOMOGpJc0fQPaazoItz zs0=Phx;n>6W}z4;B0veDbZ=(xf(l(&13E%lnf*ei6j0SFDE`29w$HGNVfGS+0XYTisr;P^|!&NdA|q&QLZ0g3(t6()|54 z%xG7t(3XE~#?#ZfUf(5LZXrtut+?9*kb&(lh6M;iYPa&DU|Lj80rmiJAsUo@HHwB} zA_!s-5#v@jD4~`Wzz_w9sWN{#oA~rc#{P!{Z}Zmvhsd<{CNDO;<1~I{rCAzj$-ZfA z)JtcFIXlR1YleEQD&cx8Wckmw<{Hy)yR$WIKsI{ZQQRx4XSsz^jgkmV%WY$1%Go1T zCFu$jLyTTKVG!*Y<zU+t zCXr}tAb$QBW1Z}77Nw0mvCGfLU2U-d-k^?D-W7C9_BGM{07cVTh$Rvr#sX~+Ib#Zn zvIMBG0Bp)}C5@YL%O|z^?}0UcIO`hD&lq#Mu83Iq)~ZoZeGgri28CF0ypCscbJ%v@ zbpm~MG*wS(9d(8h%&#qI7+|iYFDb|Y=g71`c0={uOPU4WiDVx&?x7+%DtBAeLw;UY zJ#PLtHt6GwjCTTuJbJE7m;s9^ZS=05yc3A4nA*xal?|Aq3>^+UCKQ{)FoP-2R17g} zwXvw(?2ESdCciBwxup#HH?-}`)wdT5olTLzAsUojrVfl?qC`L@QAX7p1VScUqQC{k z?;o|@A?S}xymfYas&{(tzV>!Vr|D0Eqi3JmQ=bh^e9Bt7X;_vQHUpL;6!brZsXQ>D z=DXqWOT{j2l%j@+2k(+ns>!3B>=jWu!x1UG_^x=gXo?{r^a zt%+T+R+U7N zv#Nwt93dK%b*+wuVj={9Bf4r`{ibSS+k~wO)*=8igl~O|p7;wDkzcmrdFnRs4E15y zF(pH=-5}~o#5)3A)q5fj%U5%b)UieLLG8g{>R%TUSThIJ=4GfrU#3PDS8iw@cny^) zTS_}-80BPwWamQ}A_;`gID!MYK=3(ukEt0tm|^#waqoVA?1@ejG&eKX!|&BB6f)#z zE3aeDUOn~G7HHf zdGQiz>b|T%rPj9qXU68EOwHO-~T2X63R zoDAqN0B>&ak^(>GQ~tki^gNU)g?j+Hus&m15b4($cGEpz)JPxsM`!Kgh(YI0FATd zYaDB)^=~-$Z`f&C&BLGRS)Z3>e3!d$G*60MkW+@V2@?I9_u4n#t(D(3KylA24eh?Y z9Y%XQuVxEqZzlI;7+UIW01=Ll)(1XG`~Fj+Le6Rf+)X*^LTbQr<((zA*n^ymife^z zJakV(rKk6ci9VjxT9UlnIHiy*lGdF0xWwis1KAVmtm zQ=OADp2h@&_a5*BT`{Ic5rCQAqO@au)LkXyBmfoz9&8I0?(ub9gsb^#?A67-ocCBX z=7$T%wAK@C;kvnoj93OLVLY9k#l@@{q_Bb&w#=&Zy{;Kf)6rdK(0f(FSk`nz^4bT| zWe23|TVVtO7K34tMZxO}B6oKi>1)i^qR5$p4aUkfo%)dqg1UoXuu8~AcY=c`lF_}2 zA=A6N^EA_RrX@-JvUB!nr1=WO1jRa?8<4!ASjgH;p-+9xs$$c^VLdKy$~){hAsUp0 zj+DnlFpy+WAlkI8+X}FOU_}7f=drgnyh2Ht zsx7ihtNzsRI~Q-&L7SUrs_M$jRw{Ytl z*ma`Zv5-i7+5j@M1FqR5r7Hc;qy~S{?HUb=!uPp?m}nL(uY~>KpuQ~F{DrwMEKW?w!R$g39P;r z%rv;zn-JG%i#3X26J$VC1!&HX>mj>{~6}g zlur(YFBm@P6JJ{xjCJ$;|~E@xlAnBJYxN&ZLeRLkQ(JxW86 zdUk<(`3=*f2qJ9aDCVL^O1AIPrEb^1mt~a8-hQZza&-fyv`l=jwiXgnlY{MoNdRA+ zr6|w9V6}y}{sYoUlZ#GqujFoqN zfN&uilzpxW&O(sHKql-e&S(m?Sb+cpaQv|k?d=CgMiXxjiW?2_I3mVMk8idfoTPV7 z!S483%+Ph%%CwF)vO^PV1W?+G71Kh{izNcct`Kj0w3I|hX6bCK@Sy1z0G#*)_L0h{ z@PvA6#}pD4%BQk{%^V0p za3mQxJ^T3KO8I*)@<^-b00i^1MuA$Fs&Q*a0yUo>IGUKHO};CFym+lW=lMSctwdD6 zfCOE!q=X{?l}QG{swoYK!yz!IYu=q9MDa#z5nUb84Y!6C>QyB-6piPZ z6zQKpp4nz*r^i^fGX~+4jj}`_T+}q6NC$&YB(t>C&r1X7u2EGY$Q-MrdsZh$m4GI_ zpvZtPh$z{8D$``Bmo1dM>j053T`6uTG$|=0d}#>|_zIN;RPCH9Zh(0}hF&n1vKD@Z zeV4tq<4EJ$HT6!s5fP$Fr8^rKY&TII5_i%0wEuZtf5dAVOn$pu?5%@hSa1N~AsUoz zt&W9apvb}?8z9S5vns}E1jrTvh0$~KF5t6`=(?|icJ~qH+k>55jhv@O>#eV3^4HsP zCsw?s^>OC~Ww<`#c!zvv6QwX#FJFS=&wwN%;QU~SpAU!gNYUBTE8@)Ml7Ozg1w_P< zIi!q9EzYWxbQrB1+1UD=Xn~w*a}=Ejt0v4uK1w!&h6(w`!XXcM26o;+zhKkwQ9HO| zBENxG$52}-U8BOXLmWofLYYc0!~VufbbbwXMt(KH7^-6(2FOAQfJ#ul*i*2w!~hxt zAQb-V+ecxx<@*)8i^(tguNg+yeEfR(uexSSF!MU{Z1~efnr(+QN0?5W73r>qwb`;B zZTs0(UjI;=X?_|LKEB_k9rt9^6y<`%^)=)ItS&RfQlj4HG?qCVPhf@hIxMbJPM!6w z%`RpEYesDorF$;0Kpqe>4Qo$Ik`mgC|6&PBdCqeTETw$>nUo~^WaPW8OpP^s2 z&Vo9prU>htx}|7aHr0g5!B4p0AsUo*nvTRmu+bu55JHv8v6o8(K!5_3!`Br6UGFH<_Z+G62VICx@#o|Vrqc+7IT4ACI&u#onP8;{7ex@WM* zt56kK#$IElk?yCmH)CAfcy%55o2$R@SP*k5YYTeCD{SUazL@=$6i4Zaecx2RkGQoT1=V<&~b-gVD{nHrdO*(h-95jSa2_SI^{ zwh|ylQ&BKBDiBNp0Zoow%R&W|fM`T%?d}TUW6rt!5a@j~~P(HNBunj$?;^xSQiskq9^(Nx?U9JQG^1T)M@ zavT9b5E)k&PR#P2W5qJO=Q;7Vv6&WfYPd}B}1ijLF z15$#IgU`=_uoQU#6_yS$r|uXu;jJ^_%W5h0qF-`r+!I#W+Egr*z5@p#8kBvlh=ziq z1i&J)C%<#d%}v3KI7Ae3idT zap~&=zT^pvWzA6w|KY(*_ZG-( zFyc-|k|-ur02tmxJ;SJsd7}@e;i0|IMBx_eCtZa#7+vv0CA6Qk9~Wq(mf&^CM+H`W z_h&3}a%FhKzWoZxwC@T_T-`<6-c_7CntwU)*J+6QYZygCK)_IJBoLegtA)2prtlY( z)=2<~fZ#-w>VkiadKS z2U5Io;{X5wnf&fPwvb%2p!U_Qn}+dBiGyU)kWTuQoV*59*N$>#eqey@R=hbKOuviX zgq3@&3zyY=*VuJ^bzKS0iXl+J+9Ar20#um1gwqf1J0eVV^a<;0Nh!Y3!4hQJ@~^>2 z_rRu7MW%&SDHs7Y_Z7^dKyV=%l!dLA0Ynl&NLhzG%8FUCXdz?)3;W1tj2s+Ob7;Qm zW;CFQ+y~w(jo$i_4P~1@D&+d_e**GIS5$rN5X@jUsgP4JaE-(5;kVJrcm!AI zT8{}TS*{L!An)FLh7FjQg?XE~%Nw{ARkxxM_bq`kP309iKM8>-JYu^)bv29wP6&V2JQoj8QXe#IN!f%|L9h1$^^K~+7bqeOkG9X z0Oq{{k}3`7I7+LDO80eJn!`>ERbsYQNHo6+Y^LV3b=9vlDb$EPWS;Mad%NxR-j?|H zZ|kedxTgE4P1fBIo1jW+a=2~|0BN}YMR*($mPpeHW)a;lyXH8uNu`v2e7vlX)vF41zOG zQ&b*}2U1u8vq>-<3pgFq%I3~RR@$;&#j)J`HFbLlxxrjlahiaG03hkzlrS*{S7TMP zAsVlAGBs_WOII9|)F^Uh@DAcjHgQ9!l%v-_cExDce|WHjyW=bv#|H~m#F5|Ukbn$g zITM(T%488nFee#@^0)l!!@oT}>jzs~1o6qNM${N~R7jizs?xISYz|j#mP$%O$Ra&b z4sQ1l?AioXI?9*2j;TW41xrD2gO>>Yl(9}sNt1|H5eSP`l|r0Yq-1#7*^3UCMo!JZ z>E^%5u8JMce{;=zXYu)#@YPPJ7KKZ#S|Y8HVO4zEl~kldvrrv=PpMVIGrRV3?g8OF z2Im?M_`ie6x*7co9c+(O4Dm3b-AK|@**=Nz0)g^QS3fsVgsY#x6H_2x5|~o)V72>b z$+TFS34lHi9}G}3U<4#i1}!Vqg^R{8% zptJk#hHU;*Iq5Fz&HerTElxuluDWdZQTAKcXyo95>I{gd5uSD$Mb>s0f=C*I^^<(- z^!e-fH-^-8N5REt-{NDjVYk{I7UP1U)pS5gf+7L#bqq?jAC`S9x%yt@Z@uRmHJsD~ zb!5nrhjkdSIm6Q*dWK`PCk_c8rgttm(4qjQtifwOM3{l7j?l9vHiIpQk;9E$u&k#n z4489Aovwlv#~fDzMw*79u+U5-86X7WmQK-G$WczP)e8g>AU%HTM{WEa{%9EsbZ$O9 z=)@~hsr7q3NgLJrC1Q!A`u>SAbAFqk*n^mS&$ieWAZ$23H{^A{-1BOe0^>T`_uB(-zZBhz1{4 zLrb3xu~fX~+9)%bnf6lS{D5XxJVZM&S1}v?vpU0sYzI0?9%>(V)WKkBXen(tF@mmI zK=KuESOW(k8kChSlLBEPguo*!q8#Sewt;A7fEEzZ^N+;HeGPW^+t&2C2kyb{PnxG5 zo}tgNkV?J!HNIHdQHHXP>-t{6B?J);HJV0DRgz^<^R#PD%vqI}sLm|I!$tYrsI5u{ zy9E|oT-~*-JRT9`1pLvM$9nh;E#{o@1N%(54#Wj4N6;EUmi_geG6jqtjTvgo;j3XI z1%tY^I7KTem(MdzE%R)NwCi-tw3d}?IZOJ^GOAGbUO4*MTyjcNZxD*YKAeSup%_RZ zKnSv|b6L$^Ybqt>qRbad5di8(cgnMWeZ*x#LVLb}1zKgD`i4;D5kXy4vj`&fL>yS6 z>}dVoS2R3iXJiYlf9nXKdTzFqZKcSiYUniFYo8{=T#1n_YGG)O@PN2TRD%35R8Y+E zbWWBCEVIUbNF)E^MaGp!K{>ed05?#y8E>k}mVrq!AdM60W&r)n989QH6gQw|6Qf3%S_qgC88kAk0lE^`^P@*6jgB8t23d~}E-sqt^@quzBkkq1_rln`>^I`d}{FU_NkRoY}cp4pogcAUpWyxLCu6DSkO4Jpq0bx^X z4!4}7fqS*)W(IfgIhSjWyat2lWadCjd~2lXl%{LgF92DeSqg?hr5l&Tx5X_1@p>9mq@Uz>JB zw}+&+0PDDGZLdV?Gd|gJJ|)gLovkU1&$3dCDHwHzy`8VPC!m|B{)ZT2h}ZN1d6yP& z?+3>M+=Oam;tlHEv!Wjs2_ZoO=E5k>sZM|ye#aR!ma~PHwiO~FVOJF7T{--keAh*h ziaUUbw80suHVGg^0w@`q!(~f1sd%jb0z}jpcXcW|i4Qoh4 z1u&r8go)Hu=J>q0)dRD_v-dQ=PzPO$sHmj z4}P6aXA-G@%oS#GvpYLt(#_ z-RWRJB(?TKPlSw?UHPdZ_6!;&Wq8LL(LbXhLynM0MoQlz1onIjj zAm$`2tQc!CHJnux!BO9%xY~B?kHYKlVXE@MZnY@^*0mhE*m)3TiL`S8l-Oj1>%1i> zi%0WqkZ>Uyly$0x!yz${>=HN(`SS7~V-qx$Apt4{g5ke0(@{B!<&C)2f`AW+v6jn*Wi*a zAN8$}-?~$~>$Tp+{Uv4f?%$~wKTX~&r9)hhZ|}dmC_Vdie$Hw!4=#4k^h_f@e;7Ws<&e< zL8cqgo=TkZP8U4ZEVqg&GR`VfJ$8dbh7+(U3^W(`3o)1=|2-L+r~RaN%3T_OocC_- zjhpE8DXC8T=`A;Kw<+a<^zng+b}wl|X+&egox7Wde8Ro`#$HRTG@ts<%ns&eNkdoi z&%cKQ;b~zY6iY~HP0`A`>q7E{uPD!aDGS(*xbwiT$va-6CYH`Yu|(h{QoG>WnSv@* zf&>DC{T3DNg1&+Gyusv>?7$S3J1Q`0J(Wmcd!HZk8hj|Yz*f`aiqi*Mcw@BQ(ZU#Q zvO}2#= zROv7@Lb?%@m)iG`74%PR6{O5i=2yr!Ae2v*fyn!S*brIK%Gt#xa5z9T@BM$rp8uMY z`*nqE@k@r)66dixF4Zi!|95Garlep8AsUojmXydtFi?VE6Si0!&=pf$=pg{Gg4^3< z4@C0HRzd0PyT#|X)&9fySZmd1b-nuyi_x!%!oxv68ZAo2O9xVzi`|;XNnI`uSjs63 zsXUcKJcMROL@v&9Wi0U{>3R;k=3C|WvypL|xr!a(LxYkaKf zke2Ph#)3(s$S~eN)KGzfY6G2L%(?wMc`o(iit9Q*HYj79tm2EZVl~-slcRMXzPP&9 zvgIt;nWa`mV4N0KE*Pg_4NgWe5G)i3oCL03HB$MZ;H!ZU0>FBHFK*5q<^E@|)o%Oo zkRMHAUgxd}u^60KEnt!}NK`7y32TI@H*R}{>#y1q4NLz9(3SS|llcTJJ|obyI-qb3 z-jmGR>;H2g^?WW4a?+j}WFeuHZA*f#nW9g7w1Crzb=vnbwdAIOJ5;N_CITFdO=$l< z-a$=t3PbC9%@#m{J(LcvFjTR8|2|Ft4hwa~?elk1B(~C6$nC0BvDLaRu>%Jo8kBXh zjFA$6f@P~I+#@Uzmk0o5HF+j{Cx%3Nw!Nx4O9^dkHt$<8MxFfehvAzdY+#X}k_-mn z>6ZV6x!>Ye6rK54HyGA)H6TmC6Z5}n@In>_Ws06Ec#kajYBP)imJSD|5aOM{J^6c{x1)bM)`fHbzL&P}wnP;A>tok? zSQWe)88D93TgN^&ghPMdSDIwbnANhZ?X!_Vqr#gaQ#q#=9G z;;aIJdyS%+DAeM}RhLxMmvGZHErn?@vZQ|kEn$}BzY$)@9tat^Qb7k4ZnjDr&QxrJJX;#JdFsoRbb=BIz`Yu;1a(F* zaZ6LJrhXK2T&it1pXXEgFik0t zSunx(SeK0sSDrGwULfgp9_gskm4z<*{%;+-`Z#z0r?js$B0E@WhpkD)xPOi;YJyWS zV7uXO*xdbdMWBspSb8JZC~zSfl!Y;lh+&}!pf*%h#`dhb)zca&VE_|j`$&U4#`RnE)k940H@c`MpRpx#8_No$w!oDo#VF0&9Aq(!{IXRS?e7FD(r=wW zJ70Pug7ky|<+Qib!UB0aA%+v~_G;QamRBe?$il>mr&p58IUXSPvQCsl2sNBkse?we zNF@jiB9AUm-z*(rE@%{HDmoKD>yTP~kVRTle3c17kWVQOMq$mJ5OPuLZ4HnZ)~pN@ z6oXvhr1r1@7fWZNgn%P;TryL^v+MvaR; zt<83Qp!T6!sP~PQ?LjM5^9qJ=%0!Krg_v}D6_(JUsFdfjV@kGW zcb`vqIiEptv~B<|r=Opul& z-py52f6=l3Lpc61y{~KNoF=_*`J%-#sD=34)xxn0csG<#%G%vDa!|(UsnRu=s#_vN z*|oM+LdY2(T;)z~CJm(BbL1@~15{}NUn27~qI9jr(}pB86~bl-jvh45#90G4%F!Qs;7x!y+eGCQINxaeRLB~M z1_cbUDH8`F8kA+Viiu&Mh~PIl&||Y|(U!GLy0KsgIqdy`MyqKyFS+t}vbx!}r9gB% z#DWd+NoFV_%qfwoB`vWk@garF9i9YVjL|12NS?=&vA1}TQyOGFH=d;}6>NEw_!K#ZqxJwP?86-Gs(LMws$o?u~{ zs<1{#?rIL2n(aI4?m*=&7nf<~*M|GK`a1pnKFypDsD!qqMgj#|#sN-=MlC%RPGun& zgh3z(r#G$LS0{@q&E}hXXDxPB%$v!ZDACOk))`tn0CH`v_n{sM+UId!_@7P2b}$|~S3YNew~x`5JFQ*k6}oup=QYz`HWTJ$i? zhj(etWFKB@F(u=cYjbha{^KJ)&-^G#tw=1bu0ch?b#(a$AsUormXgjxh=@QjRBE>H zDhomsEFeG|U$p*feVFjJ+oN7@8!t}%Wb0aw@qViv!Rnj0ja+EOGW$?G0r{7mipYR3ssbG$R1gvv9vZ1ud>s$owkVid!hN{0!~nPU0-2w&jv zrXr;@_v>1@zi5Fd^h;mTfYh%fx}I9gLyYhfohq%jFtOdLyLPZ1)` z&#G0Qwc3^6c$5pnZcAT0`q-v<;`7>@sz8 z-9O_UbvN%H$L8C zgT2;5@&h{hj4W4^B7y0O>77LYE0M24Rms4xyRS{I|NLH+G)o%i^Qa=CcdU0VdYYb# z@aP9bT|M>TU|vG*b&H+Z%$Yd4PjJQ=4dqfsAyvABoiIQNm%ESNCMf4lE`0<5iKd|$ z2sH`}j3NO%H4&V%#46?HsR#lT8y(F-B4zZkxWA+QEmwW*Pgd^Hj(m_1uyOGweW;`m zGm`{o{>`mQovi@T%5r*w*jCsUw1Eobwbs>ZcaFhyA!8(9R+l*2iP*M~HiH6FGH4pV zf#^T)Q{r3;f}d=o=IV&nRjIr`*UZg%=WZ&v&^x@v{fvqfND`NFp1j-RCmK}e(s?oa zRB)6WujvN!p*8o`RlE#Do~7$%FnSPP_5$LvTyP;8lx?aC#Yd3DKp|Chs^@D}jIgo* z0foQV>@F)bohiEHE(HqWsy;&R*&{sf2R_BL{-oyV&}KUCyL|37-v}X0)!VJWTzalL_$c3;I$tta#@hG&vTIs=U>% z%Ac&tbTx?im`pURt&cYK$gvz2>WcQb5pJ zZv!s6pP1Rdn?F*-kz3O$y5|fst9`Bhb^%)SAv$S9P!tn_qGcVxD7PI`IKs@Q+m%LL zFSNRK%hx#-$s^sMnpb)9lA(D-ZR_ei#?STs9d&Btcf(Wl$$ZH1$1U=u)+pW*XVY?` z_WyI;|0rg_YA$22>Z1J1#zsHY4|%@X;CdR{>0D@YPPPjAixl9Xp8>VSP^2wQqhwGS z0bnTO1-wgHn}!evAsUo*F_1%Hp~#{T8C>e=g?PHOVR0!c1%QRSm35yx@4c>j-7k5s zw@twLI#ycV0>6sW?flm>Z1L!Pak#W*hhzhK3y+YmD%5v%@a9C)^7nHcLZWPr>j1{%@T&)+w+Bj&btIo{PAk&tOskpx98 zn2ZvDj>@`+ssN0+QA8G4c*tuNcM?_^&QpY$VPE9yoF*@pY;+HknU_iHuekkO@y745 zbKOqLAYFDH)x1mmQSfRkV#1+oTRa|ga_v?uE4F?ZRV6c=QP8=90RBNTf&Z`8yZ1-q z?0Xyb?I8TYB*nWCzXr3EjU<>8gc6+bGxR9qj}p%3F$@0{ZdtjeJq-E1%;5mK?B?;M^lAL)xUw%;uss_vK^3u?t;IMgz_l$!$%pp2*+1X>#8 zpqZfS34no;0=MW2OJPuQB`l?govsaaIn+!vfKOtelkg^-8CSM1qmB=@VhWgD?Ut9W zOx9_I+={^|z#X71pzs(rDM;Cpw}_HLu1Xq;;+z6(z~4|9^8+euh{;qkL9GUa&RKNL z4GLvJUPTQ&JnM~AMV5{aTiZzp@`rxTs&<}f>nxGAOYDZXqI*rkzv@)MCr77%*Y@kBo1SSo4Rf{jy)fKR=<5YFvqc&=;?zC$_$2rVka7hos zPA!6x3qWHNNVX0D03otLn!Zh_L2JpBzzKfZXxa+S{mysoqO|v3g{`_KdP!9#^Rn#F4T%`X*_g2Kz+QqeOY`)e#;<1O z&_ut*;ax_rMa!l0yi-{~B@LHN9`8h6jCnu-r=3CC;v1%eJfWyMZ2KA!7eO3ba zjrh&zzlJ%$|8#=b#;nP*n92=UJejYm#2jk+3MbaH)T5UIp%8g~p{4;CPM~n!bo#EV z5RMvCWw`Fr1~7ua7=|MnIw3lQ1EV>>n`VafWyp>;W+x%qipXVZU3}A7YdH9@R@Cdb z-oe{71Zd3w-%M?l?6ZkX_WD=I`!$-ScBs=qdJl=>K;ubC{)Ro;%*{Y)-fVbdTu3^G zUmcns&26+(b6;{T#Az~JEN3Z4b1>!@;1N}qneu}eXa;4E4Q5zhv>YsOOQG@Y#0vLl0W*rBLdNZ8y!eS@kZut z>#0iTm^^WGzdC?|k>yosL3JZE`FHGWZMX9%U_R9)EyyDGa_5(-UFMZqD1Ai>zY6-~ zf(L1VlEhXQ9f&##IUls&}UtJID$M;FQjIfR!QM-AD`H?S` zAuz185lqd~ABB}xxC^;|FkM6O0%qGGF<+9OU>wM4;fZ@}4@mf-`6~y|Gz>&&Ax~wE z4o}uRu+OT>UEE#Z!(Ln-*`I}Di;6sxqqM<7=F_nVEfo;mVeYlt5P4SYvo6CTy7thV z$b#S!$WB|IZ}U={kOq_o4x}ohG4RO{MkR(B?<%|!A%`P)yb9L%Jb0|u(N_lZAwWFB zUQ{xWoE@dFJW3~ZpmPrfp^W-vn&SyZ$~}KYQq&b1B_d(9qP_AIbQ!Wpp~uPr=WulN z5rA|rz*IylgWm?Q{FALoO-rohyNq{xzi7+bePC`G(>M^Z*iSe8bAmH>#~6>$C>uD5 zV5Ia;lizNn0nCO-TvUruHufS7ef!)IKp^sEfm;f{Zr&RKoEyCKZSgA@uNSy%rd#v% zlTHf}bXS|W(K2dDh2BNYO4x%Hr^B5lXE+WARQ)-Y+<|DFoF7JG!Bs-g)} zh0`4Q{V0+1w?w@IRpGW&|3;q6`YP#KhvZO)s#y`;Zrj;%bisd&gSe58%tp1yH$-G-Ym^0792h8`YA+kR;6u_J$hKSmYJ|mI;G+Cl zG?f1%H_gh-9^aFl6!j6x-YVEwv&A5^Ew*G7>RxS31+z;Kn1g;&IHsGr5c48ri$)&E zEcai~s2f$0hKC`<e0`6M3KG6+}OQ#V=v9xoO)iiM)wM^EG~$kZ51}oZ@R^x zkzlH@;I-@e=t9kmTx8O%@r4^!0(Bxpa!2FY@(!+$ck_n;mhEF+>Gd!x$V&JRCMG~A z#3J^{ojFa{LnG|OAUqJ#)r8u)E{RlERO9ENFsCFcyXJM{x1Is?W$Fw z;%9zoaR4UxaF|D?c92?GX<+|lpa!UfdIxt%W}F!E=%(L!sm+SV8lxT^N#A5l>(BO4 zjuvmZfw@KBs1(N@8i;L-QTDuawDLz^E%bz6oZNT_U^zY0ZPq6$DYvdF^vy(V{kI0e z-siCf+YwEhVXSb?{{=T~YG-1-i!pSF(lv_6RwtF4xt{jYg12^GHA#(9?tGeNY=4$Q zUe8)}vXN)4%a_dZl+%mtBu&qnn?(#~Y{wo=f;|6iZtr}Q42X$3$azphIfMzJ=k6HJ zWzCSeclH@tY!aHAnM{5r$VlJSyFq0bd588DqOymD>^~+6i}q{~@1Kq2H*@tfo~CtV z`W~Ubi?U+>XIC3mi^sDHV$WJi7v64z$(`xQeG4I1QSE3rMNb`u4ehpC^)I5!dYIIY zencaf#<+$DVES~JkH!_d#*GI!`z1R)-k!|GMc|u$`pk=n#T3yZD@re%PW;lQjQD?Z zj-}c-g6}{D)G(X?fdvq{;O{hNJ>VP?z1@-I0)Zd^JR2v;S##v3;6x5wa#m>iR)PS zsSbq1SNiK(KMaLjOiIMoQrZ+t7uy~~r{Ky8B@8E{6{d3#YtnmopI3=jzNqhkx4^$4 zYHJ_lxIc=`tXRwsOF=U8osz|DkIZ7kIc-#r5y`u^2khmZ;VC)0t~rBU5om1E_KsW| z+)F6^3#E%N~ zL}z8nN~R(TZM9Dw`L1H!V-o#jN0-_~Gvd7M^IehqF-zK`-_&cKfMvKnHnBQPceeYG zjmv7ht|*SzV=(@ucm#Y15j>pYWB;30_>L>`O0-mUo}hz6 z1v6;(flZwcD`JgW9ns{ssW5Owa%1c zOD(pR6v$DLz_y8=VnIykSflT-%h>vJXc$bmX(_Daf;*c6T3%ez2h;-vyu81~!|J>k zUJMDJ*f@!8zUl=i6?8-AfS_AhS@)R`@-9SS$VC*B+`x4Ub6O(wAaoclVD7QfQK z-N=orDUS!&L2M)O|90eW>KvsM=D-rv#0D>i{E{6C=!Gd=IV>%Bhl$&8Pq%c8yG15I zl!io=u7=Gvx2Ch26ih;u|E;MT!DRr5j<(D2*Gf800OJMg^n-~Pz|7E7MpBrXCEloN z2FtyT7g1#(10NFPhOekCor;J&m+CF!kH@!|@-cHejbNzMj_DzIouTmt{8ZGCDrH zKP5X4_Ro8Kkqgcq?-Kx`VSt5MYwZT+6H~$j5Mp*_axJB!cizEadmGE8(75NDslu?z9Qti#DGLf7H zQvRkx4uzQab8JOaQmvDy2xDF3sN-JOd&F!SO8aLK=(${8o_Zf&$#44w{`0)UC2HuR z6dx8dbK+5X3WAz#;W@?Cx!Vrp&zd0}_bM@Y1CO|*qIR;d?7JFFVEPQBx8j7V`{WUj z?dfTCIanLRHaOh!07tzCTqXk+w^ci1#)GOtZ4~+l8pee0v4#KlEC+)&==tU_J|nd) z+RbG{n1}AgMTH$A@dO+(BFWrzKNyqLLPw7VNfNE_!=Yda)SX`b4;k6lkp;D?6la-m$28AEstMvBI4e3y zLk9J3^p4|-8!Jx0?pZB`O3yOsA0*TJJcfbm&)^5;FKgfx0*P?$P2EVBSI>f75j%rn z(+L9shlpHiwP*#B2=rll%4q&LyG5p$=uc%jZ58Cs#hMwg>CI(H8Q$L3$;ZLnH=o4G zJ(?;T+;}06uoAiJO82)|CHSzBw7EBS=au=ZT>lRO)D#ZoMPXPWU zw_djgm*sy^vcJg7q&Z#pUkVIDON|vT!E+sNQu|Uh+_Sn>I2i573s7hoO~LpQtOk}= z{isKBGaQ&z%^#BX30=TVM%l{s&X&y`GzkM~h+!I3*j9JM%^#O7n!2H}$a4R;(a@RFU?lEHEt55Ysr=V6r9MQ8r1+CcDH&oU+SOPMd^2g{hZ)0*qPDM)cPf2|UH7x<1J|jpvRsmX*S6 z%J@0Q6DBFdYgeAOHRsHYf&4Q1;~7KD(1G0)`nQ%Cfb-f~no(YP=C`dM!sSpImzXP5 zk(q#dr+i%h!$8FSocr%vNr=U%dL zjnLZ;{K)#1r#>D-DD_Qo=cMS==7a@GJ{M;TO5_Ul+uEM21zQ09O^V)iwYNMbefU#_ zsJ}6<3*>o#ht|D~4?2|!fQDdEo%_O1$ZN~bJAFJ9IH*9dCyCzNFTO6`B$6#0RTV%7 zzkaIVrcu~(XNL^5cRg;{sso6s98`MaBe5)aS;NMvp=ffk;ih|)ywXL*H z?CeQf`&@v^{}b+ix^u2M=YvvO_dytU4_ExMY!eDatPoN=lElos{?SQsqcZeD@k;Yg z14kL!$fz*9U42&8S@!$4ch4Soe%g6=&zDLTqMf`&p8SP}@B>bM_}j+pZ5iD(c)}Gk z?z41gnv#y*ycp)XYYCz*6#*vnvv|&DmR7g@?R&2I-d{u@;oJl*!J-yu#C-r$YPFlf z$BaK40$wa}vU&1oZrfn1cl@hM#-QNvjh~5UK5p~u6>G8wPE-`OEB(Ps`G~;GN@Xi^ zF|7vWdigB7PKsb=tfF2utZj0y3!XiCZr)kY51B%^aLO{IJ=cT(cao86{d3nvHK8W; zHpL@uy+kn*XoCDielL5Tee;g9CVn+u_Gzy&9 z8TFR}Wwf;8-51}N%r*Gtq69q3xsjG^u=B~w$H+@CzI@Jvrmw#<>X$7=erNF1d`%StWjfTMall1_WjtK z)w+gQpzIR!rg$+;sCBl1u3ACzL^L;p75-GX<%w&yXNlecTZmyY zzxeE^7xB)ebG86B&z^%Z>hHC=wy_)hC)!Hm4ReS6mIA$BW)~)1@l3xN`qTW2fTnoQ zco*n)VbZnHm4aTEqB?5Cp0&$)n;|*(SbK0NpkJ7w1*y-_(pt>aZZe~#=Tlx7dQJ&y z@pY|`ks+=>En{bQ2gszAw-3qZU=H?QU^6N4a2Qjtsj`ooOMtf8@mK_pcKRug>IRbg z=?F(Q3C}C_p~H`RAiAY0V5im+*gk6(BE(BW{9oQvIOr=MLhlPj+3HOxyI+}>D>DRC zaggbfuvyJyoonV;6P@sKt;lPr?AN^ zGnd4ubB8GSNLC4e?Pib!d;t|kX!ufLb+&iHY_!WR&ml7y;bPze0Tsar8o-lK2@@Mc zy`+l%DL;;rrRxC7HCy-J)Oj{GbN6G(21fGENw&+1N-yhW*o$(umlVM3l=ru?;SJ>6 zIZ!wyCi@hpaZG#bP&ehq>Yc}#`Y z$*hoyXCVIn48*LjbbLdvaJW(ud;yn zg=9n@mtjNRY`CdMV)`>Q`=DpIo~pJ>A66$1^U@kV7$1DQ>2V908TURF%P;!R z&D5`oFj8AUL}pQknGUcUW^`koA;%7y;lO2o$bZ9(`#goWHLAgZGm+fWeFGfOQDe{E zQ05F<2MrlY+=jm$e%1fXU*e0>88L}s+=IPTKSC0^gEU-nh2CX+*t2Q9o-@u6730GW z0#oS}=KKt~Z9rf`5~^>SdZQJ@pp&%77pt54{S3DTGC{39SfHrN+{)Baj7DS{wp%Lg zNW=~9;rMy#H-%0W`qS&@tUJ-gS3})u8@&k5oP|}_XtjmnQPE)gaHs^Q~TzADXjk<-=uSQj ziGwN(Jxur=#n6-3;M$61-FW#mW>_90swKdbm;NdqEe+6VGM?j8i2Ci6-lEUU$Mkwu zs20<3DcQ;Dl;kdQ4{86M?Y+iS?JBDg^hkK9Vmz0!rnb8`wp)OIGm2+_QIG<@Q;p~O zq`&&=qId1$_U@)qenISd*%M*J{lMzAM3fPH$Z!$pZ2kknaGX*-a&)W7O9M*rK*8Rn zi_{G�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¯Njg>dpdk% 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¨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

J~!wWc(f@p)~%^p!XfNj75w%`;!g(5^$1LlUM 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 z z+ocvZkRQ^N$*;=7+2nfP8p9mt4|W7LVx$5{`|^KnJ+ox%K|>O<4cTAwqax4DLBu#{ zfNh@KMLQ#HQPMl*Ji?pqQGB*@N6zA2@G31NOWf8{P$&z&2ct|{s`G5Z8a6qKsGK?U zgyAThd>8z@`NS7S6v zJqrPuMQkOdUc_j@dzJkhH=cjTtf%dJ@qsc@uW!L8KXGc;>a3x7Wi=my9YvXe!_!S} zdg!B&2&a>uUCn@VDrM2PP5KS9YHI>VK8eiq6-Q6_N_J$Ivk^qQqVEF|nqkc)yt=1_ ze9BBaTIkkqR4lf4kg1_ZdrM4oS)3xhX)Gac&Z5-`7r#v()<-fTa*)7!rR`O1Ki0TE z;8R9Si6>9sv!juYL7tPEmLn~QSiXjC@dxe}2XsEzM`Dc9WLj!##g?;ZiT_aT?WA)E zPpns}qgMyUV{So3yhctaWyLBOuwOwfD3A?zTD4ZnZXV17pn#Vq4G2x_ikh=Rw8iE| zgy)i$Hg=W>lbt($)mWx8CtQ6qE9e6#S_fLVv`~xeAgTW1;RZJ1A`m_QoMI=Q2z0s0 zUNz#J$xwxIj#2Wu)3Kk+z^CR-X%%ZQx^)5!}Xl&8}6nu-dq1b0wyO#Wzn zF^Gl;Jj*?*Vc>6$~wu3R55#92vQ;GKN6xFPh zQ5W3*SnRZGrH!_0ZuxjE+tihW*_oCseA(GPNrJUHzwS~)N8#45wJ8k+*ZH}DfuH`e zwarpYfW~fkueaGI4t8K&@iZkHcw?-%S8?1)2^lbKPt8m;s&Il+Ign?MOWa@66uAgI!jEv>mR-ZsFOIFG}3k)$KPp9t4gnm<=1%;rw1hjeN~Ijbra-|GbQXxB$`( zsWzD5r2Z2CkAX&6p08^fRg!@JCnE_dXh z3gzvarUUlB?1qv{knU{f95dBaAZw$(e)-&0#p1h2cuRAflxx5N>=AuONC;^AfX7-d z1%hYI?vW7yeG~{((n3y<(7+_o9YHus+v6-TiZ~L01HvY-A4~x#eZ&Q5tLf0fJNKeE z8mdB$-D`4*jlykTLzDrcKV6Wj;-2eFwnB}|u4D2g_7oL@V4hhUPw|(6`?U)r`jPMY zi6Vk#b)GzjdyS9v zVH8%_z8hj79Thy8#9_^ZsIS+d&9a~c9gNyW-14}8K&N9@tAmF*P^2eK=v~Tg3?67LWTN{;L3BUI028Cs3Ax*V-9&ve7lDc_XArO$9%D~G8F~_}Jyvko zgM_`^1y~KR;~iSTF1#YiO^&sPzlS;Mpid6fxBeQ^hX9E{RxyLpy#l)TJXv`&n6nUbYikEG!A&Z^ zoltsx?tzqR@Pw!UceD-?Os$u;g*6z8DOCg}=zY*Thxd9e1I*7i=>Lma8)a5BSX#<| zoBqYe7z`ZN%M?}2y3j1zyDS~$P|3>OXYth(iA)I&6_9DTvznWr4D3VIz2w`O`UB6J zI_fu#7^-Uwwn+#Pp((ME_rANP;$!tultQ4|=KghbI@S=F;f%$wu7jHF>;hTOeq3F> zDtk=MqmYpBs_XoJ0wq(7j~e7QsgipDbaO5W9tg+Qdck4n=vVSE zM_Jx_T=RPsC;{CWQ3xe)&8g)vidREoy-W9UG~+UPaWF`mZgRyE`IMR1 zUD5F{mQw9*a#+l|+p<+j;`ZKJYuIXL^H@s_x8m^t!CFTs*+M_0q^qxZ`)LO|zDX{x z$9CHw=~=$-*4xZQZ`!C0UbT-{YOGmOK}XL|;r6|_knX)$oPxg3s+~-V5wXCF#3c&O zdh7H;Qp`z&8W%n*(o7wE5L4SZmrXpHc1RxA0sT|<=i=WU*=|p(d7W{0+68dV^Z$46 zSirSn(O$FB@5-w|Hbl27t$|8bMem%1U6uIMW#34hNS z)+0bqprVB$?J9OGUiL^GO3s#ALIzUVKC)bzK2ghkxx!4h6DdJ0UEUSs(Z$nF-v;Fl zDaO)*da0XkYkXm}tm^g@sDUNP4um8$;RegkNxZ(|qHweqPxbR3 zi{TsR4ro*h{^<_bLN<3|5%-8~vZ}8yt@nLwiUP@?IMHEl+T9 z1M*&B$|Oo)g>=Ji@1n@$Oe*iIHaHOd>qliug|jG-fjFq}x(;0IBx;GK(N6L5nKK@uHv zRm!KS>6rRS65bUL3}Y&G<-)q|qYwv)q)HdwMT(i13x?>jbXAq8;&Bq@9>brXk&L|Pqr+JIuqmR zGKUe|y!X& za!yxAxyH+LVS4E=f$L5-QD9oyb|v1rHKD{9y^^-ijfunV3R);OH-p zclA07)#0--0|YCxl8a6b^XamHcYe1?SYT z;2FH3r&lOaDm?eEjQe+qBy83~DO`Ch{^IGnRpxCY2XAwl{i3JN1#Jsz_0fha_z&RL zoF!7teo|65YA6G1^_&ef0)VM|%0m9NJ<3J_1c6wvu;MoP<&~TBQUQpPYF3fq7#XUb zV{QE*h~q%#Sn(J7(bx*67fWqD&9{>*nZ&&`Z-pU~;xD(a{Yi6kyLXQ&Nw)b(PN&)= zAySzZ0x;bW{n=4v1Jv&~G$_U59@ZhaKHlSDP@igRTzZ`Y{upm+jF=(eo)yhel{5l& ze{&Q8D@6h&{F{SQ%(xDt4!aN6cA*VEtdvO31}qn0LA0o0JbGj+R$39QuS13$E>T3I z6gS-RgrArjsg)yys5IxNdt#Tpt#BUgiw~68WPx_Oum5_xU|;*k=hsL1bV1a)y?{S| zH!fP(EC(K!S%4I#1d*g7eekw=Lfkmjrd5}19!WE;T(UH>35RZoZ*n&HT%=uW1$4^P z3%Pqwkf-NX;@qfm)C{{AQPY5NtdMHEKIqD8R#1}M+r!sF!7X!57UTSd0vxyG@2GHt z9^%9MmQ)_^Tq=+Sz~rw9|MSsO03`KV^xEMs4mdV50nj4#{8GBc?vs=eXOG?Il6-ov zt>S3kY2~?D7GjHC(yu(j%c&e$A?<5iYNcOYc334y$qC9OjO4h|+Zv4nDLo~6Qi!vA zd?a$c(xyMvzvs)~hkg_L6*8!!FK?h1ZDLnekc025!JykwGUAq`mFk3}a70;&2$v^n zt61$$3gmmtq+=S&miGwD#3VfKFP&bz$;(aHo`~zjvU!_S;Pe12OoVOU%|+VIv^7L? z9wvf!Ysz_|plb7F9bC_P*G08VKLL;qWKWA2hJScsRyfxWI~Y1k?@5__$p2St=cU}2 z8?D5-MGt8OFP}p9$-{EUgp5^2s?K6BMjwOZqlpU-afceA9|TnKCib!Fsj&=CG7L9> zr5kHa+uW*7Rs0oVwYr_lSG)b12|a}gIg#V@VNj*K)S#6nDi~g z0#_WWFAl$0XfVf1#!Fh9z3ENP^3A0<;R9m#tL89|4G@|>XM`!G40msqZg1A zR-fKY<)&q^BOiO#H?-b&YDZmtZx3LVqhQi|PXs}ENEC)XlS4m0a_;T6PX1oQECuEp z2eQW_96vLL!*W=Y;Z-q!b4(Zy1UiwJw1snc+>~wF4xmX;wKxO$sm-b%a39{E%~vW2 zEG98>byLDBrd7i8aT3_q7d5$1_!#|kE{iUjuS+_rwsLGdO0&G@<4@JPB|{GxQ$-eOG=hDQ^t>y*i4H>K~tMw+()C2>4*vrSOy$7en3{Yf_(GB*q_UaTP*U4nJ(jZNCb@`;t zhulZbESIxPN-IyAp`3|sK(6r%dF5>=RG@OrCMhXXO_O(?>9;x_e_V_Gct7-F0qJ&ddp(zG9%GcvvCi^gC{z9<@ zz5DS$>y~httooZ(NvSrQw#dpiE|s4h2gyNwRd=KJJpDBvlzF-nuz1DfThpDYJpR5Q zRwn+{9MJ|4WaB&J-`g!$1fUs9Bu?j4agZq~K^;udU2Zi2GTSz8)};>_^vOC6;(W=Z z%Et#80cGJ+JsXlr$+kM%N6zCybQG65ZaN7GJ* zI<=%6Txn!Rp9~O?qs?`f;^0Q08ew{qwbhBvZeU*y|5Pc7m#W(c7~-D|`ut-`mBkJ4U|16BQhSP3_EgqN#i zBw9DJO^YD4AIp2iGj$n{u-iT)C=vSjMeQw3f@H)=Ep0C90aW_Qv>JFD1DqCRn#z3i z?_UBjt#PXIiU2h0fte6^i?QB{_HA_7_(HL}88R#=7x>sU9oPnY3u{eY@s?z+mMSP- z-G%$)wE&PzN|!MhWFK_h-byBWTE z=2A_TC9eb_?Tc{h(`?#z;rTY^bqsFqaBsN?6&v!L_4s;4eXjAMv^n4dkDLxy=;b7F z(mopTAlm_!CdToFHJpmV&Sj!gxrjrxaZ5*1K*Ow-c*x~$M3E^uOAyo+L5QQqkpo5z zOnL?l6Dw+hk;kh_Jg{L21m;&QlcW9i>esB4_Bm-&PV$6;hDQ~-HQQc<{wv?a1C-}8 z9_OrXR9ET5JkQ?DmW8G??d7$}#e*BuzV6n1VC~9h!5ZJf04#uXJkZ*1I-O>6*A@?c zqv>#|d2XzMOh!t*vrt}!vgb4TaJ!u?%Dl2kJZwnc0r>?ZF2v?jlvQrCugi)l5;d)a ztDBW*)9qX0Uf~n#P?K)Lxv}vntO}KPKP_ieqb!vLJ}Y088VV; zjg7h^cC)1flNBNpd$K|q5#C9vCV!fE~F#F+%Zk%T%o+!hWv)mm@faHzWDhT6mc`BY4cAJE>+@NNbfa&7M zQt941YRpi9XHpPN%l z5y1s?+5m>v`y1;FYt8c5^OacK#0Of_Y5M|p<$-ukD;|82_LD%OuY4lKDLi8b?c1Sa ztn}m7F9>P$Xo9&iP1_ltuqa8NxwsvAWDV}lrg61+A`rJGI{hxc~ykJ~X8a8KIx50gNluP_Sykmv5 zh0y#!zyhI9WD?P$c$g<^q*U4?ZuL{r0Jf?uk=tT*`^3V?r$9u+37<+{q}UDXHp5Kk z3bD0*5#^#F7GcZA>CWko{NOcI3dq59tC6J=`8Gq{Ef1 zZ#aUI%th@q)>cYtv>+zu(D}rI=Gk(w@D%|lhTNrA^vvz+?>t;Iu`TP+uTE>KCC?=m zT$D{ouwxN{TYuTHXo&L%E$UVxPxxW-%ATXCZhQ1lr+u} zkf2^yH07_3kO<@qHv*j_|mhY^C(Q7oh2Z((W7{mHnh(zAEviE z3f5$?K)>AP$*ApvS({KDad=wzSBnmpeJ+owuj0~x7u?a%#5BdaP1UGPR zTdj#f^VW&!*Fcd=UB3|GgpcM8#zqgLDxn2JZ3WK`8ymt}bVK_Hz4|b>;*`6wwo6HX zi%{X)x|Rxvgi4`CV$h4TB(uplk)}gb(6!@Rrxrb5O6|*dwyn-e(u;j{IP@W2Hlf{| zRs=DstJY8x4+UiznuaQvW6>93j>Pr|o>Itpmp>Pk#R%RyfG}f5QBAWf;CGR0xJ>H$ znRGy;b!$|C@H{=F&v~Jj1fUvta2v4u)}_kB=Z~VvV0B_P;#HB8{D5*X7UXfQBk-#2 z6M4R$M7?CXPJ;-OYQp{U54W;1Ax)k+Xn#HGC)Uqr$^F9QhBEys%%YV zI#wSkk9tyPaQ;ePkA@5vjxb>0sA@@9-~VYPm2);V5&K}0!E0{*6Ez&CV#ipyJySiL z3wnD+Nl$?Spx!_6ahJrCvw!M%@h%gniCKGEg%~+ArSp7Ev9fbQ*SPpLqljZjK39&0 zKqX1xtadaUDtZejG{?x7Rdr^=kMh^y_f_)B*dVNS^lp86xw>*M7dC?N{3!+1c$p0$ z1vb1mhb{5&iD|kh3@@zL9IH?+=!PJ42Y_@!mENE>2AEXNBS^mTKq4xDZ)oCWMkY-E z=vNOWcR(P#F(43#`*IH zzl{?(9D-d#i^#ypd}P4e)$L)VpdHL>@}-;n=1LschBL;V`{g6@JRuO^k_a8$;J9p_ z^M=PNT@^}HJ=SQL#)6^Jw!kF*hmKJILP@(PF&=8;5-DJjD7seP-64a%c4fopH4N8e zM7&&6;aKK@_?2k<0a|rBV;K1oiU$}RcQCfhHUZS73X=pLpcrliE?yWG20HcPq_=-D zfGTGY4o$Ahe%No>oa(ek4xOsd9h#7$Dj|E|%I1#sLrIfJ_w0mrLsHy9)@qcDWJUw( z1SEeS1)|~(yQ))XH>{8h@ zQ3P-`e~9QRGj%2z^CH!j?y$6W^>>-fE7j53aMfr)# zsU*zpnbc7z_?AoXj*G;sQI%OoMLx5+QR z_hb_M4vAp>M*uRU)q4Q(ye4-wIoKC*nm!G&&-h6q5)FE)sj*iP61GFI;X)kF#) z{SFolFr0pLVQJ!|1+t5MRbelM@#aHP!9nrrqzNEyZ30e@(UTnhBH5MG5J7N7R3s^Q zU%0cn>d`h|VMxbKs3%u6Hcy6rL6lRKh<8(Tf$-<63K)>&r}4*I03(}RUUh-Tbtq^n zAte^7C0{Pn)jF!saUycMqY7FpX=p@kyGen>&=^rLW31gOUh`4rvl_LZ0f5 z8EutbWjv!-cLK@cGISUm8bL7iPLq9T3nRdT_FaqHNnQY8;>9T{%Ke%s+FDq1M@J?oWVssqFL29p07rsPw6D95z z;t!V7u{p*cd24%w1A0-aGZ`b>p@sxg1TTcDQ3G{$qnTm^)Jr)K*Tj_hyHowKa>yUR zzKmMRy;@?;!k?h-rqeQ5`c>=|lx(Ex5v8;Ezm4~H^IUG{zGNy|HNS~c+yO;zsyY{3 zXngL%DApMyk>*#T4&AT#*2z%WCvO&?k>i-=;=Qzooo2JLy7V`)v!bK+g;aEQ|9dqz zU-MWBZS}X3b`F%~YjlVvY&h!`nMNCRS7;Kx4KuSN_Z7>5k|@1|n0%8#fz4ZV?Ylw7 zwWXcAQzaz5Jnt;M%T#-?*)&&a#;-JQxXOufhPdQ??NJFd6C+KkAjALZv<~&PduV&; z-IUxIt4e3ZB<{os>bMB*x_iO@JTwoWOyV`USCE$Amp(K+s#B`VB{}LF)sa8vnMLjdG)w{gwcv)5X2S~r1(xMJ#r|-;z4=^ z9n_5DA*ZC~CiZ~>?$9i^Oxo;A_0@3xphR(8AvoV62yBRq^Y5>Uip(+3VX&c(Jhc?-!+A@)qc=gZ3f zlOB~(3in!CD=JboDmk;G=h*fmds3>PFdGP6PsG?_C}ct7Ia zU%Ps@z*5*ur_%Af)l(MsH3*##3#w-B*l`&5rd2IM_fv>yq<1<7=<O4$G+5V7p|bS%c_0NSXPOg8 zZHGLiFM@DfYW{i17i3@FA`%1DdTH|?Rk8)Z=8oLqsRu|~*y?LkADS8rA$5zZLwlAF~R zHy%~9sqgKeOap|9JWqn5f8R>UbdWO)HJA3FfeP0Ko6+PtjVL_ zV-d;HMAjc>)oibdQjfZIm7*XP!BG^bEXQAYMSa1B z)%%<@3t#}?=7T&Vz9yNifS0zwaSiKX06DHJiiNrte?uanYC zwr#0drFaKqC8)S(B$v86sr3STSM$6n0Y&>deu_u`4IO@d^LGS$X^8C)`N8}8A>5V}=bB_#f+Y#(Z2xoel~v#=)Y@hCAR@}=P8(U6`@x|hY*ENMF#|hz zGo8)SEWoH1v3TI!tF$fS*yh40d0(Z0kU7B#yG%bb*{o$7e1wC-8PgT^8bp_}%l92E zqMxGnGcZ_AW1}{Uj7jinNJ!H|$xf)%yT6}vdfmPl`V~uLc3`1KVg&Uh7S}!M$9`3p zTLFh4AI$VVzX< z%p=8D-c}si0%J>Sf<~JU*3wjlk`N7@ow9 zL+ow}m*gQf>c$&@*+Xd-s0g=1P|GxG0N>(>xx9&L$~vt`HtC!iwUXQsW8-rlb#+Gm zYoAqFbXeOWBQ)u4xi4Y~kK`qhP>Oe^K=|%&rn_4P{6)-BWT;Tw5llT!8@sAnFbG%sIFj89wJ+KU zx8u|Y)bXwGZdyJO%$?4+byNIf*3SQGi4{XkDCL!@%=(uUJ+NZbx$vcP;+zjOQtA() z-LoJ9<s3KHWvZE47v^30MBrDfjA7dKv!SWeMuqZ6&x zzxi|Rgd^r}z%OLW&S+-$Y7ZVqAgD})b1YU~vN7wVW-B&)m0K~0vZ(XiEuv1pC?VZCH1_}R8 z(wIk@_F4Zi%?0i9gj&tKJI_#bYI#U6p`c5I)^!qjLhD&uC;0hj-%n9w?GL5PQ2}4q zwOaN5Ei>4C$AOudB*oziw_!kBPV7LZug9<`n6J%20XxE*d?Q`ip!j-X0KDKQ(wd1W ztSeV7kWfW;FsITjQ>j`-{oKm_xfz3br0d8_N+*=zULTJDstO5c_JINE0Q}5T#8maL z{tByxq(IHxyI0HbFjiW0JGZ0V08i9H4Sxb^1r>vw;6iyVbCM5Jeo5~p|Cv~B$YYG?6}@OG?Q2#@e7sF(ze?JUgz z02@>Ro-l9gB7f#aBq`m^^Y{^zNn6rtV`u`c zYd#8X{VBifW#1rQvmDC6|D7FHM*2guQp#a&Ibyhdt%Fq6D#^IriBW(Nh1@M?k59 z`FIdV3ds#l+PnEftSxf4XR3s zo65cDt(mc*)gYvDBHkRifq7h^Y}>wnuY$l?yj;Zzle_)8*IlpHil2I_L-p~nE6=d( zK>aQ4DY2i>7x(bVP=xO*r7_i6i9C!L+@}mG06*bm7KVI6C=vW}|NEkO^x*X7XRy-E z|8um+?A2r2-4BiQU=dU|$j!qOlY-j5g$B^xb0*8)8-ZF8ep<}8GWM_OZh3Dk9l;13 zxwgOv+r=bhQ{DYL*(5t9U)v$#jk36iFT)gpl3Zo9*I)#nrb1`=(1UAECxalCwa^5h zF>Q6I_X}!=alBqx$Tyb7=_mh({+<`9w2}oCOOwS0FI&0#3c8uBev-l2B#1B$bj)XG zHDkMR=DI7Lb93;ZL{E2F2=qehxPnF5QuYxcLPkw8UXGKkaL}xoZ%&~t1Bo*oDx}Mw zbIiZ)>T&8gF$^s!9@G0&A3bvqqH;Bk5%KJ|J`Se{CMm_(a9>QofFQrw-&5nx0=OB) zd?8=?|3JB9)Mu~hf7OgtlTOR1)108nW59%ZBKa>)Wbb`b?z{KxXO7OajQ-&VZ}N+} zen&qIjktLqRecg!p)@*N>Lp#4N%zq;XCaw z1uVfIQ-<{bCQ_jCnW&NXl|e?5M8JDQNKx8rmu|S^nisRF1|=x9?s66*Kd_z5!5Hk; z#R9%{!8S$}A#kOCb~2VQr?D1ggx~0*XD7p)^>zg}g2$WYK?|Bzwb#H^V!tGUH{;Q^ zUmbd3?}FtaJ6{Us#=d~L=al9HtBXO)VeW0xRR5!V?h(7E%&Y;3WaUj?OmC+oLm^w0 zyTY8?fpl7Bnnxf!x%g~*zUY-lWWQA=V5vkTAXYw|H zDi0o=4olZNoV4>`nNL)IE$yDPjoUt`9}BA7PXHKU@f7zGGzxxu=i6O0S5zI4IhDF% zXYGKFtVb@Hp*9Fbb^dyq=SAKf1|9QfXcNV@ z@4i{-Bw&4;qt2NiVv`gWs1<+ON5@BO;Kse0C70!=XPySX`}L8Yie=R@5o9&}Os^QO zv*Or(8npsv38T-bt6282QNR4|18Vg`ley8cl?#r7~6E|=}efgMzLKe!{ons(|Tg%w4 zcrj)+pYR4L4wD4b|6EEGhE`87MK}E0I?gUp&U;JnbvizBCf0zH_t9}%We_(XQUe$_ zH+0F5?+hs|bf`7b)RuUI0OUOtOVvw$o3)6&hY)>{rvOthx;TCe)e=ll>l0Vf#bj~h z;8eND{~%juzOvb^e$)xe^cC0&7w>E}nmpR7$UHS8PQ025xWs!&#xtq1XV~0iAMv!b zcIvz!D0xRHnp3HV5k!Z@LF*A;uzPwFmz_9e|Xa;coVdl$`a$Ia{3IJF`TROjdwo$&S8seKu zk2M~@VxL<*7XA_v)M#IPC$)RI3l0{AdEXc?xl@yRs=Vjnf*q3T&~#0>(2`u}4s93X z{r5=NMgwW*)_jcBA$4DZVUBR@@f?or*$a0}%IJQ~$vi9@w^=w8B2M$2(+O7rhkTii z2SFxy`=zp;%qOrBETn*DGd;u+M3b@TYvA`0FK=)*vG}Cd%mSJsw7H~y=!jA~DWfW7 z9xL4+p2JWDdjyBns?kt@mvhi})>yIDIOEDapPlN&NqR7{R+B*H{lG>x-{R0uGJap; z0Y51F&8OJ*f^|@oFwAT#;+k{#_!ia3#@uRdkFm_9yt`T(XmaRR$FaYri72%pwf=~x zaXj|tC@P#?8O2~C82QZ8Y8;?CLV)YOp*_RBSj&hfJ!$QefeaS4^0Lg5$Sqos#_&P@ zK7Qe{^BYpzYd#%pu0p9WPb_Ht{>RKqI^k`WA1_D|&ILE=y{4KzqbkHGp$eaz zuBt45?mE+9O@8FJTZCT0WPe0eXMw9N7G8k1Oii}(3jcjs?f{6kPQ5TX$TL#LoGOd zfbwh&OQ)1*Y-fEcz#I_ij6uUqfB3wEOKW`Fq^K#3;UA#-IH*`CQ*I9^IV6S-`2^Dw zpO!%GAgBEVWLab{<%@&O|4<%q%y=wiI+u|b6JCH?B``!d(&|AQ=4J?IL~9zB$##rd zcl*!N;Fy^IoVW4%01lJ0sD-|TOH4kDip5%p9U=>l6_fLjjbC?a={-vJ|7fU&0)%T) zI-8Sd%potPH3i~XW91mx=R-+T7Vu8B-jlUzYb=P3ZE_Y*yslWL$))nVa9(BSkUKxq!zD-SNV& zW|`y%QifN^0QyWD;0l{zitB8a{a=Zst1hV2w&LZXkM#1d4&LqX#Xtm_r-_uZj^oyV z6P&<6onCPxNLN>QU}z>wt&H`JR@8pFMqrEtW@)4}Kd z)ej{*a&kOP+QuTf<54A**(x01j-x(u080wXOS3@zyg8mz$H>Q*14~{G$5uE&@PQwK z27tk>+L@aZ+Wo!*PyX8B|Cu@@OP|WelQ@tndCAF23A^S{-zU}9<72YyLq3A&T+)p{ z%DYPW;FeZ6&-1VXctrc*%mcsTJBRq#s~kcrvs!Gdb~ZKIlx77<790WBMGy#=h&BN4 z3&jEX(!2+i;uSf&zWfypE79L)vl>l2DDhHmAsDXM@Ktqsi-DB3ZVY}Y`S%T`Ddn^6 z+!VRx(@oG1B{$bcZJ7WF);}{NR`cu&d$4#IrHD99xM4&#s-0+V z$<6UheM;AHd%;4nrinja1zT<(4q{Ut)|#yg3wq>QiM4M$_)R?F4d*FB7Q-(vr^M+Z zGGz)ISP%tW+S7KiTuiX~AGZ2EjSMX2de732Dr2$Nq0APdzK+Gm1&JD=NoEd{af8rr z{IVA=BJEfI`_eXbw-o6C_Qw8xowAY|{XwP{Fv-Ag-(*NH@Y9Od6LiMni>@C<~czzS8Da+L13RvV*AF=9n z?v~dRna&vbHOBsWT?1GY6P(ezA}RE`liCppol# z_Wt?yW6Q9HcEX5GDMC%cOhDABi9yiDe3gp_H$oxvD%)LSZfX%aLUA?@|A@(T4hHTP zngF}MXaiDqnhrBtd>YYLs(33|e<|1wKv{cW{URksut=u%ve%r8BEF?HmfrQJBLr#w z8uJ7;H6uWmG7t>9h*e$+1G{01}Fai9CI{40ad`&XsL`k@dJ5D&D+EIAQ z0US~C;TAq)b>LJD@{XAwdCy-ka`E*jk8H8Z`5EgD8}lRw*yXy>C>#+nSpRk>_2md) ztQ%Ng&^;p#IXLJpGMxC9Wd%p~tA4eQbU1O#{_vQ!5B^t`i^af^t*Q~Tafr(nO?MGhRqKr2f98E>U;A6{M-;`8 z{+UBm?Dx!*VkBcCok@9GnxU+X{d8RI3?lryp)vgkBF8Q5NkL$^K z!&vwvY-Aylkb^`8D-P6hk7Sz#d0PH`KB_8A`+o~Q9w6MZGyY+2_()TNlv=4RB2fjA zcJbZg*Xoi^oaso+aXVVHZ8(hHlsX=ByjG|ISw#%`)|gz-6?4KQAYAj!fH>K@{bh9a z;QkkjkX8a?K$EaCFI~*-2L<<+*$T(qFMF_SuwDwqVKxJ*X8aOZ|Fx6Aht%r|WKoX_ ze8KnkLMC`KKT3)gNVA%4CL!`fyrGtnYhZG(B!g*?diuo{L4IV_bf^tmcutfT;sJDA ziywqN+UjqE|BvY55|dQ{K+T~@Eq4M?C)VJ&)kvu(5naR_9}o`x89nB|6SQJ!DfqcC zudIs$V$H>`)9JjrH`+Kan-|tsX!^C)(QJ#QtSAVFtjIbwc1>UK)dPMArVsHRyU@@? zQj)Fdm8R(G2q4y@$dB7#cYe^~s7qd>Rx3E4<|q1ja;)o!#NxQF#q3Y?LJ&vdJS^Nh zu6IED8mw_pQEHt4@3(%?&?5+qb6TV0!jT&2&lx*ho!8g&lq#m z`ObFHd^D%Er0plozZNgW2#)N=c6ELT`?##R)1py9B!w$bjU!`{s83F|DW-PQs0FD> zndXvl+7fH-QhCbY&;TTFpWczzho!S&*d?HU4ozD34(C2XDp|sI9=H_+uj}pi0U&3% zMKb-kM7Wcokjua_&MCYOS)dFA4B$Mwr8I_>);!T3$&Uqrg?Y&qhKFO6p9QL%)x@z2 zr2dJvHH~)4E_PTOS=mO-!+fP=sC;_4Ti)Jw1F0O)VKy%$)nG#ZD%OLm!ccOua(zXB zkVTg1ztTLR=w1#ncdiKU6j`-!I`W5UW!i@vajM{&n-gaK`s}pcj5)_@FCY9>`|t>{ z%x4o+G%DRm_V)F~K&M>ZL&`?tM?(xd4>7aNiEm(r`b1?E88DSy9ILl^fjM>*AwF}f z4ouqw>2wLsPB=KeVjS`-`Pzurukvh}e_A(8yWUe=ga9q5UC?m;K(dF0Z$W!DAUEP= zq$fI=Z+gkM+gDEogF#secD+NS5nV|U29P}4!9;7S`9jOt6a|>>>t577Qy@EmKzJwP zSx(L^&BQ$^@2cZ@Pg8bz4>HncpB4ykHz;>n6eKSVUaT+p9geXz+kJlG zkogIc38%GD1rO<9vkP!&+%Xu(iP5D-UhDZ^^=hifvQNmg7pfJm=h>LHnC!>q3Z@N8 z8)uWVrDj$Oqi2wxy{KaQ@+OLKOLgtTWVR?yQ_!Urt!R`s-MoLJkYu{!ZeMEx zYENXJTESh}FBm43d8Z!L1j`{Ex@dqs zYIPAiXt69S`4yywo*k zKT3xLtVDq9m7r{Ikfld+a6fq1PbdrGz;jZ7C+#jY&Qu0jFR3g{SzI%tYMk?1)?4Hl z5Eo|~PEQ)!M)x&%AK#nP5ubyK%A>f?>F<|MPGLXu4|H?qDx|-gh@6ZbkJoHQ=z#9u z!LK9R340*`a!*AR#f;xnyicF}h9@k=dU|KrOv!|Z9OMH>@Xjq06ficXQ%WF7sS`Cl z;I1+7x-7K*{<6mAjB&F-r!V*Yth^R&epWgENZ6HG^7pDW+Sfb!YBKu#<(~Mpr0x{L zGw?gT_m-#z`zp=n|N1H_>VE3+s^-cQcOA!B^S0TsnNr%xb)D6l$`66Y}hOGZkP^CG03NzO{)UaFh90I=tQlY$WY~I@%VGg#}~o z3gHom@j_fufIbMJZoyB3skKYc%Jyr{R#YhjB~tmpo@U=PqFelgx$ra> z_~K~=G;?Zm17YP-ha@8H3ti55WwJOos@cTQpr~&CL3DoY=y>(78kkg)BbbB8gR^Hq zVef-z8sqF+r7xOl%>?B;Q3b3VGSaTZPIi5e6M z7Z*@CiBA`JL2_a=Zc&9yj8~Z5N9@^ZXMBp4dNhfzH%*HNVaKIF^MaRBhwZ!u7>GJi zdxpUDyf*%0s_`p8GdEX8{+s2SMeRJd)2Ui z2pJ9T^$y8wa_&&)z3ZS~OQ0E|KTXHhra6JsH&lOwT^O&DsqQXVjT9dkBrf~buR{lC zCYfy3O#LO^UNB>#C%O}|>8g6T@Rmxed3P;lplZIiv|?dHR4*b7ogBJN&Niok3v zAs{`g>Jll22J~h*BO6Z~!dTD0!pZC$WaMNK&ysWBtvD+$dE%-NbDD8(7>o_`ECzkf ziad5lMLlLcN=|43B?yw6+5Y-eaDDz2R>4rMb8U$t$dp?WywJ0l--3E!Bb{HZ5C3O# ztc;D;f-gcjM!mH;8B)_Evdth`>i`A)YG3>NyM7H_tyJK+4YR7uRb8R9bDWFaS&OmU zIHhyc1-*Pk_hdV#<}EK6NM=}Ff?%O-bPR#vo98cJl+71dX^79jlFl3@wAG-)30ZsF z-Zv^%9jEi1-t%VnpLb4dsF&JwIjn^8ngmkdYb8_uwWK|6*i(?+VFcn^678)TcCs|Bri0@;P5aR5ul4_HA7N}_H5Bw1g0Z%h&lfaf#7kT?Xz zClO0(uTkphZJ`1%E{V`$tDX7wvNBe+OEeIy3}IA#K#ba^a0~IYrH)hPFt17Gpm(;sKOMJ+LKn;uz=%CttR`BM{{?+lDm13Y zWtFfvNa&a>V@O@Llp{g+1k4{G7qa}R!g1g1T^Tl=(0Z^>oAq}@Q)bY1`RJ!Fp%aQd z_G#yT0E5pnG*S{|{dXyi;#328B`R21KJp5}%VDr6%|NuTEQpU_|H=w?T*wUg`{`C> ziX}nx2!UFDI*5yB4FW1iqo9X0p@Tz-KUYLCkzN9=p&6yVAot{bzhD2`dSAPF*_!r| z7>?SG0WTAMUBXuJp$JJ5EUPfNafsU^TCb1bQe&s$Cp5(x0j?X}EcZ8qgjS=s#^wzL zD1~;|xh{YpiK&>S$k{B$Q2G&Q&GOt%inTZX9An_uCNm_S>y2Ki*^@0ZhGTZmqAZH6 zw+c>hf~0@DyG@Y`=D*}crX8InAvRI4b;PAv{Q7+VE#4`}9{x{N&brVO!8rBMrQ?^U zSR40b&?}e&bvk-{W%DKq=1w$WIr-T)dTHxocSh!6a-`#O8(*Wg#Z6pg}7fEGn z0M}$#baV-YvZ=Q%idwHMPoQ?HbQ}Xoe&k`g4#}!Q8!f++(~+RkV?u^nQCd?Cqes}C z9KqaiYh(HK80iqIQQe2y_${g*B2q#{OT~F48i#tugWpqEbe>S;ERm^k7OxG`I=@G-(ey;gW(^=Qxcy5tWWIq~ow zOoown9zF|vv3E~QX^yUh%Yv!+zvd{evT)(68B%@yhvrchvF3SQdCb7q7o)|B>&+({ zSlU$m5-8M^S>iz&d3A}-KIpa0$7EDLMUm-wd=x372Ds-_Y}-WpqNq`sr2yXd9I2%D zdisRS9INCTSyAI2A6?2pk_l*wMa9Y96l-*h)-Lt?#Ih(5rT9WxAAd@BTg8mY5rA$L zQZ}UbZx?H#qZKbluQg)Hkv4UFlqXrwFa^bneqtTN<{E<2$v;lGDmBM1uJv>Or#aE>cYom;L!ifG59Ms%cTWDz_6ee0jP&lO%u zHGguUhMscFq-Cm9PEX}y?tjouI9LuZm{APX{IY7qTG{5>oOSW79^_Cw>DlUJ9oVU5QJl-zE(zh$E+L7j(BJ?wpGvTxe3%Msepm;XLR_S^IC_lbiUTv_O-&vsnb?bj1n zu#zh2Yp_#JQW|=T2R6E_sG8yHa+v!k4ev&rNu+)T3QA2q-5b8bS0(>h?HKO9_AIEI zTSULrAW(PKZpf+$h=OQx>El8;#b!?VBL{KJiW~^ZWd&oVs>e^)kRa8&Di`<0B`=)0 zJ<4|X-OJb8EA?D&-H$wNM23T_bbMM6WrH`@k77`t>!;f;ZBn;v^I+PrMpN{;-L-`+ z@Q#Y}Bw|&emC+%f?9r=yobD0hQV)YPwyea}A7Ox0$BQ&WS-90qmXbji*qZZ;J~?S+ zi#c(WxBS!g3@#V}!q~48K2b&zH($P5I?^C_?taXGtYpcz-tL1vr$@&=_XPE04%o^O zJ_z|ktg&Jw2HT;1Dr%l307XE$zp9mYR<=O_)PJ{W$jvoEjLBX4A{3w5?_T*Xom6oKhWRJ8csEti%=F2* zT()QB?+x{o0W={=oav3AcvL97TL!V|q1(pY9rpY-dUDiR|HzXX_OuX-Zpyb~H!sb; zRxQlwX-1)a-E5?No#m$PU*^qasm}$-rXX|1t}{`Vi@1R z=vZiA%v=@9p-!r56ddwt-`!}bnkYS_A0u28tr`rP0k{8fE$XULSk{9 z2`Ls;`tF>iH;+snbE6JJBfByNYuyNH205zO7j;DuAlR>C38!e=u}Vn@;NgrBRfRc~ zo61s01wDVnXsQcl%IO$fQ$wsVG*7NR28w&G7zd?ENK44N%UWjR@fonV^H7p25HTrr z%SOy(gY=iL+Sr*tfTUsQ9_V;Om}~iob6l_n$3nNj9t_&>eUq~ftu{ByGmW5|y2;;} zYU9@mXk}JzVJ9uenRTl4qz4GU4-7Cc>aThV^I>kiupxuU{Sec)f_6*rG+CSHETsp| zmmG-FIR6bQ{o_^HrgsZ_8n`yYPms?4&1p8c^ju=E$Y8&?5&aId<-0>(3 z(BTSpw@3Lh6-K=n6$2 z{Z~Rt)MFN$8*H$&qS$f0adnKn`kMQx{MSkq)d8kuO39rM0FXzdW?;-3ZD{4I%PF#Jn z&Uvry3U(b&C5k2DAg)?^ud2i=n4cLEyETbdKGB~4v!V%P*n1-ju2837c*t0%W|F#d zv0s}!T!5XUksrwI^?xdR#HdYrUI$5Y^)`aw`RQAbD|2l+8ETY^lju(=6cDVwTZA_G zjyI8)qvYid)}M$O+cd&^{2|ghS+HWwnE%|;)5lTfadROznL`@H@Fh9fdA!%Mb;ZG_ zpuMgN?+H-KGQCHbNoJ`k%wo^99Lt`dF&3%;5(3!kCRw=yR78P@&)&TyMU`P26VxIO z^zHP8OQ>{PwaEJa&MoTP6`6$X@R^jool6oq(9bIG#B6;@98=PaT4V>A3Yqtu{}G#O zs+$PP#(T$KnX=J=a+a+wVFLkXMxNWLfR*$d)KRL z3+>F~TC%t{O#9~i)Tj9suD-faj!s#s_@i23F$DYZDtFIEU(UX-{2tu$dvHd5R#yjX zlKjmr$K%Mm1EbOxV6zyS$hjccEazYuzD-}JZpD8IGUlr8SlQ1IcgR`$b2b0rhI{k& zym}tl?}CB4CCJYc`P+O_kzjhZz<{e02=2`VcQzWmM1a>G-puyxPy;mGJKWO}M{+ZG z6@KP>ZV4u8s{r3LRT!sQqs&^mb`Y))f`sn4XBT{>JvnxUV3($VtAnzQiwG@R7<)sRR%9Y!Rb2BbHnJJl5GES`&QzTJn)^=zj34N)B z5K~~Eg@Ie0U?g28Y>Y5t{MbJ#yDey7F=SA8xpE6MS>tyF^@zUGIRA^iGx~kLz z0O=GPB8`*V3jmjWUawzfLSCHeOZP%O`DyWob5@T-if&c)sAecBY<#fcmnz*3wHF^< z6pnV)nA+27n)pe^b(wyo4>&JZQG@ zs7o91>r9QskG)&O16Y3~)n)7NFF`d$os{X$Fe`(Di@Q_@dL0Q2gmG{e$0T-*hD9D$ zw}JSZW{40gGtT!27$7WHW`!9^64aOS^aPvMq4}fthOxUVxC_mbZ)oW!`8{?#Huw|O z8bu-Cse@$tRb5Q|kT+UXVXucVgp!4BH~C1;Sw~m9Dm!#lV)A$w4eu2p@$!Vhz0TON zsL5CJq`fV4Kd@gkbQCrL*vVbd-rjAx6a1H!tqg{E@Pim&FL1*BawX zCAV^6ASnPEl9k;}FxWlqBwMY#E8NJc`siL=KWABWr*eCv2K^Wk7tRNxAM*tbO9jpHL0&SoPlR3)m?@|F$Puw)vKIse{AE0Ae%@DGM`&?fj%C~J|# zmt;ZFgf^C;3aD_QmCLP&b*-AUVzBRX*FPkCA$Tq0D!j5%;$D6J%#OGa+!vA-9rYb& z#Sz#XO;}%kVybU`u=iDeO-l>v^ZxJ|6c`2bzllGtTez4aDFKHP95x)eb<^@!3>tkp z&1%lNEyYskB_{xFJt$vc9s!*SyW**!*LrN%QMKQ6S~~5q{wk_Lu;zr?nb`&XWe)Ce zPS87=Edt{lfkwySyZVWxHlYhedb+y?wEOrQ$RRrIPb$4rLyS;w8^oo;Q{EB%xl_v_ zR`DQ)E~Cz$S}naZ1o}5Xffo!|)vKIMGnl2;5^Za3CMO+`)+yht9&V!WAVR)|pK%9) z2A8>`t38w*oQ?<@L(@DB4QEWi&H)1AzVzVr?Q(RG%7Wd_(?B+VPh0ukWU>(p>wNZJ zPV!?HHnZnQ6UZAj{{p&c7vP#wo5+zHb;h_=72`(mUX<1F(DnnjdoxP6 zWXtO>G>)CJ?ke`5BC>QV+qWCx z&DvB!0E^uH=X_ADm0AOg)yfJ^`Uo3u}9?bjq4>x)g$m6plv@2}X-S zW*eCalmSJ!ZkWkkQD)c45ix%e`I%D)Koh^Jg4q+t0n*OHIjOcv$K(iqF!VUJy5XmDiH&^4y;4FjG#~zex*Fx`zF}Nmd7` zaF-)W2}Z7bCtGg`ncjU=q2zY_tJ{MRW0#Q4%R&OYP^M$$XbhOGdG$^MR1K~?IBI&*1I>z(gi)~(KqW zY>P3l*gdFo10F%)NsW8W*S`YP8B3pehJy(Oy#<$B2l|LeeVnH6jT1E7s~ZQkDa?-A zv1=IYiMrioK((q3NdR?5uiDMLP1Pic|(L#asr;p%GyUo2@-j}T4_6mGY}@|E^5N7xG55pn#$sauyPJz@T1D%!7BIKFo0{md?}TY8uZ-uSiy}oG82`} ze)LTgOGIV#gF-ryJw*_LDG|RQX^6LuhCc$5FN!YU)YmbkB6~Un?3r;$h4+D_2wJ*e zK_!cs^#uf$GCgqRbfdvq6JAMKFg8dpC%$YJ)-2i5X1!WG3+71B@6-aNP``j2a}EJ9 z39xMV*!6DDf#Nsv>u49%bpLz$LUD;XO+4(zv?FhU(7Qkwh@IwwRo)KV`m-Oce?rz4 z($t=&cisY@=lO&#bQFZ{OWvGcOotrIs#~%_|6yIXz*t0>sF%Xya4>{D;cj zKMkw?cRJxo52AP!*h#80a1ZbV9u?JFIS$O!v zigu(4wzFCkCOMo9Evk8AW;+?kT=g%U7wJ(tTi8SoNnxp{bw<>x&lKX!YW*xj1KZ)i z#&Ch<#W!K`-6e)Tjt-oYvIO2Fvc#ROm6lCT zQ#vmx#=3Y}%REJTKj(%GJL1&Eo7|iKEd-z22oMG9R;eSb`+?G_;_I2k#>OEf|1yzl zp__6louN#IYd`I?^W2%J#kk?{gr!qCOYG%TNRtG}-OBZygbVn6gp>(o5;S>XlFFeV z17RoK-3FKo-wEkd5};oCxb@0;CHF2g2@hjV)}wkwgCm6bGm!DphD^vAGdVmk0>Oa} z!j99waYl`#UUaR8Z-Ui~$hq;BoB)Emt)CdBX z>v3z6u>d)HRa9wz(iROb)dVp*8Y{rR2|^q}I}YY)AS8}+I6d**RN&eVMh6>`WL)Ub zaFkpfVVw`jakL1U=slQ7&_R<;%M9&3PQr<|k4(<}B1My#8n@|QKp*>I53`+5JV8)# znFsHcTBMCqT$!_e68x`G+27aPD?lCk!}pN%K$yu{_FxBq`ZNJ%L^?rcAl4d_oxPJY zD?S0=QqGsTx@(9l1{bRZg;U4(Tgbm8(z9ZfC%cwT9VT@iErjxo7FgA;$6!uBSS{G_ z8$mEk3m{$Fa*ytzBMyh6O@m=1acFuA(wZau-qa6Vh;7VQ)6CeSjL^U)F~MM=DOp?# z9Kd_1LXEpF46~fzMNT=7roJRj-2ScY9ADRm$|48>Bl3EhfkkOS^%bt2)D4Fl3{=X$&| z&D)-EmX*Y>mpOvhUKRGw0=ZOUX8%WacZaQ8a=;D8=khw?)V6oz7<%EdkyI15ah%q6O|mhfozhGTAC#mO-OUyr}EdS~=jLc}%;Eb&IM6S&Me;hL0?I zg^{_6cf{o!z((1LM&O6soC7P9ST&1Teu{DEXP2s*&f)ey%PdsO2L zFp?V8%wN~*w-t|It;##P%&t|$*PL$KGDqboKnvaluKwUm2r+}sAwXXO-Oa{1uAG(} zycF6TWR|h0$)Y#J&=|ARw!`oi%S8>0Smn4;JC8~UJ#b-ak!W_{nfwuXuDkKPaDbB4 zsnRco44~+e?MGBqgliBg5%?bMe{mB^lylrGjY=Fdx~40Xw_1Cu;Hid7C~}U5WVXKi#L0PYZ((>n*1ZqnY9iOPd;<`-TOipg zn*C&vt^GM|r=5`pIh7ha9@lH{jOnW}u2PH6p#ntfJ5{GxOg+F!6XCymD>n15xJ8|q z%~L(&^SmIDBxqM`d)?B6p4;HcMvytDht&20(S9|DSpg%o?FKkSfB5PZ9iI1#%FjCn zTIT$)`2=j{c*U4va(V%8%>}K1Q!r;EUtE|NjA6f0Ag}EflJ#oo=MPpFFNfWI^z1Rm zV^!dYkGw;0q%;w2vU1y_RI{*R#;dOP4QxH#NR$oGg}3vGAY4@DHIy&tk|CO3HlC!2 z82?8ln+=CX1b_yImkFr2zwUV_Zk#Y?h3ab%JFb%ha17?22I`W>dFwzh5U7kW#F|)r z`{921J4|^69^5kv;~w2B1UF&Ugpg6(eN41MJe~e?Yg*!?HQi9*SwN`59ly;bX8Se+ zwb1LlL79t<#nstPOEp?^AfwC;er`8inYy3PtaCZE!y5B(7m=eDhcYQ@s)LCF={D5s{jEOK$n20J&NJ<$>R4Zz% z_C@eML8k0F8oOY)5*6b}J|tZZz{7z%;%UF>s9}+rUOOJ%Itfa?xE-O()B2`hJl!w|Ihef@cetM;xkTu5g} z8mc#&x(&V{p{*b)q@gAmS@{}^Y|^Upq||NIcf*{9Zu=H31Vm5Xv*ZfLQoFM$qDM#GLVDu&tgP#(N)YWELO zsI_F(^p9DtjMzII*P62L();P{7xp;Pi(XZEFe~eHZ_y8YYV%qN84wHZ9 z<*s5QYpxds-I2M-U~_an%aqXgqLxlxJFM_rPMw!J`fT8TO!@Zt2OD$-#vF@VlX;HH z)?mQX8jMU0XlyX07YBHT4 zqsuJ1>gZb~Qsi8Pe8C?oN#Wjd6f$layXtWxnB4x)cxFNTH zn9qI{D&#TpTn}U?#4*hm|RHey-%~LEvrK62Fb2Vw``@s zEaJe<;Wo9>67pzL`lK<}>^IoJcRcA>M23BZAYRK;ww%`Cu$f?WOUtG^P>)ubnzXVv z-h_#MS=YcRfRF(LJi_NA{aE`=C`pR+B;*j$Zr?YL5Tfd3uxb783Bvv;;<=c~MPn3d za+DxbPPmN+<%s*O zQBOR*p1gGwpMeoO#Vm1;ZA>rQ6|rt%yB>WyEV{*ZGI{padBbwL`_VjUIp6_nU&w?} zci6@snC7AcY@z|u!PA#8{YomBH17kSza!neV5KaYRdGs$NUcNQM30ELMyPQr-VQ z(JC+|6U+u@k0PCL?#kQDp7O1Fe)Vmfb_HtJ!8TF=obHeY!VLNNhl>S+AIJf}&(boD z`gq}Ylp$m2XlJ=XUh}*gkai};qkh2Qfg0z_r$G4|qKF3;#TTV{*S;Boccv3aZC5oV zLoT*b#xhoU9s-6FId_Yw)wk!ts$fl=LFR5Ak|r5DEV=?t1679$kh*HsfA>eo3!!Qf zB4cTSps%3m44-l0&11ZUxU?*tY&=daQ?W3JkvvqgY8^9;RA?i;7+P2tHvU`+`f}-x z(@PXRf@K}{L94`+JhwG+q7SsY)e}Kf2CgO>d1w?xn+Dt^q#eZ=)M;+XL7e5V!R#Xy z1Z_(U1*oX?hFk&yM*R6jyeM<0gMmHH^_*VC3D0ewI7T>UHm$I5GC%BQ_>qH{ATa!3 z*O|aX1lZQ@2M%OYIXSM7eb$>yk`;FvDlTQhf0A3`Cs%s0!w9li5=q?q#W24+q^$f> zn|z-fM(xzg)q;)j->WQmRznBiDQge8Yo??8Mq1PuM}Ti#aKP_;_?qK^RW%mW8zqPp zqymJ3dqsVcWnrm#k6xO>EmB_Ga28T(4G>H5gPcK3TR<`#du|K3I;A@sHcF~-t${{} zcHev{@5eQ}o>LD8tk0hh?70?CbDgMLC)oGhABPBA516<5rv~APW#jwvb}mEB8Q4AeirhfR1#p>aL|*=*j6;|Y+TN$9DQ5^G?)mAljIuz zfzSqI$PUATnI%ZBzqN7b89tK_$SPSr0pSNM2>O} zO24cU)7{eIZ?ciUK4Lgs*j)dv!Tbs@8;+14D!p{jJq>lDXqWI*@zAIg;iY};i*Pga zv8oYaRnMnpY2xg6etW%ixrzNH{VWBi%atG(-6=IK-HH%ip@yr`5LD5Z6)D)J|N2Oh|6tnsn8oYy=?%>ZIM++3fz<$gc)qKR%; zd0COTQD#SSrsICsy3ktP_iyoSpq*wEhLCxb8WIzBv@i&oiRM~15~;h`htsxvfbgMb zg*dkU%k!A!vB$&!2H*dINBHpi#S`KOXO2?}43R$ll)r)|E54309%aP3)>IM{MnT z%5!SCD1FU;NwcHEGfYXszqNLkoX z$a&0sLoN{lm@IyF4nqh;cFbRDjUeJ?ejU!UExnqGZ4;unvD#nW-^I{WgLYZ(%4PL1 zj7Psw8hh(fe69jNq_gtd zFLt@L$o;1)Mr4F2C$F{9{ZZXQQ42jXO>bs~Da!;0`*cSrtklyX>D#Z;f&$LyN8>P* zu(N$*_xjYK6Qg6Ei{I7b*8ZF+>y79yKWQUBL7YnhGVO@v0yr8GnL?cxh!^>j$u!)(YoZexME!Ni+L!)ubjp5P9m zgUB8rqpNFS^>d^c_p2hnz5;6oL;LwrWc{qW31fD)3qkT8B9zJVYcmYk=F39T7~BET z5bBT6mUcFrX$8gnt`!T)1eR7v-M%nNGex*cOc6gHeoWQF;te%`lsp6-iRDr(0-dn?!JZ3Bgu


h@@J~8YbRTkgJI+)lTUZfdZx1 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

q@i_Hu6iRm*~btx~j12 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

bmfmvJX{>f`Bb&pvls25QTu>f*JR; zd90MGM2~_?>+2{pR;V^ju?<#W40DFdtyCy;4_|?X-9D}x0S10<++H|Ng923U(s(`1 zH7=;R<_VLOFZGkF!ZuNA2MBZIOCC0y2;L`!Pg!zQqh}m6iRbBfq|hESSqmC0Lja~% zMLB3gGSV*L8tCO@;8zzQ%o3%X0x+MkoGhe^jwyFuY6fd8e1G0Bb3%tf}BQQ z{9OY1L!~meP$RHPolZZ)TJt^&6_M8;lx>9 zmCVcrw7*;?T!EklkR~PvNJ?ca<=`}~vNDcOqe12DFS~jDrU~>g{b5qGE(b-0His1! z9~F|R-ihX-WUvf|U=$Jue)g$tsIioT0xhpcZE~J=i)4e=F6Z%1*l52p zgAIi8mI2YvBiOf<8K$+MQ3{7q001y^*fzR%u;?43tJ-UsKFnb)&PeKW%60n7C;TGaSwwV3Ctcg+>)HR5W@sPnmflP9{JHF;si^i=WeB!8H zTiy(zTk0fiR>()iA!*eGiI^NE3|3DFvN7#{5Fa`zz2O1D=_T%OJ#v9F$@&9VP+iWzskhnY+YE^BXvydGM=kxRiOSO!JMb>k2U1eJ4j68k9YQsEyh;p zwH_G|4*ehPIxTXoEva+95hXyrsJBuMggs(KR{1kTQ<1X&0oG$#5uH%O06w_rUrDia#u*Tb$%c_@O5N`QwjO}TihHu z488GeeGtUuHB(YkM+d~v=TjFdphY+u#)2Ws<;ohSl$#`PfFs%JIDjd~Xov@%hTg(< z5r$mgLraa{1Jq;SG8fbH(Jbfy+=`bwvhr}wlOwa&GKsJ?atARq&uUhkf}@ zst>>k^&mdr{I`C^31tSaN}|3Z*1WFlTPW&u;)*nBN#{`nB}fEfj1TV5tXLdXc}7m_ zKE3+WxWvMQ@3k|+iiyO;WvsE*)q{i&RfSIQ`UzvSeYx7~fXkA}6dHa6GiV~!!iwEWl#DREATuFl$Q;1Qk+Uc9 zQ{P=4+AlGU1!>zo=$Y#5DXN zl@DJZnfnl2rS^U1K*nWy_6I6y1q3W6u`c{tqf}DJ)#7H2(8#@2p`1=gQA@U;T;q^MCn!3vU_`_CIJlU9cq43D#QF*bDIt{kB2Z*LqSTRudF8+?(x8k9Z9NhUPm@TWdmMH zMCV)V@o+nMkoCQ}qn#G35A`WUEm}nw+C}|qf!8Ir0$qOjnAUM8U7o!5C7Dv2vbKg> z!BFr!%i0w7#6{VS%w){K;XkvQ3@l&)v^ARYemd#6)<)iu%0=@rvoKx%=XZ=7+51qU zcYvc*A=UT1hd~nrC37>DoJT@Tt$YtW#?(ud8CO6a%a;yO+qJ_;j^A`68tGPMeR4-fL+_xvsO8K3QYtb7zYF)5Wn zL}5HjJq+ZTvy!w`8JHQ=FGZSESjqAAwSGgGO!zKx2G z*jVZ8x`>-rV8PnQ{N}Ny(2`Ll1$@N=oJ3*T3QtO^#UrzGVYw-71-Wv+<@ccfqr(Cf zA2~v^&C*<@s*7*y(SII2of2>PF4W16B+Qz<12FXxD}%ac*Qzq+N4W$yL=;Dl>$!`M zBWQd>FA}g>KzyWWifyOTSGS9F!0{Lfk3!4rk`iW-i9v}{?{EJ6K6dGWioMneJ4=yi2 z??6lpe}yjGzPu@%a_nnQ2dYr6t-Wd6YVLo@2!jpxkdpe z$rw|CL60QH0mSe5Va40dX|XI-I{zU}`_s+3yCABuDBDCYFhzks2v0>DVNz1k1Hkum z9C!^#PWa}!>=c_aS>ph=0AWg5Uz%_&`(4-+2vdpc1^(1<^EftNC}J4hb>pVi?U%gw zmI_^Sb!|k#1^PfKTU1M+1Ap}1HDIs*GhI_-jb6nY+ub;x!H3VfW=bA1V=r~um^C#i zBuEhoW$1l^Euk=3nLYq)7L2>VvTa04$TZUd!@5WIUdJxEz@?JlZwgQSV}i*nepwNFvF^D~M<0FK=fBnlXK<)a2&#$$ACHl) z+G|%q;0(*vcJJ`)`LT(Ta5AVr)j8g+vR94VuI~WkjZS8;(%$S3+bMI6%vp71zDu1E zoSyv;TC+Kk6-S4)b~UOLxfye(?j!3l0rg@GhIR?+L9a@OnE-fYj(X!sI#fq~5#zV3 zO%Edl${=gt5B4odZou+k+K&=r^_-R@xcZwWLLpAmHSSC#0e}LLQRR#}Xa`Mf6cJ{6 z7YEjpB|s&Tc%W`77sI2MxuyGZ3pqwiN>w5YsdO=74?6gblwQ9DnrdE_F#Z{xBTuqc z<*jR=@lOj`xP!`vG@B++Dx`I2A=?%EeQ8IUJkt1PFQ`pzNkN3{H>FSMJdBwqjkk8p$UZ8iMMVB@MXInY^K0OGlrHI zwW54*i}pz3Y5puVWDO$o%yln}hg!X3Hq$$gcn$Xt{EIG^*cdTdHzW8}R+#oALS+yH)}@ z(BO@!&eRc6SWFF_s zlcWWlgx+#>KQk{yw$JvV;%&r5!nrVbhUT7hGCRS zlSkzg_Y~fr0@;WQYA__6pzth1-_HPSRn*e1Ozqpp7Wo#Hz}V# zk7sk*w_M!utT=Sm10Bk&>a-?{kSl!M4Aa`r3H%?bI5Ozyc!#?8|D|(NDZPyL=DTAg z2t?ql(k0;KN!siK)(Nxr@Xx>l0`&FJ_;FZvdk;iZ3_{JG4Qj$3bv4deV|!P)f=;O6 zE@S)Lp>Nt5$!aO=QpL5-9i+BdTiPjVIpcq#8wc$YdotYy6&#IX{FaR?I5o9Uq5Do|{j(WC`PnEurqDl2)PTp!A1b=(FO zJ+$4?QZbH0Z1A-|($c9@gG*mpoMBGStC~7%MY&>p+Y!B@|2A+c(ikHIB-T6Bz(*>S zrpAfTIMc&nK*>{{vrJiwDa|w;A(P)mVs(k=4SX&D1eAWK&Z@5r15PmDXgoD|`_=4_ z788d|XJ`y-0IEgAP}n6GWKVVSm3d9&1P)U06LK;(q1B#&@FhQSS+ZJUi~=%1RpTc! zFk@=twR8bzN#FiJkXZrwz z05M2bm*XBxQS1VATY6PVSFO+$AcO`*DGX3G^j@HbLI1vc?&Gwcsl<0IRK*@|o+S}@ zd~D&HwuW+~thL|g8^wMRP%7u4nd8}SHsTXU-o|e7H?id)FTo1ue5)CPZ?OnzG6xS- zUr^j`pb&?vd-ksvD2>cO?IQSRMYFn=+zONx4yj1#w@{9HnLAWm{)G01*?fdF6dpUz8}VL7L{9{T!N zCxlytThttign4JBT@Q-bw%(CUdChT#&7Jn%CMde9%3ONDA zWtIeE1~;X^5#jl3F=essu##4J)}m)uw2K+ty9`EVq6EZT!X4}##x z6sbRKFqn4Ru0Vv0ZZJpHhOu;yDZk}_=aASiLOVp?>@mb(uAhpBgbN&CoxSEWCtqX; zr`J#cuOp_>cZHorVk5xt9#9Ov|9D&hJuyUdrD>E|jwhbO}lBQboBq5bmU+pI+hoeMCb!6KDebUswe$AP=v!@}5x zA2+vn0LG^sAVN!cG@2VeOWFqm)8I=86qyn)bmmD)`uM<_>~qX1WOFD+a*`GO4LT7b z6csb)D!%`9<07CZ?V9qt*YKl2Ohefkt%U)F2>^kxdtd1{WOV=0K?FmsFEEg#TGu?V z2*RA4B$%=NzMUQ9@8E0vlCH=xvyC7Q?lSVSk9~g`-*uk8nbihDvMGgj#T0Ig9}GO1 z!64*k6lEk>ENM2hf<~x80KRoY7}s`N&JE$IVcJ1XJ5-C?m&f-Qt%wnVzaQ>_1Ke7f zxO<0`*%yCbr0o+O@PSyxEb%7(c^%RWHut!KIk=Mx+fZyz`|v4K*$O~zE&$D>={ z#|CNzpG5xuaoe67i;3FF~M4T-6+!^soa8l49xlM-y~L$x&z` z*5#~Iu(YI#d}bc$@O@ElA8|=zrK1+W=9<0=0^B9%9M%xd*Kmdq+%;${Zb&7iwe*YW zs?QDq@U-a`zH+>^prbM$&3>Cv?AM8q+=3ADHBbfWZMQ`(!JCkn(E1BN%G+6S?4Yj? zbkLB3bT9!E)ub`pHuiu(QWF#of*(xXlf^xjIx%$|7Cm{*tLg(|*Uvtf@_QC!LW+Dp z;ouqQ8#-j%jh-Y1hB65TCX_PTj)jeQg1@TLl_4km60J7<5D;olBvX=?SCW;$hL{81 zU@u5_PEj5i8Yv$;Ihce=I4g#a2=0giXccr(joy;mwsIGaH+cLT)h@hOC(g>S*6hb41 z8MulZ6qUVB6!7b?$<<9A40-bqo0NCHhp?ytgpTt|Zi2R3SAEK9+_zC( z#bms%1#o#Rix{xIByA^L!r$?DjQLX)hrLk2uo2D?LeU16l+u@4dh3_6mb1%kG- z&Rs>yml6RJT$a9gDGR}0xZ$Nt<3^FRl=F4kuGKY2m9rc~o^~Z_y4Hs_kzvD7)8EEM z*F$oj=Rf|2ERp9(As=uQN{xA8abCxJsg&imz1K?dDa2}9Cu88{g>{saRBPCvi?|>` z6g4}Th&h*;E+ln83ZDy-%#Wybn=%4mM83JBX@xQC0n~Z3PM~voOP5Y70^08}cvFs$ z(5&nF_Hphc_|>2L z!}m{VEN6bJBHB?=r=1oV{q5i2K?`ro&iX;8b?GWChzE%tyzq+s>5S#qZpjY@D#0>7 zX04}t!}Iu!zW(dZS;=`sE`;oIT@TY!RvDAH{K?d7n|&g(=}Oe7ta4g|tNS1?3FJgA z@vRE9b94(at5LS821Kw|>>>myoYd)|m*y`)Qq%t~1HM9yEeDT09i~qALQG-x&td@N zs>~gh^Ke37au_-F?57X4xEJf=rtC2WJ!vxyI^43z}ll@f&PFyFL-H%f1_&A)W0ceo9$(zG7%>^0Oi*;z>$Qsjf^~ zVsf+q_Jz*BU)e)<+$@jnQICtp+OwC#vY+l(h6B{ zG|nDr<7}_Ps**twl{IIp$LN3g5NpoCLs~#yeN(RcJ$~#zqm(T(HDTvTL=bjjx zelS#kpYv~tY;BgV8I)m8#&{$FCGrhHIvGJFF(HS7`?0U+s{P+>meg>Fcu!(*R=rl; z0qd*bl*8%g&~}R!2))=W>Eda&F!@=3oLP+4=JdgLRq5vkP#7-FY>ync{>A1yoVu<` z@vho5_VqIq^ENu!Old-0muf;o3QzYDj)&;Y2Lj*mKNv6>DT6TOKQN>b?fFvB0X@pX zORqeqSJ|F|qU<*E%!U)G2=_f4pX7A=H)9_vpdz_e#K>HF#yZgWw*gVzC3uMYp#d+ks#Xkc!FA&0Mgjn!9%qUa_D{UUdos0JkW^SPWzfDH_ZQhN^2D`@L6p zfz-lbQcQo=^@!C=h3g}i(bn2Vp0S9zr`LbNk5&Z5syt?coiDv1#cN9t_z=0v4xYVM z{so&~>Rh0-?|*kHyW76v!Dt3PfNZsqfBhv*5H-d9RbO6fClzqYp`9i{`!dr<3QfLs zfiTqWWAp^iKiWkY7xNwd-ceIr?@^S^U{inD#9r-hg;{9hCC>p04gE(wP6Tp*iaRa# zF^WA4vm&>Y=C^D2+)^%AbHfR{XNmDljUy3-Ye##K-3&1HxaVM}7BjIuQXI z=`!SIAQ3L`rbR&o8hAIC$PmDw?k?tUuB7x)krGaP7Qt@qXBA}eh?@LlZVc%2Pau=Rc8P)@Kb%J0_= zl%aYC)BzymTf}&=RumcN-v>r6EI<3^14`YVs(~x*mXjKa;kNF7YZEMS#M>xYvz&uzt@1)_`4gGVRI?Du?5N0OE82 zw#Eg{ef*|mKm?(ga}vv4ZyykPFFIP)?9axWu7%QMNFbWLH@E0>Zq-;qXXk5o14t@b zen^gek8^uKNy2S9Nldcbk)uUsx|7fzyQwNH5L*lD5S>*)nS(VWjB^OQGn+iQ&ni|% zDFXT9;R#0{9V2w&Ly?J_mex$SvMLM>rb3jLw?x)-269s?0H191Yh+v0!u)`t*vx!N zz7T@iLwbLn2f|RuEiX+A{*eAg_zGl0D*q8OYWB22ZQ%Bu^Io>cfdwVe2QGrCf-U}f z_nbb(a66Q)f@%Ff!sex*#<(>?vcI^ax{9z#d$8tQM&FXv7_9`ldj{@+vD)zHKJsH~ zOBaE8(2{0^szuqCy`gA0OzD{ALja3QErilk`CUYwAdgUey+s=G+v3{Pul^|Xu!tpA z7myO>AEHQa?B=o2Us_U+rVn;VInk{t^EsJ3BMW|ae+TsDd#*y%vz(_#^d>hb!_)X~ieJ& z@~9VpNv;p{5^wn85Do_?7#@2dcG*(KJ~VQQVDAj89LBP^ehh_xy{-S^Ph55F|It1J`7Wes#)0E#fj{jval+J5 z&8EG14?rI@+|Tv`4^GxrngQ(K95nL49gK(7OJ(|_5Ao;Hc=6@VP18jaXI5sV-s=D!-E2-s5^Gb7^T6sGxlT!zA z6NT&Yaw79c**84bkc#i{H%rt#Hcv-lN<1|uO87ImoJf#dL)J8_Zfya2j<{n3*|^Wf zau(j?E+BS z+j>ZTN@sG|Rhbfffka*TaihFG3xM#_ShT4shB+wv5nDrI9o-Q|kP^R~P5GXcJuPw` z`kZEYE}d>)SQR9GImEuS(jKzmXRpXt1Sw~QP7uLiDo^?h|6!!k=Vz(yLK&xNyg5Bj zb^`N*F#z_fbKq8|qB;EPUHrJ^wMQ0h>^STiub)uQ-`td7(>1WL*)L3PsV!W z3x^qFL!}Yjdpvd#?|_0j`Tt@Ko%C|i)A0!6dXTdW#ZH)iaH5ktWLd3kLzASqBqm)t z2kS01A^?YrE;>tJeezXl zU6F=%7L84!VG6q0x%~v4IpC}Ei#xhuJkoRy!Nv0V+(~f{EZ_&berY$`X2XE7jB*i3$V zF0z??%BnJA@p3>!C-NH(eQ9Z7D34MHahv*P|05!K{U~0QduBDqTk(Rh+m9O)+!*h~ z!-s{(?u@{05dYm4N2h|DSfD)wEXb$4Q`&6%ze&3@?$yt`x3?fqUKAFgaI%8~;v|m` zaSrfTv0%+H=oIrxDPJsDhabZin`Tb;#w3~!|0Y>m%Qr*y?4Szm*w5dtjpUic%KXat z+UGOZ6Or^&%a1RE&=}LMadjsYZVMK3ui;uJ9P0>~f7Hh@o=^U2y30^6S&pScT;Qd3;Ss*#K=PXB#pVIZyS}W?AN(FRb>ef4MzeGG;Rpw z2;_?E0c;rx^%Zq?fjsEsxlC(YpXE+SB3g0nH^z6O={7#YFD`de#gA~5pVXval*w(r zuN$nL%WlYJwS`uLareKpT`c92_z&3e^=SpMf`iYmq@Wmm!4?Pr=-m$aA%ME-RkYr& zmF0>FJ%j;5|McS2faw{bhG7hK1qC{Q=UjL?no(&qm`FJ2|H~l)G3Nz%tmSPOWEQQ| zqU#a3jV0`WQbK3R}fdG-Q^{u=@ZnI6x?LG}XTM=d!^>2DZWzRT(UsVTZOM zEe_Xi7_B;PS+6laQdOZhSzpVdMdB?xUmooEgd(Xwypd5E;b9LYe)Lijq38w1{Tw}$ z@h8*;$#QhmtN%nPGca7JpT18*NQ*RPwXr=;wCp83U^nOvi@ncY@5>VK$kKo$ljx%U zAI0VzwgS~8a?VovjUP_=H>qq2ORFlB^v(gc_z74sAsUornvT#y5d>f(24&`C09!5s z01ikcpbdNfFRS$btZ)ur`;Qk-GUYl~X0JnsJtWC98L3gP0>w`Y=f6i=g6p&vXW(7+ zB`Y zW{3owOs2LFV|kVmP&Sp#6>=zDnsv5QWu!c;ur&C|bf7U`-EtmbGv2q&tH&_$Uj?f6 z8fz3tU^x=DTToi~QaG<_l~$|tdO$=|8K^c27)kL;%%{!@R6Dmg}6u zbly4alyrAQa<#7&#^5)bxxVtq=fb}F@C*WVk;ZpD^xi#`zoG=61+LnLNR>r zTb%vQ;=eV5zvLuq9{daw*Z!j5y(Q5oE7&rS<9J=CFP(YKuEaL4JPf*glF~xXh+fSN z0#SqK?*Fp$ntUv?4j~$pl{K1%6ablVW^+(w!*bw&K(IEA?_~0iW9ok)1zF*HFM0CK zYWL!KU{ke%tndUeaSaY_Q3-%iH@AtRDJ>Byx>VDnNlF`qQ9C$6IntuYbE$$8+;;D=uM`F3zZc zIW6XBhZamBa5t`O-^rfbBCV%Y#e>o!VE>+|hg;Vq;Idq&qC6%zAsUp0qK?Bx5rlvxi&kXV#ND7+ zKo$_pJlXQo=;mYB`_Hj>$4fN6nmX5~A9IL#OOtQ3HQVd9k}aUsGZuBr(TD(UY3{ho ze8p{jJiLE=?ur1ib^5n}FCXA?jRKoi0(!cp6X~4o7z@fr zeg{}_Aqh=k%`jdy6c>tpC!4*5RicW}L6QJL2J%zDR8^*0Wy%GG$7MeseI(bLzKfx7*yA&4LR2rG9`o`iDJaf=0~<6<*B#-a zlb)*+j{7y;EY@S6s;<13KEEF3N8m2-lQjl6LzQrad`Pm$w%Y>knGHh5jB(DzYp4v*9e^41 z)(@^WZ=!O!Y5(K2^9J<|SwV>m581W1o1`=ge~atBwtv1N0FekE=lHW$f>aEbSLj@5 zR}BtdWiLTTZ2v*X#b6y>9n?C-F{35;HdDM)iG!Vp4 z8yjl!t?8CjWR?k*60A+?qd!PgV3T0Yh^q@%YYlCsf5r`2u2+T$>8I-rIZruN+>`%m z^d{utGy>Rl<#Q%=d1l+!D#OHt5ck%{ir6|HN1nBFHnhAa)lGZolx!4wYj+W5MQP}% zo+}c!BL@;ll)wOa3l`@&4l&^NZhQnM{H+5~FaS8VHq%lzp+Bh9QXnZe^@^w=$~5?nEpv z04dmUN`B|)-a1c0_BYL2*7w8mGU~5q&4`@|5XhQ)m7#O3Lww=3Np;!cty8ElOV+d| z+fuuvx>VK_O-xT*C162)mDU#_173v#e6Z;OaZ^BufU_W3vvk$NRxSHPD2Xzg0+Cs> zgwt=T(K}bzK`MD*e@k8G?z4g-6w1@^_1Ly*6xN+${7h zYnbENI3I2TX@4Hvk+T zwr3N-^X&nx;1 zoWJP3cQRL0r+ASl`w&*#N~xm0hqN8)VI--<>bOkp!%93Dg!U5K(qNV3f;DV4N+6E$ z@6qu!#{2$Q_b)TpEU;`JH1gv6*5#s#a$HsR|eu&BD-g{}aQk=I1X z`G!(MEXWn1ni3Ga0x9M{@2`4*9S-dwH!%HW^OKp03S$TkAu5z@l90+mu#kjN5D3Ij z?NM2BWuOZH5V~h({mZc^f_4VPhw{&;VLb8hp9$rkkCR(IV`RjU1yNZh_VI?bZGPJ5 zP`L;w&8Z+VTc zcas*G;<35wlF9^7jP?j%Q99O;#o#q3yWBEH=CSlYC($&Rp8W2w0F3{DKqEP(LE87l zzduJ^Q=>wsf39acj|Mc0`vjjeVwN-+R@MnyB)b+7R>o>DMgmYNPJ5MQO0|krrC0)- zHL#XA;#%3l*lc3n^|aw`NMXxSN$L|+Fy`sYR0z9o-nVHwg@D(=Wj(<+bV^h}X0t3` zK*W{O@)*-SLiQ3{CA8qEL2$Mi(EyTkIk6q20eQZi@jpvQ@%vKU=30yWy!%-e`i`dh z=#JP3vwFqw!c|356aY1`V@K2R^?93f28MO#cL4Yb05-e!mmNTPs_$VX2V zix<~5fek*#6)k8fEI&?zRP$SALZs;*5wI1km^*DzN(8;w{804WfQ*bYVAA3;d&VA; zFn3)OIT4u(5V{v%_d1R#f?`n7n7{X9I}3`>AzDvxMR;MvuCTKA@VYT!8o-r$HSYnu z0>jUGV%pCVzem=?mY>}lA{Oq%ANx8DERf!={?AXNM>JeBRbP4JI3}G}bC0}0uUAc7 zXEhCT%6E5~RVR{ciy9FMP34_gBr>5W8(DU!B&oz|(9f3@|l_ zfwdBw5g=Ciri1H!>}M3~9MPtGK0-wxq_Ef()GCx~cMRKR@oO|X2-@bJl;7bsS8aae zSeFq=PeGubA`D@Pd%C}gO$2&ybj-Zv{GiziD$H8-Fy8g9R;c4`juf|OHh-nX2}urByf3 znUxrEEaurB?W z_a6BFw&HB;Xa0DAy}08#R?j57pT2Y+u98?WDyL0#S*|Hoiv+Yrx)nu&oT95uL{_vd~$5J}KC9o4&iF!Qi|bUaq7qh8ykQ z7D{^kHq_@KD06J-2munL)og|a8)l1W$Qb_Mmp;UIt+#u zn~mxz9F(PsRVFmFBzhLKcQuM>FA1Oa;bi4#DdG-Jyt2j0`u@N#0UA}-%BYaUp4VVm zVW9yQoF2w(T=^VZCD>L{n90xG#c@<30l-e#9$np$H1X?+vJyO)7NgRs(9{p8OYot`X5cG@>Z;&`48kA+K6v#pf06~Q} z+F?cVyGR9#1uvI2xyw9ubP{1)u>&fLNZ1-h{XFNpY)z-E=_Vk@_r;^AB}h(_c#x5B z!=6Y)Hp5S?l@tn#d6>|n3jw6TCJTT-9q7XQ=WdmQslZoV0E z#}4?W?frRS*M6ECNOR}xu+=Kr3Rx-HdEGSQE-Qf52b?xCDZ#S^x4 zdW1u=9-ty=jG7A#2EjuKKtq#NzVlmcSBOgxAWIx>;aB{}{vUzu)wS6~yO8kChai~(Ut zSR_O+m;~OcNl@j0t+fdt1&1^1HoG40?fKhYv)g-pE|L5e?8J?$^4sV;h6|xfOv$bu z0t!uUl4sp4tvD@sjkqL~%oI|nfIqLO-7!;YKF#~RPg>_XEEOe|(2cC_rww~{99qc{ zPmtyPq*nCb+~??{@C;A_EcaK3$Luxi;Wc_#ASE|B8iFuVY?5m7qKeDd*p7RQ;_XSn zmNXJarnx}Ld=cFsZBd|11dTP1V__kPqCgOZ=CE(ED_ySOfFLS-2}CA*b*5~c+3l;b zR`v<&)h+17<1HfEP2wM+!o3fQ{}NQC%EmBeq*R7!u7y|-4vlxl2eI~l$a@8lsGMbk zlV_-D=N2P&6{UnjoXt$OP0VfYY(%1xMD=iC#qMccw$JO~TAV_U_tns==G@3~sw-LZ zPFj=$o4^z@XD4Kv9qx6PhsrvhoXVSx|IOUtsKu&k5 z1kyloAsUpOwT%Qr5`bu=>fY|bYQs{j78VnoGW_BFlgyZ8Rv*opQEAsVVcIir&;wzD zZYO5Cf+a+~dz9lR`0|WoXLdP-j^|;#?8sRx1PcSIgD|Nq?@uo@K!(e;xkq(@I20qh zX2b8Vseb>>^TexW%2=2*3mZC;vykFlujzRy zt=E%#c*)_FV*)l+4;6&8J+vs?kynhmsm7hyu}YHy<_LiF`L2z4=d;-8^W!au4OuF> z=+T52=pW9H8T9Lk@7LCwd04*J$#i{q2HX5*OW!*hcFXm1=WO)HhLfuGZw$Uns8zC7 z1MSAENu>-MKbb{;|U;J^#I_NhbxuGU@NZF~A`jl$D+kgn^*gh%z{Y1aNIz1s7W! zLI?%&9O<9@&-;sTbKo`xAEa9CmDT}?w(X`TrYp0(N}#x}&(-9#pCw2-|YIIgC(Pf>0_+e z=(6QD0}k9K1wzgc!XSXadvE{4vKUenuo2Ys9TgBM0V!7{IZ*B=CA3*FWCIVFKUSin zt6H|Vnc^=WqpDcx|7a2B$#D5oDeanp9fb0@CIF~oOYaK%DIxTwawumnN<*cq4RP$| z5>x)u!Y{i)Ihqpmn>aVnF%lTP`(1KD9Ce+BAvCWuPGyQiZ3qj1)cLQE?$q5o%Uk0p zKLP&jLR+R-pwK3MB~4a%Lcvh7d@xm8>&xcTJlm;@0JoCTp<8M2Ww)CJn-Saj%TBBa z>bKP@Jl4|_Si893AsUp8o{q(Vs8M2I6C;B!cxFLWD(!THP*`kn47WGy=>`KZ{k%wy z{iA72-?;P)l}lyM7y}<5_>!#D%QKTkzL%gj^v$FdPWdOH2yQ`p0)V7M)^@hi>10#k zHccu}LsD)J@^PW(pl3d^5wi8VECZc$N?#iIfRHi1nv{Tm z=~9%;C!huB?Gb~2YD5SqerrqXt(`tlb;y`YX$ppz1Zw5v+NgIZ9xn*m{+fiX*bwH#Md$>1Htp7{IQW6vVx3UqkA!d^&j zPf84FV!NUDJR*)1mBPxo7x3``sbQ9I$rSsT{T>HWbVeK3clAba{!`A^9 zgU+a-LIPW*j%YEe2p$A=u^%yo&OiGXf-?%)b&|gLhIv}R6&xWNl#P~<#(^-QJd*+k|I*Qp%DN-g3k$mYNxlEU7n<0g`e^WHLYrRn@P znpNar+tpy&Novj$n}+|4l!aG&(N3EVdfCl|GhxPN>Fs%0RqrbR+Y&;v=B`4r^8^Ub z77hsh&b&-82`+mOy)4+>LbEEecBg7uJ(P(VVx=-5h&2ifh$R65bn!N&0E8ewga4Jd z2^1e)>+_g?bFU+II&Y|~_Dxc*^;%^0kTA)CxJ{Sr$4O6D?ps-M+fe;8WaP$I{TC58ISJu{N`?TmUgUW!8 z=fLYhXv}8w$27dS9G0`6ni{XO@9!9%^oveA>fu=q>T+%(F~(p(KrluwbvWLP74hR_ z-BMjxlU~{ziwoNpGovqXN$WIWHunA+9x2kifbyj}J>)@3W`k_KRhXu|gF_{*MhUiwR`P5E zvexQJ2-}>%inPHQG&Kqwh$8_8)^|r_IhSk1AO?hu`1M+Rr;BeG-tuFf<7~{j$m>^c zJlLgwF1q7gZ)>fR%NDn+TvDX6nU#XqU=<$)mFE;B1O#zhqSq>hMtysvpUME2BvKnz z9UOKZc-}Ame}7!w1VI`YFrqWAh=e`YI#8uE^OY5vyo8;n<f^&X*X8W14k0|sa=ZVq=O>~ikZqjwU`YPCfKsKNqfC% zjv*S9g_4rWfuP9ZKogtOTbbQT0IZPG1QrH3;+S14=wAi&o`&(dQ*hY(f2TYaJw<_B z=!Yr`k+25Dhdo+GnmE&4H(Ew?!nzTOoDmY-M&QR=8UZ#H=ul;rnK7CkX-vut4Gu>EG0WSO--c2NN-ZEP zAcN_(@H0+nO2Yct>G@p7it$_zKeJQ%}lYK_e_pkEwGjwwj_T@?l|YNivj>gL^DFWgq_J zbC~=9YehuMt47cp+GdVzNMKVA#@nf!!F|9EAsUp0v5bU)Fp)w)5~Na4@l|LxMc9C9 z0~CBO7T3MCA1zMc-F*SF?|p@)@ot5%ocOIQ>CD=uG>((zL+$X1Z}O|pH5DrfK8Yt~ zI%>?Y_&&sCZ$2_uF+_7LuJCX*krFyr6$o7?Y%xevaoX7ayJS}<003h3DW8pAJ13ux+FL{^Kwc1(wIBKQANER-RnKtOkns$bDW6{@*A&t@N?Ez>#|P%mNg}J zg581W11Xn5+x=RdeZDV`jv_{y$f3ZH!hjQn=GTQhWvz*ob-`dP5fxgi43AdmRNIo; z1*cnPsOR6(n!&xcV5a`BCY(WlglJNDH@HM;2(0+tFOwOI5e=dsjX~}O9LO?bKz6x& zR;wN9xplD;*+aK5)p>@|3WEFZK=!)0W@zc+y-~Pv%y`8;KdEr#UrDsXVwJu`uMp&bo zKPsZfVZb38l!ZBu149u|BtR24q#?~-%DlIUHK14^54dfe%R8@h9S^}!=;o~Qj*z*J zq`&Fou+f=$i?;qe6ws8dDPZcB+#K}XzeLSzV#HXqmB>oqK<3YKP-YOkgiV_P@gp_R zO6{ac3$wK-T)8yWX{5hiNFv9d@5k<5q=i49fEVy{1%z)GsIjETF}KJNyOHe3b>hfo zd)Ym07jp%!%F8>+&andi@WwZ^YEX{HS*@;%%q)qFUZ7=*!LJT5GIm{evZJCcpa}?L zD#XG;Fpxrk5N4HXs_$n4u{2mQYX#(RD8b2$q*bwgs{3ssNhu^=|zd2`-e4goTeJ?fIIYPHe*=iHJ8)inU20x%Z%W4>oO&?bNMhvEgd zvPY=%qQr`El&E3l^j!zjVktSXI$HOwp9RJ5Phd`}6Ohx67g>)SsAYvG44Rs z-ptv8)%Me6Y7W7C&0Fb}CB{iTaK2Tyw!)MMTBo9$8p-tA*GpSsNL0?ssHv72RLsC| zS$_QyDmnkhKlnhY^f+I(vrYNt1E~XC-1A`}z`b&|VSx(0JMOa|Cu%yQ6lF@KNJqU= zphBZJE}-Bc8kB{xl>uR)#NZ@Zmt51DX;W}o>aGB=7ko@MnDaccE=1CN)iCaSO*&;MO-eyU1^5m4A1o{XfQ!REy%c*7RSvXE<<&yslTN}^e2Nonz{Qglua5wRewGInr#XOX9@xJ`dIoCz^ zUu`zoTm=&4*A&OBpdNyX{5zsaalPrjSGL%REK-))(9O<9u90X+lDmTI2_+jcf>}b0 zoL?#Ec}46~PJjiMD)MQVGw&VM&NaIUfB~oP`_5^t2_p4ncaRBg8sjeE>XOws@Si*! z84#9eK=Ksl0)}#SVwyGyno(kb7ffTo=qzL*5Jmz4TQ`LOLdvj|f$z!X^fgTn;Dx>M zt=ByBeBwK2f39^EG>f_nunRxfonuHmrg-)PGclUDBUN}Ws7KDooP(AOR4Bd}P) z{rN&7SSk^@^ON8VjTOKYgK(sp?F2j25M+>IRuUMBEUF;F5NLCgAceb$t*-;B=*ev% z6T0m5ofIp$Q)T<1uo8)zSex%ZjtmZ#ngLQb6!q#|P zjb{n>*-Kv18)-~X@|wAhAsUorri{r!uuv={7?=d08I904pt_3`3l7~I)A?(SEL3E} z*%^Nw}J5~q&++k5BCIN;U#g3!V^!YDoLz zQ>TS>SyKtEWv0^-u3b@cBLg)?)Y;q>!iDko$7+El#@L3YO|Amjt;_f$TlKraN&EFZ zgiwT1fw?558nQt+f-;uKf-z8pK_CbMsn2@eS!qiM2m$7Cvub*rezx(STBSGA(C#(# zmX?Iob}oZ2nXx%6owZU{lfnQ*^GXN~ZB(R%HM4NG_^P1_Apx0U?IW=w?7sNGk_Mag zu7e910)&+F)10ll%x@hQhFdOlbdRueWu3{msp$7UZ4^Z zW8st-Pvo5YO0Prg@jsa{71VXIyL<-KyL=j+Iuc_LTgx3C=T1yX>rY;yu(>=?Tdm=1 z+bRPGAsUoTs))uzFi_%P6T}8KQA$x}vJeXhSFc}6SeN}|JwpKP*7P4%N+aHG>ur-y zO1JWyY?a-kj6l5A(@2ySg#0hFzlQ8CE3Q82pR&u1A`0oPWGKW%-l7IJtz-f}J^M*| zR1kpK+nMb>5sCMF}B{+ zocLGpfOJ<9@tW#E;l3A?aQ7M2W}T8uyjAc_#cXWSsG0Mh4pEKJWQ{U^5~PA6Dw@iG zvJgUG5rZO+1yQ~PfdDuK(APS=*~;OskIJ=RUuuu`h5G$JMwi<0-B}{cuF$~Hq^`cF zIoPJ3NZ=b<;y_5sr-(zLrbvs2wy=#ioYPm@R}9Za zG8sbVz#1P@fPVOcJO zhw^1^l&$p$i{GcICHY>CC?F}A>7?U~A{io|Hjgj3#2S7IfV9?b&_6nvO7<>9_V zYi`)`+2Wizdpvca=e9z2mH10)utrIcOMpwfX(M-s8m_hwLN2W2aA;NvgeM#&vf_qX zrLZf9M-g@qtm~{S?5)Zpfvb_b+thKSczuJB$k|hH*s%2FDPN zqTcT>ldit^+w#I_*8LS*Mg6R%dZ!=qoW`*^13>~z#Y*dqI(p}t+!|C-rpcz?dfhmK zh#XJ`eJ}CsEpmj(%6vKCv{xelv{PGiDALPxsVo$`+wmKM5j8|`or^tMe6j)W5cmu4AE%iV77rY$6^$w06Jg1iFTV9vi` z<3O^0*E%i!t$(KTc>Dmw{9oV~>% z9$>9xz0?%z3FVBV7?m6$8jH_&|L6G|Xri(aBvB{`1!pC8;B31Q04ynMxO>mHJ}zD- z7u!w=NKVLia9*4q67UQG5JGQ}fQKro+%oT|iovWT!RXy$F)K|(;^T62@%9oXi#g@M zW&n+GvtQmjZ>QwtXo1drB=k8BsSs-}N``2V6u92cAzk7ExpY)XwKftF0pf9&B#3)F zL%;RYXJ$Bz9IE6VN({77@1s_*>s#=9)%F;vRFbXF&Y7uAK4kR7j zLKFebM0c=4l=1+XjOunIjliMa33(7f!#fir9O=r3299McR1N_Lm@|jqxZ*=VBwt^S ziuF^8v}Zj5*Yy*yjPAx6NTyd|tD5VUQ;R|Pqq-B9bYJ6Jta@-BFR6OY>y=?m^6~qZ zm9bL(i#0v)6NvmE*)AqBHG)%&5cVOMxOmeE6Zt%cA2$lM{sw{Qf*>*>i^&31{x+#w#2uT0M`tg0L%(MWQZ ziM-4u$`k9~pI9oCH%Ll|^=d(xpU71%%!CMFL~JPIkJPtHcib zPk#diK;D`FLsva6vZSa)k;x1So)sF!phO1yo1`Ykg$@f2-q+r3-5%3-&Dx@k9XSmi zhepPl@O4Y!7Q1G$+UI-Jsd|dj4F67t+0-XXw`kWyGzaC}v>g{Guw~8P0O%=p=Wx)f zpOyDyQQiT*6=7Z?od`C#M9@sdUpE0e14)UpX{7NY$C-FRz`qX~GMO+qAsUozv66*g zp@?EY69uU4%IhTE%?J<$hUYyvy4TeH60`;9$hyC8L6FQ5+;=78x<8E`wQh6KP7QL- zCX;lYT$FP6+!uM6$lUQTtin`sN=XUssBozpEhYKjXKPabDU~ts`v||fK>hCjS)rPR zAbvo{DIAwDcXC(B5PxDz!hsG|BVjEjx>3jsu7>`r`=S4E{9Y`yOR@5-DK|Ho_#=U( z^y+2KkEw)~F_|LM%o8|2AID0W1Tg7ZadwkA8%p}ZGL*=IAecyTI0y@m9o4V|fhldG zz#8M;YtSkraP!iTR^zgAovOh1J8m5;-{xyPW;=T#dt|bPl6r_9RETNkuAwwxV`VxB zRL{*9MuJKMQjT^4nOjp1>-tqMMCf4v;kH4 zrgGhQ>(qB}%39od#@OW7D^B!`Ts2-Oh#jNObWaL~Y44~bDDcKI4TOUt2*6B&QyVZj z($xt777SUh+q^sKn+n{CgB(6%?nL;$U)8N2(PEwryHiuUl}Ivdrr=5f%B%yOb!z)5 zAVwo}4>f_+pdhbz@QX1UwSwklZoVM2Q7nm`N^@ZLnrQ|$c#iY(ew>>a59T);8AO>K zeOgH^oM2zfs__@CMooi^Le*Qj5iBxXxqSI|`f65)2Cxm8>F$XbOc5hD;67ZGzH4mA zV74BSVg^IadiJpH1YU(qNY3b{ZAS1MAsUp8t_s6P3BX9ujq<{&VQCp)3lqgTue*!2 zHSGRRN&YM&u{mp-kYX)c3IgIr7F9taaOn@K&Da$!6^F{0Yu9rxyI3rA9`mn&w4d-%0-%rQpyukkiEV+O7HW*_Sl5eKk|7Wb18{SqtTjHeW|a zj?ty|$_OroBLzfVF`R}Wi9k;Cg{b9CimSYhR_1^K>ib!14W#F(BkJ}(&(w7%blL9@ z(VyE@+%ua0SXNWu`JZmYs)HPBB8ha#Rpwl$D%9pCkuA?Of~h*Py#l&?W~8{Rgo;5T8EEhF#9v&`;lLtx?>-6MZeUX?W2$e8<$!$DN?#sWyktwl31Q>#-7SbU6|h0Dp(~vCD?@s@~aG859eIm7gC`Y{?8~S?W#T=It(mnD=@4u z6Ys#e`Y0*Ihio9f^x<0!YZFoNy<;Un;6*~*F0GbLvoRF?;%OyIdjC-$v2XWn#WX4$ ze8mtbF%A+c3zaeJ2SBycnPC9Vpu1FKgFbKsWwDZsA&CG$ftmKy1w!f;0I}V(ur$`R zZ<~iv&TAu*o$&rAp`%8QDd5e=)t1`vTWq&1_jPAhiiDNhSxC;EHD*tb2kC)#RNcpw z(@LR!R9NHwd%v&8`_=(OT9@Ph)jeJ`yjS}{wGII1$Nmw}`EYHtb)P-bDjm#y@!L6~ z0)l1~1^N#on)IK1zpl2A+ZOq8p7yL8ErTHQndpIAsUo@suGA{Ac(>+5QJ7{>HvvXRw^I?|FGW|Ks?)z z{x2?f?-Sn8D2KGBkPv$J= zPQE2+lAuLe*@!j@7=|PP1&sB4(;$+Ngb6@BFp@yqu=^fHt9nD@u&he>tFA0l`SVu3 zeK=$Wn5(ab!23~YY|y<~#4-+6PSAmK-+c5+txcsn)~sk}HbqF4ZjrZIJ6XSS zsS$`#_6d3!E1pgV>%EL)MCy(M+WgRa)adaspe>BOEk;v*n?kt0T>YjDk_QIt!iwpw zL08?GSRYtoU}EIi@Yf?v5Th@3te&Pf=#s(iVOlN4MoKb^*Ftg%t{fp6lzozv%tR2x zU?AR=lrTVus|W>%BxLH8hfx6hqCS}FSbr4-{LA;P*MyRlWy(9n-6B~5$D}PMKU@s9 z3$@H+l;RYW$=xE{Mcl@m2I`~kRxbT8?u8;B{w1D1A8Gg1kt zwLH0bpJ#E-)IXVZJ`yQinj5bKSVP3Ue=%C5>BvGSheiq|Y8r0BwrJf>4?@pbOyeOx- zSEqN~BmX+`FrLHJ_5acLM7mCseUoTB8A%ZY|m@tpM$@*@1V!_Pv&gLqF32x?#fv7 zJb~0IUx_e|oG!Q`tu#hjAc`Xa11#Sv-Ab_ntbhSe^;eIerfHV_z{GI`Z5UpiNvd?N z1PUc4AKyh#qLgNf5|d=KDcyZCYSnfy>^pF&x#ea05<|+GDtaxl+o5cya_2!k_*qQ2 zNfk~kR|Hq(a69?}{O@eP+lKgat554QZka*<=P+#s(h;os9lNLcd7D~97GuxLHPvrz z&q7C~$brB;S?jay)!6{MNq_dm>zW7WgN5AC}y9vTngaN=I z8kA+K2*pIOkb+b<6U7NPRA@KGrJ>IBPO zB0;z-AlU~MH}`sA0lcNZbAyyeZ8w0VK#(-Xj-{nU4UXW_VP_~%4g~wbY~Es{_+nHl zWO1vT>tR-F0(7bB>lyqvH+F|VZPnxQoLQwJ3&VZ#2zRz6A!R}?pvpv$gkU3joZegA zAI^0>bFL5EphA z=wDrr#*;X9XOmSt*|m6#!PawJ?q5!&?xRMJxo)PDadd>_B$#2-84JT1R*GMwp`7mI z0L__ds?6&bun6wRt>PNwPBky{5rSLjP z!J8T!8>{%Qzx5VyXc~}(mWn8*|X0ZP)MWhs5Pn94Hfsn_SI-iqRp1iQ4K5G+O6t;*Pr(Q&cmHdPu8lFl8HV*QJ6n;gS_L7tz-Ouo0Bjat%aA5de|n%%yU! zXt1yl0b?P>dxwQm+t@3FecN=1`1}{jd%QgY_L30KlVz|P9B^GsfB=&%+AXBos*@X< z?%a~Nkgv)sDI8!HqMA$7DQ}+uxr!vO4z~#se(`(@T}O4v2>_uzQV!HCl9d=ifsAtP z1>5M~kDhW7oxL`}?{bX5IYk!937|mc)gn^~gwZAA3}|tzN)a?~DIPkxoD}(IA{Su0 zZ=+k-VSCuF3O+53x;WR0lBakhBCt4yTzwV!;~>?e60@Yeb-Mhz#$rxb&i^lBmjoj zta4x}2#JkQunxTL4*rDVnil_fliYcIViERt2kd{MlDFbFm6tw0QEOe@Ejf7pxBDe6RwOF+%X~MYj9q<)u|+80a9-knx}_bx&lxZ? zEd2Nflile5P3d4T0Q^30oQP)0$r3Nfehfgp)lb`2p8fo4yng5mw$u+~V!>q3mlg&u zvGcd!`YIi0G-`Evu`p8E(*fV+X447*$!BfElHm$1006MraWL8K1)E`4fpSbMkUP z7Jh|JVq2eN_*MAfYk0XtQO0xHut18XFwzVS@dp&GM$$nph?U! zZ+_duC@Flz?Rc-J@%6o3@0YOaB%7`T4YHE$z|fk|OcJIgD0W(Tl|t6VgDJ?X9VM8A zVhD(!FfmGa6qQ^Ux|RSEcrEZ~x_1f1EbR;Hyho#Oby)oNitX{&jhc3?7KXKu`tWM# z_tx`wwnhyuVJ#(36z-199pJ4=>i}dzFsGdzDw0yB@SKL7H#xTqX4m0g8^50_&#p4m z&+lj+GpSOBY1SXUPDBMbOC*?1g%0I1@@t;z?|J1oDGJA_o@2g>z%M|VY>92G(!fzP z6bHmR%fkYa_5k|ydp+CKDC9`*_)NDYNWS1CFgxjTr|YMzx~){LQ#b;^$~s4rb1rV}W(e2J7v9TS(=)1YUm)GEa|qPhhLr(QZoBO{NsgY*Cx~a|A$c+ZCGg_^)>1ICmqSO~#a{4ox z>;wIUfaCKWw6?VPN5}s%1;7%*;qgYMgaH^UAvbTBK{q@4FM{Cyv3T}Yf@$s#59E(( zl>qlHae9}UFgkeA2VtD7EG+}Q_WZ;x&Hw^7#Y=;+(4>G6bH*|mv<6*-B?f`Pd}n6j zy%t6q9ej$(Ip}Je1moXbuBL9;jFvF;tf<|D3QP~bU{loS1`)LoxPxq3Afg$}U^)?! zjEi|aR+E9PLKo2?tCnLe1xW>8oc(0^!V}W(`C9Zo}wk@3@XtC~&?%_LZcsY&2 zu&aY80C$FMPR`wbLN|Z$gu0JAOl%MwAsUp0l9I_nu+W4dAPB6SGYY~D-PFj?AOT7% zRrIc0r1wT_;g9GoeyitoxpY&kpI+WK`tY``TxqZltT?tmFSbKM`=uFtktRcZ_xH_2 z??mikZg+|-g;jKOmDUrGPhSMGK6rDqFy z)}S-4SXSH~N&N;ElxC%pwcrtS=7JE4Aps~UDBkE&>e*-@0K{0XcMQ)VmHF3jk~OAO z*jhe6nQ`j$bEg*exXR9uPP}!g$m}MdojF?f6}#HOn*nO%YAP$~1z_c5uL2S!mU$2{ zqO6a9qA9#E7(~k(nZQc?yPrG>$xF)lHMX)CNStR85M zJ00?XEbOi0lO*Xba=2+wNVB2W;l)K)nGLI^+>2&|7>zB*l=zT(YL?wEaZ zi~N^{MJ?T8H;=~jnLa+wNm9qdc%@_{G*c9p)_SPQCEXf^d276xJJy6Csjii*884AE zjAiI#oh0G=<6O#fo(;(;UrVhh!~iZ_obeV_fN1akU8rhF=+b>R75fhicH z(4#6x#ig}p12~coZCemmb(1Az7H5*zo+2`m(HQ?72}nEnAV3p0v1IeZN}8#&d`Q}J zJq9QN6>ZxMo+Xe7fCY^+l^0kAymRUD#Z=X{MJsK2*YdmM)OF~?Ya&n)$6~%Cwi+f02fA)xq!|6rU z>SgXamm2XSIw=!uqiM{R_MOG8)vNcyBp`M~Pmg94L8YBUhbftbO$k-Jz#~v%5~0SFphkxP0733Sn%_<2 zMD&PbK!o5xfhZGzK!j2^|7(+CY#j@+_+K7ZLZ4vO-9M-Z;`D&z?q3dZrot;vS&nCI zF1ZiEgv77RrVfM*fEh_@!f>3DdHOZ3{}Evwa8s~(=Tt8F_bJ-sn`=-}7cMZo$uGFL z6cJG%Z;)tua9EOvifBSnc}?jbBul!RYr)0VXN$m)OII9>7)>nCQF_dF@rN9waFTrF zp4v9iDvi#KC6{e+JKO1A=pfCdKZs>FK@Eas6LsH=F5JQl@zfA1j~qvDY*FoGT`{^a zpciu^eWEa2;<++Q_8EH-Z@)yQ;~2du%%Y0?NU=G=tkg;T59))p9#Zn=dhKs!9=ye@ z!P)J(i~h1gkEz&}4}Ex)_ICF7W5&vQksh>>J@zs|)~_-wmJaFY&o4xZhW&c}7-M}w z^m$p-i3b4yJI#ksP`Hlm;5^66T7=3!(1SmX^w*6K4Ap|1uPtLOLK7@KKn@o9lwinX z=kolykG+%WtzP1s{8B^3WPZOAd)-f$E{>!iEI`KGL&X56sA9qi53oU5R zx@6ZHe=2e5uf_#w5MHqh;i@tff! zTZQ&DL!9^S0)I)6A>5f4O!dL}>C#Uh_-5E_k58#eK9#`O7DJp9~=pAmeOFlNp&_XWox>Y3c zgxoS(en*UBkt4edUL=-?Mc3{DK{(N?$ZE}eDVgArSeW#OE=c2jyoc$Lv3x9|7?qK@ zw379~VGv0znwIOO(Gd}{oCXPWOf^%<7Usl_ExR?a9ib?in9qWzqr0J6l{`cfIYKCF zTm=dj6r-d84xI3Jz+6`jjFY?1P-Zq8r)a)p05hIvfF7?f0rQNNhBE=hV~}cwoHOss z2A3VZTeOn`9>iTS)o6`e_D0Q<6(?%Z*tes4h}Qfkt*f2ql2!)x?%Vv*Bur=%?~W9jUB#K5Z}yc$OXez8IeL)>#&B-(6ZcRA z@&vR8+?DRe@UtU7>XR8l02?Xqht+8FosdlY;r;fa#d5hAW<7{%nrh}O^IN_Uc%4L>Hr2J0s^~>+Tz{yueJ^I?MuJG6^6>pLCkNJYpysoTayi>1+?vRj19x2 zWz{x<-gE=a%^oJ^`}lb6xStFz|HVM3!Yn0pEK$%b5R)*u+vRX%3_OmnS#E+K9DKe@ zNM|zpRvBQJ_5D3_?4uTA^y7v40aQd;$D6TfjWMaok2xfQ&)T@zywsG|E84I&XR<73 zpqZh*N^nH6s?k4;c>ed)i13h~Q5p2=VrQ{|D+c=*=*e=Q2-J`q5wJsBdL)-0kap7t z)}s=R9?q?6t19*nCpk`1$oK0Scal=b>c2LS*`dHcOo6l6*{+=Hw4C9`V}EP;l6?FG zJv5lM#9Q#w#f)*We?i=1+`I%HDcp(iyBtror)F z$P&|#0qWlYsPcCpqXd(iNDWv)qshP!4X&6zlYX$bED7mk=+3Y`{FRXbS+{1Zd{78W{Qm}~* zA<YCg=MF(U43BWOJFa2};miNd4K2A$!zt0f3n+SI=Pv(y4qy+O zLE213F|TBv0s@+hDA2JdU+cGLJiTHIR~Ze7+5AL<>ko;AXP+)$sh7JOS85xW-5!lr6Kx*5&eFjL_| zt&i->U5C5|j0iQbbkdh`(_4tq{nm3Hvjt~1m!O~DQFmX3YO8Zdf(Z!U)>V(4=a*1 zKI=UaKI~{3`cWd{Zh(-8{13b#{mox14ZR39c2PA*i+$ZC<^M16Wc0GJ1WXP%@=Tw_ zcb#0k2&ysJqKkL4$4?~gx3;XTf=fYLz&Cz>jQ~?XtiO?FDK0`m;x|WeS!+JXzRm=4 z+-8~CZG6*ECgsPH>r z6ifq2`jn~S7MI;@i`KuJvb%-X-ctT)BJ7&ey$1KILiS!m{*qd=jGBjlX=;}-I)s21 z8}_0L;_how@>{&qprw9P9E*>9yVnGjYxyn|c$i5*oz2}WM{PZ}5v4PPtnJ`?x$em5 z$a5WfgJObGrsk55#Aubu?+6Hrgcm1IJPz1Pj?|7JnonZE&EXp)DZ|8T8`0k%(CsAV zgvcI7+mF@hcX8?o3gIxbt^KOW$_b|MC^~xEQt@iZ7r5wrD#5c2!CxDf?nVHMN4-in z!E0D6KPtg8)5zv1(Xg1ub#%n1QpsT_!>H>IdoTX59~fTN@2!gA8llfxzKlQgFrS07 zZci1_uFx2F=}(!p*UEOMN21l@n*6n0f%ol6r~qWrxa?(e!f|7p#I@@$rVo{_g8!Y% zLRfAn8P!gRDn}rS>nS6Geg2MwmtRic13uNAEmyy(DaoHCaKdOPueax&Tz zl>MKORLVjp7*2%`c}5cxypHrjC`?w0WNK8j+qGMq)PndJ8ixih-C;E`{r5+v7>>0Z z&T^(a;7M0F**Tilkld&*9j$8Pv!A%nE=KoC0FbnvaW4>>=n9_Qd++1{&x@jBJ<0*a zG0$t%WHN_3wh=w&2-il9TI&3m3Yw^GPVsfH`B2W=uJ}0PVIG!B1AxG?Po;v%GdpE0 z^qnm!8ZxTvTzv0nAyV~t_v*a$2Bg%|aLgP6cu7A=r0H?w_*0gvbn=fx3v?7wEw2)R zEra)ox@CEbHVfKe^TdzfBc0ZX*N2@OQm-42qEOyWtPu{VkZYB9t^S7VH@aNnL({N1LtbeNa{P2_l1AWmbjOWhkNlPBPX z&=SC835lO1n-cnWm7fJrvJs}Kg-S2`OSF)4{g582?CUUw=yA-$bx7JZ-tOR{*&{qha zB5l%pQ|m{2I^$jkQ9OB#Eron5Sud@gRRsr?@u|n2bZgU2E~YfyyEqljM~BsR^YhM& z-ty4g0WEg*pFXcdd@8xxOou%$Xj)YX&{i=e)3x0-yOZSqNX#aTMw^ZJ0F?hAc8~#y z#=3SEcgdT9p>`(+Hd7k<09bihrhN!$t^hlRk`;TM?j=Riw_ zA>Z`8NXaG7H^U)6AXQJGp1fnX0=jP_IFiB+%7$M5i5C2KEQXsqS~ZDBp18Hg#me(M z>%TOCSF04-i)ALX#?fW+x*J0tHm7;xAPQ~4Kc1{HqmC-@?*tAWtk#=1h(arWJetL{ zYH==k*6|02W*(sh%=G%rp|Ki|#e zR0P3ZLMT20Hw>ph%t$Yg<0nc4rqE`aJMF;P}PU37*o{u5?=AI=KI06Tkusi1C^II+qPi)ldgC7sYq7rnGigU zfQ{HRGJN2Kk@}PunY|$?S+`4+^*xGaxPPK;t1nX(7$44BRO}qTz$DO1=$AD2sfazE z@N7V;t9(2R&QT)Ae66~zqVB9G{4`!Zu$ee$y~WnsLW?CtV#&}pcUf~h)uA@R8dKsFi*`ZZ@&?`ybRxNu@DUVOnH*AK(Ej6~mKJY5PhZLK@-eELJQ{z~*N`I-@Z z3BXqlD}PjJTg1uvnh3HIIK-->JSmAM)ZP)IfjGI9Dv0i(uq^SLnq=qD(_U_9a}bGG zJ^IrIK^0&0tgoM1CKEy37T%Woxd};3)>a8plg~M6ve4ZNab(OE#c}use7#e`!#Q>G z+*Q4BCB)i38wH7>Xh>da_AqsH)&rQUO&94CS+OxW(oO*uorF%n9*`zb$ql*ZV2?l303u&lw=N>I2!Ix=;LEqTQNUA$VqA%dIw?tgC) zSC8sT{n2wh82S_ve0ytXtk5N!57eO0CV-S8@H45@(`8p3eZtEK5a&>Zr#MXQ@?yk4 z^SJGr^D{T_jX_T$#mCTCYEpnOBZ>#D`-%+}NL%~1ufR-e^Q`q}&{$2EkN3%Xi{M^; zV{5X2E^?4x?fO#wC89_A(lTk6?W$6`BWoo%gi?P*L{sEA&4GV3vCpH}%K{}|uf2M| z=Fz%9B|u372#(f~xZ9=m>U+_tz0bx&pS`pm-x5#dxW;e;8LBzWFv_2zYYU!Bf^K(B z-{mNb4D1nx7Mau%LMRU_X-GUwL!{9*!p1%Fk!CA0CPnzSoStnOUg+G3v|KGiqk}j~ zn4HY?Yk(C%Jh*SYj))iAPOoQ-FLZGZK?rP7Amw?UEKFgR)WG3l6MmBOWW_TF>i{4f z43z)gk!{3+3UO2C)AnuqkS}M-sP5ADTrALtd(VnuJ&d=}JSutQI zHFlkY<{U`2-`J$r*K70g1rpP9>#i&jT8sEYa$L5_OC6D z%hUs}Ms{o0Dk3bMMio{N@UgXnQx%eU>&ow}A4gL^K*-1e(qAj%B4(4Ej$~xVg&d3R z;m%ZddmZLYnOMoO$SQ)Ftq>k<|8fOA%{~ZSzR_G%QkGGf{9xo5P5ZND0CB#OHDV)A zru1Z7pVo--|2uIsGffSTqK|L>ayFmgp=(FBba0Zg6=SO1&g`g=oYid*|L{*o2iBMW z1b_waG<>rs+0~UNbmjmXxaH1fNhhxu08kaOd`RVKU zyZ&P+%gj$MSU|2UM#96Zi}QmY3lhfs0Q7bAA?@?SU~G!!+EiJLg4i!q-hR}Atu5#V z`G_7zA9*ftmlPYJD0QBaGXg=dAcTxhG9Olr{#T;HCt>+;uSwM8CZnY>!4X_`D&tRRfBfN{}(7b7=13q(RXukuxmUJ}5s!yEciller8h^@q5&N7M zOtGQQ<8mfaJ9c6N$053;huv}Cb3Fszk@3<~6CYz-uUcaj=37N&z0&}#5m!!P6qq9C z+rHAgJ?;5;wOLl&nx+}o?>A0XzL$LrG|M4vio#+B3gNehY8F+Cl4BWXK&-c&0A@rA zGm0vc))0r($rE=!&6L-0;EKZz53H_Ld-+CN^8-95>|p-?#g~3_)~qAYNJ9w(_&0@o zf#QmFSC@lBzD>kvc3VEb3N=jy8(de_gh7Jts9hd+fMQ$w+9V)FML98s)*h)3E{7-8 zS2im_%eQ;>TCsfNOFnTa?m^)=WR{kFLu)}PuPw)d3&^m%b_v&A?A}HNgr12hAQ-1h zyuFc!7Uk}pg82(`eq{akk5`T&gh820xiLuLcn#-(tp#4lM3+Nw*k@3K&Y(>ReiJyLh>rr~Td;t2FIPklWYi1AE(cYs zyM8YbCEQ~D`D#nmXj%^W@Y7|PBZjvHv2qb2j!*m zk#dr%K^4+2WchGP3uvv~+^IvZGeM|`z-%t3a84i6X|(t;sZChs{&al$A%QpVk^*tJMF-akr}tw3cgS(UtOTN7{bQ;}^0q`4}4 z>~l7W8K)AAbzT9cWdYbL33f$qpd@r_K;A7hz>2Y$X&NclDbw z;gdnWhrN+s^CL|#PiUf6$aP6OGi3k;r z4@4DfR>heSLU7>2Rx#25wJCfv&uy1@(XMpbu?`k?mczT5#){QXxAGaMA@rPzwn-v? z0lat$ep?nS;*;(M=i!{{d)Pe?d1g;1l6vEX!RZC!$0bjl_kOjORHDr zFV72tn8cMcW(h%zh4PqBJeTSo{9!w;ha~oaLJHCP>pr*edNGZ}gncF#O|76lS+g@}Gh9#-ocq=t;uuUwm{dsG9vUg%O zxw3W*`t}DSnTP}2lCQ&e6;MXSyze2s^Knw)wsseXgD?k2qEcOX9f!oPq21V-3pXw> zF8|M|gkqPda@#M#b3GrXS-LU6gv3b+3GKnq@nSCGIkVaYT1v@9Zr0u2$9pC&e6!&d z;H}`i`=HyK#P?q0{Q0Juu+!KQ_!V9*gTI+qrPFZig+dATpLma2w)KGk?k~mxoibNn zvdz57Q6-9ke{*RsS=Fv2>;J_7GC;+6rEgWI=dx?IY-;LkCN5*|Bs{?Zv9fRc<9;*L z{C^j}(D_M#sp@NWdHry8^GBK9Kj?U)si0dqJHt6|4bhVWRSP^<;^RIJWaBs{E{ooIF(OJiPo$Qs*P?}1Ipq?RdxlMnxl0M^Nt)$3y>w+jlHQs3T%Nk2GU zAWMpMd=$0XCgtxEI^W{5YOQt;e^*fpxT%t_OMI=94?az)w2xh5#~A+vZJ$3X9YU_$ zF8{!q0SBpx7x}vWl;P(wFtFbW9Vwu8CRn!x0=HaD>F;S~vqjaDD=mxzQ|f2(AvOti zn}gl`b;R{{)$ET4jneBpU#wbegks89{UP^B0g@LSwiX&hta3z{Zm!E-<51nFicnrj z*=PwRR|oz!yZuLJSlYoHn#$L|K?huOXm&@DN$` z%!&>b+3r>xT&w;IqVdtJFzXGfy_yA-1A)`c%0v>6p!O%0WXg$J@o4l%y1+c+kE!JY zgH4!2#<)oc0e7L2z8rSxYvY2u_jbX-#&z40_a#A7kR6DPT1l})l}_so30h@5CNMfK z_)Xn;g~xkSU9NEzRgl-)N_L_uJ)7HTdK+w{QcTBv@4s4Jd}nAW4WzS^T9dsxaM47l zGmYuA#=0)99feMu&@73bs(!MYOY}6nUMq`lo=%H17#C9?xgRtJwu60UzS(g)^L(In zT(uyGO%wdZFRKWPf5)jDoTr2u2MkMw(AmpVD+|%lHXd)D)Nbb$y}XpJCtUO%lCx?T z?Jj+rVoA3bws|0F!1U{6eOv}s-y4m8b2A^W8N z&F26{$cEgCrv2&Ea(h-~yrsa6#Jg^Q>*NkHj8#gh?X3~W-`bF2Qa`#3W@&G&cR@fH z_dcu`iE-D4kq_V)L+T3hJ^rz7(v^MjyscJ~_sCE;42Is%@Lx#lW|Ug>e{({nkCXE? zT;z6~#o1K(ou6|$ft}telX>>vF`RVcJpwMl^|cA=f2#_;iP7B|gzH9?fOEb@)b$9r z&^i+xqA5S6qCetG73wn`smQFbx|#WY#%!DDf?+bkl(%}8$Cm;@f?#Mp@b#aa9337} zV2VukG?6LxEFnmGlueP|1@d`sC(hziN*fndu~{c__Q@m@I=f0PPvuozgZUo z$m8#G-;-EEDx&B?d!+h6l)wTcXO79`lFVgC6GuLxN`a@j_dlQ>i+Aj8{j~l}T>RAO z7a$hoH{QrvD=G!@8NqKEYJ|3~@6*~WXDErS7-BH>t$!x~V9ciwbILna_D%~1F7wRL zI$EoDcxDMW!P<^*>;a4GdKx1P#<5A`5z{YU^Wn=&woN{-eR4l_*$j5S@f!P@AG*pX z)*cThdoies^q-Dgk6vwlW>&%`mNGD0>fW`H*)$bCx<+m@N@zy?HFR>~G%`SiYRmgwoiHBXEg%)2VFi)3&nAFlZlUL` znvCJw^jFHFuvL@085n;L{95|l_YaKHq&H1}92U5hqS3uZ!nN0LyPPDoj(dCO2W^&& zZ|cTP2ows`{=6rP#1Ku@m^v~}DpC1&s!#f4p_j3|4?t(ldwyEmjm%Vb|k{;OXF-aI{H;GJ|spY zfUu;8tbB)aIQIV$s%gFGX;UqzDdq>>e=}E%) z?#4Z^j{3~25zMZ4_`vZ=t8HDQ9DFxPHsCY*uF5B+E0cGQ+V%;&^flm01Ld)2;@SPF zI0%+I=U)jIQsJGL{3F$LQlWv8?9+~Ba>Yt}>uAjQYx-@6oi_|tzC&+oQ8M7X~J zd|=8nD~f3%`tm4UkEhBbdLsdgG!}zo_|<5+MjJb6w>nRug8JqyIJdPfpYJ?)=LPOK zr%X^LanpBN$zziomm{6BFA(uguVpL?o2Oz7aD@rc`kAIn8%W|(^!E{#SGT6$n*?sV zC)w6QRC0LCM7dAw^Pe5u>-lJog7XeBwAQ5;yFbsHn=0q`RYsB=P(W)po_)P-ex{EH zujx91*iU+csgxqqjHy-bQm^oCf($2u0+wYruRNGLjH9y^6T~B}GtbSX>@;B|U z9>Dy^OKonvq9iZW47DWXIP4gv-GX9>M-_n$5a@QYL*ziGPX{X}J=o5J<(9cnt?jK) z(#`onD5WC@|33%C)?v2n9{}fwZF)tvw+By`psEENxXP_7+BloDl`YZTI6O`D9_vos z4f6x$CsQct5tF;4**&Q(`Ol9v(P~K}5r)%UlA;;p24}{?pUbjDnh3WT<&r`4Y-D{Y zpb@8wK%ShR!jF;rZ-WRYuK5ggYVbSVgbh-y5_ifPYh$}ye;|Nh#$g#bs&}7n zY$P?ntcQj?%i+s-7)GlnVd2KG0H1wA?nu_2CZc_L^}1+nXJhl(*(G_Bx5%DvJTfAH zfbY`Z^(=S<@k};@`y@nfpo9mqneyq5lRq96RVQ(H$!VvzO@s2Y3px^*E6cil&q?S_s0v8G*toyiXtpL-zcOc`CHMeL=jPYRlz9J9NzKXr#qRbGB_L*bM)acD$dcO`EH9QO=lwrfOLp| z1)aG1K(v>*WaiO`IWAY0Von1$yzJY+36r9kNuNLB8%X^xJr`1C*tFLl-6 zFS)WR1YOv+#^ekg>lF{;I`Y-XqK4k@hKuTl3L5IV)+)A;=;cOSE;o$jP#4QpwLyf!Ma0mkeHq_P8! z7}^~D|EQc~2Br39#5XH=XMC892X*bJxO5RDuIgSAf+4hejRTgR>Ee5}=6xZKF&(&| z;6~7kd!&j$h71G60SHX_4vS1OG_V?88n@vscJ1Bo)HU=;%06Q|$Z85`+P=4(y(R`J z#a|yB{~MC;D9kyQqSV!)@@&n`*n$YhE=h_O%pt(UyVC~ud%8+hTAsI#OjPNh_-HvT zre;MQ3M9;GmnVSNLUiW~ftm9KNAqQYI3l79FldalN2M&I8^dd_RIP_SKFWYazV`ax z8HK${71;X+&vKuZe^PF+n?N%(89k;tnQ)HBtj41iQMx#lG=2Sb zHj~!oHuE(8^S+&OJ!t`-_D#`o<6sr{RPO~5Lc1V9x2PtO5(X+2a9 zSG2*)&D+N|R*+WsYg&s(74u`_a=x!!(cw~^WU`x9V+HK*D)LJ$yYfl7CL|2Eu7`9s zNwq1PJY0VIi%pU#FhUF#FJ0HyRIBe8?9Em#S)uy?L1|HH zdN|Z~hSjlUPqFH2`LXdI-G)ILkeDkMalvX)nR6p7?p1LI}3#qbe{AnQlQ$AqlPVJZUaNfioKp2(wA6aYSMnb-psN-*J$I zQQ@t%b}!0eZ4#=*Vnv#9a5%7R|1Te80MO}fFf@h)xQh4<3fr`~_~>%tdZI#UkoBqmRQ^}W=OCqtQgzaKiv+B`z`5&PR-sh{T(2o#`t&(3Zq^EpT9@#6 zBI`Pb><+F zgc}P}&{)F{eR+wl)7os(l(@rD?9=xWFSja6q}PgzN$|owML!VtAQp&t3W{s`uI{r_ zj;FroO+x2=d%23H{8!4!$_GWy1DQ^qngCo|$-yEsqm*6kSW#KF&GY9pgh1O4f8ypU zzl_axoX|TKY`WX-aQ~r=Vyv|#A9p;0DT~);zPt{+1G^(BkrhQycg_7Q;))Ly$$wbn5vp@MS6Yu? zn*JdV4NCBJn&|6@)pLMp_77II%uJw?BecU$eNcv;lB+DwGbou%#nRBbwgGg7Xjsft zFgF_qhr#ggd+1Kgp@!Y~U(^4{(_CkYI(9}}$IN%ueMx15en{CAPjoxMnkUGvu8j=# zdjxDdn2VgX!p&L0OpNKATwRBCCI{(*aw1Ripw?wZQiyCFpT4p8k6KajaWSZRzJGN# zSvVBs6Cu~wwX`hrP`!2q1}rB3(2C)(LLW~qV=n|KXpI^r17F|ehp@k5$Vy9LizB1h z!NY*<5@s*P)?*iQ!A`_Aq)Vngb5WJt3Qx;o1-$bF;;iE0s3;1$`6915swdhvhBOhJ zv7+&bd;;3OVxF?C2GWV0?GZO{tQ%)ejAGCs`}tN?3&+ig_NheU+G0f@uEz#?saH_2 zagQ@rEXdJp|4hTAyQ>=+7^wh?b%BY9dj#InjI$L6VnqhTO!1kywB?THxR%L;vT2Wy=YHl91uldc z6T4Y*u^n?BviFvuL*kQm!gt|Qk(P(JF}hJ#B8uQY_hriz1wz8s+88`W44VYB7`s^O z(bPfMQHO+m?-fB@=_6BQ;k=*sn2n4wIb{ckbjBFzqFzn*?84HT!pxm)Rame|7dDbzrnI9$?W|@CgS`q&7faj2Om~jdU?F%uS3@)m~dS7A1Wk6Fd zvivf>v8nIEslsg5W6AUShdn@oS8iZNVB@zV3K&9h(x!!}cXb>D?Bq5OJ5zfuj){8W zt6s7lfTb~8Z?Vm3uTiy~#ONYMciLNxyX;I=$2QK0r!JJ!kMgDm!Fn6vAW5H1dYh_6 zZ?)9-p{;U3CD7XDytd;WymOfL$+CQ~P&!pVdbYZe+-xjdgQ1irAOm=O-4;d7SrF+PDyp=7;o)Cr?tm>8D@$f9L;{(nl5nOs*n+)b65k8{@-WC{6+# zJd|2YQOd>i3Yj&E*1``AW^SX5M?$pKCg9z3^wHVW3#Z<9vn|)ZsJ4+uiBYn8qT@76 z=}8;WvzQ}ZF=9~Z*JRg0lihP*kSL6@5MBl&y~U zNUx?YVUs`gVrUybTfgXq>vdDSY>UBh?KB#0VJoDDG1 z)NBWFzhbH72j;J;Q1{$O=N#&8t!fmZ3DV_kdpTo-nQ`61a_EJ8fKD)g^oQOo4%i}l z*KB`kE^IXum?nzweH84wQ-q*J{^REENT4852a`H72GeYVoR%5!nK9qZ|Cqbk#CTeA zfa~gQw(O_N6?gGyFo0HsUrzJubR~Z^1BHBzhMVn;j`@$`TyQwef6M#_uFBHiQ5eWT z6Y-KwyG7A?FB_TAiREH6u>MJVN9H01+f#KG`w<6&yi)-Y*NVyz`VCXY?Xw)m>dl`X z!rQQvo1RImna|jiefNfB+zq_(Nd3}Iyt74SgcS(U#s`C62p^HK4a0!Ur@COF@n9E4 z$v@!!1s@PM{}{TMcnYvgTfY#XvXiI?#q!Z1Sq1NLS9pe>lzU`nF3ZMvZ1B;s&T*Ck zgYb~PBo}H0s%)BS3-0U3A97dL#+3<65Vuqnd!+K#NE=OMZMF^(o*X1pyS zvoNx20L+wQ?Fdvb-Qhv4G26$2!i(Uw{Mm zx+d1r^1)k-EH%oOKMLw49FS^SZ9NcsmSC3P6rO zfUJUf73x_ig81(A2YQ6z&g~^W>*WeoZTJB0hSGFGOHJ%}+DkVF2rq@ULt})Bvh=@{ zTZoGG7wu+gzl|-n@~P2myB*`vFqu`--Q&{5b5FNSp~q)=;Pa&%vzNYWt3Cr49|jr8 zpj%!rnP7b>*Pw_lkc3FKcr}TyNXI{CEGh_VTMwA z^bDKg+oW5g)IdN`4?*lksr~H3C`ABX&L`^hm_q(Ch+Y(Z>d(OnpU*2o8Z2Q>?a}aI zww83Ct#GO4i4`n|O@mo(IJLQ@TA42%MjqF(C@LaS;9k+#SL#dA<;oyIF`mffI{|KB z!}hysp!EJh*H9e)2DXY5wHYurzHC@RlgrvD2qEB_Ni|F zWn#GXAx)R}u_Pdu5Bl0Fo9bud)m(iOX)|J`c@q72inwzUR`g|>PEx>~3C~9uRWg|E z;iCYYA@tG!NT93Rf(CUL9cZ9L;=n3*s;yP#5s-c|uqjd?RB>uHke^V*nl=INPoZ*w ze}la*20$gYxYmYIi7jO5-o1H~(vbEmVRo%3lzrP8HrO!W8}{SID45OQjc#&#tgGnmrqquW0Ukly+R0JVGV4xKlD!}Pl_@T&9wDixZ~vYF{hwI6kqE8 zJLw}$g`cGQw!VwTm?w|$*%+fHCRYE@fK_zl$)wZ@ZZuobMvXZIW<~q9J zzA6E{qAv226~xxH{bdtAx0?Zm?-LAoDW?WJ7oHJY{re|Ar)e=R8wE2v3X5F?X?){K zQA?hTimoY%NOur^qZ`KHZ@MYR3piWrJr;l}yuB8)Q&6Wl~XyxE5K^w0W?q5W((SD%TwNFLH!?q({RF%W$y16r;6 zAm={;%$D591~lT=Oh&;@n_RT*JkFyrGnj@4)0Lp63T$07&So%CFJ(acUxQ?wp4bhFs5#szE(|^ZC zPY;Y-!K_jW+ILJbbdqz7YJ{0MScIO9>l%#i@9_!B$m&e|qvMDBCY?Be{dbIm!1Lg3tMpz$~Nh_)7Ftq!tJTMXph&5b(MPx0sGph1*VT{Y-9T)z)9 zeCg)oeA{)80_&E4u84786#~*kM3Z_X%Kn7xmM(MvGo}0ym=ZrHjLKF>!6Qtwj$RFnKB#O*^ZQaiw@57!Mi*_ij4@shJ9`Ygsh=9aF zu)mOD&Lb9P(8J?yN*|s7Ss<(w?|Pf4KJQ~C(F^}voBpj-cz2g-y?!6HK!&vdz%uZ6$t~i$f(HG4X%B!Go!_o2dvV^i61M~`ztoN z5rqEUwblDI39odAyW2_aC_Y~2c+4kI-8Ucn3l)lyA=|Xp7_jv0_b+?rjLDTRA(xIi z8nc2&X@LtuCQE0=C8Sk$$VytrCzoqP4JgUy@RoU%k^T)If7m#OP72yqR>wBDW9KhW z*>QiqNRL80;Q2yU29%f1;s(u-_qg>P+uaQ8E8g{kBmf{fEaD*kv)kQmeI-JB+BSU{ z1Y=ov+^^*~VlVoAi}B(zZhsQ{AgK=Alm+i5;H0>iF@IyUIyXc^48m1#=V;SeJ|{LB zqyS{gJVKr$5 zcR{MW3b%NNK$YAgI9&1p1ypgO##Yd+b(iCuXzTTvWrqQYCZ0?Z5I*sVEP9?xtwjyA zn3jJE-e4+D9rgNetz=M0ul%V&gQnhPAg`O+4`?JIO!Lxpss;>`$%%Ob_`p){cpB&A zq-Ocu`~*2~!}p*tBW3)3Yt`!X+%QW_(47ilNUpctobyG5U}UQhdx1WvPt^{?p3A57 z7KFOq^u#6!p{|E-H$#I?FdSxuTI<^s75fI!24H~9#2JFtM)G=TRmfgxXajuKIJ1!n^jK}r1 z>`5nqxvEK*?32&@z1~i=kC6e*R9Em0GPI^QD$_oYws5E5BvNL?0@cA zqY&~hnNi0%esJ2ggUFD%xRo5r6_%_h#XqRF4bz~yBZCEvGq4~Cai9lCPsZzZrhDyG z7HlV1{y6TFqAGqg1-iBO%5F-jsy)_VFa{*5R11b7N#I}lZI|68iL)bCWK8}rBAi^A z4Q-8Gaw$;!JF(i4bc{^MpW(+rvx(|;2ugp3n*yl0-d?xZH=TIM62clQ8ib1Q>TaY) zFF%HCYq_*ZY7a6&Nb=^j&vGd;cQ!(3&KRUS{dPfT^bu}G0JwfGSSE3dhMd24r!Ha?+J;d_t_4 zdUhVM6UHDjrjIH29ccXUF4K>r0;gEvpaGajm^J_aGB-h<{bcLtiZXfs=3fgX{A>2$ zXvKh0;xynHgHRb&&3HkcaCOBOR;$pO?)$*JhbcC0)%p*R~7x3RQD?x`i+CK$i> zo{FbV3rZV`p?S1)wrJsu4>hwZ83VI{rwSO6xZc`>MkM7o&e*$d!;FGgP*M>N8j^p5 z`!SGGa>_3Qn{H0whmO$cRPl~Y&F--dfs`JM-gFfE9^!mURNE2+kNf34e=^oWs&L>Z zm>Yu>Nt%L+%;I+%(fPud?G_fn@;}r_qygYS+pc(s_@=jit22{UU6NG1OVUR4&DxX7 zC~m>%RoXFWZD308ehMD8g*!oKyI08Fn_934(>XkRj;VT0=Cp34wv3zdTl6A5*{r>M zZoLx~-OfYWeVkoGL_23Zf6Q+r3_E*tF974qX}9mewi};F;FQUv(M@1Ce!#`@Qlh?BHM=}F z+!c~;5tc3dWDFv^{*cNEod*fq=F8!x;F(XlRU{A$%2p*OQ7Bdc@7s=__3_*N3Vm{vx|_=3kQzR>G2dvDliHI?c` zjn)Y_t#B&)zHFUelYwO1IZ*w&A&~1=GZ|YmEk^PiOpR5Hq2nAlK9&h+ohH4)E)Qn6 zdxfzB4QhsQx1CU86;q&NZSzQX>S=hfkB{EIeltE5krBT^)II+lxzo$*(npYU*NG(w z@Z4mha5b-)Fb@Z^MRa@Oske2zb5Zthfj!;3ThvyIpVA< zF3fKS&$@dd!Ux?z(~FXH{=J23Ak z>)%7rLY?7AE&a5Xmar9@G7CydSLp5?_u!GNLG>Y*TdYM+;M)&(XWL{07$A1Zg0Occ zb0;gZyx}QT$9)}Ip*rOR-6H&vo{@I?^^F^{Nekm*3#;e@8Is){Y?z3O3fXmvRieq+ zrNIJ_OW}RHO}MluyhgZQ-dt2n2!dM@;TDT$UCT0eOIC|RPo2at8|gmjL=)gCS#2!zOg30AZ@3?!7Bw@X6o1w~*PRiy%E znTPs=s=JWelA5eOHGjgcQ6OEQX>XY$0TCtGn1@cBJ7w&cZ@dAOCMCdlNQKuj1D9O2O=%LWA$@wIPt9K66Tgg}&48n=v8IzFZ# z(t?rddjvRIpN9h$LnpK9QM6f7BiE)$&E1{ys5|yx4PC#w$KOv$$jBi8y_2_=5G2n{NQB|Pt-5`0oBCs7tXP$qHfc=2wr;SOtsD~H9b;F ztI0g9UC+f`@}E)yo2M4V8IJ-$(OYOaT4=)UF_K_SbSeMMd;0v<0(=kl4&(pDKMO6U zbU0MkK3eG+e4T{rN*yvErcigUlkd8of5_Vn@uqe51jF-cV$nL_$fne;t|-7rm$u>< zqTDPLXG7!T?sg>z&Fk5Va(a$Ni7`X~futwyo$sgp~6c8NJxn>Y;B~SHd`k4Qs zyGDPp14pvgeLzb}R~YtYUVMyc97n0!-Iq65@zZKHSg< zVG$*)0$}A(e?1Hxk&r`}VC2&7Rc#JxM-sdKm_3b5CR28?o()oXSx70p-f;QbpJazxx3`9pd+?S zrn4tj&{->jRKlEq)^k%7Tj^qt%_i(5y9t7F_VwHl?U9s_h8%Ga%m8=A*oM+;0D`)g z1}6cnqh~ee{_=-6WO+IZLE(&=G|tYdNBA*en@wHfHko?(cCY?FMRJixqp2_33?Wy; zti+u8WjW+3(dOo_oP;rlMK(6eAGS-K?dijB#f*yNP?Im*P zgVA7Srq1f5RqB; zvPm+08qfkX4}3UW63oZic4JZD0WEn}fn}e>T$*bQc;>+ZuS1$Mp?&E;+oUq)K6r{T zumXDJlh^*D0wxvLf*av76On@iAw;&JPgaggT`vN%wu9r1HmJ2S3)xPIox11pUK-cB z+M{4*Nta?%K83*L_Gd&f45ho_yhqm6NLN>bS}mF|T(cinm%YB1!i5mK5Z{h@wf&D< zJ0oB`wT^w-K~3A(gWW*s^@dHqEC_F%+g1{98^I zh!M77^#>(osjnO2PKC6(b4fNor7#;5ljBI10}Quhk6!f4%`r$XoP@CGat;X+e(dH>{x8C? z3JA*Q>BNT=X63ICa8dVi0vhlw!PC>(X=DHXqWuVOnR zx9|M}b21u1_tJ`=pD>@Ib^tL)|>Q5)Y;_SuV z=x)&xs3Z%xbxyxwopyMN-sRMgkV1&onKjnt45!VW<3AIgBQ%Lt zE^%&AB6^SuOH)T%s=UshxzYG%{B#603(mVLWCR;=L5@BNJ!VN(*xq*wQ=8TJNAAeK z-{)cGHC8Bp5MX*tFR;v2_JWq1(%RESYycCZ;6;@Y^E>)+_sFaw%b zu9&wH-xS;-{iz|do(1B-galbYDouS3kVJ`mReBd%Da{*7NBZ8r)GpZ-EexoFmU;lD zeQ*m^E3_5IU!#qcJ*~OY)EPrH6>dn7y`e6)VnI|G#qox=%~wFtJ4i@whQ`FbB4n9h zxm0$^%FXBqyE!X9M9LoB4P!$UEi-y)CnE_Dqibk0d{_wNd7h3#KO|EYbOo!4kuD8J z5nC|7onIX=bP*>0CMXnywcn)mcs7bniUGp?KGeu#F38D(exn{2U{}QPxmd20kew7tOk!@8tu?M(cgf!1{#Y;c27(s_`Q73UDB* zdb*sQk@uAN${v9Xo`UvVeBn83nuyZOP*IX{c*|2Tlf6+@z*Ayxuo7-n+U{89vQ$q=}A40rb`5NA&$-8lE2gH9}g5RzZyXbvLAg4%HDw$fjX(^ zpGZn;Xx|MQt-XMRJf3v@NCZ;^v|<4M%b*DUMc}a=*$VGT-M6}<IbMzq=1#8?7aeK<%u$QE|d2ytA0FBQgL@FG+S&nwAW${qsn@QOM)I6uOgGN`$Z7 zjG29pXss)ZigJ0FBt57CQ2nI&W%yMRHDVsN9tTRC3WsQanyrPCvdu5eyNHE>JwF$} zeRt@!C(B<6iep%#nWwM8k_HGKxWvbZzOEVa@xW%=cc2q?8K1S`>-4IUXb=Dwl*GJs z%m#XFXfA1+Z&8{E=<8&u>GLl?D~Zdi716ZMCZ$R_zxWf%x2qbiJ*|^K=x1l~WA>Q9 zh6$mAXplRQSU>Pn=$pE%J_wGq6f#=6}*yzC@l27?yM7d}`MwxLb@e|V_bEZT!<(lNb&?O3r2 z;BE{^j0U&w&2G*bU;E*}AGZ(!y2T7n)iWN79Vig4;PA*1MR+=)yc*tN$)94N@r9Ml zLFQAwK%4m(m~a9z81FDlNF3!?m@^y?JKpgo`B1vI3FvsVuIeJX7qWKdXix1VY)4lM zS4y9oqoKQ1&6HOT(wdpHVm@Sy7gZ(L>ayq^LZIAo&Trh zuSO=*q#zq|g9%@lr&noQ;_XS+ZGaw&vl1JVWfQ<<51&C(SeHkz_Zz1V*)wX&5K_eO zCq#SLo2gdH)tw}wk?iywK5GdC^goBNz}=7jGS?5ZVF0RS@F8sF4KoN4z-B+tz9=#58@Op9Ag~u zoeDnR91vKR{2y0eDw|4QaJhyuyM`aKB?erQz2+v0ZCarO0CvxTKefx2#BnLC3)~zI zvjUJJm6AaseigwbL2!<$l<46!_vK*D?_~@`->*4eJp_B<4cx!r&Z~2Tr1Uen=<;6Q z_`2y6mV!dIzs`6}ezohUmD={?(vu}l*qGMNzN6Lr#HMUS#ZFWwRh>~KG3=8)%pUJ0 z(T<)cWub3Y?;2Ak!=w4dNlUTOCXA_ZgsQC`22|jzOPXWZZ5C}s(-kDf5qxTYa|&-1 z8Z|Nl`uxnk;-&4Y#C;~dJ`8f=_@7f0X0ks&_Q@EKiE*5pU`fMi&$9@<~;1#O9%n z%n!;r)ErHwxk7xzxTmI&nFbB1>r-A$m|iVr9frIqaGKjE9tBdAhfdQO!=`gqb0~vp zJJ2hb-RLc`MQK>8!Ah-$PY~D)T00!AQiHGG=-a`W@57v*lO||)Ohq|6HsTx9@)wpt zNK)I?O2W~PZg+HSO}U@2@9F-aT0H8IZPdO}^L9r~3gBKS`b1Zq=#7XT+ilnEqN!OALGTGMXLU|x?IbH1N;fpV( ziX$}S4qdzbBa1>`am{V7mH6Un1|}@2p!=5ck@c9LGz=EEU!bA(q!0l&4GUm#k3Lm_ zJp9UHCTaVRP*|=HzIWXqe>@FHL>!>3O&0R;Lc@eg?6vs-00RJHcQS84{oMS+IN`AT zr4hkxG|NSZeKjLYTTNQ83dyCiG0rI6|FE-i8NS0@=JWa`3c6x+AnSL_FGsMDyQ!f!ZCh*ROeS3XlpGmd=t73Ds)0RO1=8y$xhd~uR}t|< zW)UNTHk37`Q}E39ksTh#Y0cv+nGY2Qx4K zkGL8{#?SPmP`(>X*kK&&4hgH;Q3fN6q#70v=r(+kx3Bbb_Aw(>&6XNWqJB~(|G~8g zo787n}VrZ=){pmdNJ#yN)m;W_?H{;@VYFBOa${Y4*KC`;Wp^t&>>H zX(Ws=7~8sqW`C{5V=pQ4$C?Nw@o}72nlC>_r{mn_5o2J+Qn2*;PAE7(bM48deKwWW zlE46R-k0N$*iA|u!`xRvfkY*jxeI!yRnttL$r5Z<2gs$&S(q-hS>x!#iW*SnMC!XB=Rl}4E2u@O7j?Gv^(FsFo!H4RlwyR-I4 znVloH)+`sUxaF8}GxDT~GG!V@Mi=6KV~EB^lUxg_egVF$3#t!>BT?>DBhRr41MO<` zuiK65v~>)9Z$+6CcPy6h^Uz2?`xrub*bh#qx{=5rXuAC?iOFm#`~v!H(L7n?w^|3d z9K56=jYjHzVc_u$1hb7>Bz1Sa(Pq_j6H zjppB6l7)43eVQG$8ygcmrK7N3jnKfU%7_}08M-3Ah&czZ*<17^h>U5hlTQWGP65q4EH3~v=E!#`{`&9cYyPd z7?z%a!e&H}H>Cje>u|pSr$IuaQ~A$YM42Q+dnR!9xX=K5LwsDwMU%I>i;F&t#y&ZB zJ$7rjTa?z2Kx#)7Url!WU}i}F?w~v;?a++O=9Tt(r<}9Zj4!v=}H7m$^acl z-6HM$RaeAa1HtR`vAI)c1eok@v#?I>l&9g6>?_@Y%{!1HZ|EtGUx>jcelRN|D2RrC z$TP}1RNDmJj>WU8Y@gW`J5-6a`xN4BkNhrnpv*mHPo)ZrU>f`!lP$z3bK*XUL)p&2 zZV#yLvyZ30zNTjH@2ruNMHESm4-JVsO9*-g12OP!y3_zUtSi(sHySu&R zYH?v&CdyuX6mI2D3_+Hbb3R2#1EZuDgER*q&;{dVe`wwz<|Bx{tWJx>vmmITyI$?eO2bcYuJm@RO1}=W zQE*d6{8Te`ynHhALUX&{e${{)&hwmZYn@j^BY9LNmRBn0v+o6MB$)MKVG!uQL(hDL zA?NDvZwEbk6)`Et%@!q)@mP4FGxr|$TzBfr%uKin9$c0V(cPJGU0z(yW?+}^!Q`0q z-ucP{19TkT8Cl>uo~2^|fw$Ekwu&m+G&Tdh7r_`>VLOiEMLXE*A_o zFg0Zxn(9oaxqM*Az?yX6&x<^Fh4q*369pj}JYpK_KF|VuO6)>BZ*P$_&e-J6z+auI zTvO>dgmAoVX%J%MVwTG#GT%bWiPy@?+^3fa?_NQW8|Hi3b`R=*K0K>iwt$A^Z^wEn z1l`544sMQF|8w7WIn;-S7N6Cxh#T@lRm(mFud-Q>ryC#*WW1`uuSnMM5d_Gtw++=V z#G|sD7p3%{j!zBjCSif75-%Uk^g?W5x5W;nTzI@>$jy1-uI2o!P(X!Yk$dLW|3De~ zRuqjQD+>16U>{AWfOY@k(S9kt@;yS&*Od8j%PNDbXnnDl%r#I<3L|C7*4YyjfpfaG zLEsjqtb~al4cma%J>Ca(Mw9ei!NBIWGHNIiGh*yTPLbL}IlmKF&h00ZBrVz@9k3fUx2f8-`(-@a$Q8$T{%BlcHdS1h ziL`9SDG%jvIC|&?-Ylp}e-y*)MC(~Uzu7ax2tlfzg)UyJR}Hj zpbOfsT3n0LV-OU=*h(|u?%KFv)|d5$?XutW?U&BY)l*4nl&$53B`^i49wjlqQgMKh zeAquRSd?xf{LT^D$W4;KyzIR^V(@2Z+ ztfm0{(y|Ccrh=Xm}-#_rU_EFtDGxN%4t8x7BcdNaUlxAu8<$M0|varH*= zqC3th&TZvy`~=q2XTec$TS@nW=B)XQ$c2qQqSUIQAz2|Ux_kp!g60#W*r+;51% zUY*)Cyp{};g%$$40$+v$aGFKp*&t=I2(~gmu9cjWk zX0+Xzl9ppGXEs5dy>LdQ6HDNszCO?gK~Z|n+wpAa)Iq5+08X;CF6JPg zdsfZYPRj62xiLd#O!@k{PTz}cGXF*?EJ~slK%5PI`GL<&o60%uYhfFmk*nl}(?!;< zOye5IK0hgqJvMxKGqAU;iJOV^kV5wynaty%QoWF?7tRO;zI&3+06*6I8*Y>vn&sFA zVj|B+W&=Vyf3 zWA0547*Ld@VZ6wsA*HB!L(Lci)v=23VYc zRrJPpuN8d;^;gvif>)pp?iq++TGqO}f{1ZY`xAsd1{kw?UsP8Y6kkuxiXa$Mp{BN_ zjH47pmkmkpKjwAYx(r!w7;%bq4f81Jki`5B?1j9OQ$W!D)UR%^_M_6or`@`bkm>Rr zeAF@CjdHh;w~JGCB?-VJs?|53gROu)yG4WN&kL`54F@JVdQOlPjO;83Z2k4$MS^?h zlSBcSsm1V+SxY>S_{o;|^;~Hd?$fOXNN>C=;~uSb*47!LgI_3OICOSm10$ zzl*EWaQsnyC^jWd5{;~a12D#%^u@C#$Y*(T5Ij1Rg5P)Tb30oWvT4OYT%Oo+#X>ds zJBB@93CmaT4LEv+f|Q*7w$23C!9FGfD=BR90(z>GZieMnXVm1s9G#T%;~5;Yae@ND z1@hdU>q^AiSCUk>F7&PE?l4g)I+9U;F}!#Irbkp_~NcmtuQ+<-h;{ z#nYH?QhNP||NI|PRBJ1@m4iN~kQmZ6>L7%8Z&)09(VCL41|FZw;HT__8%*>Wm?LIu zT%orbN0p#)k9%N>+da1x?Y@*3P>Z4@G(73S1ir)OC7{Selhpph&S0(&ACSTpz+w(g zIplJGL2=-;Oh*KVFQ|!qoPiT)3x%07XMgPEsPD4%X?CI5hE@vc(>VbFcGe zB2>F1i}POLK6Q-;qF6e7xYjm})xxXo3T07f8lbWOTN|e;lh3&acUA^9>xxnG@S1Z| z&u=p@rv9U;HFVE$oM0;8I-AE*pAqrb0GvU_*&D3a%)9zD`-U9>n3k`|(9^u{A#4&Qt+ zevOgNleu1EqLu3KX^`vC_K!N;&s&7ZLjW+ zZLi7~wx~O_tQ|C=0Ae}p>25Fq-OLR>g0yTpPFCp&D3j$Q&jZv>Y4;cr$!rSe;ckDE zDa0l)v~%Hql|EtksA$|tJ5it2*K77azQ@uUHevXzq5tkCmTXtxW-OK zK;Q(XCCP7ERdTb$bEMPoa{L2LR&L1YM zoTXNFVF*G15`k806rFyZt`>Tjohw{7j0KT`?ST`$U4)XxJ2N8>no&czPQ?Mbx8bqP zrMRzM>H$?ZX*7!GT5&s8XQ~px1ks{`&ylmj6%GwPiQtpsT}xk8+1ER!USFD8b^0R} z4gn%{jFWiB;!w+0lSdatauUlEU#{Opg6wA%SpH(J-F31}aHvD{XkI{vO0MC|9;Z4s z@4s#sO|JOaYTt_XxWSW;n|DspK&~olGlb&^XYSc)plGT%+}sOvVbo~d0jJFPqtOhn ztZKTmdn8U&V`E$N3bmA!nLAFJoj{(|b>-$EMW(j);^WyOWAY&|NH3B#O(W+x-;}r* zU$>eOh)1!~a8-*8WU(8J{nk1IT%*y;VS`nCg=z9Kl=6=fSRi!-JB>ObQPv?GDLXRgrhrbcHW8$`n=6SH5x0k0h;Ljw*0H(~O+I~Y7yRX;# z$10XlDe=`RW;k|Cn_zjV70YQa)Qk|ZO3*)c5%#U2i!$-rHg!@9!-`el-YFpNgKq)`inTI zt#DGoM$P@^?1_3$fmkRHDC3jGowDaBjUslP0pzGYd{dl3T}bgw*NAr%`L2HKioZ?f zE|$uX!hunThggcOG5X8TwF%8^N^!9L?uwSHs$@z4toW7xuObBepn$7fA96CtE%6XCT7Et#qbsB^<6-mBpz z9_eVwio3n^^n)6Z6md0Yf=~w8Dfd3EX!kzJ+s5cd>hdXj*$baFk=PC2PF!`Ne(dn7Uh@j`UL^$S&?NWN#l&HRq3i^haH3O?M zlx~c^i%utLYvll7(56{9Ququ~Feob5PH0mTbEB(uXJ@)fbKWT_H0(NBA0gM47iLw- zlZI~4G)j7w*uk-uZ13!C+H@*4#(X2^sZ2kufkpcbm_}LuD7N{Gy=TQWCA#j6an=HZ zhxhgv;)LnLzodXLATM8@wQOE9FEGq$$0V%*3;DJ_UI6I}|2sR`-Eh0aZFwiLooDCg z3!=%i#4U5EuI=u$UVUUIO8NfZi`nu0=~}LIo&)v&j^xS=jfRcoOQDl5t_?Mgc7P<% zC^Ld|I+@8p27CN`dn zt2~glPQj6rcMjh}?nXqoH}Dm*09a`&Oo+5ey zIX`Vc!o&Mja@Mj^$iQcekBbH{#_+pbqNkiK-obpizrsbbUNRgfXb!iUf2JUB(niCY zWDUUfr1!l=S>467A^XT0E>YwC6w+v~j5dzhb&3)eNSDO+qPWTvP!KIj^v*r*Nmm)# zq`M(_rxEd>n)e|Wgim05iFG`mppC$bW5p?&(r1>4AT8)cB@Yd^1v0eae~V8YC-DFP z8hrtu9BC5&=9-p(>k^f4X$EveomxGcxATzoDgg^pMHCSrKjxH`UqR&%ifDdS%-3O* z-j8mM7Kn-RJI6$;Y?OEo0nkP-+uybACwcqXJmJ-m8OK7KT9^jqJt6B1W-ceZmG?hZV zn}WRW>$}Z_5u3WU4l}?ZQg`-swCDhN89f!)B(9dgllqkY&6#SE{N7bf$(yt8tPXfL@1Q-}TGY_3+2rbBrGfW;Le%sKKH zqmcmiq}eC>tLE3Djbvekp=aHiLMBc>K%{Uey->=0=~XWRQKjGaQ^gSt>BKbY&F=2R zujx*m+#1}Mxy?ASu>SyOLA3F22V)k<1l`$3tekMNSziqibJ>Ao19S>K@IE?kc0Y(> znzV*emd^6-3#jMA2;WvEzKQ8@G_%Gd5pAKfPtOb21P9EGUXGluzuK>3Bx(MqDyf@U zLZxV3)CV*DJDU};RKHB$*I`LT@12e+aaiHG_RRP40yFDqrV_DZS@lXb-okK(OLo_v zA*kFImp2#uh>D=M}VR}hnP+9o&aNmn`rxMGS3>#Y|sIn9%KBPje| z(p7`V?_lbof?V|)4eGa&u3;HxzHNXAP;oR=2PzF#m${JTe| zhYqd??DOAW<|%+!~@Lei;Mx(246^{ou`dRXsNN~ zM-P@O7~@l}zgZqp`=qI)8T3xMNqaPLE>U8OF!NIiDVMNoY8!y~)D<1RCTo3c4RZR2 zG{hb0_IrgCYM%?~P>t2BLRYn^S6HlF%OxP&T8(d*$?niru6o3ov34;GKSkfimfBV= zr3W!MOIAc;LUCzMz%MUSXE0O1gAQLq+xkPSf^rKa-o@Qte!MV*almO3vuUqM9_Zz7 z6Jr?kEaqGjnh0)R@L)S8V77RN{HcJR>=g{3%`PQTu7gW91+1uFeTvh%4b}m`=*{9$NZ~wOIY^aXupQPNA)P zsV1jX%TV4Wwx~6*ZG#S?YA6dS^E4OZBZ038MQxq5s#q2};zWfDVoeGSx4q-H1j3$l0-0Gjs~oOe8)g{Nn|}6yjQ1@bA1L{+P&%( zphtC|KS_R6C08%OYfx8rsLJlK@Q%)r=x~6j@Bz&`U8cU8$TI|S_~aCY4FIp`$9-PQ z_D=CiVy#2zv1Z1XZ`q=s;_=NDEJz&%yvR}KHx?{EW?p}`E;Vg45m>t1_@)3T|Y8LVIX3}#k4=)%9Mc-zPtXor( znDi>h=u#QNg?K}r{STr@L87lw52{AQmKws8A9ZxV0hF(Q(V>4W$ z{uvyHn9Kefu1hQ`($kl|5b+)dC>SJiDfJJrLbTo-aTFdgOfo1&YVlV2ZsAzj_mKF- z0{rDLxkogo3{!Kj*fu8U!W5$(+LR{LeT_B^23O9;m-9Xm&tdy{hakR1z4-fZ%)gm| z1hySqrr}IL%Z2%W-r2lFK5IOCd2oG6@Jh$)uCH|fA+8Qzk7AYTDCK`F5B*}*E?TABRd zT+#%V(y+D^e*P}fXX;Oez%nmB1!{+3JEz*7O@R25+Q|PFntqn()pDjzotQ8EBepFJ<{2Et5dZ4N^b$RZ|Y@$mlxDH66y^M6x-;JlHn>f z+!S{dk+;b3isM50%}R7Yt)UukZ3to4D{;AI1k2feDTK-<@gyq$4}*j$bDmV%`5?h) z{sggz+{5Pxph5@UHok;7_USmB!X{m-JteDxTAu{WIadVmPk`RdMMMSadhXVz7PeQ` zIsSfP%)JrV8YhvSL@fd-v!yr68?+jyyz+V@`92~g!4q?7-o_?|eTP~h{dV{%Kc)W4m zwm9dnQs6AHt8cJtpso1;!z$(<@)anvnvcEqSI=rM!lWFT0y`_mBMjP8w0srmI6ENu zCG8)8nY?RdxoJ7-CnP`yk8Ach`rcA&)caC0GcQu@ca*z~rR@MVXsesZq%0D~1+}i; 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%0D(63pYYDbX1 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
aM_GDTg42*kyl+@tzG;? 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%0AqIj5fCiy2AU-XBhjH0b{)4KRnB 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 literal 0 HcmV?d00001 diff --git a/packages/video_player/video_player_android/example/assets/flutter-mark-square-64.png b/packages/video_player/video_player_android/example/assets/flutter-mark-square-64.png new file mode 100644 index 0000000000000000000000000000000000000000..56f22d5bd8f4a90724fcd9aca3a0ec85932c4a48 GIT binary patch literal 482 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU|i|x;uuoF`1Z2ugcwJGV;|p6 z=ni)jO-nO7y=V2ICr!s!^v(QOS^8?m8P&kV5BJt^?KqcXA$#u8o}as3UH)A6f7SB? zPp>{KSXli&>dLdr8;Y$S>6-c-)_L&xQvu z98wQbTSPu6HMxNV_H-=ZjEmz!=r$12SI%gi&KPyYV(MDis-vgRf9-qmWSZcb|Ht_E zJxN{YwyZ!uxa5G5)}dSf()Z{DoECW%_~Ty+L$T7Dmz76O-?{43ShmdL{&fa5mx#On z8m7DPd9a%G+A=}A2j$0S`b{@ry`_5YpWrmt zS-M=`IVU6vPLls`%O)tm2ttrBt*_x}KfIUy@9U-0TlteyfpN#+>FVdQ&MBb@01H0d A0ssI2 literal 0 HcmV?d00001 diff --git a/packages/video_player/video_player_android/example/integration_test/video_player_test.dart b/packages/video_player/video_player_android/example/integration_test/video_player_test.dart new file mode 100644 index 000000000000..b80ed745a6f9 --- /dev/null +++ b/packages/video_player/video_player_android/example/integration_test/video_player_test.dart @@ -0,0 +1,169 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:flutter/services.dart' show rootBundle; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:video_player_android/video_player_android.dart'; +// TODO(stuartmorgan): Remove the use of MiniController in tests, as that is +// testing test code; tests should instead be written directly against the +// platform interface. (These tests were copied from the app-facing package +// during federation and minimally modified, which is why they currently use the +// controller.) +import 'package:video_player_example/mini_controller.dart'; +import 'package:video_player_platform_interface/video_player_platform_interface.dart'; + +const Duration _playDuration = Duration(seconds: 1); + +const String _videoAssetKey = 'assets/Butterfly-209.mp4'; + +// Returns the URL to load an asset from this example app as a network source. +String getUrlForAssetAsNetworkSource(String assetKey) { + return 'https://github.com/flutter/plugins/blob/' + // This hash can be rolled forward to pick up newly-added assets. + 'cba393233e559c925a4daf71b06b4bb01c606762' + '/packages/video_player/video_player/example/' + '$assetKey' + '?raw=true'; +} + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + late MiniController _controller; + tearDown(() async => _controller.dispose()); + + group('asset videos', () { + setUp(() { + _controller = MiniController.asset(_videoAssetKey); + }); + + testWidgets('registers expected implementation', + (WidgetTester tester) async { + AndroidVideoPlayer.registerWith(); + expect(VideoPlayerPlatform.instance, isA()); + }); + + testWidgets('can be initialized', (WidgetTester tester) async { + await _controller.initialize(); + + expect(_controller.value.isInitialized, true); + expect(await _controller.position, const Duration(seconds: 0)); + expect(_controller.value.duration, + const Duration(seconds: 7, milliseconds: 540)); + }); + + testWidgets('can be played', (WidgetTester tester) async { + await _controller.initialize(); + + await _controller.play(); + await tester.pumpAndSettle(_playDuration); + + expect( + await _controller.position, greaterThan(const Duration(seconds: 0))); + }); + + testWidgets('can seek', (WidgetTester tester) async { + await _controller.initialize(); + + await _controller.seekTo(const Duration(seconds: 3)); + + expect(await _controller.position, const Duration(seconds: 3)); + }); + + testWidgets('can be paused', (WidgetTester tester) async { + await _controller.initialize(); + + // Play for a second, then pause, and then wait a second. + await _controller.play(); + await tester.pumpAndSettle(_playDuration); + await _controller.pause(); + await tester.pumpAndSettle(_playDuration); + final Duration pausedPosition = (await _controller.position)!; + await tester.pumpAndSettle(_playDuration); + + // Verify that we stopped playing after the pause. + expect(await _controller.position, pausedPosition); + }); + }); + + group('file-based videos', () { + setUp(() async { + // Load the data from the asset. + final String tempDir = (await getTemporaryDirectory()).path; + final ByteData bytes = await rootBundle.load(_videoAssetKey); + + // Write it to a file to use as a source. + final String filename = _videoAssetKey.split('/').last; + final File file = File('$tempDir/$filename'); + await file.writeAsBytes(bytes.buffer.asInt8List()); + + _controller = MiniController.file(file); + }); + + testWidgets('test video player using static file() method as constructor', + (WidgetTester tester) async { + await _controller.initialize(); + + await _controller.play(); + await tester.pumpAndSettle(_playDuration); + + expect( + await _controller.position, greaterThan(const Duration(seconds: 0))); + }); + }); + + group('network videos', () { + setUp(() { + final String videoUrl = getUrlForAssetAsNetworkSource(_videoAssetKey); + _controller = MiniController.network(videoUrl); + }); + + testWidgets('reports buffering status', (WidgetTester tester) async { + await _controller.initialize(); + + final Completer started = Completer(); + final Completer ended = Completer(); + _controller.addListener(() { + if (!started.isCompleted && _controller.value.isBuffering) { + started.complete(); + } + if (started.isCompleted && + !_controller.value.isBuffering && + !ended.isCompleted) { + ended.complete(); + } + }); + + await _controller.play(); + await _controller.seekTo(const Duration(seconds: 5)); + await tester.pumpAndSettle(_playDuration); + await _controller.pause(); + + expect( + await _controller.position, greaterThan(const Duration(seconds: 0))); + + await expectLater(started.future, completes); + await expectLater(ended.future, completes); + }); + + testWidgets('live stream duration != 0', (WidgetTester tester) async { + final MiniController livestreamController = MiniController.network( + 'https://cph-p2p-msl.akamaized.net/hls/live/2000341/test/master.m3u8', + ); + await livestreamController.initialize(); + + expect(livestreamController.value.isInitialized, true); + // Live streams should have either a positive duration or C.TIME_UNSET if the duration is unknown + // See https://exoplayer.dev/doc/reference/com/google/android/exoplayer2/Player.html#getDuration-- + expect(livestreamController.value.duration, + (Duration duration) => duration != Duration.zero); + }); + }); +} diff --git a/packages/video_player/video_player_android/example/lib/main.dart b/packages/video_player/video_player_android/example/lib/main.dart new file mode 100644 index 000000000000..cab6eb802ca5 --- /dev/null +++ b/packages/video_player/video_player_android/example/lib/main.dart @@ -0,0 +1,235 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +import 'mini_controller.dart'; + +void main() { + runApp( + MaterialApp( + home: _App(), + ), + ); +} + +class _App extends StatelessWidget { + @override + Widget build(BuildContext context) { + return DefaultTabController( + length: 2, + child: Scaffold( + key: const ValueKey('home_page'), + appBar: AppBar( + title: const Text('Video player example'), + bottom: const TabBar( + isScrollable: true, + tabs: [ + Tab( + icon: Icon(Icons.cloud), + text: 'Remote', + ), + Tab(icon: Icon(Icons.insert_drive_file), text: 'Asset'), + ], + ), + ), + body: TabBarView( + children: [ + _BumbleBeeRemoteVideo(), + _ButterFlyAssetVideo(), + ], + ), + ), + ); + } +} + +class _ButterFlyAssetVideo extends StatefulWidget { + @override + _ButterFlyAssetVideoState createState() => _ButterFlyAssetVideoState(); +} + +class _ButterFlyAssetVideoState extends State<_ButterFlyAssetVideo> { + late MiniController _controller; + + @override + void initState() { + super.initState(); + _controller = MiniController.asset('assets/Butterfly-209.mp4'); + + _controller.addListener(() { + setState(() {}); + }); + _controller.initialize().then((_) => setState(() {})); + _controller.play(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Column( + children: [ + Container( + padding: const EdgeInsets.only(top: 20.0), + ), + const Text('With assets mp4'), + Container( + padding: const EdgeInsets.all(20), + child: AspectRatio( + aspectRatio: _controller.value.aspectRatio, + child: Stack( + alignment: Alignment.bottomCenter, + children: [ + VideoPlayer(_controller), + _ControlsOverlay(controller: _controller), + VideoProgressIndicator(_controller), + ], + ), + ), + ), + ], + ), + ); + } +} + +class _BumbleBeeRemoteVideo extends StatefulWidget { + @override + _BumbleBeeRemoteVideoState createState() => _BumbleBeeRemoteVideoState(); +} + +class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { + late MiniController _controller; + + @override + void initState() { + super.initState(); + _controller = MiniController.network( + 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4', + ); + + _controller.addListener(() { + setState(() {}); + }); + _controller.initialize(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Column( + children: [ + Container(padding: const EdgeInsets.only(top: 20.0)), + const Text('With remote mp4'), + Container( + padding: const EdgeInsets.all(20), + child: AspectRatio( + aspectRatio: _controller.value.aspectRatio, + child: Stack( + alignment: Alignment.bottomCenter, + children: [ + VideoPlayer(_controller), + _ControlsOverlay(controller: _controller), + VideoProgressIndicator(_controller), + ], + ), + ), + ), + ], + ), + ); + } +} + +class _ControlsOverlay extends StatelessWidget { + const _ControlsOverlay({Key? key, required this.controller}) + : super(key: key); + + static const List _examplePlaybackRates = [ + 0.25, + 0.5, + 1.0, + 1.5, + 2.0, + 3.0, + 5.0, + 10.0, + ]; + + final MiniController controller; + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + AnimatedSwitcher( + duration: const Duration(milliseconds: 50), + reverseDuration: const Duration(milliseconds: 200), + child: controller.value.isPlaying + ? const SizedBox.shrink() + : Container( + color: Colors.black26, + child: const Center( + child: Icon( + Icons.play_arrow, + color: Colors.white, + size: 100.0, + semanticLabel: 'Play', + ), + ), + ), + ), + GestureDetector( + onTap: () { + controller.value.isPlaying ? controller.pause() : controller.play(); + }, + ), + Align( + alignment: Alignment.topRight, + child: PopupMenuButton( + initialValue: controller.value.playbackSpeed, + tooltip: 'Playback speed', + onSelected: (double speed) { + controller.setPlaybackSpeed(speed); + }, + itemBuilder: (BuildContext context) { + return >[ + for (final double speed in _examplePlaybackRates) + PopupMenuItem( + value: speed, + child: Text('${speed}x'), + ) + ]; + }, + child: Padding( + padding: const EdgeInsets.symmetric( + // Using less vertical padding as the text is also longer + // horizontally, so it feels like it would need more spacing + // horizontally (matching the aspect ratio of the video). + vertical: 12, + horizontal: 16, + ), + child: Text('${controller.value.playbackSpeed}x'), + ), + ), + ), + ], + ); + } +} diff --git a/packages/video_player/video_player_android/example/lib/mini_controller.dart b/packages/video_player/video_player_android/example/lib/mini_controller.dart new file mode 100644 index 000000000000..9bb8e90b65ae --- /dev/null +++ b/packages/video_player/video_player_android/example/lib/mini_controller.dart @@ -0,0 +1,538 @@ +// 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. + +// TODO(stuartmorgan): Consider extracting this to a shared local (path-based) +// package for use in all implementation packages. + +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:video_player_platform_interface/video_player_platform_interface.dart'; + +VideoPlayerPlatform? _cachedPlatform; + +VideoPlayerPlatform get _platform { + if (_cachedPlatform == null) { + _cachedPlatform = VideoPlayerPlatform.instance; + _cachedPlatform!.init(); + } + return _cachedPlatform!; +} + +/// The duration, current position, buffering state, error state and settings +/// of a [MiniController]. +class VideoPlayerValue { + /// Constructs a video with the given values. Only [duration] is required. The + /// rest will initialize with default values when unset. + VideoPlayerValue({ + required this.duration, + this.size = Size.zero, + this.position = Duration.zero, + this.buffered = const [], + this.isInitialized = false, + this.isPlaying = false, + this.isBuffering = false, + this.playbackSpeed = 1.0, + this.errorDescription, + }); + + /// Returns an instance for a video that hasn't been loaded. + VideoPlayerValue.uninitialized() + : this(duration: Duration.zero, isInitialized: false); + + /// Returns an instance with the given [errorDescription]. + VideoPlayerValue.erroneous(String errorDescription) + : this( + duration: Duration.zero, + isInitialized: false, + errorDescription: errorDescription); + + /// The total duration of the video. + /// + /// The duration is [Duration.zero] if the video hasn't been initialized. + final Duration duration; + + /// The current playback position. + final Duration position; + + /// The currently buffered ranges. + final List buffered; + + /// True if the video is playing. False if it's paused. + final bool isPlaying; + + /// True if the video is currently buffering. + final bool isBuffering; + + /// The current speed of the playback. + final double playbackSpeed; + + /// A description of the error if present. + /// + /// If [hasError] is false this is `null`. + final String? errorDescription; + + /// The [size] of the currently loaded video. + final Size size; + + /// Indicates whether or not the video has been loaded and is ready to play. + final bool isInitialized; + + /// Indicates whether or not the video is in an error state. If this is true + /// [errorDescription] should have information about the problem. + bool get hasError => errorDescription != null; + + /// Returns [size.width] / [size.height]. + /// + /// Will return `1.0` if: + /// * [isInitialized] is `false` + /// * [size.width], or [size.height] is equal to `0.0` + /// * aspect ratio would be less than or equal to `0.0` + double get aspectRatio { + if (!isInitialized || size.width == 0 || size.height == 0) { + return 1.0; + } + final double aspectRatio = size.width / size.height; + if (aspectRatio <= 0) { + return 1.0; + } + return aspectRatio; + } + + /// Returns a new instance that has the same values as this current instance, + /// except for any overrides passed in as arguments to [copyWidth]. + VideoPlayerValue copyWith({ + Duration? duration, + Size? size, + Duration? position, + List? buffered, + bool? isInitialized, + bool? isPlaying, + bool? isBuffering, + double? playbackSpeed, + String? errorDescription, + }) { + return VideoPlayerValue( + duration: duration ?? this.duration, + size: size ?? this.size, + position: position ?? this.position, + buffered: buffered ?? this.buffered, + isInitialized: isInitialized ?? this.isInitialized, + isPlaying: isPlaying ?? this.isPlaying, + isBuffering: isBuffering ?? this.isBuffering, + playbackSpeed: playbackSpeed ?? this.playbackSpeed, + errorDescription: errorDescription ?? this.errorDescription, + ); + } +} + +/// A very minimal version of `VideoPlayerController` for running the example +/// without relying on `video_player`. +class MiniController extends ValueNotifier { + /// Constructs a [MiniController] playing a video from an asset. + /// + /// The name of the asset is given by the [dataSource] argument and must not be + /// null. The [package] argument must be non-null when the asset comes from a + /// package and null otherwise. + MiniController.asset(this.dataSource, {this.package}) + : dataSourceType = DataSourceType.asset, + super(VideoPlayerValue(duration: Duration.zero)); + + /// Constructs a [MiniController] playing a video from obtained from + /// the network. + MiniController.network(this.dataSource) + : dataSourceType = DataSourceType.network, + package = null, + super(VideoPlayerValue(duration: Duration.zero)); + + /// Constructs a [MiniController] playing a video from obtained from a file. + MiniController.file(File file) + : dataSource = 'file://${file.path}', + dataSourceType = DataSourceType.file, + package = null, + super(VideoPlayerValue(duration: Duration.zero)); + + /// The URI to the video file. This will be in different formats depending on + /// the [DataSourceType] of the original video. + final String dataSource; + + /// Describes the type of data source this [MiniController] + /// is constructed with. + final DataSourceType dataSourceType; + + /// Only set for [asset] videos. The package that the asset was loaded from. + final String? package; + + Timer? _timer; + Completer? _creatingCompleter; + StreamSubscription? _eventSubscription; + + /// The id of a texture that hasn't been initialized. + @visibleForTesting + static const int kUninitializedTextureId = -1; + int _textureId = kUninitializedTextureId; + + /// This is just exposed for testing. It shouldn't be used by anyone depending + /// on the plugin. + @visibleForTesting + int get textureId => _textureId; + + /// Attempts to open the given [dataSource] and load metadata about the video. + Future initialize() async { + _creatingCompleter = Completer(); + + late DataSource dataSourceDescription; + switch (dataSourceType) { + case DataSourceType.asset: + dataSourceDescription = DataSource( + sourceType: DataSourceType.asset, + asset: dataSource, + package: package, + ); + break; + case DataSourceType.network: + dataSourceDescription = DataSource( + sourceType: DataSourceType.network, + uri: dataSource, + ); + break; + case DataSourceType.file: + dataSourceDescription = DataSource( + sourceType: DataSourceType.file, + uri: dataSource, + ); + break; + case DataSourceType.contentUri: + dataSourceDescription = DataSource( + sourceType: DataSourceType.contentUri, + uri: dataSource, + ); + break; + } + + _textureId = (await _platform.create(dataSourceDescription)) ?? + kUninitializedTextureId; + _creatingCompleter!.complete(null); + final Completer initializingCompleter = Completer(); + + void eventListener(VideoEvent event) { + switch (event.eventType) { + case VideoEventType.initialized: + value = value.copyWith( + duration: event.duration, + size: event.size, + isInitialized: event.duration != null, + ); + initializingCompleter.complete(null); + _platform.setVolume(_textureId, 1.0); + _platform.setLooping(_textureId, true); + _applyPlayPause(); + break; + case VideoEventType.completed: + pause().then((void pauseResult) => seekTo(value.duration)); + break; + case VideoEventType.bufferingUpdate: + value = value.copyWith(buffered: event.buffered); + break; + case VideoEventType.bufferingStart: + value = value.copyWith(isBuffering: true); + break; + case VideoEventType.bufferingEnd: + value = value.copyWith(isBuffering: false); + break; + case VideoEventType.unknown: + break; + } + } + + void errorListener(Object obj) { + final PlatformException e = obj as PlatformException; + value = VideoPlayerValue.erroneous(e.message!); + _timer?.cancel(); + if (!initializingCompleter.isCompleted) { + initializingCompleter.completeError(obj); + } + } + + _eventSubscription = _platform + .videoEventsFor(_textureId) + .listen(eventListener, onError: errorListener); + return initializingCompleter.future; + } + + @override + Future dispose() async { + if (_creatingCompleter != null) { + await _creatingCompleter!.future; + _timer?.cancel(); + await _eventSubscription?.cancel(); + await _platform.dispose(_textureId); + } + super.dispose(); + } + + /// Starts playing the video. + Future play() async { + value = value.copyWith(isPlaying: true); + await _applyPlayPause(); + } + + /// Pauses the video. + Future pause() async { + value = value.copyWith(isPlaying: false); + await _applyPlayPause(); + } + + Future _applyPlayPause() async { + _timer?.cancel(); + if (value.isPlaying) { + await _platform.play(_textureId); + + _timer = Timer.periodic( + const Duration(milliseconds: 500), + (Timer timer) async { + final Duration? newPosition = await position; + if (newPosition == null) { + return; + } + _updatePosition(newPosition); + }, + ); + await _applyPlaybackSpeed(); + } else { + await _platform.pause(_textureId); + } + } + + Future _applyPlaybackSpeed() async { + if (value.isPlaying) { + await _platform.setPlaybackSpeed( + _textureId, + value.playbackSpeed, + ); + } + } + + /// The position in the current video. + Future get position async { + return await _platform.getPosition(_textureId); + } + + /// Sets the video's current timestamp to be at [position]. + Future seekTo(Duration position) async { + if (position > value.duration) { + position = value.duration; + } else if (position < const Duration()) { + position = const Duration(); + } + await _platform.seekTo(_textureId, position); + _updatePosition(position); + } + + /// Sets the playback speed. + Future setPlaybackSpeed(double speed) async { + value = value.copyWith(playbackSpeed: speed); + await _applyPlaybackSpeed(); + } + + void _updatePosition(Duration position) { + value = value.copyWith(position: position); + } + + @override + void removeListener(VoidCallback listener) { + super.removeListener(listener); + } +} + +/// Widget that displays the video controlled by [controller]. +class VideoPlayer extends StatefulWidget { + /// Uses the given [controller] for all video rendered in this widget. + const VideoPlayer(this.controller); + + /// The [MiniController] responsible for the video being rendered in + /// this widget. + final MiniController controller; + + @override + _VideoPlayerState createState() => _VideoPlayerState(); +} + +class _VideoPlayerState extends State { + _VideoPlayerState() { + _listener = () { + final int newTextureId = widget.controller.textureId; + if (newTextureId != _textureId) { + setState(() { + _textureId = newTextureId; + }); + } + }; + } + + late VoidCallback _listener; + + late int _textureId; + + @override + void initState() { + super.initState(); + _textureId = widget.controller.textureId; + // Need to listen for initialization events since the actual texture ID + // becomes available after asynchronous initialization finishes. + widget.controller.addListener(_listener); + } + + @override + void didUpdateWidget(VideoPlayer oldWidget) { + super.didUpdateWidget(oldWidget); + oldWidget.controller.removeListener(_listener); + _textureId = widget.controller.textureId; + widget.controller.addListener(_listener); + } + + @override + void deactivate() { + super.deactivate(); + widget.controller.removeListener(_listener); + } + + @override + Widget build(BuildContext context) { + return _textureId == MiniController.kUninitializedTextureId + ? Container() + : _platform.buildView(_textureId); + } +} + +class _VideoScrubber extends StatefulWidget { + const _VideoScrubber({ + required this.child, + required this.controller, + }); + + final Widget child; + final MiniController controller; + + @override + _VideoScrubberState createState() => _VideoScrubberState(); +} + +class _VideoScrubberState extends State<_VideoScrubber> { + MiniController get controller => widget.controller; + + @override + Widget build(BuildContext context) { + void seekToRelativePosition(Offset globalPosition) { + final RenderBox box = context.findRenderObject()! as RenderBox; + final Offset tapPos = box.globalToLocal(globalPosition); + final double relative = tapPos.dx / box.size.width; + final Duration position = controller.value.duration * relative; + controller.seekTo(position); + } + + return GestureDetector( + behavior: HitTestBehavior.opaque, + child: widget.child, + onTapDown: (TapDownDetails details) { + if (controller.value.isInitialized) { + seekToRelativePosition(details.globalPosition); + } + }, + ); + } +} + +/// Displays the play/buffering status of the video controlled by [controller]. +class VideoProgressIndicator extends StatefulWidget { + /// Construct an instance that displays the play/buffering status of the video + /// controlled by [controller]. + const VideoProgressIndicator(this.controller); + + /// The [MiniController] that actually associates a video with this + /// widget. + final MiniController controller; + + @override + _VideoProgressIndicatorState createState() => _VideoProgressIndicatorState(); +} + +class _VideoProgressIndicatorState extends State { + _VideoProgressIndicatorState() { + listener = () { + if (mounted) { + setState(() {}); + } + }; + } + + late VoidCallback listener; + + MiniController get controller => widget.controller; + + @override + void initState() { + super.initState(); + controller.addListener(listener); + } + + @override + void deactivate() { + controller.removeListener(listener); + super.deactivate(); + } + + @override + Widget build(BuildContext context) { + const Color playedColor = Color.fromRGBO(255, 0, 0, 0.7); + const Color bufferedColor = Color.fromRGBO(50, 50, 200, 0.2); + const Color backgroundColor = Color.fromRGBO(200, 200, 200, 0.5); + + Widget progressIndicator; + if (controller.value.isInitialized) { + final int duration = controller.value.duration.inMilliseconds; + final int position = controller.value.position.inMilliseconds; + + int maxBuffering = 0; + for (final DurationRange range in controller.value.buffered) { + final int end = range.end.inMilliseconds; + if (end > maxBuffering) { + maxBuffering = end; + } + } + + progressIndicator = Stack( + fit: StackFit.passthrough, + children: [ + LinearProgressIndicator( + value: maxBuffering / duration, + valueColor: const AlwaysStoppedAnimation(bufferedColor), + backgroundColor: backgroundColor, + ), + LinearProgressIndicator( + value: position / duration, + valueColor: const AlwaysStoppedAnimation(playedColor), + backgroundColor: Colors.transparent, + ), + ], + ); + } else { + progressIndicator = const LinearProgressIndicator( + value: null, + valueColor: AlwaysStoppedAnimation(playedColor), + backgroundColor: backgroundColor, + ); + } + return _VideoScrubber( + child: Padding( + padding: const EdgeInsets.only(top: 5.0), + child: progressIndicator, + ), + controller: controller, + ); + } +} diff --git a/packages/video_player/video_player_android/example/pubspec.yaml b/packages/video_player/video_player_android/example/pubspec.yaml new file mode 100644 index 000000000000..14c06f4123d4 --- /dev/null +++ b/packages/video_player/video_player_android/example/pubspec.yaml @@ -0,0 +1,35 @@ +name: video_player_example +description: Demonstrates how to use the video_player plugin. +publish_to: none + +environment: + sdk: ">=2.12.0 <3.0.0" + flutter: ">=2.8.0" + +dependencies: + flutter: + sdk: flutter + video_player_android: + # When depending on this package from a real application you should use: + # video_player_android: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + video_player_platform_interface: ">=4.2.0 <6.0.0" + +dev_dependencies: + flutter_driver: + sdk: flutter + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + path_provider: ^2.0.6 + test: any + +flutter: + uses-material-design: true + assets: + - assets/flutter-mark-square-64.png + - assets/Butterfly-209.mp4 diff --git a/packages/video_player/video_player_android/example/test_driver/integration_test.dart b/packages/video_player/video_player_android/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4f10f2a522f3 --- /dev/null +++ b/packages/video_player/video_player_android/example/test_driver/integration_test.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); diff --git a/packages/video_player/video_player_android/example/test_driver/video_player.dart b/packages/video_player/video_player_android/example/test_driver/video_player.dart new file mode 100644 index 000000000000..b72354e2187f --- /dev/null +++ b/packages/video_player/video_player_android/example/test_driver/video_player.dart @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_driver/driver_extension.dart'; +import 'package:video_player_example/main.dart' as app; + +void main() { + enableFlutterDriverExtension(); + app.main(); +} diff --git a/packages/video_player/video_player_android/lib/src/android_video_player.dart b/packages/video_player/video_player_android/lib/src/android_video_player.dart new file mode 100644 index 000000000000..adb386afa2b8 --- /dev/null +++ b/packages/video_player/video_player_android/lib/src/android_video_player.dart @@ -0,0 +1,173 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:ui'; + +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:video_player_platform_interface/video_player_platform_interface.dart'; + +import 'messages.dart'; + +/// An Android implementation of [VideoPlayerPlatform] that uses the +/// Pigeon-generated [VideoPlayerApi]. +class AndroidVideoPlayer extends VideoPlayerPlatform { + final VideoPlayerApi _api = VideoPlayerApi(); + + /// Registers this class as the default instance of [PathProviderPlatform]. + static void registerWith() { + VideoPlayerPlatform.instance = AndroidVideoPlayer(); + } + + @override + Future init() { + return _api.initialize(); + } + + @override + Future dispose(int textureId) { + return _api.dispose(TextureMessage()..textureId = textureId); + } + + @override + Future create(DataSource dataSource) async { + final CreateMessage message = CreateMessage(); + + switch (dataSource.sourceType) { + case DataSourceType.asset: + message.asset = dataSource.asset; + message.packageName = dataSource.package; + break; + case DataSourceType.network: + message.uri = dataSource.uri; + message.formatHint = _videoFormatStringMap[dataSource.formatHint]; + message.httpHeaders = dataSource.httpHeaders; + break; + case DataSourceType.file: + message.uri = dataSource.uri; + break; + case DataSourceType.contentUri: + message.uri = dataSource.uri; + break; + } + + final TextureMessage response = await _api.create(message); + return response.textureId; + } + + @override + Future setLooping(int textureId, bool looping) { + return _api.setLooping(LoopingMessage() + ..textureId = textureId + ..isLooping = looping); + } + + @override + Future play(int textureId) { + return _api.play(TextureMessage()..textureId = textureId); + } + + @override + Future pause(int textureId) { + return _api.pause(TextureMessage()..textureId = textureId); + } + + @override + Future setVolume(int textureId, double volume) { + return _api.setVolume(VolumeMessage() + ..textureId = textureId + ..volume = volume); + } + + @override + Future setPlaybackSpeed(int textureId, double speed) { + assert(speed > 0); + + return _api.setPlaybackSpeed(PlaybackSpeedMessage() + ..textureId = textureId + ..speed = speed); + } + + @override + Future seekTo(int textureId, Duration position) { + return _api.seekTo(PositionMessage() + ..textureId = textureId + ..position = position.inMilliseconds); + } + + @override + Future getPosition(int textureId) async { + final PositionMessage response = + await _api.position(TextureMessage()..textureId = textureId); + return Duration(milliseconds: response.position!); + } + + @override + Stream videoEventsFor(int textureId) { + return _eventChannelFor(textureId) + .receiveBroadcastStream() + .map((dynamic event) { + final Map map = event as Map; + switch (map['event']) { + case 'initialized': + return VideoEvent( + eventType: VideoEventType.initialized, + duration: Duration(milliseconds: map['duration'] as int), + size: Size((map['width'] as num?)?.toDouble() ?? 0.0, + (map['height'] as num?)?.toDouble() ?? 0.0), + ); + case 'completed': + return VideoEvent( + eventType: VideoEventType.completed, + ); + case 'bufferingUpdate': + final List values = map['values'] as List; + + return VideoEvent( + buffered: values.map(_toDurationRange).toList(), + eventType: VideoEventType.bufferingUpdate, + ); + case 'bufferingStart': + return VideoEvent(eventType: VideoEventType.bufferingStart); + case 'bufferingEnd': + return VideoEvent(eventType: VideoEventType.bufferingEnd); + default: + return VideoEvent(eventType: VideoEventType.unknown); + } + }); + } + + @override + Widget buildView(int textureId) { + return Texture(textureId: textureId); + } + + @override + Future setMixWithOthers(bool mixWithOthers) { + return _api.setMixWithOthers( + MixWithOthersMessage()..mixWithOthers = mixWithOthers, + ); + } + + EventChannel _eventChannelFor(int textureId) { + return EventChannel('flutter.io/videoPlayer/videoEvents$textureId'); + } + + static const Map _videoFormatStringMap = + { + VideoFormat.ss: 'ss', + VideoFormat.hls: 'hls', + VideoFormat.dash: 'dash', + VideoFormat.other: 'other', + }; + + DurationRange _toDurationRange(dynamic value) { + final List pair = value as List; + return DurationRange( + Duration(milliseconds: pair[0] as int), + Duration(milliseconds: pair[1] as int), + ); + } +} diff --git a/packages/video_player/video_player_android/lib/src/messages.dart b/packages/video_player/video_player_android/lib/src/messages.dart new file mode 100644 index 000000000000..831f4e3755d9 --- /dev/null +++ b/packages/video_player/video_player_android/lib/src/messages.dart @@ -0,0 +1,425 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Autogenerated from Pigeon (v0.1.21), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, cast_nullable_to_non_nullable +// @dart = 2.12 +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; + +import 'package:flutter/services.dart'; + +class TextureMessage { + int? textureId; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['textureId'] = textureId; + return pigeonMap; + } + + static TextureMessage decode(Object message) { + final Map pigeonMap = message as Map; + return TextureMessage()..textureId = pigeonMap['textureId'] as int?; + } +} + +class CreateMessage { + String? asset; + String? uri; + String? packageName; + String? formatHint; + Map? httpHeaders; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['asset'] = asset; + pigeonMap['uri'] = uri; + pigeonMap['packageName'] = packageName; + pigeonMap['formatHint'] = formatHint; + pigeonMap['httpHeaders'] = httpHeaders; + return pigeonMap; + } + + static CreateMessage decode(Object message) { + final Map pigeonMap = message as Map; + return CreateMessage() + ..asset = pigeonMap['asset'] as String? + ..uri = pigeonMap['uri'] as String? + ..packageName = pigeonMap['packageName'] as String? + ..formatHint = pigeonMap['formatHint'] as String? + ..httpHeaders = pigeonMap['httpHeaders'] as Map?; + } +} + +class LoopingMessage { + int? textureId; + bool? isLooping; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['textureId'] = textureId; + pigeonMap['isLooping'] = isLooping; + return pigeonMap; + } + + static LoopingMessage decode(Object message) { + final Map pigeonMap = message as Map; + return LoopingMessage() + ..textureId = pigeonMap['textureId'] as int? + ..isLooping = pigeonMap['isLooping'] as bool?; + } +} + +class VolumeMessage { + int? textureId; + double? volume; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['textureId'] = textureId; + pigeonMap['volume'] = volume; + return pigeonMap; + } + + static VolumeMessage decode(Object message) { + final Map pigeonMap = message as Map; + return VolumeMessage() + ..textureId = pigeonMap['textureId'] as int? + ..volume = pigeonMap['volume'] as double?; + } +} + +class PlaybackSpeedMessage { + int? textureId; + double? speed; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['textureId'] = textureId; + pigeonMap['speed'] = speed; + return pigeonMap; + } + + static PlaybackSpeedMessage decode(Object message) { + final Map pigeonMap = message as Map; + return PlaybackSpeedMessage() + ..textureId = pigeonMap['textureId'] as int? + ..speed = pigeonMap['speed'] as double?; + } +} + +class PositionMessage { + int? textureId; + int? position; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['textureId'] = textureId; + pigeonMap['position'] = position; + return pigeonMap; + } + + static PositionMessage decode(Object message) { + final Map pigeonMap = message as Map; + return PositionMessage() + ..textureId = pigeonMap['textureId'] as int? + ..position = pigeonMap['position'] as int?; + } +} + +class MixWithOthersMessage { + bool? mixWithOthers; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['mixWithOthers'] = mixWithOthers; + return pigeonMap; + } + + static MixWithOthersMessage decode(Object message) { + final Map pigeonMap = message as Map; + return MixWithOthersMessage() + ..mixWithOthers = pigeonMap['mixWithOthers'] as bool?; + } +} + +class VideoPlayerApi { + Future initialize() async { + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.initialize', StandardMessageCodec()); + final Map? replyMap = + await channel.send(null) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + replyMap['error'] as Map; + throw PlatformException( + code: error['code'] as String, + message: error['message'] as String?, + details: error['details'], + ); + } else { + // noop + } + } + + Future create(CreateMessage arg) async { + final Object encoded = arg.encode(); + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.create', StandardMessageCodec()); + final Map? replyMap = + await channel.send(encoded) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + replyMap['error'] as Map; + throw PlatformException( + code: error['code'] as String, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return TextureMessage.decode(replyMap['result']!); + } + } + + Future dispose(TextureMessage arg) async { + final Object encoded = arg.encode(); + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.dispose', StandardMessageCodec()); + final Map? replyMap = + await channel.send(encoded) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + replyMap['error'] as Map; + throw PlatformException( + code: error['code'] as String, + message: error['message'] as String?, + details: error['details'], + ); + } else { + // noop + } + } + + Future setLooping(LoopingMessage arg) async { + final Object encoded = arg.encode(); + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setLooping', StandardMessageCodec()); + final Map? replyMap = + await channel.send(encoded) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + replyMap['error'] as Map; + throw PlatformException( + code: error['code'] as String, + message: error['message'] as String?, + details: error['details'], + ); + } else { + // noop + } + } + + Future setVolume(VolumeMessage arg) async { + final Object encoded = arg.encode(); + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setVolume', StandardMessageCodec()); + final Map? replyMap = + await channel.send(encoded) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + replyMap['error'] as Map; + throw PlatformException( + code: error['code'] as String, + message: error['message'] as String?, + details: error['details'], + ); + } else { + // noop + } + } + + Future setPlaybackSpeed(PlaybackSpeedMessage arg) async { + final Object encoded = arg.encode(); + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed', + StandardMessageCodec()); + final Map? replyMap = + await channel.send(encoded) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + replyMap['error'] as Map; + throw PlatformException( + code: error['code'] as String, + message: error['message'] as String?, + details: error['details'], + ); + } else { + // noop + } + } + + Future play(TextureMessage arg) async { + final Object encoded = arg.encode(); + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.play', StandardMessageCodec()); + final Map? replyMap = + await channel.send(encoded) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + replyMap['error'] as Map; + throw PlatformException( + code: error['code'] as String, + message: error['message'] as String?, + details: error['details'], + ); + } else { + // noop + } + } + + Future position(TextureMessage arg) async { + final Object encoded = arg.encode(); + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.position', StandardMessageCodec()); + final Map? replyMap = + await channel.send(encoded) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + replyMap['error'] as Map; + throw PlatformException( + code: error['code'] as String, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return PositionMessage.decode(replyMap['result']!); + } + } + + Future seekTo(PositionMessage arg) async { + final Object encoded = arg.encode(); + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.seekTo', StandardMessageCodec()); + final Map? replyMap = + await channel.send(encoded) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + replyMap['error'] as Map; + throw PlatformException( + code: error['code'] as String, + message: error['message'] as String?, + details: error['details'], + ); + } else { + // noop + } + } + + Future pause(TextureMessage arg) async { + final Object encoded = arg.encode(); + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.pause', StandardMessageCodec()); + final Map? replyMap = + await channel.send(encoded) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + replyMap['error'] as Map; + throw PlatformException( + code: error['code'] as String, + message: error['message'] as String?, + details: error['details'], + ); + } else { + // noop + } + } + + Future setMixWithOthers(MixWithOthersMessage arg) async { + final Object encoded = arg.encode(); + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers', + StandardMessageCodec()); + final Map? replyMap = + await channel.send(encoded) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + replyMap['error'] as Map; + throw PlatformException( + code: error['code'] as String, + message: error['message'] as String?, + details: error['details'], + ); + } else { + // noop + } + } +} diff --git a/packages/video_player/video_player_android/lib/video_player_android.dart b/packages/video_player/video_player_android/lib/video_player_android.dart new file mode 100644 index 000000000000..4e06756f1529 --- /dev/null +++ b/packages/video_player/video_player_android/lib/video_player_android.dart @@ -0,0 +1,5 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +export 'src/android_video_player.dart'; diff --git a/packages/video_player/video_player/pigeons/messages.dart b/packages/video_player/video_player_android/pigeons/messages.dart similarity index 91% rename from packages/video_player/video_player/pigeons/messages.dart rename to packages/video_player/video_player_android/pigeons/messages.dart index e893aaa6830d..42d96408da87 100644 --- a/packages/video_player/video_player/pigeons/messages.dart +++ b/packages/video_player/video_player_android/pigeons/messages.dart @@ -58,8 +58,8 @@ abstract class VideoPlayerApi { } void configurePigeon(PigeonOptions opts) { - opts.dartOut = '../video_player_platform_interface/lib/messages.dart'; - opts.dartTestOut = '../video_player_platform_interface/lib/test.dart'; + opts.dartOut = 'lib/src/messages.dart'; + opts.dartTestOut = 'test/test_api.dart'; opts.objcHeaderOut = 'ios/Classes/messages.h'; opts.objcSourceOut = 'ios/Classes/messages.m'; opts.objcOptions.prefix = 'FLT'; diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml new file mode 100644 index 000000000000..423c5ade0ac8 --- /dev/null +++ b/packages/video_player/video_player_android/pubspec.yaml @@ -0,0 +1,28 @@ +name: video_player_android +description: Android implementation of the video_player plugin. +repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_android +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 +version: 2.2.17 + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.8.0" + +flutter: + plugin: + implements: video_player + platforms: + android: + dartPluginClass: AndroidVideoPlayer + package: io.flutter.plugins.videoplayer + pluginClass: VideoPlayerPlugin + +dependencies: + flutter: + sdk: flutter + video_player_platform_interface: ">=4.2.0 <6.0.0" + +dev_dependencies: + flutter_test: + sdk: flutter + pigeon: ^0.1.21 diff --git a/packages/video_player/video_player_android/test/android_video_player_test.dart b/packages/video_player/video_player_android/test/android_video_player_test.dart new file mode 100644 index 000000000000..9eac540160a0 --- /dev/null +++ b/packages/video_player/video_player_android/test/android_video_player_test.dart @@ -0,0 +1,341 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:ui'; + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:video_player_android/src/messages.dart'; +import 'package:video_player_android/video_player_android.dart'; +import 'package:video_player_platform_interface/video_player_platform_interface.dart'; + +import 'test_api.dart'; + +class _ApiLogger implements TestHostVideoPlayerApi { + final List log = []; + TextureMessage? textureMessage; + CreateMessage? createMessage; + PositionMessage? positionMessage; + LoopingMessage? loopingMessage; + VolumeMessage? volumeMessage; + PlaybackSpeedMessage? playbackSpeedMessage; + MixWithOthersMessage? mixWithOthersMessage; + + @override + TextureMessage create(CreateMessage arg) { + log.add('create'); + createMessage = arg; + return TextureMessage()..textureId = 3; + } + + @override + void dispose(TextureMessage arg) { + log.add('dispose'); + textureMessage = arg; + } + + @override + void initialize() { + log.add('init'); + } + + @override + void pause(TextureMessage arg) { + log.add('pause'); + textureMessage = arg; + } + + @override + void play(TextureMessage arg) { + log.add('play'); + textureMessage = arg; + } + + @override + void setMixWithOthers(MixWithOthersMessage arg) { + log.add('setMixWithOthers'); + mixWithOthersMessage = arg; + } + + @override + PositionMessage position(TextureMessage arg) { + log.add('position'); + textureMessage = arg; + return PositionMessage()..position = 234; + } + + @override + void seekTo(PositionMessage arg) { + log.add('seekTo'); + positionMessage = arg; + } + + @override + void setLooping(LoopingMessage arg) { + log.add('setLooping'); + loopingMessage = arg; + } + + @override + void setVolume(VolumeMessage arg) { + log.add('setVolume'); + volumeMessage = arg; + } + + @override + void setPlaybackSpeed(PlaybackSpeedMessage arg) { + log.add('setPlaybackSpeed'); + playbackSpeedMessage = arg; + } +} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + test('registration', () async { + AndroidVideoPlayer.registerWith(); + expect(VideoPlayerPlatform.instance, isA()); + }); + + group('$AndroidVideoPlayer', () { + final AndroidVideoPlayer player = AndroidVideoPlayer(); + late _ApiLogger log; + + setUp(() { + log = _ApiLogger(); + TestHostVideoPlayerApi.setup(log); + }); + + test('init', () async { + await player.init(); + expect( + log.log.last, + 'init', + ); + }); + + test('dispose', () async { + await player.dispose(1); + expect(log.log.last, 'dispose'); + expect(log.textureMessage?.textureId, 1); + }); + + test('create with asset', () async { + final int? textureId = await player.create(DataSource( + sourceType: DataSourceType.asset, + asset: 'someAsset', + package: 'somePackage', + )); + expect(log.log.last, 'create'); + expect(log.createMessage?.asset, 'someAsset'); + expect(log.createMessage?.packageName, 'somePackage'); + expect(textureId, 3); + }); + + test('create with network', () async { + final int? textureId = await player.create(DataSource( + sourceType: DataSourceType.network, + uri: 'someUri', + formatHint: VideoFormat.dash, + )); + expect(log.log.last, 'create'); + expect(log.createMessage?.asset, null); + expect(log.createMessage?.uri, 'someUri'); + expect(log.createMessage?.packageName, null); + expect(log.createMessage?.formatHint, 'dash'); + expect(log.createMessage?.httpHeaders, {}); + expect(textureId, 3); + }); + + test('create with network (some headers)', () async { + final int? textureId = await player.create(DataSource( + sourceType: DataSourceType.network, + uri: 'someUri', + httpHeaders: {'Authorization': 'Bearer token'}, + )); + expect(log.log.last, 'create'); + expect(log.createMessage?.asset, null); + expect(log.createMessage?.uri, 'someUri'); + expect(log.createMessage?.packageName, null); + expect(log.createMessage?.formatHint, null); + expect(log.createMessage?.httpHeaders, + {'Authorization': 'Bearer token'}); + expect(textureId, 3); + }); + + test('create with file', () async { + final int? textureId = await player.create(DataSource( + sourceType: DataSourceType.file, + uri: 'someUri', + )); + expect(log.log.last, 'create'); + expect(log.createMessage?.uri, 'someUri'); + expect(textureId, 3); + }); + + test('setLooping', () async { + await player.setLooping(1, true); + expect(log.log.last, 'setLooping'); + expect(log.loopingMessage?.textureId, 1); + expect(log.loopingMessage?.isLooping, true); + }); + + test('play', () async { + await player.play(1); + expect(log.log.last, 'play'); + expect(log.textureMessage?.textureId, 1); + }); + + test('pause', () async { + await player.pause(1); + expect(log.log.last, 'pause'); + expect(log.textureMessage?.textureId, 1); + }); + + test('setMixWithOthers', () async { + await player.setMixWithOthers(true); + expect(log.log.last, 'setMixWithOthers'); + expect(log.mixWithOthersMessage?.mixWithOthers, true); + + await player.setMixWithOthers(false); + expect(log.log.last, 'setMixWithOthers'); + expect(log.mixWithOthersMessage?.mixWithOthers, false); + }); + + test('setVolume', () async { + await player.setVolume(1, 0.7); + expect(log.log.last, 'setVolume'); + expect(log.volumeMessage?.textureId, 1); + expect(log.volumeMessage?.volume, 0.7); + }); + + test('setPlaybackSpeed', () async { + await player.setPlaybackSpeed(1, 1.5); + expect(log.log.last, 'setPlaybackSpeed'); + expect(log.playbackSpeedMessage?.textureId, 1); + expect(log.playbackSpeedMessage?.speed, 1.5); + }); + + test('seekTo', () async { + await player.seekTo(1, const Duration(milliseconds: 12345)); + expect(log.log.last, 'seekTo'); + expect(log.positionMessage?.textureId, 1); + expect(log.positionMessage?.position, 12345); + }); + + test('getPosition', () async { + final Duration position = await player.getPosition(1); + expect(log.log.last, 'position'); + expect(log.textureMessage?.textureId, 1); + expect(position, const Duration(milliseconds: 234)); + }); + + test('videoEventsFor', () async { + _ambiguate(ServicesBinding.instance) + ?.defaultBinaryMessenger + .setMockMessageHandler( + 'flutter.io/videoPlayer/videoEvents123', + (ByteData? message) async { + final MethodCall methodCall = + const StandardMethodCodec().decodeMethodCall(message); + if (methodCall.method == 'listen') { + await _ambiguate(ServicesBinding.instance) + ?.defaultBinaryMessenger + .handlePlatformMessage( + 'flutter.io/videoPlayer/videoEvents123', + const StandardMethodCodec() + .encodeSuccessEnvelope({ + 'event': 'initialized', + 'duration': 98765, + 'width': 1920, + 'height': 1080, + }), + (ByteData? data) {}); + + await _ambiguate(ServicesBinding.instance) + ?.defaultBinaryMessenger + .handlePlatformMessage( + 'flutter.io/videoPlayer/videoEvents123', + const StandardMethodCodec() + .encodeSuccessEnvelope({ + 'event': 'completed', + }), + (ByteData? data) {}); + + await _ambiguate(ServicesBinding.instance) + ?.defaultBinaryMessenger + .handlePlatformMessage( + 'flutter.io/videoPlayer/videoEvents123', + const StandardMethodCodec() + .encodeSuccessEnvelope({ + 'event': 'bufferingUpdate', + 'values': >[ + [0, 1234], + [1235, 4000], + ], + }), + (ByteData? data) {}); + + await _ambiguate(ServicesBinding.instance) + ?.defaultBinaryMessenger + .handlePlatformMessage( + 'flutter.io/videoPlayer/videoEvents123', + const StandardMethodCodec() + .encodeSuccessEnvelope({ + 'event': 'bufferingStart', + }), + (ByteData? data) {}); + + await _ambiguate(ServicesBinding.instance) + ?.defaultBinaryMessenger + .handlePlatformMessage( + 'flutter.io/videoPlayer/videoEvents123', + const StandardMethodCodec() + .encodeSuccessEnvelope({ + 'event': 'bufferingEnd', + }), + (ByteData? data) {}); + + return const StandardMethodCodec().encodeSuccessEnvelope(null); + } else if (methodCall.method == 'cancel') { + return const StandardMethodCodec().encodeSuccessEnvelope(null); + } else { + fail('Expected listen or cancel'); + } + }, + ); + expect( + player.videoEventsFor(123), + emitsInOrder([ + VideoEvent( + eventType: VideoEventType.initialized, + duration: const Duration(milliseconds: 98765), + size: const Size(1920, 1080), + ), + VideoEvent(eventType: VideoEventType.completed), + VideoEvent( + eventType: VideoEventType.bufferingUpdate, + buffered: [ + DurationRange( + const Duration(milliseconds: 0), + const Duration(milliseconds: 1234), + ), + DurationRange( + const Duration(milliseconds: 1235), + const Duration(milliseconds: 4000), + ), + ]), + VideoEvent(eventType: VideoEventType.bufferingStart), + VideoEvent(eventType: VideoEventType.bufferingEnd), + ])); + }); + }); +} + +/// This allows a value of type T or T? to be treated as a value of type T?. +/// +/// We use this so that APIs that have become non-nullable can still be used +/// with `!` and `?` on the stable branch. +// TODO(ianh): Remove this once we roll stable in late 2021. +T? _ambiguate(T? value) => value; diff --git a/packages/video_player/video_player_android/test/test_api.dart b/packages/video_player/video_player_android/test/test_api.dart new file mode 100644 index 000000000000..2fbcbcf9e898 --- /dev/null +++ b/packages/video_player/video_player_android/test/test_api.dart @@ -0,0 +1,200 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Autogenerated from Pigeon (v0.1.21), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import +// @dart = 2.12 +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:video_player_android/src/messages.dart'; + +abstract class TestHostVideoPlayerApi { + void initialize(); + TextureMessage create(CreateMessage arg); + void dispose(TextureMessage arg); + void setLooping(LoopingMessage arg); + void setVolume(VolumeMessage arg); + void setPlaybackSpeed(PlaybackSpeedMessage arg); + void play(TextureMessage arg); + PositionMessage position(TextureMessage arg); + void seekTo(PositionMessage arg); + void pause(TextureMessage arg); + void setMixWithOthers(MixWithOthersMessage arg); + static void setup(TestHostVideoPlayerApi? api) { + { + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.initialize', + StandardMessageCodec()); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + // ignore message + api.initialize(); + return {}; + }); + } + } + { + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.create', StandardMessageCodec()); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.create was null. Expected CreateMessage.'); + final CreateMessage input = CreateMessage.decode(message!); + final TextureMessage output = api.create(input); + return {'result': output.encode()}; + }); + } + } + { + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.dispose', StandardMessageCodec()); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.dispose was null. Expected TextureMessage.'); + final TextureMessage input = TextureMessage.decode(message!); + api.dispose(input); + return {}; + }); + } + } + { + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setLooping', + StandardMessageCodec()); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.setLooping was null. Expected LoopingMessage.'); + final LoopingMessage input = LoopingMessage.decode(message!); + api.setLooping(input); + return {}; + }); + } + } + { + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setVolume', + StandardMessageCodec()); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.setVolume was null. Expected VolumeMessage.'); + final VolumeMessage input = VolumeMessage.decode(message!); + api.setVolume(input); + return {}; + }); + } + } + { + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed', + StandardMessageCodec()); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed was null. Expected PlaybackSpeedMessage.'); + final PlaybackSpeedMessage input = + PlaybackSpeedMessage.decode(message!); + api.setPlaybackSpeed(input); + return {}; + }); + } + } + { + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.play', StandardMessageCodec()); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.play was null. Expected TextureMessage.'); + final TextureMessage input = TextureMessage.decode(message!); + api.play(input); + return {}; + }); + } + } + { + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.position', StandardMessageCodec()); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.position was null. Expected TextureMessage.'); + final TextureMessage input = TextureMessage.decode(message!); + final PositionMessage output = api.position(input); + return {'result': output.encode()}; + }); + } + } + { + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.seekTo', StandardMessageCodec()); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.seekTo was null. Expected PositionMessage.'); + final PositionMessage input = PositionMessage.decode(message!); + api.seekTo(input); + return {}; + }); + } + } + { + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.pause', StandardMessageCodec()); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.pause was null. Expected TextureMessage.'); + final TextureMessage input = TextureMessage.decode(message!); + api.pause(input); + return {}; + }); + } + } + { + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers', + StandardMessageCodec()); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers was null. Expected MixWithOthersMessage.'); + final MixWithOthersMessage input = + MixWithOthersMessage.decode(message!); + api.setMixWithOthers(input); + return {}; + }); + } + } + } +} diff --git a/packages/video_player/video_player_avfoundation/AUTHORS b/packages/video_player/video_player_avfoundation/AUTHORS new file mode 100644 index 000000000000..493a0b4ef9c2 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/AUTHORS @@ -0,0 +1,66 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md new file mode 100644 index 000000000000..626b9ef887cf --- /dev/null +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -0,0 +1,3 @@ +## 2.2.17 + +* Splits from `video_player` as a federated implementation. diff --git a/packages/video_player/video_player_avfoundation/CONTRIBUTING.md b/packages/video_player/video_player_avfoundation/CONTRIBUTING.md new file mode 100644 index 000000000000..387551bda2f6 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/CONTRIBUTING.md @@ -0,0 +1,33 @@ +## Updating pigeon-generated files + +If you update files in the pigeons/ directory, run the following +command in this directory (ignore the errors you get about +dependencies in the examples directory): + +```bash +flutter pub upgrade +flutter pub run pigeon --dart_null_safety --input pigeons/messages.dart +# git commit your changes so that your working environment is clean +(cd ../../../; ./script/tool_runner.sh format --clang-format=clang-format-7) +``` + +If you update pigeon itself and want to test the changes here, +temporarily update the pubspec.yaml by adding the following to the +`dependency_overrides` section, assuming you have checked out the +`flutter/packages` repo in a sibling directory to the `plugins` repo: + +```yaml + pigeon: + path: + ../../../../packages/packages/pigeon/ +``` + +Then, run the commands above. When you run `pub get` it should warn +you that you're using an override. If you do this, you will need to +publish pigeon before you can land the updates to this package, since +the CI tests run the analysis using latest published version of +pigeon, not your version or the version on master. + +In either case, the configuration will be obtained automatically from +the `pigeons/messages.dart` file (see `configurePigeon` at the bottom +of that file). diff --git a/packages/video_player/video_player_avfoundation/LICENSE b/packages/video_player/video_player_avfoundation/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/LICENSE @@ -0,0 +1,25 @@ +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/video_player/video_player_avfoundation/README.md b/packages/video_player/video_player_avfoundation/README.md new file mode 100644 index 000000000000..97e028cf8cf5 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/README.md @@ -0,0 +1,11 @@ +# video\_player\_avfoundation + +The iOS implementation of [`video_player`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use `video_player` +normally. This package will be automatically included in your app when you do. + +[1]: https://pub.dev/packages/video_player +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin diff --git a/packages/video_player/video_player_avfoundation/example/.gitignore b/packages/video_player/video_player_avfoundation/example/.gitignore new file mode 100644 index 000000000000..d3e68fd01e5d --- /dev/null +++ b/packages/video_player/video_player_avfoundation/example/.gitignore @@ -0,0 +1 @@ +lib/generated_plugin_registrant.dart diff --git a/packages/video_player/video_player_avfoundation/example/README.md b/packages/video_player/video_player_avfoundation/example/README.md new file mode 100644 index 000000000000..8ceb0ff485fa --- /dev/null +++ b/packages/video_player/video_player_avfoundation/example/README.md @@ -0,0 +1,8 @@ +# video_player_example + +Demonstrates how to use the video_player plugin. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](https://flutter.dev/). diff --git a/packages/video_player/video_player_avfoundation/example/assets/Butterfly-209.mp4 b/packages/video_player/video_player_avfoundation/example/assets/Butterfly-209.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..c8489799f549457b017664d6eb7ccbd82ae5bff4 GIT binary patch literal 2429896 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

B*|L2|Ac!qBB* zo#SXfy8SEoD;*@Xum9UtwF{TzXH&6DlsSpaq*wka;&tNf#TnAhbt$~A4flT991u7{ z$pDCU+_%=5;n)y+^t7YIqe?<~3JD?;TXlKAn?k^wtRFKrX2%^`%P@u-vd2DXbXZH} z67l$v;8yu?-|xcqKRtECGb7f&Q?qtmxkR_-vf6m%vD1M+;MKJJEzE#K^2vZh9>vTa zHce_z+o}QBdlTI;*>*O-A=KES@(uD$9)*-hWt-bTWmPe4duoGzwdY>#Xzz_kzPaUN zA}3ph&+b89f8Q@x8!fS9%-BKx}c~&Z#`inivfQ+fX$dl*pc1l!MJ>7C# zlP&gNFM7<583Lv@B+k8+cP{Ksl{#WT+r>YRM-B5*xh>W_8kJ%& z zB^cDXpJ%t*hg3Yw8wMow(E!w$tZ?~JWMTH@7(I(*$!fxy2kUEjJ94LXCPZ(;D7wk! zr+w{~FxKB*Lf${?;xSGu|G|_OW2$5}3@NjqywC-7)2=_cA&5YyQ2ZyUufpB@}D6m$(5v*RUXog1i6xc9>u~1C9$`bIzJWC)rR_#Tx6@ z$lz_#BaXQ>)}SkMuX9?H2%+vP)3aUX9cT5q&u~2TCPe+!PCJ&O zSm;2?Y05gaKR9Q=_)lvcr_1iO3#&;@YS{PizwvFA#&{v$V8{7s!H`AsEacj_9LqWt zPwCL@qjV2gPpELpJi6I+mE`43p+z|s;M-}}^vY4VM-67=#h>K;(>fL(AT-_n2-;*M z$(Cx-_#llr-rnQ)XGLBH*=N9^KNV*niHQ(E6l< zdKRw&X@yykXxO#c7D{foUVH9s*FjP|j2Km@r9Xt#TZU)0te`JKPBG!qG6Dkk>DCvi z*s2-gReo!~d}S;>32V$9M2faU@^m48cF7Hup`jK8veNByo$gIQ61`--%LWk)muXpt zIZSzn_F~7q$w(Pi#?QkxWVswF4#)bF^!|g$&8jjnInFrg8tsKTPPSH}*UkNJ|*~#j# zWG)IMTpJAFwuX*k1MXlw=Y|mEF`>ab-&M&t-hK07ZKykm#uMKyM&!i9GY>!T!7V6^ zGCnZREgax^32i9e0K>CCcrav_*8%(p53Y;s$_Muw=^?(bq^Ma)!`)%Asp3;>vAL6n z{5tLP<(YmPLpAFRD$fO`75gcp?j-MezXY+A^7T-`IN<8{HJ+9T(oRI=ih}53+sbJ( z5hFnJpBddlB7yS(UF49)a|-ly>k&k^Uo3BzX}7wabC8YvhtqX$>%!4zXjC;R0iR(! z+pog@Zn7^2WA~`fv9ruepapMP%dyl`oqMQsL)yp&?1e9nj|fhAso^Bg^clS&Gwv-i zQY}PB?y{o071)>NTO`eX4dMMx>$9zkj~7>$g*RBC!rI$@CMkTqO_;KZj!%Mu6}3(- zPP)I|tiY+O(|x4ESP33DOR<4=o<9sAg<8#6oaiZ_hc#XS|J~~tf3l(lL`>aubKF-DSz0EO zuQp0{g#2TY#QLSaZ{?PDTc6Zz;1Pb~25i+kVh$8?EiE6_Gk-vP#|9T%{}Gr2mSREX zYp2f251}-Oy34P_mTzc!b|f;NI9R&@A{@xH$NqM5J2@5X-iyw+sv&FEf$PV|?qcX4 zASm;mu70bP_a%;puqYu{Ul($qx^T9QUPFeceOdEpH;4ZJ(nf6tu2RQ

mQCCR=mr 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

6YyB?IruOYrH1pAf0Ec_2x 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

m^q5xLb>Hq$y&{mj< zUB%Iu=A?{fK1E`yOXqfpc%Gv|!o39#|3VR5>4X)>V;9eV2L>3-CJMf@9FksW6E0FH z2aZzJ4?C2iIz*G@sP(O?#18pE)N&E+IWY};yKIytfh+#aqj|bbj&dDccj=T1YDMvoo4=LEwv4cx(br(&cN$?d-J`@ zSL@azP|}NbWdc?SgsMH0ai0Be8`p;BH6n^$ZU(nQ6#@^9nS%&ueSV>SR>ejA$r5(i zBKIM$FMjSrs^koX;1SOK#T}wJvu5G1&T!wYbF}|A)~|pJq(0gRjkW(AOW3$%P-dI`n$gwwS33xg znPhDT9p-{jqb~RgN(v+j^c*HL(rF=?HS0RcRMGqzwVW%MB+d z%9L9@x1IpiLXRVbp6*)PGp>E`Kt=Mq3EV;3nj)^%l-@RQy3C=z_-$;REqd(YBLgf` zMh$0!rKM~t%5fDIk{nl2xR~O4?lkBY<;E9BGOc>|3@x2cK&I{om`H7ibbzV!H2!t) z7yfd^xh~7FqN4ez7%j09)eUMU_WS6=>qJ@;yc;jeQM{|@a)LKQI+`8B4B6?@l8V+E()QYMA8jWaWbhIpthvR8dy|_8Zhj;= zTF{!a931njY$0?nKF6#9jx2tBKIlT+(=`rm0y71vibTdey!^~E?#!>r(VFhgPQ-gtY_ntOEvE7&^nY({uuHl-rW423}!AGW8GMIRnUu0 zn&5nqD$6o77AGlTl!(O4EIRLW8PlfOD%E{#v`%K5dc=%@j&U~> z=K>FFlqFs+&Vl#1FHw?*Jw(GIJWc&Vd_xwLNZu>mYF(@+cC|m0#GM6*y;>^C;6RYsL9O0?D@b8_&yo@QPycL|rIKZ{S3MiUtwgV> z;a25XSbhHzV%Ah%?MR^>fEfqk;?=CtP?;kXceFKX{tUuSRZiy~SfiiOihf`{CECZE z{JB;htaO+cM=@_bG)WR~i7Sa#F1syCd41+K{d0kA*3CDqRupB72}DRU6k~uCR&)Hy z9I1$k$ZI-5@~Vy+4Q%Fr3)~JcI@J9=~5MPGX62OpTn*mZ{HapMz4j-US#9KeM?69m0NQBBE{$?snnJrUz3yWrb6};cZH$2=0d96q1bE2BA=aE|P z{s~9*`u1r(EWo%q1w7sqJJh-bG5>ELm0AM)#ez)xkQef@+XF4)?MS!j8jFuUI|~fl zReZ?w^kpN&QnOb|d@BEswFj+lB+W4kki=~V)+9g;qAX=w$gVj$#Sn+A*5h*2Ipt(nj`o_j0bx<>@j@UH#&4FDkOfbEFg>={~R2RhIG zi(r_|ovgJ7j=_!MM*UHoR?0l@Kc|Zd4`JCJ?<~am)J6)e}*Kk#_UYK=L`! z2Oe8iOg!1D@TH76PUy6%;%=w}FcqC0w~MeaHeK(pdS&*<$@7yUei^ZKpUa~{{bMKW z9K~W=#rRlFlqHNXyTG~sY=8cgQF)OYuDQUVYDV(0<5DB%iaswn4R}bBV#UVGNH=

=*{1YvYROi!gXBjT}H0a+!M4 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%0AmO?%1XBKEawzu2hG^kW|Yc2g# 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

Sc`q>UsG>XmWfI0kq%llYo90ff` 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�Qsqb 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

mhB9twI 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Ηs%QYf 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�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¯Njg>dpdk% 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¨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

J~!wWc(f@p)~%^p!XfNj75w%`;!g(5^$1LlUM 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 z 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


h@@J~8YbRTkgJI+)lTUZfdZx1 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

q@i_Hu6iRm*~btx~j12 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

bmfmvJX{>f`Bb&pvls25QTu>f*JR; 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%0D~1+}i; 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%0D(63pYYDbX1 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
aM_GDTg42*kyl+@tzG;? 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%0AqIj5fCiy2AU-XBhjH0b{)4KRnB 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 literal 0 HcmV?d00001 diff --git a/packages/video_player/video_player_avfoundation/example/assets/flutter-mark-square-64.png b/packages/video_player/video_player_avfoundation/example/assets/flutter-mark-square-64.png new file mode 100644 index 0000000000000000000000000000000000000000..56f22d5bd8f4a90724fcd9aca3a0ec85932c4a48 GIT binary patch literal 482 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU|i|x;uuoF`1Z2ugcwJGV;|p6 z=ni)jO-nO7y=V2ICr!s!^v(QOS^8?m8P&kV5BJt^?KqcXA$#u8o}as3UH)A6f7SB? zPp>{KSXli&>dLdr8;Y$S>6-c-)_L&xQvu z98wQbTSPu6HMxNV_H-=ZjEmz!=r$12SI%gi&KPyYV(MDis-vgRf9-qmWSZcb|Ht_E zJxN{YwyZ!uxa5G5)}dSf()Z{DoECW%_~Ty+L$T7Dmz76O-?{43ShmdL{&fa5mx#On z8m7DPd9a%G+A=}A2j$0S`b{@ry`_5YpWrmt zS-M=`IVU6vPLls`%O)tm2ttrBt*_x}KfIUy@9U-0TlteyfpN#+>FVdQ&MBb@01H0d A0ssI2 literal 0 HcmV?d00001 diff --git a/packages/video_player/video_player_avfoundation/example/integration_test/video_player_test.dart b/packages/video_player/video_player_avfoundation/example/integration_test/video_player_test.dart new file mode 100644 index 000000000000..a457d9226e3e --- /dev/null +++ b/packages/video_player/video_player_avfoundation/example/integration_test/video_player_test.dart @@ -0,0 +1,181 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:flutter/services.dart' show rootBundle; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:video_player_avfoundation/video_player_avfoundation.dart'; +// TODO(stuartmorgan): Remove the use of MiniController in tests, as that is +// testing test code; tests should instead be written directly against the +// platform interface. (These tests were copied from the app-facing package +// during federation and minimally modified, which is why they currently use the +// controller.) +import 'package:video_player_example/mini_controller.dart'; +import 'package:video_player_platform_interface/video_player_platform_interface.dart'; + +const Duration _playDuration = Duration(seconds: 1); + +const String _videoAssetKey = 'assets/Butterfly-209.mp4'; + +// Returns the URL to load an asset from this example app as a network source. +String getUrlForAssetAsNetworkSource(String assetKey) { + return 'https://github.com/flutter/plugins/blob/' + // This hash can be rolled forward to pick up newly-added assets. + 'cba393233e559c925a4daf71b06b4bb01c606762' + '/packages/video_player/video_player/example/' + '$assetKey' + '?raw=true'; +} + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + late MiniController _controller; + tearDown(() async => _controller.dispose()); + + group('asset videos', () { + setUp(() { + _controller = MiniController.asset(_videoAssetKey); + }); + + testWidgets('registers expected implementation', + (WidgetTester tester) async { + AVFoundationVideoPlayer.registerWith(); + expect(VideoPlayerPlatform.instance, isA()); + }); + + testWidgets('can be initialized', (WidgetTester tester) async { + await _controller.initialize(); + + expect(_controller.value.isInitialized, true); + expect(await _controller.position, const Duration(seconds: 0)); + expect(_controller.value.duration, + const Duration(seconds: 7, milliseconds: 540)); + }); + + testWidgets('can be played', (WidgetTester tester) async { + await _controller.initialize(); + + await _controller.play(); + await tester.pumpAndSettle(_playDuration); + + expect( + await _controller.position, greaterThan(const Duration(seconds: 0))); + }); + + testWidgets('can seek', (WidgetTester tester) async { + await _controller.initialize(); + + await _controller.seekTo(const Duration(seconds: 3)); + + // TODO(stuartmorgan): Switch to _controller.position once seekTo is + // fixed on the native side to wait for completion, so this is testing + // the native code rather than the MiniController position cache. + expect(_controller.value.position, const Duration(seconds: 3)); + }); + + testWidgets('can be paused', (WidgetTester tester) async { + await _controller.initialize(); + + // Play for a second, then pause, and then wait a second. + await _controller.play(); + await tester.pumpAndSettle(_playDuration); + await _controller.pause(); + final Duration pausedPosition = (await _controller.position)!; + await tester.pumpAndSettle(_playDuration); + + // Verify that we stopped playing after the pause. + // TODO(stuartmorgan): Investigate why this has a slight discrepency, and + // fix it if possible. Is AVPlayer's pause method internally async? + const Duration allowableDelta = Duration(milliseconds: 10); + expect(await _controller.position, + lessThan(pausedPosition + allowableDelta)); + }); + }); + + group('file-based videos', () { + setUp(() async { + // Load the data from the asset. + final String tempDir = (await getTemporaryDirectory()).path; + final ByteData bytes = await rootBundle.load(_videoAssetKey); + + // Write it to a file to use as a source. + final String filename = _videoAssetKey.split('/').last; + final File file = File('$tempDir/$filename'); + await file.writeAsBytes(bytes.buffer.asInt8List()); + + _controller = MiniController.file(file); + }); + + testWidgets('test video player using static file() method as constructor', + (WidgetTester tester) async { + await _controller.initialize(); + + await _controller.play(); + await tester.pumpAndSettle(_playDuration); + + expect( + await _controller.position, greaterThan(const Duration(seconds: 0))); + }); + }); + + group('network videos', () { + setUp(() { + final String videoUrl = getUrlForAssetAsNetworkSource(_videoAssetKey); + _controller = MiniController.network(videoUrl); + }); + + testWidgets('reports buffering status', (WidgetTester tester) async { + await _controller.initialize(); + + final Completer started = Completer(); + final Completer ended = Completer(); + _controller.addListener(() { + if (!started.isCompleted && _controller.value.isBuffering) { + started.complete(); + } + if (started.isCompleted && + !_controller.value.isBuffering && + !ended.isCompleted) { + ended.complete(); + } + }); + + await _controller.play(); + await _controller.seekTo(const Duration(seconds: 5)); + await tester.pumpAndSettle(_playDuration); + await _controller.pause(); + + // TODO(stuartmorgan): Switch to _controller.position once seekTo is + // fixed on the native side to wait for completion, so this is testing + // the native code rather than the MiniController position cache. + expect( + _controller.value.position, greaterThan(const Duration(seconds: 0))); + + await expectLater(started.future, completes); + await expectLater(ended.future, completes); + }, + // TODO(stuartmorgan): Skipped on iOS without explanation in main + // package. Needs investigation. + skip: true); + + testWidgets('live stream duration != 0', (WidgetTester tester) async { + final MiniController livestreamController = MiniController.network( + 'https://cph-p2p-msl.akamaized.net/hls/live/2000341/test/master.m3u8', + ); + await livestreamController.initialize(); + + expect(livestreamController.value.isInitialized, true); + // Live streams should have either a positive duration or C.TIME_UNSET if the duration is unknown + // See https://exoplayer.dev/doc/reference/com/google/android/exoplayer2/Player.html#getDuration-- + expect(livestreamController.value.duration, + (Duration duration) => duration != Duration.zero); + }); + }); +} diff --git a/packages/video_player/video_player_avfoundation/example/ios/Flutter/AppFrameworkInfo.plist b/packages/video_player/video_player_avfoundation/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 000000000000..3a9c234f96d4 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + UIRequiredDeviceCapabilities + + arm64 + + MinimumOSVersion + 9.0 + + diff --git a/packages/video_player/video_player_avfoundation/example/ios/Flutter/Debug.xcconfig b/packages/video_player/video_player_avfoundation/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 000000000000..e8efba114687 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/packages/video_player/video_player_avfoundation/example/ios/Flutter/Release.xcconfig b/packages/video_player/video_player_avfoundation/example/ios/Flutter/Release.xcconfig new file mode 100644 index 000000000000..399e9340e6f6 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/packages/video_player/video_player_avfoundation/example/ios/Podfile b/packages/video_player/video_player_avfoundation/example/ios/Podfile new file mode 100644 index 000000000000..fe37427f8a74 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/example/ios/Podfile @@ -0,0 +1,42 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + pod 'OCMock', '3.5' + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/packages/video_player/video_player_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj b/packages/video_player/video_player_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..d19d578b6fb4 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,717 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; + 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + B0F5C77B94E32FB72444AE9F /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 20721C28387E1F78689EC502 /* libPods-Runner.a */; }; + D182ECB59C06DBC7E2D5D913 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7BD232FD3BD3343A5F52AF50 /* libPods-RunnerTests.a */; }; + F7151F2F26603EBD0028CB91 /* VideoPlayerUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = F7151F2E26603EBD0028CB91 /* VideoPlayerUITests.m */; }; + F7151F3D26603ECA0028CB91 /* VideoPlayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F7151F3C26603ECA0028CB91 /* VideoPlayerTests.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + F7151F3126603EBD0028CB91 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; + F7151F3F26603ECA0028CB91 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 20721C28387E1F78689EC502 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 2A2EA522BDC492279A91AB75 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 6CDC4DA5940705A6E7671616 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 7BD232FD3BD3343A5F52AF50 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B15EC39F4617FE1082B18834 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + C18C242FF01156F58C0DAF1C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + F7151F2C26603EBD0028CB91 /* RunnerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + F7151F2E26603EBD0028CB91 /* VideoPlayerUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VideoPlayerUITests.m; sourceTree = ""; }; + F7151F3026603EBD0028CB91 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + F7151F3A26603ECA0028CB91 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + F7151F3C26603ECA0028CB91 /* VideoPlayerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VideoPlayerTests.m; sourceTree = ""; }; + F7151F3E26603ECA0028CB91 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + B0F5C77B94E32FB72444AE9F /* libPods-Runner.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F7151F2926603EBD0028CB91 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F7151F3726603ECA0028CB91 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D182ECB59C06DBC7E2D5D913 /* libPods-RunnerTests.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 05E898481BC29A7FA83AA441 /* Pods */ = { + isa = PBXGroup; + children = ( + C18C242FF01156F58C0DAF1C /* Pods-Runner.debug.xcconfig */, + B15EC39F4617FE1082B18834 /* Pods-Runner.release.xcconfig */, + 6CDC4DA5940705A6E7671616 /* Pods-RunnerTests.debug.xcconfig */, + 2A2EA522BDC492279A91AB75 /* Pods-RunnerTests.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + 23104BB9DCF267F65AD246F9 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 20721C28387E1F78689EC502 /* libPods-Runner.a */, + 7BD232FD3BD3343A5F52AF50 /* libPods-RunnerTests.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + F7151F3B26603ECA0028CB91 /* RunnerTests */, + F7151F2D26603EBD0028CB91 /* RunnerUITests */, + 97C146EF1CF9000F007C117D /* Products */, + 05E898481BC29A7FA83AA441 /* Pods */, + 23104BB9DCF267F65AD246F9 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + F7151F2C26603EBD0028CB91 /* RunnerUITests.xctest */, + F7151F3A26603ECA0028CB91 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 97C146F11CF9000F007C117D /* Supporting Files */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + ); + path = Runner; + sourceTree = ""; + }; + 97C146F11CF9000F007C117D /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 97C146F21CF9000F007C117D /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + F7151F2D26603EBD0028CB91 /* RunnerUITests */ = { + isa = PBXGroup; + children = ( + F7151F2E26603EBD0028CB91 /* VideoPlayerUITests.m */, + F7151F3026603EBD0028CB91 /* Info.plist */, + ); + path = RunnerUITests; + sourceTree = ""; + }; + F7151F3B26603ECA0028CB91 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + F7151F3C26603ECA0028CB91 /* VideoPlayerTests.m */, + F7151F3E26603ECA0028CB91 /* Info.plist */, + ); + path = RunnerTests; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + F31A669BD45D5A7C940BF077 /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; + F7151F2B26603EBD0028CB91 /* RunnerUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = F7151F3526603EBD0028CB91 /* Build configuration list for PBXNativeTarget "RunnerUITests" */; + buildPhases = ( + F7151F2826603EBD0028CB91 /* Sources */, + F7151F2926603EBD0028CB91 /* Frameworks */, + F7151F2A26603EBD0028CB91 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + F7151F3226603EBD0028CB91 /* PBXTargetDependency */, + ); + name = RunnerUITests; + productName = RunnerUITests; + productReference = F7151F2C26603EBD0028CB91 /* RunnerUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; + F7151F3926603ECA0028CB91 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = F7151F4126603ECB0028CB91 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + E9F7B01F913C69934A6629F6 /* [CP] Check Pods Manifest.lock */, + F7151F3626603ECA0028CB91 /* Sources */, + F7151F3726603ECA0028CB91 /* Frameworks */, + F7151F3826603ECA0028CB91 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + F7151F4026603ECA0028CB91 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = F7151F3A26603ECA0028CB91 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = "The Flutter Authors"; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + }; + F7151F2B26603EBD0028CB91 = { + CreatedOnToolsVersion = 12.5; + ProvisioningStyle = Automatic; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + F7151F3926603ECA0028CB91 = { + CreatedOnToolsVersion = 12.5; + ProvisioningStyle = Automatic; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + F7151F3926603ECA0028CB91 /* RunnerTests */, + F7151F2B26603EBD0028CB91 /* RunnerUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F7151F2A26603EBD0028CB91 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F7151F3826603ECA0028CB91 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + E9F7B01F913C69934A6629F6 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + F31A669BD45D5A7C940BF077 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, + 97C146F31CF9000F007C117D /* main.m in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F7151F2826603EBD0028CB91 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F7151F2F26603EBD0028CB91 /* VideoPlayerUITests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F7151F3626603ECA0028CB91 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F7151F3D26603ECA0028CB91 /* VideoPlayerTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + F7151F3226603EBD0028CB91 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = F7151F3126603EBD0028CB91 /* PBXContainerItemProxy */; + }; + F7151F4026603ECA0028CB91 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = F7151F3F26603ECA0028CB91 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.videoPlayerExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.videoPlayerExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + F7151F3326603EBD0028CB91 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = RunnerUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_TARGET_NAME = Runner; + }; + name = Debug; + }; + F7151F3426603EBD0028CB91 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = RunnerUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_TARGET_NAME = Runner; + }; + name = Release; + }; + F7151F4226603ECB0028CB91 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6CDC4DA5940705A6E7671616 /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = RunnerTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Debug; + }; + F7151F4326603ECB0028CB91 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 2A2EA522BDC492279A91AB75 /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = RunnerTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F7151F3526603EBD0028CB91 /* Build configuration list for PBXNativeTarget "RunnerUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F7151F3326603EBD0028CB91 /* Debug */, + F7151F3426603EBD0028CB91 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F7151F4126603ECB0028CB91 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F7151F4226603ECB0028CB91 /* Debug */, + F7151F4326603ECB0028CB91 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/packages/video_player/video_player_avfoundation/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/video_player/video_player_avfoundation/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..919434a6254f --- /dev/null +++ b/packages/video_player/video_player_avfoundation/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/video_player/video_player_avfoundation/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/video_player/video_player_avfoundation/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000000..0632b6533bc8 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/video_player/video_player_avfoundation/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/video_player/video_player_avfoundation/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..21a3cc14c74e --- /dev/null +++ b/packages/video_player/video_player_avfoundation/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/video_player/video_player_avfoundation/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/video_player/video_player_avfoundation/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/video_player/video_player_avfoundation/example/ios/Runner/AppDelegate.h b/packages/video_player/video_player_avfoundation/example/ios/Runner/AppDelegate.h new file mode 100644 index 000000000000..0681d288bb70 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/example/ios/Runner/AppDelegate.h @@ -0,0 +1,10 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import + +@interface AppDelegate : FlutterAppDelegate + +@end diff --git a/packages/video_player/video_player_avfoundation/example/ios/Runner/AppDelegate.m b/packages/video_player/video_player_avfoundation/example/ios/Runner/AppDelegate.m new file mode 100644 index 000000000000..30b87969f44a --- /dev/null +++ b/packages/video_player/video_player_avfoundation/example/ios/Runner/AppDelegate.m @@ -0,0 +1,17 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "AppDelegate.h" +#include "GeneratedPluginRegistrant.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [GeneratedPluginRegistrant registerWithRegistry:self]; + // Override point for customization after application launch. + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +@end diff --git a/packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000000..d22f10b2ab63 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..28c6bf03016f6c994b70f38d1b7346e5831b531f GIT binary patch literal 564 zcmV-40?Yl0P)Px$?ny*JR5%f>l)FnDQ543{x%ZCiu33$Wg!pQFfT_}?5Q|_VSlIbLC`dpoMXL}9 zHfd9&47Mo(7D231gb+kjFxZHS4-m~7WurTH&doVX2KI5sU4v(sJ1@T9eCIKPjsqSr z)C01LsCxk=72-vXmX}CQD#BD;Cthymh&~=f$Q8nn0J<}ZrusBy4PvRNE}+1ceuj8u z0mW5k8fmgeLnTbWHGwfKA3@PdZxhn|PypR&^p?weGftrtCbjF#+zk_5BJh7;0`#Wr zgDpM_;Ax{jO##IrT`Oz;MvfwGfV$zD#c2xckpcXC6oou4ML~ezCc2EtnsQTB4tWNg z?4bkf;hG7IMfhgNI(FV5Gs4|*GyMTIY0$B=_*mso9Ityq$m^S>15>-?0(zQ<8Qy<_TjHE33(?_M8oaM zyc;NxzRVK@DL6RJnX%U^xW0Gpg(lXp(!uK1v0YgHjs^ZXSQ|m#lV7ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 literal 0 HcmV?d00001 diff --git a/packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..f091b6b0bca859a3f474b03065bef75ba58a9e4c GIT binary patch literal 1588 zcmV-42Fv-0P)C1SqPt}wig>|5Crh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)y zRAZ>eDe#*r`yDAVgB_R*LB*MAc)8(b{g{9McCXW!lq7r(btRoB9!8B-#AI6JMb~YFBEvdsV)`mEQO^&#eRKx@b&x- z5lZm*!WfD8oCLzfHGz#u7sT0^VLMI1MqGxF^v+`4YYnVYgk*=kU?HsSz{v({E3lb9 z>+xILjBN)t6`=g~IBOelGQ(O990@BfXf(DRI5I$qN$0Gkz-FSc$3a+2fX$AedL4u{ z4V+5Ong(9LiGcIKW?_352sR;LtDPmPJXI{YtT=O8=76o9;*n%_m|xo!i>7$IrZ-{l z-x3`7M}qzHsPV@$v#>H-TpjDh2UE$9g6sysUREDy_R(a)>=eHw-WAyfIN z*qb!_hW>G)Tu8nSw9yn#3wFMiLcfc4pY0ek1}8(NqkBR@t4{~oC>ryc-h_ByH(Cg5 z>ao-}771+xE3um9lWAY1FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zm zZQj(aA_HeBY&OC^jj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5Kh zX*|AU4QE#~SgPzOXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&g3j|zDgC+}2Q_v%YfDax z!?umcN^n}KYQ|a$Lr+51Nf9dkkYFSjZZjkma$0KOj+;aQ&721~t7QUKx61J3(P4P1 zstI~7-wOACnWP4=8oGOwz%vNDqD8w&Q`qcNGGrbbf&0s9L0De{4{mRS?o0MU+nR_! zrvshUau0G^DeMhM_v{5BuLjb#Hh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG z;P%vktHcXwTj~+IE(~px)3*MY77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC z(zYC$g|@+I+kY;dg_dE>scBf&bP1Nc@Hz<3R)V`=AGkc;8CXqdi=B4l2k|g;2%#m& z*jfX^%b!A8#bI!j9-0Fi0bOXl(-c^AB9|nQaE`*)Hw+o&jS9@7&Gov#HbD~#d{twV zXd^Tr^mWLfFh$@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)y zI9C9*oUga6=hxw6QasLPnee@3^Rr*M{CdaL5=R41nLs(AHk_=Y+A9$2&H(B7!_pURs&8aNw7?`&Z&xY_Ye z)~D5Bog^td-^QbUtkTirdyK^mTHAOuptDflut!#^lnKqU md>ggs(5nOWAqO?umG&QVYK#ibz}*4>0000U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~cTNYR~@Y9KepMPrrB8cABapAFa=`H+UGhkXUZV1GnwR1*lPyZ;*K(i~2gp|@bzp8}og7e*#% zEnr|^CWdVV!-4*Y_7rFvlww2Ze+>j*!Z!pQ?2l->4q#nqRu9`ELo6RMS5=br47g_X zRw}P9a7RRYQ%2Vsd0Me{_(EggTnuN6j=-?uFS6j^u69elMypu?t>op*wBx<=Wx8?( ztpe^(fwM6jJX7M-l*k3kEpWOl_Vk3@(_w4oc}4YF4|Rt=2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX?F&rExl^W|G8Z8;`5z-k}OGNZ0#WVb$WCpQu-_YsiqKP?BB# vzVHS-CTUF4Ozn5G+mq_~Qqto~ahA+K`|lyv3(-e}00000NkvXXu0mjfd`9t{ literal 0 HcmV?d00001 diff --git a/packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d0ef06e7edb86cdfe0d15b4b0d98334a86163658 GIT binary patch literal 1716 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 literal 0 HcmV?d00001 diff --git a/packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c8f9ed8f5cee1c98386d13b17e89f719e83555b2 GIT binary patch literal 1895 zcmV-t2blPYP)FQtfgmafE#=YDCq`qUBt#QpG%*H6QHY765~R=q zZ6iudfM}q!Pz#~9JgOi8QJ|DSu?1-*(kSi1K4#~5?#|rh?sS)(-JQqX*}ciXJ56_H zdw=^s_srbAdqxlvGyrgGet#6T7_|j;95sL%MtM;q86vOxKM$f#puR)Bjv9Zvz9-di zXOTSsZkM83)E9PYBXC<$6(|>lNLVBb&&6y{NByFCp%6+^ALR@NCTse_wqvNmSWI-m z!$%KlHFH2omF!>#%1l3LTZg(s7eof$7*xB)ZQ0h?ejh?Ta9fDv59+u#MokW+1t8Zb zgHv%K(u9G^Lv`lh#f3<6!JVTL3(dCpxHbnbA;kKqQyd1~^Xe0VIaYBSWm6nsr;dFj z4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3QU4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKubPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`Q zykI#*+rQ=z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRe zt3L_uNyQ*cE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=ky zx=~RKa4{iTm{_>_vSCm?$Ej=i6@=m%@VvAITnigVg{&@!7CDgs908761meDK5azA} z4?=NOH|PdvabgJ&fW2{Mo$Q0CcD8Qc84%{JPYt5EiG{MdLIAeX%T=D7NIP4%Hw}p9 zg)==!2Lbp#j{u_}hMiao9=!VSyx0gHbeCS`;q&vzeq|fs`y&^X-lso(Ls@-706qmA z7u*T5PMo_w3{se1t2`zWeO^hOvTsohG_;>J0wVqVe+n)AbQCx)yh9;w+J6?NF5Lmo zecS@ieAKL8%bVd@+-KT{yI|S}O>pYckUFs;ry9Ow$CD@ztz5K-*D$^{i(_1llhSh^ zEkL$}tsQt5>QA^;QgjgIfBDmcOgi5YDyu?t6vSnbp=1+@6D& z5MJ}B8q;bRlVoxasyhcUF1+)o`&3r0colr}QJ3hcSdLu;9;td>kf@Tcn<@9sIx&=m z;AD;SCh95=&p;$r{Xz3iWCO^MX83AGJ(yH&eTXgv|0=34#-&WAmw{)U7OU9!Wz^!7 zZ%jZFi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i h0WYBP*#0Ks^FNSabJA*5${_#%002ovPDHLkV1oKhTl@e3 literal 0 HcmV?d00001 diff --git a/packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..75b2d164a5a98e212cca15ea7bf2ab5de5108680 GIT binary patch literal 3831 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 literal 0 HcmV?d00001 diff --git a/packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..c4df70d39da7941ef3f6dcb7f06a192d8dcb308d GIT binary patch literal 1888 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 iW8Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 000000000000..89c2725b70f1 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/packages/video_player/video_player_avfoundation/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/video_player/video_player_avfoundation/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 000000000000..f2e259c7c939 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/video_player/video_player_avfoundation/example/ios/Runner/Base.lproj/Main.storyboard b/packages/video_player/video_player_avfoundation/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 000000000000..f3c28516fb38 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/video_player/video_player_avfoundation/example/ios/Runner/Info.plist b/packages/video_player/video_player_avfoundation/example/ios/Runner/Info.plist new file mode 100644 index 000000000000..ff775ec6e32e --- /dev/null +++ b/packages/video_player/video_player_avfoundation/example/ios/Runner/Info.plist @@ -0,0 +1,54 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + video_player_example + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/packages/video_player/video_player_avfoundation/example/ios/Runner/main.m b/packages/video_player/video_player_avfoundation/example/ios/Runner/main.m new file mode 100644 index 000000000000..f143297b30d6 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/example/ios/Runner/main.m @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import +#import "AppDelegate.h" + +int main(int argc, char *argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/packages/video_player/video_player/example/ios/RunnerTests/Info.plist b/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/Info.plist similarity index 100% rename from packages/video_player/video_player/example/ios/RunnerTests/Info.plist rename to packages/video_player/video_player_avfoundation/example/ios/RunnerTests/Info.plist diff --git a/packages/video_player/video_player/example/ios/RunnerTests/VideoPlayerTests.m b/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m similarity index 99% rename from packages/video_player/video_player/example/ios/RunnerTests/VideoPlayerTests.m rename to packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m index 1e717fa67846..a858b1423702 100644 --- a/packages/video_player/video_player/example/ios/RunnerTests/VideoPlayerTests.m +++ b/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m @@ -3,7 +3,7 @@ // found in the LICENSE file. @import AVFoundation; -@import video_player; +@import video_player_avfoundation; @import XCTest; #import diff --git a/packages/video_player/video_player/example/ios/RunnerUITests/Info.plist b/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/Info.plist similarity index 100% rename from packages/video_player/video_player/example/ios/RunnerUITests/Info.plist rename to packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/Info.plist diff --git a/packages/video_player/video_player/example/ios/RunnerUITests/VideoPlayerUITests.m b/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m similarity index 85% rename from packages/video_player/video_player/example/ios/RunnerUITests/VideoPlayerUITests.m rename to packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m index 7c92493ee17a..2933cf36feae 100644 --- a/packages/video_player/video_player/example/ios/RunnerUITests/VideoPlayerUITests.m +++ b/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m @@ -30,12 +30,6 @@ - (void)testPlayVideo { XCTAssertTrue([playButton waitForExistenceWithTimeout:30.0]); [playButton tap]; - XCUIElement *chirpClosedCaption = app.staticTexts[@"[ Birds chirping ]"]; - XCTAssertTrue([chirpClosedCaption waitForExistenceWithTimeout:30.0]); - - XCUIElement *buzzClosedCaption = app.staticTexts[@"[ Buzzing ]"]; - XCTAssertTrue([buzzClosedCaption waitForExistenceWithTimeout:30.0]); - XCUIElement *playbackSpeed1x = app.staticTexts[@"Playback speed\n1.0x"]; XCTAssertTrue([playbackSpeed1x waitForExistenceWithTimeout:30.0]); [playbackSpeed1x tap]; @@ -48,7 +42,7 @@ - (void)testPlayVideo { XCTAssertTrue([playbackSpeed5x waitForExistenceWithTimeout:30.0]); // Cycle through tabs. - for (NSString *tabName in @[ @"Asset", @"List example" ]) { + for (NSString *tabName in @[ @"Asset", @"Remote" ]) { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"label BEGINSWITH %@", tabName]; XCUIElement *unselectedTab = [app.staticTexts elementMatchingPredicate:predicate]; XCTAssertTrue([unselectedTab waitForExistenceWithTimeout:30.0]); diff --git a/packages/video_player/video_player_avfoundation/example/lib/main.dart b/packages/video_player/video_player_avfoundation/example/lib/main.dart new file mode 100644 index 000000000000..cab6eb802ca5 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/example/lib/main.dart @@ -0,0 +1,235 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +import 'mini_controller.dart'; + +void main() { + runApp( + MaterialApp( + home: _App(), + ), + ); +} + +class _App extends StatelessWidget { + @override + Widget build(BuildContext context) { + return DefaultTabController( + length: 2, + child: Scaffold( + key: const ValueKey('home_page'), + appBar: AppBar( + title: const Text('Video player example'), + bottom: const TabBar( + isScrollable: true, + tabs: [ + Tab( + icon: Icon(Icons.cloud), + text: 'Remote', + ), + Tab(icon: Icon(Icons.insert_drive_file), text: 'Asset'), + ], + ), + ), + body: TabBarView( + children: [ + _BumbleBeeRemoteVideo(), + _ButterFlyAssetVideo(), + ], + ), + ), + ); + } +} + +class _ButterFlyAssetVideo extends StatefulWidget { + @override + _ButterFlyAssetVideoState createState() => _ButterFlyAssetVideoState(); +} + +class _ButterFlyAssetVideoState extends State<_ButterFlyAssetVideo> { + late MiniController _controller; + + @override + void initState() { + super.initState(); + _controller = MiniController.asset('assets/Butterfly-209.mp4'); + + _controller.addListener(() { + setState(() {}); + }); + _controller.initialize().then((_) => setState(() {})); + _controller.play(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Column( + children: [ + Container( + padding: const EdgeInsets.only(top: 20.0), + ), + const Text('With assets mp4'), + Container( + padding: const EdgeInsets.all(20), + child: AspectRatio( + aspectRatio: _controller.value.aspectRatio, + child: Stack( + alignment: Alignment.bottomCenter, + children: [ + VideoPlayer(_controller), + _ControlsOverlay(controller: _controller), + VideoProgressIndicator(_controller), + ], + ), + ), + ), + ], + ), + ); + } +} + +class _BumbleBeeRemoteVideo extends StatefulWidget { + @override + _BumbleBeeRemoteVideoState createState() => _BumbleBeeRemoteVideoState(); +} + +class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { + late MiniController _controller; + + @override + void initState() { + super.initState(); + _controller = MiniController.network( + 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4', + ); + + _controller.addListener(() { + setState(() {}); + }); + _controller.initialize(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Column( + children: [ + Container(padding: const EdgeInsets.only(top: 20.0)), + const Text('With remote mp4'), + Container( + padding: const EdgeInsets.all(20), + child: AspectRatio( + aspectRatio: _controller.value.aspectRatio, + child: Stack( + alignment: Alignment.bottomCenter, + children: [ + VideoPlayer(_controller), + _ControlsOverlay(controller: _controller), + VideoProgressIndicator(_controller), + ], + ), + ), + ), + ], + ), + ); + } +} + +class _ControlsOverlay extends StatelessWidget { + const _ControlsOverlay({Key? key, required this.controller}) + : super(key: key); + + static const List _examplePlaybackRates = [ + 0.25, + 0.5, + 1.0, + 1.5, + 2.0, + 3.0, + 5.0, + 10.0, + ]; + + final MiniController controller; + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + AnimatedSwitcher( + duration: const Duration(milliseconds: 50), + reverseDuration: const Duration(milliseconds: 200), + child: controller.value.isPlaying + ? const SizedBox.shrink() + : Container( + color: Colors.black26, + child: const Center( + child: Icon( + Icons.play_arrow, + color: Colors.white, + size: 100.0, + semanticLabel: 'Play', + ), + ), + ), + ), + GestureDetector( + onTap: () { + controller.value.isPlaying ? controller.pause() : controller.play(); + }, + ), + Align( + alignment: Alignment.topRight, + child: PopupMenuButton( + initialValue: controller.value.playbackSpeed, + tooltip: 'Playback speed', + onSelected: (double speed) { + controller.setPlaybackSpeed(speed); + }, + itemBuilder: (BuildContext context) { + return >[ + for (final double speed in _examplePlaybackRates) + PopupMenuItem( + value: speed, + child: Text('${speed}x'), + ) + ]; + }, + child: Padding( + padding: const EdgeInsets.symmetric( + // Using less vertical padding as the text is also longer + // horizontally, so it feels like it would need more spacing + // horizontally (matching the aspect ratio of the video). + vertical: 12, + horizontal: 16, + ), + child: Text('${controller.value.playbackSpeed}x'), + ), + ), + ), + ], + ); + } +} diff --git a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart new file mode 100644 index 000000000000..9bb8e90b65ae --- /dev/null +++ b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart @@ -0,0 +1,538 @@ +// 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. + +// TODO(stuartmorgan): Consider extracting this to a shared local (path-based) +// package for use in all implementation packages. + +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:video_player_platform_interface/video_player_platform_interface.dart'; + +VideoPlayerPlatform? _cachedPlatform; + +VideoPlayerPlatform get _platform { + if (_cachedPlatform == null) { + _cachedPlatform = VideoPlayerPlatform.instance; + _cachedPlatform!.init(); + } + return _cachedPlatform!; +} + +/// The duration, current position, buffering state, error state and settings +/// of a [MiniController]. +class VideoPlayerValue { + /// Constructs a video with the given values. Only [duration] is required. The + /// rest will initialize with default values when unset. + VideoPlayerValue({ + required this.duration, + this.size = Size.zero, + this.position = Duration.zero, + this.buffered = const [], + this.isInitialized = false, + this.isPlaying = false, + this.isBuffering = false, + this.playbackSpeed = 1.0, + this.errorDescription, + }); + + /// Returns an instance for a video that hasn't been loaded. + VideoPlayerValue.uninitialized() + : this(duration: Duration.zero, isInitialized: false); + + /// Returns an instance with the given [errorDescription]. + VideoPlayerValue.erroneous(String errorDescription) + : this( + duration: Duration.zero, + isInitialized: false, + errorDescription: errorDescription); + + /// The total duration of the video. + /// + /// The duration is [Duration.zero] if the video hasn't been initialized. + final Duration duration; + + /// The current playback position. + final Duration position; + + /// The currently buffered ranges. + final List buffered; + + /// True if the video is playing. False if it's paused. + final bool isPlaying; + + /// True if the video is currently buffering. + final bool isBuffering; + + /// The current speed of the playback. + final double playbackSpeed; + + /// A description of the error if present. + /// + /// If [hasError] is false this is `null`. + final String? errorDescription; + + /// The [size] of the currently loaded video. + final Size size; + + /// Indicates whether or not the video has been loaded and is ready to play. + final bool isInitialized; + + /// Indicates whether or not the video is in an error state. If this is true + /// [errorDescription] should have information about the problem. + bool get hasError => errorDescription != null; + + /// Returns [size.width] / [size.height]. + /// + /// Will return `1.0` if: + /// * [isInitialized] is `false` + /// * [size.width], or [size.height] is equal to `0.0` + /// * aspect ratio would be less than or equal to `0.0` + double get aspectRatio { + if (!isInitialized || size.width == 0 || size.height == 0) { + return 1.0; + } + final double aspectRatio = size.width / size.height; + if (aspectRatio <= 0) { + return 1.0; + } + return aspectRatio; + } + + /// Returns a new instance that has the same values as this current instance, + /// except for any overrides passed in as arguments to [copyWidth]. + VideoPlayerValue copyWith({ + Duration? duration, + Size? size, + Duration? position, + List? buffered, + bool? isInitialized, + bool? isPlaying, + bool? isBuffering, + double? playbackSpeed, + String? errorDescription, + }) { + return VideoPlayerValue( + duration: duration ?? this.duration, + size: size ?? this.size, + position: position ?? this.position, + buffered: buffered ?? this.buffered, + isInitialized: isInitialized ?? this.isInitialized, + isPlaying: isPlaying ?? this.isPlaying, + isBuffering: isBuffering ?? this.isBuffering, + playbackSpeed: playbackSpeed ?? this.playbackSpeed, + errorDescription: errorDescription ?? this.errorDescription, + ); + } +} + +/// A very minimal version of `VideoPlayerController` for running the example +/// without relying on `video_player`. +class MiniController extends ValueNotifier { + /// Constructs a [MiniController] playing a video from an asset. + /// + /// The name of the asset is given by the [dataSource] argument and must not be + /// null. The [package] argument must be non-null when the asset comes from a + /// package and null otherwise. + MiniController.asset(this.dataSource, {this.package}) + : dataSourceType = DataSourceType.asset, + super(VideoPlayerValue(duration: Duration.zero)); + + /// Constructs a [MiniController] playing a video from obtained from + /// the network. + MiniController.network(this.dataSource) + : dataSourceType = DataSourceType.network, + package = null, + super(VideoPlayerValue(duration: Duration.zero)); + + /// Constructs a [MiniController] playing a video from obtained from a file. + MiniController.file(File file) + : dataSource = 'file://${file.path}', + dataSourceType = DataSourceType.file, + package = null, + super(VideoPlayerValue(duration: Duration.zero)); + + /// The URI to the video file. This will be in different formats depending on + /// the [DataSourceType] of the original video. + final String dataSource; + + /// Describes the type of data source this [MiniController] + /// is constructed with. + final DataSourceType dataSourceType; + + /// Only set for [asset] videos. The package that the asset was loaded from. + final String? package; + + Timer? _timer; + Completer? _creatingCompleter; + StreamSubscription? _eventSubscription; + + /// The id of a texture that hasn't been initialized. + @visibleForTesting + static const int kUninitializedTextureId = -1; + int _textureId = kUninitializedTextureId; + + /// This is just exposed for testing. It shouldn't be used by anyone depending + /// on the plugin. + @visibleForTesting + int get textureId => _textureId; + + /// Attempts to open the given [dataSource] and load metadata about the video. + Future initialize() async { + _creatingCompleter = Completer(); + + late DataSource dataSourceDescription; + switch (dataSourceType) { + case DataSourceType.asset: + dataSourceDescription = DataSource( + sourceType: DataSourceType.asset, + asset: dataSource, + package: package, + ); + break; + case DataSourceType.network: + dataSourceDescription = DataSource( + sourceType: DataSourceType.network, + uri: dataSource, + ); + break; + case DataSourceType.file: + dataSourceDescription = DataSource( + sourceType: DataSourceType.file, + uri: dataSource, + ); + break; + case DataSourceType.contentUri: + dataSourceDescription = DataSource( + sourceType: DataSourceType.contentUri, + uri: dataSource, + ); + break; + } + + _textureId = (await _platform.create(dataSourceDescription)) ?? + kUninitializedTextureId; + _creatingCompleter!.complete(null); + final Completer initializingCompleter = Completer(); + + void eventListener(VideoEvent event) { + switch (event.eventType) { + case VideoEventType.initialized: + value = value.copyWith( + duration: event.duration, + size: event.size, + isInitialized: event.duration != null, + ); + initializingCompleter.complete(null); + _platform.setVolume(_textureId, 1.0); + _platform.setLooping(_textureId, true); + _applyPlayPause(); + break; + case VideoEventType.completed: + pause().then((void pauseResult) => seekTo(value.duration)); + break; + case VideoEventType.bufferingUpdate: + value = value.copyWith(buffered: event.buffered); + break; + case VideoEventType.bufferingStart: + value = value.copyWith(isBuffering: true); + break; + case VideoEventType.bufferingEnd: + value = value.copyWith(isBuffering: false); + break; + case VideoEventType.unknown: + break; + } + } + + void errorListener(Object obj) { + final PlatformException e = obj as PlatformException; + value = VideoPlayerValue.erroneous(e.message!); + _timer?.cancel(); + if (!initializingCompleter.isCompleted) { + initializingCompleter.completeError(obj); + } + } + + _eventSubscription = _platform + .videoEventsFor(_textureId) + .listen(eventListener, onError: errorListener); + return initializingCompleter.future; + } + + @override + Future dispose() async { + if (_creatingCompleter != null) { + await _creatingCompleter!.future; + _timer?.cancel(); + await _eventSubscription?.cancel(); + await _platform.dispose(_textureId); + } + super.dispose(); + } + + /// Starts playing the video. + Future play() async { + value = value.copyWith(isPlaying: true); + await _applyPlayPause(); + } + + /// Pauses the video. + Future pause() async { + value = value.copyWith(isPlaying: false); + await _applyPlayPause(); + } + + Future _applyPlayPause() async { + _timer?.cancel(); + if (value.isPlaying) { + await _platform.play(_textureId); + + _timer = Timer.periodic( + const Duration(milliseconds: 500), + (Timer timer) async { + final Duration? newPosition = await position; + if (newPosition == null) { + return; + } + _updatePosition(newPosition); + }, + ); + await _applyPlaybackSpeed(); + } else { + await _platform.pause(_textureId); + } + } + + Future _applyPlaybackSpeed() async { + if (value.isPlaying) { + await _platform.setPlaybackSpeed( + _textureId, + value.playbackSpeed, + ); + } + } + + /// The position in the current video. + Future get position async { + return await _platform.getPosition(_textureId); + } + + /// Sets the video's current timestamp to be at [position]. + Future seekTo(Duration position) async { + if (position > value.duration) { + position = value.duration; + } else if (position < const Duration()) { + position = const Duration(); + } + await _platform.seekTo(_textureId, position); + _updatePosition(position); + } + + /// Sets the playback speed. + Future setPlaybackSpeed(double speed) async { + value = value.copyWith(playbackSpeed: speed); + await _applyPlaybackSpeed(); + } + + void _updatePosition(Duration position) { + value = value.copyWith(position: position); + } + + @override + void removeListener(VoidCallback listener) { + super.removeListener(listener); + } +} + +/// Widget that displays the video controlled by [controller]. +class VideoPlayer extends StatefulWidget { + /// Uses the given [controller] for all video rendered in this widget. + const VideoPlayer(this.controller); + + /// The [MiniController] responsible for the video being rendered in + /// this widget. + final MiniController controller; + + @override + _VideoPlayerState createState() => _VideoPlayerState(); +} + +class _VideoPlayerState extends State { + _VideoPlayerState() { + _listener = () { + final int newTextureId = widget.controller.textureId; + if (newTextureId != _textureId) { + setState(() { + _textureId = newTextureId; + }); + } + }; + } + + late VoidCallback _listener; + + late int _textureId; + + @override + void initState() { + super.initState(); + _textureId = widget.controller.textureId; + // Need to listen for initialization events since the actual texture ID + // becomes available after asynchronous initialization finishes. + widget.controller.addListener(_listener); + } + + @override + void didUpdateWidget(VideoPlayer oldWidget) { + super.didUpdateWidget(oldWidget); + oldWidget.controller.removeListener(_listener); + _textureId = widget.controller.textureId; + widget.controller.addListener(_listener); + } + + @override + void deactivate() { + super.deactivate(); + widget.controller.removeListener(_listener); + } + + @override + Widget build(BuildContext context) { + return _textureId == MiniController.kUninitializedTextureId + ? Container() + : _platform.buildView(_textureId); + } +} + +class _VideoScrubber extends StatefulWidget { + const _VideoScrubber({ + required this.child, + required this.controller, + }); + + final Widget child; + final MiniController controller; + + @override + _VideoScrubberState createState() => _VideoScrubberState(); +} + +class _VideoScrubberState extends State<_VideoScrubber> { + MiniController get controller => widget.controller; + + @override + Widget build(BuildContext context) { + void seekToRelativePosition(Offset globalPosition) { + final RenderBox box = context.findRenderObject()! as RenderBox; + final Offset tapPos = box.globalToLocal(globalPosition); + final double relative = tapPos.dx / box.size.width; + final Duration position = controller.value.duration * relative; + controller.seekTo(position); + } + + return GestureDetector( + behavior: HitTestBehavior.opaque, + child: widget.child, + onTapDown: (TapDownDetails details) { + if (controller.value.isInitialized) { + seekToRelativePosition(details.globalPosition); + } + }, + ); + } +} + +/// Displays the play/buffering status of the video controlled by [controller]. +class VideoProgressIndicator extends StatefulWidget { + /// Construct an instance that displays the play/buffering status of the video + /// controlled by [controller]. + const VideoProgressIndicator(this.controller); + + /// The [MiniController] that actually associates a video with this + /// widget. + final MiniController controller; + + @override + _VideoProgressIndicatorState createState() => _VideoProgressIndicatorState(); +} + +class _VideoProgressIndicatorState extends State { + _VideoProgressIndicatorState() { + listener = () { + if (mounted) { + setState(() {}); + } + }; + } + + late VoidCallback listener; + + MiniController get controller => widget.controller; + + @override + void initState() { + super.initState(); + controller.addListener(listener); + } + + @override + void deactivate() { + controller.removeListener(listener); + super.deactivate(); + } + + @override + Widget build(BuildContext context) { + const Color playedColor = Color.fromRGBO(255, 0, 0, 0.7); + const Color bufferedColor = Color.fromRGBO(50, 50, 200, 0.2); + const Color backgroundColor = Color.fromRGBO(200, 200, 200, 0.5); + + Widget progressIndicator; + if (controller.value.isInitialized) { + final int duration = controller.value.duration.inMilliseconds; + final int position = controller.value.position.inMilliseconds; + + int maxBuffering = 0; + for (final DurationRange range in controller.value.buffered) { + final int end = range.end.inMilliseconds; + if (end > maxBuffering) { + maxBuffering = end; + } + } + + progressIndicator = Stack( + fit: StackFit.passthrough, + children: [ + LinearProgressIndicator( + value: maxBuffering / duration, + valueColor: const AlwaysStoppedAnimation(bufferedColor), + backgroundColor: backgroundColor, + ), + LinearProgressIndicator( + value: position / duration, + valueColor: const AlwaysStoppedAnimation(playedColor), + backgroundColor: Colors.transparent, + ), + ], + ); + } else { + progressIndicator = const LinearProgressIndicator( + value: null, + valueColor: AlwaysStoppedAnimation(playedColor), + backgroundColor: backgroundColor, + ); + } + return _VideoScrubber( + child: Padding( + padding: const EdgeInsets.only(top: 5.0), + child: progressIndicator, + ), + controller: controller, + ); + } +} diff --git a/packages/video_player/video_player_avfoundation/example/pubspec.yaml b/packages/video_player/video_player_avfoundation/example/pubspec.yaml new file mode 100644 index 000000000000..40b88d577ce6 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/example/pubspec.yaml @@ -0,0 +1,35 @@ +name: video_player_example +description: Demonstrates how to use the video_player plugin. +publish_to: none + +environment: + sdk: ">=2.12.0 <3.0.0" + flutter: ">=2.8.0" + +dependencies: + flutter: + sdk: flutter + video_player_avfoundation: + # When depending on this package from a real application you should use: + # video_player_avfoundation: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + video_player_platform_interface: ">=4.2.0 <6.0.0" + +dev_dependencies: + flutter_driver: + sdk: flutter + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + path_provider: ^2.0.6 + test: any + +flutter: + uses-material-design: true + assets: + - assets/flutter-mark-square-64.png + - assets/Butterfly-209.mp4 diff --git a/packages/video_player/video_player_avfoundation/example/test_driver/integration_test.dart b/packages/video_player/video_player_avfoundation/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4f10f2a522f3 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/example/test_driver/integration_test.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); diff --git a/packages/video_player/video_player_avfoundation/example/test_driver/video_player.dart b/packages/video_player/video_player_avfoundation/example/test_driver/video_player.dart new file mode 100644 index 000000000000..b72354e2187f --- /dev/null +++ b/packages/video_player/video_player_avfoundation/example/test_driver/video_player.dart @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_driver/driver_extension.dart'; +import 'package:video_player_example/main.dart' as app; + +void main() { + enableFlutterDriverExtension(); + app.main(); +} diff --git a/packages/video_player/video_player/ios/Assets/.gitkeep b/packages/video_player/video_player_avfoundation/ios/Assets/.gitkeep similarity index 100% rename from packages/video_player/video_player/ios/Assets/.gitkeep rename to packages/video_player/video_player_avfoundation/ios/Assets/.gitkeep diff --git a/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.h b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.h similarity index 100% rename from packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.h rename to packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.h diff --git a/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m similarity index 99% rename from packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m rename to packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m index 5d09cfed61d2..9cc40e388cef 100644 --- a/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m +++ b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m @@ -397,6 +397,9 @@ - (int64_t)duration { } - (void)seekTo:(int)location { + // TODO(stuartmorgan): Update this to use completionHandler: to only return + // once the seek operation is complete once the Pigeon API is updated to a + // version that handles async calls. [_player seekToTime:CMTimeMake(location, 1000) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero]; diff --git a/packages/video_player/video_player/ios/Classes/messages.h b/packages/video_player/video_player_avfoundation/ios/Classes/messages.h similarity index 100% rename from packages/video_player/video_player/ios/Classes/messages.h rename to packages/video_player/video_player_avfoundation/ios/Classes/messages.h diff --git a/packages/video_player/video_player/ios/Classes/messages.m b/packages/video_player/video_player_avfoundation/ios/Classes/messages.m similarity index 100% rename from packages/video_player/video_player/ios/Classes/messages.m rename to packages/video_player/video_player_avfoundation/ios/Classes/messages.m diff --git a/packages/video_player/video_player/ios/video_player.podspec b/packages/video_player/video_player_avfoundation/ios/video_player_avfoundation.podspec similarity index 87% rename from packages/video_player/video_player/ios/video_player.podspec rename to packages/video_player/video_player_avfoundation/ios/video_player_avfoundation.podspec index 12ea7e91514b..80dd2a53a23a 100644 --- a/packages/video_player/video_player/ios/video_player.podspec +++ b/packages/video_player/video_player_avfoundation/ios/video_player_avfoundation.podspec @@ -2,7 +2,7 @@ # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html # Pod::Spec.new do |s| - s.name = 'video_player' + s.name = 'video_player_avfoundation' s.version = '0.0.1' s.summary = 'Flutter Video Player' s.description = <<-DESC @@ -12,13 +12,12 @@ Downloaded by pub (not CocoaPods). s.homepage = 'https://github.com/flutter/plugins' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/video_player/video_player' } + s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/video_player/video_player_avfoundation' } s.documentation_url = 'https://pub.dev/packages/video_player' s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' s.dependency 'Flutter' - + s.platform = :ios, '9.0' s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } end - diff --git a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart new file mode 100644 index 000000000000..db7e04bd682a --- /dev/null +++ b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart @@ -0,0 +1,173 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:ui'; + +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:video_player_platform_interface/video_player_platform_interface.dart'; + +import 'messages.dart'; + +/// An iOS implementation of [VideoPlayerPlatform] that uses the +/// Pigeon-generated [VideoPlayerApi]. +class AVFoundationVideoPlayer extends VideoPlayerPlatform { + final VideoPlayerApi _api = VideoPlayerApi(); + + /// Registers this class as the default instance of [VideoPlayerPlatform]. + static void registerWith() { + VideoPlayerPlatform.instance = AVFoundationVideoPlayer(); + } + + @override + Future init() { + return _api.initialize(); + } + + @override + Future dispose(int textureId) { + return _api.dispose(TextureMessage()..textureId = textureId); + } + + @override + Future create(DataSource dataSource) async { + final CreateMessage message = CreateMessage(); + + switch (dataSource.sourceType) { + case DataSourceType.asset: + message.asset = dataSource.asset; + message.packageName = dataSource.package; + break; + case DataSourceType.network: + message.uri = dataSource.uri; + message.formatHint = _videoFormatStringMap[dataSource.formatHint]; + message.httpHeaders = dataSource.httpHeaders; + break; + case DataSourceType.file: + message.uri = dataSource.uri; + break; + case DataSourceType.contentUri: + message.uri = dataSource.uri; + break; + } + + final TextureMessage response = await _api.create(message); + return response.textureId; + } + + @override + Future setLooping(int textureId, bool looping) { + return _api.setLooping(LoopingMessage() + ..textureId = textureId + ..isLooping = looping); + } + + @override + Future play(int textureId) { + return _api.play(TextureMessage()..textureId = textureId); + } + + @override + Future pause(int textureId) { + return _api.pause(TextureMessage()..textureId = textureId); + } + + @override + Future setVolume(int textureId, double volume) { + return _api.setVolume(VolumeMessage() + ..textureId = textureId + ..volume = volume); + } + + @override + Future setPlaybackSpeed(int textureId, double speed) { + assert(speed > 0); + + return _api.setPlaybackSpeed(PlaybackSpeedMessage() + ..textureId = textureId + ..speed = speed); + } + + @override + Future seekTo(int textureId, Duration position) { + return _api.seekTo(PositionMessage() + ..textureId = textureId + ..position = position.inMilliseconds); + } + + @override + Future getPosition(int textureId) async { + final PositionMessage response = + await _api.position(TextureMessage()..textureId = textureId); + return Duration(milliseconds: response.position!); + } + + @override + Stream videoEventsFor(int textureId) { + return _eventChannelFor(textureId) + .receiveBroadcastStream() + .map((dynamic event) { + final Map map = event as Map; + switch (map['event']) { + case 'initialized': + return VideoEvent( + eventType: VideoEventType.initialized, + duration: Duration(milliseconds: map['duration'] as int), + size: Size((map['width'] as num?)?.toDouble() ?? 0.0, + (map['height'] as num?)?.toDouble() ?? 0.0), + ); + case 'completed': + return VideoEvent( + eventType: VideoEventType.completed, + ); + case 'bufferingUpdate': + final List values = map['values'] as List; + + return VideoEvent( + buffered: values.map(_toDurationRange).toList(), + eventType: VideoEventType.bufferingUpdate, + ); + case 'bufferingStart': + return VideoEvent(eventType: VideoEventType.bufferingStart); + case 'bufferingEnd': + return VideoEvent(eventType: VideoEventType.bufferingEnd); + default: + return VideoEvent(eventType: VideoEventType.unknown); + } + }); + } + + @override + Widget buildView(int textureId) { + return Texture(textureId: textureId); + } + + @override + Future setMixWithOthers(bool mixWithOthers) { + return _api.setMixWithOthers( + MixWithOthersMessage()..mixWithOthers = mixWithOthers, + ); + } + + EventChannel _eventChannelFor(int textureId) { + return EventChannel('flutter.io/videoPlayer/videoEvents$textureId'); + } + + static const Map _videoFormatStringMap = + { + VideoFormat.ss: 'ss', + VideoFormat.hls: 'hls', + VideoFormat.dash: 'dash', + VideoFormat.other: 'other', + }; + + DurationRange _toDurationRange(dynamic value) { + final List pair = value as List; + return DurationRange( + Duration(milliseconds: pair[0] as int), + Duration(milliseconds: pair[1] as int), + ); + } +} diff --git a/packages/video_player/video_player_avfoundation/lib/src/messages.dart b/packages/video_player/video_player_avfoundation/lib/src/messages.dart new file mode 100644 index 000000000000..831f4e3755d9 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/lib/src/messages.dart @@ -0,0 +1,425 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Autogenerated from Pigeon (v0.1.21), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, cast_nullable_to_non_nullable +// @dart = 2.12 +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; + +import 'package:flutter/services.dart'; + +class TextureMessage { + int? textureId; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['textureId'] = textureId; + return pigeonMap; + } + + static TextureMessage decode(Object message) { + final Map pigeonMap = message as Map; + return TextureMessage()..textureId = pigeonMap['textureId'] as int?; + } +} + +class CreateMessage { + String? asset; + String? uri; + String? packageName; + String? formatHint; + Map? httpHeaders; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['asset'] = asset; + pigeonMap['uri'] = uri; + pigeonMap['packageName'] = packageName; + pigeonMap['formatHint'] = formatHint; + pigeonMap['httpHeaders'] = httpHeaders; + return pigeonMap; + } + + static CreateMessage decode(Object message) { + final Map pigeonMap = message as Map; + return CreateMessage() + ..asset = pigeonMap['asset'] as String? + ..uri = pigeonMap['uri'] as String? + ..packageName = pigeonMap['packageName'] as String? + ..formatHint = pigeonMap['formatHint'] as String? + ..httpHeaders = pigeonMap['httpHeaders'] as Map?; + } +} + +class LoopingMessage { + int? textureId; + bool? isLooping; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['textureId'] = textureId; + pigeonMap['isLooping'] = isLooping; + return pigeonMap; + } + + static LoopingMessage decode(Object message) { + final Map pigeonMap = message as Map; + return LoopingMessage() + ..textureId = pigeonMap['textureId'] as int? + ..isLooping = pigeonMap['isLooping'] as bool?; + } +} + +class VolumeMessage { + int? textureId; + double? volume; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['textureId'] = textureId; + pigeonMap['volume'] = volume; + return pigeonMap; + } + + static VolumeMessage decode(Object message) { + final Map pigeonMap = message as Map; + return VolumeMessage() + ..textureId = pigeonMap['textureId'] as int? + ..volume = pigeonMap['volume'] as double?; + } +} + +class PlaybackSpeedMessage { + int? textureId; + double? speed; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['textureId'] = textureId; + pigeonMap['speed'] = speed; + return pigeonMap; + } + + static PlaybackSpeedMessage decode(Object message) { + final Map pigeonMap = message as Map; + return PlaybackSpeedMessage() + ..textureId = pigeonMap['textureId'] as int? + ..speed = pigeonMap['speed'] as double?; + } +} + +class PositionMessage { + int? textureId; + int? position; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['textureId'] = textureId; + pigeonMap['position'] = position; + return pigeonMap; + } + + static PositionMessage decode(Object message) { + final Map pigeonMap = message as Map; + return PositionMessage() + ..textureId = pigeonMap['textureId'] as int? + ..position = pigeonMap['position'] as int?; + } +} + +class MixWithOthersMessage { + bool? mixWithOthers; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['mixWithOthers'] = mixWithOthers; + return pigeonMap; + } + + static MixWithOthersMessage decode(Object message) { + final Map pigeonMap = message as Map; + return MixWithOthersMessage() + ..mixWithOthers = pigeonMap['mixWithOthers'] as bool?; + } +} + +class VideoPlayerApi { + Future initialize() async { + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.initialize', StandardMessageCodec()); + final Map? replyMap = + await channel.send(null) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + replyMap['error'] as Map; + throw PlatformException( + code: error['code'] as String, + message: error['message'] as String?, + details: error['details'], + ); + } else { + // noop + } + } + + Future create(CreateMessage arg) async { + final Object encoded = arg.encode(); + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.create', StandardMessageCodec()); + final Map? replyMap = + await channel.send(encoded) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + replyMap['error'] as Map; + throw PlatformException( + code: error['code'] as String, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return TextureMessage.decode(replyMap['result']!); + } + } + + Future dispose(TextureMessage arg) async { + final Object encoded = arg.encode(); + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.dispose', StandardMessageCodec()); + final Map? replyMap = + await channel.send(encoded) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + replyMap['error'] as Map; + throw PlatformException( + code: error['code'] as String, + message: error['message'] as String?, + details: error['details'], + ); + } else { + // noop + } + } + + Future setLooping(LoopingMessage arg) async { + final Object encoded = arg.encode(); + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setLooping', StandardMessageCodec()); + final Map? replyMap = + await channel.send(encoded) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + replyMap['error'] as Map; + throw PlatformException( + code: error['code'] as String, + message: error['message'] as String?, + details: error['details'], + ); + } else { + // noop + } + } + + Future setVolume(VolumeMessage arg) async { + final Object encoded = arg.encode(); + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setVolume', StandardMessageCodec()); + final Map? replyMap = + await channel.send(encoded) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + replyMap['error'] as Map; + throw PlatformException( + code: error['code'] as String, + message: error['message'] as String?, + details: error['details'], + ); + } else { + // noop + } + } + + Future setPlaybackSpeed(PlaybackSpeedMessage arg) async { + final Object encoded = arg.encode(); + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed', + StandardMessageCodec()); + final Map? replyMap = + await channel.send(encoded) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + replyMap['error'] as Map; + throw PlatformException( + code: error['code'] as String, + message: error['message'] as String?, + details: error['details'], + ); + } else { + // noop + } + } + + Future play(TextureMessage arg) async { + final Object encoded = arg.encode(); + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.play', StandardMessageCodec()); + final Map? replyMap = + await channel.send(encoded) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + replyMap['error'] as Map; + throw PlatformException( + code: error['code'] as String, + message: error['message'] as String?, + details: error['details'], + ); + } else { + // noop + } + } + + Future position(TextureMessage arg) async { + final Object encoded = arg.encode(); + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.position', StandardMessageCodec()); + final Map? replyMap = + await channel.send(encoded) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + replyMap['error'] as Map; + throw PlatformException( + code: error['code'] as String, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return PositionMessage.decode(replyMap['result']!); + } + } + + Future seekTo(PositionMessage arg) async { + final Object encoded = arg.encode(); + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.seekTo', StandardMessageCodec()); + final Map? replyMap = + await channel.send(encoded) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + replyMap['error'] as Map; + throw PlatformException( + code: error['code'] as String, + message: error['message'] as String?, + details: error['details'], + ); + } else { + // noop + } + } + + Future pause(TextureMessage arg) async { + final Object encoded = arg.encode(); + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.pause', StandardMessageCodec()); + final Map? replyMap = + await channel.send(encoded) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + replyMap['error'] as Map; + throw PlatformException( + code: error['code'] as String, + message: error['message'] as String?, + details: error['details'], + ); + } else { + // noop + } + } + + Future setMixWithOthers(MixWithOthersMessage arg) async { + final Object encoded = arg.encode(); + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers', + StandardMessageCodec()); + final Map? replyMap = + await channel.send(encoded) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + replyMap['error'] as Map; + throw PlatformException( + code: error['code'] as String, + message: error['message'] as String?, + details: error['details'], + ); + } else { + // noop + } + } +} diff --git a/packages/video_player/video_player_avfoundation/lib/video_player_avfoundation.dart b/packages/video_player/video_player_avfoundation/lib/video_player_avfoundation.dart new file mode 100644 index 000000000000..b36daa1b3ef8 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/lib/video_player_avfoundation.dart @@ -0,0 +1,5 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +export 'src/avfoundation_video_player.dart'; diff --git a/packages/video_player/video_player_avfoundation/pigeons/messages.dart b/packages/video_player/video_player_avfoundation/pigeons/messages.dart new file mode 100644 index 000000000000..42d96408da87 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/pigeons/messages.dart @@ -0,0 +1,69 @@ +// 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. + +// @dart = 2.9 + +import 'package:pigeon/pigeon_lib.dart'; + +class TextureMessage { + int textureId; +} + +class LoopingMessage { + int textureId; + bool isLooping; +} + +class VolumeMessage { + int textureId; + double volume; +} + +class PlaybackSpeedMessage { + int textureId; + double speed; +} + +class PositionMessage { + int textureId; + int position; +} + +class CreateMessage { + String asset; + String uri; + String packageName; + String formatHint; + Map httpHeaders; +} + +class MixWithOthersMessage { + bool mixWithOthers; +} + +@HostApi(dartHostTestHandler: 'TestHostVideoPlayerApi') +abstract class VideoPlayerApi { + void initialize(); + TextureMessage create(CreateMessage msg); + void dispose(TextureMessage msg); + void setLooping(LoopingMessage msg); + void setVolume(VolumeMessage msg); + void setPlaybackSpeed(PlaybackSpeedMessage msg); + void play(TextureMessage msg); + PositionMessage position(TextureMessage msg); + void seekTo(PositionMessage msg); + void pause(TextureMessage msg); + void setMixWithOthers(MixWithOthersMessage msg); +} + +void configurePigeon(PigeonOptions opts) { + opts.dartOut = 'lib/src/messages.dart'; + opts.dartTestOut = 'test/test_api.dart'; + opts.objcHeaderOut = 'ios/Classes/messages.h'; + opts.objcSourceOut = 'ios/Classes/messages.m'; + opts.objcOptions.prefix = 'FLT'; + opts.javaOut = + 'android/src/main/java/io/flutter/plugins/videoplayer/Messages.java'; + opts.javaOptions.package = 'io.flutter.plugins.videoplayer'; +} diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml new file mode 100644 index 000000000000..82ffedec2527 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -0,0 +1,27 @@ +name: video_player_avfoundation +description: iOS implementation of the video_player plugin. +repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_avfoundation +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 +version: 2.2.17 + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.8.0" + +flutter: + plugin: + implements: video_player + platforms: + ios: + dartPluginClass: AVFoundationVideoPlayer + pluginClass: FLTVideoPlayerPlugin + +dependencies: + flutter: + sdk: flutter + video_player_platform_interface: ">=4.2.0 <6.0.0" + +dev_dependencies: + flutter_test: + sdk: flutter + pigeon: ^0.1.21 diff --git a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart new file mode 100644 index 000000000000..adb29a53562f --- /dev/null +++ b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart @@ -0,0 +1,341 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:ui'; + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:video_player_avfoundation/src/messages.dart'; +import 'package:video_player_avfoundation/video_player_avfoundation.dart'; +import 'package:video_player_platform_interface/video_player_platform_interface.dart'; + +import 'test_api.dart'; + +class _ApiLogger implements TestHostVideoPlayerApi { + final List log = []; + TextureMessage? textureMessage; + CreateMessage? createMessage; + PositionMessage? positionMessage; + LoopingMessage? loopingMessage; + VolumeMessage? volumeMessage; + PlaybackSpeedMessage? playbackSpeedMessage; + MixWithOthersMessage? mixWithOthersMessage; + + @override + TextureMessage create(CreateMessage arg) { + log.add('create'); + createMessage = arg; + return TextureMessage()..textureId = 3; + } + + @override + void dispose(TextureMessage arg) { + log.add('dispose'); + textureMessage = arg; + } + + @override + void initialize() { + log.add('init'); + } + + @override + void pause(TextureMessage arg) { + log.add('pause'); + textureMessage = arg; + } + + @override + void play(TextureMessage arg) { + log.add('play'); + textureMessage = arg; + } + + @override + void setMixWithOthers(MixWithOthersMessage arg) { + log.add('setMixWithOthers'); + mixWithOthersMessage = arg; + } + + @override + PositionMessage position(TextureMessage arg) { + log.add('position'); + textureMessage = arg; + return PositionMessage()..position = 234; + } + + @override + void seekTo(PositionMessage arg) { + log.add('seekTo'); + positionMessage = arg; + } + + @override + void setLooping(LoopingMessage arg) { + log.add('setLooping'); + loopingMessage = arg; + } + + @override + void setVolume(VolumeMessage arg) { + log.add('setVolume'); + volumeMessage = arg; + } + + @override + void setPlaybackSpeed(PlaybackSpeedMessage arg) { + log.add('setPlaybackSpeed'); + playbackSpeedMessage = arg; + } +} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + test('registration', () async { + AVFoundationVideoPlayer.registerWith(); + expect(VideoPlayerPlatform.instance, isA()); + }); + + group('$AVFoundationVideoPlayer', () { + final AVFoundationVideoPlayer player = AVFoundationVideoPlayer(); + late _ApiLogger log; + + setUp(() { + log = _ApiLogger(); + TestHostVideoPlayerApi.setup(log); + }); + + test('init', () async { + await player.init(); + expect( + log.log.last, + 'init', + ); + }); + + test('dispose', () async { + await player.dispose(1); + expect(log.log.last, 'dispose'); + expect(log.textureMessage?.textureId, 1); + }); + + test('create with asset', () async { + final int? textureId = await player.create(DataSource( + sourceType: DataSourceType.asset, + asset: 'someAsset', + package: 'somePackage', + )); + expect(log.log.last, 'create'); + expect(log.createMessage?.asset, 'someAsset'); + expect(log.createMessage?.packageName, 'somePackage'); + expect(textureId, 3); + }); + + test('create with network', () async { + final int? textureId = await player.create(DataSource( + sourceType: DataSourceType.network, + uri: 'someUri', + formatHint: VideoFormat.dash, + )); + expect(log.log.last, 'create'); + expect(log.createMessage?.asset, null); + expect(log.createMessage?.uri, 'someUri'); + expect(log.createMessage?.packageName, null); + expect(log.createMessage?.formatHint, 'dash'); + expect(log.createMessage?.httpHeaders, {}); + expect(textureId, 3); + }); + + test('create with network (some headers)', () async { + final int? textureId = await player.create(DataSource( + sourceType: DataSourceType.network, + uri: 'someUri', + httpHeaders: {'Authorization': 'Bearer token'}, + )); + expect(log.log.last, 'create'); + expect(log.createMessage?.asset, null); + expect(log.createMessage?.uri, 'someUri'); + expect(log.createMessage?.packageName, null); + expect(log.createMessage?.formatHint, null); + expect(log.createMessage?.httpHeaders, + {'Authorization': 'Bearer token'}); + expect(textureId, 3); + }); + + test('create with file', () async { + final int? textureId = await player.create(DataSource( + sourceType: DataSourceType.file, + uri: 'someUri', + )); + expect(log.log.last, 'create'); + expect(log.createMessage?.uri, 'someUri'); + expect(textureId, 3); + }); + + test('setLooping', () async { + await player.setLooping(1, true); + expect(log.log.last, 'setLooping'); + expect(log.loopingMessage?.textureId, 1); + expect(log.loopingMessage?.isLooping, true); + }); + + test('play', () async { + await player.play(1); + expect(log.log.last, 'play'); + expect(log.textureMessage?.textureId, 1); + }); + + test('pause', () async { + await player.pause(1); + expect(log.log.last, 'pause'); + expect(log.textureMessage?.textureId, 1); + }); + + test('setMixWithOthers', () async { + await player.setMixWithOthers(true); + expect(log.log.last, 'setMixWithOthers'); + expect(log.mixWithOthersMessage?.mixWithOthers, true); + + await player.setMixWithOthers(false); + expect(log.log.last, 'setMixWithOthers'); + expect(log.mixWithOthersMessage?.mixWithOthers, false); + }); + + test('setVolume', () async { + await player.setVolume(1, 0.7); + expect(log.log.last, 'setVolume'); + expect(log.volumeMessage?.textureId, 1); + expect(log.volumeMessage?.volume, 0.7); + }); + + test('setPlaybackSpeed', () async { + await player.setPlaybackSpeed(1, 1.5); + expect(log.log.last, 'setPlaybackSpeed'); + expect(log.playbackSpeedMessage?.textureId, 1); + expect(log.playbackSpeedMessage?.speed, 1.5); + }); + + test('seekTo', () async { + await player.seekTo(1, const Duration(milliseconds: 12345)); + expect(log.log.last, 'seekTo'); + expect(log.positionMessage?.textureId, 1); + expect(log.positionMessage?.position, 12345); + }); + + test('getPosition', () async { + final Duration position = await player.getPosition(1); + expect(log.log.last, 'position'); + expect(log.textureMessage?.textureId, 1); + expect(position, const Duration(milliseconds: 234)); + }); + + test('videoEventsFor', () async { + _ambiguate(ServicesBinding.instance) + ?.defaultBinaryMessenger + .setMockMessageHandler( + 'flutter.io/videoPlayer/videoEvents123', + (ByteData? message) async { + final MethodCall methodCall = + const StandardMethodCodec().decodeMethodCall(message); + if (methodCall.method == 'listen') { + await _ambiguate(ServicesBinding.instance) + ?.defaultBinaryMessenger + .handlePlatformMessage( + 'flutter.io/videoPlayer/videoEvents123', + const StandardMethodCodec() + .encodeSuccessEnvelope({ + 'event': 'initialized', + 'duration': 98765, + 'width': 1920, + 'height': 1080, + }), + (ByteData? data) {}); + + await _ambiguate(ServicesBinding.instance) + ?.defaultBinaryMessenger + .handlePlatformMessage( + 'flutter.io/videoPlayer/videoEvents123', + const StandardMethodCodec() + .encodeSuccessEnvelope({ + 'event': 'completed', + }), + (ByteData? data) {}); + + await _ambiguate(ServicesBinding.instance) + ?.defaultBinaryMessenger + .handlePlatformMessage( + 'flutter.io/videoPlayer/videoEvents123', + const StandardMethodCodec() + .encodeSuccessEnvelope({ + 'event': 'bufferingUpdate', + 'values': >[ + [0, 1234], + [1235, 4000], + ], + }), + (ByteData? data) {}); + + await _ambiguate(ServicesBinding.instance) + ?.defaultBinaryMessenger + .handlePlatformMessage( + 'flutter.io/videoPlayer/videoEvents123', + const StandardMethodCodec() + .encodeSuccessEnvelope({ + 'event': 'bufferingStart', + }), + (ByteData? data) {}); + + await _ambiguate(ServicesBinding.instance) + ?.defaultBinaryMessenger + .handlePlatformMessage( + 'flutter.io/videoPlayer/videoEvents123', + const StandardMethodCodec() + .encodeSuccessEnvelope({ + 'event': 'bufferingEnd', + }), + (ByteData? data) {}); + + return const StandardMethodCodec().encodeSuccessEnvelope(null); + } else if (methodCall.method == 'cancel') { + return const StandardMethodCodec().encodeSuccessEnvelope(null); + } else { + fail('Expected listen or cancel'); + } + }, + ); + expect( + player.videoEventsFor(123), + emitsInOrder([ + VideoEvent( + eventType: VideoEventType.initialized, + duration: const Duration(milliseconds: 98765), + size: const Size(1920, 1080), + ), + VideoEvent(eventType: VideoEventType.completed), + VideoEvent( + eventType: VideoEventType.bufferingUpdate, + buffered: [ + DurationRange( + const Duration(milliseconds: 0), + const Duration(milliseconds: 1234), + ), + DurationRange( + const Duration(milliseconds: 1235), + const Duration(milliseconds: 4000), + ), + ]), + VideoEvent(eventType: VideoEventType.bufferingStart), + VideoEvent(eventType: VideoEventType.bufferingEnd), + ])); + }); + }); +} + +/// This allows a value of type T or T? to be treated as a value of type T?. +/// +/// We use this so that APIs that have become non-nullable can still be used +/// with `!` and `?` on the stable branch. +// TODO(ianh): Remove this once we roll stable in late 2021. +T? _ambiguate(T? value) => value; diff --git a/packages/video_player/video_player_avfoundation/test/test_api.dart b/packages/video_player/video_player_avfoundation/test/test_api.dart new file mode 100644 index 000000000000..b173705891d5 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/test/test_api.dart @@ -0,0 +1,199 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Autogenerated from Pigeon (v0.1.21), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import +// @dart = 2.12 +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:video_player_avfoundation/src/messages.dart'; + +abstract class TestHostVideoPlayerApi { + void initialize(); + TextureMessage create(CreateMessage arg); + void dispose(TextureMessage arg); + void setLooping(LoopingMessage arg); + void setVolume(VolumeMessage arg); + void setPlaybackSpeed(PlaybackSpeedMessage arg); + void play(TextureMessage arg); + PositionMessage position(TextureMessage arg); + void seekTo(PositionMessage arg); + void pause(TextureMessage arg); + void setMixWithOthers(MixWithOthersMessage arg); + static void setup(TestHostVideoPlayerApi? api) { + { + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.initialize', + StandardMessageCodec()); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + // ignore message + api.initialize(); + return {}; + }); + } + } + { + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.create', StandardMessageCodec()); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.create was null. Expected CreateMessage.'); + final CreateMessage input = CreateMessage.decode(message!); + final TextureMessage output = api.create(input); + return {'result': output.encode()}; + }); + } + } + { + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.dispose', StandardMessageCodec()); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.dispose was null. Expected TextureMessage.'); + final TextureMessage input = TextureMessage.decode(message!); + api.dispose(input); + return {}; + }); + } + } + { + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setLooping', + StandardMessageCodec()); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.setLooping was null. Expected LoopingMessage.'); + final LoopingMessage input = LoopingMessage.decode(message!); + api.setLooping(input); + return {}; + }); + } + } + { + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setVolume', + StandardMessageCodec()); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.setVolume was null. Expected VolumeMessage.'); + final VolumeMessage input = VolumeMessage.decode(message!); + api.setVolume(input); + return {}; + }); + } + } + { + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed', + StandardMessageCodec()); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed was null. Expected PlaybackSpeedMessage.'); + final PlaybackSpeedMessage input = + PlaybackSpeedMessage.decode(message!); + api.setPlaybackSpeed(input); + return {}; + }); + } + } + { + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.play', StandardMessageCodec()); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.play was null. Expected TextureMessage.'); + final TextureMessage input = TextureMessage.decode(message!); + api.play(input); + return {}; + }); + } + } + { + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.position', StandardMessageCodec()); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.position was null. Expected TextureMessage.'); + final TextureMessage input = TextureMessage.decode(message!); + final PositionMessage output = api.position(input); + return {'result': output.encode()}; + }); + } + } + { + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.seekTo', StandardMessageCodec()); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.seekTo was null. Expected PositionMessage.'); + final PositionMessage input = PositionMessage.decode(message!); + api.seekTo(input); + return {}; + }); + } + } + { + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.pause', StandardMessageCodec()); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.pause was null. Expected TextureMessage.'); + final TextureMessage input = TextureMessage.decode(message!); + api.pause(input); + return {}; + }); + } + } + { + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers', + StandardMessageCodec()); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers was null. Expected MixWithOthersMessage.'); + final MixWithOthersMessage input = + MixWithOthersMessage.decode(message!); + api.setMixWithOthers(input); + return {}; + }); + } + } + } +} diff --git a/packages/video_player/video_player_platform_interface/CHANGELOG.md b/packages/video_player/video_player_platform_interface/CHANGELOG.md index bf51960bcb40..8d295aa06a0a 100644 --- a/packages/video_player/video_player_platform_interface/CHANGELOG.md +++ b/packages/video_player/video_player_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adds the Pigeon definitions used to create the method channel implementation. + ## 5.0.1 * Update to use the `verify` method introduced in platform_plugin_interface 2.1.0. diff --git a/packages/video_player/video_player_platform_interface/CONTRIBUTING.md b/packages/video_player/video_player_platform_interface/CONTRIBUTING.md new file mode 100644 index 000000000000..dbbfbf66c9a1 --- /dev/null +++ b/packages/video_player/video_player_platform_interface/CONTRIBUTING.md @@ -0,0 +1,46 @@ +## Updating pigeon-generated files + +**WARNING**: Because `messages.dart` is part of the public API of this package, +breaking changes in that file are breaking changes for the package. This means +that: +- You should never update the version of Pigeon used for this package unless + making a breaking change to the package for other reasons. +- Because the method channel is a legacy implementation for compatibility with + existing third-party `video_player` implementations, in many cases the best + option may be to simply not implemented new features in + `MethodChannelVideoPlayer`. Breaking changes in this package should never + be made solely to change `MethodChannelVideoPlayer`. + +### Update process + +If you update files in the pigeons/ directory, run the following +command in this directory (ignore the errors you get about +dependencies in the examples directory): + +```bash +flutter pub upgrade +flutter pub run pigeon --dart_null_safety --input pigeons/messages.dart +# git commit your changes so that your working environment is clean +(cd ../../../; ./script/tool_runner.sh format --clang-format=clang-format-7) +``` + +If you update pigeon itself and want to test the changes here, +temporarily update the pubspec.yaml by adding the following to the +`dependency_overrides` section, assuming you have checked out the +`flutter/packages` repo in a sibling directory to the `plugins` repo: + +```yaml + pigeon: + path: + ../../../../packages/packages/pigeon/ +``` + +Then, run the commands above. When you run `pub get` it should warn +you that you're using an override. If you do this, you will need to +publish pigeon before you can land the updates to this package, since +the CI tests run the analysis using latest published version of +pigeon, not your version or the version on master. + +In either case, the configuration will be obtained automatically from +the `pigeons/messages.dart` file (see `configurePigeon` at the bottom +of that file). diff --git a/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart b/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart index e01e5b8c072c..31d1839595e3 100644 --- a/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart +++ b/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart @@ -12,6 +12,10 @@ import 'messages.dart'; import 'video_player_platform_interface.dart'; /// An implementation of [VideoPlayerPlatform] that uses method channels. +/// +/// This is the default implementation, for compatibility with existing +/// third-party implementations. It is not used by other implementations in +/// this repository. class MethodChannelVideoPlayer extends VideoPlayerPlatform { VideoPlayerApi _api = VideoPlayerApi(); diff --git a/packages/video_player/video_player_platform_interface/pigeons/messages.dart b/packages/video_player/video_player_platform_interface/pigeons/messages.dart new file mode 100644 index 000000000000..144edb6133b0 --- /dev/null +++ b/packages/video_player/video_player_platform_interface/pigeons/messages.dart @@ -0,0 +1,63 @@ +// 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. + +// @dart = 2.9 + +import 'package:pigeon/pigeon_lib.dart'; + +class TextureMessage { + int textureId; +} + +class LoopingMessage { + int textureId; + bool isLooping; +} + +class VolumeMessage { + int textureId; + double volume; +} + +class PlaybackSpeedMessage { + int textureId; + double speed; +} + +class PositionMessage { + int textureId; + int position; +} + +class CreateMessage { + String asset; + String uri; + String packageName; + String formatHint; + Map httpHeaders; +} + +class MixWithOthersMessage { + bool mixWithOthers; +} + +@HostApi(dartHostTestHandler: 'TestHostVideoPlayerApi') +abstract class VideoPlayerApi { + void initialize(); + TextureMessage create(CreateMessage msg); + void dispose(TextureMessage msg); + void setLooping(LoopingMessage msg); + void setVolume(VolumeMessage msg); + void setPlaybackSpeed(PlaybackSpeedMessage msg); + void play(TextureMessage msg); + PositionMessage position(TextureMessage msg); + void seekTo(PositionMessage msg); + void pause(TextureMessage msg); + void setMixWithOthers(MixWithOthersMessage msg); +} + +void configurePigeon(PigeonOptions opts) { + opts.dartOut = 'lib/messages.dart'; + opts.dartTestOut = 'test/test.dart'; +} diff --git a/packages/video_player/video_player_platform_interface/pubspec.yaml b/packages/video_player/video_player_platform_interface/pubspec.yaml index f0448f7d9c35..b7d5745b3525 100644 --- a/packages/video_player/video_player_platform_interface/pubspec.yaml +++ b/packages/video_player/video_player_platform_interface/pubspec.yaml @@ -19,3 +19,4 @@ dev_dependencies: flutter_test: sdk: flutter pedantic: ^1.10.0 + pigeon: 0.1.21 From 93786708929ff02273a7f767d58f3f4b33f10fcb Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 2 Feb 2022 14:45:25 -0500 Subject: [PATCH 131/600] [video_player] Publish fully federation version (#4724) --- packages/video_player/video_player/CHANGELOG.md | 2 +- packages/video_player/video_player/pubspec.yaml | 14 ++++---------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 76c0a4a9ab9d..adf696ad0120 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,4 +1,4 @@ -## NEXT +## 2.2.18 * Moves Android and iOS implementations to federated packages. * Update audio URL in iOS tests. diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 02739bb7c502..a444fc69c912 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -3,14 +3,11 @@ description: Flutter plugin for displaying inline video with other Flutter widgets on Android, iOS, and web. repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.2.17 -# Temporarily disable publishing to allow moving Android and iOS -# implementations. -publish_to: none +version: 2.2.18 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: @@ -25,11 +22,8 @@ flutter: dependencies: flutter: sdk: flutter - # Temporary path dependencies to allow moving Android and iOS implementations. - video_player_android: - path: ../video_player_android - video_player_avfoundation: - path: ../video_player_avfoundation + video_player_android: ^2.2.17 + video_player_avfoundation: ^2.2.17 video_player_platform_interface: ">=4.2.0 <6.0.0" video_player_web: ^2.0.0 html: ^0.15.0 From dbbd21fcd6b10d85513d6a78f8b70ed84a3a0ae2 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 2 Feb 2022 18:35:19 -0500 Subject: [PATCH 132/600] [video_player] Adjust iOS test timeouts (#4725) --- packages/video_player/video_player_avfoundation/CHANGELOG.md | 4 ++++ .../example/ios/RunnerTests/VideoPlayerTests.m | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index 626b9ef887cf..cc042ba2bfaa 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adjusts test timeouts for network-dependent native tests to avoid flake. + ## 2.2.17 * Splits from `video_player` as a federated implementation. diff --git a/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m b/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m index a858b1423702..261f18ad2786 100644 --- a/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m +++ b/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m @@ -67,7 +67,7 @@ - (void)testDeregistersFromPlayer { XCTAssertNil(error); [self keyValueObservingExpectationForObject:avPlayer keyPath:@"currentItem" expectedValue:nil]; - [self waitForExpectationsWithTimeout:1 handler:nil]; + [self waitForExpectationsWithTimeout:30.0 handler:nil]; } - (void)testVideoControls { @@ -127,7 +127,7 @@ - (void)testAudioControls { [initializedExpectation fulfill]; } }]; - [self waitForExpectationsWithTimeout:1.0 handler:nil]; + [self waitForExpectationsWithTimeout:30.0 handler:nil]; // Starts paused. AVPlayer *avPlayer = player.player; From bf01875d528280b4788ad78d251603df4660bbf3 Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Thu, 3 Feb 2022 18:27:03 +0200 Subject: [PATCH 133/600] [camera_windows] Support camera on windows desktop platform (#4641) This PR adds a Windows Desktop implementation of [camera plugin](https://pub.dev/packages/camera) using Media Foundation and CaptureEngine interface. The implementation supports multiple cameras, video preview, and capturing photos and videos. Works with camera plugin and [camera example](https://pub.dev/packages/camera/example). The current implementation uses FlutterDesktopPixelBuffer, but could be switched to use shared textures or Platform views after those are supported. Unit tests are partially done; for the CaptureControllerImpl class all falilure code paths are not yet covered. Still missing some features, like exposure and focus controls. These can be done later with interfaces IAMCameraControl and IAMVideoProcAmp, Resolves flutter/flutter#41709: [camera] Add Windows support --- packages/camera/camera_windows/.gitignore | 7 + packages/camera/camera_windows/.metadata | 10 + packages/camera/camera_windows/AUTHORS | 8 + packages/camera/camera_windows/CHANGELOG.md | 3 + packages/camera/camera_windows/LICENSE | 25 + packages/camera/camera_windows/README.md | 66 ++ .../camera/camera_windows/example/.gitignore | 46 + .../camera/camera_windows/example/.metadata | 10 + .../camera/camera_windows/example/README.md | 3 + .../example/integration_test/camera_test.dart | 100 ++ .../camera_windows/example/lib/main.dart | 452 ++++++++ .../camera_windows/example/pubspec.yaml | 28 + .../example/test_driver/integration_test.dart | 7 + .../camera_windows/example/windows/.gitignore | 17 + .../example/windows/CMakeLists.txt | 100 ++ .../example/windows/flutter/CMakeLists.txt | 103 ++ .../windows/flutter/generated_plugins.cmake | 24 + .../example/windows/runner/CMakeLists.txt | 18 + .../example/windows/runner/Runner.rc | 121 ++ .../example/windows/runner/flutter_window.cpp | 65 ++ .../example/windows/runner/flutter_window.h | 37 + .../example/windows/runner/main.cpp | 46 + .../example/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../windows/runner/runner.exe.manifest | 20 + .../example/windows/runner/utils.cpp | 67 ++ .../example/windows/runner/utils.h | 23 + .../example/windows/runner/win32_window.cpp | 241 ++++ .../example/windows/runner/win32_window.h | 99 ++ .../camera_windows/lib/camera_windows.dart | 433 +++++++ packages/camera/camera_windows/pubspec.yaml | 29 + .../test/camera_windows_test.dart | 664 +++++++++++ .../test/utils/method_channel_mock.dart | 45 + .../camera/camera_windows/windows/.gitignore | 17 + .../camera_windows/windows/CMakeLists.txt | 99 ++ .../camera/camera_windows/windows/camera.cpp | 264 +++++ .../camera/camera_windows/windows/camera.h | 194 ++++ .../camera_windows/windows/camera_plugin.cpp | 594 ++++++++++ .../camera_windows/windows/camera_plugin.h | 132 +++ .../camera_windows/windows/camera_windows.cpp | 16 + .../windows/capture_controller.cpp | 861 ++++++++++++++ .../windows/capture_controller.h | 292 +++++ .../windows/capture_controller_listener.h | 104 ++ .../windows/capture_device_info.cpp | 29 + .../windows/capture_device_info.h | 49 + .../windows/capture_engine_listener.cpp | 90 ++ .../windows/capture_engine_listener.h | 69 ++ .../camera_windows/windows/com_heap_ptr.h | 66 ++ .../include/camera_windows/camera_windows.h | 27 + .../camera_windows/windows/photo_handler.cpp | 141 +++ .../camera_windows/windows/photo_handler.h | 80 ++ .../windows/preview_handler.cpp | 164 +++ .../camera_windows/windows/preview_handler.h | 103 ++ .../camera_windows/windows/record_handler.cpp | 260 +++++ .../camera_windows/windows/record_handler.h | 118 ++ .../camera_windows/windows/string_utils.cpp | 60 + .../camera_windows/windows/string_utils.h | 22 + .../windows/test/camera_plugin_test.cpp | 1010 ++++++++++++++++ .../windows/test/camera_test.cpp | 344 ++++++ .../windows/test/capture_controller_test.cpp | 503 ++++++++ .../camera_windows/windows/test/mocks.h | 1015 +++++++++++++++++ .../windows/texture_handler.cpp | 144 +++ .../camera_windows/windows/texture_handler.h | 91 ++ 63 files changed, 9891 insertions(+) create mode 100644 packages/camera/camera_windows/.gitignore create mode 100644 packages/camera/camera_windows/.metadata create mode 100644 packages/camera/camera_windows/AUTHORS create mode 100644 packages/camera/camera_windows/CHANGELOG.md create mode 100644 packages/camera/camera_windows/LICENSE create mode 100644 packages/camera/camera_windows/README.md create mode 100644 packages/camera/camera_windows/example/.gitignore create mode 100644 packages/camera/camera_windows/example/.metadata create mode 100644 packages/camera/camera_windows/example/README.md create mode 100644 packages/camera/camera_windows/example/integration_test/camera_test.dart create mode 100644 packages/camera/camera_windows/example/lib/main.dart create mode 100644 packages/camera/camera_windows/example/pubspec.yaml create mode 100644 packages/camera/camera_windows/example/test_driver/integration_test.dart create mode 100644 packages/camera/camera_windows/example/windows/.gitignore create mode 100644 packages/camera/camera_windows/example/windows/CMakeLists.txt create mode 100644 packages/camera/camera_windows/example/windows/flutter/CMakeLists.txt create mode 100644 packages/camera/camera_windows/example/windows/flutter/generated_plugins.cmake create mode 100644 packages/camera/camera_windows/example/windows/runner/CMakeLists.txt create mode 100644 packages/camera/camera_windows/example/windows/runner/Runner.rc create mode 100644 packages/camera/camera_windows/example/windows/runner/flutter_window.cpp create mode 100644 packages/camera/camera_windows/example/windows/runner/flutter_window.h create mode 100644 packages/camera/camera_windows/example/windows/runner/main.cpp create mode 100644 packages/camera/camera_windows/example/windows/runner/resource.h create mode 100644 packages/camera/camera_windows/example/windows/runner/resources/app_icon.ico create mode 100644 packages/camera/camera_windows/example/windows/runner/runner.exe.manifest create mode 100644 packages/camera/camera_windows/example/windows/runner/utils.cpp create mode 100644 packages/camera/camera_windows/example/windows/runner/utils.h create mode 100644 packages/camera/camera_windows/example/windows/runner/win32_window.cpp create mode 100644 packages/camera/camera_windows/example/windows/runner/win32_window.h create mode 100644 packages/camera/camera_windows/lib/camera_windows.dart create mode 100644 packages/camera/camera_windows/pubspec.yaml create mode 100644 packages/camera/camera_windows/test/camera_windows_test.dart create mode 100644 packages/camera/camera_windows/test/utils/method_channel_mock.dart create mode 100644 packages/camera/camera_windows/windows/.gitignore create mode 100644 packages/camera/camera_windows/windows/CMakeLists.txt create mode 100644 packages/camera/camera_windows/windows/camera.cpp create mode 100644 packages/camera/camera_windows/windows/camera.h create mode 100644 packages/camera/camera_windows/windows/camera_plugin.cpp create mode 100644 packages/camera/camera_windows/windows/camera_plugin.h create mode 100644 packages/camera/camera_windows/windows/camera_windows.cpp create mode 100644 packages/camera/camera_windows/windows/capture_controller.cpp create mode 100644 packages/camera/camera_windows/windows/capture_controller.h create mode 100644 packages/camera/camera_windows/windows/capture_controller_listener.h create mode 100644 packages/camera/camera_windows/windows/capture_device_info.cpp create mode 100644 packages/camera/camera_windows/windows/capture_device_info.h create mode 100644 packages/camera/camera_windows/windows/capture_engine_listener.cpp create mode 100644 packages/camera/camera_windows/windows/capture_engine_listener.h create mode 100644 packages/camera/camera_windows/windows/com_heap_ptr.h create mode 100644 packages/camera/camera_windows/windows/include/camera_windows/camera_windows.h create mode 100644 packages/camera/camera_windows/windows/photo_handler.cpp create mode 100644 packages/camera/camera_windows/windows/photo_handler.h create mode 100644 packages/camera/camera_windows/windows/preview_handler.cpp create mode 100644 packages/camera/camera_windows/windows/preview_handler.h create mode 100644 packages/camera/camera_windows/windows/record_handler.cpp create mode 100644 packages/camera/camera_windows/windows/record_handler.h create mode 100644 packages/camera/camera_windows/windows/string_utils.cpp create mode 100644 packages/camera/camera_windows/windows/string_utils.h create mode 100644 packages/camera/camera_windows/windows/test/camera_plugin_test.cpp create mode 100644 packages/camera/camera_windows/windows/test/camera_test.cpp create mode 100644 packages/camera/camera_windows/windows/test/capture_controller_test.cpp create mode 100644 packages/camera/camera_windows/windows/test/mocks.h create mode 100644 packages/camera/camera_windows/windows/texture_handler.cpp create mode 100644 packages/camera/camera_windows/windows/texture_handler.h diff --git a/packages/camera/camera_windows/.gitignore b/packages/camera/camera_windows/.gitignore new file mode 100644 index 000000000000..e9dc58d3d6e2 --- /dev/null +++ b/packages/camera/camera_windows/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +.dart_tool/ + +.packages +.pub/ + +build/ diff --git a/packages/camera/camera_windows/.metadata b/packages/camera/camera_windows/.metadata new file mode 100644 index 000000000000..5bed5265e818 --- /dev/null +++ b/packages/camera/camera_windows/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 18116933e77adc82f80866c928266a5b4f1ed645 + channel: stable + +project_type: plugin diff --git a/packages/camera/camera_windows/AUTHORS b/packages/camera/camera_windows/AUTHORS new file mode 100644 index 000000000000..b2178a5e8444 --- /dev/null +++ b/packages/camera/camera_windows/AUTHORS @@ -0,0 +1,8 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. +Joonas Kerttula +Codemate Ltd. diff --git a/packages/camera/camera_windows/CHANGELOG.md b/packages/camera/camera_windows/CHANGELOG.md new file mode 100644 index 000000000000..1318780830f8 --- /dev/null +++ b/packages/camera/camera_windows/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.1.0 + +* Initial release diff --git a/packages/camera/camera_windows/LICENSE b/packages/camera/camera_windows/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/camera/camera_windows/LICENSE @@ -0,0 +1,25 @@ +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/camera/camera_windows/README.md b/packages/camera/camera_windows/README.md new file mode 100644 index 000000000000..dc27bcc85e9d --- /dev/null +++ b/packages/camera/camera_windows/README.md @@ -0,0 +1,66 @@ +# Camera Windows Plugin + +The Windows implementation of [`camera`][camera]. + +*Note*: This plugin is under development. +See [missing implementations and limitations](#missing-features-on-the-windows-platform). + +## Usage + +### Depend on the package + +This package is not an [endorsed][endorsed-federated-plugin] +implementation of the [`camera`][camera] plugin, so you'll need to +[add it explicitly][install]. + +## Missing features on the Windows platform + +### Device orientation + +Device orientation detection +is not yet implemented: [issue #97540][device-orientation-issue]. + +### Pause and Resume video recording + +Pausing and resuming the video recording +is not supported due to Windows API limitations. + +### Exposure mode, point and offset + +Support for explosure mode and offset +is not yet implemented: [issue #97537][camera-control-issue]. + +Exposure points are not supported due to +limitations of the Windows API. + +### Focus mode and point + +Support for explosure mode and offset +is not yet implemented: [issue #97537][camera-control-issue]. + +### Flash mode + +Support for flash mode is not yet implemented: [issue #97537][camera-control-issue]. + +Focus points are not supported due to +current limitations of the Windows API. + +### Streaming of frames + +Support for image streaming is not yet implemented: [issue #97542][image-streams-issue]. + +## Error handling + +Camera errors can be listened using the platform's `onCameraError` method. + +Listening to errors is important, and in certain situations, +disposing of the camera is the only way to reset the situation. + + + +[camera]: https://pub.dev/packages/camera +[endorsed-federated-plugin]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin +[install]: https://pub.dev/packages/camera_windows/install +[camera-control-issue]: https://github.com/flutter/flutter/issues/97537 +[device-orientation-issue]: https://github.com/flutter/flutter/issues/97540 +[image-streams-issue]: https://github.com/flutter/flutter/issues/97542 \ No newline at end of file diff --git a/packages/camera/camera_windows/example/.gitignore b/packages/camera/camera_windows/example/.gitignore new file mode 100644 index 000000000000..0fa6b675c0a5 --- /dev/null +++ b/packages/camera/camera_windows/example/.gitignore @@ -0,0 +1,46 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/packages/camera/camera_windows/example/.metadata b/packages/camera/camera_windows/example/.metadata new file mode 100644 index 000000000000..a5584fc372d9 --- /dev/null +++ b/packages/camera/camera_windows/example/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 18116933e77adc82f80866c928266a5b4f1ed645 + channel: stable + +project_type: app diff --git a/packages/camera/camera_windows/example/README.md b/packages/camera/camera_windows/example/README.md new file mode 100644 index 000000000000..ee7326472eaf --- /dev/null +++ b/packages/camera/camera_windows/example/README.md @@ -0,0 +1,3 @@ +# camera_windows_example + +Demonstrates how to use the camera_windows plugin. diff --git a/packages/camera/camera_windows/example/integration_test/camera_test.dart b/packages/camera/camera_windows/example/integration_test/camera_test.dart new file mode 100644 index 000000000000..cda0f402de6c --- /dev/null +++ b/packages/camera/camera_windows/example/integration_test/camera_test.dart @@ -0,0 +1,100 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'package:async/async.dart'; +import 'package:camera_platform_interface/camera_platform_interface.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + +// Note that these integration tests do not currently cover +// most features and code paths, as they can only be tested if +// one or more cameras are available in the test environment. +// Native unit tests with better coverage are available at +// the native part of the plugin implementation. + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group('initializeCamera', () { + testWidgets('throws exception if camera is not created', + (WidgetTester _) async { + final CameraPlatform camera = CameraPlatform.instance; + + expect(() async => await camera.initializeCamera(1234), + throwsA(isA())); + }); + }); + + group('takePicture', () { + testWidgets('throws exception if camera is not created', + (WidgetTester _) async { + final CameraPlatform camera = CameraPlatform.instance; + + expect(() async => await camera.takePicture(1234), + throwsA(isA())); + }); + }); + + group('startVideoRecording', () { + testWidgets('throws exception if camera is not created', + (WidgetTester _) async { + final CameraPlatform camera = CameraPlatform.instance; + + expect(() async => await camera.startVideoRecording(1234), + throwsA(isA())); + }); + }); + + group('stopVideoRecording', () { + testWidgets('throws exception if camera is not created', + (WidgetTester _) async { + final CameraPlatform camera = CameraPlatform.instance; + + expect(() async => await camera.stopVideoRecording(1234), + throwsA(isA())); + }); + }); + + group('pausePreview', () { + testWidgets('throws exception if camera is not created', + (WidgetTester _) async { + final CameraPlatform camera = CameraPlatform.instance; + + expect(() async => await camera.pausePreview(1234), + throwsA(isA())); + }); + }); + + group('resumePreview', () { + testWidgets('throws exception if camera is not created', + (WidgetTester _) async { + final CameraPlatform camera = CameraPlatform.instance; + + expect(() async => await camera.resumePreview(1234), + throwsA(isA())); + }); + }); + + group('onDeviceOrientationChanged', () { + testWidgets('emits the initial DeviceOrientationChangedEvent', + (WidgetTester _) async { + final Stream eventStream = + CameraPlatform.instance.onDeviceOrientationChanged(); + + final StreamQueue streamQueue = + StreamQueue(eventStream); + + expect( + await streamQueue.next, + equals( + const DeviceOrientationChangedEvent( + DeviceOrientation.landscapeRight, + ), + ), + ); + }); + }); +} diff --git a/packages/camera/camera_windows/example/lib/main.dart b/packages/camera/camera_windows/example/lib/main.dart new file mode 100644 index 000000000000..b73e00cac52b --- /dev/null +++ b/packages/camera/camera_windows/example/lib/main.dart @@ -0,0 +1,452 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:camera_platform_interface/camera_platform_interface.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +void main() { + runApp(MyApp()); +} + +/// Example app for Camera Windows plugin. +class MyApp extends StatefulWidget { + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + String _cameraInfo = 'Unknown'; + List _cameras = []; + int _cameraIndex = 0; + int _cameraId = -1; + bool _initialized = false; + bool _recording = false; + bool _recordingTimed = false; + bool _recordAudio = true; + bool _previewPaused = false; + Size? _previewSize; + ResolutionPreset _resolutionPreset = ResolutionPreset.veryHigh; + StreamSubscription? _errorStreamSubscription; + StreamSubscription? _cameraClosingStreamSubscription; + + @override + void initState() { + super.initState(); + WidgetsFlutterBinding.ensureInitialized(); + _fetchCameras(); + } + + @override + void dispose() { + _disposeCurrentCamera(); + _errorStreamSubscription?.cancel(); + _errorStreamSubscription = null; + _cameraClosingStreamSubscription?.cancel(); + _cameraClosingStreamSubscription = null; + super.dispose(); + } + + /// Fetches list of available cameras from camera_windows plugin. + Future _fetchCameras() async { + String cameraInfo; + List cameras = []; + + int cameraIndex = 0; + try { + cameras = await CameraPlatform.instance.availableCameras(); + if (cameras.isEmpty) { + cameraInfo = 'No available cameras'; + } else { + cameraIndex = _cameraIndex % cameras.length; + cameraInfo = 'Found camera: ${cameras[cameraIndex].name}'; + } + } on PlatformException catch (e) { + cameraInfo = 'Failed to get cameras: ${e.code}: ${e.message}'; + } + + if (mounted) { + setState(() { + _cameraIndex = cameraIndex; + _cameras = cameras; + _cameraInfo = cameraInfo; + }); + } + } + + /// Initializes the camera on the device. + Future _initializeCamera() async { + assert(!_initialized); + + if (_cameras.isEmpty) { + return; + } + + int cameraId = -1; + try { + final int cameraIndex = _cameraIndex % _cameras.length; + final CameraDescription camera = _cameras[cameraIndex]; + + cameraId = await CameraPlatform.instance.createCamera( + camera, + _resolutionPreset, + enableAudio: _recordAudio, + ); + + _errorStreamSubscription?.cancel(); + _errorStreamSubscription = CameraPlatform.instance + .onCameraError(cameraId) + .listen(_onCameraError); + + _cameraClosingStreamSubscription?.cancel(); + _cameraClosingStreamSubscription = CameraPlatform.instance + .onCameraClosing(cameraId) + .listen(_onCameraClosing); + + final Future initialized = + CameraPlatform.instance.onCameraInitialized(cameraId).first; + + await CameraPlatform.instance.initializeCamera( + cameraId, + imageFormatGroup: ImageFormatGroup.unknown, + ); + + final CameraInitializedEvent event = await initialized; + _previewSize = Size( + event.previewWidth, + event.previewHeight, + ); + + if (mounted) { + setState(() { + _initialized = true; + _cameraId = cameraId; + _cameraIndex = cameraIndex; + _cameraInfo = 'Capturing camera: ${camera.name}'; + }); + } + } on CameraException catch (e) { + try { + if (cameraId >= 0) { + await CameraPlatform.instance.dispose(cameraId); + } + } on CameraException catch (e) { + debugPrint('Failed to dispose camera: ${e.code}: ${e.description}'); + } + + // Reset state. + if (mounted) { + setState(() { + _initialized = false; + _cameraId = -1; + _cameraIndex = 0; + _previewSize = null; + _recording = false; + _recordingTimed = false; + _cameraInfo = + 'Failed to initialize camera: ${e.code}: ${e.description}'; + }); + } + } + } + + Future _disposeCurrentCamera() async { + if (_cameraId >= 0 && _initialized) { + try { + await CameraPlatform.instance.dispose(_cameraId); + + if (mounted) { + setState(() { + _initialized = false; + _cameraId = -1; + _previewSize = null; + _recording = false; + _recordingTimed = false; + _previewPaused = false; + _cameraInfo = 'Camera disposed'; + }); + } + } on CameraException catch (e) { + if (mounted) { + setState(() { + _cameraInfo = + 'Failed to dispose camera: ${e.code}: ${e.description}'; + }); + } + } + } + } + + Widget _buildPreview() { + return CameraPlatform.instance.buildPreview(_cameraId); + } + + Future _takePicture() async { + final XFile _file = await CameraPlatform.instance.takePicture(_cameraId); + _showInSnackBar('Picture captured to: ${_file.path}'); + } + + Future _recordTimed(int seconds) async { + if (_initialized && _cameraId > 0 && !_recordingTimed) { + CameraPlatform.instance + .onVideoRecordedEvent(_cameraId) + .first + .then((VideoRecordedEvent event) async { + if (mounted) { + setState(() { + _recordingTimed = false; + }); + + _showInSnackBar('Video captured to: ${event.file.path}'); + } + }); + + await CameraPlatform.instance.startVideoRecording( + _cameraId, + maxVideoDuration: Duration(seconds: seconds), + ); + + if (mounted) { + setState(() { + _recordingTimed = true; + }); + } + } + } + + Future _toggleRecord() async { + if (_initialized && _cameraId > 0) { + if (_recordingTimed) { + /// Request to stop timed recording short. + await CameraPlatform.instance.stopVideoRecording(_cameraId); + } else { + if (!_recording) { + await CameraPlatform.instance.startVideoRecording(_cameraId); + } else { + final XFile _file = + await CameraPlatform.instance.stopVideoRecording(_cameraId); + + _showInSnackBar('Video captured to: ${_file.path}'); + } + + if (mounted) { + setState(() { + _recording = !_recording; + }); + } + } + } + } + + Future _togglePreview() async { + if (_initialized && _cameraId >= 0) { + if (!_previewPaused) { + await CameraPlatform.instance.pausePreview(_cameraId); + } else { + await CameraPlatform.instance.resumePreview(_cameraId); + } + if (mounted) { + setState(() { + _previewPaused = !_previewPaused; + }); + } + } + } + + Future _switchCamera() async { + if (_cameras.isNotEmpty) { + // select next index; + _cameraIndex = (_cameraIndex + 1) % _cameras.length; + if (_initialized && _cameraId >= 0) { + await _disposeCurrentCamera(); + await _fetchCameras(); + if (_cameras.isNotEmpty) { + await _initializeCamera(); + } + } else { + await _fetchCameras(); + } + } + } + + Future _onResolutionChange(ResolutionPreset newValue) async { + setState(() { + _resolutionPreset = newValue; + }); + if (_initialized && _cameraId >= 0) { + // Re-inits camera with new resolution preset. + await _disposeCurrentCamera(); + await _initializeCamera(); + } + } + + Future _onAudioChange(bool recordAudio) async { + setState(() { + _recordAudio = recordAudio; + }); + if (_initialized && _cameraId >= 0) { + // Re-inits camera with new record audio setting. + await _disposeCurrentCamera(); + await _initializeCamera(); + } + } + + void _onCameraError(CameraErrorEvent event) { + if (mounted) { + _scaffoldMessengerKey.currentState?.showSnackBar( + SnackBar(content: Text('Error: ${event.description}'))); + + // Dispose camera on camera error as it can not be used anymore. + _disposeCurrentCamera(); + _fetchCameras(); + } + } + + void _onCameraClosing(CameraClosingEvent event) { + if (mounted) { + _showInSnackBar('Camera is closing'); + } + } + + void _showInSnackBar(String message) { + _scaffoldMessengerKey.currentState?.showSnackBar(SnackBar( + content: Text(message), + duration: const Duration(seconds: 1), + )); + } + + final GlobalKey _scaffoldMessengerKey = + GlobalKey(); + + @override + Widget build(BuildContext context) { + final List> resolutionItems = + ResolutionPreset.values + .map>((ResolutionPreset value) { + return DropdownMenuItem( + value: value, + child: Text(value.toString()), + ); + }).toList(); + + return MaterialApp( + scaffoldMessengerKey: _scaffoldMessengerKey, + home: Scaffold( + appBar: AppBar( + title: const Text('Plugin example app'), + ), + body: ListView( + children: [ + Padding( + padding: const EdgeInsets.symmetric( + vertical: 5, + horizontal: 10, + ), + child: Text(_cameraInfo), + ), + if (_cameras.isEmpty) + ElevatedButton( + onPressed: _fetchCameras, + child: const Text('Re-check available cameras'), + ), + if (_cameras.isNotEmpty) + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + DropdownButton( + value: _resolutionPreset, + onChanged: (ResolutionPreset? value) { + if (value != null) { + _onResolutionChange(value); + } + }, + items: resolutionItems, + ), + const SizedBox(width: 20), + const Text('Audio:'), + Switch( + value: _recordAudio, + onChanged: (bool state) => _onAudioChange(state)), + const SizedBox(width: 20), + ElevatedButton( + onPressed: _initialized + ? _disposeCurrentCamera + : _initializeCamera, + child: + Text(_initialized ? 'Dispose camera' : 'Create camera'), + ), + const SizedBox(width: 5), + ElevatedButton( + onPressed: _initialized ? _takePicture : null, + child: const Text('Take picture'), + ), + const SizedBox(width: 5), + ElevatedButton( + onPressed: _initialized ? _togglePreview : null, + child: Text( + _previewPaused ? 'Resume preview' : 'Pause preview', + ), + ), + const SizedBox(width: 5), + ElevatedButton( + onPressed: _initialized ? _toggleRecord : null, + child: Text( + (_recording || _recordingTimed) + ? 'Stop recording' + : 'Record Video', + ), + ), + const SizedBox(width: 5), + ElevatedButton( + onPressed: (_initialized && !_recording && !_recordingTimed) + ? () => _recordTimed(5) + : null, + child: const Text( + 'Record 5 seconds', + ), + ), + if (_cameras.length > 1) ...[ + const SizedBox(width: 5), + ElevatedButton( + onPressed: _switchCamera, + child: const Text( + 'Switch camera', + ), + ), + ] + ], + ), + const SizedBox(height: 5), + if (_initialized && _cameraId > 0 && _previewSize != null) + Padding( + padding: const EdgeInsets.symmetric( + vertical: 10, + ), + child: Align( + alignment: Alignment.center, + child: Container( + constraints: const BoxConstraints( + maxHeight: 500, + ), + child: AspectRatio( + aspectRatio: _previewSize!.width / _previewSize!.height, + child: _buildPreview(), + ), + ), + ), + ), + if (_previewSize != null) + Center( + child: Text( + 'Preview size: ${_previewSize!.width.toStringAsFixed(0)}x${_previewSize!.height.toStringAsFixed(0)}', + ), + ), + ], + ), + ), + ); + } +} diff --git a/packages/camera/camera_windows/example/pubspec.yaml b/packages/camera/camera_windows/example/pubspec.yaml new file mode 100644 index 000000000000..aa806a292333 --- /dev/null +++ b/packages/camera/camera_windows/example/pubspec.yaml @@ -0,0 +1,28 @@ +name: camera_windows_example +description: Demonstrates how to use the camera_windows plugin. +publish_to: 'none' + +environment: + sdk: ">=2.12.0 <3.0.0" + flutter: ">=2.8.0" + +dependencies: + camera_platform_interface: ^2.1.2 + camera_windows: + # When depending on this package from a real application you should use: + # camera_windows: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/camera/camera_windows/example/test_driver/integration_test.dart b/packages/camera/camera_windows/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4f10f2a522f3 --- /dev/null +++ b/packages/camera/camera_windows/example/test_driver/integration_test.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); diff --git a/packages/camera/camera_windows/example/windows/.gitignore b/packages/camera/camera_windows/example/windows/.gitignore new file mode 100644 index 000000000000..d492d0d98c8f --- /dev/null +++ b/packages/camera/camera_windows/example/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/packages/camera/camera_windows/example/windows/CMakeLists.txt b/packages/camera/camera_windows/example/windows/CMakeLists.txt new file mode 100644 index 000000000000..28757c79ca2f --- /dev/null +++ b/packages/camera/camera_windows/example/windows/CMakeLists.txt @@ -0,0 +1,100 @@ +cmake_minimum_required(VERSION 3.14) +project(camera_windows_example LANGUAGES CXX) + +set(BINARY_NAME "camera_windows_example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() + +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build +add_subdirectory("runner") + +# Enable the test target. +set(include_camera_windows_tests TRUE) +# Provide an alias for the test target using the name expected by repo tooling. +add_custom_target(unit_tests DEPENDS camera_windows_test) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/packages/camera/camera_windows/example/windows/flutter/CMakeLists.txt b/packages/camera/camera_windows/example/windows/flutter/CMakeLists.txt new file mode 100644 index 000000000000..b2e4bd8d658b --- /dev/null +++ b/packages/camera/camera_windows/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,103 @@ +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/packages/camera/camera_windows/example/windows/flutter/generated_plugins.cmake b/packages/camera/camera_windows/example/windows/flutter/generated_plugins.cmake new file mode 100644 index 000000000000..458d22dac410 --- /dev/null +++ b/packages/camera/camera_windows/example/windows/flutter/generated_plugins.cmake @@ -0,0 +1,24 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + camera_windows +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/packages/camera/camera_windows/example/windows/runner/CMakeLists.txt b/packages/camera/camera_windows/example/windows/runner/CMakeLists.txt new file mode 100644 index 000000000000..adb2052b6050 --- /dev/null +++ b/packages/camera/camera_windows/example/windows/runner/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +apply_standard_settings(${BINARY_NAME}) +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/packages/camera/camera_windows/example/windows/runner/Runner.rc b/packages/camera/camera_windows/example/windows/runner/Runner.rc new file mode 100644 index 000000000000..f1cfa4391ebd --- /dev/null +++ b/packages/camera/camera_windows/example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#ifdef FLUTTER_BUILD_NUMBER +#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#else +#define VERSION_AS_NUMBER 1,0,0 +#endif + +#ifdef FLUTTER_BUILD_NAME +#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "Demonstrates how to use the camera_windows plugin." "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "camera_windows_example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2021 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "camera_windows_example.exe" "\0" + VALUE "ProductName", "camera_windows_example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/packages/camera/camera_windows/example/windows/runner/flutter_window.cpp b/packages/camera/camera_windows/example/windows/runner/flutter_window.cpp new file mode 100644 index 000000000000..8254bd9ff3c1 --- /dev/null +++ b/packages/camera/camera_windows/example/windows/runner/flutter_window.cpp @@ -0,0 +1,65 @@ +// 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. + +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/packages/camera/camera_windows/example/windows/runner/flutter_window.h b/packages/camera/camera_windows/example/windows/runner/flutter_window.h new file mode 100644 index 000000000000..f1fc669093d0 --- /dev/null +++ b/packages/camera/camera_windows/example/windows/runner/flutter_window.h @@ -0,0 +1,37 @@ +// 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. + +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/packages/camera/camera_windows/example/windows/runner/main.cpp b/packages/camera/camera_windows/example/windows/runner/main.cpp new file mode 100644 index 000000000000..755a90b42f19 --- /dev/null +++ b/packages/camera/camera_windows/example/windows/runner/main.cpp @@ -0,0 +1,46 @@ +// 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. + +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t* command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"camera_windows_example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/packages/camera/camera_windows/example/windows/runner/resource.h b/packages/camera/camera_windows/example/windows/runner/resource.h new file mode 100644 index 000000000000..d5d958dc4257 --- /dev/null +++ b/packages/camera/camera_windows/example/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/packages/camera/camera_windows/example/windows/runner/resources/app_icon.ico b/packages/camera/camera_windows/example/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/packages/camera/camera_windows/example/windows/runner/runner.exe.manifest b/packages/camera/camera_windows/example/windows/runner/runner.exe.manifest new file mode 100644 index 000000000000..c977c4a42589 --- /dev/null +++ b/packages/camera/camera_windows/example/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/packages/camera/camera_windows/example/windows/runner/utils.cpp b/packages/camera/camera_windows/example/windows/runner/utils.cpp new file mode 100644 index 000000000000..fb7e945a63b7 --- /dev/null +++ b/packages/camera/camera_windows/example/windows/runner/utils.cpp @@ -0,0 +1,67 @@ +// 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. + +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE* unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = + ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, + nullptr, 0, nullptr, nullptr); + if (target_length == 0) { + return std::string(); + } + std::string utf8_string; + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/packages/camera/camera_windows/example/windows/runner/utils.h b/packages/camera/camera_windows/example/windows/runner/utils.h new file mode 100644 index 000000000000..bd81e1e02338 --- /dev/null +++ b/packages/camera/camera_windows/example/windows/runner/utils.h @@ -0,0 +1,23 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/packages/camera/camera_windows/example/windows/runner/win32_window.cpp b/packages/camera/camera_windows/example/windows/runner/win32_window.cpp new file mode 100644 index 000000000000..85aa3614e8ad --- /dev/null +++ b/packages/camera/camera_windows/example/windows/runner/win32_window.cpp @@ -0,0 +1,241 @@ +// 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. + +#include "win32_window.h" + +#include + +#include "resource.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + FreeLibrary(user32_module); + } +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { ++g_active_window_count; } + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::CreateAndShow(const std::wstring& title, const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { return window_handle_; } + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} diff --git a/packages/camera/camera_windows/example/windows/runner/win32_window.h b/packages/camera/camera_windows/example/windows/runner/win32_window.h new file mode 100644 index 000000000000..d2a730052223 --- /dev/null +++ b/packages/camera/camera_windows/example/windows/runner/win32_window.h @@ -0,0 +1,99 @@ +// 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. + +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates and shows a win32 window with |title| and position and size using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring& title, const Point& origin, + const Size& size); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/packages/camera/camera_windows/lib/camera_windows.dart b/packages/camera/camera_windows/lib/camera_windows.dart new file mode 100644 index 000000000000..33f8bfb68fac --- /dev/null +++ b/packages/camera/camera_windows/lib/camera_windows.dart @@ -0,0 +1,433 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:math'; + +import 'package:camera_platform_interface/camera_platform_interface.dart'; +import 'package:cross_file/cross_file.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:stream_transform/stream_transform.dart'; + +/// An implementation of [CameraPlatform] for Windows. +class CameraWindows extends CameraPlatform { + /// Registers the Windows implementation of CameraPlatform. + static void registerWith() { + CameraPlatform.instance = CameraWindows(); + } + + /// The method channel used to interact with the native platform. + @visibleForTesting + final MethodChannel pluginChannel = + const MethodChannel('plugins.flutter.io/camera_windows'); + + /// Camera specific method channels to allow comminicating with specific cameras. + final Map _cameraChannels = {}; + + /// The controller that broadcasts events coming from handleCameraMethodCall + /// + /// It is a `broadcast` because multiple controllers will connect to + /// different stream views of this Controller. + /// This is only exposed for test purposes. It shouldn't be used by clients of + /// the plugin as it may break or change at any time. + @visibleForTesting + final StreamController cameraEventStreamController = + StreamController.broadcast(); + + /// Returns a stream of camera events for the given [cameraId]. + Stream _cameraEvents(int cameraId) => + cameraEventStreamController.stream + .where((CameraEvent event) => event.cameraId == cameraId); + + @override + Future> availableCameras() async { + try { + final List>? cameras = await pluginChannel + .invokeListMethod>('availableCameras'); + + if (cameras == null) { + return []; + } + + return cameras.map((Map camera) { + return CameraDescription( + name: camera['name'] as String, + lensDirection: + parseCameraLensDirection(camera['lensFacing'] as String), + sensorOrientation: camera['sensorOrientation'] as int, + ); + }).toList(); + } on PlatformException catch (e) { + throw CameraException(e.code, e.message); + } + } + + @override + Future createCamera( + CameraDescription cameraDescription, + ResolutionPreset? resolutionPreset, { + bool enableAudio = false, + }) async { + try { + // If resolutionPreset is not specified, plugin selects the highest resolution possible. + final Map? reply = await pluginChannel + .invokeMapMethod('create', { + 'cameraName': cameraDescription.name, + 'resolutionPreset': _serializeResolutionPreset(resolutionPreset), + 'enableAudio': enableAudio, + }); + + if (reply == null) { + throw CameraException('System', 'Cannot create camera'); + } + + return reply['cameraId']! as int; + } on PlatformException catch (e) { + throw CameraException(e.code, e.message); + } + } + + @override + Future initializeCamera( + int cameraId, { + ImageFormatGroup imageFormatGroup = ImageFormatGroup.unknown, + }) async { + final int requestedCameraId = cameraId; + + /// Creates channel for camera events. + _cameraChannels.putIfAbsent(requestedCameraId, () { + final MethodChannel channel = MethodChannel( + 'plugins.flutter.io/camera_windows/camera$requestedCameraId'); + channel.setMethodCallHandler( + (MethodCall call) => handleCameraMethodCall(call, requestedCameraId), + ); + return channel; + }); + + final Map? reply; + try { + reply = await pluginChannel.invokeMapMethod( + 'initialize', + { + 'cameraId': requestedCameraId, + }, + ); + } on PlatformException catch (e) { + throw CameraException(e.code, e.message); + } + + cameraEventStreamController.add( + CameraInitializedEvent( + requestedCameraId, + reply!['previewWidth']!, + reply['previewHeight']!, + ExposureMode.auto, + false, + FocusMode.auto, + false, + ), + ); + } + + @override + Future dispose(int cameraId) async { + await pluginChannel.invokeMethod( + 'dispose', + {'cameraId': cameraId}, + ); + + // Destroy method channel after camera is disposed to be able to handle last messages. + if (_cameraChannels.containsKey(cameraId)) { + final MethodChannel? cameraChannel = _cameraChannels[cameraId]; + cameraChannel?.setMethodCallHandler(null); + _cameraChannels.remove(cameraId); + } + } + + @override + Stream onCameraInitialized(int cameraId) { + return _cameraEvents(cameraId).whereType(); + } + + @override + Stream onCameraResolutionChanged(int cameraId) { + /// Windows API does not automatically change the camera's resolution + /// during capture so these events are never send from the platform. + /// Support for changing resolution should be implemented, if support for + /// requesting resolution change is added to camera platform interface. + return const Stream.empty(); + } + + @override + Stream onCameraClosing(int cameraId) { + return _cameraEvents(cameraId).whereType(); + } + + @override + Stream onCameraError(int cameraId) { + return _cameraEvents(cameraId).whereType(); + } + + @override + Stream onVideoRecordedEvent(int cameraId) { + return _cameraEvents(cameraId).whereType(); + } + + @override + Stream onDeviceOrientationChanged() { + // TODO(jokerttu): Implement device orientation detection, https://github.com/flutter/flutter/issues/97540. + // Force device orientation to landscape as by default camera plugin uses portraitUp orientation. + return Stream.value( + const DeviceOrientationChangedEvent(DeviceOrientation.landscapeRight), + ); + } + + @override + Future lockCaptureOrientation( + int cameraId, + DeviceOrientation orientation, + ) async { + // TODO(jokerttu): Implement lock capture orientation feature, https://github.com/flutter/flutter/issues/97540. + throw UnimplementedError('lockCaptureOrientation() is not implemented.'); + } + + @override + Future unlockCaptureOrientation(int cameraId) async { + // TODO(jokerttu): Implement unlock capture orientation feature, https://github.com/flutter/flutter/issues/97540. + throw UnimplementedError('unlockCaptureOrientation() is not implemented.'); + } + + @override + Future takePicture(int cameraId) async { + final String? path; + path = await pluginChannel.invokeMethod( + 'takePicture', + {'cameraId': cameraId}, + ); + + return XFile(path!); + } + + @override + Future prepareForVideoRecording() => + pluginChannel.invokeMethod('prepareForVideoRecording'); + + @override + Future startVideoRecording( + int cameraId, { + Duration? maxVideoDuration, + }) async { + await pluginChannel.invokeMethod( + 'startVideoRecording', + { + 'cameraId': cameraId, + 'maxVideoDuration': maxVideoDuration?.inMilliseconds, + }, + ); + } + + @override + Future stopVideoRecording(int cameraId) async { + final String? path; + + path = await pluginChannel.invokeMethod( + 'stopVideoRecording', + {'cameraId': cameraId}, + ); + + return XFile(path!); + } + + @override + Future pauseVideoRecording(int cameraId) async { + throw UnsupportedError( + 'pauseVideoRecording() is not supported due to Win32 API limitations.'); + } + + @override + Future resumeVideoRecording(int cameraId) async { + throw UnsupportedError( + 'resumeVideoRecording() is not supported due to Win32 API limitations.'); + } + + @override + Future setFlashMode(int cameraId, FlashMode mode) async { + // TODO(jokerttu): Implement flash mode support, https://github.com/flutter/flutter/issues/97537. + throw UnimplementedError('setFlashMode() is not implemented.'); + } + + @override + Future setExposureMode(int cameraId, ExposureMode mode) async { + // TODO(jokerttu): Implement explosure mode support, https://github.com/flutter/flutter/issues/97537. + throw UnimplementedError('setExposureMode() is not implemented.'); + } + + @override + Future setExposurePoint(int cameraId, Point? point) async { + assert(point == null || point.x >= 0 && point.x <= 1); + assert(point == null || point.y >= 0 && point.y <= 1); + + throw UnsupportedError( + 'setExposurePoint() is not supported due to Win32 API limitations.'); + } + + @override + Future getMinExposureOffset(int cameraId) async { + // TODO(jokerttu): Implement exposure control support, https://github.com/flutter/flutter/issues/97537. + // Value is returned to support existing implementations. + return 0.0; + } + + @override + Future getMaxExposureOffset(int cameraId) async { + // TODO(jokerttu): Implement exposure control support, https://github.com/flutter/flutter/issues/97537. + // Value is returned to support existing implementations. + return 0.0; + } + + @override + Future getExposureOffsetStepSize(int cameraId) async { + // TODO(jokerttu): Implement exposure control support, https://github.com/flutter/flutter/issues/97537. + // Value is returned to support existing implementations. + return 1.0; + } + + @override + Future setExposureOffset(int cameraId, double offset) async { + // TODO(jokerttu): Implement exposure control support, https://github.com/flutter/flutter/issues/97537. + throw UnimplementedError('setExposureOffset() is not implemented.'); + } + + @override + Future setFocusMode(int cameraId, FocusMode mode) async { + // TODO(jokerttu): Implement focus mode support, https://github.com/flutter/flutter/issues/97537. + throw UnimplementedError('setFocusMode() is not implemented.'); + } + + @override + Future setFocusPoint(int cameraId, Point? point) async { + assert(point == null || point.x >= 0 && point.x <= 1); + assert(point == null || point.y >= 0 && point.y <= 1); + + throw UnsupportedError( + 'setFocusPoint() is not supported due to Win32 API limitations.'); + } + + @override + Future getMinZoomLevel(int cameraId) async { + // TODO(jokerttu): Implement zoom level support, https://github.com/flutter/flutter/issues/97537. + // Value is returned to support existing implementations. + return 1.0; + } + + @override + Future getMaxZoomLevel(int cameraId) async { + // TODO(jokerttu): Implement zoom level support, https://github.com/flutter/flutter/issues/97537. + // Value is returned to support existing implementations. + return 1.0; + } + + @override + Future setZoomLevel(int cameraId, double zoom) async { + // TODO(jokerttu): Implement zoom level support, https://github.com/flutter/flutter/issues/97537. + throw UnimplementedError('setZoomLevel() is not implemented.'); + } + + @override + Future pausePreview(int cameraId) async { + await pluginChannel.invokeMethod( + 'pausePreview', + {'cameraId': cameraId}, + ); + } + + @override + Future resumePreview(int cameraId) async { + await pluginChannel.invokeMethod( + 'resumePreview', + {'cameraId': cameraId}, + ); + } + + @override + Widget buildPreview(int cameraId) { + return Texture(textureId: cameraId); + } + + /// Returns the resolution preset as a nullable String. + String? _serializeResolutionPreset(ResolutionPreset? resolutionPreset) { + switch (resolutionPreset) { + case null: + return null; + case ResolutionPreset.max: + return 'max'; + case ResolutionPreset.ultraHigh: + return 'ultraHigh'; + case ResolutionPreset.veryHigh: + return 'veryHigh'; + case ResolutionPreset.high: + return 'high'; + case ResolutionPreset.medium: + return 'medium'; + case ResolutionPreset.low: + return 'low'; + } + } + + /// Converts messages received from the native platform into camera events. + /// + /// This is only exposed for test purposes. It shouldn't be used by clients + /// of the plugin as it may break or change at any time. + @visibleForTesting + Future handleCameraMethodCall(MethodCall call, int cameraId) async { + switch (call.method) { + case 'camera_closing': + cameraEventStreamController.add( + CameraClosingEvent( + cameraId, + ), + ); + break; + case 'video_recorded': + // This is called if maxVideoDuration was given on record start. + cameraEventStreamController.add( + VideoRecordedEvent( + cameraId, + XFile(call.arguments['path'] as String), + call.arguments['maxVideoDuration'] != null + ? Duration( + milliseconds: call.arguments['maxVideoDuration'] as int, + ) + : null, + ), + ); + break; + case 'error': + cameraEventStreamController.add( + CameraErrorEvent( + cameraId, + call.arguments['description'] as String, + ), + ); + break; + default: + throw UnimplementedError(); + } + } + + /// Parses string presentation of the camera lens direction and returns enum value. + @visibleForTesting + CameraLensDirection parseCameraLensDirection(String string) { + switch (string) { + case 'front': + return CameraLensDirection.front; + case 'back': + return CameraLensDirection.back; + case 'external': + return CameraLensDirection.external; + } + throw ArgumentError('Unknown CameraLensDirection value'); + } +} diff --git a/packages/camera/camera_windows/pubspec.yaml b/packages/camera/camera_windows/pubspec.yaml new file mode 100644 index 000000000000..1081c3dfc01f --- /dev/null +++ b/packages/camera/camera_windows/pubspec.yaml @@ -0,0 +1,29 @@ +name: camera_windows +description: A Flutter plugin for getting information about and controlling the camera on Windows. +version: 0.1.0 +repository: https://github.com/flutter/plugins/tree/master/packages/camera/camera_windows +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 + +environment: + sdk: ">=2.12.0 <3.0.0" + flutter: ">=2.8.0" + +flutter: + plugin: + implements: camera + platforms: + windows: + pluginClass: CameraWindows + dartPluginClass: CameraWindows + +dependencies: + camera_platform_interface: ^2.1.2 + cross_file: ^0.3.1 + flutter: + sdk: flutter + stream_transform: ^2.0.0 + +dev_dependencies: + async: ^2.5.0 + flutter_test: + sdk: flutter diff --git a/packages/camera/camera_windows/test/camera_windows_test.dart b/packages/camera/camera_windows/test/camera_windows_test.dart new file mode 100644 index 000000000000..c1a0fe40325f --- /dev/null +++ b/packages/camera/camera_windows/test/camera_windows_test.dart @@ -0,0 +1,664 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:async/async.dart'; +import 'package:camera_platform_interface/camera_platform_interface.dart'; +import 'package:camera_windows/camera_windows.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import './utils/method_channel_mock.dart'; + +void main() { + const String pluginChannelName = 'plugins.flutter.io/camera_windows'; + TestWidgetsFlutterBinding.ensureInitialized(); + + group('$CameraWindows()', () { + test('registered instance', () { + CameraWindows.registerWith(); + expect(CameraPlatform.instance, isA()); + }); + + group('Creation, Initialization & Disposal Tests', () { + test('Should send creation data and receive back a camera id', () async { + // Arrange + final MethodChannelMock cameraMockChannel = MethodChannelMock( + channelName: pluginChannelName, + methods: { + 'create': { + 'cameraId': 1, + 'imageFormatGroup': 'unknown', + } + }); + final CameraWindows plugin = CameraWindows(); + + // Act + final int cameraId = await plugin.createCamera( + const CameraDescription( + name: 'Test', + lensDirection: CameraLensDirection.front, + sensorOrientation: 0), + ResolutionPreset.high, + ); + + // Assert + expect(cameraMockChannel.log, [ + isMethodCall( + 'create', + arguments: { + 'cameraName': 'Test', + 'resolutionPreset': 'high', + 'enableAudio': false + }, + ), + ]); + expect(cameraId, 1); + }); + + test( + 'Should throw CameraException when create throws a PlatformException', + () { + // Arrange + MethodChannelMock( + channelName: pluginChannelName, + methods: { + 'create': PlatformException( + code: 'TESTING_ERROR_CODE', + message: 'Mock error message used during testing.', + ) + }); + final CameraWindows plugin = CameraWindows(); + + // Act + expect( + () => plugin.createCamera( + const CameraDescription( + name: 'Test', + lensDirection: CameraLensDirection.back, + sensorOrientation: 0, + ), + ResolutionPreset.high, + ), + throwsA( + isA() + .having( + (CameraException e) => e.code, 'code', 'TESTING_ERROR_CODE') + .having((CameraException e) => e.description, 'description', + 'Mock error message used during testing.'), + ), + ); + }); + + test( + 'Should throw CameraException when initialize throws a PlatformException', + () { + // Arrange + MethodChannelMock( + channelName: pluginChannelName, + methods: { + 'initialize': PlatformException( + code: 'TESTING_ERROR_CODE', + message: 'Mock error message used during testing.', + ) + }, + ); + final CameraWindows plugin = CameraWindows(); + + // Act + expect( + () => plugin.initializeCamera(0), + throwsA( + isA() + .having((CameraException e) => e.code, 'code', + 'TESTING_ERROR_CODE') + .having( + (CameraException e) => e.description, + 'description', + 'Mock error message used during testing.', + ), + ), + ); + }, + ); + + test('Should send initialization data', () async { + // Arrange + final MethodChannelMock cameraMockChannel = MethodChannelMock( + channelName: pluginChannelName, + methods: { + 'create': { + 'cameraId': 1, + 'imageFormatGroup': 'unknown', + }, + 'initialize': { + 'previewWidth': 1920.toDouble(), + 'previewHeight': 1080.toDouble() + }, + }); + final CameraWindows plugin = CameraWindows(); + final int cameraId = await plugin.createCamera( + const CameraDescription( + name: 'Test', + lensDirection: CameraLensDirection.back, + sensorOrientation: 0, + ), + ResolutionPreset.high, + ); + + // Act + await plugin.initializeCamera(cameraId); + + // Assert + expect(cameraId, 1); + expect(cameraMockChannel.log, [ + anything, + isMethodCall( + 'initialize', + arguments: {'cameraId': 1}, + ), + ]); + }); + + test('Should send a disposal call on dispose', () async { + // Arrange + final MethodChannelMock cameraMockChannel = MethodChannelMock( + channelName: pluginChannelName, + methods: { + 'create': {'cameraId': 1}, + 'initialize': { + 'previewWidth': 1920.toDouble(), + 'previewHeight': 1080.toDouble() + }, + 'dispose': {'cameraId': 1} + }); + + final CameraWindows plugin = CameraWindows(); + final int cameraId = await plugin.createCamera( + const CameraDescription( + name: 'Test', + lensDirection: CameraLensDirection.back, + sensorOrientation: 0, + ), + ResolutionPreset.high, + ); + await plugin.initializeCamera(cameraId); + + // Act + await plugin.dispose(cameraId); + + // Assert + expect(cameraId, 1); + expect(cameraMockChannel.log, [ + anything, + anything, + isMethodCall( + 'dispose', + arguments: {'cameraId': 1}, + ), + ]); + }); + }); + + group('Event Tests', () { + late CameraWindows plugin; + late int cameraId; + setUp(() async { + MethodChannelMock( + channelName: pluginChannelName, + methods: { + 'create': {'cameraId': 1}, + 'initialize': { + 'previewWidth': 1920.toDouble(), + 'previewHeight': 1080.toDouble() + }, + }, + ); + + plugin = CameraWindows(); + cameraId = await plugin.createCamera( + const CameraDescription( + name: 'Test', + lensDirection: CameraLensDirection.back, + sensorOrientation: 0, + ), + ResolutionPreset.high, + ); + await plugin.initializeCamera(cameraId); + }); + + test('Should receive camera closing events', () async { + // Act + final Stream eventStream = + plugin.onCameraClosing(cameraId); + final StreamQueue streamQueue = + StreamQueue(eventStream); + + // Emit test events + final CameraClosingEvent event = CameraClosingEvent(cameraId); + await plugin.handleCameraMethodCall( + MethodCall('camera_closing', event.toJson()), cameraId); + await plugin.handleCameraMethodCall( + MethodCall('camera_closing', event.toJson()), cameraId); + await plugin.handleCameraMethodCall( + MethodCall('camera_closing', event.toJson()), cameraId); + + // Assert + expect(await streamQueue.next, event); + expect(await streamQueue.next, event); + expect(await streamQueue.next, event); + + // Clean up + await streamQueue.cancel(); + }); + + test('Should receive camera error events', () async { + // Act + final Stream errorStream = + plugin.onCameraError(cameraId); + final StreamQueue streamQueue = + StreamQueue(errorStream); + + // Emit test events + final CameraErrorEvent event = + CameraErrorEvent(cameraId, 'Error Description'); + await plugin.handleCameraMethodCall( + MethodCall('error', event.toJson()), cameraId); + await plugin.handleCameraMethodCall( + MethodCall('error', event.toJson()), cameraId); + await plugin.handleCameraMethodCall( + MethodCall('error', event.toJson()), cameraId); + + // Assert + expect(await streamQueue.next, event); + expect(await streamQueue.next, event); + expect(await streamQueue.next, event); + + // Clean up + await streamQueue.cancel(); + }); + }); + + group('Function Tests', () { + late CameraWindows plugin; + late int cameraId; + + setUp(() async { + MethodChannelMock( + channelName: pluginChannelName, + methods: { + 'create': {'cameraId': 1}, + 'initialize': { + 'previewWidth': 1920.toDouble(), + 'previewHeight': 1080.toDouble() + }, + }, + ); + plugin = CameraWindows(); + cameraId = await plugin.createCamera( + const CameraDescription( + name: 'Test', + lensDirection: CameraLensDirection.back, + sensorOrientation: 0, + ), + ResolutionPreset.high, + ); + await plugin.initializeCamera(cameraId); + }); + + test('Should fetch CameraDescription instances for available cameras', + () async { + // Arrange + final List returnData = [ + { + 'name': 'Test 1', + 'lensFacing': 'front', + 'sensorOrientation': 1 + }, + { + 'name': 'Test 2', + 'lensFacing': 'back', + 'sensorOrientation': 2 + } + ]; + final MethodChannelMock channel = MethodChannelMock( + channelName: pluginChannelName, + methods: {'availableCameras': returnData}, + ); + + // Act + final List cameras = await plugin.availableCameras(); + + // Assert + expect(channel.log, [ + isMethodCall('availableCameras', arguments: null), + ]); + expect(cameras.length, returnData.length); + for (int i = 0; i < returnData.length; i++) { + final CameraDescription cameraDescription = CameraDescription( + name: returnData[i]['name']! as String, + lensDirection: plugin.parseCameraLensDirection( + returnData[i]['lensFacing']! as String), + sensorOrientation: returnData[i]['sensorOrientation']! as int, + ); + expect(cameras[i], cameraDescription); + } + }); + + test( + 'Should throw CameraException when availableCameras throws a PlatformException', + () { + // Arrange + MethodChannelMock( + channelName: pluginChannelName, + methods: { + 'availableCameras': PlatformException( + code: 'TESTING_ERROR_CODE', + message: 'Mock error message used during testing.', + ) + }); + + // Act + expect( + plugin.availableCameras, + throwsA( + isA() + .having( + (CameraException e) => e.code, 'code', 'TESTING_ERROR_CODE') + .having((CameraException e) => e.description, 'description', + 'Mock error message used during testing.'), + ), + ); + }); + + test('Should take a picture and return an XFile instance', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: pluginChannelName, + methods: {'takePicture': '/test/path.jpg'}); + + // Act + final XFile file = await plugin.takePicture(cameraId); + + // Assert + expect(channel.log, [ + isMethodCall('takePicture', arguments: { + 'cameraId': cameraId, + }), + ]); + expect(file.path, '/test/path.jpg'); + }); + + test('Should prepare for video recording', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: pluginChannelName, + methods: {'prepareForVideoRecording': null}, + ); + + // Act + await plugin.prepareForVideoRecording(); + + // Assert + expect(channel.log, [ + isMethodCall('prepareForVideoRecording', arguments: null), + ]); + }); + + test('Should start recording a video', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: pluginChannelName, + methods: {'startVideoRecording': null}, + ); + + // Act + await plugin.startVideoRecording(cameraId); + + // Assert + expect(channel.log, [ + isMethodCall('startVideoRecording', arguments: { + 'cameraId': cameraId, + 'maxVideoDuration': null, + }), + ]); + }); + + test('Should pass maxVideoDuration when starting recording a video', + () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: pluginChannelName, + methods: {'startVideoRecording': null}, + ); + + // Act + await plugin.startVideoRecording( + cameraId, + maxVideoDuration: const Duration(seconds: 10), + ); + + // Assert + expect(channel.log, [ + isMethodCall('startVideoRecording', arguments: { + 'cameraId': cameraId, + 'maxVideoDuration': 10000 + }), + ]); + }); + + test('Should stop a video recording and return the file', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: pluginChannelName, + methods: {'stopVideoRecording': '/test/path.mp4'}, + ); + + // Act + final XFile file = await plugin.stopVideoRecording(cameraId); + + // Assert + expect(channel.log, [ + isMethodCall('stopVideoRecording', arguments: { + 'cameraId': cameraId, + }), + ]); + expect(file.path, '/test/path.mp4'); + }); + + test('Should throw UnsupportedError when pause video recording is called', + () async { + // Act + expect( + () => plugin.pauseVideoRecording(cameraId), + throwsA(isA()), + ); + }); + + test( + 'Should throw UnsupportedError when resume video recording is called', + () async { + // Act + expect( + () => plugin.resumeVideoRecording(cameraId), + throwsA(isA()), + ); + }); + + test('Should throw UnimplementedError when flash mode is set', () async { + // Act + expect( + () => plugin.setFlashMode(cameraId, FlashMode.torch), + throwsA(isA()), + ); + }); + + test('Should throw UnimplementedError when exposure mode is set', + () async { + // Act + expect( + () => plugin.setExposureMode(cameraId, ExposureMode.auto), + throwsA(isA()), + ); + }); + + test('Should throw UnsupportedError when exposure point is set', + () async { + // Act + expect( + () => plugin.setExposurePoint(cameraId, null), + throwsA(isA()), + ); + }); + + test('Should get the min exposure offset', () async { + // Act + final double minExposureOffset = + await plugin.getMinExposureOffset(cameraId); + + // Assert + expect(minExposureOffset, 0.0); + }); + + test('Should get the max exposure offset', () async { + // Act + final double maxExposureOffset = + await plugin.getMaxExposureOffset(cameraId); + + // Assert + expect(maxExposureOffset, 0.0); + }); + + test('Should get the exposure offset step size', () async { + // Act + final double stepSize = + await plugin.getExposureOffsetStepSize(cameraId); + + // Assert + expect(stepSize, 1.0); + }); + + test('Should throw UnimplementedError when exposure offset is set', + () async { + // Act + expect( + () => plugin.setExposureOffset(cameraId, 0.5), + throwsA(isA()), + ); + }); + + test('Should throw UnimplementedError when focus mode is set', () async { + // Act + expect( + () => plugin.setFocusMode(cameraId, FocusMode.auto), + throwsA(isA()), + ); + }); + + test('Should throw UnsupportedError when exposure point is set', + () async { + // Act + expect( + () => plugin.setFocusMode(cameraId, FocusMode.auto), + throwsA(isA()), + ); + }); + + test('Should build a texture widget as preview widget', () async { + // Act + final Widget widget = plugin.buildPreview(cameraId); + + // Act + expect(widget is Texture, isTrue); + expect((widget as Texture).textureId, cameraId); + }); + + test('Should throw UnimplementedError when handling unknown method', () { + final CameraWindows plugin = CameraWindows(); + + expect( + () => plugin.handleCameraMethodCall( + const MethodCall('unknown_method'), 1), + throwsA(isA())); + }); + + test('Should get the max zoom level', () async { + // Act + final double maxZoomLevel = await plugin.getMaxZoomLevel(cameraId); + + // Assert + expect(maxZoomLevel, 1.0); + }); + + test('Should get the min zoom level', () async { + // Act + final double maxZoomLevel = await plugin.getMinZoomLevel(cameraId); + + // Assert + expect(maxZoomLevel, 1.0); + }); + + test('Should throw UnimplementedError when zoom level is set', () async { + // Act + expect( + () => plugin.setZoomLevel(cameraId, 2.0), + throwsA(isA()), + ); + }); + + test( + 'Should throw UnimplementedError when lock capture orientation is called', + () async { + // Act + expect( + () => plugin.setZoomLevel(cameraId, 2.0), + throwsA(isA()), + ); + }); + + test( + 'Should throw UnimplementedError when unlock capture orientation is called', + () async { + // Act + expect( + () => plugin.unlockCaptureOrientation(cameraId), + throwsA(isA()), + ); + }); + + test('Should pause the camera preview', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: pluginChannelName, + methods: {'pausePreview': null}, + ); + + // Act + await plugin.pausePreview(cameraId); + + // Assert + expect(channel.log, [ + isMethodCall('pausePreview', + arguments: {'cameraId': cameraId}), + ]); + }); + + test('Should resume the camera preview', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: pluginChannelName, + methods: {'resumePreview': null}, + ); + + // Act + await plugin.resumePreview(cameraId); + + // Assert + expect(channel.log, [ + isMethodCall('resumePreview', + arguments: {'cameraId': cameraId}), + ]); + }); + }); + }); +} diff --git a/packages/camera/camera_windows/test/utils/method_channel_mock.dart b/packages/camera/camera_windows/test/utils/method_channel_mock.dart new file mode 100644 index 000000000000..22f7ecead589 --- /dev/null +++ b/packages/camera/camera_windows/test/utils/method_channel_mock.dart @@ -0,0 +1,45 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +/// A mock [MethodChannel] implementation for use in tests. +class MethodChannelMock { + /// Creates a new instance with the specified channel name. + /// + /// This method channel will handle all method invocations specified by + /// returning the value mapped to the method name key. If a delay is + /// specified, results are returned after the delay has elapsed. + MethodChannelMock({ + required String channelName, + this.delay, + required this.methods, + }) : methodChannel = MethodChannel(channelName) { + methodChannel.setMockMethodCallHandler(_handler); + } + + final Duration? delay; + final MethodChannel methodChannel; + final Map methods; + final List log = []; + + Future _handler(MethodCall methodCall) async { + log.add(methodCall); + + if (!methods.containsKey(methodCall.method)) { + throw MissingPluginException('No TEST implementation found for method ' + '${methodCall.method} on channel ${methodChannel.name}'); + } + + return Future.delayed(delay ?? Duration.zero, () { + final dynamic result = methods[methodCall.method]; + if (result is Exception) { + throw result; + } + + return Future.value(result); + }); + } +} diff --git a/packages/camera/camera_windows/windows/.gitignore b/packages/camera/camera_windows/windows/.gitignore new file mode 100644 index 000000000000..b3eb2be169a5 --- /dev/null +++ b/packages/camera/camera_windows/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/packages/camera/camera_windows/windows/CMakeLists.txt b/packages/camera/camera_windows/windows/CMakeLists.txt new file mode 100644 index 000000000000..caeb1095f5a5 --- /dev/null +++ b/packages/camera/camera_windows/windows/CMakeLists.txt @@ -0,0 +1,99 @@ +cmake_minimum_required(VERSION 3.14) +set(PROJECT_NAME "camera_windows") +project(${PROJECT_NAME} LANGUAGES CXX) + +# This value is used when generating builds using this plugin, so it must +# not be changed +set(PLUGIN_NAME "${PROJECT_NAME}_plugin") + +list(APPEND PLUGIN_SOURCES + "camera_plugin.h" + "camera_plugin.cpp" + "camera.h" + "camera.cpp" + "capture_controller.h" + "capture_controller.cpp" + "capture_controller_listener.h" + "capture_engine_listener.h" + "capture_engine_listener.cpp" + "string_utils.h" + "string_utils.cpp" + "capture_device_info.h" + "capture_device_info.cpp" + "preview_handler.h" + "preview_handler.cpp" + "record_handler.h" + "record_handler.cpp" + "photo_handler.h" + "photo_handler.cpp" + "texture_handler.h" + "texture_handler.cpp" + "com_heap_ptr.h" +) + +add_library(${PLUGIN_NAME} SHARED + "camera_windows.cpp" + "include/camera_windows/camera_windows.h" + ${PLUGIN_SOURCES} +) + +apply_standard_settings(${PLUGIN_NAME}) +set_target_properties(${PLUGIN_NAME} PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) +target_include_directories(${PLUGIN_NAME} INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin) +target_link_libraries(${PLUGIN_NAME} PRIVATE mf mfplat mfuuid d3d11) + +# List of absolute paths to libraries that should be bundled with the plugin +set(camera_windows_bundled_libraries + "" + PARENT_SCOPE +) + + +# === Tests === + +if (${include_${PROJECT_NAME}_tests}) +set(TEST_RUNNER "${PROJECT_NAME}_test") +enable_testing() +# TODO(stuartmorgan): Consider using a single shared, pre-checked-in googletest +# instance rather than downloading for each plugin. This approach makes sense +# for a template, but not for a monorepo with many plugins. +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/release-1.11.0.zip +) +# Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +# Disable install commands for gtest so it doesn't end up in the bundle. +set(INSTALL_GTEST OFF CACHE BOOL "Disable installation of googletest" FORCE) + +FetchContent_MakeAvailable(googletest) + +# The plugin's C API is not very useful for unit testing, so build the sources +# directly into the test binary rather than using the DLL. +add_executable(${TEST_RUNNER} + test/mocks.h + test/camera_plugin_test.cpp + test/camera_test.cpp + test/capture_controller_test.cpp + ${PLUGIN_SOURCES} +) +apply_standard_settings(${TEST_RUNNER}) +target_include_directories(${TEST_RUNNER} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}") +target_link_libraries(${TEST_RUNNER} PRIVATE flutter_wrapper_plugin) +target_link_libraries(${TEST_RUNNER} PRIVATE mf mfplat mfuuid d3d11) +target_link_libraries(${TEST_RUNNER} PRIVATE gtest_main gmock) + +# flutter_wrapper_plugin has link dependencies on the Flutter DLL. +add_custom_command(TARGET ${TEST_RUNNER} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${FLUTTER_LIBRARY}" $ +) + +include(GoogleTest) +gtest_discover_tests(${TEST_RUNNER}) +endif() diff --git a/packages/camera/camera_windows/windows/camera.cpp b/packages/camera/camera_windows/windows/camera.cpp new file mode 100644 index 000000000000..c21f8ab0af78 --- /dev/null +++ b/packages/camera/camera_windows/windows/camera.cpp @@ -0,0 +1,264 @@ +// 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. + +#include "camera.h" + +namespace camera_windows { +using flutter::EncodableList; +using flutter::EncodableMap; +using flutter::EncodableValue; + +// Camera channel events. +constexpr char kCameraMethodChannelBaseName[] = + "plugins.flutter.io/camera_windows/camera"; +constexpr char kVideoRecordedEvent[] = "video_recorded"; +constexpr char kCameraClosingEvent[] = "camera_closing"; +constexpr char kErrorEvent[] = "error"; + +CameraImpl::CameraImpl(const std::string& device_id) + : device_id_(device_id), Camera(device_id) {} + +CameraImpl::~CameraImpl() { + // Sends camera closing event. + OnCameraClosing(); + + capture_controller_ = nullptr; + SendErrorForPendingResults("plugin_disposed", + "Plugin disposed before request was handled"); +} + +void CameraImpl::InitCamera(flutter::TextureRegistrar* texture_registrar, + flutter::BinaryMessenger* messenger, + bool record_audio, + ResolutionPreset resolution_preset) { + auto capture_controller_factory = + std::make_unique(); + InitCamera(std::move(capture_controller_factory), texture_registrar, + messenger, record_audio, resolution_preset); +} + +void CameraImpl::InitCamera( + std::unique_ptr capture_controller_factory, + flutter::TextureRegistrar* texture_registrar, + flutter::BinaryMessenger* messenger, bool record_audio, + ResolutionPreset resolution_preset) { + assert(!device_id_.empty()); + messenger_ = messenger; + capture_controller_ = + capture_controller_factory->CreateCaptureController(this); + capture_controller_->InitCaptureDevice(texture_registrar, device_id_, + record_audio, resolution_preset); +} + +bool CameraImpl::AddPendingResult( + PendingResultType type, std::unique_ptr> result) { + assert(result); + + auto it = pending_results_.find(type); + if (it != pending_results_.end()) { + result->Error("Duplicate request", "Method handler already called"); + return false; + } + + pending_results_.insert(std::make_pair(type, std::move(result))); + return true; +} + +std::unique_ptr> CameraImpl::GetPendingResultByType( + PendingResultType type) { + auto it = pending_results_.find(type); + if (it == pending_results_.end()) { + return nullptr; + } + auto result = std::move(it->second); + pending_results_.erase(it); + return result; +} + +bool CameraImpl::HasPendingResultByType(PendingResultType type) const { + auto it = pending_results_.find(type); + if (it == pending_results_.end()) { + return false; + } + return it->second != nullptr; +} + +void CameraImpl::SendErrorForPendingResults(const std::string& error_code, + const std::string& descripion) { + for (const auto& pending_result : pending_results_) { + pending_result.second->Error(error_code, descripion); + } + pending_results_.clear(); +} + +MethodChannel<>* CameraImpl::GetMethodChannel() { + assert(messenger_); + assert(camera_id_); + + // Use existing channel if initialized + if (camera_channel_) { + return camera_channel_.get(); + } + + auto channel_name = + std::string(kCameraMethodChannelBaseName) + std::to_string(camera_id_); + + camera_channel_ = std::make_unique>( + messenger_, channel_name, &flutter::StandardMethodCodec::GetInstance()); + + return camera_channel_.get(); +} + +void CameraImpl::OnCreateCaptureEngineSucceeded(int64_t texture_id) { + // Use texture id as camera id + camera_id_ = texture_id; + auto pending_result = + GetPendingResultByType(PendingResultType::kCreateCamera); + if (pending_result) { + pending_result->Success(EncodableMap( + {{EncodableValue("cameraId"), EncodableValue(texture_id)}})); + } +} + +void CameraImpl::OnCreateCaptureEngineFailed(const std::string& error) { + auto pending_result = + GetPendingResultByType(PendingResultType::kCreateCamera); + if (pending_result) { + pending_result->Error("camera_error", error); + } +} + +void CameraImpl::OnStartPreviewSucceeded(int32_t width, int32_t height) { + auto pending_result = GetPendingResultByType(PendingResultType::kInitialize); + if (pending_result) { + pending_result->Success(EncodableValue(EncodableMap({ + {EncodableValue("previewWidth"), + EncodableValue(static_cast(width))}, + {EncodableValue("previewHeight"), + EncodableValue(static_cast(height))}, + }))); + } +}; + +void CameraImpl::OnStartPreviewFailed(const std::string& error) { + auto pending_result = GetPendingResultByType(PendingResultType::kInitialize); + if (pending_result) { + pending_result->Error("camera_error", error); + } +}; + +void CameraImpl::OnResumePreviewSucceeded() { + auto pending_result = + GetPendingResultByType(PendingResultType::kResumePreview); + if (pending_result) { + pending_result->Success(); + } +} + +void CameraImpl::OnResumePreviewFailed(const std::string& error) { + auto pending_result = + GetPendingResultByType(PendingResultType::kResumePreview); + if (pending_result) { + pending_result->Error("camera_error", error); + } +} + +void CameraImpl::OnPausePreviewSucceeded() { + auto pending_result = + GetPendingResultByType(PendingResultType::kPausePreview); + if (pending_result) { + pending_result->Success(); + } +} + +void CameraImpl::OnPausePreviewFailed(const std::string& error) { + auto pending_result = + GetPendingResultByType(PendingResultType::kPausePreview); + if (pending_result) { + pending_result->Error("camera_error", error); + } +} + +void CameraImpl::OnStartRecordSucceeded() { + auto pending_result = GetPendingResultByType(PendingResultType::kStartRecord); + if (pending_result) { + pending_result->Success(); + } +}; + +void CameraImpl::OnStartRecordFailed(const std::string& error) { + auto pending_result = GetPendingResultByType(PendingResultType::kStartRecord); + if (pending_result) { + pending_result->Error("camera_error", error); + } +}; + +void CameraImpl::OnStopRecordSucceeded(const std::string& file_path) { + auto pending_result = GetPendingResultByType(PendingResultType::kStopRecord); + if (pending_result) { + pending_result->Success(EncodableValue(file_path)); + } +}; + +void CameraImpl::OnStopRecordFailed(const std::string& error) { + auto pending_result = GetPendingResultByType(PendingResultType::kStopRecord); + if (pending_result) { + pending_result->Error("camera_error", error); + } +}; + +void CameraImpl::OnTakePictureSucceeded(const std::string& file_path) { + auto pending_result = GetPendingResultByType(PendingResultType::kTakePicture); + if (pending_result) { + pending_result->Success(EncodableValue(file_path)); + } +}; + +void CameraImpl::OnTakePictureFailed(const std::string& error) { + auto pending_take_picture_result = + GetPendingResultByType(PendingResultType::kTakePicture); + if (pending_take_picture_result) { + pending_take_picture_result->Error("camera_error", error); + } +}; + +void CameraImpl::OnVideoRecordSucceeded(const std::string& file_path, + int64_t video_duration_ms) { + if (messenger_ && camera_id_ >= 0) { + auto channel = GetMethodChannel(); + + std::unique_ptr message_data = + std::make_unique( + EncodableMap({{EncodableValue("path"), EncodableValue(file_path)}, + {EncodableValue("maxVideoDuration"), + EncodableValue(video_duration_ms)}})); + + channel->InvokeMethod(kVideoRecordedEvent, std::move(message_data)); + } +} + +void CameraImpl::OnVideoRecordFailed(const std::string& error){}; + +void CameraImpl::OnCaptureError(const std::string& error) { + if (messenger_ && camera_id_ >= 0) { + auto channel = GetMethodChannel(); + + std::unique_ptr message_data = + std::make_unique(EncodableMap( + {{EncodableValue("description"), EncodableValue(error)}})); + channel->InvokeMethod(kErrorEvent, std::move(message_data)); + } + + SendErrorForPendingResults("capture_error", error); +} + +void CameraImpl::OnCameraClosing() { + if (messenger_ && camera_id_ >= 0) { + auto channel = GetMethodChannel(); + channel->InvokeMethod(kCameraClosingEvent, + std::move(std::make_unique())); + } +} + +} // namespace camera_windows diff --git a/packages/camera/camera_windows/windows/camera.h b/packages/camera/camera_windows/windows/camera.h new file mode 100644 index 000000000000..6996231c7ab4 --- /dev/null +++ b/packages/camera/camera_windows/windows/camera.h @@ -0,0 +1,194 @@ +// 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. + +#ifndef PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_CAMERA_H_ +#define PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_CAMERA_H_ + +#include +#include + +#include + +#include "capture_controller.h" + +namespace camera_windows { + +using flutter::EncodableMap; +using flutter::MethodChannel; +using flutter::MethodResult; + +// A set of result types that are stored +// for processing asynchronous commands. +enum class PendingResultType { + kCreateCamera, + kInitialize, + kTakePicture, + kStartRecord, + kStopRecord, + kPausePreview, + kResumePreview, +}; + +// Interface implemented by cameras. +// +// Access is provided to an associated |CaptureController|, which can be used +// to capture video or photo from the camera. +class Camera : public CaptureControllerListener { + public: + explicit Camera(const std::string& device_id) {} + virtual ~Camera() = default; + + // Disallow copy and move. + Camera(const Camera&) = delete; + Camera& operator=(const Camera&) = delete; + + // Tests if this camera has the specified device ID. + virtual bool HasDeviceId(std::string& device_id) const = 0; + + // Tests if this camera has the specified camera ID. + virtual bool HasCameraId(int64_t camera_id) const = 0; + + // Adds a pending result. + // + // Returns an error result if the result has already been added. + virtual bool AddPendingResult(PendingResultType type, + std::unique_ptr> result) = 0; + + // Checks if a pending result of the specified type already exists. + virtual bool HasPendingResultByType(PendingResultType type) const = 0; + + // Returns a |CaptureController| that allows capturing video or still photos + // from this camera. + virtual camera_windows::CaptureController* GetCaptureController() = 0; + + // Initializes this camera and its associated capture controller. + virtual void InitCamera(flutter::TextureRegistrar* texture_registrar, + flutter::BinaryMessenger* messenger, + bool record_audio, + ResolutionPreset resolution_preset) = 0; +}; + +// Concrete implementation of the |Camera| interface. +// +// This implementation is responsible for initializing the capture controller, +// listening for camera events, processing pending results, and notifying +// application code of processed events via the method channel. +class CameraImpl : public Camera { + public: + explicit CameraImpl(const std::string& device_id); + virtual ~CameraImpl(); + + // Disallow copy and move. + CameraImpl(const CameraImpl&) = delete; + CameraImpl& operator=(const CameraImpl&) = delete; + + // CaptureControllerListener + void OnCreateCaptureEngineSucceeded(int64_t texture_id) override; + void OnCreateCaptureEngineFailed(const std::string& error) override; + void OnStartPreviewSucceeded(int32_t width, int32_t height) override; + void OnStartPreviewFailed(const std::string& error) override; + void OnPausePreviewSucceeded() override; + void OnPausePreviewFailed(const std::string& error) override; + void OnResumePreviewSucceeded() override; + void OnResumePreviewFailed(const std::string& error) override; + void OnStartRecordSucceeded() override; + void OnStartRecordFailed(const std::string& error) override; + void OnStopRecordSucceeded(const std::string& file_path) override; + void OnStopRecordFailed(const std::string& error) override; + void OnTakePictureSucceeded(const std::string& file_path) override; + void OnTakePictureFailed(const std::string& error) override; + void OnVideoRecordSucceeded(const std::string& file_path, + int64_t video_duration) override; + void OnVideoRecordFailed(const std::string& error) override; + void OnCaptureError(const std::string& error) override; + + // Camera + bool HasDeviceId(std::string& device_id) const override { + return device_id_ == device_id; + } + bool HasCameraId(int64_t camera_id) const override { + return camera_id_ == camera_id; + } + bool AddPendingResult(PendingResultType type, + std::unique_ptr> result) override; + bool HasPendingResultByType(PendingResultType type) const override; + camera_windows::CaptureController* GetCaptureController() override { + return capture_controller_.get(); + } + void InitCamera(flutter::TextureRegistrar* texture_registrar, + flutter::BinaryMessenger* messenger, bool record_audio, + ResolutionPreset resolution_preset) override; + + // Initializes the camera and its associated capture controller. + // + // This is a convenience method called by |InitCamera| but also used in + // tests. + void InitCamera( + std::unique_ptr capture_controller_factory, + flutter::TextureRegistrar* texture_registrar, + flutter::BinaryMessenger* messenger, bool record_audio, + ResolutionPreset resolution_preset); + + private: + // Loops through all pending results and calls their error handler with given + // error ID and description. Pending results are cleared in the process. + // + // error_code: A string error code describing the error. + // error_message: A user-readable error message (optional). + void SendErrorForPendingResults(const std::string& error_code, + const std::string& descripion); + + // Called when camera is disposed. + // Sends camera closing message to the cameras method channel. + void OnCameraClosing(); + + // Initializes method channel instance and returns pointer it. + MethodChannel<>* GetMethodChannel(); + + // Finds pending result by type. + // Returns nullptr if type is not present. + std::unique_ptr> GetPendingResultByType( + PendingResultType type); + + std::map>> pending_results_; + std::unique_ptr capture_controller_; + std::unique_ptr> camera_channel_; + flutter::BinaryMessenger* messenger_ = nullptr; + int64_t camera_id_ = -1; + std::string device_id_; +}; + +// Factory class for creating |Camera| instances from a specified device ID. +class CameraFactory { + public: + CameraFactory() {} + virtual ~CameraFactory() = default; + + // Disallow copy and move. + CameraFactory(const CameraFactory&) = delete; + CameraFactory& operator=(const CameraFactory&) = delete; + + // Creates camera for given device id. + virtual std::unique_ptr CreateCamera( + const std::string& device_id) = 0; +}; + +// Concrete implementation of |CameraFactory|. +class CameraFactoryImpl : public CameraFactory { + public: + CameraFactoryImpl() {} + virtual ~CameraFactoryImpl() = default; + + // Disallow copy and move. + CameraFactoryImpl(const CameraFactoryImpl&) = delete; + CameraFactoryImpl& operator=(const CameraFactoryImpl&) = delete; + + std::unique_ptr CreateCamera(const std::string& device_id) override { + return std::make_unique(device_id); + } +}; + +} // namespace camera_windows + +#endif // PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_CAMERA_H_ diff --git a/packages/camera/camera_windows/windows/camera_plugin.cpp b/packages/camera/camera_windows/windows/camera_plugin.cpp new file mode 100644 index 000000000000..3b795e02047a --- /dev/null +++ b/packages/camera/camera_windows/windows/camera_plugin.cpp @@ -0,0 +1,594 @@ +// 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. + +#include "camera_plugin.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "capture_device_info.h" +#include "com_heap_ptr.h" +#include "string_utils.h" + +namespace camera_windows { +using flutter::EncodableList; +using flutter::EncodableMap; +using flutter::EncodableValue; + +namespace { + +// Channel events +constexpr char kChannelName[] = "plugins.flutter.io/camera_windows"; + +constexpr char kAvailableCamerasMethod[] = "availableCameras"; +constexpr char kCreateMethod[] = "create"; +constexpr char kInitializeMethod[] = "initialize"; +constexpr char kTakePictureMethod[] = "takePicture"; +constexpr char kStartVideoRecordingMethod[] = "startVideoRecording"; +constexpr char kStopVideoRecordingMethod[] = "stopVideoRecording"; +constexpr char kPausePreview[] = "pausePreview"; +constexpr char kResumePreview[] = "resumePreview"; +constexpr char kDisposeMethod[] = "dispose"; + +constexpr char kCameraNameKey[] = "cameraName"; +constexpr char kResolutionPresetKey[] = "resolutionPreset"; +constexpr char kEnableAudioKey[] = "enableAudio"; + +constexpr char kCameraIdKey[] = "cameraId"; +constexpr char kMaxVideoDurationKey[] = "maxVideoDuration"; + +constexpr char kResolutionPresetValueLow[] = "low"; +constexpr char kResolutionPresetValueMedium[] = "medium"; +constexpr char kResolutionPresetValueHigh[] = "high"; +constexpr char kResolutionPresetValueVeryHigh[] = "veryHigh"; +constexpr char kResolutionPresetValueUltraHigh[] = "ultraHigh"; +constexpr char kResolutionPresetValueMax[] = "max"; + +const std::string kPictureCaptureExtension = "jpeg"; +const std::string kVideoCaptureExtension = "mp4"; + +// Looks for |key| in |map|, returning the associated value if it is present, or +// a nullptr if not. +const EncodableValue* ValueOrNull(const EncodableMap& map, const char* key) { + auto it = map.find(EncodableValue(key)); + if (it == map.end()) { + return nullptr; + } + return &(it->second); +} + +// Looks for |key| in |map|, returning the associated int64 value if it is +// present, or std::nullopt if not. +std::optional GetInt64ValueOrNull(const EncodableMap& map, + const char* key) { + auto value = ValueOrNull(map, key); + if (!value) { + return std::nullopt; + } + + if (std::holds_alternative(*value)) { + return static_cast(std::get(*value)); + } + auto val64 = std::get_if(value); + if (!val64) { + return std::nullopt; + } + return *val64; +} + +// Parses resolution preset argument to enum value. +ResolutionPreset ParseResolutionPreset(const std::string& resolution_preset) { + if (resolution_preset.compare(kResolutionPresetValueLow) == 0) { + return ResolutionPreset::kLow; + } else if (resolution_preset.compare(kResolutionPresetValueMedium) == 0) { + return ResolutionPreset::kMedium; + } else if (resolution_preset.compare(kResolutionPresetValueHigh) == 0) { + return ResolutionPreset::kHigh; + } else if (resolution_preset.compare(kResolutionPresetValueVeryHigh) == 0) { + return ResolutionPreset::kVeryHigh; + } else if (resolution_preset.compare(kResolutionPresetValueUltraHigh) == 0) { + return ResolutionPreset::kUltraHigh; + } else if (resolution_preset.compare(kResolutionPresetValueMax) == 0) { + return ResolutionPreset::kMax; + } + return ResolutionPreset::kAuto; +} + +// Builds CaptureDeviceInfo object from given device holding device name and id. +std::unique_ptr GetDeviceInfo(IMFActivate* device) { + assert(device); + auto device_info = std::make_unique(); + ComHeapPtr name; + UINT32 name_size; + + HRESULT hr = device->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, + &name, &name_size); + if (FAILED(hr)) { + return device_info; + } + + ComHeapPtr id; + UINT32 id_size; + hr = device->GetAllocatedString( + MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, &id, &id_size); + + if (FAILED(hr)) { + return device_info; + } + + device_info->SetDisplayName(Utf8FromUtf16(std::wstring(name, name_size))); + device_info->SetDeviceID(Utf8FromUtf16(std::wstring(id, id_size))); + return device_info; +} + +// Builds datetime string from current time. +// Used as part of the filenames for captured pictures and videos. +std::string GetCurrentTimeString() { + std::chrono::system_clock::duration now = + std::chrono::system_clock::now().time_since_epoch(); + + auto s = std::chrono::duration_cast(now).count(); + auto ms = + std::chrono::duration_cast(now).count() % 1000; + + struct tm newtime; + localtime_s(&newtime, &s); + + std::string time_start = ""; + time_start.resize(80); + size_t len = + strftime(&time_start[0], time_start.size(), "%Y_%m%d_%H%M%S_", &newtime); + if (len > 0) { + time_start.resize(len); + } + + // Add milliseconds to make sure the filename is unique + return time_start + std::to_string(ms); +} + +// Builds file path for picture capture. +std::optional GetFilePathForPicture() { + ComHeapPtr known_folder_path; + HRESULT hr = SHGetKnownFolderPath(FOLDERID_Pictures, KF_FLAG_CREATE, nullptr, + &known_folder_path); + if (FAILED(hr)) { + return std::nullopt; + } + + std::string path = Utf8FromUtf16(std::wstring(known_folder_path)); + + return path + "\\" + "PhotoCapture_" + GetCurrentTimeString() + "." + + kPictureCaptureExtension; +} + +// Builds file path for video capture. +std::optional GetFilePathForVideo() { + ComHeapPtr known_folder_path; + HRESULT hr = SHGetKnownFolderPath(FOLDERID_Videos, KF_FLAG_CREATE, nullptr, + &known_folder_path); + if (FAILED(hr)) { + return std::nullopt; + } + + std::string path = Utf8FromUtf16(std::wstring(known_folder_path)); + + return path + "\\" + "VideoCapture_" + GetCurrentTimeString() + "." + + kVideoCaptureExtension; +} +} // namespace + +// static +void CameraPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarWindows* registrar) { + auto channel = std::make_unique>( + registrar->messenger(), kChannelName, + &flutter::StandardMethodCodec::GetInstance()); + + std::unique_ptr plugin = std::make_unique( + registrar->texture_registrar(), registrar->messenger()); + + channel->SetMethodCallHandler( + [plugin_pointer = plugin.get()](const auto& call, auto result) { + plugin_pointer->HandleMethodCall(call, std::move(result)); + }); + + registrar->AddPlugin(std::move(plugin)); +} + +CameraPlugin::CameraPlugin(flutter::TextureRegistrar* texture_registrar, + flutter::BinaryMessenger* messenger) + : texture_registrar_(texture_registrar), + messenger_(messenger), + camera_factory_(std::make_unique()) {} + +CameraPlugin::CameraPlugin(flutter::TextureRegistrar* texture_registrar, + flutter::BinaryMessenger* messenger, + std::unique_ptr camera_factory) + : texture_registrar_(texture_registrar), + messenger_(messenger), + camera_factory_(std::move(camera_factory)) {} + +CameraPlugin::~CameraPlugin() {} + +void CameraPlugin::HandleMethodCall( + const flutter::MethodCall<>& method_call, + std::unique_ptr> result) { + const std::string& method_name = method_call.method_name(); + + if (method_name.compare(kAvailableCamerasMethod) == 0) { + return AvailableCamerasMethodHandler(std::move(result)); + } else if (method_name.compare(kCreateMethod) == 0) { + const auto* arguments = + std::get_if(method_call.arguments()); + assert(arguments); + + return CreateMethodHandler(*arguments, std::move(result)); + } else if (method_name.compare(kInitializeMethod) == 0) { + const auto* arguments = + std::get_if(method_call.arguments()); + assert(arguments); + + return this->InitializeMethodHandler(*arguments, std::move(result)); + } else if (method_name.compare(kTakePictureMethod) == 0) { + const auto* arguments = + std::get_if(method_call.arguments()); + assert(arguments); + + return TakePictureMethodHandler(*arguments, std::move(result)); + } else if (method_name.compare(kStartVideoRecordingMethod) == 0) { + const auto* arguments = + std::get_if(method_call.arguments()); + assert(arguments); + + return StartVideoRecordingMethodHandler(*arguments, std::move(result)); + } else if (method_name.compare(kStopVideoRecordingMethod) == 0) { + const auto* arguments = + std::get_if(method_call.arguments()); + assert(arguments); + + return StopVideoRecordingMethodHandler(*arguments, std::move(result)); + } else if (method_name.compare(kPausePreview) == 0) { + const auto* arguments = + std::get_if(method_call.arguments()); + assert(arguments); + + return PausePreviewMethodHandler(*arguments, std::move(result)); + } else if (method_name.compare(kResumePreview) == 0) { + const auto* arguments = + std::get_if(method_call.arguments()); + assert(arguments); + + return ResumePreviewMethodHandler(*arguments, std::move(result)); + } else if (method_name.compare(kDisposeMethod) == 0) { + const auto* arguments = + std::get_if(method_call.arguments()); + assert(arguments); + + return DisposeMethodHandler(*arguments, std::move(result)); + } else { + result->NotImplemented(); + } +} + +Camera* CameraPlugin::GetCameraByDeviceId(std::string& device_id) { + for (auto it = begin(cameras_); it != end(cameras_); ++it) { + if ((*it)->HasDeviceId(device_id)) { + return it->get(); + } + } + return nullptr; +} + +Camera* CameraPlugin::GetCameraByCameraId(int64_t camera_id) { + for (auto it = begin(cameras_); it != end(cameras_); ++it) { + if ((*it)->HasCameraId(camera_id)) { + return it->get(); + } + } + return nullptr; +} + +void CameraPlugin::DisposeCameraByCameraId(int64_t camera_id) { + for (auto it = begin(cameras_); it != end(cameras_); ++it) { + if ((*it)->HasCameraId(camera_id)) { + cameras_.erase(it); + return; + } + } +} + +void CameraPlugin::AvailableCamerasMethodHandler( + std::unique_ptr> result) { + // Enumerate devices. + ComHeapPtr devices; + UINT32 count = 0; + if (!this->EnumerateVideoCaptureDeviceSources(&devices, &count)) { + result->Error("System error", "Failed to get available cameras"); + // No need to free devices here, cos allocation failed. + return; + } + + if (count == 0) { + result->Success(EncodableValue(EncodableList())); + return; + } + + // Format found devices to the response. + EncodableList devices_list; + for (UINT32 i = 0; i < count; ++i) { + auto device_info = GetDeviceInfo(devices[i]); + auto deviceName = device_info->GetUniqueDeviceName(); + + devices_list.push_back(EncodableMap({ + {EncodableValue("name"), EncodableValue(deviceName)}, + {EncodableValue("lensFacing"), EncodableValue("front")}, + {EncodableValue("sensorOrientation"), EncodableValue(0)}, + })); + } + + result->Success(std::move(EncodableValue(devices_list))); +} + +bool CameraPlugin::EnumerateVideoCaptureDeviceSources(IMFActivate*** devices, + UINT32* count) { + return CaptureControllerImpl::EnumerateVideoCaptureDeviceSources(devices, + count); +} + +void CameraPlugin::CreateMethodHandler( + const EncodableMap& args, std::unique_ptr> result) { + // Parse enableAudio argument. + const auto* record_audio = + std::get_if(ValueOrNull(args, kEnableAudioKey)); + if (!record_audio) { + return result->Error("argument_error", + std::string(kEnableAudioKey) + " argument missing"); + } + + // Parse cameraName argument. + const auto* camera_name = + std::get_if(ValueOrNull(args, kCameraNameKey)); + if (!camera_name) { + return result->Error("argument_error", + std::string(kCameraNameKey) + " argument missing"); + } + + auto device_info = std::make_unique(); + if (!device_info->ParseDeviceInfoFromCameraName(*camera_name)) { + return result->Error( + "camera_error", "Cannot parse argument " + std::string(kCameraNameKey)); + } + + auto device_id = device_info->GetDeviceId(); + if (GetCameraByDeviceId(device_id)) { + return result->Error("camera_error", + "Camera with given device id already exists. Existing " + "camera must be disposed before creating it again."); + } + + std::unique_ptr camera = + camera_factory_->CreateCamera(device_id); + + if (camera->HasPendingResultByType(PendingResultType::kCreateCamera)) { + return result->Error("camera_error", + "Pending camera creation request exists"); + } + + if (camera->AddPendingResult(PendingResultType::kCreateCamera, + std::move(result))) { + // Parse resolution preset argument. + const auto* resolution_preset_argument = + std::get_if(ValueOrNull(args, kResolutionPresetKey)); + ResolutionPreset resolution_preset; + if (resolution_preset_argument) { + resolution_preset = ParseResolutionPreset(*resolution_preset_argument); + } else { + resolution_preset = ResolutionPreset::kAuto; + } + + camera->InitCamera(texture_registrar_, messenger_, *record_audio, + resolution_preset); + cameras_.push_back(std::move(camera)); + } +} + +void CameraPlugin::InitializeMethodHandler( + const EncodableMap& args, std::unique_ptr> result) { + auto camera_id = GetInt64ValueOrNull(args, kCameraIdKey); + if (!camera_id) { + return result->Error("argument_error", + std::string(kCameraIdKey) + " missing"); + } + + auto camera = GetCameraByCameraId(*camera_id); + if (!camera) { + return result->Error("camera_error", "Camera not created"); + } + + if (camera->HasPendingResultByType(PendingResultType::kInitialize)) { + return result->Error("camera_error", + "Pending initialization request exists"); + } + + if (camera->AddPendingResult(PendingResultType::kInitialize, + std::move(result))) { + auto cc = camera->GetCaptureController(); + assert(cc); + cc->StartPreview(); + } +} + +void CameraPlugin::PausePreviewMethodHandler( + const EncodableMap& args, std::unique_ptr> result) { + auto camera_id = GetInt64ValueOrNull(args, kCameraIdKey); + if (!camera_id) { + return result->Error("argument_error", + std::string(kCameraIdKey) + " missing"); + } + + auto camera = GetCameraByCameraId(*camera_id); + if (!camera) { + return result->Error("camera_error", "Camera not created"); + } + + if (camera->HasPendingResultByType(PendingResultType::kPausePreview)) { + return result->Error("camera_error", + "Pending pause preview request exists"); + } + + if (camera->AddPendingResult(PendingResultType::kPausePreview, + std::move(result))) { + auto cc = camera->GetCaptureController(); + assert(cc); + cc->PausePreview(); + } +} + +void CameraPlugin::ResumePreviewMethodHandler( + const EncodableMap& args, std::unique_ptr> result) { + auto camera_id = GetInt64ValueOrNull(args, kCameraIdKey); + if (!camera_id) { + return result->Error("argument_error", + std::string(kCameraIdKey) + " missing"); + } + + auto camera = GetCameraByCameraId(*camera_id); + if (!camera) { + return result->Error("camera_error", "Camera not created"); + } + + if (camera->HasPendingResultByType(PendingResultType::kResumePreview)) { + return result->Error("camera_error", + "Pending resume preview request exists"); + } + + if (camera->AddPendingResult(PendingResultType::kResumePreview, + std::move(result))) { + auto cc = camera->GetCaptureController(); + assert(cc); + cc->ResumePreview(); + } +} + +void CameraPlugin::StartVideoRecordingMethodHandler( + const EncodableMap& args, std::unique_ptr> result) { + auto camera_id = GetInt64ValueOrNull(args, kCameraIdKey); + if (!camera_id) { + return result->Error("argument_error", + std::string(kCameraIdKey) + " missing"); + } + + auto camera = GetCameraByCameraId(*camera_id); + if (!camera) { + return result->Error("camera_error", "Camera not created"); + } + + if (camera->HasPendingResultByType(PendingResultType::kStartRecord)) { + return result->Error("camera_error", + "Pending start recording request exists"); + } + + int64_t max_video_duration_ms = -1; + auto requested_max_video_duration_ms = + std::get_if(ValueOrNull(args, kMaxVideoDurationKey)); + + if (requested_max_video_duration_ms != nullptr) { + max_video_duration_ms = *requested_max_video_duration_ms; + } + + std::optional path = GetFilePathForVideo(); + if (path) { + if (camera->AddPendingResult(PendingResultType::kStartRecord, + std::move(result))) { + auto cc = camera->GetCaptureController(); + assert(cc); + cc->StartRecord(*path, max_video_duration_ms); + } + } else { + return result->Error("system_error", + "Failed to get path for video capture"); + } +} + +void CameraPlugin::StopVideoRecordingMethodHandler( + const EncodableMap& args, std::unique_ptr> result) { + auto camera_id = GetInt64ValueOrNull(args, kCameraIdKey); + if (!camera_id) { + return result->Error("argument_error", + std::string(kCameraIdKey) + " missing"); + } + + auto camera = GetCameraByCameraId(*camera_id); + if (!camera) { + return result->Error("camera_error", "Camera not created"); + } + + if (camera->HasPendingResultByType(PendingResultType::kStopRecord)) { + return result->Error("camera_error", + "Pending stop recording request exists"); + } + + if (camera->AddPendingResult(PendingResultType::kStopRecord, + std::move(result))) { + auto cc = camera->GetCaptureController(); + assert(cc); + cc->StopRecord(); + } +} + +void CameraPlugin::TakePictureMethodHandler( + const EncodableMap& args, std::unique_ptr> result) { + auto camera_id = GetInt64ValueOrNull(args, kCameraIdKey); + if (!camera_id) { + return result->Error("argument_error", + std::string(kCameraIdKey) + " missing"); + } + + auto camera = GetCameraByCameraId(*camera_id); + if (!camera) { + return result->Error("camera_error", "Camera not created"); + } + + if (camera->HasPendingResultByType(PendingResultType::kTakePicture)) { + return result->Error("camera_error", "Pending take picture request exists"); + } + + std::optional path = GetFilePathForPicture(); + if (path) { + if (camera->AddPendingResult(PendingResultType::kTakePicture, + std::move(result))) { + auto cc = camera->GetCaptureController(); + assert(cc); + cc->TakePicture(*path); + } + } else { + return result->Error("system_error", + "Failed to get capture path for picture"); + } +} + +void CameraPlugin::DisposeMethodHandler( + const EncodableMap& args, std::unique_ptr> result) { + auto camera_id = GetInt64ValueOrNull(args, kCameraIdKey); + if (!camera_id) { + return result->Error("argument_error", + std::string(kCameraIdKey) + " missing"); + } + + DisposeCameraByCameraId(*camera_id); + result->Success(); +} + +} // namespace camera_windows diff --git a/packages/camera/camera_windows/windows/camera_plugin.h b/packages/camera/camera_windows/windows/camera_plugin.h new file mode 100644 index 000000000000..1baa2477beb5 --- /dev/null +++ b/packages/camera/camera_windows/windows/camera_plugin.h @@ -0,0 +1,132 @@ +// 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. + +#ifndef PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_CAMERA_PLUGIN_H_ +#define PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_CAMERA_PLUGIN_H_ + +#include +#include +#include +#include + +#include + +#include "camera.h" +#include "capture_controller.h" +#include "capture_controller_listener.h" + +namespace camera_windows { +using flutter::MethodResult; + +namespace test { +namespace { +// Forward declaration of test class. +class MockCameraPlugin; +} // namespace +} // namespace test + +class CameraPlugin : public flutter::Plugin, + public VideoCaptureDeviceEnumerator { + public: + static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); + + CameraPlugin(flutter::TextureRegistrar* texture_registrar, + flutter::BinaryMessenger* messenger); + + // Creates a plugin instance with the given CameraFactory instance. + // Exists for unit testing with mock implementations. + CameraPlugin(flutter::TextureRegistrar* texture_registrar, + flutter::BinaryMessenger* messenger, + std::unique_ptr camera_factory); + + virtual ~CameraPlugin(); + + // Disallow copy and move. + CameraPlugin(const CameraPlugin&) = delete; + CameraPlugin& operator=(const CameraPlugin&) = delete; + + // Called when a method is called on plugin channel. + void HandleMethodCall(const flutter::MethodCall<>& method_call, + std::unique_ptr> result); + + private: + // Loops through cameras and returns camera + // with matching device_id or nullptr. + Camera* GetCameraByDeviceId(std::string& device_id); + + // Loops through cameras and returns camera + // with matching camera_id or nullptr. + Camera* GetCameraByCameraId(int64_t camera_id); + + // Disposes camera by camera id. + void DisposeCameraByCameraId(int64_t camera_id); + + // Enumerates video capture devices. + bool EnumerateVideoCaptureDeviceSources(IMFActivate*** devices, + UINT32* count) override; + + // Handles availableCameras method calls. + // Enumerates video capture devices and + // returns list of available camera devices. + void AvailableCamerasMethodHandler( + std::unique_ptr> result); + + // Handles create method calls. + // Creates camera and initializes capture controller for requested device. + // Stores result object to be handled after request is processed. + void CreateMethodHandler(const EncodableMap& args, + std::unique_ptr> result); + + // Handles initialize method calls. + // Requests existing camera controller to start preview. + // Stores result object to be handled after request is processed. + void InitializeMethodHandler(const EncodableMap& args, + std::unique_ptr> result); + + // Handles takePicture method calls. + // Requests existing camera controller to take photo. + // Stores result object to be handled after request is processed. + void TakePictureMethodHandler(const EncodableMap& args, + std::unique_ptr> result); + + // Handles startVideoRecording method calls. + // Requests existing camera controller to start recording. + // Stores result object to be handled after request is processed. + void StartVideoRecordingMethodHandler(const EncodableMap& args, + std::unique_ptr> result); + + // Handles stopVideoRecording method calls. + // Requests existing camera controller to stop recording. + // Stores result object to be handled after request is processed. + void StopVideoRecordingMethodHandler(const EncodableMap& args, + std::unique_ptr> result); + + // Handles pausePreview method calls. + // Requests existing camera controller to pause recording. + // Stores result object to be handled after request is processed. + void PausePreviewMethodHandler(const EncodableMap& args, + std::unique_ptr> result); + + // Handles resumePreview method calls. + // Requests existing camera controller to resume preview. + // Stores result object to be handled after request is processed. + void ResumePreviewMethodHandler(const EncodableMap& args, + std::unique_ptr> result); + + // Handles dsipose method calls. + // Disposes camera if exists. + void DisposeMethodHandler(const EncodableMap& args, + std::unique_ptr> result); + + std::unique_ptr camera_factory_; + flutter::TextureRegistrar* texture_registrar_; + flutter::BinaryMessenger* messenger_; + std::vector> cameras_; + + friend class camera_windows::test::MockCameraPlugin; +}; + +} // namespace camera_windows + +#endif // PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_CAMERA_PLUGIN_H_ diff --git a/packages/camera/camera_windows/windows/camera_windows.cpp b/packages/camera/camera_windows/windows/camera_windows.cpp new file mode 100644 index 000000000000..2d6b781af59f --- /dev/null +++ b/packages/camera/camera_windows/windows/camera_windows.cpp @@ -0,0 +1,16 @@ +// 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. + +#include "include/camera_windows/camera_windows.h" + +#include + +#include "camera_plugin.h" + +void CameraWindowsRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar) { + camera_windows::CameraPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarManager::GetInstance() + ->GetRegistrar(registrar)); +} diff --git a/packages/camera/camera_windows/windows/capture_controller.cpp b/packages/camera/camera_windows/windows/capture_controller.cpp new file mode 100644 index 000000000000..084b03640bef --- /dev/null +++ b/packages/camera/camera_windows/windows/capture_controller.cpp @@ -0,0 +1,861 @@ +// 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. + +#include "capture_controller.h" + +#include +#include +#include + +#include +#include + +#include "com_heap_ptr.h" +#include "photo_handler.h" +#include "preview_handler.h" +#include "record_handler.h" +#include "string_utils.h" +#include "texture_handler.h" + +namespace camera_windows { + +using Microsoft::WRL::ComPtr; + +CaptureControllerImpl::CaptureControllerImpl( + CaptureControllerListener* listener) + : capture_controller_listener_(listener), CaptureController(){}; + +CaptureControllerImpl::~CaptureControllerImpl() { + ResetCaptureController(); + capture_controller_listener_ = nullptr; +}; + +// static +bool CaptureControllerImpl::EnumerateVideoCaptureDeviceSources( + IMFActivate*** devices, UINT32* count) { + ComPtr attributes; + + HRESULT hr = MFCreateAttributes(&attributes, 1); + if (FAILED(hr)) { + return false; + } + + hr = attributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, + MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID); + if (FAILED(hr)) { + return false; + } + + hr = MFEnumDeviceSources(attributes.Get(), devices, count); + if (FAILED(hr)) { + return false; + } + + return true; +} + +HRESULT CaptureControllerImpl::CreateDefaultAudioCaptureSource() { + audio_source_ = nullptr; + ComHeapPtr devices; + UINT32 count = 0; + + ComPtr attributes; + HRESULT hr = MFCreateAttributes(&attributes, 1); + + if (SUCCEEDED(hr)) { + hr = attributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, + MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID); + } + + if (SUCCEEDED(hr)) { + hr = MFEnumDeviceSources(attributes.Get(), &devices, &count); + } + + if (SUCCEEDED(hr) && count > 0) { + ComHeapPtr audio_device_id; + UINT32 audio_device_id_size; + + // Use first audio device. + hr = devices[0]->GetAllocatedString( + MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_ENDPOINT_ID, &audio_device_id, + &audio_device_id_size); + + if (SUCCEEDED(hr)) { + ComPtr audio_capture_source_attributes; + hr = MFCreateAttributes(&audio_capture_source_attributes, 2); + + if (SUCCEEDED(hr)) { + hr = audio_capture_source_attributes->SetGUID( + MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, + MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID); + } + + if (SUCCEEDED(hr)) { + hr = audio_capture_source_attributes->SetString( + MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_ENDPOINT_ID, + audio_device_id); + } + + if (SUCCEEDED(hr)) { + hr = MFCreateDeviceSource(audio_capture_source_attributes.Get(), + audio_source_.GetAddressOf()); + } + } + } + + return hr; +} + +HRESULT CaptureControllerImpl::CreateVideoCaptureSourceForDevice( + const std::string& video_device_id) { + video_source_ = nullptr; + + ComPtr video_capture_source_attributes; + + HRESULT hr = MFCreateAttributes(&video_capture_source_attributes, 2); + if (FAILED(hr)) { + return hr; + } + + hr = video_capture_source_attributes->SetGUID( + MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, + MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID); + if (FAILED(hr)) { + return hr; + } + + hr = video_capture_source_attributes->SetString( + MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, + Utf16FromUtf8(video_device_id).c_str()); + if (FAILED(hr)) { + return hr; + } + + hr = MFCreateDeviceSource(video_capture_source_attributes.Get(), + video_source_.GetAddressOf()); + return hr; +} + +HRESULT CaptureControllerImpl::CreateD3DManagerWithDX11Device() { + // TODO: Use existing ANGLE device + + HRESULT hr = S_OK; + hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, + D3D11_CREATE_DEVICE_VIDEO_SUPPORT, nullptr, 0, + D3D11_SDK_VERSION, &dx11_device_, nullptr, nullptr); + if (FAILED(hr)) { + return hr; + } + + // Enable multithread protection + ComPtr multi_thread; + hr = dx11_device_.As(&multi_thread); + if (FAILED(hr)) { + return hr; + } + + multi_thread->SetMultithreadProtected(TRUE); + + hr = MFCreateDXGIDeviceManager(&dx_device_reset_token_, + dxgi_device_manager_.GetAddressOf()); + if (FAILED(hr)) { + return hr; + } + + hr = dxgi_device_manager_->ResetDevice(dx11_device_.Get(), + dx_device_reset_token_); + return hr; +} + +HRESULT CaptureControllerImpl::CreateCaptureEngine() { + assert(!video_device_id_.empty()); + + HRESULT hr = S_OK; + ComPtr attributes; + + // Creates capture engine only if not already initialized by test framework + if (!capture_engine_) { + ComPtr capture_engine_factory; + + hr = CoCreateInstance(CLSID_MFCaptureEngineClassFactory, nullptr, + CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&capture_engine_factory)); + if (FAILED(hr)) { + return hr; + } + + // Creates CaptureEngine. + hr = capture_engine_factory->CreateInstance(CLSID_MFCaptureEngine, + IID_PPV_ARGS(&capture_engine_)); + if (FAILED(hr)) { + return hr; + } + } + + hr = CreateD3DManagerWithDX11Device(); + + if (FAILED(hr)) { + return hr; + } + + // Creates video source only if not already initialized by test framework + if (!video_source_) { + hr = CreateVideoCaptureSourceForDevice(video_device_id_); + if (FAILED(hr)) { + return hr; + } + } + + // Creates audio source only if not already initialized by test framework + if (record_audio_ && !audio_source_) { + hr = CreateDefaultAudioCaptureSource(); + if (FAILED(hr)) { + return hr; + } + } + + if (!capture_engine_callback_handler_) { + capture_engine_callback_handler_ = + ComPtr(new CaptureEngineListener(this)); + } + + hr = MFCreateAttributes(&attributes, 2); + if (FAILED(hr)) { + return hr; + } + + hr = attributes->SetUnknown(MF_CAPTURE_ENGINE_D3D_MANAGER, + dxgi_device_manager_.Get()); + if (FAILED(hr)) { + return hr; + } + + hr = attributes->SetUINT32(MF_CAPTURE_ENGINE_USE_VIDEO_DEVICE_ONLY, + !record_audio_); + if (FAILED(hr)) { + return hr; + } + + hr = capture_engine_->Initialize(capture_engine_callback_handler_.Get(), + attributes.Get(), audio_source_.Get(), + video_source_.Get()); + return hr; +} + +void CaptureControllerImpl::ResetCaptureController() { + if (record_handler_) { + if (record_handler_->IsContinuousRecording()) { + StopRecord(); + } else if (record_handler_->IsTimedRecording()) { + StopTimedRecord(); + } + } + + if (preview_handler_) { + StopPreview(); + } + + // Shuts down the media foundation platform object. + // Releases all resources including threads. + // Application should call MFShutdown the same number of times as MFStartup + if (media_foundation_started_) { + MFShutdown(); + } + + // States + media_foundation_started_ = false; + capture_engine_state_ = CaptureEngineState::kNotInitialized; + preview_frame_width_ = 0; + preview_frame_height_ = 0; + capture_engine_callback_handler_ = nullptr; + capture_engine_ = nullptr; + audio_source_ = nullptr; + video_source_ = nullptr; + base_preview_media_type_ = nullptr; + base_capture_media_type_ = nullptr; + + if (dxgi_device_manager_) { + dxgi_device_manager_->ResetDevice(dx11_device_.Get(), + dx_device_reset_token_); + } + dxgi_device_manager_ = nullptr; + dx11_device_ = nullptr; + + record_handler_ = nullptr; + preview_handler_ = nullptr; + photo_handler_ = nullptr; + texture_handler_ = nullptr; +} + +void CaptureControllerImpl::InitCaptureDevice( + flutter::TextureRegistrar* texture_registrar, const std::string& device_id, + bool record_audio, ResolutionPreset resolution_preset) { + assert(capture_controller_listener_); + + if (IsInitialized()) { + return capture_controller_listener_->OnCreateCaptureEngineFailed( + "Capture device already initialized"); + } else if (capture_engine_state_ == CaptureEngineState::kInitializing) { + return capture_controller_listener_->OnCreateCaptureEngineFailed( + "Capture device already initializing"); + } + + capture_engine_state_ = CaptureEngineState::kInitializing; + resolution_preset_ = resolution_preset; + record_audio_ = record_audio; + texture_registrar_ = texture_registrar; + video_device_id_ = device_id; + + // MFStartup must be called before using Media Foundation. + if (!media_foundation_started_) { + HRESULT hr = MFStartup(MF_VERSION); + + if (FAILED(hr)) { + capture_controller_listener_->OnCreateCaptureEngineFailed( + "Failed to create camera"); + ResetCaptureController(); + return; + } + + media_foundation_started_ = true; + } + + HRESULT hr = CreateCaptureEngine(); + if (FAILED(hr)) { + capture_controller_listener_->OnCreateCaptureEngineFailed( + "Failed to create camera"); + ResetCaptureController(); + return; + } +} + +void CaptureControllerImpl::TakePicture(const std::string& file_path) { + assert(capture_engine_callback_handler_); + assert(capture_engine_); + + if (!IsInitialized()) { + return OnPicture(false, "Not initialized"); + } + + if (!base_capture_media_type_) { + // Enumerates mediatypes and finds media type for video capture. + if (FAILED(FindBaseMediaTypes())) { + return OnPicture(false, "Failed to initialize photo capture"); + } + } + + if (!photo_handler_) { + photo_handler_ = std::make_unique(); + } else if (photo_handler_->IsTakingPhoto()) { + return OnPicture(false, "Photo already requested"); + } + + // Check MF_CAPTURE_ENGINE_PHOTO_TAKEN event handling + // for response process. + if (!photo_handler_->TakePhoto(file_path, capture_engine_.Get(), + base_capture_media_type_.Get())) { + // Destroy photo handler on error cases to make sure state is resetted. + photo_handler_ = nullptr; + return OnPicture(false, "Failed to take photo"); + } +} + +uint32_t CaptureControllerImpl::GetMaxPreviewHeight() const { + switch (resolution_preset_) { + case ResolutionPreset::kLow: + return 240; + break; + case ResolutionPreset::kMedium: + return 480; + break; + case ResolutionPreset::kHigh: + return 720; + break; + case ResolutionPreset::kVeryHigh: + return 1080; + break; + case ResolutionPreset::kUltraHigh: + return 2160; + break; + case ResolutionPreset::kMax: + case ResolutionPreset::kAuto: + default: + // no limit. + return 0xffffffff; + break; + } +} + +// Finds best mediat type for given source stream index and max height; +bool FindBestMediaType(DWORD source_stream_index, IMFCaptureSource* source, + IMFMediaType** target_media_type, uint32_t max_height, + uint32_t* target_frame_width, + uint32_t* target_frame_height, + float minimum_accepted_framerate = 15.f) { + assert(source); + ComPtr media_type; + + uint32_t best_width = 0; + uint32_t best_height = 0; + float best_framerate = 0.f; + + // Loop native media types. + for (int i = 0;; i++) { + if (FAILED(source->GetAvailableDeviceMediaType( + source_stream_index, i, media_type.GetAddressOf()))) { + break; + } + + uint32_t frame_rate_numerator, frame_rate_denominator; + if (FAILED(MFGetAttributeRatio(media_type.Get(), MF_MT_FRAME_RATE, + &frame_rate_numerator, + &frame_rate_denominator)) || + !frame_rate_denominator) { + continue; + } + + float frame_rate = + static_cast(frame_rate_numerator) / frame_rate_denominator; + if (frame_rate < minimum_accepted_framerate) { + continue; + } + + uint32_t frame_width; + uint32_t frame_height; + if (SUCCEEDED(MFGetAttributeSize(media_type.Get(), MF_MT_FRAME_SIZE, + &frame_width, &frame_height))) { + // Update target mediatype + if (frame_height <= max_height && + (best_width < frame_width || best_height < frame_height || + best_framerate < frame_rate)) { + media_type.CopyTo(target_media_type); + best_width = frame_width; + best_height = frame_height; + best_framerate = frame_rate; + } + } + } + + if (target_frame_width && target_frame_height) { + *target_frame_width = best_width; + *target_frame_height = best_height; + } + + return *target_media_type != nullptr; +} + +HRESULT CaptureControllerImpl::FindBaseMediaTypes() { + if (!IsInitialized()) { + return E_FAIL; + } + + ComPtr source; + HRESULT hr = capture_engine_->GetSource(&source); + if (FAILED(hr)) { + return hr; + } + + // Find base media type for previewing. + if (!FindBestMediaType( + (DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_VIDEO_PREVIEW, + source.Get(), base_preview_media_type_.GetAddressOf(), + GetMaxPreviewHeight(), &preview_frame_width_, + &preview_frame_height_)) { + return E_FAIL; + } + + // Find base media type for record and photo capture. + if (!FindBestMediaType( + (DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_VIDEO_RECORD, + source.Get(), base_capture_media_type_.GetAddressOf(), 0xffffffff, + nullptr, nullptr)) { + return E_FAIL; + } + + return S_OK; +} + +void CaptureControllerImpl::StartRecord(const std::string& file_path, + int64_t max_video_duration_ms) { + assert(capture_engine_); + + if (!IsInitialized()) { + return OnRecordStarted(false, + "Camera not initialized. Camera should be " + "disposed and reinitialized."); + } + + if (!base_capture_media_type_) { + // Enumerates mediatypes and finds media type for video capture. + if (FAILED(FindBaseMediaTypes())) { + return OnRecordStarted(false, "Failed to initialize video recording"); + } + } + + if (!record_handler_) { + record_handler_ = std::make_unique(record_audio_); + } else if (!record_handler_->CanStart()) { + return OnRecordStarted( + false, + "Recording cannot be started. Previous recording must be stopped " + "first."); + } + + // Check MF_CAPTURE_ENGINE_RECORD_STARTED event handling for response + // process. + if (!record_handler_->StartRecord(file_path, max_video_duration_ms, + capture_engine_.Get(), + base_capture_media_type_.Get())) { + // Destroy record handler on error cases to make sure state is resetted. + record_handler_ = nullptr; + return OnRecordStarted(false, "Failed to start video recording"); + } +} + +void CaptureControllerImpl::StopRecord() { + assert(capture_controller_listener_); + + if (!IsInitialized()) { + return OnRecordStopped(false, + "Camera not initialized. Camera should be " + "disposed and reinitialized."); + } + + if (!record_handler_ && !record_handler_->CanStop()) { + return OnRecordStopped(false, "Recording cannot be stopped."); + } + + // Check MF_CAPTURE_ENGINE_RECORD_STOPPED event handling for response + // process. + if (!record_handler_->StopRecord(capture_engine_.Get())) { + // Destroy record handler on error cases to make sure state is resetted. + record_handler_ = nullptr; + return OnRecordStopped(false, "Failed to stop video recording"); + } +} + +// Stops timed recording. Called internally when requested time is passed. +// Check MF_CAPTURE_ENGINE_RECORD_STOPPED event handling for response process. +void CaptureControllerImpl::StopTimedRecord() { + assert(capture_controller_listener_); + if (!record_handler_ || !record_handler_->IsTimedRecording()) { + return; + } + + if (!record_handler_->StopRecord(capture_engine_.Get())) { + // Destroy record handler on error cases to make sure state is resetted. + record_handler_ = nullptr; + return capture_controller_listener_->OnVideoRecordFailed( + "Failed to record video"); + } +} + +// Starts capturing preview frames using preview handler +// After first frame is captured, OnPreviewStarted is called +void CaptureControllerImpl::StartPreview() { + assert(capture_engine_callback_handler_); + assert(capture_engine_); + assert(texture_handler_); + + if (!IsInitialized() || !texture_handler_) { + return OnPreviewStarted(false, + "Camera not initialized. Camera should be " + "disposed and reinitialized."); + } + + if (!base_preview_media_type_) { + // Enumerates mediatypes and finds media type for video capture. + if (FAILED(FindBaseMediaTypes())) { + return OnPreviewStarted(false, "Failed to initialize video preview"); + } + } + + texture_handler_->UpdateTextureSize(preview_frame_width_, + preview_frame_height_); + + if (!preview_handler_) { + preview_handler_ = std::make_unique(); + } else if (preview_handler_->IsInitialized()) { + return OnPreviewStarted(true, ""); + } else { + return OnPreviewStarted(false, "Preview already exists"); + } + + // Check MF_CAPTURE_ENGINE_PREVIEW_STARTED event handling for response + // process. + if (!preview_handler_->StartPreview(capture_engine_.Get(), + base_preview_media_type_.Get(), + capture_engine_callback_handler_.Get())) { + // Destroy preview handler on error cases to make sure state is resetted. + preview_handler_ = nullptr; + return OnPreviewStarted(false, "Failed to start video preview"); + } +} + +// Stops preview. Called by destructor +// Use PausePreview and ResumePreview methods to for +// pausing and resuming the preview. +// Check MF_CAPTURE_ENGINE_PREVIEW_STOPPED event handling for response +// process. +void CaptureControllerImpl::StopPreview() { + assert(capture_engine_); + + if (!IsInitialized() && !preview_handler_) { + return; + } + + // Requests to stop preview. + preview_handler_->StopPreview(capture_engine_.Get()); +} + +// Marks preview as paused. +// When preview is paused, captured frames are not processed for preview +// and flutter texture is not updated +void CaptureControllerImpl::PausePreview() { + assert(capture_controller_listener_); + + if (!preview_handler_ && !preview_handler_->IsInitialized()) { + return capture_controller_listener_->OnPausePreviewFailed( + "Preview not started"); + } + + if (preview_handler_->PausePreview()) { + capture_controller_listener_->OnPausePreviewSucceeded(); + } else { + capture_controller_listener_->OnPausePreviewFailed( + "Failed to pause preview"); + } +} + +// Marks preview as not paused. +// When preview is not paused, captured frames are processed for preview +// and flutter texture is updated. +void CaptureControllerImpl::ResumePreview() { + assert(capture_controller_listener_); + + if (!preview_handler_ && !preview_handler_->IsInitialized()) { + return capture_controller_listener_->OnResumePreviewFailed( + "Preview not started"); + } + + if (preview_handler_->ResumePreview()) { + capture_controller_listener_->OnResumePreviewSucceeded(); + } else { + capture_controller_listener_->OnResumePreviewFailed( + "Failed to pause preview"); + } +} + +// Handles capture engine events. +// Called via IMFCaptureEngineOnEventCallback implementation. +// Implements CaptureEngineObserver::OnEvent. +void CaptureControllerImpl::OnEvent(IMFMediaEvent* event) { + if (!IsInitialized() && + capture_engine_state_ != CaptureEngineState::kInitializing) { + return; + } + + GUID extended_type_guid; + if (SUCCEEDED(event->GetExtendedType(&extended_type_guid))) { + std::string error; + + HRESULT event_hr; + if (FAILED(event->GetStatus(&event_hr))) { + return; + } + + if (FAILED(event_hr)) { + // Reads system error + _com_error err(event_hr); + error = Utf8FromUtf16(err.ErrorMessage()); + } + + if (extended_type_guid == MF_CAPTURE_ENGINE_ERROR) { + OnCaptureEngineError(event_hr, error); + } else if (extended_type_guid == MF_CAPTURE_ENGINE_INITIALIZED) { + OnCaptureEngineInitialized(SUCCEEDED(event_hr), error); + } else if (extended_type_guid == MF_CAPTURE_ENGINE_PREVIEW_STARTED) { + // Preview is marked as started after first frame is captured. + // This is because, CaptureEngine might inform that preview is started + // even if error is thrown right after. + } else if (extended_type_guid == MF_CAPTURE_ENGINE_PREVIEW_STOPPED) { + OnPreviewStopped(SUCCEEDED(event_hr), error); + } else if (extended_type_guid == MF_CAPTURE_ENGINE_RECORD_STARTED) { + OnRecordStarted(SUCCEEDED(event_hr), error); + } else if (extended_type_guid == MF_CAPTURE_ENGINE_RECORD_STOPPED) { + OnRecordStopped(SUCCEEDED(event_hr), error); + } else if (extended_type_guid == MF_CAPTURE_ENGINE_PHOTO_TAKEN) { + OnPicture(SUCCEEDED(event_hr), error); + } else if (extended_type_guid == MF_CAPTURE_ENGINE_CAMERA_STREAM_BLOCKED) { + // TODO: Inform capture state to flutter. + } else if (extended_type_guid == + MF_CAPTURE_ENGINE_CAMERA_STREAM_UNBLOCKED) { + // TODO: Inform capture state to flutter. + } + } +} + +// Handles Picture event and informs CaptureControllerListener. +void CaptureControllerImpl::OnPicture(bool success, const std::string& error) { + if (success && photo_handler_) { + if (capture_controller_listener_) { + std::string path = photo_handler_->GetPhotoPath(); + capture_controller_listener_->OnTakePictureSucceeded(path); + } + photo_handler_->OnPhotoTaken(); + } else { + if (capture_controller_listener_) { + capture_controller_listener_->OnTakePictureFailed(error); + } + // Destroy photo handler on error cases to make sure state is resetted. + photo_handler_ = nullptr; + } +} + +// Handles CaptureEngineInitialized event and informs +// CaptureControllerListener. +void CaptureControllerImpl::OnCaptureEngineInitialized( + bool success, const std::string& error) { + if (capture_controller_listener_) { + // Create texture handler and register new texture. + texture_handler_ = std::make_unique(texture_registrar_); + + int64_t texture_id = texture_handler_->RegisterTexture(); + if (texture_id >= 0) { + capture_controller_listener_->OnCreateCaptureEngineSucceeded(texture_id); + capture_engine_state_ = CaptureEngineState::kInitialized; + } else { + capture_controller_listener_->OnCreateCaptureEngineFailed( + "Failed to create texture_id"); + // Reset state + ResetCaptureController(); + } + } +} + +// Handles CaptureEngineError event and informs CaptureControllerListener. +void CaptureControllerImpl::OnCaptureEngineError(HRESULT hr, + const std::string& error) { + if (capture_controller_listener_) { + capture_controller_listener_->OnCaptureError(error); + } + + // TODO: If MF_CAPTURE_ENGINE_ERROR is returned, + // should capture controller be reinitialized automatically? +} + +// Handles PreviewStarted event and informs CaptureControllerListener. +// This should be called only after first frame has been received or +// in error cases. +void CaptureControllerImpl::OnPreviewStarted(bool success, + const std::string& error) { + if (preview_handler_ && success) { + preview_handler_->OnPreviewStarted(); + } else { + // Destroy preview handler on error cases to make sure state is resetted. + preview_handler_ = nullptr; + } + + if (capture_controller_listener_) { + if (success && preview_frame_width_ > 0 && preview_frame_height_ > 0) { + capture_controller_listener_->OnStartPreviewSucceeded( + preview_frame_width_, preview_frame_height_); + } else { + capture_controller_listener_->OnStartPreviewFailed(error); + } + } +}; + +// Handles PreviewStopped event. +void CaptureControllerImpl::OnPreviewStopped(bool success, + const std::string& error) { + // Preview handler is destroyed if preview is stopped as it + // does not have any use anymore. + preview_handler_ = nullptr; +}; + +// Handles RecordStarted event and informs CaptureControllerListener. +void CaptureControllerImpl::OnRecordStarted(bool success, + const std::string& error) { + if (success && record_handler_) { + record_handler_->OnRecordStarted(); + if (capture_controller_listener_) { + capture_controller_listener_->OnStartRecordSucceeded(); + } + } else { + if (capture_controller_listener_) { + capture_controller_listener_->OnStartRecordFailed(error); + } + + // Destroy record handler on error cases to make sure state is resetted. + record_handler_ = nullptr; + } +}; + +// Handles RecordStopped event and informs CaptureControllerListener. +void CaptureControllerImpl::OnRecordStopped(bool success, + const std::string& error) { + if (capture_controller_listener_ && record_handler_) { + // Always calls OnStopRecord listener methods + // to handle separate stop record request for timed records. + + if (success) { + std::string path = record_handler_->GetRecordPath(); + capture_controller_listener_->OnStopRecordSucceeded(path); + if (record_handler_->IsTimedRecording()) { + capture_controller_listener_->OnVideoRecordSucceeded( + path, (record_handler_->GetRecordedDuration() / 1000)); + } + } else { + capture_controller_listener_->OnStopRecordFailed(error); + if (record_handler_->IsTimedRecording()) { + capture_controller_listener_->OnVideoRecordFailed(error); + } + } + } + + if (success && record_handler_) { + record_handler_->OnRecordStopped(); + } else { + // Destroy record handler on error cases to make sure state is resetted. + record_handler_ = nullptr; + } +} + +// Updates texture handlers buffer with given data. +// Called via IMFCaptureEngineOnSampleCallback implementation. +// Implements CaptureEngineObserver::UpdateBuffer. +bool CaptureControllerImpl::UpdateBuffer(uint8_t* buffer, + uint32_t data_length) { + if (!texture_handler_) { + return false; + } + return texture_handler_->UpdateBuffer(buffer, data_length); +} + +// Handles capture time update from each processed frame. +// Stops timed recordings if requested recording duration has passed. +// Called via IMFCaptureEngineOnSampleCallback implementation. +// Implements CaptureEngineObserver::UpdateCaptureTime. +void CaptureControllerImpl::UpdateCaptureTime(uint64_t capture_time_us) { + if (!IsInitialized()) { + return; + } + + if (preview_handler_ && preview_handler_->IsStarting()) { + // Informs that first frame is captured succeffully and preview has + // started. + OnPreviewStarted(true, ""); + } + + // Checks if max_video_duration_ms is passed. + if (record_handler_) { + record_handler_->UpdateRecordingTime(capture_time_us); + if (record_handler_->ShouldStopTimedRecording()) { + StopTimedRecord(); + } + } +} + +} // namespace camera_windows diff --git a/packages/camera/camera_windows/windows/capture_controller.h b/packages/camera/camera_windows/windows/capture_controller.h new file mode 100644 index 000000000000..34e378109d8f --- /dev/null +++ b/packages/camera/camera_windows/windows/capture_controller.h @@ -0,0 +1,292 @@ +// 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. + +#ifndef PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_CAPTURE_CONTROLLER_H_ +#define PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_CAPTURE_CONTROLLER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "capture_controller_listener.h" +#include "capture_engine_listener.h" +#include "photo_handler.h" +#include "preview_handler.h" +#include "record_handler.h" +#include "texture_handler.h" + +namespace camera_windows { +using flutter::TextureRegistrar; +using Microsoft::WRL::ComPtr; + +// Camera resolution presets. Used to request a capture resolution. +enum class ResolutionPreset { + // Automatic resolution, uses the highest resolution available. + kAuto, + // 240p (320x240) + kLow, + // 480p (720x480) + kMedium, + // 720p (1280x720) + kHigh, + // 1080p (1920x1080) + kVeryHigh, + // 2160p (4096x2160) + kUltraHigh, + // The highest resolution available. + kMax, +}; + +// Camera capture engine state. +// +// On creation, |CaptureControllers| start in state |kNotInitialized|. +// On initialization, the capture controller transitions to the |kInitializing| +// and then |kInitialized| state. +enum class CaptureEngineState { kNotInitialized, kInitializing, kInitialized }; + +// Interface for a class that enumerates video capture device sources. +class VideoCaptureDeviceEnumerator { + private: + virtual bool EnumerateVideoCaptureDeviceSources(IMFActivate*** devices, + UINT32* count) = 0; +}; + +// Interface implemented by capture controllers. +// +// Capture controllers are used to capture video streams or still photos from +// their associated |Camera|. +class CaptureController { + public: + CaptureController() {} + virtual ~CaptureController() = default; + + // Disallow copy and move. + CaptureController(const CaptureController&) = delete; + CaptureController& operator=(const CaptureController&) = delete; + + // Initializes the capture controller with the specified device id. + // + // texture_registrar: Pointer to Flutter TextureRegistrar instance. Used to + // register texture for capture preview. + // device_id: A string that holds information of camera device id to + // be captured. + // record_audio: A boolean value telling if audio should be captured on + // video recording. + // resolution_preset: Maximum capture resolution height. + virtual void InitCaptureDevice(TextureRegistrar* texture_registrar, + const std::string& device_id, + bool record_audio, + ResolutionPreset resolution_preset) = 0; + + // Returns preview frame width + virtual uint32_t GetPreviewWidth() const = 0; + + // Returns preview frame height + virtual uint32_t GetPreviewHeight() const = 0; + + // Starts the preview. + virtual void StartPreview() = 0; + + // Pauses the preview. + virtual void PausePreview() = 0; + + // Resumes the preview. + virtual void ResumePreview() = 0; + + // Starts recording video. + virtual void StartRecord(const std::string& file_path, + int64_t max_video_duration_ms) = 0; + + // Stops the current video recording. + virtual void StopRecord() = 0; + + // Captures a still photo. + virtual void TakePicture(const std::string& file_path) = 0; +}; + +// Concrete implementation of the |CaptureController| interface. +// +// Handles the video preview stream via a |PreviewHandler| instance, video +// capture via a |RecordHandler| instance, and still photo capture via a +// |PhotoHandler| instance. +class CaptureControllerImpl : public CaptureController, + public CaptureEngineObserver { + public: + static bool EnumerateVideoCaptureDeviceSources(IMFActivate*** devices, + UINT32* count); + + explicit CaptureControllerImpl(CaptureControllerListener* listener); + virtual ~CaptureControllerImpl(); + + // Disallow copy and move. + CaptureControllerImpl(const CaptureControllerImpl&) = delete; + CaptureControllerImpl& operator=(const CaptureControllerImpl&) = delete; + + // CaptureController + void InitCaptureDevice(TextureRegistrar* texture_registrar, + const std::string& device_id, bool record_audio, + ResolutionPreset resolution_preset) override; + uint32_t GetPreviewWidth() const override { return preview_frame_width_; } + uint32_t GetPreviewHeight() const override { return preview_frame_height_; } + void StartPreview() override; + void PausePreview() override; + void ResumePreview() override; + void StartRecord(const std::string& file_path, + int64_t max_video_duration_ms) override; + void StopRecord() override; + void TakePicture(const std::string& file_path) override; + + // CaptureEngineObserver + void OnEvent(IMFMediaEvent* event) override; + bool IsReadyForSample() const override { + return capture_engine_state_ == CaptureEngineState::kInitialized && + preview_handler_ && preview_handler_->IsRunning(); + } + bool UpdateBuffer(uint8_t* data, uint32_t data_length) override; + void UpdateCaptureTime(uint64_t capture_time) override; + + // Sets capture engine, for testing purposes. + void SetCaptureEngine(IMFCaptureEngine* capture_engine) { + capture_engine_ = capture_engine; + } + + // Sets video source, for testing purposes. + void SetVideoSource(IMFMediaSource* video_source) { + video_source_ = video_source; + } + + // Sets audio source, for testing purposes. + void SetAudioSource(IMFMediaSource* audio_source) { + audio_source_ = audio_source; + } + + private: + // Helper function to return initialized state as boolean; + bool IsInitialized() const { + return capture_engine_state_ == CaptureEngineState::kInitialized; + } + + // Resets capture controller state. + // This is called if capture engine creation fails or is disposed. + void ResetCaptureController(); + + // Returns max preview height calculated from resolution present. + uint32_t GetMaxPreviewHeight() const; + + // Uses first audio source to capture audio. + // Note: Enumerating audio sources via platform interface is not supported. + HRESULT CreateDefaultAudioCaptureSource(); + + // Initializes video capture source from camera device. + HRESULT CreateVideoCaptureSourceForDevice(const std::string& video_device_id); + + // Creates DX11 Device and D3D Manager. + HRESULT CreateD3DManagerWithDX11Device(); + + // Initializes capture engine object. + HRESULT CreateCaptureEngine(); + + // Enumerates video_sources media types and finds out best resolution + // for preview and video capture. + HRESULT FindBaseMediaTypes(); + + // Stops timed video record. Called internally when record handler when max + // recording time is exceeded. + void StopTimedRecord(); + + // Stops preview. Called internally on camera reset and dispose. + void StopPreview(); + + // Handles capture engine initalization event. + void OnCaptureEngineInitialized(bool success, const std::string& error); + + // Handles capture engine errors. + void OnCaptureEngineError(HRESULT hr, const std::string& error); + + // Handles picture events. + void OnPicture(bool success, const std::string& error); + + // Handles preview started events. + void OnPreviewStarted(bool success, const std::string& error); + + // Handles preview stopped events. + void OnPreviewStopped(bool success, const std::string& error); + + // Handles record started events. + void OnRecordStarted(bool success, const std::string& error); + + // Handles record stopped events. + void OnRecordStopped(bool success, const std::string& error); + + bool media_foundation_started_ = false; + bool record_audio_ = false; + uint32_t preview_frame_width_ = 0; + uint32_t preview_frame_height_ = 0; + UINT dx_device_reset_token_ = 0; + std::unique_ptr record_handler_; + std::unique_ptr preview_handler_; + std::unique_ptr photo_handler_; + std::unique_ptr texture_handler_; + CaptureControllerListener* capture_controller_listener_; + + std::string video_device_id_; + CaptureEngineState capture_engine_state_ = + CaptureEngineState::kNotInitialized; + ResolutionPreset resolution_preset_ = ResolutionPreset::kMedium; + ComPtr capture_engine_; + ComPtr capture_engine_callback_handler_; + ComPtr dxgi_device_manager_; + ComPtr dx11_device_; + ComPtr base_capture_media_type_; + ComPtr base_preview_media_type_; + ComPtr video_source_; + ComPtr audio_source_; + + TextureRegistrar* texture_registrar_ = nullptr; +}; + +// Inferface for factory classes that create |CaptureController| instances. +class CaptureControllerFactory { + public: + CaptureControllerFactory() {} + virtual ~CaptureControllerFactory() = default; + + // Disallow copy and move. + CaptureControllerFactory(const CaptureControllerFactory&) = delete; + CaptureControllerFactory& operator=(const CaptureControllerFactory&) = delete; + + // Create and return a |CaptureController| that makes callbacks on the + // specified |CaptureControllerListener|, which must not be null. + virtual std::unique_ptr CreateCaptureController( + CaptureControllerListener* listener) = 0; +}; + +// Concreate implementation of |CaptureControllerFactory|. +class CaptureControllerFactoryImpl : public CaptureControllerFactory { + public: + CaptureControllerFactoryImpl() {} + virtual ~CaptureControllerFactoryImpl() = default; + + // Disallow copy and move. + CaptureControllerFactoryImpl(const CaptureControllerFactoryImpl&) = delete; + CaptureControllerFactoryImpl& operator=(const CaptureControllerFactoryImpl&) = + delete; + + std::unique_ptr CreateCaptureController( + CaptureControllerListener* listener) override { + return std::make_unique(listener); + } +}; + +} // namespace camera_windows + +#endif // PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_CAPTURE_CONTROLLER_H_ diff --git a/packages/camera/camera_windows/windows/capture_controller_listener.h b/packages/camera/camera_windows/windows/capture_controller_listener.h new file mode 100644 index 000000000000..0e713ea7af18 --- /dev/null +++ b/packages/camera/camera_windows/windows/capture_controller_listener.h @@ -0,0 +1,104 @@ +// 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. + +#ifndef PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_CAPTURE_CONTROLLER_LISTENER_H_ +#define PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_CAPTURE_CONTROLLER_LISTENER_H_ + +#include + +namespace camera_windows { + +// Interface for classes that receives callbacks on events from the associated +// |CaptureController|. +class CaptureControllerListener { + public: + virtual ~CaptureControllerListener() = default; + + // Called by CaptureController on successful capture engine initialization. + // + // texture_id: A 64bit integer id registered by TextureRegistrar + virtual void OnCreateCaptureEngineSucceeded(int64_t texture_id) = 0; + + // Called by CaptureController if initializing the capture engine fails. + // + // error: A string describing the error. + virtual void OnCreateCaptureEngineFailed(const std::string& error) = 0; + + // Called by CaptureController on successfully started preview. + // + // width: Preview frame width. + // height: Preview frame height. + virtual void OnStartPreviewSucceeded(int32_t width, int32_t height) = 0; + + // Called by CaptureController if starting the preview fails. + // + // error: A string describing the error. + virtual void OnStartPreviewFailed(const std::string& error) = 0; + + // Called by CaptureController on successfully paused preview. + virtual void OnPausePreviewSucceeded() = 0; + + // Called by CaptureController if pausing the preview fails. + // + // error: A string describing the error. + virtual void OnPausePreviewFailed(const std::string& error) = 0; + + // Called by CaptureController on successfully resumed preview. + virtual void OnResumePreviewSucceeded() = 0; + + // Called by CaptureController if resuming the preview fails. + // + // error: A string describing the error. + virtual void OnResumePreviewFailed(const std::string& error) = 0; + + // Called by CaptureController on successfully started recording. + virtual void OnStartRecordSucceeded() = 0; + + // Called by CaptureController if starting the recording fails. + // + // error: A string describing the error. + virtual void OnStartRecordFailed(const std::string& error) = 0; + + // Called by CaptureController on successfully stopped recording. + // + // file_path: Filesystem path of the recorded video file. + virtual void OnStopRecordSucceeded(const std::string& file_path) = 0; + + // Called by CaptureController if stopping the recording fails. + // + // error: A string describing the error. + virtual void OnStopRecordFailed(const std::string& error) = 0; + + // Called by CaptureController on successfully captured picture. + // + // file_path: Filesystem path of the captured image. + virtual void OnTakePictureSucceeded(const std::string& file_path) = 0; + + // Called by CaptureController if taking picture fails. + // + // error: A string describing the error. + virtual void OnTakePictureFailed(const std::string& error) = 0; + + // Called by CaptureController when timed recording is successfully recorded. + // + // file_path: Filesystem path of the captured image. + // video_duration: Duration of recorded video in milliseconds. + virtual void OnVideoRecordSucceeded(const std::string& file_path, + int64_t video_duration_ms) = 0; + + // Called by CaptureController if timed recording fails. + // + // error: A string describing the error. + virtual void OnVideoRecordFailed(const std::string& error) = 0; + + // Called by CaptureController if capture engine returns error. + // For example when camera is disconnected while on use. + // + // error: A string describing the error. + virtual void OnCaptureError(const std::string& error) = 0; +}; + +} // namespace camera_windows + +#endif // PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_CAPTURE_CONTROLLER_LISTENER_H_ diff --git a/packages/camera/camera_windows/windows/capture_device_info.cpp b/packages/camera/camera_windows/windows/capture_device_info.cpp new file mode 100644 index 000000000000..446056a71c44 --- /dev/null +++ b/packages/camera/camera_windows/windows/capture_device_info.cpp @@ -0,0 +1,29 @@ +// 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. + +#include "capture_device_info.h" + +#include +#include + +namespace camera_windows { +std::string CaptureDeviceInfo::GetUniqueDeviceName() const { + return display_name_ + " <" + device_id_ + ">"; +} + +bool CaptureDeviceInfo::ParseDeviceInfoFromCameraName( + const std::string& camera_name) { + size_t delimeter_index = camera_name.rfind(' ', camera_name.length()); + if (delimeter_index != std::string::npos) { + auto deviceInfo = std::make_unique(); + display_name_ = camera_name.substr(0, delimeter_index); + device_id_ = camera_name.substr(delimeter_index + 2, + camera_name.length() - delimeter_index - 3); + return true; + } + + return false; +} + +} // namespace camera_windows diff --git a/packages/camera/camera_windows/windows/capture_device_info.h b/packages/camera/camera_windows/windows/capture_device_info.h new file mode 100644 index 000000000000..63ffa8571092 --- /dev/null +++ b/packages/camera/camera_windows/windows/capture_device_info.h @@ -0,0 +1,49 @@ +// 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. + +#ifndef PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_CAPTURE_DEVICE_INFO_H_ +#define PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_CAPTURE_DEVICE_INFO_H_ + +#include + +namespace camera_windows { + +// Name and device ID information for a capture device. +class CaptureDeviceInfo { + public: + CaptureDeviceInfo() {} + virtual ~CaptureDeviceInfo() = default; + + // Disallow copy and move. + CaptureDeviceInfo(const CaptureDeviceInfo&) = delete; + CaptureDeviceInfo& operator=(const CaptureDeviceInfo&) = delete; + + // Build unique device name from display name and device id. + // Format: "display_name ". + std::string GetUniqueDeviceName() const; + + // Parses display name and device id from unique device name format. + // Format: "display_name ". + bool CaptureDeviceInfo::ParseDeviceInfoFromCameraName( + const std::string& camera_name); + + // Updates display name. + void SetDisplayName(const std::string& display_name) { + display_name_ = display_name; + } + + // Updates device id. + void SetDeviceID(const std::string& device_id) { device_id_ = device_id; } + + // Returns device id. + std::string GetDeviceId() const { return device_id_; } + + private: + std::string display_name_; + std::string device_id_; +}; + +} // namespace camera_windows + +#endif // PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_CAPTURE_DEVICE_INFO_H_ diff --git a/packages/camera/camera_windows/windows/capture_engine_listener.cpp b/packages/camera/camera_windows/windows/capture_engine_listener.cpp new file mode 100644 index 000000000000..5425b388287a --- /dev/null +++ b/packages/camera/camera_windows/windows/capture_engine_listener.cpp @@ -0,0 +1,90 @@ + +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "capture_engine_listener.h" + +#include +#include + +namespace camera_windows { + +using Microsoft::WRL::ComPtr; + +// IUnknown +STDMETHODIMP_(ULONG) CaptureEngineListener::AddRef() { + return InterlockedIncrement(&ref_); +} + +// IUnknown +STDMETHODIMP_(ULONG) +CaptureEngineListener::Release() { + LONG ref = InterlockedDecrement(&ref_); + if (ref == 0) { + delete this; + } + return ref; +} + +// IUnknown +STDMETHODIMP_(HRESULT) +CaptureEngineListener::QueryInterface(const IID& riid, void** ppv) { + *ppv = nullptr; + + if (riid == IID_IMFCaptureEngineOnEventCallback) { + *ppv = static_cast(this); + ((IUnknown*)*ppv)->AddRef(); + return S_OK; + } else if (riid == IID_IMFCaptureEngineOnSampleCallback) { + *ppv = static_cast(this); + ((IUnknown*)*ppv)->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + +STDMETHODIMP CaptureEngineListener::OnEvent(IMFMediaEvent* event) { + if (observer_) { + observer_->OnEvent(event); + } + return S_OK; +} + +// IMFCaptureEngineOnSampleCallback +HRESULT CaptureEngineListener::OnSample(IMFSample* sample) { + HRESULT hr = S_OK; + + if (this->observer_ && sample) { + LONGLONG raw_time_stamp = 0; + // Receives the presentation time, in 100-nanosecond units. + sample->GetSampleTime(&raw_time_stamp); + + // Report time in microseconds. + this->observer_->UpdateCaptureTime( + static_cast(raw_time_stamp / 10)); + + if (!this->observer_->IsReadyForSample()) { + // No texture target available or not previewing, just return status. + return hr; + } + + ComPtr buffer; + hr = sample->ConvertToContiguousBuffer(&buffer); + + // Draw the frame. + if (SUCCEEDED(hr) && buffer) { + DWORD max_length = 0; + DWORD current_length = 0; + uint8_t* data; + if (SUCCEEDED(buffer->Lock(&data, &max_length, ¤t_length))) { + this->observer_->UpdateBuffer(data, current_length); + } + hr = buffer->Unlock(); + } + } + return hr; +} + +} // namespace camera_windows diff --git a/packages/camera/camera_windows/windows/capture_engine_listener.h b/packages/camera/camera_windows/windows/capture_engine_listener.h new file mode 100644 index 000000000000..081e3ea0f764 --- /dev/null +++ b/packages/camera/camera_windows/windows/capture_engine_listener.h @@ -0,0 +1,69 @@ +// 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. + +#ifndef PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_CAPTURE_ENGINE_LISTENER_H_ +#define PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_CAPTURE_ENGINE_LISTENER_H_ + +#include + +#include +#include + +namespace camera_windows { + +// A class that implements callbacks for events from a |CaptureEngineListener|. +class CaptureEngineObserver { + public: + virtual ~CaptureEngineObserver() = default; + + // Returns true if sample can be processed. + virtual bool IsReadyForSample() const = 0; + + // Handles Capture Engine media events. + virtual void OnEvent(IMFMediaEvent* event) = 0; + + // Updates texture buffer + virtual bool UpdateBuffer(uint8_t* data, uint32_t new_length) = 0; + + // Handles capture timestamps updates. + // Used to stop timed recordings when recorded time is exceeded. + virtual void UpdateCaptureTime(uint64_t capture_time) = 0; +}; + +// Listener for Windows Media Foundation capture engine events and samples. +// +// Events are redirected to observers for processing. Samples are preprosessed +// and sent to the associated observer if it is ready to process samples. +class CaptureEngineListener : public IMFCaptureEngineOnSampleCallback, + public IMFCaptureEngineOnEventCallback { + public: + CaptureEngineListener(CaptureEngineObserver* observer) : observer_(observer) { + assert(observer); + } + + ~CaptureEngineListener() {} + + // Disallow copy and move. + CaptureEngineListener(const CaptureEngineListener&) = delete; + CaptureEngineListener& operator=(const CaptureEngineListener&) = delete; + + // IUnknown + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + STDMETHODIMP_(HRESULT) QueryInterface(const IID& riid, void** ppv); + + // IMFCaptureEngineOnEventCallback + STDMETHODIMP OnEvent(IMFMediaEvent* pEvent); + + // IMFCaptureEngineOnSampleCallback + STDMETHODIMP_(HRESULT) OnSample(IMFSample* pSample); + + private: + CaptureEngineObserver* observer_; + volatile ULONG ref_ = 0; +}; + +} // namespace camera_windows + +#endif // PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_CAPTURE_ENGINE_LISTENER_H_ diff --git a/packages/camera/camera_windows/windows/com_heap_ptr.h b/packages/camera/camera_windows/windows/com_heap_ptr.h new file mode 100644 index 000000000000..a314ed3c8878 --- /dev/null +++ b/packages/camera/camera_windows/windows/com_heap_ptr.h @@ -0,0 +1,66 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_COMHEAPPTR_H_ +#define PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_COMHEAPPTR_H_ + +#include + +#include + +namespace camera_windows { +// Wrapper for COM object for automatic memory release support +// Destructor uses CoTaskMemFree to release memory allocations. +template +class ComHeapPtr { + public: + ComHeapPtr() : p_obj_(nullptr) {} + ComHeapPtr(T* p_obj) : p_obj_(p_obj) {} + + // Frees memory on destruction. + ~ComHeapPtr() { Free(); } + + // Prevent copying / ownership transfer as not currently needed. + ComHeapPtr(ComHeapPtr const&) = delete; + ComHeapPtr& operator=(ComHeapPtr const&) = delete; + + // Returns the pointer to the memory. + operator T*() { return p_obj_; } + + // Returns the pointer to the memory. + T* operator->() { + assert(p_obj_ != nullptr); + return p_obj_; + } + + // Returns the pointer to the memory. + const T* operator->() const { + assert(p_obj_ != nullptr); + return p_obj_; + } + + // Returns the pointer to the memory. + T** operator&() { + // Wrapped object must be nullptr to avoid memory leaks. + // Object can be released with Reset(nullptr). + assert(p_obj_ == nullptr); + return &p_obj_; + } + + // Frees the memory pointed to, and sets the pointer to nullptr. + void Free() { + if (p_obj_) { + CoTaskMemFree(p_obj_); + } + p_obj_ = nullptr; + } + + private: + // Pointer to memory. + T* p_obj_; +}; + +} // namespace camera_windows + +#endif // PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_COMHEAPPTR_H_ diff --git a/packages/camera/camera_windows/windows/include/camera_windows/camera_windows.h b/packages/camera/camera_windows/windows/include/camera_windows/camera_windows.h new file mode 100644 index 000000000000..b1e28b8aa8df --- /dev/null +++ b/packages/camera/camera_windows/windows/include/camera_windows/camera_windows.h @@ -0,0 +1,27 @@ +// 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. + +#ifndef PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_INCLUDE_CAMERA_WINDOWS_CAMERA_WINDOWS_H_ +#define PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_INCLUDE_CAMERA_WINDOWS_CAMERA_WINDOWS_H_ + +#include + +#ifdef FLUTTER_PLUGIN_IMPL +#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport) +#else +#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport) +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +FLUTTER_PLUGIN_EXPORT void CameraWindowsRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_INCLUDE_CAMERA_WINDOWS_CAMERA_WINDOWS_H_ diff --git a/packages/camera/camera_windows/windows/photo_handler.cpp b/packages/camera/camera_windows/windows/photo_handler.cpp new file mode 100644 index 000000000000..10df230c2cf2 --- /dev/null +++ b/packages/camera/camera_windows/windows/photo_handler.cpp @@ -0,0 +1,141 @@ +// 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. + +#include "photo_handler.h" + +#include +#include +#include + +#include + +#include "capture_engine_listener.h" +#include "string_utils.h" + +namespace camera_windows { + +using Microsoft::WRL::ComPtr; + +// Initializes media type for photo capture for jpeg images. +HRESULT BuildMediaTypeForPhotoCapture(IMFMediaType* src_media_type, + IMFMediaType** photo_media_type, + GUID image_format) { + assert(src_media_type); + ComPtr new_media_type; + + HRESULT hr = MFCreateMediaType(&new_media_type); + if (FAILED(hr)) { + return hr; + } + + // Clones everything from original media type. + hr = src_media_type->CopyAllItems(new_media_type.Get()); + if (FAILED(hr)) { + return hr; + } + + hr = new_media_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Image); + if (FAILED(hr)) { + return hr; + } + + hr = new_media_type->SetGUID(MF_MT_SUBTYPE, image_format); + if (FAILED(hr)) { + return hr; + } + + new_media_type.CopyTo(photo_media_type); + return hr; +} + +HRESULT PhotoHandler::InitPhotoSink(IMFCaptureEngine* capture_engine, + IMFMediaType* base_media_type) { + assert(capture_engine); + assert(base_media_type); + + HRESULT hr = S_OK; + + if (photo_sink_) { + // If photo sink already exists, only update output filename. + hr = photo_sink_->SetOutputFileName(Utf16FromUtf8(file_path_).c_str()); + + if (FAILED(hr)) { + photo_sink_ = nullptr; + } + + return hr; + } + + ComPtr photo_media_type; + ComPtr capture_sink; + + // Get sink with photo type. + hr = + capture_engine->GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_PHOTO, &capture_sink); + if (FAILED(hr)) { + return hr; + } + + hr = capture_sink.As(&photo_sink_); + if (FAILED(hr)) { + photo_sink_ = nullptr; + return hr; + } + + hr = photo_sink_->RemoveAllStreams(); + if (FAILED(hr)) { + photo_sink_ = nullptr; + return hr; + } + + hr = BuildMediaTypeForPhotoCapture(base_media_type, + photo_media_type.GetAddressOf(), + GUID_ContainerFormatJpeg); + + if (FAILED(hr)) { + photo_sink_ = nullptr; + return hr; + } + + DWORD photo_sink_stream_index; + hr = photo_sink_->AddStream( + (DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_PHOTO, + photo_media_type.Get(), nullptr, &photo_sink_stream_index); + if (FAILED(hr)) { + photo_sink_ = nullptr; + return hr; + } + + hr = photo_sink_->SetOutputFileName(Utf16FromUtf8(file_path_).c_str()); + if (FAILED(hr)) { + photo_sink_ = nullptr; + return hr; + } + + return hr; +} + +bool PhotoHandler::TakePhoto(const std::string& file_path, + IMFCaptureEngine* capture_engine, + IMFMediaType* base_media_type) { + assert(!file_path.empty()); + assert(capture_engine); + assert(base_media_type); + + file_path_ = file_path; + + if (FAILED(InitPhotoSink(capture_engine, base_media_type))) { + return false; + } + + photo_state_ = PhotoState::kTakingPhoto; + return SUCCEEDED(capture_engine->TakePhoto()); +} + +void PhotoHandler::OnPhotoTaken() { + assert(photo_state_ == PhotoState::kTakingPhoto); + photo_state_ = PhotoState::kIdle; +} + +} // namespace camera_windows diff --git a/packages/camera/camera_windows/windows/photo_handler.h b/packages/camera/camera_windows/windows/photo_handler.h new file mode 100644 index 000000000000..ef0d98bfc45f --- /dev/null +++ b/packages/camera/camera_windows/windows/photo_handler.h @@ -0,0 +1,80 @@ +// 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. + +#ifndef PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_PHOTO_HANDLER_H_ +#define PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_PHOTO_HANDLER_H_ + +#include +#include +#include + +#include +#include + +#include "capture_engine_listener.h" + +namespace camera_windows { +using Microsoft::WRL::ComPtr; + +// Various states that the photo handler can be in. +// +// When created, the handler is in |kNotStarted| state and transtions in +// sequential order through the states. +enum class PhotoState { + kNotStarted, + kIdle, + kTakingPhoto, +}; + +// Handles photo sink initialization and tracks photo capture states. +class PhotoHandler { + public: + PhotoHandler() {} + virtual ~PhotoHandler() = default; + + // Prevent copying. + PhotoHandler(PhotoHandler const&) = delete; + PhotoHandler& operator=(PhotoHandler const&) = delete; + + // Initializes photo sink if not initialized and requests the capture engine + // to take photo. + // + // Sets photo state to: kTakingPhoto. + // Returns false if photo cannot be taken. + // + // capture_engine: A pointer to capture engine instance. + // Called to take the photo. + // base_media_type: A pointer to base media type used as a base + // for the actual photo capture media type. + // file_path: A string that hold file path for photo capture. + bool TakePhoto(const std::string& file_path, IMFCaptureEngine* capture_engine, + IMFMediaType* base_media_type); + + // Set the photo handler recording state to: kIdel. + void OnPhotoTaken(); + + // Returns true if photo state is kIdle. + bool IsInitialized() const { return photo_state_ == PhotoState::kIdle; } + + // Returns true if photo state is kTakingPhoto. + bool IsTakingPhoto() const { + return photo_state_ == PhotoState::kTakingPhoto; + } + + // Returns the filesystem path of the captured photo. + std::string GetPhotoPath() const { return file_path_; } + + private: + // Initializes record sink for video file capture. + HRESULT InitPhotoSink(IMFCaptureEngine* capture_engine, + IMFMediaType* base_media_type); + + std::string file_path_; + PhotoState photo_state_ = PhotoState::kNotStarted; + ComPtr photo_sink_; +}; + +} // namespace camera_windows + +#endif // PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_PHOTO_HANDLER_H_ diff --git a/packages/camera/camera_windows/windows/preview_handler.cpp b/packages/camera/camera_windows/windows/preview_handler.cpp new file mode 100644 index 000000000000..d7fb2721259c --- /dev/null +++ b/packages/camera/camera_windows/windows/preview_handler.cpp @@ -0,0 +1,164 @@ +// 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. + +#include "preview_handler.h" + +#include +#include + +#include + +#include "capture_engine_listener.h" +#include "string_utils.h" + +namespace camera_windows { + +using Microsoft::WRL::ComPtr; + +// Initializes media type for video preview. +HRESULT BuildMediaTypeForVideoPreview(IMFMediaType* src_media_type, + IMFMediaType** preview_media_type) { + assert(src_media_type); + ComPtr new_media_type; + + HRESULT hr = MFCreateMediaType(&new_media_type); + if (FAILED(hr)) { + return hr; + } + + // Clones everything from original media type. + hr = src_media_type->CopyAllItems(new_media_type.Get()); + if (FAILED(hr)) { + return hr; + } + + // Changes subtype to MFVideoFormat_RGB32. + hr = new_media_type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32); + if (FAILED(hr)) { + return hr; + } + + hr = new_media_type->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE); + if (FAILED(hr)) { + return hr; + } + + new_media_type.CopyTo(preview_media_type); + + return hr; +} + +HRESULT PreviewHandler::InitPreviewSink( + IMFCaptureEngine* capture_engine, IMFMediaType* base_media_type, + CaptureEngineListener* sample_callback) { + assert(capture_engine); + assert(base_media_type); + assert(sample_callback); + + HRESULT hr = S_OK; + + if (preview_sink_) { + // Preview sink already initialized. + return hr; + } + + ComPtr preview_media_type; + ComPtr capture_sink; + + // Get sink with preview type. + hr = capture_engine->GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_PREVIEW, + &capture_sink); + if (FAILED(hr)) { + return hr; + } + + hr = capture_sink.As(&preview_sink_); + if (FAILED(hr)) { + preview_sink_ = nullptr; + return hr; + } + + hr = preview_sink_->RemoveAllStreams(); + if (FAILED(hr)) { + preview_sink_ = nullptr; + return hr; + } + + hr = BuildMediaTypeForVideoPreview(base_media_type, + preview_media_type.GetAddressOf()); + + if (FAILED(hr)) { + preview_sink_ = nullptr; + return hr; + } + + DWORD preview_sink_stream_index; + hr = preview_sink_->AddStream( + (DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_VIDEO_PREVIEW, + preview_media_type.Get(), nullptr, &preview_sink_stream_index); + + if (FAILED(hr)) { + return hr; + } + + hr = preview_sink_->SetSampleCallback(preview_sink_stream_index, + sample_callback); + + if (FAILED(hr)) { + preview_sink_ = nullptr; + return hr; + } + + return hr; +} + +bool PreviewHandler::StartPreview(IMFCaptureEngine* capture_engine, + IMFMediaType* base_media_type, + CaptureEngineListener* sample_callback) { + assert(capture_engine); + assert(base_media_type); + + if (FAILED( + InitPreviewSink(capture_engine, base_media_type, sample_callback))) { + return false; + } + + preview_state_ = PreviewState::kStarting; + return SUCCEEDED(capture_engine->StartPreview()); +} + +bool PreviewHandler::StopPreview(IMFCaptureEngine* capture_engine) { + if (preview_state_ == PreviewState::kStarting || + preview_state_ == PreviewState::kRunning || + preview_state_ == PreviewState::kPaused) { + preview_state_ = PreviewState::kStopping; + return SUCCEEDED(capture_engine->StopPreview()); + } + return false; +} + +bool PreviewHandler::PausePreview() { + if (preview_state_ != PreviewState::kRunning) { + return false; + } + preview_state_ = PreviewState::kPaused; + return true; +} + +bool PreviewHandler::ResumePreview() { + if (preview_state_ != PreviewState::kPaused) { + return false; + } + preview_state_ = PreviewState::kRunning; + return true; +} + +void PreviewHandler::OnPreviewStarted() { + assert(preview_state_ == PreviewState::kStarting); + if (preview_state_ == PreviewState::kStarting) { + preview_state_ = PreviewState::kRunning; + } +} + +} // namespace camera_windows diff --git a/packages/camera/camera_windows/windows/preview_handler.h b/packages/camera/camera_windows/windows/preview_handler.h new file mode 100644 index 000000000000..97b85fc28568 --- /dev/null +++ b/packages/camera/camera_windows/windows/preview_handler.h @@ -0,0 +1,103 @@ +// 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. + +#ifndef PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_PREVIEW_HANDLER_H_ +#define PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_PREVIEW_HANDLER_H_ + +#include +#include +#include + +#include +#include + +#include "capture_engine_listener.h" + +namespace camera_windows { +using Microsoft::WRL::ComPtr; + +// States the preview handler can be in. +// +// When created, the handler starts in |kNotStarted| state and mostly +// transitions in sequential order of the states. When the preview is running, +// it can be set to the |kPaused| state and later resumed to |kRunning| state. +enum class PreviewState { + kNotStarted, + kStarting, + kRunning, + kPaused, + kStopping +}; + +// Handler for a camera's video preview. +// +// Handles preview sink initialization and manages the state of the video +// preview. +class PreviewHandler { + public: + PreviewHandler() {} + virtual ~PreviewHandler() = default; + + // Prevent copying. + PreviewHandler(PreviewHandler const&) = delete; + PreviewHandler& operator=(PreviewHandler const&) = delete; + + // Initializes preview sink and requests capture engine to start previewing. + // Sets preview state to: starting. + // Returns false if recording cannot be started. + // + // capture_engine: A pointer to capture engine instance. Used to start + // the actual recording. + // base_media_type: A pointer to base media type used as a base + // for the actual video capture media type. + // sample_callback: A pointer to capture engine listener. + // This is set as sample callback for preview sink. + bool StartPreview(IMFCaptureEngine* capture_engine, + IMFMediaType* base_media_type, + CaptureEngineListener* sample_callback); + + // Stops existing recording. + // Returns false if recording cannot be stopped. + // + // capture_engine: A pointer to capture engine instance. Used to stop + // the ongoing recording. + bool StopPreview(IMFCaptureEngine* capture_engine); + + // Set the preview handler recording state to: paused. + bool PausePreview(); + + // Set the preview handler recording state to: running. + bool ResumePreview(); + + // Set the preview handler recording state to: running. + void OnPreviewStarted(); + + // Returns true if preview state is running or paused. + bool IsInitialized() const { + return preview_state_ == PreviewState::kRunning && + preview_state_ == PreviewState::kPaused; + } + + // Returns true if preview state is running. + bool IsRunning() const { return preview_state_ == PreviewState::kRunning; } + + // Return true if preview state is paused. + bool IsPaused() const { return preview_state_ == PreviewState::kPaused; } + + // Returns true if preview state is starting. + bool IsStarting() const { return preview_state_ == PreviewState::kStarting; } + + private: + // Initializes record sink for video file capture. + HRESULT InitPreviewSink(IMFCaptureEngine* capture_engine, + IMFMediaType* base_media_type, + CaptureEngineListener* sample_callback); + + PreviewState preview_state_ = PreviewState::kNotStarted; + ComPtr preview_sink_; +}; + +} // namespace camera_windows + +#endif // PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_PREVIEW_HANDLER_H_ diff --git a/packages/camera/camera_windows/windows/record_handler.cpp b/packages/camera/camera_windows/windows/record_handler.cpp new file mode 100644 index 000000000000..1cb258e162a5 --- /dev/null +++ b/packages/camera/camera_windows/windows/record_handler.cpp @@ -0,0 +1,260 @@ +// 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. + +#include "record_handler.h" + +#include +#include + +#include + +#include "string_utils.h" + +namespace camera_windows { + +using Microsoft::WRL::ComPtr; + +// Initializes media type for video capture. +HRESULT BuildMediaTypeForVideoCapture(IMFMediaType* src_media_type, + IMFMediaType** video_record_media_type, + GUID capture_format) { + assert(src_media_type); + ComPtr new_media_type; + + HRESULT hr = MFCreateMediaType(&new_media_type); + if (FAILED(hr)) { + return hr; + } + + // Clones everything from original media type. + hr = src_media_type->CopyAllItems(new_media_type.Get()); + if (FAILED(hr)) { + return hr; + } + + hr = new_media_type->SetGUID(MF_MT_SUBTYPE, capture_format); + if (FAILED(hr)) { + return hr; + } + + new_media_type.CopyTo(video_record_media_type); + return S_OK; +} + +// Queries interface object from collection. +template +HRESULT GetCollectionObject(IMFCollection* pCollection, DWORD index, + Q** ppObj) { + ComPtr pUnk; + HRESULT hr = pCollection->GetElement(index, pUnk.GetAddressOf()); + if (FAILED(hr)) { + return hr; + } + return pUnk->QueryInterface(IID_PPV_ARGS(ppObj)); +} + +// Initializes media type for audo capture. +HRESULT BuildMediaTypeForAudioCapture(IMFMediaType** audio_record_media_type) { + ComPtr audio_output_attributes; + ComPtr src_media_type; + ComPtr new_media_type; + ComPtr available_output_types; + DWORD mt_count = 0; + + HRESULT hr = MFCreateAttributes(&audio_output_attributes, 1); + if (FAILED(hr)) { + return hr; + } + + // Enumerates only low latency audio outputs. + hr = audio_output_attributes->SetUINT32(MF_LOW_LATENCY, TRUE); + if (FAILED(hr)) { + return hr; + } + + DWORD mft_flags = (MFT_ENUM_FLAG_ALL & (~MFT_ENUM_FLAG_FIELDOFUSE)) | + MFT_ENUM_FLAG_SORTANDFILTER; + + hr = MFTranscodeGetAudioOutputAvailableTypes( + MFAudioFormat_AAC, mft_flags, audio_output_attributes.Get(), + available_output_types.GetAddressOf()); + if (FAILED(hr)) { + return hr; + } + + hr = GetCollectionObject(available_output_types.Get(), 0, + src_media_type.GetAddressOf()); + if (FAILED(hr)) { + return hr; + } + + hr = available_output_types->GetElementCount(&mt_count); + if (FAILED(hr)) { + return hr; + } + + if (mt_count == 0) { + // No sources found, mark process as failure. + return E_FAIL; + } + + // Create new media type to copy original media type to. + hr = MFCreateMediaType(&new_media_type); + if (FAILED(hr)) { + return hr; + } + + hr = src_media_type->CopyAllItems(new_media_type.Get()); + if (FAILED(hr)) { + return hr; + } + + new_media_type.CopyTo(audio_record_media_type); + return hr; +} + +HRESULT RecordHandler::InitRecordSink(IMFCaptureEngine* capture_engine, + IMFMediaType* base_media_type) { + assert(!file_path_.empty()); + assert(capture_engine); + assert(base_media_type); + + HRESULT hr = S_OK; + if (record_sink_) { + // If record sink already exists, only update output filename. + hr = record_sink_->SetOutputFileName(Utf16FromUtf8(file_path_).c_str()); + + if (FAILED(hr)) { + record_sink_ = nullptr; + } + return hr; + } + + ComPtr video_record_media_type; + ComPtr capture_sink; + + // Gets sink from capture engine with record type. + + hr = capture_engine->GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_RECORD, + &capture_sink); + if (FAILED(hr)) { + return hr; + } + + hr = capture_sink.As(&record_sink_); + if (FAILED(hr)) { + return hr; + } + + // Removes existing streams if available. + hr = record_sink_->RemoveAllStreams(); + if (FAILED(hr)) { + return hr; + } + + hr = BuildMediaTypeForVideoCapture(base_media_type, + video_record_media_type.GetAddressOf(), + MFVideoFormat_H264); + if (FAILED(hr)) { + return hr; + } + + DWORD video_record_sink_stream_index; + hr = record_sink_->AddStream( + (DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_VIDEO_RECORD, + video_record_media_type.Get(), nullptr, &video_record_sink_stream_index); + if (FAILED(hr)) { + return hr; + } + + if (record_audio_) { + ComPtr audio_record_media_type; + HRESULT audio_capture_hr = S_OK; + audio_capture_hr = + BuildMediaTypeForAudioCapture(audio_record_media_type.GetAddressOf()); + + if (SUCCEEDED(audio_capture_hr)) { + DWORD audio_record_sink_stream_index; + hr = record_sink_->AddStream( + (DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_AUDIO, + audio_record_media_type.Get(), nullptr, + &audio_record_sink_stream_index); + } + + if (FAILED(hr)) { + return hr; + } + } + + hr = record_sink_->SetOutputFileName(Utf16FromUtf8(file_path_).c_str()); + + return hr; +} + +bool RecordHandler::StartRecord(const std::string& file_path, + int64_t max_duration, + IMFCaptureEngine* capture_engine, + IMFMediaType* base_media_type) { + assert(!file_path.empty()); + assert(capture_engine); + assert(base_media_type); + + type_ = max_duration < 0 ? RecordingType::kContinuous : RecordingType::kTimed; + max_video_duration_ms_ = max_duration; + file_path_ = file_path; + recording_start_timestamp_us_ = -1; + recording_duration_us_ = 0; + + if (FAILED(InitRecordSink(capture_engine, base_media_type))) { + return false; + } + + recording_state_ = RecordState::kStarting; + capture_engine->StartRecord(); + + return true; +} + +bool RecordHandler::StopRecord(IMFCaptureEngine* capture_engine) { + if (recording_state_ == RecordState::kRunning) { + recording_state_ = RecordState::kStopping; + HRESULT hr = capture_engine->StopRecord(true, false); + return SUCCEEDED(hr); + } + return false; +} + +void RecordHandler::OnRecordStarted() { + if (recording_state_ == RecordState::kStarting) { + recording_state_ = RecordState::kRunning; + } +} + +void RecordHandler::OnRecordStopped() { + if (recording_state_ == RecordState::kStopping) { + file_path_ = ""; + recording_start_timestamp_us_ = -1; + recording_duration_us_ = 0; + max_video_duration_ms_ = -1; + recording_state_ = RecordState::kNotStarted; + } +} + +void RecordHandler::UpdateRecordingTime(uint64_t timestamp) { + if (recording_start_timestamp_us_ < 0) { + recording_start_timestamp_us_ = timestamp; + } + + recording_duration_us_ = (timestamp - recording_start_timestamp_us_); +} + +bool RecordHandler::ShouldStopTimedRecording() const { + return type_ == RecordingType::kTimed && + recording_state_ == RecordState::kRunning && + max_video_duration_ms_ > 0 && + recording_duration_us_ >= + (static_cast(max_video_duration_ms_) * 1000); +} + +} // namespace camera_windows diff --git a/packages/camera/camera_windows/windows/record_handler.h b/packages/camera/camera_windows/windows/record_handler.h new file mode 100644 index 000000000000..0daa7f6546a1 --- /dev/null +++ b/packages/camera/camera_windows/windows/record_handler.h @@ -0,0 +1,118 @@ +// 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. + +#ifndef PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_RECORD_HANDLER_H_ +#define PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_RECORD_HANDLER_H_ + +#include +#include +#include + +#include +#include + +namespace camera_windows { +using Microsoft::WRL::ComPtr; + +enum class RecordingType { + // Recording continues until it is stopped with a separate stop command. + kContinuous, + // Recording stops automatically after requested record time is passed. + kTimed +}; + +// States that the record handler can be in. +// +// When created, the handler starts in |kNotStarted| state and transtions in +// sequential order through the states. +enum class RecordState { kNotStarted, kStarting, kRunning, kStopping }; + +// Handler for video recording via the camera. +// +// Handles record sink initialization and manages the state of video recording. +class RecordHandler { + public: + RecordHandler(bool record_audio) : record_audio_(record_audio) {} + virtual ~RecordHandler() = default; + + // Prevent copying. + RecordHandler(RecordHandler const&) = delete; + RecordHandler& operator=(RecordHandler const&) = delete; + + // Initializes record sink and requests capture engine to start recording. + // + // Sets record state to: starting. + // Returns false if recording cannot be started. + // + // file_path: A string that hold file path for video capture. + // max_duration: A int64 value of maximun recording duration. + // If value is -1 video recording is considered as + // a continuous recording. + // capture_engine: A pointer to capture engine instance. Used to start + // the actual recording. + // base_media_type: A pointer to base media type used as a base + // for the actual video capture media type. + bool StartRecord(const std::string& file_path, int64_t max_duration, + IMFCaptureEngine* capture_engine, + IMFMediaType* base_media_type); + + // Stops existing recording. + // Returns false if recording cannot be stopped. + // + // capture_engine: A pointer to capture engine instance. Used to stop + // the ongoing recording. + bool StopRecord(IMFCaptureEngine* capture_engine); + + // Set the record handler recording state to: running. + void OnRecordStarted(); + + // Resets the record handler state and + // sets recording state to: not started. + void OnRecordStopped(); + + // Returns true if recording type is continuous recording. + bool IsContinuousRecording() const { + return type_ == RecordingType::kContinuous; + } + + // Returns true if recording type is timed recording. + bool IsTimedRecording() const { return type_ == RecordingType::kTimed; } + + // Returns true if new recording can be started. + bool CanStart() const { return recording_state_ == RecordState::kNotStarted; } + + // Returns true if recording can be stopped. + bool CanStop() const { return recording_state_ == RecordState::kRunning; } + + // Returns the filesystem path of the video recording. + std::string GetRecordPath() const { return file_path_; } + + // Returns the duration of the video recording in microseconds. + uint64_t GetRecordedDuration() const { return recording_duration_us_; } + + // Calculates new recording time from capture timestamp. + void UpdateRecordingTime(uint64_t timestamp); + + // Returns true if recording time has exceeded the maximum duration for timed + // recordings. + bool ShouldStopTimedRecording() const; + + private: + // Initializes record sink for video file capture. + HRESULT InitRecordSink(IMFCaptureEngine* capture_engine, + IMFMediaType* base_media_type); + + bool record_audio_ = false; + int64_t max_video_duration_ms_ = -1; + int64_t recording_start_timestamp_us_ = -1; + uint64_t recording_duration_us_ = 0; + std::string file_path_; + RecordState recording_state_ = RecordState::kNotStarted; + RecordingType type_; + ComPtr record_sink_; +}; + +} // namespace camera_windows + +#endif // PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_RECORD_HANDLER_H_ diff --git a/packages/camera/camera_windows/windows/string_utils.cpp b/packages/camera/camera_windows/windows/string_utils.cpp new file mode 100644 index 000000000000..2e60e1bb01a7 --- /dev/null +++ b/packages/camera/camera_windows/windows/string_utils.cpp @@ -0,0 +1,60 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "string_utils.h" + +#include +#include + +#include + +namespace camera_windows { + +// Converts the given UTF-16 string to UTF-8. +std::string Utf8FromUtf16(const std::wstring& utf16_string) { + if (utf16_string.empty()) { + return std::string(); + } + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string.data(), + static_cast(utf16_string.length()), nullptr, 0, nullptr, nullptr); + if (target_length == 0) { + return std::string(); + } + std::string utf8_string; + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string.data(), + static_cast(utf16_string.length()), utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} + +// Converts the given UTF-8 string to UTF-16. +std::wstring Utf16FromUtf8(const std::string& utf8_string) { + if (utf8_string.empty()) { + return std::wstring(); + } + int target_length = + ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8_string.data(), + static_cast(utf8_string.length()), nullptr, 0); + if (target_length == 0) { + return std::wstring(); + } + std::wstring utf16_string; + utf16_string.resize(target_length); + int converted_length = + ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8_string.data(), + static_cast(utf8_string.length()), + utf16_string.data(), target_length); + if (converted_length == 0) { + return std::wstring(); + } + return utf16_string; +} + +} // namespace camera_windows diff --git a/packages/camera/camera_windows/windows/string_utils.h b/packages/camera/camera_windows/windows/string_utils.h new file mode 100644 index 000000000000..562c46a0feea --- /dev/null +++ b/packages/camera/camera_windows/windows/string_utils.h @@ -0,0 +1,22 @@ +// 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. + +#ifndef PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_STRING_UTILS_H_ +#define PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_STRING_UTILS_H_ + +#include + +#include + +namespace camera_windows { + +// Converts the given UTF-16 string to UTF-8. +std::string Utf8FromUtf16(const std::wstring& utf16_string); + +// Converts the given UTF-8 string to UTF-16. +std::wstring Utf16FromUtf8(const std::string& utf8_string); + +} // namespace camera_windows + +#endif // PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_STRING_UTILS_H_ diff --git a/packages/camera/camera_windows/windows/test/camera_plugin_test.cpp b/packages/camera/camera_windows/windows/test/camera_plugin_test.cpp new file mode 100644 index 000000000000..309268a1fb90 --- /dev/null +++ b/packages/camera/camera_windows/windows/test/camera_plugin_test.cpp @@ -0,0 +1,1010 @@ +// 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. + +#include "camera_plugin.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "mocks.h" + +namespace camera_windows { +namespace test { + +using flutter::EncodableMap; +using flutter::EncodableValue; +using ::testing::_; +using ::testing::DoAll; +using ::testing::EndsWith; +using ::testing::Eq; +using ::testing::Pointee; +using ::testing::Return; + +TEST(CameraPlugin, AvailableCamerasHandlerSuccessIfNoCameras) { + std::unique_ptr texture_registrar_ = + std::make_unique(); + std::unique_ptr messenger_ = + std::make_unique(); + std::unique_ptr camera_factory_ = + std::make_unique(); + std::unique_ptr result = + std::make_unique(); + + MockCameraPlugin plugin(texture_registrar_.get(), messenger_.get(), + std::move(camera_factory_)); + + EXPECT_CALL(plugin, EnumerateVideoCaptureDeviceSources) + .Times(1) + .WillOnce([](IMFActivate*** devices, UINT32* count) { + *count = 0U; + *devices = static_cast( + CoTaskMemAlloc(sizeof(IMFActivate*) * (*count))); + return true; + }); + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal).Times(1); + + plugin.HandleMethodCall( + flutter::MethodCall("availableCameras", + std::make_unique()), + std::move(result)); +} + +TEST(CameraPlugin, AvailableCamerasHandlerErrorIfFailsToEnumerateDevices) { + std::unique_ptr texture_registrar_ = + std::make_unique(); + std::unique_ptr messenger_ = + std::make_unique(); + std::unique_ptr camera_factory_ = + std::make_unique(); + std::unique_ptr result = + std::make_unique(); + + MockCameraPlugin plugin(texture_registrar_.get(), messenger_.get(), + std::move(camera_factory_)); + + EXPECT_CALL(plugin, EnumerateVideoCaptureDeviceSources) + .Times(1) + .WillOnce([](IMFActivate*** devices, UINT32* count) { return false; }); + + EXPECT_CALL(*result, ErrorInternal).Times(1); + EXPECT_CALL(*result, SuccessInternal).Times(0); + + plugin.HandleMethodCall( + flutter::MethodCall("availableCameras", + std::make_unique()), + std::move(result)); +} + +TEST(CameraPlugin, CreateHandlerCallsInitCamera) { + std::unique_ptr result = + std::make_unique(); + std::unique_ptr texture_registrar_ = + std::make_unique(); + std::unique_ptr messenger_ = + std::make_unique(); + std::unique_ptr camera_factory_ = + std::make_unique(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + + EXPECT_CALL(*camera, + HasPendingResultByType(Eq(PendingResultType::kCreateCamera))) + .Times(1) + .WillOnce(Return(false)); + + EXPECT_CALL(*camera, + AddPendingResult(Eq(PendingResultType::kCreateCamera), _)) + .Times(1) + .WillOnce([cam = camera.get()](PendingResultType type, + std::unique_ptr> result) { + cam->pending_result_ = std::move(result); + return true; + }); + EXPECT_CALL(*camera, InitCamera) + .Times(1) + .WillOnce([cam = camera.get()]( + flutter::TextureRegistrar* texture_registrar, + flutter::BinaryMessenger* messenger, bool record_audio, + ResolutionPreset resolution_preset) { + assert(cam->pending_result_); + return cam->pending_result_->Success(EncodableValue(1)); + }); + + // Move mocked camera to the factory to be passed + // for plugin with CreateCamera function. + camera_factory_->pending_camera_ = std::move(camera); + + EXPECT_CALL(*camera_factory_, CreateCamera(MOCK_DEVICE_ID)); + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(1)))); + + CameraPlugin plugin(texture_registrar_.get(), messenger_.get(), + std::move(camera_factory_)); + EncodableMap args = { + {EncodableValue("cameraName"), EncodableValue(MOCK_CAMERA_NAME)}, + {EncodableValue("resolutionPreset"), EncodableValue(nullptr)}, + {EncodableValue("enableAudio"), EncodableValue(true)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("create", + std::make_unique(EncodableMap(args))), + std::move(result)); +} + +TEST(CameraPlugin, CreateHandlerErrorOnInvalidDeviceId) { + std::unique_ptr result = + std::make_unique(); + std::unique_ptr texture_registrar_ = + std::make_unique(); + std::unique_ptr messenger_ = + std::make_unique(); + std::unique_ptr camera_factory_ = + std::make_unique(); + + CameraPlugin plugin(texture_registrar_.get(), messenger_.get(), + std::move(camera_factory_)); + EncodableMap args = { + {EncodableValue("cameraName"), EncodableValue(MOCK_INVALID_CAMERA_NAME)}, + {EncodableValue("resolutionPreset"), EncodableValue(nullptr)}, + {EncodableValue("enableAudio"), EncodableValue(true)}, + }; + + EXPECT_CALL(*result, ErrorInternal).Times(1); + + plugin.HandleMethodCall( + flutter::MethodCall("create", + std::make_unique(EncodableMap(args))), + std::move(result)); +} + +TEST(CameraPlugin, CreateHandlerErrorOnExistingDeviceId) { + std::unique_ptr first_create_result = + std::make_unique(); + std::unique_ptr second_create_result = + std::make_unique(); + std::unique_ptr texture_registrar_ = + std::make_unique(); + std::unique_ptr messenger_ = + std::make_unique(); + std::unique_ptr camera_factory_ = + std::make_unique(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + + EXPECT_CALL(*camera, + HasPendingResultByType(Eq(PendingResultType::kCreateCamera))) + .Times(1) + .WillOnce(Return(false)); + + EXPECT_CALL(*camera, + AddPendingResult(Eq(PendingResultType::kCreateCamera), _)) + .Times(1) + .WillOnce([cam = camera.get()](PendingResultType type, + std::unique_ptr> result) { + cam->pending_result_ = std::move(result); + return true; + }); + EXPECT_CALL(*camera, InitCamera) + .Times(1) + .WillOnce([cam = camera.get()]( + flutter::TextureRegistrar* texture_registrar, + flutter::BinaryMessenger* messenger, bool record_audio, + ResolutionPreset resolution_preset) { + assert(cam->pending_result_); + return cam->pending_result_->Success(EncodableValue(1)); + }); + + EXPECT_CALL(*camera, HasDeviceId(Eq(MOCK_DEVICE_ID))) + .Times(1) + .WillOnce([cam = camera.get()](std::string& device_id) { + return cam->device_id_ == device_id; + }); + + // Move mocked camera to the factory to be passed + // for plugin with CreateCamera function. + camera_factory_->pending_camera_ = std::move(camera); + + EXPECT_CALL(*camera_factory_, CreateCamera(MOCK_DEVICE_ID)); + + EXPECT_CALL(*first_create_result, ErrorInternal).Times(0); + EXPECT_CALL(*first_create_result, + SuccessInternal(Pointee(EncodableValue(1)))); + + CameraPlugin plugin(texture_registrar_.get(), messenger_.get(), + std::move(camera_factory_)); + EncodableMap args = { + {EncodableValue("cameraName"), EncodableValue(MOCK_CAMERA_NAME)}, + {EncodableValue("resolutionPreset"), EncodableValue(nullptr)}, + {EncodableValue("enableAudio"), EncodableValue(true)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("create", + std::make_unique(EncodableMap(args))), + std::move(first_create_result)); + + EXPECT_CALL(*second_create_result, ErrorInternal).Times(1); + EXPECT_CALL(*second_create_result, SuccessInternal).Times(0); + + plugin.HandleMethodCall( + flutter::MethodCall("create", + std::make_unique(EncodableMap(args))), + std::move(second_create_result)); +} + +TEST(CameraPlugin, InitializeHandlerCallStartPreview) { + int64_t mock_camera_id = 1234; + + std::unique_ptr initialize_result = + std::make_unique(); + + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + + std::unique_ptr capture_controller = + std::make_unique(); + + EXPECT_CALL(*camera, HasCameraId(Eq(mock_camera_id))) + .Times(1) + .WillOnce([cam = camera.get()](int64_t camera_id) { + return cam->camera_id_ == camera_id; + }); + + EXPECT_CALL(*camera, + HasPendingResultByType(Eq(PendingResultType::kInitialize))) + .Times(1) + .WillOnce(Return(false)); + + EXPECT_CALL(*camera, AddPendingResult(Eq(PendingResultType::kInitialize), _)) + .Times(1) + .WillOnce([cam = camera.get()](PendingResultType type, + std::unique_ptr> result) { + cam->pending_result_ = std::move(result); + return true; + }); + + EXPECT_CALL(*camera, GetCaptureController) + .Times(1) + .WillOnce([cam = camera.get()]() { + assert(cam->pending_result_); + return cam->capture_controller_.get(); + }); + + EXPECT_CALL(*capture_controller, StartPreview()) + .Times(1) + .WillOnce([cam = camera.get()]() { + assert(cam->pending_result_); + return cam->pending_result_->Success(); + }); + + camera->camera_id_ = mock_camera_id; + camera->capture_controller_ = std::move(capture_controller); + + MockCameraPlugin plugin(std::make_unique().get(), + std::make_unique().get(), + std::make_unique()); + + // Add mocked camera to plugins camera list. + plugin.AddCamera(std::move(camera)); + + EXPECT_CALL(*initialize_result, ErrorInternal).Times(0); + EXPECT_CALL(*initialize_result, SuccessInternal).Times(1); + + EncodableMap args = { + {EncodableValue("cameraId"), EncodableValue(mock_camera_id)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("initialize", + std::make_unique(EncodableMap(args))), + std::move(initialize_result)); +} + +TEST(CameraPlugin, InitializeHandlerErrorOnInvalidCameraId) { + int64_t mock_camera_id = 1234; + int64_t missing_camera_id = 5678; + + std::unique_ptr initialize_result = + std::make_unique(); + + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + + std::unique_ptr capture_controller = + std::make_unique(); + + EXPECT_CALL(*camera, HasCameraId) + .Times(1) + .WillOnce([cam = camera.get()](int64_t camera_id) { + return cam->camera_id_ == camera_id; + }); + + EXPECT_CALL(*camera, HasPendingResultByType).Times(0); + EXPECT_CALL(*camera, AddPendingResult).Times(0); + EXPECT_CALL(*camera, GetCaptureController).Times(0); + EXPECT_CALL(*capture_controller, StartPreview).Times(0); + + camera->camera_id_ = mock_camera_id; + + MockCameraPlugin plugin(std::make_unique().get(), + std::make_unique().get(), + std::make_unique()); + + // Add mocked camera to plugins camera list. + plugin.AddCamera(std::move(camera)); + + EXPECT_CALL(*initialize_result, ErrorInternal).Times(1); + EXPECT_CALL(*initialize_result, SuccessInternal).Times(0); + + EncodableMap args = { + {EncodableValue("cameraId"), EncodableValue(missing_camera_id)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("initialize", + std::make_unique(EncodableMap(args))), + std::move(initialize_result)); +} + +TEST(CameraPlugin, TakePictureHandlerCallsTakePictureWithPath) { + int64_t mock_camera_id = 1234; + + std::unique_ptr initialize_result = + std::make_unique(); + + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + + std::unique_ptr capture_controller = + std::make_unique(); + + EXPECT_CALL(*camera, HasCameraId(Eq(mock_camera_id))) + .Times(1) + .WillOnce([cam = camera.get()](int64_t camera_id) { + return cam->camera_id_ == camera_id; + }); + + EXPECT_CALL(*camera, + HasPendingResultByType(Eq(PendingResultType::kTakePicture))) + .Times(1) + .WillOnce(Return(false)); + + EXPECT_CALL(*camera, AddPendingResult(Eq(PendingResultType::kTakePicture), _)) + .Times(1) + .WillOnce([cam = camera.get()](PendingResultType type, + std::unique_ptr> result) { + cam->pending_result_ = std::move(result); + return true; + }); + + EXPECT_CALL(*camera, GetCaptureController) + .Times(1) + .WillOnce([cam = camera.get()]() { + assert(cam->pending_result_); + return cam->capture_controller_.get(); + }); + + EXPECT_CALL(*capture_controller, TakePicture(EndsWith(".jpeg"))) + .Times(1) + .WillOnce([cam = camera.get()](const std::string& file_path) { + assert(cam->pending_result_); + return cam->pending_result_->Success(); + }); + + camera->camera_id_ = mock_camera_id; + camera->capture_controller_ = std::move(capture_controller); + + MockCameraPlugin plugin(std::make_unique().get(), + std::make_unique().get(), + std::make_unique()); + + // Add mocked camera to plugins camera list. + plugin.AddCamera(std::move(camera)); + + EXPECT_CALL(*initialize_result, ErrorInternal).Times(0); + EXPECT_CALL(*initialize_result, SuccessInternal).Times(1); + + EncodableMap args = { + {EncodableValue("cameraId"), EncodableValue(mock_camera_id)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("takePicture", + std::make_unique(EncodableMap(args))), + std::move(initialize_result)); +} + +TEST(CameraPlugin, TakePictureHandlerErrorOnInvalidCameraId) { + int64_t mock_camera_id = 1234; + int64_t missing_camera_id = 5678; + + std::unique_ptr initialize_result = + std::make_unique(); + + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + + std::unique_ptr capture_controller = + std::make_unique(); + + EXPECT_CALL(*camera, HasCameraId) + .Times(1) + .WillOnce([cam = camera.get()](int64_t camera_id) { + return cam->camera_id_ == camera_id; + }); + + EXPECT_CALL(*camera, HasPendingResultByType).Times(0); + EXPECT_CALL(*camera, AddPendingResult).Times(0); + EXPECT_CALL(*camera, GetCaptureController).Times(0); + EXPECT_CALL(*capture_controller, TakePicture).Times(0); + + camera->camera_id_ = mock_camera_id; + + MockCameraPlugin plugin(std::make_unique().get(), + std::make_unique().get(), + std::make_unique()); + + // Add mocked camera to plugins camera list. + plugin.AddCamera(std::move(camera)); + + EXPECT_CALL(*initialize_result, ErrorInternal).Times(1); + EXPECT_CALL(*initialize_result, SuccessInternal).Times(0); + + EncodableMap args = { + {EncodableValue("cameraId"), EncodableValue(missing_camera_id)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("takePicture", + std::make_unique(EncodableMap(args))), + std::move(initialize_result)); +} + +TEST(CameraPlugin, StartVideoRecordingHandlerCallsStartRecordWithPath) { + int64_t mock_camera_id = 1234; + + std::unique_ptr initialize_result = + std::make_unique(); + + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + + std::unique_ptr capture_controller = + std::make_unique(); + + EXPECT_CALL(*camera, HasCameraId(Eq(mock_camera_id))) + .Times(1) + .WillOnce([cam = camera.get()](int64_t camera_id) { + return cam->camera_id_ == camera_id; + }); + + EXPECT_CALL(*camera, + HasPendingResultByType(Eq(PendingResultType::kStartRecord))) + .Times(1) + .WillOnce(Return(false)); + + EXPECT_CALL(*camera, AddPendingResult(Eq(PendingResultType::kStartRecord), _)) + .Times(1) + .WillOnce([cam = camera.get()](PendingResultType type, + std::unique_ptr> result) { + cam->pending_result_ = std::move(result); + return true; + }); + + EXPECT_CALL(*camera, GetCaptureController) + .Times(1) + .WillOnce([cam = camera.get()]() { + assert(cam->pending_result_); + return cam->capture_controller_.get(); + }); + + EXPECT_CALL(*capture_controller, StartRecord(EndsWith(".mp4"), -1)) + .Times(1) + .WillOnce([cam = camera.get()](const std::string& file_path, + int64_t max_video_duration_ms) { + assert(cam->pending_result_); + return cam->pending_result_->Success(); + }); + + camera->camera_id_ = mock_camera_id; + camera->capture_controller_ = std::move(capture_controller); + + MockCameraPlugin plugin(std::make_unique().get(), + std::make_unique().get(), + std::make_unique()); + + // Add mocked camera to plugins camera list. + plugin.AddCamera(std::move(camera)); + + EXPECT_CALL(*initialize_result, ErrorInternal).Times(0); + EXPECT_CALL(*initialize_result, SuccessInternal).Times(1); + + EncodableMap args = { + {EncodableValue("cameraId"), EncodableValue(mock_camera_id)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("startVideoRecording", + std::make_unique(EncodableMap(args))), + std::move(initialize_result)); +} + +TEST(CameraPlugin, + StartVideoRecordingHandlerCallsStartRecordWithPathAndCaptureDuration) { + int64_t mock_camera_id = 1234; + int32_t mock_video_duration = 100000; + + std::unique_ptr initialize_result = + std::make_unique(); + + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + + std::unique_ptr capture_controller = + std::make_unique(); + + EXPECT_CALL(*camera, HasCameraId(Eq(mock_camera_id))) + .Times(1) + .WillOnce([cam = camera.get()](int64_t camera_id) { + return cam->camera_id_ == camera_id; + }); + + EXPECT_CALL(*camera, + HasPendingResultByType(Eq(PendingResultType::kStartRecord))) + .Times(1) + .WillOnce(Return(false)); + + EXPECT_CALL(*camera, AddPendingResult(Eq(PendingResultType::kStartRecord), _)) + .Times(1) + .WillOnce([cam = camera.get()](PendingResultType type, + std::unique_ptr> result) { + cam->pending_result_ = std::move(result); + return true; + }); + + EXPECT_CALL(*camera, GetCaptureController) + .Times(1) + .WillOnce([cam = camera.get()]() { + assert(cam->pending_result_); + return cam->capture_controller_.get(); + }); + + EXPECT_CALL(*capture_controller, + StartRecord(EndsWith(".mp4"), Eq(mock_video_duration))) + .Times(1) + .WillOnce([cam = camera.get()](const std::string& file_path, + int64_t max_video_duration_ms) { + assert(cam->pending_result_); + return cam->pending_result_->Success(); + }); + + camera->camera_id_ = mock_camera_id; + camera->capture_controller_ = std::move(capture_controller); + + MockCameraPlugin plugin(std::make_unique().get(), + std::make_unique().get(), + std::make_unique()); + + // Add mocked camera to plugins camera list. + plugin.AddCamera(std::move(camera)); + + EXPECT_CALL(*initialize_result, ErrorInternal).Times(0); + EXPECT_CALL(*initialize_result, SuccessInternal).Times(1); + + EncodableMap args = { + {EncodableValue("cameraId"), EncodableValue(mock_camera_id)}, + {EncodableValue("maxVideoDuration"), EncodableValue(mock_video_duration)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("startVideoRecording", + std::make_unique(EncodableMap(args))), + std::move(initialize_result)); +} + +TEST(CameraPlugin, StartVideoRecordingHandlerErrorOnInvalidCameraId) { + int64_t mock_camera_id = 1234; + int64_t missing_camera_id = 5678; + + std::unique_ptr initialize_result = + std::make_unique(); + + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + + std::unique_ptr capture_controller = + std::make_unique(); + + EXPECT_CALL(*camera, HasCameraId) + .Times(1) + .WillOnce([cam = camera.get()](int64_t camera_id) { + return cam->camera_id_ == camera_id; + }); + + EXPECT_CALL(*camera, HasPendingResultByType).Times(0); + EXPECT_CALL(*camera, AddPendingResult).Times(0); + EXPECT_CALL(*camera, GetCaptureController).Times(0); + EXPECT_CALL(*capture_controller, StartRecord(_, -1)).Times(0); + + camera->camera_id_ = mock_camera_id; + + MockCameraPlugin plugin(std::make_unique().get(), + std::make_unique().get(), + std::make_unique()); + + // Add mocked camera to plugins camera list. + plugin.AddCamera(std::move(camera)); + + EXPECT_CALL(*initialize_result, ErrorInternal).Times(1); + EXPECT_CALL(*initialize_result, SuccessInternal).Times(0); + + EncodableMap args = { + {EncodableValue("cameraId"), EncodableValue(missing_camera_id)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("startVideoRecording", + std::make_unique(EncodableMap(args))), + std::move(initialize_result)); +} + +TEST(CameraPlugin, StopVideoRecordingHandlerCallsStopRecord) { + int64_t mock_camera_id = 1234; + + std::unique_ptr initialize_result = + std::make_unique(); + + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + + std::unique_ptr capture_controller = + std::make_unique(); + + EXPECT_CALL(*camera, HasCameraId(Eq(mock_camera_id))) + .Times(1) + .WillOnce([cam = camera.get()](int64_t camera_id) { + return cam->camera_id_ == camera_id; + }); + + EXPECT_CALL(*camera, + HasPendingResultByType(Eq(PendingResultType::kStopRecord))) + .Times(1) + .WillOnce(Return(false)); + + EXPECT_CALL(*camera, AddPendingResult(Eq(PendingResultType::kStopRecord), _)) + .Times(1) + .WillOnce([cam = camera.get()](PendingResultType type, + std::unique_ptr> result) { + cam->pending_result_ = std::move(result); + return true; + }); + + EXPECT_CALL(*camera, GetCaptureController) + .Times(1) + .WillOnce([cam = camera.get()]() { + assert(cam->pending_result_); + return cam->capture_controller_.get(); + }); + + EXPECT_CALL(*capture_controller, StopRecord) + .Times(1) + .WillOnce([cam = camera.get()]() { + assert(cam->pending_result_); + return cam->pending_result_->Success(); + }); + + camera->camera_id_ = mock_camera_id; + camera->capture_controller_ = std::move(capture_controller); + + MockCameraPlugin plugin(std::make_unique().get(), + std::make_unique().get(), + std::make_unique()); + + // Add mocked camera to plugins camera list. + plugin.AddCamera(std::move(camera)); + + EXPECT_CALL(*initialize_result, ErrorInternal).Times(0); + EXPECT_CALL(*initialize_result, SuccessInternal).Times(1); + + EncodableMap args = { + {EncodableValue("cameraId"), EncodableValue(mock_camera_id)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("stopVideoRecording", + std::make_unique(EncodableMap(args))), + std::move(initialize_result)); +} + +TEST(CameraPlugin, StopVideoRecordingHandlerErrorOnInvalidCameraId) { + int64_t mock_camera_id = 1234; + int64_t missing_camera_id = 5678; + + std::unique_ptr initialize_result = + std::make_unique(); + + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + + std::unique_ptr capture_controller = + std::make_unique(); + + EXPECT_CALL(*camera, HasCameraId) + .Times(1) + .WillOnce([cam = camera.get()](int64_t camera_id) { + return cam->camera_id_ == camera_id; + }); + + EXPECT_CALL(*camera, HasPendingResultByType).Times(0); + EXPECT_CALL(*camera, AddPendingResult).Times(0); + EXPECT_CALL(*camera, GetCaptureController).Times(0); + EXPECT_CALL(*capture_controller, StopRecord).Times(0); + + camera->camera_id_ = mock_camera_id; + + MockCameraPlugin plugin(std::make_unique().get(), + std::make_unique().get(), + std::make_unique()); + + // Add mocked camera to plugins camera list. + plugin.AddCamera(std::move(camera)); + + EXPECT_CALL(*initialize_result, ErrorInternal).Times(1); + EXPECT_CALL(*initialize_result, SuccessInternal).Times(0); + + EncodableMap args = { + {EncodableValue("cameraId"), EncodableValue(missing_camera_id)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("stopVideoRecording", + std::make_unique(EncodableMap(args))), + std::move(initialize_result)); +} + +TEST(CameraPlugin, ResumePreviewHandlerCallsResumePreview) { + int64_t mock_camera_id = 1234; + + std::unique_ptr initialize_result = + std::make_unique(); + + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + + std::unique_ptr capture_controller = + std::make_unique(); + + EXPECT_CALL(*camera, HasCameraId(Eq(mock_camera_id))) + .Times(1) + .WillOnce([cam = camera.get()](int64_t camera_id) { + return cam->camera_id_ == camera_id; + }); + + EXPECT_CALL(*camera, + HasPendingResultByType(Eq(PendingResultType::kResumePreview))) + .Times(1) + .WillOnce(Return(false)); + + EXPECT_CALL(*camera, + AddPendingResult(Eq(PendingResultType::kResumePreview), _)) + .Times(1) + .WillOnce([cam = camera.get()](PendingResultType type, + std::unique_ptr> result) { + cam->pending_result_ = std::move(result); + return true; + }); + + EXPECT_CALL(*camera, GetCaptureController) + .Times(1) + .WillOnce([cam = camera.get()]() { + assert(cam->pending_result_); + return cam->capture_controller_.get(); + }); + + EXPECT_CALL(*capture_controller, ResumePreview) + .Times(1) + .WillOnce([cam = camera.get()]() { + assert(cam->pending_result_); + return cam->pending_result_->Success(); + }); + + camera->camera_id_ = mock_camera_id; + camera->capture_controller_ = std::move(capture_controller); + + MockCameraPlugin plugin(std::make_unique().get(), + std::make_unique().get(), + std::make_unique()); + + // Add mocked camera to plugins camera list. + plugin.AddCamera(std::move(camera)); + + EXPECT_CALL(*initialize_result, ErrorInternal).Times(0); + EXPECT_CALL(*initialize_result, SuccessInternal).Times(1); + + EncodableMap args = { + {EncodableValue("cameraId"), EncodableValue(mock_camera_id)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("resumePreview", + std::make_unique(EncodableMap(args))), + std::move(initialize_result)); +} + +TEST(CameraPlugin, ResumePreviewHandlerErrorOnInvalidCameraId) { + int64_t mock_camera_id = 1234; + int64_t missing_camera_id = 5678; + + std::unique_ptr initialize_result = + std::make_unique(); + + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + + std::unique_ptr capture_controller = + std::make_unique(); + + EXPECT_CALL(*camera, HasCameraId) + .Times(1) + .WillOnce([cam = camera.get()](int64_t camera_id) { + return cam->camera_id_ == camera_id; + }); + + EXPECT_CALL(*camera, HasPendingResultByType).Times(0); + EXPECT_CALL(*camera, AddPendingResult).Times(0); + EXPECT_CALL(*camera, GetCaptureController).Times(0); + EXPECT_CALL(*capture_controller, ResumePreview).Times(0); + + camera->camera_id_ = mock_camera_id; + + MockCameraPlugin plugin(std::make_unique().get(), + std::make_unique().get(), + std::make_unique()); + + // Add mocked camera to plugins camera list. + plugin.AddCamera(std::move(camera)); + + EXPECT_CALL(*initialize_result, ErrorInternal).Times(1); + EXPECT_CALL(*initialize_result, SuccessInternal).Times(0); + + EncodableMap args = { + {EncodableValue("cameraId"), EncodableValue(missing_camera_id)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("resumePreview", + std::make_unique(EncodableMap(args))), + std::move(initialize_result)); +} + +TEST(CameraPlugin, PausePreviewHandlerCallsPausePreview) { + int64_t mock_camera_id = 1234; + + std::unique_ptr initialize_result = + std::make_unique(); + + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + + std::unique_ptr capture_controller = + std::make_unique(); + + EXPECT_CALL(*camera, HasCameraId(Eq(mock_camera_id))) + .Times(1) + .WillOnce([cam = camera.get()](int64_t camera_id) { + return cam->camera_id_ == camera_id; + }); + + EXPECT_CALL(*camera, + HasPendingResultByType(Eq(PendingResultType::kPausePreview))) + .Times(1) + .WillOnce(Return(false)); + + EXPECT_CALL(*camera, + AddPendingResult(Eq(PendingResultType::kPausePreview), _)) + .Times(1) + .WillOnce([cam = camera.get()](PendingResultType type, + std::unique_ptr> result) { + cam->pending_result_ = std::move(result); + return true; + }); + + EXPECT_CALL(*camera, GetCaptureController) + .Times(1) + .WillOnce([cam = camera.get()]() { + assert(cam->pending_result_); + return cam->capture_controller_.get(); + }); + + EXPECT_CALL(*capture_controller, PausePreview) + .Times(1) + .WillOnce([cam = camera.get()]() { + assert(cam->pending_result_); + return cam->pending_result_->Success(); + }); + + camera->camera_id_ = mock_camera_id; + camera->capture_controller_ = std::move(capture_controller); + + MockCameraPlugin plugin(std::make_unique().get(), + std::make_unique().get(), + std::make_unique()); + + // Add mocked camera to plugins camera list. + plugin.AddCamera(std::move(camera)); + + EXPECT_CALL(*initialize_result, ErrorInternal).Times(0); + EXPECT_CALL(*initialize_result, SuccessInternal).Times(1); + + EncodableMap args = { + {EncodableValue("cameraId"), EncodableValue(mock_camera_id)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("pausePreview", + std::make_unique(EncodableMap(args))), + std::move(initialize_result)); +} + +TEST(CameraPlugin, PausePreviewHandlerErrorOnInvalidCameraId) { + int64_t mock_camera_id = 1234; + int64_t missing_camera_id = 5678; + + std::unique_ptr initialize_result = + std::make_unique(); + + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + + std::unique_ptr capture_controller = + std::make_unique(); + + EXPECT_CALL(*camera, HasCameraId) + .Times(1) + .WillOnce([cam = camera.get()](int64_t camera_id) { + return cam->camera_id_ == camera_id; + }); + + EXPECT_CALL(*camera, HasPendingResultByType).Times(0); + EXPECT_CALL(*camera, AddPendingResult).Times(0); + EXPECT_CALL(*camera, GetCaptureController).Times(0); + EXPECT_CALL(*capture_controller, PausePreview).Times(0); + + camera->camera_id_ = mock_camera_id; + + MockCameraPlugin plugin(std::make_unique().get(), + std::make_unique().get(), + std::make_unique()); + + // Add mocked camera to plugins camera list. + plugin.AddCamera(std::move(camera)); + + EXPECT_CALL(*initialize_result, ErrorInternal).Times(1); + EXPECT_CALL(*initialize_result, SuccessInternal).Times(0); + + EncodableMap args = { + {EncodableValue("cameraId"), EncodableValue(missing_camera_id)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("pausePreview", + std::make_unique(EncodableMap(args))), + std::move(initialize_result)); +} + +} // namespace test +} // namespace camera_windows diff --git a/packages/camera/camera_windows/windows/test/camera_test.cpp b/packages/camera/camera_windows/windows/test/camera_test.cpp new file mode 100644 index 000000000000..899c1fdaea62 --- /dev/null +++ b/packages/camera/camera_windows/windows/test/camera_test.cpp @@ -0,0 +1,344 @@ +// 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. + +#include "camera.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "mocks.h" + +namespace camera_windows { +using ::testing::_; +using ::testing::Eq; +using ::testing::NiceMock; +using ::testing::Pointee; + +namespace test { + +TEST(Camera, InitCameraCreatesCaptureController) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller_factory = + std::make_unique(); + + EXPECT_CALL(*capture_controller_factory, CreateCaptureController) + .Times(1) + .WillOnce( + []() { return std::make_unique>(); }); + + EXPECT_TRUE(camera->GetCaptureController() == nullptr); + + // Init camera with mock capture controller factory + camera->InitCamera(std::move(capture_controller_factory), + std::make_unique().get(), + std::make_unique().get(), false, + ResolutionPreset::kAuto); + + EXPECT_TRUE(camera->GetCaptureController() != nullptr); +} + +TEST(Camera, AddPendingResultReturnsErrorForDuplicates) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr first_pending_result = + std::make_unique(); + std::unique_ptr second_pending_result = + std::make_unique(); + + EXPECT_CALL(*first_pending_result, ErrorInternal).Times(0); + EXPECT_CALL(*first_pending_result, SuccessInternal); + EXPECT_CALL(*second_pending_result, ErrorInternal).Times(1); + + camera->AddPendingResult(PendingResultType::kCreateCamera, + std::move(first_pending_result)); + + // This should fail + camera->AddPendingResult(PendingResultType::kCreateCamera, + std::move(second_pending_result)); + + // Mark pending result as succeeded + camera->OnCreateCaptureEngineSucceeded(0); +} + +TEST(Camera, OnCreateCaptureEngineSucceededReturnsCameraId) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + const int64_t texture_id = 12345; + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL( + *result, + SuccessInternal(Pointee(EncodableValue(EncodableMap( + {{EncodableValue("cameraId"), EncodableValue(texture_id)}}))))); + + camera->AddPendingResult(PendingResultType::kCreateCamera, std::move(result)); + + camera->OnCreateCaptureEngineSucceeded(texture_id); +} + +TEST(Camera, OnCreateCaptureEngineFailedReturnsError) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + std::string error_text = "error_text"; + + EXPECT_CALL(*result, SuccessInternal).Times(0); + EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); + + camera->AddPendingResult(PendingResultType::kCreateCamera, std::move(result)); + + camera->OnCreateCaptureEngineFailed(error_text); +} + +TEST(Camera, OnStartPreviewSucceededReturnsFrameSize) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + const int32_t width = 123; + const int32_t height = 456; + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL( + *result, + SuccessInternal(Pointee(EncodableValue(EncodableMap({ + {EncodableValue("previewWidth"), EncodableValue((float)width)}, + {EncodableValue("previewHeight"), EncodableValue((float)height)}, + }))))); + + camera->AddPendingResult(PendingResultType::kInitialize, std::move(result)); + + camera->OnStartPreviewSucceeded(width, height); +} + +TEST(Camera, OnStartPreviewFailedReturnsError) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + std::string error_text = "error_text"; + + EXPECT_CALL(*result, SuccessInternal).Times(0); + EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); + + camera->AddPendingResult(PendingResultType::kInitialize, std::move(result)); + + camera->OnStartPreviewFailed(error_text); +} + +TEST(Camera, OnPausePreviewSucceededReturnsSuccess) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal(nullptr)); + + camera->AddPendingResult(PendingResultType::kPausePreview, std::move(result)); + + camera->OnPausePreviewSucceeded(); +} + +TEST(Camera, OnPausePreviewFailedReturnsError) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + std::string error_text = "error_text"; + + EXPECT_CALL(*result, SuccessInternal).Times(0); + EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); + + camera->AddPendingResult(PendingResultType::kPausePreview, std::move(result)); + + camera->OnPausePreviewFailed(error_text); +} + +TEST(Camera, OnResumePreviewSucceededReturnsSuccess) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal(nullptr)); + + camera->AddPendingResult(PendingResultType::kResumePreview, + std::move(result)); + + camera->OnResumePreviewSucceeded(); +} + +TEST(Camera, OnResumePreviewFailedReturnsError) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + std::string error_text = "error_text"; + + EXPECT_CALL(*result, SuccessInternal).Times(0); + EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); + + camera->AddPendingResult(PendingResultType::kResumePreview, + std::move(result)); + + camera->OnResumePreviewFailed(error_text); +} + +TEST(Camera, OnStartRecordSucceededReturnsSuccess) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal(nullptr)); + + camera->AddPendingResult(PendingResultType::kStartRecord, std::move(result)); + + camera->OnStartRecordSucceeded(); +} + +TEST(Camera, OnStartRecordFailedReturnsError) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + std::string error_text = "error_text"; + + EXPECT_CALL(*result, SuccessInternal).Times(0); + EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); + + camera->AddPendingResult(PendingResultType::kStartRecord, std::move(result)); + + camera->OnStartRecordFailed(error_text); +} + +TEST(Camera, OnStopRecordSucceededReturnsSuccess) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + std::string file_path = "C:\temp\filename.mp4"; + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(file_path)))); + + camera->AddPendingResult(PendingResultType::kStopRecord, std::move(result)); + + camera->OnStopRecordSucceeded(file_path); +} + +TEST(Camera, OnStopRecordFailedReturnsError) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + std::string error_text = "error_text"; + + EXPECT_CALL(*result, SuccessInternal).Times(0); + EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); + + camera->AddPendingResult(PendingResultType::kStopRecord, std::move(result)); + + camera->OnStopRecordFailed(error_text); +} + +TEST(Camera, OnTakePictureSucceededReturnsSuccess) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + std::string file_path = "C:\temp\filename.jpeg"; + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(file_path)))); + + camera->AddPendingResult(PendingResultType::kTakePicture, std::move(result)); + + camera->OnTakePictureSucceeded(file_path); +} + +TEST(Camera, OnTakePictureFailedReturnsError) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + std::string error_text = "error_text"; + + EXPECT_CALL(*result, SuccessInternal).Times(0); + EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); + + camera->AddPendingResult(PendingResultType::kTakePicture, std::move(result)); + + camera->OnTakePictureFailed(error_text); +} + +TEST(Camera, OnVideoRecordSucceededInvokesCameraChannelEvent) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller_factory = + std::make_unique(); + + std::unique_ptr binary_messenger = + std::make_unique(); + + std::string file_path = "C:\temp\filename.mp4"; + int64_t camera_id = 12345; + std::string camera_channel = + std::string("plugins.flutter.io/camera_windows/camera") + + std::to_string(camera_id); + int64_t video_duration = 1000000; + + EXPECT_CALL(*capture_controller_factory, CreateCaptureController) + .Times(1) + .WillOnce( + []() { return std::make_unique>(); }); + + // TODO: test binary content. + // First time is video record success message, + // and second is camera closing message. + EXPECT_CALL(*binary_messenger, Send(Eq(camera_channel), _, _, _)).Times(2); + + // Init camera with mock capture controller factory + camera->InitCamera(std::move(capture_controller_factory), + std::make_unique().get(), + binary_messenger.get(), false, ResolutionPreset::kAuto); + + // Pass camera id for camera + camera->OnCreateCaptureEngineSucceeded(camera_id); + + camera->OnVideoRecordSucceeded(file_path, video_duration); + + // Dispose camera before message channel. + camera = nullptr; +} + +} // namespace test +} // namespace camera_windows diff --git a/packages/camera/camera_windows/windows/test/capture_controller_test.cpp b/packages/camera/camera_windows/windows/test/capture_controller_test.cpp new file mode 100644 index 000000000000..7520af7a4af8 --- /dev/null +++ b/packages/camera/camera_windows/windows/test/capture_controller_test.cpp @@ -0,0 +1,503 @@ +// 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. + +#include "capture_controller.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "mocks.h" +#include "string_utils.h" + +namespace camera_windows { + +namespace test { + +using Microsoft::WRL::ComPtr; +using ::testing::_; +using ::testing::Eq; +using ::testing::Return; + +void MockInitCaptureController(CaptureControllerImpl* capture_controller, + MockTextureRegistrar* texture_registrar, + MockCaptureEngine* engine, MockCamera* camera, + int64_t mock_texture_id) { + ComPtr video_source = new MockMediaSource(); + ComPtr audio_source = new MockMediaSource(); + + capture_controller->SetCaptureEngine( + reinterpret_cast(engine)); + capture_controller->SetVideoSource( + reinterpret_cast(video_source.Get())); + capture_controller->SetAudioSource( + reinterpret_cast(audio_source.Get())); + + EXPECT_CALL(*texture_registrar, RegisterTexture) + .Times(1) + .WillOnce([reg = texture_registrar, + mock_texture_id](flutter::TextureVariant* texture) -> int64_t { + EXPECT_TRUE(texture); + reg->texture_ = texture; + reg->texture_id_ = mock_texture_id; + return reg->texture_id_; + }); + EXPECT_CALL(*texture_registrar, UnregisterTexture(Eq(mock_texture_id))) + .Times(1); + EXPECT_CALL(*camera, OnCreateCaptureEngineFailed).Times(0); + EXPECT_CALL(*camera, OnCreateCaptureEngineSucceeded(Eq(mock_texture_id))) + .Times(1); + EXPECT_CALL(*engine, Initialize).Times(1); + + capture_controller->InitCaptureDevice(texture_registrar, MOCK_DEVICE_ID, true, + ResolutionPreset::kAuto); + + // MockCaptureEngine::Initialize is called + EXPECT_TRUE(engine->initialized_); + + engine->CreateFakeEvent(S_OK, MF_CAPTURE_ENGINE_INITIALIZED); +} + +void MockStartPreview(CaptureControllerImpl* capture_controller, + MockCaptureSource* capture_source, + MockCapturePreviewSink* preview_sink, + MockTextureRegistrar* texture_registrar, + MockCaptureEngine* engine, MockCamera* camera, + std::unique_ptr mock_source_buffer, + uint32_t mock_source_buffer_size, + uint32_t mock_preview_width, uint32_t mock_preview_height, + int64_t mock_texture_id) { + EXPECT_CALL(*engine, GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_PREVIEW, _)) + .Times(1) + .WillOnce([src_sink = preview_sink](MF_CAPTURE_ENGINE_SINK_TYPE sink_type, + IMFCaptureSink** target_sink) { + *target_sink = src_sink; + src_sink->AddRef(); + return S_OK; + }); + + EXPECT_CALL(*preview_sink, RemoveAllStreams).Times(1).WillOnce(Return(S_OK)); + EXPECT_CALL(*preview_sink, AddStream).Times(1).WillOnce(Return(S_OK)); + EXPECT_CALL(*preview_sink, SetSampleCallback) + .Times(1) + .WillOnce([sink = preview_sink]( + DWORD dwStreamSinkIndex, + IMFCaptureEngineOnSampleCallback* pCallback) -> HRESULT { + sink->sample_callback_ = pCallback; + return S_OK; + }); + + EXPECT_CALL(*engine, GetSource) + .Times(1) + .WillOnce( + [src_source = capture_source](IMFCaptureSource** target_source) { + *target_source = src_source; + src_source->AddRef(); + return S_OK; + }); + + EXPECT_CALL( + *capture_source, + GetAvailableDeviceMediaType( + Eq((DWORD) + MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_VIDEO_PREVIEW), + _, _)) + .WillRepeatedly([mock_preview_width, mock_preview_height]( + DWORD stream_index, DWORD media_type_index, + IMFMediaType** media_type) { + // We give only one media type to loop through + if (media_type_index != 0) return MF_E_NO_MORE_TYPES; + *media_type = + new FakeMediaType(MFMediaType_Video, MFVideoFormat_RGB32, + mock_preview_width, mock_preview_height); + (*media_type)->AddRef(); + return S_OK; + }); + + EXPECT_CALL( + *capture_source, + GetAvailableDeviceMediaType( + Eq((DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_VIDEO_RECORD), + _, _)) + .WillRepeatedly([mock_preview_width, mock_preview_height]( + DWORD stream_index, DWORD media_type_index, + IMFMediaType** media_type) { + // We give only one media type to loop through + if (media_type_index != 0) return MF_E_NO_MORE_TYPES; + *media_type = + new FakeMediaType(MFMediaType_Video, MFVideoFormat_RGB32, + mock_preview_width, mock_preview_height); + (*media_type)->AddRef(); + return S_OK; + }); + + EXPECT_CALL(*engine, StartPreview()).Times(1).WillOnce(Return(S_OK)); + + // Called by destructor + EXPECT_CALL(*engine, StopPreview()).Times(1).WillOnce(Return(S_OK)); + + // Called after first processed sample + EXPECT_CALL(*camera, + OnStartPreviewSucceeded(mock_preview_width, mock_preview_height)) + .Times(1); + EXPECT_CALL(*camera, OnStartPreviewFailed).Times(0); + EXPECT_CALL(*texture_registrar, MarkTextureFrameAvailable(mock_texture_id)) + .Times(1); + + capture_controller->StartPreview(); + + EXPECT_EQ(capture_controller->GetPreviewHeight(), mock_preview_height); + EXPECT_EQ(capture_controller->GetPreviewWidth(), mock_preview_width); + + // Capture engine is now started and will first send event of started preview + engine->CreateFakeEvent(S_OK, MF_CAPTURE_ENGINE_PREVIEW_STARTED); + + // SendFake sample + preview_sink->SendFakeSample(mock_source_buffer.get(), + mock_source_buffer_size); +} + +void MockRecordStart(CaptureControllerImpl* capture_controller, + MockCaptureEngine* engine, + MockCaptureRecordSink* record_sink, MockCamera* camera, + const std::string& mock_path_to_video) { + EXPECT_CALL(*engine, StartRecord()).Times(1).WillOnce(Return(S_OK)); + + EXPECT_CALL(*engine, GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_RECORD, _)) + .Times(1) + .WillOnce([src_sink = record_sink](MF_CAPTURE_ENGINE_SINK_TYPE sink_type, + IMFCaptureSink** target_sink) { + *target_sink = src_sink; + src_sink->AddRef(); + return S_OK; + }); + + EXPECT_CALL(*record_sink, RemoveAllStreams).Times(1).WillOnce(Return(S_OK)); + EXPECT_CALL(*record_sink, AddStream).Times(2).WillRepeatedly(Return(S_OK)); + EXPECT_CALL(*record_sink, SetOutputFileName).Times(1).WillOnce(Return(S_OK)); + + capture_controller->StartRecord(mock_path_to_video, -1); + + EXPECT_CALL(*camera, OnStartRecordSucceeded()).Times(1); + engine->CreateFakeEvent(S_OK, MF_CAPTURE_ENGINE_RECORD_STARTED); +} + +TEST(CaptureController, + InitCaptureEngineCallsOnCreateCaptureEngineSucceededWithTextureId) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + uint64_t mock_texture_id = 1234; + + // Init capture controller with mocks and tests + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + capture_controller = nullptr; + camera = nullptr; + texture_registrar = nullptr; + engine = nullptr; +} + +TEST(CaptureController, StartPreviewStartsProcessingSamples) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + uint64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr preview_sink = new MockCapturePreviewSink(); + ComPtr capture_source = new MockCaptureSource(); + + // Let's keep these small for mock texture data. Two pixels should be + // enough. + uint32_t mock_preview_width = 2; + uint32_t mock_preview_height = 1; + uint32_t pixels_total = mock_preview_width * mock_preview_height; + uint32_t pixel_size = 4; + + // Build mock texture + uint32_t mock_texture_data_size = pixels_total * pixel_size; + + std::unique_ptr mock_source_buffer = + std::make_unique(mock_texture_data_size); + + uint8_t mock_red_pixel = 0x11; + uint8_t mock_green_pixel = 0x22; + uint8_t mock_blue_pixel = 0x33; + MFVideoFormatRGB32Pixel* mock_source_buffer_data = + (MFVideoFormatRGB32Pixel*)mock_source_buffer.get(); + + for (uint32_t i = 0; i < pixels_total; i++) { + mock_source_buffer_data[i].r = mock_red_pixel; + mock_source_buffer_data[i].g = mock_green_pixel; + mock_source_buffer_data[i].b = mock_blue_pixel; + } + + // Start preview and run preview tests + MockStartPreview(capture_controller.get(), capture_source.Get(), + preview_sink.Get(), texture_registrar.get(), engine.Get(), + camera.get(), std::move(mock_source_buffer), + mock_texture_data_size, mock_preview_width, + mock_preview_height, mock_texture_id); + + // Test texture processing + EXPECT_TRUE(texture_registrar->texture_); + if (texture_registrar->texture_) { + auto pixel_buffer_texture = + std::get_if(texture_registrar->texture_); + EXPECT_TRUE(pixel_buffer_texture); + + if (pixel_buffer_texture) { + auto converted_buffer = + pixel_buffer_texture->CopyPixelBuffer((size_t)100, (size_t)100); + + EXPECT_TRUE(converted_buffer); + if (converted_buffer) { + EXPECT_EQ(converted_buffer->height, mock_preview_height); + EXPECT_EQ(converted_buffer->width, mock_preview_width); + + FlutterDesktopPixel* converted_buffer_data = + (FlutterDesktopPixel*)(converted_buffer->buffer); + + for (uint32_t i = 0; i < pixels_total; i++) { + EXPECT_EQ(converted_buffer_data[i].r, mock_red_pixel); + EXPECT_EQ(converted_buffer_data[i].g, mock_green_pixel); + EXPECT_EQ(converted_buffer_data[i].b, mock_blue_pixel); + } + + // Call release callback to get mutex lock unlocked. + converted_buffer->release_callback(converted_buffer->release_context); + } + converted_buffer = nullptr; + } + pixel_buffer_texture = nullptr; + } + + capture_controller = nullptr; + engine = nullptr; + camera = nullptr; + texture_registrar = nullptr; +} + +TEST(CaptureController, StartRecordSuccess) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + uint64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr preview_sink = new MockCapturePreviewSink(); + ComPtr capture_source = new MockCaptureSource(); + + std::unique_ptr mock_source_buffer = + std::make_unique(0); + + // Start preview to be able to start record + MockStartPreview(capture_controller.get(), capture_source.Get(), + preview_sink.Get(), texture_registrar.get(), engine.Get(), + camera.get(), std::move(mock_source_buffer), 0, 1, 1, + mock_texture_id); + + // Start record + ComPtr record_sink = new MockCaptureRecordSink(); + std::string mock_path_to_video = "mock_path_to_video"; + MockRecordStart(capture_controller.get(), engine.Get(), record_sink.Get(), + camera.get(), mock_path_to_video); + + // Called by destructor + EXPECT_CALL(*(engine.Get()), StopRecord(true, false)) + .Times(1) + .WillOnce(Return(S_OK)); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; + record_sink = nullptr; +} + +TEST(CaptureController, StopRecordSuccess) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + uint64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr preview_sink = new MockCapturePreviewSink(); + ComPtr capture_source = new MockCaptureSource(); + + std::unique_ptr mock_source_buffer = + std::make_unique(0); + + // Start preview to be able to start record + MockStartPreview(capture_controller.get(), capture_source.Get(), + preview_sink.Get(), texture_registrar.get(), engine.Get(), + camera.get(), std::move(mock_source_buffer), 0, 1, 1, + mock_texture_id); + + // Start record + ComPtr record_sink = new MockCaptureRecordSink(); + std::string mock_path_to_video = "mock_path_to_video"; + MockRecordStart(capture_controller.get(), engine.Get(), record_sink.Get(), + camera.get(), mock_path_to_video); + + // Request to stop record + EXPECT_CALL(*(engine.Get()), StopRecord(true, false)) + .Times(1) + .WillOnce(Return(S_OK)); + capture_controller->StopRecord(); + + // OnStopRecordSucceeded should be called with mocked file path + EXPECT_CALL(*camera, OnStopRecordSucceeded(Eq(mock_path_to_video))).Times(1); + engine->CreateFakeEvent(S_OK, MF_CAPTURE_ENGINE_RECORD_STOPPED); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; + record_sink = nullptr; +} + +TEST(CaptureController, TakePictureSuccess) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + uint64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr preview_sink = new MockCapturePreviewSink(); + ComPtr capture_source = new MockCaptureSource(); + + std::unique_ptr mock_source_buffer = + std::make_unique(0); + + // Start preview to be able to start record + MockStartPreview(capture_controller.get(), capture_source.Get(), + preview_sink.Get(), texture_registrar.get(), engine.Get(), + camera.get(), std::move(mock_source_buffer), 0, 1, 1, + mock_texture_id); + + // Init photo sink tests + ComPtr photo_sink = new MockCapturePhotoSink(); + EXPECT_CALL(*(engine.Get()), GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_PHOTO, _)) + .Times(1) + .WillOnce( + [src_sink = photo_sink.Get()](MF_CAPTURE_ENGINE_SINK_TYPE sink_type, + IMFCaptureSink** target_sink) { + *target_sink = src_sink; + src_sink->AddRef(); + return S_OK; + }); + EXPECT_CALL(*(photo_sink.Get()), RemoveAllStreams) + .Times(1) + .WillOnce(Return(S_OK)); + EXPECT_CALL(*(photo_sink.Get()), AddStream).Times(1).WillOnce(Return(S_OK)); + EXPECT_CALL(*(photo_sink.Get()), SetOutputFileName) + .Times(1) + .WillOnce(Return(S_OK)); + + // Request photo + std::string mock_path_to_photo = "mock_path_to_photo"; + EXPECT_CALL(*(engine.Get()), TakePhoto()).Times(1).WillOnce(Return(S_OK)); + capture_controller->TakePicture(mock_path_to_photo); + + // OnTakePictureSucceeded should be called with mocked file path + EXPECT_CALL(*camera, OnTakePictureSucceeded(Eq(mock_path_to_photo))).Times(1); + engine->CreateFakeEvent(S_OK, MF_CAPTURE_ENGINE_PHOTO_TAKEN); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; + photo_sink = nullptr; +} + +TEST(CaptureController, PauseResumePreviewSuccess) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + uint64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr preview_sink = new MockCapturePreviewSink(); + ComPtr capture_source = new MockCaptureSource(); + + std::unique_ptr mock_source_buffer = + std::make_unique(0); + + // Start preview to be able to start record + MockStartPreview(capture_controller.get(), capture_source.Get(), + preview_sink.Get(), texture_registrar.get(), engine.Get(), + camera.get(), std::move(mock_source_buffer), 0, 1, 1, + mock_texture_id); + + EXPECT_CALL(*camera, OnPausePreviewSucceeded()).Times(1); + capture_controller->PausePreview(); + + EXPECT_CALL(*camera, OnResumePreviewSucceeded()).Times(1); + capture_controller->ResumePreview(); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; +} + +} // namespace test +} // namespace camera_windows diff --git a/packages/camera/camera_windows/windows/test/mocks.h b/packages/camera/camera_windows/windows/test/mocks.h new file mode 100644 index 000000000000..0781989e94c2 --- /dev/null +++ b/packages/camera/camera_windows/windows/test/mocks.h @@ -0,0 +1,1015 @@ +// 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. + +#ifndef PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_TEST_MOCKS_H_ +#define PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_TEST_MOCKS_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "camera.h" +#include "camera_plugin.h" +#include "capture_controller.h" +#include "capture_controller_listener.h" +#include "capture_engine_listener.h" + +namespace camera_windows { +namespace test { + +namespace { + +using flutter::EncodableMap; +using flutter::EncodableValue; +using ::testing::_; + +class MockMethodResult : public flutter::MethodResult<> { + public: + ~MockMethodResult() = default; + + MOCK_METHOD(void, SuccessInternal, (const EncodableValue* result), + (override)); + MOCK_METHOD(void, ErrorInternal, + (const std::string& error_code, const std::string& error_message, + const EncodableValue* details), + (override)); + MOCK_METHOD(void, NotImplementedInternal, (), (override)); +}; + +class MockBinaryMessenger : public flutter::BinaryMessenger { + public: + ~MockBinaryMessenger() = default; + + MOCK_METHOD(void, Send, + (const std::string& channel, const uint8_t* message, + size_t message_size, flutter::BinaryReply reply), + (const)); + + MOCK_METHOD(void, SetMessageHandler, + (const std::string& channel, + flutter::BinaryMessageHandler handler), + ()); +}; + +class MockTextureRegistrar : public flutter::TextureRegistrar { + public: + MockTextureRegistrar() { + ON_CALL(*this, RegisterTexture) + .WillByDefault([this](flutter::TextureVariant* texture) -> int64_t { + EXPECT_TRUE(texture); + this->texture_ = texture; + this->texture_id_ = 1000; + return this->texture_id_; + }); + + ON_CALL(*this, UnregisterTexture) + .WillByDefault([this](int64_t tid) -> bool { + if (tid == this->texture_id_) { + texture_ = nullptr; + this->texture_id_ = -1; + return true; + } + return false; + }); + + ON_CALL(*this, MarkTextureFrameAvailable) + .WillByDefault([this](int64_t tid) -> bool { + if (tid == this->texture_id_) { + return true; + } + return false; + }); + } + + ~MockTextureRegistrar() { texture_ = nullptr; } + + MOCK_METHOD(int64_t, RegisterTexture, (flutter::TextureVariant * texture), + (override)); + + MOCK_METHOD(bool, UnregisterTexture, (int64_t), (override)); + MOCK_METHOD(bool, MarkTextureFrameAvailable, (int64_t), (override)); + + int64_t texture_id_ = -1; + flutter::TextureVariant* texture_ = nullptr; +}; + +class MockCameraFactory : public CameraFactory { + public: + MockCameraFactory() { + ON_CALL(*this, CreateCamera).WillByDefault([this]() { + assert(this->pending_camera_); + return std::move(this->pending_camera_); + }); + } + + ~MockCameraFactory() = default; + + // Disallow copy and move. + MockCameraFactory(const MockCameraFactory&) = delete; + MockCameraFactory& operator=(const MockCameraFactory&) = delete; + + MOCK_METHOD(std::unique_ptr, CreateCamera, + (const std::string& device_id), (override)); + + std::unique_ptr pending_camera_; +}; + +class MockCamera : public Camera { + public: + MockCamera(const std::string& device_id) + : device_id_(device_id), Camera(device_id){}; + + ~MockCamera() = default; + + // Disallow copy and move. + MockCamera(const MockCamera&) = delete; + MockCamera& operator=(const MockCamera&) = delete; + + MOCK_METHOD(void, OnCreateCaptureEngineSucceeded, (int64_t texture_id), + (override)); + MOCK_METHOD(std::unique_ptr>, GetPendingResultByType, + (PendingResultType type)); + MOCK_METHOD(void, OnCreateCaptureEngineFailed, (const std::string& error), + (override)); + + MOCK_METHOD(void, OnStartPreviewSucceeded, (int32_t width, int32_t height), + (override)); + MOCK_METHOD(void, OnStartPreviewFailed, (const std::string& error), + (override)); + + MOCK_METHOD(void, OnResumePreviewSucceeded, (), (override)); + MOCK_METHOD(void, OnResumePreviewFailed, (const std::string& error), + (override)); + + MOCK_METHOD(void, OnPausePreviewSucceeded, (), (override)); + MOCK_METHOD(void, OnPausePreviewFailed, (const std::string& error), + (override)); + + MOCK_METHOD(void, OnStartRecordSucceeded, (), (override)); + MOCK_METHOD(void, OnStartRecordFailed, (const std::string& error), + (override)); + + MOCK_METHOD(void, OnStopRecordSucceeded, (const std::string& file_path), + (override)); + MOCK_METHOD(void, OnStopRecordFailed, (const std::string& error), (override)); + + MOCK_METHOD(void, OnTakePictureSucceeded, (const std::string& file_path), + (override)); + MOCK_METHOD(void, OnTakePictureFailed, (const std::string& error), + (override)); + + MOCK_METHOD(void, OnVideoRecordSucceeded, + (const std::string& file_path, int64_t video_duration), + (override)); + MOCK_METHOD(void, OnVideoRecordFailed, (const std::string& error), + (override)); + MOCK_METHOD(void, OnCaptureError, (const std::string& error), (override)); + + MOCK_METHOD(bool, HasDeviceId, (std::string & device_id), (const override)); + MOCK_METHOD(bool, HasCameraId, (int64_t camera_id), (const override)); + + MOCK_METHOD(bool, AddPendingResult, + (PendingResultType type, std::unique_ptr> result), + (override)); + MOCK_METHOD(bool, HasPendingResultByType, (PendingResultType type), + (const override)); + + MOCK_METHOD(camera_windows::CaptureController*, GetCaptureController, (), + (override)); + + MOCK_METHOD(void, InitCamera, + (flutter::TextureRegistrar * texture_registrar, + flutter::BinaryMessenger* messenger, bool record_audio, + ResolutionPreset resolution_preset), + (override)); + + std::unique_ptr capture_controller_; + std::unique_ptr> pending_result_; + std::string device_id_; + int64_t camera_id_ = -1; +}; + +class MockCaptureControllerFactory : public CaptureControllerFactory { + public: + MockCaptureControllerFactory(){}; + virtual ~MockCaptureControllerFactory() = default; + + // Disallow copy and move. + MockCaptureControllerFactory(const MockCaptureControllerFactory&) = delete; + MockCaptureControllerFactory& operator=(const MockCaptureControllerFactory&) = + delete; + + MOCK_METHOD(std::unique_ptr, CreateCaptureController, + (CaptureControllerListener * listener), (override)); +}; + +class MockCaptureController : public CaptureController { + public: + ~MockCaptureController() = default; + + MOCK_METHOD(void, InitCaptureDevice, + (flutter::TextureRegistrar * texture_registrar, + const std::string& device_id, bool record_audio, + ResolutionPreset resolution_preset), + (override)); + + MOCK_METHOD(uint32_t, GetPreviewWidth, (), (const override)); + MOCK_METHOD(uint32_t, GetPreviewHeight, (), (const override)); + + // Actions + MOCK_METHOD(void, StartPreview, (), (override)); + MOCK_METHOD(void, ResumePreview, (), (override)); + MOCK_METHOD(void, PausePreview, (), (override)); + MOCK_METHOD(void, StartRecord, + (const std::string& file_path, int64_t max_video_duration_ms), + (override)); + MOCK_METHOD(void, StopRecord, (), (override)); + MOCK_METHOD(void, TakePicture, (const std::string& file_path), (override)); +}; + +// MockCameraPlugin extends CameraPlugin behaviour a bit to allow adding cameras +// without creating them first with create message handler and mocking static +// system calls +class MockCameraPlugin : public CameraPlugin { + public: + MockCameraPlugin(flutter::TextureRegistrar* texture_registrar, + flutter::BinaryMessenger* messenger) + : CameraPlugin(texture_registrar, messenger){}; + + // Creates a plugin instance with the given CameraFactory instance. + // Exists for unit testing with mock implementations. + MockCameraPlugin(flutter::TextureRegistrar* texture_registrar, + flutter::BinaryMessenger* messenger, + std::unique_ptr camera_factory) + : CameraPlugin(texture_registrar, messenger, std::move(camera_factory)){}; + + ~MockCameraPlugin() = default; + + // Disallow copy and move. + MockCameraPlugin(const MockCameraPlugin&) = delete; + MockCameraPlugin& operator=(const MockCameraPlugin&) = delete; + + MOCK_METHOD(bool, EnumerateVideoCaptureDeviceSources, + (IMFActivate * **devices, UINT32* count), (override)); + + // Helper to add camera without creating it via CameraFactory for testing + // purposes + void AddCamera(std::unique_ptr camera) { + cameras_.push_back(std::move(camera)); + } +}; + +class MockCaptureSource : public IMFCaptureSource { + public: + MockCaptureSource(){}; + ~MockCaptureSource() = default; + + // IUnknown + STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&ref_); } + + // IUnknown + STDMETHODIMP_(ULONG) Release() { + LONG ref = InterlockedDecrement(&ref_); + if (ref == 0) { + delete this; + } + return ref; + } + + // IUnknown + STDMETHODIMP_(HRESULT) QueryInterface(const IID& riid, void** ppv) { + *ppv = nullptr; + + if (riid == IID_IMFCaptureSource) { + *ppv = static_cast(this); + ((IUnknown*)*ppv)->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; + } + + MOCK_METHOD(HRESULT, GetCaptureDeviceSource, + (MF_CAPTURE_ENGINE_DEVICE_TYPE mfCaptureEngineDeviceType, + IMFMediaSource** ppMediaSource)); + MOCK_METHOD(HRESULT, GetCaptureDeviceActivate, + (MF_CAPTURE_ENGINE_DEVICE_TYPE mfCaptureEngineDeviceType, + IMFActivate** ppActivate)); + MOCK_METHOD(HRESULT, GetService, + (REFIID rguidService, REFIID riid, IUnknown** ppUnknown)); + MOCK_METHOD(HRESULT, AddEffect, + (DWORD dwSourceStreamIndex, IUnknown* pUnknown)); + + MOCK_METHOD(HRESULT, RemoveEffect, + (DWORD dwSourceStreamIndex, IUnknown* pUnknown)); + MOCK_METHOD(HRESULT, RemoveAllEffects, (DWORD dwSourceStreamIndex)); + MOCK_METHOD(HRESULT, GetAvailableDeviceMediaType, + (DWORD dwSourceStreamIndex, DWORD dwMediaTypeIndex, + IMFMediaType** ppMediaType)); + MOCK_METHOD(HRESULT, SetCurrentDeviceMediaType, + (DWORD dwSourceStreamIndex, IMFMediaType* pMediaType)); + MOCK_METHOD(HRESULT, GetCurrentDeviceMediaType, + (DWORD dwSourceStreamIndex, IMFMediaType** ppMediaType)); + MOCK_METHOD(HRESULT, GetDeviceStreamCount, (DWORD * pdwStreamCount)); + MOCK_METHOD(HRESULT, GetDeviceStreamCategory, + (DWORD dwSourceStreamIndex, + MF_CAPTURE_ENGINE_STREAM_CATEGORY* pStreamCategory)); + MOCK_METHOD(HRESULT, GetMirrorState, + (DWORD dwStreamIndex, BOOL* pfMirrorState)); + MOCK_METHOD(HRESULT, SetMirrorState, + (DWORD dwStreamIndex, BOOL fMirrorState)); + MOCK_METHOD(HRESULT, GetStreamIndexFromFriendlyName, + (UINT32 uifriendlyName, DWORD* pdwActualStreamIndex)); + + private: + volatile ULONG ref_ = 0; +}; + +// Uses IMFMediaSourceEx which has SetD3DManager method. +class MockMediaSource : public IMFMediaSourceEx { + public: + MockMediaSource(){}; + ~MockMediaSource() = default; + + // IUnknown + STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&ref_); } + + // IUnknown + STDMETHODIMP_(ULONG) Release() { + LONG ref = InterlockedDecrement(&ref_); + if (ref == 0) { + delete this; + } + return ref; + } + + // IUnknown + STDMETHODIMP_(HRESULT) QueryInterface(const IID& riid, void** ppv) { + *ppv = nullptr; + + if (riid == IID_IMFMediaSource) { + *ppv = static_cast(this); + ((IUnknown*)*ppv)->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; + } + + // IMFMediaSource + HRESULT GetCharacteristics(DWORD* dwCharacteristics) override { + return E_NOTIMPL; + } + // IMFMediaSource + HRESULT CreatePresentationDescriptor( + IMFPresentationDescriptor** presentationDescriptor) override { + return E_NOTIMPL; + } + // IMFMediaSource + HRESULT Start(IMFPresentationDescriptor* presentationDescriptor, + const GUID* guidTimeFormat, + const PROPVARIANT* varStartPosition) override { + return E_NOTIMPL; + } + // IMFMediaSource + HRESULT Stop(void) override { return E_NOTIMPL; } + // IMFMediaSource + HRESULT Pause(void) override { return E_NOTIMPL; } + // IMFMediaSource + HRESULT Shutdown(void) override { return E_NOTIMPL; } + + // IMFMediaEventGenerator + HRESULT GetEvent(DWORD dwFlags, IMFMediaEvent** event) override { + return E_NOTIMPL; + } + // IMFMediaEventGenerator + HRESULT BeginGetEvent(IMFAsyncCallback* callback, + IUnknown* unkState) override { + return E_NOTIMPL; + } + // IMFMediaEventGenerator + HRESULT EndGetEvent(IMFAsyncResult* result, IMFMediaEvent** event) override { + return E_NOTIMPL; + } + // IMFMediaEventGenerator + HRESULT QueueEvent(MediaEventType met, REFGUID guidExtendedType, + HRESULT hrStatus, const PROPVARIANT* value) override { + return E_NOTIMPL; + } + + // IMFMediaSourceEx + HRESULT GetSourceAttributes(IMFAttributes** attributes) { return E_NOTIMPL; } + // IMFMediaSourceEx + HRESULT GetStreamAttributes(DWORD stream_id, IMFAttributes** attributes) { + return E_NOTIMPL; + } + // IMFMediaSourceEx + HRESULT SetD3DManager(IUnknown* manager) { return S_OK; } + + private: + volatile ULONG ref_ = 0; +}; + +class MockCapturePreviewSink : public IMFCapturePreviewSink { + public: + // IMFCaptureSink + MOCK_METHOD(HRESULT, GetOutputMediaType, + (DWORD dwSinkStreamIndex, IMFMediaType** ppMediaType)); + + // IMFCaptureSink + MOCK_METHOD(HRESULT, GetService, + (DWORD dwSinkStreamIndex, REFGUID rguidService, REFIID riid, + IUnknown** ppUnknown)); + + // IMFCaptureSink + MOCK_METHOD(HRESULT, AddStream, + (DWORD dwSourceStreamIndex, IMFMediaType* pMediaType, + IMFAttributes* pAttributes, DWORD* pdwSinkStreamIndex)); + + // IMFCaptureSink + MOCK_METHOD(HRESULT, Prepare, ()); + + // IMFCaptureSink + MOCK_METHOD(HRESULT, RemoveAllStreams, ()); + + // IMFCapturePreviewSink + MOCK_METHOD(HRESULT, SetRenderHandle, (HANDLE handle)); + + // IMFCapturePreviewSink + MOCK_METHOD(HRESULT, SetRenderSurface, (IUnknown * pSurface)); + + // IMFCapturePreviewSink + MOCK_METHOD(HRESULT, UpdateVideo, + (const MFVideoNormalizedRect* pSrc, const RECT* pDst, + const COLORREF* pBorderClr)); + + // IMFCapturePreviewSink + MOCK_METHOD(HRESULT, SetSampleCallback, + (DWORD dwStreamSinkIndex, + IMFCaptureEngineOnSampleCallback* pCallback)); + + // IMFCapturePreviewSink + MOCK_METHOD(HRESULT, GetMirrorState, (BOOL * pfMirrorState)); + + // IMFCapturePreviewSink + MOCK_METHOD(HRESULT, SetMirrorState, (BOOL fMirrorState)); + + // IMFCapturePreviewSink + MOCK_METHOD(HRESULT, GetRotation, + (DWORD dwStreamIndex, DWORD* pdwRotationValue)); + + // IMFCapturePreviewSink + MOCK_METHOD(HRESULT, SetRotation, + (DWORD dwStreamIndex, DWORD dwRotationValue)); + + // IMFCapturePreviewSink + MOCK_METHOD(HRESULT, SetCustomSink, (IMFMediaSink * pMediaSink)); + + // IUnknown + STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&ref_); } + + // IUnknown + STDMETHODIMP_(ULONG) Release() { + LONG ref = InterlockedDecrement(&ref_); + if (ref == 0) { + delete this; + } + return ref; + } + + // IUnknown + STDMETHODIMP_(HRESULT) QueryInterface(const IID& riid, void** ppv) { + *ppv = nullptr; + + if (riid == IID_IMFCapturePreviewSink) { + *ppv = static_cast(this); + ((IUnknown*)*ppv)->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; + } + + void SendFakeSample(uint8_t* src_buffer, uint32_t size) { + assert(sample_callback_); + ComPtr sample; + ComPtr buffer; + HRESULT hr = MFCreateSample(&sample); + + if (SUCCEEDED(hr)) { + hr = MFCreateMemoryBuffer(size, &buffer); + } + + if (SUCCEEDED(hr)) { + uint8_t* target_data; + if (SUCCEEDED(buffer->Lock(&target_data, nullptr, nullptr))) { + std::copy(src_buffer, src_buffer + size, target_data); + } + hr = buffer->Unlock(); + } + + if (SUCCEEDED(hr)) { + hr = buffer->SetCurrentLength(size); + } + + if (SUCCEEDED(hr)) { + hr = sample->AddBuffer(buffer.Get()); + } + + if (SUCCEEDED(hr)) { + sample_callback_->OnSample(sample.Get()); + } + } + + ComPtr sample_callback_; + + private: + ~MockCapturePreviewSink() = default; + volatile ULONG ref_ = 0; +}; + +class MockCaptureRecordSink : public IMFCaptureRecordSink { + public: + // IMFCaptureSink + MOCK_METHOD(HRESULT, GetOutputMediaType, + (DWORD dwSinkStreamIndex, IMFMediaType** ppMediaType)); + + // IMFCaptureSink + MOCK_METHOD(HRESULT, GetService, + (DWORD dwSinkStreamIndex, REFGUID rguidService, REFIID riid, + IUnknown** ppUnknown)); + + // IMFCaptureSink + MOCK_METHOD(HRESULT, AddStream, + (DWORD dwSourceStreamIndex, IMFMediaType* pMediaType, + IMFAttributes* pAttributes, DWORD* pdwSinkStreamIndex)); + + // IMFCaptureSink + MOCK_METHOD(HRESULT, Prepare, ()); + + // IMFCaptureSink + MOCK_METHOD(HRESULT, RemoveAllStreams, ()); + + // IMFCaptureRecordSink + MOCK_METHOD(HRESULT, SetOutputByteStream, + (IMFByteStream * pByteStream, REFGUID guidContainerType)); + + // IMFCaptureRecordSink + MOCK_METHOD(HRESULT, SetOutputFileName, (LPCWSTR fileName)); + + // IMFCaptureRecordSink + MOCK_METHOD(HRESULT, SetSampleCallback, + (DWORD dwStreamSinkIndex, + IMFCaptureEngineOnSampleCallback* pCallback)); + + // IMFCaptureRecordSink + MOCK_METHOD(HRESULT, SetCustomSink, (IMFMediaSink * pMediaSink)); + + // IMFCaptureRecordSink + MOCK_METHOD(HRESULT, GetRotation, + (DWORD dwStreamIndex, DWORD* pdwRotationValue)); + + // IMFCaptureRecordSink + MOCK_METHOD(HRESULT, SetRotation, + (DWORD dwStreamIndex, DWORD dwRotationValue)); + + // IUnknown + STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&ref_); } + + // IUnknown + STDMETHODIMP_(ULONG) Release() { + LONG ref = InterlockedDecrement(&ref_); + if (ref == 0) { + delete this; + } + return ref; + } + + // IUnknown + STDMETHODIMP_(HRESULT) QueryInterface(const IID& riid, void** ppv) { + *ppv = nullptr; + + if (riid == IID_IMFCaptureRecordSink) { + *ppv = static_cast(this); + ((IUnknown*)*ppv)->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; + } + + private: + ~MockCaptureRecordSink() = default; + volatile ULONG ref_ = 0; +}; + +class MockCapturePhotoSink : public IMFCapturePhotoSink { + public: + // IMFCaptureSink + MOCK_METHOD(HRESULT, GetOutputMediaType, + (DWORD dwSinkStreamIndex, IMFMediaType** ppMediaType)); + + // IMFCaptureSink + MOCK_METHOD(HRESULT, GetService, + (DWORD dwSinkStreamIndex, REFGUID rguidService, REFIID riid, + IUnknown** ppUnknown)); + + // IMFCaptureSink + MOCK_METHOD(HRESULT, AddStream, + (DWORD dwSourceStreamIndex, IMFMediaType* pMediaType, + IMFAttributes* pAttributes, DWORD* pdwSinkStreamIndex)); + + // IMFCaptureSink + MOCK_METHOD(HRESULT, Prepare, ()); + + // IMFCaptureSink + MOCK_METHOD(HRESULT, RemoveAllStreams, ()); + + // IMFCapturePhotoSink + MOCK_METHOD(HRESULT, SetOutputFileName, (LPCWSTR fileName)); + + // IMFCapturePhotoSink + MOCK_METHOD(HRESULT, SetSampleCallback, + (IMFCaptureEngineOnSampleCallback * pCallback)); + + // IMFCapturePhotoSink + MOCK_METHOD(HRESULT, SetOutputByteStream, (IMFByteStream * pByteStream)); + + // IUnknown + STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&ref_); } + + // IUnknown + STDMETHODIMP_(ULONG) Release() { + LONG ref = InterlockedDecrement(&ref_); + if (ref == 0) { + delete this; + } + return ref; + } + + // IUnknown + STDMETHODIMP_(HRESULT) QueryInterface(const IID& riid, void** ppv) { + *ppv = nullptr; + + if (riid == IID_IMFCapturePhotoSink) { + *ppv = static_cast(this); + ((IUnknown*)*ppv)->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; + } + + private: + ~MockCapturePhotoSink() = default; + volatile ULONG ref_ = 0; +}; + +template +class FakeIMFAttributesBase : public T { + static_assert(std::is_base_of::value, + "I must inherit from IMFAttributes"); + + // IIMFAttributes + HRESULT GetItem(REFGUID guidKey, PROPVARIANT* pValue) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT GetItemType(REFGUID guidKey, MF_ATTRIBUTE_TYPE* pType) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT CompareItem(REFGUID guidKey, REFPROPVARIANT Value, + BOOL* pbResult) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT Compare(IMFAttributes* pTheirs, MF_ATTRIBUTES_MATCH_TYPE MatchType, + BOOL* pbResult) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT GetUINT32(REFGUID guidKey, UINT32* punValue) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT GetUINT64(REFGUID guidKey, UINT64* punValue) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT GetDouble(REFGUID guidKey, double* pfValue) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT GetGUID(REFGUID guidKey, GUID* pguidValue) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT GetStringLength(REFGUID guidKey, UINT32* pcchLength) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT GetString(REFGUID guidKey, LPWSTR pwszValue, UINT32 cchBufSize, + UINT32* pcchLength) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT GetAllocatedString(REFGUID guidKey, LPWSTR* ppwszValue, + UINT32* pcchLength) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT GetBlobSize(REFGUID guidKey, UINT32* pcbBlobSize) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT GetBlob(REFGUID guidKey, UINT8* pBuf, UINT32 cbBufSize, + UINT32* pcbBlobSize) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT GetAllocatedBlob(REFGUID guidKey, UINT8** ppBuf, + UINT32* pcbSize) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT GetUnknown(REFGUID guidKey, REFIID riid, + __RPC__deref_out_opt LPVOID* ppv) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT SetItem(REFGUID guidKey, REFPROPVARIANT Value) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT DeleteItem(REFGUID guidKey) override { return E_NOTIMPL; } + + // IIMFAttributes + HRESULT DeleteAllItems(void) override { return E_NOTIMPL; } + + // IIMFAttributes + HRESULT SetUINT32(REFGUID guidKey, UINT32 unValue) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT SetUINT64(REFGUID guidKey, UINT64 unValue) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT SetDouble(REFGUID guidKey, double fValue) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT SetGUID(REFGUID guidKey, REFGUID guidValue) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT SetString(REFGUID guidKey, LPCWSTR wszValue) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT SetBlob(REFGUID guidKey, const UINT8* pBuf, + UINT32 cbBufSize) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT SetUnknown(REFGUID guidKey, IUnknown* pUnknown) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT LockStore(void) override { return E_NOTIMPL; } + + // IIMFAttributes + HRESULT UnlockStore(void) override { return E_NOTIMPL; } + + // IIMFAttributes + HRESULT GetCount(UINT32* pcItems) override { return E_NOTIMPL; } + + // IIMFAttributes + HRESULT GetItemByIndex(UINT32 unIndex, GUID* pguidKey, + PROPVARIANT* pValue) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT CopyAllItems(IMFAttributes* pDest) override { return E_NOTIMPL; } +}; + +class FakeMediaType : public FakeIMFAttributesBase { + public: + FakeMediaType(GUID major_type, GUID sub_type, int width, int height) + : major_type_(major_type), + sub_type_(sub_type), + width_(width), + height_(height){}; + + // IMFAttributes + HRESULT GetUINT64(REFGUID key, UINT64* value) override { + if (key == MF_MT_FRAME_SIZE) { + *value = (int64_t)width_ << 32 | (int64_t)height_; + return S_OK; + } else if (key == MF_MT_FRAME_RATE) { + *value = (int64_t)frame_rate_ << 32 | 1; + return S_OK; + } + return E_FAIL; + }; + + // IMFAttributes + HRESULT GetGUID(REFGUID key, GUID* value) override { + if (key == MF_MT_MAJOR_TYPE) { + *value = major_type_; + return S_OK; + } else if (key == MF_MT_SUBTYPE) { + *value = sub_type_; + return S_OK; + } + return E_FAIL; + } + + // IIMFAttributes + HRESULT CopyAllItems(IMFAttributes* pDest) override { + pDest->SetUINT64(MF_MT_FRAME_SIZE, + (int64_t)width_ << 32 | (int64_t)height_); + pDest->SetUINT64(MF_MT_FRAME_RATE, (int64_t)frame_rate_ << 32 | 1); + pDest->SetGUID(MF_MT_MAJOR_TYPE, major_type_); + pDest->SetGUID(MF_MT_SUBTYPE, sub_type_); + return S_OK; + } + + // IMFMediaType + HRESULT STDMETHODCALLTYPE GetMajorType(GUID* pguidMajorType) override { + return E_NOTIMPL; + }; + + // IMFMediaType + HRESULT STDMETHODCALLTYPE IsCompressedFormat(BOOL* pfCompressed) override { + return E_NOTIMPL; + } + + // IMFMediaType + HRESULT STDMETHODCALLTYPE IsEqual(IMFMediaType* pIMediaType, + DWORD* pdwFlags) override { + return E_NOTIMPL; + } + + // IMFMediaType + HRESULT STDMETHODCALLTYPE GetRepresentation( + GUID guidRepresentation, LPVOID* ppvRepresentation) override { + return E_NOTIMPL; + } + + // IMFMediaType + HRESULT STDMETHODCALLTYPE FreeRepresentation( + GUID guidRepresentation, LPVOID pvRepresentation) override { + return E_NOTIMPL; + } + + // IUnknown + STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&ref_); } + + // IUnknown + STDMETHODIMP_(ULONG) Release() { + LONG ref = InterlockedDecrement(&ref_); + if (ref == 0) { + delete this; + } + return ref; + } + + // IUnknown + STDMETHODIMP_(HRESULT) QueryInterface(const IID& riid, void** ppv) { + *ppv = nullptr; + + if (riid == IID_IMFMediaType) { + *ppv = static_cast(this); + ((IUnknown*)*ppv)->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; + } + + private: + ~FakeMediaType() = default; + volatile ULONG ref_ = 0; + const GUID major_type_; + const GUID sub_type_; + const int width_; + const int height_; + const int frame_rate_ = 30; +}; + +class MockCaptureEngine : public IMFCaptureEngine { + public: + MockCaptureEngine() { + ON_CALL(*this, Initialize) + .WillByDefault([this](IMFCaptureEngineOnEventCallback* callback, + IMFAttributes* attributes, IUnknown* audioSource, + IUnknown* videoSource) -> HRESULT { + EXPECT_TRUE(callback); + EXPECT_TRUE(attributes); + EXPECT_TRUE(videoSource); + // audioSource is allowed to be nullptr; + callback_ = callback; + videoSource_ = reinterpret_cast(videoSource); + audioSource_ = reinterpret_cast(audioSource); + initialized_ = true; + return S_OK; + }); + }; + + virtual ~MockCaptureEngine() = default; + + MOCK_METHOD(HRESULT, Initialize, + (IMFCaptureEngineOnEventCallback * callback, + IMFAttributes* attributes, IUnknown* audioSource, + IUnknown* videoSource)); + MOCK_METHOD(HRESULT, StartPreview, ()); + MOCK_METHOD(HRESULT, StopPreview, ()); + MOCK_METHOD(HRESULT, StartRecord, ()); + MOCK_METHOD(HRESULT, StopRecord, + (BOOL finalize, BOOL flushUnprocessedSamples)); + MOCK_METHOD(HRESULT, TakePhoto, ()); + MOCK_METHOD(HRESULT, GetSink, + (MF_CAPTURE_ENGINE_SINK_TYPE type, IMFCaptureSink** sink)); + MOCK_METHOD(HRESULT, GetSource, (IMFCaptureSource * *ppSource)); + + // IUnknown + STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&ref_); } + + // IUnknown + STDMETHODIMP_(ULONG) Release() { + LONG ref = InterlockedDecrement(&ref_); + if (ref == 0) { + delete this; + } + return ref; + } + + // IUnknown + STDMETHODIMP_(HRESULT) QueryInterface(const IID& riid, void** ppv) { + *ppv = nullptr; + + if (riid == IID_IMFCaptureEngine) { + *ppv = static_cast(this); + ((IUnknown*)*ppv)->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; + } + + void CreateFakeEvent(HRESULT hrStatus, GUID event_type) { + EXPECT_TRUE(initialized_); + ComPtr event; + MFCreateMediaEvent(MEExtendedType, event_type, hrStatus, nullptr, &event); + if (callback_) { + callback_->OnEvent(event.Get()); + } + } + + ComPtr callback_; + ComPtr videoSource_; + ComPtr audioSource_; + volatile ULONG ref_ = 0; + bool initialized_ = false; +}; + +#define MOCK_DEVICE_ID "mock_device_id" +#define MOCK_CAMERA_NAME "mock_camera_name <" MOCK_DEVICE_ID ">" +#define MOCK_INVALID_CAMERA_NAME "invalid_camera_name" + +} // namespace +} // namespace test +} // namespace camera_windows + +#endif // PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_TEST_MOCKS_H_ diff --git a/packages/camera/camera_windows/windows/texture_handler.cpp b/packages/camera/camera_windows/windows/texture_handler.cpp new file mode 100644 index 000000000000..a7c94738698a --- /dev/null +++ b/packages/camera/camera_windows/windows/texture_handler.cpp @@ -0,0 +1,144 @@ +// 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. + +#include "texture_handler.h" + +#include + +namespace camera_windows { + +TextureHandler::~TextureHandler() { + // Texture might still be processed while destructor is called. + // Lock mutex for safe destruction + const std::lock_guard lock(buffer_mutex_); + if (texture_registrar_ && texture_id_ > 0) { + texture_registrar_->UnregisterTexture(texture_id_); + } + texture_id_ = -1; + texture_ = nullptr; + texture_registrar_ = nullptr; +} + +int64_t TextureHandler::RegisterTexture() { + if (!texture_registrar_) { + return -1; + } + + // Create flutter desktop pixelbuffer texture; + texture_ = + std::make_unique(flutter::PixelBufferTexture( + [this](size_t width, + size_t height) -> const FlutterDesktopPixelBuffer* { + return this->ConvertPixelBufferForFlutter(width, height); + })); + + texture_id_ = texture_registrar_->RegisterTexture(texture_.get()); + return texture_id_; +} + +bool TextureHandler::UpdateBuffer(uint8_t* data, uint32_t data_length) { + // Scoped lock guard. + { + const std::lock_guard lock(buffer_mutex_); + if (!TextureRegistered()) { + return false; + } + + if (source_buffer_.size() != data_length) { + // Update source buffer size. + source_buffer_.resize(data_length); + } + std::copy(data, data + data_length, source_buffer_.data()); + } + OnBufferUpdated(); + return true; +}; + +// Marks texture frame available after buffer is updated. +void TextureHandler::OnBufferUpdated() { + if (TextureRegistered()) { + texture_registrar_->MarkTextureFrameAvailable(texture_id_); + } +} + +const FlutterDesktopPixelBuffer* TextureHandler::ConvertPixelBufferForFlutter( + size_t target_width, size_t target_height) { + // TODO: optimize image processing size by adjusting capture size + // dynamically to match target_width and target_height. + // If target size changes, create new media type for preview and set new + // target framesize to MF_MT_FRAME_SIZE attribute. + // Size should be kept inside requested resolution preset. + // Update output media type with IMFCaptureSink2::SetOutputMediaType method + // call and implement IMFCaptureEngineOnSampleCallback2::OnSynchronizedEvent + // to detect size changes. + + // Lock buffer mutex to protect texture processing + std::unique_lock buffer_lock(buffer_mutex_); + if (!TextureRegistered()) { + return nullptr; + } + + const uint32_t bytes_per_pixel = 4; + const uint32_t pixels_total = preview_frame_width_ * preview_frame_height_; + const uint32_t data_size = pixels_total * bytes_per_pixel; + if (data_size > 0 && source_buffer_.size() == data_size) { + if (dest_buffer_.size() != data_size) { + dest_buffer_.resize(data_size); + } + + // Map buffers to structs for easier conversion. + MFVideoFormatRGB32Pixel* src = + reinterpret_cast(source_buffer_.data()); + FlutterDesktopPixel* dst = + reinterpret_cast(dest_buffer_.data()); + + for (uint32_t y = 0; y < preview_frame_height_; y++) { + for (uint32_t x = 0; x < preview_frame_width_; x++) { + uint32_t sp = (y * preview_frame_width_) + x; + if (mirror_preview_) { + // Software mirror mode. + // IMFCapturePreviewSink also has the SetMirrorState setting, + // but if enabled, samples will not be processed. + + // Calculates mirrored pixel position. + uint32_t tp = + (y * preview_frame_width_) + ((preview_frame_width_ - 1) - x); + dst[tp].r = src[sp].r; + dst[tp].g = src[sp].g; + dst[tp].b = src[sp].b; + dst[tp].a = 255; + } else { + dst[sp].r = src[sp].r; + dst[sp].g = src[sp].g; + dst[sp].b = src[sp].b; + dst[sp].a = 255; + } + } + } + + if (!flutter_desktop_pixel_buffer_) { + flutter_desktop_pixel_buffer_ = + std::make_unique(); + + // Unlocks mutex after texture is processed. + flutter_desktop_pixel_buffer_->release_callback = + [](void* release_context) { + auto mutex = reinterpret_cast(release_context); + mutex->unlock(); + }; + } + + flutter_desktop_pixel_buffer_->buffer = dest_buffer_.data(); + flutter_desktop_pixel_buffer_->width = preview_frame_width_; + flutter_desktop_pixel_buffer_->height = preview_frame_height_; + + // Releases unique_lock and set mutex pointer for release context. + flutter_desktop_pixel_buffer_->release_context = buffer_lock.release(); + + return flutter_desktop_pixel_buffer_.get(); + } + return nullptr; +} + +} // namespace camera_windows diff --git a/packages/camera/camera_windows/windows/texture_handler.h b/packages/camera/camera_windows/windows/texture_handler.h new file mode 100644 index 000000000000..b85611c25608 --- /dev/null +++ b/packages/camera/camera_windows/windows/texture_handler.h @@ -0,0 +1,91 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_TEXTURE_HANDLER_H_ +#define PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_TEXTURE_HANDLER_H_ + +#include + +#include +#include +#include + +namespace camera_windows { + +// Describes flutter desktop pixelbuffers pixel data order. +struct FlutterDesktopPixel { + uint8_t r = 0; + uint8_t g = 0; + uint8_t b = 0; + uint8_t a = 0; +}; + +// Describes MFVideoFormat_RGB32 data order. +struct MFVideoFormatRGB32Pixel { + uint8_t b = 0; + uint8_t g = 0; + uint8_t r = 0; + uint8_t x = 0; +}; + +// Handles the registration of Flutter textures, pixel buffers, and the +// conversion of texture formats. +class TextureHandler { + public: + TextureHandler(flutter::TextureRegistrar* texture_registrar) + : texture_registrar_(texture_registrar) {} + virtual ~TextureHandler(); + + // Prevent copying. + TextureHandler(TextureHandler const&) = delete; + TextureHandler& operator=(TextureHandler const&) = delete; + + // Updates source data buffer with given data. + bool UpdateBuffer(uint8_t* data, uint32_t data_length); + + // Registers texture and updates given texture_id pointer value. + int64_t RegisterTexture(); + + // Updates current preview texture size. + void UpdateTextureSize(uint32_t width, uint32_t height) { + preview_frame_width_ = width; + preview_frame_height_ = height; + } + + // Sets software mirror state. + void SetMirrorPreviewState(bool mirror) { mirror_preview_ = mirror; } + + private: + // Informs flutter texture registrar of updated texture. + void OnBufferUpdated(); + + // Converts local pixel buffer to flutter pixel buffer. + const FlutterDesktopPixelBuffer* ConvertPixelBufferForFlutter(size_t width, + size_t height); + + // Checks if texture registrar, texture id and texture are available. + bool TextureRegistered() { + return texture_registrar_ && texture_ && texture_id_ > -1; + } + + bool mirror_preview_ = true; + int64_t texture_id_ = -1; + uint32_t bytes_per_pixel_ = 4; + uint32_t source_buffer_size_ = 0; + uint32_t preview_frame_width_ = 0; + uint32_t preview_frame_height_ = 0; + + std::vector source_buffer_; + std::vector dest_buffer_; + std::unique_ptr texture_; + std::unique_ptr flutter_desktop_pixel_buffer_ = + nullptr; + flutter::TextureRegistrar* texture_registrar_ = nullptr; + + std::mutex buffer_mutex_; +}; + +} // namespace camera_windows + +#endif // PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_TEXTURE_HANDLER_H_ From 0af13596052560002757d245aaf82848270c4a3f Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 3 Feb 2022 17:54:43 -0500 Subject: [PATCH 134/600] [ci] Skip Android webview FTL tests on stable (#4730) These tests crash on stable due to https://github.com/flutter/flutter/issues/96661 now that 2.10 has been released to stable. Fixes out-of-band tree closure from the 2.10 release. Co-authored-by: David Iglesias --- .cirrus.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index f2372a91f2f5..c4951a6a7750 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -230,7 +230,15 @@ task: - export CIRRUS_COMMIT_MESSAGE="" - if [[ -n "$GCLOUD_FIREBASE_TESTLAB_KEY" ]]; then - echo $GCLOUD_FIREBASE_TESTLAB_KEY > ${HOME}/gcloud-service-key.json - - ./script/tool_runner.sh firebase-test-lab --device model=redfin,version=30 --device model=starqlteue,version=26 --exclude=script/configs/exclude_integration_android.yaml + # TODO(stuartmorgan): Remove this condition once + # https://github.com/flutter/flutter/issues/96661 is fixed and + # cherry picked. Currently webview_flutter tests crash on stable on + # Android so must be skipped. + - if [[ "$CHANNEL" == "stable" ]]; then + - ./script/tool_runner.sh firebase-test-lab --device model=redfin,version=30 --device model=starqlteue,version=26 --exclude=script/configs/exclude_integration_android.yaml,webview_flutter + - else + - ./script/tool_runner.sh firebase-test-lab --device model=redfin,version=30 --device model=starqlteue,version=26 --exclude=script/configs/exclude_integration_android.yaml + - fi - else - echo "This user does not have permission to run Firebase Test Lab tests." - fi From 4d92d900bf9c58e9271f5f7818740968c7186c6d Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Thu, 3 Feb 2022 15:35:21 -0800 Subject: [PATCH 135/600] [camera]writing file on background queue (#4721) --- packages/camera/camera/CHANGELOG.md | 4 + .../ios/Runner.xcodeproj/project.pbxproj | 6 +- .../RunnerTests/FLTSavePhotoDelegateTests.m | 133 ++++++++++++++++++ .../camera/ios/Classes/CameraPlugin.modulemap | 6 + packages/camera/camera/ios/Classes/FLTCam.m | 80 ++--------- .../camera/ios/Classes/FLTSavePhotoDelegate.h | 37 +++++ .../camera/ios/Classes/FLTSavePhotoDelegate.m | 65 +++++++++ .../ios/Classes/FLTSavePhotoDelegate_Test.h | 17 +++ .../camera/ios/Classes/camera-umbrella.h | 5 - packages/camera/camera/pubspec.yaml | 2 +- 10 files changed, 279 insertions(+), 76 deletions(-) create mode 100644 packages/camera/camera/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m create mode 100644 packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.h create mode 100644 packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.m create mode 100644 packages/camera/camera/ios/Classes/FLTSavePhotoDelegate_Test.h diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 9b32af241032..f846774ffe0a 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.4+10 + +* iOS performance improvement by moving file writing from the main queue to a background IO queue. + ## 0.9.4+9 * iOS performance improvement by moving sample buffer handling from the main queue to a background session queue. diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj index 65a6bbda3261..ac39de2e3f84 100644 --- a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ @@ -22,6 +22,7 @@ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; E01EE4A82799F3A5008C1950 /* QueueHelperTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E01EE4A72799F3A5008C1950 /* QueueHelperTests.m */; }; E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */; }; + E04F108627A87CA600573D0C /* FLTSavePhotoDelegateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */; }; E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */; }; E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */; }; E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */; }; @@ -83,6 +84,7 @@ A24F9E418BA48BCC7409B117 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; E01EE4A72799F3A5008C1950 /* QueueHelperTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QueueHelperTests.m; sourceTree = ""; }; E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CameraCaptureSessionQueueRaceConditionTests.m; sourceTree = ""; }; + E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTSavePhotoDelegateTests.m; sourceTree = ""; }; E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeMethodChannelTests.m; sourceTree = ""; }; E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeTextureRegistryTests.m; sourceTree = ""; }; E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeEventChannelTests.m; sourceTree = ""; }; @@ -125,6 +127,7 @@ E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */, E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */, E0F95E4327A36B9200699390 /* SampleBufferQueueTests.m */, + E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */, E01EE4A72799F3A5008C1950 /* QueueHelperTests.m */, E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */, F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */, @@ -399,6 +402,7 @@ E0F95E3D27A32AB900699390 /* CameraPropertiesTests.m in Sources */, 03BB766B2665316900CE5A93 /* CameraFocusTests.m in Sources */, E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */, + E04F108627A87CA600573D0C /* FLTSavePhotoDelegateTests.m in Sources */, F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */, 334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */, E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */, diff --git a/packages/camera/camera/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m b/packages/camera/camera/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m new file mode 100644 index 000000000000..b6ea84da449c --- /dev/null +++ b/packages/camera/camera/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m @@ -0,0 +1,133 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import camera; +@import camera.Test; +@import AVFoundation; +@import XCTest; +#import + +@interface FLTSavePhotoDelegateTests : XCTestCase + +@end + +@implementation FLTSavePhotoDelegateTests + +- (void)testHandlePhotoCaptureResult_mustSendErrorIfFailedToCapture { + NSError *error = [NSError errorWithDomain:@"test" code:0 userInfo:nil]; + dispatch_queue_t ioQueue = dispatch_queue_create("test", NULL); + id mockResult = OCMClassMock([FLTThreadSafeFlutterResult class]); + FLTSavePhotoDelegate *delegate = [[FLTSavePhotoDelegate alloc] initWithPath:@"test" + result:mockResult + ioQueue:ioQueue]; + + [delegate handlePhotoCaptureResultWithError:error + photoDataProvider:^NSData * { + return nil; + }]; + OCMVerify([mockResult sendError:error]); +} + +- (void)testHandlePhotoCaptureResult_mustSendErrorIfFailedToWrite { + XCTestExpectation *resultExpectation = + [self expectationWithDescription:@"Must send IOError to the result if failed to write file."]; + dispatch_queue_t ioQueue = dispatch_queue_create("test", NULL); + id mockResult = OCMClassMock([FLTThreadSafeFlutterResult class]); + + NSError *ioError = [NSError errorWithDomain:@"IOError" + code:0 + userInfo:@{NSLocalizedDescriptionKey : @"Localized IO Error"}]; + + OCMStub([mockResult sendErrorWithCode:@"IOError" + message:@"Unable to write file" + details:ioError.localizedDescription]) + .andDo(^(NSInvocation *invocation) { + [resultExpectation fulfill]; + }); + FLTSavePhotoDelegate *delegate = [[FLTSavePhotoDelegate alloc] initWithPath:@"test" + result:mockResult + ioQueue:ioQueue]; + + // We can't use OCMClassMock for NSData because some XCTest APIs uses NSData (e.g. + // `XCTRunnerIDESession::logDebugMessage:`) on a private queue. + id mockData = OCMPartialMock([NSData data]); + OCMStub([mockData writeToFile:OCMOCK_ANY + options:NSDataWritingAtomic + error:[OCMArg setTo:ioError]]) + .andReturn(NO); + [delegate handlePhotoCaptureResultWithError:nil + photoDataProvider:^NSData * { + return mockData; + }]; + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testHandlePhotoCaptureResult_mustSendSuccessIfSuccessToWrite { + XCTestExpectation *resultExpectation = [self + expectationWithDescription:@"Must send file path to the result if success to write file."]; + + dispatch_queue_t ioQueue = dispatch_queue_create("test", NULL); + id mockResult = OCMClassMock([FLTThreadSafeFlutterResult class]); + FLTSavePhotoDelegate *delegate = [[FLTSavePhotoDelegate alloc] initWithPath:@"test" + result:mockResult + ioQueue:ioQueue]; + OCMStub([mockResult sendSuccessWithData:delegate.path]).andDo(^(NSInvocation *invocation) { + [resultExpectation fulfill]; + }); + + // We can't use OCMClassMock for NSData because some XCTest APIs uses NSData (e.g. + // `XCTRunnerIDESession::logDebugMessage:`) on a private queue. + id mockData = OCMPartialMock([NSData data]); + OCMStub([mockData writeToFile:OCMOCK_ANY options:NSDataWritingAtomic error:[OCMArg setTo:nil]]) + .andReturn(YES); + + [delegate handlePhotoCaptureResultWithError:nil + photoDataProvider:^NSData * { + return mockData; + }]; + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testHandlePhotoCaptureResult_bothProvideDataAndSaveFileMustRunOnIOQueue { + XCTestExpectation *dataProviderQueueExpectation = + [self expectationWithDescription:@"Data provider must run on io queue."]; + XCTestExpectation *writeFileQueueExpectation = + [self expectationWithDescription:@"File writing must run on io queue"]; + XCTestExpectation *resultExpectation = [self + expectationWithDescription:@"Must send file path to the result if success to write file."]; + + dispatch_queue_t ioQueue = dispatch_queue_create("test", NULL); + const char *ioQueueSpecific = "io_queue_specific"; + dispatch_queue_set_specific(ioQueue, ioQueueSpecific, (void *)ioQueueSpecific, NULL); + id mockResult = OCMClassMock([FLTThreadSafeFlutterResult class]); + OCMStub([mockResult sendSuccessWithData:OCMOCK_ANY]).andDo(^(NSInvocation *invocation) { + [resultExpectation fulfill]; + }); + + // We can't use OCMClassMock for NSData because some XCTest APIs uses NSData (e.g. + // `XCTRunnerIDESession::logDebugMessage:`) on a private queue. + id mockData = OCMPartialMock([NSData data]); + OCMStub([mockData writeToFile:OCMOCK_ANY options:NSDataWritingAtomic error:[OCMArg setTo:nil]]) + .andDo(^(NSInvocation *invocation) { + if (dispatch_get_specific(ioQueueSpecific)) { + [writeFileQueueExpectation fulfill]; + } + }) + .andReturn(YES); + + FLTSavePhotoDelegate *delegate = [[FLTSavePhotoDelegate alloc] initWithPath:@"test" + result:mockResult + ioQueue:ioQueue]; + [delegate handlePhotoCaptureResultWithError:nil + photoDataProvider:^NSData * { + if (dispatch_get_specific(ioQueueSpecific)) { + [dataProviderQueueExpectation fulfill]; + } + return mockData; + }]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +@end diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.modulemap b/packages/camera/camera/ios/Classes/CameraPlugin.modulemap index a695728fc87a..529c6580e908 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin.modulemap +++ b/packages/camera/camera/ios/Classes/CameraPlugin.modulemap @@ -9,5 +9,11 @@ framework module camera { header "CameraProperties.h" header "FLTCam.h" header "FLTCam_Test.h" + header "FLTSavePhotoDelegate_Test.h" + header "FLTThreadSafeEventChannel.h" + header "FLTThreadSafeFlutterResult.h" + header "FLTThreadSafeMethodChannel.h" + header "FLTThreadSafeTextureRegistry.h" + header "QueueHelper.h" } } diff --git a/packages/camera/camera/ios/Classes/FLTCam.m b/packages/camera/camera/ios/Classes/FLTCam.m index 82ac6714d689..94f985066675 100644 --- a/packages/camera/camera/ios/Classes/FLTCam.m +++ b/packages/camera/camera/ios/Classes/FLTCam.m @@ -4,6 +4,7 @@ #import "FLTCam.h" #import "FLTCam_Test.h" +#import "FLTSavePhotoDelegate.h" @import CoreMotion; #import @@ -41,71 +42,6 @@ - (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments } @end -@interface FLTSavePhotoDelegate : NSObject -@property(readonly, nonatomic) NSString *path; -@property(readonly, nonatomic) FLTThreadSafeFlutterResult *result; -@end - -@implementation FLTSavePhotoDelegate { - /// Used to keep the delegate alive until didFinishProcessingPhotoSampleBuffer. - FLTSavePhotoDelegate *selfReference; -} - -- initWithPath:(NSString *)path result:(FLTThreadSafeFlutterResult *)result { - self = [super init]; - NSAssert(self, @"super init cannot be nil"); - _path = path; - selfReference = self; - _result = result; - return self; -} - -- (void)captureOutput:(AVCapturePhotoOutput *)output - didFinishProcessingPhotoSampleBuffer:(CMSampleBufferRef)photoSampleBuffer - previewPhotoSampleBuffer:(CMSampleBufferRef)previewPhotoSampleBuffer - resolvedSettings:(AVCaptureResolvedPhotoSettings *)resolvedSettings - bracketSettings:(AVCaptureBracketedStillImageSettings *)bracketSettings - error:(NSError *)error API_AVAILABLE(ios(10)) { - selfReference = nil; - if (error) { - [_result sendError:error]; - return; - } - - NSData *data = [AVCapturePhotoOutput - JPEGPhotoDataRepresentationForJPEGSampleBuffer:photoSampleBuffer - previewPhotoSampleBuffer:previewPhotoSampleBuffer]; - - // TODO(sigurdm): Consider writing file asynchronously. - bool success = [data writeToFile:_path atomically:YES]; - - if (!success) { - [_result sendErrorWithCode:@"IOError" message:@"Unable to write file" details:nil]; - return; - } - [_result sendSuccessWithData:_path]; -} - -- (void)captureOutput:(AVCapturePhotoOutput *)output - didFinishProcessingPhoto:(AVCapturePhoto *)photo - error:(NSError *)error API_AVAILABLE(ios(11.0)) { - selfReference = nil; - if (error) { - [_result sendError:error]; - return; - } - - NSData *photoData = [photo fileDataRepresentation]; - - bool success = [photoData writeToFile:_path atomically:YES]; - if (!success) { - [_result sendErrorWithCode:@"IOError" message:@"Unable to write file" details:nil]; - return; - } - [_result sendSuccessWithData:_path]; -} -@end - @interface FLTCam () @@ -138,8 +74,11 @@ @interface FLTCam () +/// The file path for the captured photo. +@property(readonly, nonatomic) NSString *path; +/// The thread safe flutter result wrapper to report the result. +@property(readonly, nonatomic) FLTThreadSafeFlutterResult *result; +/// The queue on which captured photos are wrote to disk. +@property(strong, nonatomic) dispatch_queue_t ioQueue; +/// Used to keep the delegate alive until didFinishProcessingPhotoSampleBuffer. +@property(strong, nonatomic, nullable) FLTSavePhotoDelegate *selfReference; + +/** + * Initialize a photo capture delegate. + * @param path the path for captured photo file. + * @param result the thread safe flutter result wrapper to report the result. + * @param ioQueue the queue on which captured photos are wrote to disk. + */ +- (instancetype)initWithPath:(NSString *)path + result:(FLTThreadSafeFlutterResult *)result + ioQueue:(dispatch_queue_t)ioQueue; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.m b/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.m new file mode 100644 index 000000000000..8dadfec7fecd --- /dev/null +++ b/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.m @@ -0,0 +1,65 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FLTSavePhotoDelegate.h" + +@implementation FLTSavePhotoDelegate + +- (instancetype)initWithPath:(NSString *)path + result:(FLTThreadSafeFlutterResult *)result + ioQueue:(dispatch_queue_t)ioQueue { + self = [super init]; + NSAssert(self, @"super init cannot be nil"); + _path = path; + _selfReference = self; + _result = result; + _ioQueue = ioQueue; + return self; +} + +- (void)handlePhotoCaptureResultWithError:(NSError *)error + photoDataProvider:(NSData * (^)(void))photoDataProvider { + self.selfReference = nil; + if (error) { + [self.result sendError:error]; + return; + } + dispatch_async(self.ioQueue, ^{ + NSData *data = photoDataProvider(); + NSError *ioError; + if ([data writeToFile:self.path options:NSDataWritingAtomic error:&ioError]) { + [self.result sendSuccessWithData:self.path]; + } else { + [self.result sendErrorWithCode:@"IOError" + message:@"Unable to write file" + details:ioError.localizedDescription]; + } + }); +} + +- (void)captureOutput:(AVCapturePhotoOutput *)output + didFinishProcessingPhotoSampleBuffer:(CMSampleBufferRef)photoSampleBuffer + previewPhotoSampleBuffer:(CMSampleBufferRef)previewPhotoSampleBuffer + resolvedSettings:(AVCaptureResolvedPhotoSettings *)resolvedSettings + bracketSettings:(AVCaptureBracketedStillImageSettings *)bracketSettings + error:(NSError *)error API_AVAILABLE(ios(10)) { + [self handlePhotoCaptureResultWithError:error + photoDataProvider:^NSData * { + return [AVCapturePhotoOutput + JPEGPhotoDataRepresentationForJPEGSampleBuffer:photoSampleBuffer + previewPhotoSampleBuffer: + previewPhotoSampleBuffer]; + }]; +} + +- (void)captureOutput:(AVCapturePhotoOutput *)output + didFinishProcessingPhoto:(AVCapturePhoto *)photo + error:(NSError *)error API_AVAILABLE(ios(11.0)) { + [self handlePhotoCaptureResultWithError:error + photoDataProvider:^NSData * { + return [photo fileDataRepresentation]; + }]; +} + +@end diff --git a/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate_Test.h b/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate_Test.h new file mode 100644 index 000000000000..c0b77c7bac83 --- /dev/null +++ b/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate_Test.h @@ -0,0 +1,17 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FLTSavePhotoDelegate.h" + +/** + API exposed for unit tests. + */ +@interface FLTSavePhotoDelegate () + +/// Handler to write captured photo data into a file. +/// @param error the capture error. +/// @param photoDataProvider a closure that provides photo data. +- (void)handlePhotoCaptureResultWithError:(NSError *)error + photoDataProvider:(NSData * (^)(void))photoDataProvider; +@end diff --git a/packages/camera/camera/ios/Classes/camera-umbrella.h b/packages/camera/camera/ios/Classes/camera-umbrella.h index 428b125d3a43..5c39401e6261 100644 --- a/packages/camera/camera/ios/Classes/camera-umbrella.h +++ b/packages/camera/camera/ios/Classes/camera-umbrella.h @@ -4,11 +4,6 @@ #import #import -#import -#import -#import -#import -#import FOUNDATION_EXPORT double cameraVersionNumber; FOUNDATION_EXPORT const unsigned char cameraVersionString[]; diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 0f1a0f049d76..4516e5593023 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+9 +version: 0.9.4+10 environment: sdk: ">=2.14.0 <3.0.0" From 37b592c4b91917d45dd9e64810b52005c4d06885 Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Thu, 3 Feb 2022 16:15:23 -0800 Subject: [PATCH 136/600] [video_player] Wait to initialize m3u8 videos on iOS until size is set (#4727) --- .../video_player_avfoundation/CHANGELOG.md | 3 ++- .../example/ios/Runner.xcodeproj/project.pbxproj | 4 ++-- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- .../example/ios/RunnerTests/VideoPlayerTests.m | 16 ++++++++++++++++ .../ios/Classes/FLTVideoPlayerPlugin.m | 8 ++++++-- .../video_player_avfoundation/pubspec.yaml | 2 +- 6 files changed, 28 insertions(+), 7 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index cc042ba2bfaa..161e9002902e 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.2.18 +* Wait to initialize m3u8 videos until size is set, fixing aspect ratio. * Adjusts test timeouts for network-dependent native tests to avoid flake. ## 2.2.17 diff --git a/packages/video_player/video_player_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj b/packages/video_player/video_player_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj index d19d578b6fb4..6069bf313e8e 100644 --- a/packages/video_player/video_player_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/video_player/video_player_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 46; objects = { /* Begin PBXBuildFile section */ @@ -269,7 +269,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1320; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { diff --git a/packages/video_player/video_player_avfoundation/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/video_player/video_player_avfoundation/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 0632b6533bc8..c5858c80e959 100644 --- a/packages/video_player/video_player_avfoundation/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/video_player/video_player_avfoundation/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ *registry = + (NSObject *)[[UIApplication sharedApplication] delegate]; + NSObject *registrar = [registry registrarForPlugin:@"TestHLSControls"]; + + FLTVideoPlayerPlugin *videoPlayerPlugin = + (FLTVideoPlayerPlugin *)[[FLTVideoPlayerPlugin alloc] initWithRegistrar:registrar]; + + NSDictionary *videoInitialization = + [self testPlugin:videoPlayerPlugin + uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8"]; + XCTAssertEqualObjects(videoInitialization[@"height"], @720); + XCTAssertEqualObjects(videoInitialization[@"width"], @1280); + XCTAssertEqualWithAccuracy([videoInitialization[@"duration"] intValue], 4000, 200); +} + - (NSDictionary *)testPlugin:(FLTVideoPlayerPlugin *)videoPlayerPlugin uri:(NSString *)uri { FlutterError *error; diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m index 9cc40e388cef..55021cce64f9 100644 --- a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m +++ b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m @@ -354,9 +354,13 @@ - (void)setupEventSinkIfReadyToPlay { } BOOL hasVideoTracks = [asset tracksWithMediaType:AVMediaTypeVideo].count != 0; + BOOL hasNoTracks = asset.tracks.count == 0; - // The player has not yet initialized when it contains video tracks. - if (hasVideoTracks && height == CGSizeZero.height && width == CGSizeZero.width) { + // The player has not yet initialized when it has no size, unless it is an audio-only track. + // HLS m3u8 video files never load any tracks, and are also not yet initialized until they have + // a size. + if ((hasVideoTracks || hasNoTracks) && height == CGSizeZero.height && + width == CGSizeZero.width) { return; } // The player may be initialized but still needs to determine the duration. diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index 82ffedec2527..8b00249ee453 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_avfoundation description: iOS implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.2.17 +version: 2.2.18 environment: sdk: ">=2.14.0 <3.0.0" From 9b2d46a493e38b149a820f4cb07cebe63aa93f76 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 3 Feb 2022 20:00:21 -0500 Subject: [PATCH 137/600] [image_picker] Fix NSNull handling on iOS (#4728) --- .../image_picker/image_picker/CHANGELOG.md | 4 ++++ .../ios/Runner.xcodeproj/project.pbxproj | 2 +- .../ios/RunnerTests/PhotoAssetUtilTests.m | 5 ++-- .../ios/Classes/FLTImagePickerImageUtil.h | 5 ++-- .../ios/Classes/FLTImagePickerImageUtil.m | 4 ++-- .../ios/Classes/FLTImagePickerPlugin.m | 23 +++++++++++++------ .../FLTPHPickerSaveImageToPathOperation.m | 2 +- .../image_picker/image_picker/pubspec.yaml | 2 +- 8 files changed, 30 insertions(+), 17 deletions(-) diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index 1b7d0ffd4b57..1712aa0d3bab 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.4+6 + +* Fixes minor type issues in iOS implementation. + ## 0.8.4+5 * Improves the documentation on handling MainActivity being killed by the Android OS. diff --git a/packages/image_picker/image_picker/example/ios/Runner.xcodeproj/project.pbxproj b/packages/image_picker/image_picker/example/ios/Runner.xcodeproj/project.pbxproj index 22d203256261..f8fe6683482a 100644 --- a/packages/image_picker/image_picker/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/image_picker/image_picker/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ diff --git a/packages/image_picker/image_picker/example/ios/RunnerTests/PhotoAssetUtilTests.m b/packages/image_picker/image_picker/example/ios/RunnerTests/PhotoAssetUtilTests.m index b81b29f73cef..c38930f5cef5 100644 --- a/packages/image_picker/image_picker/example/ios/RunnerTests/PhotoAssetUtilTests.m +++ b/packages/image_picker/image_picker/example/ios/RunnerTests/PhotoAssetUtilTests.m @@ -101,11 +101,10 @@ - (void)testSaveImageWithOriginalImageData_ShouldSaveAsGifAnimation { size_t numberOfFrames = CGImageSourceGetCount(imageSource); - NSNumber *nilSize = (NSNumber *)[NSNull null]; NSString *savedPathGIF = [FLTImagePickerPhotoAssetUtil saveImageWithOriginalImageData:dataGIF image:imageGIF - maxWidth:nilSize - maxHeight:nilSize + maxWidth:nil + maxHeight:nil imageQuality:nil]; XCTAssertNotNil(savedPathGIF); XCTAssertEqualObjects([savedPathGIF substringFromIndex:savedPathGIF.length - 4], @".gif"); diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerImageUtil.h b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerImageUtil.h index b0edd03e5076..5e77a6ca67ae 100644 --- a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerImageUtil.h +++ b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerImageUtil.h @@ -18,9 +18,10 @@ NS_ASSUME_NONNULL_BEGIN @interface FLTImagePickerImageUtil : NSObject +// Resizes the given image to fit within maxWidth (if non-nil) and maxHeight (if non-nil) + (UIImage *)scaledImage:(UIImage *)image - maxWidth:(NSNumber *)maxWidth - maxHeight:(NSNumber *)maxHeight + maxWidth:(nullable NSNumber *)maxWidth + maxHeight:(nullable NSNumber *)maxHeight isMetadataAvailable:(BOOL)isMetadataAvailable; // Resize all gif animation frames. diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerImageUtil.m b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerImageUtil.m index 7b454072ecff..2d370aa2e6c8 100644 --- a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerImageUtil.m +++ b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerImageUtil.m @@ -35,8 +35,8 @@ + (UIImage *)scaledImage:(UIImage *)image double originalWidth = image.size.width; double originalHeight = image.size.height; - bool hasMaxWidth = maxWidth != (id)[NSNull null]; - bool hasMaxHeight = maxHeight != (id)[NSNull null]; + bool hasMaxWidth = maxWidth != nil; + bool hasMaxHeight = maxHeight != nil; double width = hasMaxWidth ? MIN([maxWidth doubleValue], originalWidth) : originalWidth; double height = hasMaxHeight ? MIN([maxHeight doubleValue], originalHeight) : originalHeight; diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m index cf3103195482..65a5fb8a71dd 100644 --- a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m @@ -16,6 +16,15 @@ #import "FLTImagePickerPhotoAssetUtil.h" #import "FLTPHPickerSaveImageToPathOperation.h" +/** + * Returns the value for the given key in 'dict', or nil if the value is + * NSNull. + */ +id GetNullableValueForKey(NSDictionary *dict, NSString *key) { + id value = dict[key]; + return value == [NSNull null] ? nil : value; +} + @interface FLTImagePickerPlugin () _arguments objectForKey:@"maxWidth"]; - NSNumber *maxHeight = [self->_arguments objectForKey:@"maxHeight"]; - NSNumber *imageQuality = [self->_arguments objectForKey:@"imageQuality"]; + NSNumber *maxWidth = GetNullableValueForKey(self->_arguments, @"maxWidth"); + NSNumber *maxHeight = GetNullableValueForKey(self->_arguments, @"maxHeight"); + NSNumber *imageQuality = GetNullableValueForKey(self->_arguments, @"imageQuality"); NSNumber *desiredImageQuality = [self getDesiredImageQuality:imageQuality]; NSOperationQueue *operationQueue = [NSOperationQueue new]; NSMutableArray *pathList = [self createNSMutableArrayWithSize:results.count]; @@ -480,14 +489,14 @@ - (void)imagePickerController:(UIImagePickerController *)picker if (image == nil) { image = [info objectForKey:UIImagePickerControllerOriginalImage]; } - NSNumber *maxWidth = [_arguments objectForKey:@"maxWidth"]; - NSNumber *maxHeight = [_arguments objectForKey:@"maxHeight"]; - NSNumber *imageQuality = [_arguments objectForKey:@"imageQuality"]; + NSNumber *maxWidth = GetNullableValueForKey(_arguments, @"maxWidth"); + NSNumber *maxHeight = GetNullableValueForKey(_arguments, @"maxHeight"); + NSNumber *imageQuality = GetNullableValueForKey(_arguments, @"imageQuality"); NSNumber *desiredImageQuality = [self getDesiredImageQuality:imageQuality]; PHAsset *originalAsset = [FLTImagePickerPhotoAssetUtil getAssetFromImagePickerInfo:info]; - if (maxWidth != (id)[NSNull null] || maxHeight != (id)[NSNull null]) { + if (maxWidth != nil || maxHeight != nil) { image = [FLTImagePickerImageUtil scaledImage:image maxWidth:maxWidth maxHeight:maxHeight diff --git a/packages/image_picker/image_picker/ios/Classes/FLTPHPickerSaveImageToPathOperation.m b/packages/image_picker/image_picker/ios/Classes/FLTPHPickerSaveImageToPathOperation.m index 30da22774d07..5a084c455f22 100644 --- a/packages/image_picker/image_picker/ios/Classes/FLTPHPickerSaveImageToPathOperation.m +++ b/packages/image_picker/image_picker/ios/Classes/FLTPHPickerSaveImageToPathOperation.m @@ -91,7 +91,7 @@ - (void)start { PHAsset *originalAsset = [FLTImagePickerPhotoAssetUtil getAssetFromPHPickerResult:self.result]; - if (self.maxWidth != (id)[NSNull null] || self.maxHeight != (id)[NSNull null]) { + if (self.maxWidth != nil || self.maxHeight != nil) { localImage = [FLTImagePickerImageUtil scaledImage:localImage maxWidth:self.maxWidth maxHeight:self.maxHeight diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 836c3ca93f3c..c072244263d0 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.4+5 +version: 0.8.4+6 environment: sdk: ">=2.14.0 <3.0.0" From c5c8cda753c2bd69af905f331b33147252dc4b5a Mon Sep 17 00:00:00 2001 From: Harry Terkelsen Date: Thu, 3 Feb 2022 18:15:22 -0800 Subject: [PATCH 138/600] [url_launcher] Mark Link as an invisible factory (#4578) --- packages/url_launcher/url_launcher_web/CHANGELOG.md | 4 ++++ .../url_launcher_web/lib/src/shims/dart_ui_fake.dart | 3 ++- .../url_launcher/url_launcher_web/lib/url_launcher_web.dart | 3 ++- packages/url_launcher/url_launcher_web/pubspec.yaml | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/url_launcher/url_launcher_web/CHANGELOG.md b/packages/url_launcher/url_launcher_web/CHANGELOG.md index ebc4ab975d07..d0a97f32d782 100644 --- a/packages/url_launcher/url_launcher_web/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.7 + +* Marks the `Link` widget as invisible so it can be optimized by the engine. + ## 2.0.6 * Removes dependency on `meta`. diff --git a/packages/url_launcher/url_launcher_web/lib/src/shims/dart_ui_fake.dart b/packages/url_launcher/url_launcher_web/lib/src/shims/dart_ui_fake.dart index 8757ca22be17..f51dce946acc 100644 --- a/packages/url_launcher/url_launcher_web/lib/src/shims/dart_ui_fake.dart +++ b/packages/url_launcher/url_launcher_web/lib/src/shims/dart_ui_fake.dart @@ -16,7 +16,8 @@ class platformViewRegistry { /// Shim for registerViewFactory /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L72 static bool registerViewFactory( - String viewTypeId, html.Element Function(int viewId) viewFactory) { + String viewTypeId, html.Element Function(int viewId) viewFactory, + {bool isVisible = true}) { return false; } } diff --git a/packages/url_launcher/url_launcher_web/lib/url_launcher_web.dart b/packages/url_launcher/url_launcher_web/lib/url_launcher_web.dart index 76ef1d17f64e..72540c3c3b80 100644 --- a/packages/url_launcher/url_launcher_web/lib/url_launcher_web.dart +++ b/packages/url_launcher/url_launcher_web/lib/url_launcher_web.dart @@ -46,7 +46,8 @@ class UrlLauncherPlugin extends UrlLauncherPlatform { /// Registers this class as the default instance of [UrlLauncherPlatform]. static void registerWith(Registrar registrar) { UrlLauncherPlatform.instance = UrlLauncherPlugin(); - ui.platformViewRegistry.registerViewFactory(linkViewType, linkViewFactory); + ui.platformViewRegistry + .registerViewFactory(linkViewType, linkViewFactory, isVisible: false); } @override diff --git a/packages/url_launcher/url_launcher_web/pubspec.yaml b/packages/url_launcher/url_launcher_web/pubspec.yaml index d0b1ff7c6b57..b3a2de035067 100644 --- a/packages/url_launcher/url_launcher_web/pubspec.yaml +++ b/packages/url_launcher/url_launcher_web/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_web description: Web platform implementation of url_launcher repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 2.0.6 +version: 2.0.7 environment: sdk: ">=2.12.0 <3.0.0" From c808ab8c9c71cf99ca3656895d1624caddff320e Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 4 Feb 2022 13:15:23 -0500 Subject: [PATCH 139/600] [ci] Manually roll to latest Flutter (#4736) --- .ci/flutter_master.version | 2 +- packages/webview_flutter/webview_flutter_web/CHANGELOG.md | 4 ++++ .../test/webview_flutter_web_test.mocks.dart | 6 ++++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 39e290b7650a..03eb2fd12e73 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -3e6e996f9eff50d77214d37c1c5f8b71bcc0d559 +6d96e66d0f70891fd912753abd201534aa41c0a4 diff --git a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md index 3f9f2c80c6af..ba8fc0fb01c9 100644 --- a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Fixes unit tests to run on latest `master` version of Flutter. + ## 0.1.0+1 * Adds an explanation of registering the implementation in the README. diff --git a/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.mocks.dart b/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.mocks.dart index 2507cd292142..e35d1e93c59f 100644 --- a/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.mocks.dart @@ -929,10 +929,12 @@ class MockIFrameElement extends _i1.Mock implements _i2.IFrameElement { void setPointerCapture(int? pointerId) => super.noSuchMethod(Invocation.method(#setPointerCapture, [pointerId]), returnValueForMissingStub: null); - @override + // TODO(ditman): Undo this manual change when the return type change to + // Future has propagated to stable. + /*@override void requestFullscreen() => super.noSuchMethod(Invocation.method(#requestFullscreen, []), - returnValueForMissingStub: null); + returnValueForMissingStub: null);*/ @override void after(Object? nodes) => super.noSuchMethod(Invocation.method(#after, [nodes]), From 87ffa56bfc4088c02f43c7c0b6be0c484f1affa5 Mon Sep 17 00:00:00 2001 From: Jukka-Pekka Siitonen Date: Fri, 4 Feb 2022 20:55:24 +0200 Subject: [PATCH 140/600] [shared_preferences] Documentation update for Readme.md (#4684) --- .../shared_preferences/CHANGELOG.md | 4 + .../shared_preferences/README.md | 79 +++++++++++++------ .../shared_preferences/pubspec.yaml | 2 +- 3 files changed, 58 insertions(+), 27 deletions(-) diff --git a/packages/shared_preferences/shared_preferences/CHANGELOG.md b/packages/shared_preferences/shared_preferences/CHANGELOG.md index 978fab91389e..36e60cc35763 100644 --- a/packages/shared_preferences/shared_preferences/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.13 + +* Updates documentation on README.md. + ## 2.0.12 * Removes dependency on `meta`. diff --git a/packages/shared_preferences/shared_preferences/README.md b/packages/shared_preferences/shared_preferences/README.md index 46d022f4647c..d3295ac6f6b9 100644 --- a/packages/shared_preferences/shared_preferences/README.md +++ b/packages/shared_preferences/shared_preferences/README.md @@ -3,38 +3,54 @@ [![pub package](https://img.shields.io/pub/v/shared_preferences.svg)](https://pub.dev/packages/shared_preferences) Wraps platform-specific persistent storage for simple data -(NSUserDefaults on iOS and macOS, SharedPreferences on Android, etc.). Data may be persisted to disk asynchronously, +(NSUserDefaults on iOS and macOS, SharedPreferences on Android, etc.). +Data may be persisted to disk asynchronously, and there is no guarantee that writes will be persisted to disk after returning, so this plugin must not be used for storing critical data. +Supported data types are `int`, `double`, `bool`, `String` and `List`. + ## Usage To use this plugin, add `shared_preferences` as a [dependency in your pubspec.yaml file](https://flutter.dev/docs/development/platform-integration/platform-channels). -### Example - -``` dart -import 'package:flutter/material.dart'; -import 'package:shared_preferences/shared_preferences.dart'; - -void main() { - runApp(MaterialApp( - home: Scaffold( - body: Center( - child: RaisedButton( - onPressed: _incrementCounter, - child: Text('Increment Counter'), - ), - ), - ), - )); -} - -_incrementCounter() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - int counter = (prefs.getInt('counter') ?? 0) + 1; - print('Pressed $counter times.'); - await prefs.setInt('counter', counter); -} +### Examples +Here are small examples that show you how to use the API. + +#### Write data +```dart +// Obtain shared preferences. +final prefs = await SharedPreferences.getInstance(); + +// Save an integer value to 'counter' key. +await prefs.setInt('counter', 10); +// Save an boolean value to 'repeat' key. +await prefs.setBool('repeat', true); +// Save an double value to 'decimal' key. +await prefs.setDouble('decimal', 1.5); +// Save an String value to 'action' key. +await prefs.setString('action', 'Start'); +// Save an list of strings to 'items' key. +await prefs.setStringList('items', ['Earth', 'Moon', 'Sun']); +``` + +#### Read data +```dart +// Try reading data from the 'counter' key. If it doesn't exist, returns null. +final int? counter = prefs.getInt('counter'); +// Try reading data from the 'repeat' key. If it doesn't exist, returns null. +final bool? repeat = prefs.getBool('repeat'); +// Try reading data from the 'decimal' key. If it doesn't exist, returns null. +final double? decimal = prefs.getDouble('decimal'); +// Try reading data from the 'action' key. If it doesn't exist, returns null. +final String? action = prefs.getString('action'); +// Try reading data from the 'items' key. If it doesn't exist, returns null. +final List? items = prefs.getStringList('items'); +``` + +#### Remove an entry +```dart +// Remove data for the 'counter' key. +final success = await prefs.remove('counter'); ``` ### Testing @@ -45,3 +61,14 @@ You can populate `SharedPreferences` with initial values in your tests by runnin Map values = {'counter': 1}; SharedPreferences.setMockInitialValues(values); ``` + +### Storage location by platform + +| Platform | Location | +| :--- | :--- | +| Android | SharedPreferences | +| iOS | NSUserDefaults | +| Linux | In the XDG_DATA_HOME directory | +| macOS | NSUserDefaults | +| Web | LocalStorage | +| Windows | In the roaming AppData directory | diff --git a/packages/shared_preferences/shared_preferences/pubspec.yaml b/packages/shared_preferences/shared_preferences/pubspec.yaml index d942b2b2084c..39b48ef51ed2 100644 --- a/packages/shared_preferences/shared_preferences/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for reading and writing simple key-value pairs. Wraps NSUserDefaults on iOS and SharedPreferences on Android. repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.12 +version: 2.0.13 environment: sdk: ">=2.14.0 <3.0.0" From 0907214da7411ea23460bcf103cf4919c44a94c6 Mon Sep 17 00:00:00 2001 From: godofredoc Date: Fri, 4 Feb 2022 11:35:24 -0800 Subject: [PATCH 141/600] create scorecards-analysis.yml (#4734) --- .github/workflows/scorecards-analysis.yml | 55 +++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 .github/workflows/scorecards-analysis.yml diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml new file mode 100644 index 000000000000..1256ec91b8f0 --- /dev/null +++ b/.github/workflows/scorecards-analysis.yml @@ -0,0 +1,55 @@ +name: Scorecards supply-chain security +on: + # Only the default branch is supported. + branch_protection_rule: + schedule: + - cron: '15 19 * * 6' + push: + branches: [ main ] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecards analysis + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + actions: read + contents: read + + steps: + - name: "Checkout code" + uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@b614d455ee90608b5e36e3299cd50d457eb37d5f # v1.0.3 + with: + results_file: results.sarif + results_format: sarif + # Read-only PAT token. To create it, + # follow the steps in https://github.com/ossf/scorecard-action#pat-token-creation. + repo_token: ${{ secrets.SCORECARD_READ_TOKEN }} + # Publish the results to enable scorecard badges. For more details, see + # https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories, `publish_results` will automatically be set to `false`, + # regardless of the value entered here. + publish_results: true + + # Upload the results as artifacts (optional). + - name: "Upload artifact" + uses: actions/upload-artifact@82c141cc518b40d92cc801eee768e7aafc9c2fa2 # v2.3.1 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard. + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@5f532563584d71fdef14ee64d17bafb34f751ce5 # v1.0.26 + with: + sarif_file: results.sarif From 16415e7e7b62a788ace7267aad74035da2d631ee Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 4 Feb 2022 15:15:22 -0500 Subject: [PATCH 142/600] Roll Flutter from 6d96e66d0f70 to b662d32a358e (2 revisions) (#4737) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 03eb2fd12e73..87fba173f806 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -6d96e66d0f70891fd912753abd201534aa41c0a4 +b662d32a358e6c6806422e15498dcd063ed0e892 From c0bb25242d4f5cd686ec13074b364507d2a3803d Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 4 Feb 2022 15:55:16 -0500 Subject: [PATCH 143/600] Remove most CODEOWNERS (#4669) --- CODEOWNERS | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 1d52dcefcbef..88ba1f575a4c 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -4,10 +4,9 @@ # These names are just suggestions. It is fine to have your changes # reviewed by someone else. +# Plugin-level rules. +packages/webview_flutter/** @bparrishMines -packages/camera/** @bparrishMines -packages/file_selector/** @ditman -packages/google_maps_flutter/** @cyanglaz -packages/image_picker/** @cyanglaz -packages/in_app_purchase/** @cyanglaz @LHLL -packages/ios_platform_images/** @gaaclarke +# Sub-package-level rules. These should stay last, since the last matching +# entry takes precedence. +packages/**/*_web/** @ditman From 8c7692da4ac5533ea1c23b8e842768e3462ee6bd Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 4 Feb 2022 18:30:22 -0500 Subject: [PATCH 144/600] Roll Flutter from b662d32a358e to 7868284a7342 (10 revisions) (#4740) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 87fba173f806..fc97b921f9de 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b662d32a358e6c6806422e15498dcd063ed0e892 +7868284a73425b297524ecfadcdf06cc324ecc2c From 442049fd72893cbbcd6982b0cb721e3edb80f939 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 4 Feb 2022 19:35:21 -0500 Subject: [PATCH 145/600] Roll Flutter from 7868284a7342 to 3b848ee12d47 (2 revisions) (#4741) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index fc97b921f9de..9b191a28167b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -7868284a73425b297524ecfadcdf06cc324ecc2c +3b848ee12d47f49cd2ae70b91148f6ed70c84d81 From 63f494888ff3f22a843fc1b6d6e652a9c7196ea7 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 4 Feb 2022 20:30:20 -0500 Subject: [PATCH 146/600] [url_launcher_web] Update min Flutter version (#4735) --- .../url_launcher_web/CHANGELOG.md | 57 ++++++++++--------- .../url_launcher_web/pubspec.yaml | 4 +- 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/packages/url_launcher/url_launcher_web/CHANGELOG.md b/packages/url_launcher/url_launcher_web/CHANGELOG.md index d0a97f32d782..7ac1402100bb 100644 --- a/packages/url_launcher/url_launcher_web/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.8 + +* Updates the minimum Flutter version to 2.10, which is required by the change + in 2.0.7. + ## 2.0.7 * Marks the `Link` widget as invisible so it can be optimized by the engine. @@ -22,109 +27,109 @@ - Updated installation instructions in README. -# 2.0.1 +## 2.0.1 - Change sizing code of `Link` widget's `HtmlElementView` so it works well when slotted. -# 2.0.0 +## 2.0.0 - Migrate to null safety. -# 0.1.5+3 +## 0.1.5+3 - Fix Link misalignment [issue](https://github.com/flutter/flutter/issues/70053). -# 0.1.5+2 +## 0.1.5+2 - Update Flutter SDK constraint. -# 0.1.5+1 +## 0.1.5+1 - Substitute `undefined_prefixed_name: ignore` analyzer setting by a `dart:ui` shim with conditional exports. [Issue](https://github.com/flutter/flutter/issues/69309). -# 0.1.5 +## 0.1.5 - Added the web implementation of the Link widget. -# 0.1.4+2 +## 0.1.4+2 - Move `lib/third_party` to `lib/src/third_party`. -# 0.1.4+1 +## 0.1.4+1 - Add a more correct attribution to `package:platform_detect` code. -# 0.1.4 +## 0.1.4 - (Null safety) Remove dependency on `package:platform_detect` - Port unit tests to run with `flutter drive` -# 0.1.3+2 +## 0.1.3+2 - Fix a typo in a test name and fix some style inconsistencies. -# 0.1.3+1 +## 0.1.3+1 - Depend explicitly on the `platform_interface` package that adds the `webOnlyWindowName` parameter. -# 0.1.3 +## 0.1.3 - Added webOnlyWindowName parameter to launch() -# 0.1.2+1 +## 0.1.2+1 - Update docs -# 0.1.2 +## 0.1.2 - Adds "tel" and "sms" support -# 0.1.1+6 +## 0.1.1+6 - Open "mailto" urls with target set as "\_top" on Safari browsers. - Update lower bound of dart dependency to 2.2.0. -# 0.1.1+5 +## 0.1.1+5 - Update lower bound of dart dependency to 2.1.0. -# 0.1.1+4 +## 0.1.1+4 - Declare API stability and compatibility with `1.0.0` (more details at: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0). -# 0.1.1+3 +## 0.1.1+3 - Refactor tests to not rely on the underlying browser behavior. -# 0.1.1+2 +## 0.1.1+2 - Open urls with target "\_top" on iOS PWAs. -# 0.1.1+1 +## 0.1.1+1 - Make the pedantic dev_dependency explicit. -# 0.1.1 +## 0.1.1 - Added support for mailto scheme -# 0.1.0+2 +## 0.1.0+2 - Remove androidx references from the no-op android implemenation. -# 0.1.0+1 +## 0.1.0+1 - Add an android/ folder with no-op implementation to workaround https://github.com/flutter/flutter/issues/46304. - Bump the minimal required Flutter version to 1.10.0. -# 0.1.0 +## 0.1.0 - Update docs and pubspec. -# 0.0.2 +## 0.0.2 - Switch to using `url_launcher_platform_interface`. -# 0.0.1 +## 0.0.1 - Initial open-source release. diff --git a/packages/url_launcher/url_launcher_web/pubspec.yaml b/packages/url_launcher/url_launcher_web/pubspec.yaml index b3a2de035067..8860eebb58ba 100644 --- a/packages/url_launcher/url_launcher_web/pubspec.yaml +++ b/packages/url_launcher/url_launcher_web/pubspec.yaml @@ -2,11 +2,11 @@ name: url_launcher_web description: Web platform implementation of url_launcher repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 2.0.7 +version: 2.0.8 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.10.0" flutter: plugin: From 0eee39145a66c2cc48ecf10b21af74b8b06ef097 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 4 Feb 2022 21:45:22 -0500 Subject: [PATCH 147/600] Roll Flutter from 3b848ee12d47 to ce5fdff8b03a (3 revisions) (#4744) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 9b191a28167b..399a3ed40dea 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -3b848ee12d47f49cd2ae70b91148f6ed70c84d81 +ce5fdff8b03ae12ffcea471c285ef5fba688f6a5 From f7138f7222856623796c2ee2e561f866fcf08d38 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 4 Feb 2022 22:50:19 -0500 Subject: [PATCH 148/600] Roll Flutter from ce5fdff8b03a to db816591b007 (2 revisions) (#4745) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 399a3ed40dea..79c0526b72fd 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ce5fdff8b03ae12ffcea471c285ef5fba688f6a5 +db816591b007e004249b2b38190bb6b214ed1f3d From 018b0875386ca730ae48f593c73526e8e126c28a Mon Sep 17 00:00:00 2001 From: godofredoc Date: Mon, 7 Feb 2022 16:19:08 -0800 Subject: [PATCH 149/600] Remove the on schedule event on scorecards. (#4749) Scorecards is failing because it relies on an environment variable that do not exist on cron jobs. Since we are running the workflow on every PR we can safely delete the the scheduled one. --- .github/workflows/scorecards-analysis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index 1256ec91b8f0..5148f2e19028 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -2,8 +2,6 @@ name: Scorecards supply-chain security on: # Only the default branch is supported. branch_protection_rule: - schedule: - - cron: '15 19 * * 6' push: branches: [ main ] From 6b7083fac62ea222576c401284e69c600dbd1bb4 Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Mon, 7 Feb 2022 17:00:27 -0800 Subject: [PATCH 150/600] [camera]manages ios camera's orientation states on capture session queue (#4748) --- packages/camera/camera/CHANGELOG.md | 4 ++++ .../ios/RunnerTests/CameraOrientationTests.m | 22 +++++++++++++++++++ .../camera/camera/ios/Classes/CameraPlugin.m | 12 +++++----- .../camera/ios/Classes/CameraPlugin_Test.h | 6 ++++- packages/camera/camera/pubspec.yaml | 2 +- 5 files changed, 38 insertions(+), 8 deletions(-) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index f846774ffe0a..c05ea245b9c8 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.4+11 + +* Manages iOS camera's orientation-related states on a background queue to prevent potential race conditions. + ## 0.9.4+10 * iOS performance improvement by moving file writing from the main queue to a background IO queue. diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraOrientationTests.m b/packages/camera/camera/example/ios/RunnerTests/CameraOrientationTests.m index efd65a47dff8..50f3416e7869 100644 --- a/packages/camera/camera/example/ios/RunnerTests/CameraOrientationTests.m +++ b/packages/camera/camera/example/ios/RunnerTests/CameraOrientationTests.m @@ -49,6 +49,28 @@ - (void)testOrientationNotifications { OCMVerifyAll(mockMessenger); } +- (void)testOrientationUpdateMustBeOnCaptureSessionQueue { + XCTestExpectation *queueExpectation = [self + expectationWithDescription:@"Orientation update must happen on the capture session queue"]; + + CameraPlugin *camera = [[CameraPlugin alloc] initWithRegistry:nil messenger:nil]; + const char *captureSessionQueueSpecific = "capture_session_queue"; + dispatch_queue_set_specific(camera.captureSessionQueue, captureSessionQueueSpecific, + (void *)captureSessionQueueSpecific, NULL); + FLTCam *mockCam = OCMClassMock([FLTCam class]); + camera.camera = mockCam; + OCMStub([mockCam setDeviceOrientation:UIDeviceOrientationLandscapeLeft]) + .andDo(^(NSInvocation *invocation) { + if (dispatch_get_specific(captureSessionQueueSpecific)) { + [queueExpectation fulfill]; + } + }); + + [camera orientationChanged: + [self createMockNotificationForOrientation:UIDeviceOrientationLandscapeLeft]]; + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + - (void)rotate:(UIDeviceOrientation)deviceOrientation expectedChannelOrientation:(NSString *)channelOrientation cameraPlugin:(CameraPlugin *)cameraPlugin diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.m b/packages/camera/camera/ios/Classes/CameraPlugin.m index a0adb70ad704..97e995429e14 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin.m +++ b/packages/camera/camera/ios/Classes/CameraPlugin.m @@ -17,7 +17,6 @@ @interface CameraPlugin () @property(readonly, nonatomic) FLTThreadSafeTextureRegistry *registry; @property(readonly, nonatomic) NSObject *messenger; -@property(readonly, nonatomic) FLTCam *camera; @property(readonly, nonatomic) FLTThreadSafeMethodChannel *deviceEventMethodChannel; @end @@ -69,11 +68,12 @@ - (void)orientationChanged:(NSNotification *)note { return; } - if (_camera) { - [_camera setDeviceOrientation:orientation]; - } - - [self sendDeviceOrientation:orientation]; + dispatch_async(self.captureSessionQueue, ^{ + // `FLTCam::setDeviceOrientation` must be called on capture session queue. + [self.camera setDeviceOrientation:orientation]; + // `CameraPlugin::sendDeviceOrientation` can be called on any queue. + [self sendDeviceOrientation:orientation]; + }); } - (void)sendDeviceOrientation:(UIDeviceOrientation)orientation { diff --git a/packages/camera/camera/ios/Classes/CameraPlugin_Test.h b/packages/camera/camera/ios/Classes/CameraPlugin_Test.h index e36b4ab2fa6d..826b05043f78 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin_Test.h +++ b/packages/camera/camera/ios/Classes/CameraPlugin_Test.h @@ -5,14 +5,18 @@ // This header is available in the Test module. Import via "@import camera.Test;" #import +#import #import /// Methods exposed for unit testing. @interface CameraPlugin () -// All FLTCam's state access and capture session related operations should be on run on this queue. +/// All FLTCam's state access and capture session related operations should be on run on this queue. @property(nonatomic, strong) dispatch_queue_t captureSessionQueue; +/// An internal camera object that manages camera's state and performs camera operations. +@property(nonatomic, strong) FLTCam *camera; + /// Inject @p FlutterTextureRegistry and @p FlutterBinaryMessenger for unit testing. - (instancetype)initWithRegistry:(NSObject *)registry messenger:(NSObject *)messenger diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 4516e5593023..f522608f8744 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+10 +version: 0.9.4+11 environment: sdk: ">=2.14.0 <3.0.0" From 7b4d338309dfc53ff2eee55c8dd59f5b9c43c067 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 7 Feb 2022 20:45:23 -0500 Subject: [PATCH 151/600] Roll Flutter from db816591b007 to f483b7d80831 (1 revision) (#4746) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 79c0526b72fd..d38074fc0ff2 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -db816591b007e004249b2b38190bb6b214ed1f3d +f483b7d808315411fc6ccba66cab8807cee2d371 From 039068e35fefa93273fbed750314723bb0e4a567 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Tue, 8 Feb 2022 12:00:26 +0100 Subject: [PATCH 152/600] [image_picker] Refactored tests to expose private interface in separate test header. (#4722) --- .../image_picker/image_picker/CHANGELOG.md | 4 ++ .../ios/RunnerTests/ImagePickerPluginTests.m | 7 +-- .../example/ios/RunnerTests/ImageUtilTests.m | 1 + .../ios/RunnerTests/MetaDataUtilTests.m | 1 + .../ios/RunnerTests/PhotoAssetUtilTests.m | 1 + .../ios/Classes/FLTImagePickerPlugin.m | 3 +- .../ios/Classes/FLTImagePickerPlugin_Test.h | 43 +++++++++++++++++++ .../ios/Classes/ImagePickerPlugin.modulemap | 14 ++++++ .../ios/Classes/image_picker-umbrella.h | 6 +++ .../image_picker/ios/image_picker.podspec | 3 +- .../image_picker/image_picker/pubspec.yaml | 2 +- 11 files changed, 75 insertions(+), 10 deletions(-) create mode 100644 packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin_Test.h create mode 100644 packages/image_picker/image_picker/ios/Classes/ImagePickerPlugin.modulemap create mode 100644 packages/image_picker/image_picker/ios/Classes/image_picker-umbrella.h diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index 1712aa0d3bab..3dbf2c51690e 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.4+7 + +* Refactors unit test to expose private interface via a separate test header instead of the inline declaration. + ## 0.8.4+6 * Fixes minor type issues in iOS implementation. diff --git a/packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerPluginTests.m b/packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerPluginTests.m index cc901f084071..bcaf2d1da37b 100644 --- a/packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerPluginTests.m +++ b/packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerPluginTests.m @@ -5,6 +5,7 @@ #import "ImagePickerTestImages.h" @import image_picker; +@import image_picker.Test; @import XCTest; #import @@ -21,12 +22,6 @@ - (UIViewController *)presentedViewController { @end -@interface FLTImagePickerPlugin (Test) -@property(copy, nonatomic) FlutterResult result; -- (void)handleSavedPathList:(NSMutableArray *)pathList; -- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker; -@end - @interface ImagePickerPluginTests : XCTestCase @property(readonly, nonatomic) id mockUIImagePicker; @property(readonly, nonatomic) id mockAVCaptureDevice; diff --git a/packages/image_picker/image_picker/example/ios/RunnerTests/ImageUtilTests.m b/packages/image_picker/image_picker/example/ios/RunnerTests/ImageUtilTests.m index b793d6e1f3e0..9b9719f88116 100644 --- a/packages/image_picker/image_picker/example/ios/RunnerTests/ImageUtilTests.m +++ b/packages/image_picker/image_picker/example/ios/RunnerTests/ImageUtilTests.m @@ -5,6 +5,7 @@ #import "ImagePickerTestImages.h" @import image_picker; +@import image_picker.Test; @import XCTest; @interface ImageUtilTests : XCTestCase diff --git a/packages/image_picker/image_picker/example/ios/RunnerTests/MetaDataUtilTests.m b/packages/image_picker/image_picker/example/ios/RunnerTests/MetaDataUtilTests.m index 54f9469f2053..4160c51cc600 100644 --- a/packages/image_picker/image_picker/example/ios/RunnerTests/MetaDataUtilTests.m +++ b/packages/image_picker/image_picker/example/ios/RunnerTests/MetaDataUtilTests.m @@ -5,6 +5,7 @@ #import "ImagePickerTestImages.h" @import image_picker; +@import image_picker.Test; @import XCTest; @interface MetaDataUtilTests : XCTestCase diff --git a/packages/image_picker/image_picker/example/ios/RunnerTests/PhotoAssetUtilTests.m b/packages/image_picker/image_picker/example/ios/RunnerTests/PhotoAssetUtilTests.m index c38930f5cef5..97b4b6cd8eb3 100644 --- a/packages/image_picker/image_picker/example/ios/RunnerTests/PhotoAssetUtilTests.m +++ b/packages/image_picker/image_picker/example/ios/RunnerTests/PhotoAssetUtilTests.m @@ -5,6 +5,7 @@ #import "ImagePickerTestImages.h" @import image_picker; +@import image_picker.Test; @import XCTest; @interface PhotoAssetUtilTests : XCTestCase diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m index 65a5fb8a71dd..1301398fb0c1 100644 --- a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m @@ -3,6 +3,7 @@ // found in the LICENSE file. #import "FLTImagePickerPlugin.h" +#import "FLTImagePickerPlugin_Test.h" #import #import @@ -30,8 +31,6 @@ @interface FLTImagePickerPlugin () -@property(copy, nonatomic) FlutterResult result; - @property(assign, nonatomic) int maxImagesAllowed; @property(copy, nonatomic) NSDictionary *arguments; diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin_Test.h b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin_Test.h new file mode 100644 index 000000000000..94eca425eb5e --- /dev/null +++ b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin_Test.h @@ -0,0 +1,43 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This header is available in the Test module. Import via "@import image_picker.Test;" + +#import + +/** Methods exposed for unit testing. */ +@interface FLTImagePickerPlugin () + +/** The Flutter result callback use to report results back to Flutter App. */ +@property(copy, nonatomic) FlutterResult result; + +/** + * Applies NSMutableArray on the FLutterResult. + * + * NSString must be returned by FlutterResult if the single image + * mode is active. It is checked by maxImagesAllowed and + * returns the first object of the pathlist. + * + * NSMutableArray must be returned by FlutterResult if the multi-image + * mode is active. After the pathlist count is checked then it returns + * the pathlist. + * + * @param pathList that should be applied to FlutterResult. + */ +- (void)handleSavedPathList:(NSArray *)pathList; + +/** + * Tells the delegate that the user cancelled the pick operation. + * + * Your delegate’s implementation of this method should dismiss the picker view + * by calling the dismissModalViewControllerAnimated: method of the parent + * view controller. + * + * Implementation of this method is optional, but expected. + * + * @param picker The controller object managing the image picker interface. + */ +- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker; + +@end diff --git a/packages/image_picker/image_picker/ios/Classes/ImagePickerPlugin.modulemap b/packages/image_picker/image_picker/ios/Classes/ImagePickerPlugin.modulemap new file mode 100644 index 000000000000..dc702ea49fb1 --- /dev/null +++ b/packages/image_picker/image_picker/ios/Classes/ImagePickerPlugin.modulemap @@ -0,0 +1,14 @@ +framework module image_picker { + umbrella header "image_picker-umbrella.h" + + export * + module * { export * } + + explicit module Test { + header "FLTImagePickerPlugin_Test.h" + header "FLTImagePickerImageUtil.h" + header "FLTImagePickerMetaDataUtil.h" + header "FLTImagePickerPhotoAssetUtil.h" + header "FLTPHPickerSaveImageToPathOperation.h" + } +} diff --git a/packages/image_picker/image_picker/ios/Classes/image_picker-umbrella.h b/packages/image_picker/image_picker/ios/Classes/image_picker-umbrella.h new file mode 100644 index 000000000000..0d89b2e1f636 --- /dev/null +++ b/packages/image_picker/image_picker/ios/Classes/image_picker-umbrella.h @@ -0,0 +1,6 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import diff --git a/packages/image_picker/image_picker/ios/image_picker.podspec b/packages/image_picker/image_picker/ios/image_picker.podspec index a2bba5c5397b..2a10b1ce01a8 100644 --- a/packages/image_picker/image_picker/ios/image_picker.podspec +++ b/packages/image_picker/image_picker/ios/image_picker.podspec @@ -14,8 +14,9 @@ Downloaded by pub (not CocoaPods). s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/image_picker' } s.documentation_url = 'https://pub.dev/packages/image_picker' - s.source_files = 'Classes/**/*' + s.source_files = 'Classes/**/*.{h,m}' s.public_header_files = 'Classes/**/*.h' + s.module_map = 'Classes/ImagePickerPlugin.modulemap' s.dependency 'Flutter' s.platform = :ios, '9.0' s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index c072244263d0..7142d8238b30 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.4+6 +version: 0.8.4+7 environment: sdk: ">=2.14.0 <3.0.0" From 99feee9996cb7e3213708cab9de41514d7fd9e93 Mon Sep 17 00:00:00 2001 From: BeMacized Date: Tue, 8 Feb 2022 13:39:25 +0100 Subject: [PATCH 153/600] [url_launcher] Move away from shared method channel implementation in native packages. (#4719) * Move away from shared method channel implementation in native packages. * Implement PR Feedback Implement PR Feedback Implement PR Feedback * Fix missed test * Implement PR feedback --- .../url_launcher_android/CHANGELOG.md | 4 + .../urllauncher/MethodCallHandlerImpl.java | 2 +- .../MethodCallHandlerImplTest.java | 2 +- .../lib/url_launcher_android.dart | 60 +++++ .../url_launcher_android/pubspec.yaml | 5 +- .../test/url_launcher_android_test.dart | 234 ++++++++++++++++++ .../url_launcher_ios/CHANGELOG.md | 4 + .../ios/Classes/FLTURLLauncherPlugin.m | 2 +- .../lib/url_launcher_ios.dart | 60 +++++ .../url_launcher_ios/pubspec.yaml | 5 +- .../test/url_launcher_ios_test.dart | 208 ++++++++++++++++ .../url_launcher_linux/CHANGELOG.md | 4 + .../lib/url_launcher_linux.dart | 54 ++++ .../linux/url_launcher_plugin.cc | 2 +- .../url_launcher_linux/pubspec.yaml | 11 +- .../test/url_launcher_linux_test.dart | 144 +++++++++++ .../url_launcher_macos/CHANGELOG.md | 4 + .../lib/url_launcher_macos.dart | 54 ++++ .../macos/Classes/UrlLauncherPlugin.swift | 2 +- .../url_launcher_macos/pubspec.yaml | 11 +- .../test/url_launcher_macos_test.dart | 144 +++++++++++ .../url_launcher_windows/CHANGELOG.md | 3 +- .../lib/url_launcher_windows.dart | 54 ++++ .../url_launcher_windows/pubspec.yaml | 11 +- .../test/url_launcher_windows_test.dart | 144 +++++++++++ .../windows/url_launcher_plugin.cpp | 2 +- 26 files changed, 1213 insertions(+), 17 deletions(-) create mode 100644 packages/url_launcher/url_launcher_android/lib/url_launcher_android.dart create mode 100644 packages/url_launcher/url_launcher_android/test/url_launcher_android_test.dart create mode 100644 packages/url_launcher/url_launcher_ios/lib/url_launcher_ios.dart create mode 100644 packages/url_launcher/url_launcher_ios/test/url_launcher_ios_test.dart create mode 100644 packages/url_launcher/url_launcher_linux/lib/url_launcher_linux.dart create mode 100644 packages/url_launcher/url_launcher_linux/test/url_launcher_linux_test.dart create mode 100644 packages/url_launcher/url_launcher_macos/lib/url_launcher_macos.dart create mode 100644 packages/url_launcher/url_launcher_macos/test/url_launcher_macos_test.dart create mode 100644 packages/url_launcher/url_launcher_windows/lib/url_launcher_windows.dart create mode 100644 packages/url_launcher/url_launcher_windows/test/url_launcher_windows_test.dart diff --git a/packages/url_launcher/url_launcher_android/CHANGELOG.md b/packages/url_launcher/url_launcher_android/CHANGELOG.md index 819b0cc056d5..9ec1f65911c6 100644 --- a/packages/url_launcher/url_launcher_android/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.0.15 + +* Switches to an in-package method channel implementation. + ## 6.0.14 * Updates code for new analysis options. diff --git a/packages/url_launcher/url_launcher_android/android/src/main/java/io/flutter/plugins/urllauncher/MethodCallHandlerImpl.java b/packages/url_launcher/url_launcher_android/android/src/main/java/io/flutter/plugins/urllauncher/MethodCallHandlerImpl.java index 9e798abcdbb5..f7bed8648872 100644 --- a/packages/url_launcher/url_launcher_android/android/src/main/java/io/flutter/plugins/urllauncher/MethodCallHandlerImpl.java +++ b/packages/url_launcher/url_launcher_android/android/src/main/java/io/flutter/plugins/urllauncher/MethodCallHandlerImpl.java @@ -61,7 +61,7 @@ void startListening(BinaryMessenger messenger) { stopListening(); } - channel = new MethodChannel(messenger, "plugins.flutter.io/url_launcher"); + channel = new MethodChannel(messenger, "plugins.flutter.io/url_launcher_android"); channel.setMethodCallHandler(this); } diff --git a/packages/url_launcher/url_launcher_android/android/src/test/java/io/flutter/plugins/urllauncher/MethodCallHandlerImplTest.java b/packages/url_launcher/url_launcher_android/android/src/test/java/io/flutter/plugins/urllauncher/MethodCallHandlerImplTest.java index 5e0811399ac6..b60192531dbd 100644 --- a/packages/url_launcher/url_launcher_android/android/src/test/java/io/flutter/plugins/urllauncher/MethodCallHandlerImplTest.java +++ b/packages/url_launcher/url_launcher_android/android/src/test/java/io/flutter/plugins/urllauncher/MethodCallHandlerImplTest.java @@ -27,7 +27,7 @@ @RunWith(RobolectricTestRunner.class) public class MethodCallHandlerImplTest { - private static final String CHANNEL_NAME = "plugins.flutter.io/url_launcher"; + private static final String CHANNEL_NAME = "plugins.flutter.io/url_launcher_android"; private UrlLauncher urlLauncher; private MethodCallHandlerImpl methodCallHandler; diff --git a/packages/url_launcher/url_launcher_android/lib/url_launcher_android.dart b/packages/url_launcher/url_launcher_android/lib/url_launcher_android.dart new file mode 100644 index 000000000000..52c46356489d --- /dev/null +++ b/packages/url_launcher/url_launcher_android/lib/url_launcher_android.dart @@ -0,0 +1,60 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:url_launcher_platform_interface/link.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +const MethodChannel _channel = + MethodChannel('plugins.flutter.io/url_launcher_android'); + +/// An implementation of [UrlLauncherPlatform] for Android. +class UrlLauncherAndroid extends UrlLauncherPlatform { + /// Registers this class as the default instance of [UrlLauncherPlatform]. + static void registerWith() { + UrlLauncherPlatform.instance = UrlLauncherAndroid(); + } + + @override + final LinkDelegate? linkDelegate = null; + + @override + Future canLaunch(String url) { + return _channel.invokeMethod( + 'canLaunch', + {'url': url}, + ).then((bool? value) => value ?? false); + } + + @override + Future closeWebView() { + return _channel.invokeMethod('closeWebView'); + } + + @override + Future launch( + String url, { + required bool useSafariVC, + required bool useWebView, + required bool enableJavaScript, + required bool enableDomStorage, + required bool universalLinksOnly, + required Map headers, + String? webOnlyWindowName, + }) { + return _channel.invokeMethod( + 'launch', + { + 'url': url, + 'useWebView': useWebView, + 'enableJavaScript': enableJavaScript, + 'enableDomStorage': enableDomStorage, + 'universalLinksOnly': universalLinksOnly, + 'headers': headers, + }, + ).then((bool? value) => value ?? false); + } +} diff --git a/packages/url_launcher/url_launcher_android/pubspec.yaml b/packages/url_launcher/url_launcher_android/pubspec.yaml index ffd00e58c9bc..b8706aeb13f2 100644 --- a/packages/url_launcher/url_launcher_android/pubspec.yaml +++ b/packages/url_launcher/url_launcher_android/pubspec.yaml @@ -2,11 +2,11 @@ name: url_launcher_android description: Android implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.0.14 +version: 6.0.15 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: @@ -15,6 +15,7 @@ flutter: android: package: io.flutter.plugins.urllauncher pluginClass: UrlLauncherPlugin + dartPluginClass: UrlLauncherAndroid dependencies: flutter: diff --git a/packages/url_launcher/url_launcher_android/test/url_launcher_android_test.dart b/packages/url_launcher/url_launcher_android/test/url_launcher_android_test.dart new file mode 100644 index 000000000000..909d2c100ecf --- /dev/null +++ b/packages/url_launcher/url_launcher_android/test/url_launcher_android_test.dart @@ -0,0 +1,234 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:url_launcher_android/url_launcher_android.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('$UrlLauncherAndroid', () { + const MethodChannel channel = + MethodChannel('plugins.flutter.io/url_launcher_android'); + final List log = []; + channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + + // Return null explicitly instead of relying on the implicit null + // returned by the method channel if no return statement is specified. + return null; + }); + + tearDown(() { + log.clear(); + }); + + test('registers instance', () { + UrlLauncherAndroid.registerWith(); + expect(UrlLauncherPlatform.instance, isA()); + }); + + test('canLaunch', () async { + final UrlLauncherAndroid launcher = UrlLauncherAndroid(); + await launcher.canLaunch('http://example.com/'); + expect( + log, + [ + isMethodCall('canLaunch', arguments: { + 'url': 'http://example.com/', + }) + ], + ); + }); + + test('canLaunch should return false if platform returns null', () async { + final UrlLauncherAndroid launcher = UrlLauncherAndroid(); + final bool canLaunch = await launcher.canLaunch('http://example.com/'); + + expect(canLaunch, false); + }); + + test('launch', () async { + final UrlLauncherAndroid launcher = UrlLauncherAndroid(); + await launcher.launch( + 'http://example.com/', + useSafariVC: true, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: const {}, + ); + expect( + log, + [ + isMethodCall('launch', arguments: { + 'url': 'http://example.com/', + 'useWebView': false, + 'enableJavaScript': false, + 'enableDomStorage': false, + 'universalLinksOnly': false, + 'headers': {}, + }) + ], + ); + }); + + test('launch with headers', () async { + final UrlLauncherAndroid launcher = UrlLauncherAndroid(); + await launcher.launch( + 'http://example.com/', + useSafariVC: true, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: const {'key': 'value'}, + ); + expect( + log, + [ + isMethodCall('launch', arguments: { + 'url': 'http://example.com/', + 'useWebView': false, + 'enableJavaScript': false, + 'enableDomStorage': false, + 'universalLinksOnly': false, + 'headers': {'key': 'value'}, + }) + ], + ); + }); + + test('launch universal links only', () async { + final UrlLauncherAndroid launcher = UrlLauncherAndroid(); + await launcher.launch( + 'http://example.com/', + useSafariVC: false, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: true, + headers: const {}, + ); + expect( + log, + [ + isMethodCall('launch', arguments: { + 'url': 'http://example.com/', + 'useWebView': false, + 'enableJavaScript': false, + 'enableDomStorage': false, + 'universalLinksOnly': true, + 'headers': {}, + }) + ], + ); + }); + + test('launch force WebView', () async { + final UrlLauncherAndroid launcher = UrlLauncherAndroid(); + await launcher.launch( + 'http://example.com/', + useSafariVC: true, + useWebView: true, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: const {}, + ); + expect( + log, + [ + isMethodCall('launch', arguments: { + 'url': 'http://example.com/', + 'useWebView': true, + 'enableJavaScript': false, + 'enableDomStorage': false, + 'universalLinksOnly': false, + 'headers': {}, + }) + ], + ); + }); + + test('launch force WebView enable javascript', () async { + final UrlLauncherAndroid launcher = UrlLauncherAndroid(); + await launcher.launch( + 'http://example.com/', + useSafariVC: true, + useWebView: true, + enableJavaScript: true, + enableDomStorage: false, + universalLinksOnly: false, + headers: const {}, + ); + expect( + log, + [ + isMethodCall('launch', arguments: { + 'url': 'http://example.com/', + 'useWebView': true, + 'enableJavaScript': true, + 'enableDomStorage': false, + 'universalLinksOnly': false, + 'headers': {}, + }) + ], + ); + }); + + test('launch force WebView enable DOM storage', () async { + final UrlLauncherAndroid launcher = UrlLauncherAndroid(); + await launcher.launch( + 'http://example.com/', + useSafariVC: true, + useWebView: true, + enableJavaScript: false, + enableDomStorage: true, + universalLinksOnly: false, + headers: const {}, + ); + expect( + log, + [ + isMethodCall('launch', arguments: { + 'url': 'http://example.com/', + 'useWebView': true, + 'enableJavaScript': false, + 'enableDomStorage': true, + 'universalLinksOnly': false, + 'headers': {}, + }) + ], + ); + }); + + test('launch should return false if platform returns null', () async { + final UrlLauncherAndroid launcher = UrlLauncherAndroid(); + final bool launched = await launcher.launch( + 'http://example.com/', + useSafariVC: true, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: const {}, + ); + + expect(launched, false); + }); + + test('closeWebView default behavior', () async { + final UrlLauncherAndroid launcher = UrlLauncherAndroid(); + await launcher.closeWebView(); + expect( + log, + [isMethodCall('closeWebView', arguments: null)], + ); + }); + }); +} diff --git a/packages/url_launcher/url_launcher_ios/CHANGELOG.md b/packages/url_launcher/url_launcher_ios/CHANGELOG.md index 83d4e4efc847..6e0c8d6a20d7 100644 --- a/packages/url_launcher/url_launcher_ios/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.0.15 + +* Switches to an in-package method channel implementation. + ## 6.0.14 * Updates code for new analysis options. diff --git a/packages/url_launcher/url_launcher_ios/ios/Classes/FLTURLLauncherPlugin.m b/packages/url_launcher/url_launcher_ios/ios/Classes/FLTURLLauncherPlugin.m index 9ba9b1331728..1aceedc8b1de 100644 --- a/packages/url_launcher/url_launcher_ios/ios/Classes/FLTURLLauncherPlugin.m +++ b/packages/url_launcher/url_launcher_ios/ios/Classes/FLTURLLauncherPlugin.m @@ -63,7 +63,7 @@ @implementation FLTURLLauncherPlugin + (void)registerWithRegistrar:(NSObject *)registrar { FlutterMethodChannel *channel = - [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/url_launcher" + [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/url_launcher_ios" binaryMessenger:registrar.messenger]; FLTURLLauncherPlugin *plugin = [[FLTURLLauncherPlugin alloc] init]; [registrar addMethodCallDelegate:plugin channel:channel]; diff --git a/packages/url_launcher/url_launcher_ios/lib/url_launcher_ios.dart b/packages/url_launcher/url_launcher_ios/lib/url_launcher_ios.dart new file mode 100644 index 000000000000..84b811b25728 --- /dev/null +++ b/packages/url_launcher/url_launcher_ios/lib/url_launcher_ios.dart @@ -0,0 +1,60 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:url_launcher_platform_interface/link.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +const MethodChannel _channel = + MethodChannel('plugins.flutter.io/url_launcher_ios'); + +/// An implementation of [UrlLauncherPlatform] for iOS. +class UrlLauncherIOS extends UrlLauncherPlatform { + /// Registers this class as the default instance of [UrlLauncherPlatform]. + static void registerWith() { + UrlLauncherPlatform.instance = UrlLauncherIOS(); + } + + @override + final LinkDelegate? linkDelegate = null; + + @override + Future canLaunch(String url) { + return _channel.invokeMethod( + 'canLaunch', + {'url': url}, + ).then((bool? value) => value ?? false); + } + + @override + Future closeWebView() { + return _channel.invokeMethod('closeWebView'); + } + + @override + Future launch( + String url, { + required bool useSafariVC, + required bool useWebView, + required bool enableJavaScript, + required bool enableDomStorage, + required bool universalLinksOnly, + required Map headers, + String? webOnlyWindowName, + }) { + return _channel.invokeMethod( + 'launch', + { + 'url': url, + 'useSafariVC': useSafariVC, + 'enableJavaScript': enableJavaScript, + 'enableDomStorage': enableDomStorage, + 'universalLinksOnly': universalLinksOnly, + 'headers': headers, + }, + ).then((bool? value) => value ?? false); + } +} diff --git a/packages/url_launcher/url_launcher_ios/pubspec.yaml b/packages/url_launcher/url_launcher_ios/pubspec.yaml index a2e0ac2ef577..8a5bfd20c8f4 100644 --- a/packages/url_launcher/url_launcher_ios/pubspec.yaml +++ b/packages/url_launcher/url_launcher_ios/pubspec.yaml @@ -2,11 +2,11 @@ name: url_launcher_ios description: iOS implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.0.14 +version: 6.0.15 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: @@ -14,6 +14,7 @@ flutter: platforms: ios: pluginClass: FLTURLLauncherPlugin + dartPluginClass: UrlLauncherIOS dependencies: flutter: diff --git a/packages/url_launcher/url_launcher_ios/test/url_launcher_ios_test.dart b/packages/url_launcher/url_launcher_ios/test/url_launcher_ios_test.dart new file mode 100644 index 000000000000..8fad5807bddb --- /dev/null +++ b/packages/url_launcher/url_launcher_ios/test/url_launcher_ios_test.dart @@ -0,0 +1,208 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:url_launcher_ios/url_launcher_ios.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('$UrlLauncherIOS', () { + const MethodChannel channel = + MethodChannel('plugins.flutter.io/url_launcher_ios'); + final List log = []; + channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + + // Return null explicitly instead of relying on the implicit null + // returned by the method channel if no return statement is specified. + return null; + }); + + tearDown(() { + log.clear(); + }); + + test('registers instance', () { + UrlLauncherIOS.registerWith(); + expect(UrlLauncherPlatform.instance, isA()); + }); + + test('canLaunch', () async { + final UrlLauncherIOS launcher = UrlLauncherIOS(); + await launcher.canLaunch('http://example.com/'); + expect( + log, + [ + isMethodCall('canLaunch', arguments: { + 'url': 'http://example.com/', + }) + ], + ); + }); + + test('canLaunch should return false if platform returns null', () async { + final UrlLauncherIOS launcher = UrlLauncherIOS(); + final bool canLaunch = await launcher.canLaunch('http://example.com/'); + + expect(canLaunch, false); + }); + + test('launch', () async { + final UrlLauncherIOS launcher = UrlLauncherIOS(); + await launcher.launch( + 'http://example.com/', + useSafariVC: true, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: const {}, + ); + expect( + log, + [ + isMethodCall('launch', arguments: { + 'url': 'http://example.com/', + 'useSafariVC': true, + 'enableJavaScript': false, + 'enableDomStorage': false, + 'universalLinksOnly': false, + 'headers': {}, + }) + ], + ); + }); + + test('launch with headers', () async { + final UrlLauncherIOS launcher = UrlLauncherIOS(); + await launcher.launch( + 'http://example.com/', + useSafariVC: true, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: const {'key': 'value'}, + ); + expect( + log, + [ + isMethodCall('launch', arguments: { + 'url': 'http://example.com/', + 'useSafariVC': true, + 'enableJavaScript': false, + 'enableDomStorage': false, + 'universalLinksOnly': false, + 'headers': {'key': 'value'}, + }) + ], + ); + }); + + test('launch force SafariVC', () async { + final UrlLauncherIOS launcher = UrlLauncherIOS(); + await launcher.launch( + 'http://example.com/', + useSafariVC: true, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: const {}, + ); + expect( + log, + [ + isMethodCall('launch', arguments: { + 'url': 'http://example.com/', + 'useSafariVC': true, + 'enableJavaScript': false, + 'enableDomStorage': false, + 'universalLinksOnly': false, + 'headers': {}, + }) + ], + ); + }); + + test('launch universal links only', () async { + final UrlLauncherIOS launcher = UrlLauncherIOS(); + await launcher.launch( + 'http://example.com/', + useSafariVC: false, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: true, + headers: const {}, + ); + expect( + log, + [ + isMethodCall('launch', arguments: { + 'url': 'http://example.com/', + 'useSafariVC': false, + 'enableJavaScript': false, + 'enableDomStorage': false, + 'universalLinksOnly': true, + 'headers': {}, + }) + ], + ); + }); + + test('launch force SafariVC to false', () async { + final UrlLauncherIOS launcher = UrlLauncherIOS(); + await launcher.launch( + 'http://example.com/', + useSafariVC: false, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: const {}, + ); + expect( + log, + [ + isMethodCall('launch', arguments: { + 'url': 'http://example.com/', + 'useSafariVC': false, + 'enableJavaScript': false, + 'enableDomStorage': false, + 'universalLinksOnly': false, + 'headers': {}, + }) + ], + ); + }); + + test('launch should return false if platform returns null', () async { + final UrlLauncherIOS launcher = UrlLauncherIOS(); + final bool launched = await launcher.launch( + 'http://example.com/', + useSafariVC: true, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: const {}, + ); + + expect(launched, false); + }); + + test('closeWebView default behavior', () async { + final UrlLauncherIOS launcher = UrlLauncherIOS(); + await launcher.closeWebView(); + expect( + log, + [isMethodCall('closeWebView', arguments: null)], + ); + }); + }); +} diff --git a/packages/url_launcher/url_launcher_linux/CHANGELOG.md b/packages/url_launcher/url_launcher_linux/CHANGELOG.md index a641f83a9aaa..f3460947099e 100644 --- a/packages/url_launcher/url_launcher_linux/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_linux/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.4 + +* Switches to an in-package method channel implementation. + ## 2.0.3 * Updates code for new analysis options. diff --git a/packages/url_launcher/url_launcher_linux/lib/url_launcher_linux.dart b/packages/url_launcher/url_launcher_linux/lib/url_launcher_linux.dart new file mode 100644 index 000000000000..87ef3142e3f6 --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/lib/url_launcher_linux.dart @@ -0,0 +1,54 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:url_launcher_platform_interface/link.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +const MethodChannel _channel = + MethodChannel('plugins.flutter.io/url_launcher_linux'); + +/// An implementation of [UrlLauncherPlatform] for Linux. +class UrlLauncherLinux extends UrlLauncherPlatform { + /// Registers this class as the default instance of [UrlLauncherPlatform]. + static void registerWith() { + UrlLauncherPlatform.instance = UrlLauncherLinux(); + } + + @override + final LinkDelegate? linkDelegate = null; + + @override + Future canLaunch(String url) { + return _channel.invokeMethod( + 'canLaunch', + {'url': url}, + ).then((bool? value) => value ?? false); + } + + @override + Future launch( + String url, { + required bool useSafariVC, + required bool useWebView, + required bool enableJavaScript, + required bool enableDomStorage, + required bool universalLinksOnly, + required Map headers, + String? webOnlyWindowName, + }) { + return _channel.invokeMethod( + 'launch', + { + 'url': url, + 'enableJavaScript': enableJavaScript, + 'enableDomStorage': enableDomStorage, + 'universalLinksOnly': universalLinksOnly, + 'headers': headers, + }, + ).then((bool? value) => value ?? false); + } +} diff --git a/packages/url_launcher/url_launcher_linux/linux/url_launcher_plugin.cc b/packages/url_launcher/url_launcher_linux/linux/url_launcher_plugin.cc index d0f6168171c1..b0c7fece0e7c 100644 --- a/packages/url_launcher/url_launcher_linux/linux/url_launcher_plugin.cc +++ b/packages/url_launcher/url_launcher_linux/linux/url_launcher_plugin.cc @@ -12,7 +12,7 @@ #include "url_launcher_plugin_private.h" // See url_launcher_channel.dart for documentation. -const char kChannelName[] = "plugins.flutter.io/url_launcher"; +const char kChannelName[] = "plugins.flutter.io/url_launcher_linux"; const char kBadArgumentsError[] = "Bad Arguments"; const char kLaunchError[] = "Launch Error"; const char kCanLaunchMethod[] = "canLaunch"; diff --git a/packages/url_launcher/url_launcher_linux/pubspec.yaml b/packages/url_launcher/url_launcher_linux/pubspec.yaml index 95b6c3ca04f5..ee559209c2c5 100644 --- a/packages/url_launcher/url_launcher_linux/pubspec.yaml +++ b/packages/url_launcher/url_launcher_linux/pubspec.yaml @@ -2,11 +2,11 @@ name: url_launcher_linux description: Linux implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 2.0.3 +version: 2.0.4 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.5.0" flutter: plugin: @@ -14,7 +14,14 @@ flutter: platforms: linux: pluginClass: UrlLauncherPlugin + dartPluginClass: UrlLauncherLinux dependencies: flutter: sdk: flutter + url_launcher_platform_interface: ^2.0.3 + +dev_dependencies: + flutter_test: + sdk: flutter + test: ^1.16.3 diff --git a/packages/url_launcher/url_launcher_linux/test/url_launcher_linux_test.dart b/packages/url_launcher/url_launcher_linux/test/url_launcher_linux_test.dart new file mode 100644 index 000000000000..7a4399dd4e6c --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/test/url_launcher_linux_test.dart @@ -0,0 +1,144 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:url_launcher_linux/url_launcher_linux.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('$UrlLauncherLinux', () { + const MethodChannel channel = + MethodChannel('plugins.flutter.io/url_launcher_linux'); + final List log = []; + channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + + // Return null explicitly instead of relying on the implicit null + // returned by the method channel if no return statement is specified. + return null; + }); + + tearDown(() { + log.clear(); + }); + + test('registers instance', () { + UrlLauncherLinux.registerWith(); + expect(UrlLauncherPlatform.instance, isA()); + }); + + test('canLaunch', () async { + final UrlLauncherLinux launcher = UrlLauncherLinux(); + await launcher.canLaunch('http://example.com/'); + expect( + log, + [ + isMethodCall('canLaunch', arguments: { + 'url': 'http://example.com/', + }) + ], + ); + }); + + test('canLaunch should return false if platform returns null', () async { + final UrlLauncherLinux launcher = UrlLauncherLinux(); + final bool canLaunch = await launcher.canLaunch('http://example.com/'); + + expect(canLaunch, false); + }); + + test('launch', () async { + final UrlLauncherLinux launcher = UrlLauncherLinux(); + await launcher.launch( + 'http://example.com/', + useSafariVC: true, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: const {}, + ); + expect( + log, + [ + isMethodCall('launch', arguments: { + 'url': 'http://example.com/', + 'enableJavaScript': false, + 'enableDomStorage': false, + 'universalLinksOnly': false, + 'headers': {}, + }) + ], + ); + }); + + test('launch with headers', () async { + final UrlLauncherLinux launcher = UrlLauncherLinux(); + await launcher.launch( + 'http://example.com/', + useSafariVC: true, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: const {'key': 'value'}, + ); + expect( + log, + [ + isMethodCall('launch', arguments: { + 'url': 'http://example.com/', + 'enableJavaScript': false, + 'enableDomStorage': false, + 'universalLinksOnly': false, + 'headers': {'key': 'value'}, + }) + ], + ); + }); + + test('launch universal links only', () async { + final UrlLauncherLinux launcher = UrlLauncherLinux(); + await launcher.launch( + 'http://example.com/', + useSafariVC: false, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: true, + headers: const {}, + ); + expect( + log, + [ + isMethodCall('launch', arguments: { + 'url': 'http://example.com/', + 'enableJavaScript': false, + 'enableDomStorage': false, + 'universalLinksOnly': true, + 'headers': {}, + }) + ], + ); + }); + + test('launch should return false if platform returns null', () async { + final UrlLauncherLinux launcher = UrlLauncherLinux(); + final bool launched = await launcher.launch( + 'http://example.com/', + useSafariVC: true, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: const {}, + ); + + expect(launched, false); + }); + }); +} diff --git a/packages/url_launcher/url_launcher_macos/CHANGELOG.md b/packages/url_launcher/url_launcher_macos/CHANGELOG.md index 9281c910d52d..2bf9de938956 100644 --- a/packages/url_launcher/url_launcher_macos/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_macos/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.4 + +* Switches to an in-package method channel implementation. + ## 2.0.3 * Updates code for new analysis options. diff --git a/packages/url_launcher/url_launcher_macos/lib/url_launcher_macos.dart b/packages/url_launcher/url_launcher_macos/lib/url_launcher_macos.dart new file mode 100644 index 000000000000..7dc1340083ae --- /dev/null +++ b/packages/url_launcher/url_launcher_macos/lib/url_launcher_macos.dart @@ -0,0 +1,54 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:url_launcher_platform_interface/link.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +const MethodChannel _channel = + MethodChannel('plugins.flutter.io/url_launcher_macos'); + +/// An implementation of [UrlLauncherPlatform] for macOS. +class UrlLauncherMacOS extends UrlLauncherPlatform { + /// Registers this class as the default instance of [UrlLauncherPlatform]. + static void registerWith() { + UrlLauncherPlatform.instance = UrlLauncherMacOS(); + } + + @override + final LinkDelegate? linkDelegate = null; + + @override + Future canLaunch(String url) { + return _channel.invokeMethod( + 'canLaunch', + {'url': url}, + ).then((bool? value) => value ?? false); + } + + @override + Future launch( + String url, { + required bool useSafariVC, + required bool useWebView, + required bool enableJavaScript, + required bool enableDomStorage, + required bool universalLinksOnly, + required Map headers, + String? webOnlyWindowName, + }) { + return _channel.invokeMethod( + 'launch', + { + 'url': url, + 'enableJavaScript': enableJavaScript, + 'enableDomStorage': enableDomStorage, + 'universalLinksOnly': universalLinksOnly, + 'headers': headers, + }, + ).then((bool? value) => value ?? false); + } +} diff --git a/packages/url_launcher/url_launcher_macos/macos/Classes/UrlLauncherPlugin.swift b/packages/url_launcher/url_launcher_macos/macos/Classes/UrlLauncherPlugin.swift index c311b8575017..4b799ee12094 100644 --- a/packages/url_launcher/url_launcher_macos/macos/Classes/UrlLauncherPlugin.swift +++ b/packages/url_launcher/url_launcher_macos/macos/Classes/UrlLauncherPlugin.swift @@ -36,7 +36,7 @@ public class UrlLauncherPlugin: NSObject, FlutterPlugin { public static func register(with registrar: FlutterPluginRegistrar) { let channel = FlutterMethodChannel( - name: "plugins.flutter.io/url_launcher", + name: "plugins.flutter.io/url_launcher_macos", binaryMessenger: registrar.messenger) let instance = UrlLauncherPlugin() registrar.addMethodCallDelegate(instance, channel: channel) diff --git a/packages/url_launcher/url_launcher_macos/pubspec.yaml b/packages/url_launcher/url_launcher_macos/pubspec.yaml index e993bb69015d..0cd25d2fc3ad 100644 --- a/packages/url_launcher/url_launcher_macos/pubspec.yaml +++ b/packages/url_launcher/url_launcher_macos/pubspec.yaml @@ -2,11 +2,11 @@ name: url_launcher_macos description: macOS implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 2.0.3 +version: 2.0.4 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.5.0" flutter: plugin: @@ -15,7 +15,14 @@ flutter: macos: pluginClass: UrlLauncherPlugin fileName: url_launcher_macos.dart + dartPluginClass: UrlLauncherMacOS dependencies: flutter: sdk: flutter + url_launcher_platform_interface: ^2.0.3 + +dev_dependencies: + flutter_test: + sdk: flutter + test: ^1.16.3 diff --git a/packages/url_launcher/url_launcher_macos/test/url_launcher_macos_test.dart b/packages/url_launcher/url_launcher_macos/test/url_launcher_macos_test.dart new file mode 100644 index 000000000000..0a28aea678c3 --- /dev/null +++ b/packages/url_launcher/url_launcher_macos/test/url_launcher_macos_test.dart @@ -0,0 +1,144 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:url_launcher_macos/url_launcher_macos.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('$UrlLauncherMacOS', () { + const MethodChannel channel = + MethodChannel('plugins.flutter.io/url_launcher_macos'); + final List log = []; + channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + + // Return null explicitly instead of relying on the implicit null + // returned by the method channel if no return statement is specified. + return null; + }); + + tearDown(() { + log.clear(); + }); + + test('registers instance', () { + UrlLauncherMacOS.registerWith(); + expect(UrlLauncherPlatform.instance, isA()); + }); + + test('canLaunch', () async { + final UrlLauncherMacOS launcher = UrlLauncherMacOS(); + await launcher.canLaunch('http://example.com/'); + expect( + log, + [ + isMethodCall('canLaunch', arguments: { + 'url': 'http://example.com/', + }) + ], + ); + }); + + test('canLaunch should return false if platform returns null', () async { + final UrlLauncherMacOS launcher = UrlLauncherMacOS(); + final bool canLaunch = await launcher.canLaunch('http://example.com/'); + + expect(canLaunch, false); + }); + + test('launch', () async { + final UrlLauncherMacOS launcher = UrlLauncherMacOS(); + await launcher.launch( + 'http://example.com/', + useSafariVC: true, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: const {}, + ); + expect( + log, + [ + isMethodCall('launch', arguments: { + 'url': 'http://example.com/', + 'enableJavaScript': false, + 'enableDomStorage': false, + 'universalLinksOnly': false, + 'headers': {}, + }) + ], + ); + }); + + test('launch with headers', () async { + final UrlLauncherMacOS launcher = UrlLauncherMacOS(); + await launcher.launch( + 'http://example.com/', + useSafariVC: true, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: const {'key': 'value'}, + ); + expect( + log, + [ + isMethodCall('launch', arguments: { + 'url': 'http://example.com/', + 'enableJavaScript': false, + 'enableDomStorage': false, + 'universalLinksOnly': false, + 'headers': {'key': 'value'}, + }) + ], + ); + }); + + test('launch universal links only', () async { + final UrlLauncherMacOS launcher = UrlLauncherMacOS(); + await launcher.launch( + 'http://example.com/', + useSafariVC: false, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: true, + headers: const {}, + ); + expect( + log, + [ + isMethodCall('launch', arguments: { + 'url': 'http://example.com/', + 'enableJavaScript': false, + 'enableDomStorage': false, + 'universalLinksOnly': true, + 'headers': {}, + }) + ], + ); + }); + + test('launch should return false if platform returns null', () async { + final UrlLauncherMacOS launcher = UrlLauncherMacOS(); + final bool launched = await launcher.launch( + 'http://example.com/', + useSafariVC: true, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: const {}, + ); + + expect(launched, false); + }); + }); +} diff --git a/packages/url_launcher/url_launcher_windows/CHANGELOG.md b/packages/url_launcher/url_launcher_windows/CHANGELOG.md index e599b1f98092..a562da1db97d 100644 --- a/packages/url_launcher/url_launcher_windows/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_windows/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.0.3 +* Switches to an in-package method channel implementation. * Adds unit tests. * Updates code for new analysis options. diff --git a/packages/url_launcher/url_launcher_windows/lib/url_launcher_windows.dart b/packages/url_launcher/url_launcher_windows/lib/url_launcher_windows.dart new file mode 100644 index 000000000000..b0ee8cb1a0b4 --- /dev/null +++ b/packages/url_launcher/url_launcher_windows/lib/url_launcher_windows.dart @@ -0,0 +1,54 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:url_launcher_platform_interface/link.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +const MethodChannel _channel = + MethodChannel('plugins.flutter.io/url_launcher_windows'); + +/// An implementation of [UrlLauncherPlatform] for Windows. +class UrlLauncherWindows extends UrlLauncherPlatform { + /// Registers this class as the default instance of [UrlLauncherPlatform]. + static void registerWith() { + UrlLauncherPlatform.instance = UrlLauncherWindows(); + } + + @override + final LinkDelegate? linkDelegate = null; + + @override + Future canLaunch(String url) { + return _channel.invokeMethod( + 'canLaunch', + {'url': url}, + ).then((bool? value) => value ?? false); + } + + @override + Future launch( + String url, { + required bool useSafariVC, + required bool useWebView, + required bool enableJavaScript, + required bool enableDomStorage, + required bool universalLinksOnly, + required Map headers, + String? webOnlyWindowName, + }) { + return _channel.invokeMethod( + 'launch', + { + 'url': url, + 'enableJavaScript': enableJavaScript, + 'enableDomStorage': enableDomStorage, + 'universalLinksOnly': universalLinksOnly, + 'headers': headers, + }, + ).then((bool? value) => value ?? false); + } +} diff --git a/packages/url_launcher/url_launcher_windows/pubspec.yaml b/packages/url_launcher/url_launcher_windows/pubspec.yaml index b0acdabc00a3..506e9958b72c 100644 --- a/packages/url_launcher/url_launcher_windows/pubspec.yaml +++ b/packages/url_launcher/url_launcher_windows/pubspec.yaml @@ -2,11 +2,11 @@ name: url_launcher_windows description: Windows implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 2.0.2 +version: 2.0.3 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.5.0" flutter: plugin: @@ -14,7 +14,14 @@ flutter: platforms: windows: pluginClass: UrlLauncherWindows + dartPluginClass: UrlLauncherWindows dependencies: flutter: sdk: flutter + url_launcher_platform_interface: ^2.0.3 + +dev_dependencies: + flutter_test: + sdk: flutter + test: ^1.16.3 diff --git a/packages/url_launcher/url_launcher_windows/test/url_launcher_windows_test.dart b/packages/url_launcher/url_launcher_windows/test/url_launcher_windows_test.dart new file mode 100644 index 000000000000..8b55b29bb530 --- /dev/null +++ b/packages/url_launcher/url_launcher_windows/test/url_launcher_windows_test.dart @@ -0,0 +1,144 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; +import 'package:url_launcher_windows/url_launcher_windows.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('$UrlLauncherWindows', () { + const MethodChannel channel = + MethodChannel('plugins.flutter.io/url_launcher_windows'); + final List log = []; + channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + + // Return null explicitly instead of relying on the implicit null + // returned by the method channel if no return statement is specified. + return null; + }); + + test('registers instance', () { + UrlLauncherWindows.registerWith(); + expect(UrlLauncherPlatform.instance, isA()); + }); + + tearDown(() { + log.clear(); + }); + + test('canLaunch', () async { + final UrlLauncherWindows launcher = UrlLauncherWindows(); + await launcher.canLaunch('http://example.com/'); + expect( + log, + [ + isMethodCall('canLaunch', arguments: { + 'url': 'http://example.com/', + }) + ], + ); + }); + + test('canLaunch should return false if platform returns null', () async { + final UrlLauncherWindows launcher = UrlLauncherWindows(); + final bool canLaunch = await launcher.canLaunch('http://example.com/'); + + expect(canLaunch, false); + }); + + test('launch', () async { + final UrlLauncherWindows launcher = UrlLauncherWindows(); + await launcher.launch( + 'http://example.com/', + useSafariVC: true, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: const {}, + ); + expect( + log, + [ + isMethodCall('launch', arguments: { + 'url': 'http://example.com/', + 'enableJavaScript': false, + 'enableDomStorage': false, + 'universalLinksOnly': false, + 'headers': {}, + }) + ], + ); + }); + + test('launch with headers', () async { + final UrlLauncherWindows launcher = UrlLauncherWindows(); + await launcher.launch( + 'http://example.com/', + useSafariVC: true, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: const {'key': 'value'}, + ); + expect( + log, + [ + isMethodCall('launch', arguments: { + 'url': 'http://example.com/', + 'enableJavaScript': false, + 'enableDomStorage': false, + 'universalLinksOnly': false, + 'headers': {'key': 'value'}, + }) + ], + ); + }); + + test('launch universal links only', () async { + final UrlLauncherWindows launcher = UrlLauncherWindows(); + await launcher.launch( + 'http://example.com/', + useSafariVC: false, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: true, + headers: const {}, + ); + expect( + log, + [ + isMethodCall('launch', arguments: { + 'url': 'http://example.com/', + 'enableJavaScript': false, + 'enableDomStorage': false, + 'universalLinksOnly': true, + 'headers': {}, + }) + ], + ); + }); + + test('launch should return false if platform returns null', () async { + final UrlLauncherWindows launcher = UrlLauncherWindows(); + final bool launched = await launcher.launch( + 'http://example.com/', + useSafariVC: true, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: const {}, + ); + + expect(launched, false); + }); + }); +} diff --git a/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.cpp b/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.cpp index 748c75ddd243..d5f201219c75 100644 --- a/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.cpp +++ b/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.cpp @@ -63,7 +63,7 @@ std::string GetUrlArgument(const flutter::MethodCall<>& method_call) { void UrlLauncherPlugin::RegisterWithRegistrar( flutter::PluginRegistrar* registrar) { auto channel = std::make_unique>( - registrar->messenger(), "plugins.flutter.io/url_launcher", + registrar->messenger(), "plugins.flutter.io/url_launcher_windows", &flutter::StandardMethodCodec::GetInstance()); std::unique_ptr plugin = From 9d32c83af9af125981ef847abd9d1636b65aa8e1 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Tue, 8 Feb 2022 18:30:19 +0100 Subject: [PATCH 154/600] [quick_actions] Extract iOS implementation into a separate package. (#4702) --- .../quick_actions/quick_actions/pubspec.yaml | 9 +- .../quick_actions/quick_actions_ios/AUTHORS | 68 ++ .../quick_actions_ios/CHANGELOG.md | 4 + .../quick_actions/quick_actions_ios/LICENSE | 25 + .../quick_actions/quick_actions_ios/README.md | 16 + .../quick_actions_ios/example/README.md | 8 + .../integration_test/quick_actions_test.dart | 24 + .../ios/Flutter/AppFrameworkInfo.plist | 30 + .../example/ios/Flutter/Debug.xcconfig | 2 + .../example/ios/Flutter/Release.xcconfig | 2 + .../quick_actions_ios/example/ios/Podfile | 41 + .../ios/Runner.xcodeproj/project.pbxproj | 731 ++++++++++++++++++ .../xcshareddata/xcschemes/Runner.xcscheme | 107 +++ .../xcschemes/RunnerUITests.xcscheme | 52 ++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/ios/Runner/AppDelegate.h | 10 + .../example/ios/Runner/AppDelegate.m | 17 + .../AppIcon.appiconset/Contents.json | 116 +++ .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 564 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 1588 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 1025 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 1716 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 1920 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 1895 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 3831 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 1888 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 3294 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 3612 bytes .../Runner/Base.lproj/LaunchScreen.storyboard | 27 + .../ios/Runner/Base.lproj/Main.storyboard | 26 + .../example/ios/Runner/Info.plist | 49 ++ .../example/ios/Runner/main.m | 13 + .../example/ios/RunnerTests/Info.plist | 22 + .../example/ios/RunnerTests/RunnerTests.m | 18 + .../example/ios/RunnerUITests/Info.plist | 22 + .../example/ios/RunnerUITests/RunnerUITests.m | 99 +++ .../quick_actions_ios/example/lib/main.dart | 82 ++ .../quick_actions_ios/example/pubspec.yaml | 27 + .../example/test_driver/integration_test.dart | 7 + .../ios/Assets/.gitkeep | 0 .../ios/Classes/FLTQuickActionsPlugin.h | 0 .../ios/Classes/FLTQuickActionsPlugin.m | 4 +- .../ios/quick_actions_ios.podspec} | 2 +- .../lib/quick_actions_ios.dart | 56 ++ .../quick_actions_ios/pubspec.yaml | 30 + .../test/quick_actions_ios_test.dart | 164 ++++ 51 files changed, 1924 insertions(+), 4 deletions(-) create mode 100644 packages/quick_actions/quick_actions_ios/AUTHORS create mode 100644 packages/quick_actions/quick_actions_ios/CHANGELOG.md create mode 100644 packages/quick_actions/quick_actions_ios/LICENSE create mode 100644 packages/quick_actions/quick_actions_ios/README.md create mode 100644 packages/quick_actions/quick_actions_ios/example/README.md create mode 100644 packages/quick_actions/quick_actions_ios/example/integration_test/quick_actions_test.dart create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Flutter/AppFrameworkInfo.plist create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Flutter/Debug.xcconfig create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Flutter/Release.xcconfig create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Podfile create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/project.pbxproj create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/RunnerUITests.xcscheme create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Runner/AppDelegate.h create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Runner/AppDelegate.m create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Runner/Base.lproj/Main.storyboard create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Runner/Info.plist create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/Runner/main.m create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/Info.plist create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/RunnerTests.m create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/RunnerUITests/Info.plist create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/RunnerUITests/RunnerUITests.m create mode 100644 packages/quick_actions/quick_actions_ios/example/lib/main.dart create mode 100644 packages/quick_actions/quick_actions_ios/example/pubspec.yaml create mode 100644 packages/quick_actions/quick_actions_ios/example/test_driver/integration_test.dart rename packages/quick_actions/{quick_actions => quick_actions_ios}/ios/Assets/.gitkeep (100%) rename packages/quick_actions/{quick_actions => quick_actions_ios}/ios/Classes/FLTQuickActionsPlugin.h (100%) rename packages/quick_actions/{quick_actions => quick_actions_ios}/ios/Classes/FLTQuickActionsPlugin.m (96%) rename packages/quick_actions/{quick_actions/ios/quick_actions.podspec => quick_actions_ios/ios/quick_actions_ios.podspec} (95%) create mode 100644 packages/quick_actions/quick_actions_ios/lib/quick_actions_ios.dart create mode 100644 packages/quick_actions/quick_actions_ios/pubspec.yaml create mode 100644 packages/quick_actions/quick_actions_ios/test/quick_actions_ios_test.dart diff --git a/packages/quick_actions/quick_actions/pubspec.yaml b/packages/quick_actions/quick_actions/pubspec.yaml index 0d51e828fbdd..51cf642d59a7 100644 --- a/packages/quick_actions/quick_actions/pubspec.yaml +++ b/packages/quick_actions/quick_actions/pubspec.yaml @@ -5,6 +5,10 @@ repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+quick_actions%22 version: 0.6.0+9 +# Temporarily disable publishing to allow moving Android and iOS +# implementations. +publish_to: none + environment: sdk: ">=2.14.0 <3.0.0" flutter: ">=2.5.0" @@ -16,11 +20,14 @@ flutter: package: io.flutter.plugins.quickactions pluginClass: QuickActionsPlugin ios: - pluginClass: FLTQuickActionsPlugin + default_package: quick_actions_ios dependencies: flutter: sdk: flutter + # Temporary path dependencies to allow moving Android and iOS implementations. + quick_actions_ios: + path: ../quick_actions_ios quick_actions_platform_interface: ^1.0.0 dev_dependencies: diff --git a/packages/quick_actions/quick_actions_ios/AUTHORS b/packages/quick_actions/quick_actions_ios/AUTHORS new file mode 100644 index 000000000000..5f17b78d134f --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/AUTHORS @@ -0,0 +1,68 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> +Daniel Roek +Maurits van Beusekom diff --git a/packages/quick_actions/quick_actions_ios/CHANGELOG.md b/packages/quick_actions/quick_actions_ios/CHANGELOG.md new file mode 100644 index 000000000000..d48afbd8d13a --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/CHANGELOG.md @@ -0,0 +1,4 @@ + +## 0.6.0+9 + +* Switches to a package-internal implementation of the platform interface. \ No newline at end of file diff --git a/packages/quick_actions/quick_actions_ios/LICENSE b/packages/quick_actions/quick_actions_ios/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/LICENSE @@ -0,0 +1,25 @@ +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/quick_actions/quick_actions_ios/README.md b/packages/quick_actions/quick_actions_ios/README.md new file mode 100644 index 000000000000..a0c369813a4d --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/README.md @@ -0,0 +1,16 @@ +# quick\_actions\_ios + +The iOS implementation of [`quick_actions`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use `quick_actions` +normally. This package will be automatically included in your app when you do. + +## Contributing + +If you would like to contribute to the plugin, check out our [contribution guide][3]. + +[1]: https://pub.dev/packages/quick_actions +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin +[3]: https://github.com/flutter/plugins/blob/master/CONTRIBUTING.md \ No newline at end of file diff --git a/packages/quick_actions/quick_actions_ios/example/README.md b/packages/quick_actions/quick_actions_ios/example/README.md new file mode 100644 index 000000000000..d1b72891de9e --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/example/README.md @@ -0,0 +1,8 @@ +# quick_actions_example + +Demonstrates how to use the quick_actions plugin. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](https://flutter.dev/). diff --git a/packages/quick_actions/quick_actions_ios/example/integration_test/quick_actions_test.dart b/packages/quick_actions/quick_actions_ios/example/integration_test/quick_actions_test.dart new file mode 100644 index 000000000000..b89c09d639d3 --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/example/integration_test/quick_actions_test.dart @@ -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. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:quick_actions_ios/quick_actions_ios.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('Can set shortcuts', (WidgetTester tester) async { + final QuickActionsIos quickActions = QuickActionsIos(); + await quickActions.initialize((String value) {}); + + const ShortcutItem shortCutItem = ShortcutItem( + type: 'action_one', + localizedTitle: 'Action one', + icon: 'AppIcon', + ); + expect( + quickActions.setShortcutItems([shortCutItem]), completes); + }); +} diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Flutter/AppFrameworkInfo.plist b/packages/quick_actions/quick_actions_ios/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 000000000000..3a9c234f96d4 --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + UIRequiredDeviceCapabilities + + arm64 + + MinimumOSVersion + 9.0 + + diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Flutter/Debug.xcconfig b/packages/quick_actions/quick_actions_ios/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 000000000000..e8efba114687 --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Flutter/Release.xcconfig b/packages/quick_actions/quick_actions_ios/example/ios/Flutter/Release.xcconfig new file mode 100644 index 000000000000..399e9340e6f6 --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Podfile b/packages/quick_actions/quick_actions_ios/example/ios/Podfile new file mode 100644 index 000000000000..3924e59aa0f9 --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/example/ios/Podfile @@ -0,0 +1,41 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..36dc0d81923b --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,731 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 33E20B3526EFCDFC00A4A191 /* RunnerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 33E20B3426EFCDFC00A4A191 /* RunnerTests.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 50EB54C1FE43DB743F5DEC7C /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D1A69703A518C37D73BF8B91 /* libPods-RunnerTests.a */; }; + 686BE83025E58CCF00862533 /* RunnerUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 686BE82F25E58CCF00862533 /* RunnerUITests.m */; }; + 83C36CAF23D629E5ABE75B2A /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CCC799F2B0AB50A9C34344F0 /* libPods-Runner.a */; }; + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; + 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 33E20B3726EFCDFC00A4A191 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; + 686BE83225E58CCF00862533 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 33E20B3226EFCDFC00A4A191 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 33E20B3426EFCDFC00A4A191 /* RunnerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RunnerTests.m; sourceTree = ""; }; + 33E20B3626EFCDFC00A4A191 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 5278439583922091276A37C9 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 686BE82D25E58CCF00862533 /* RunnerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 686BE82F25E58CCF00862533 /* RunnerUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RunnerUITests.m; sourceTree = ""; }; + 686BE83125E58CCF00862533 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 96F949A6B78E2DC62B93C4F8 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9D27FE1F0F21D4D47DDA16DE /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + CCC799F2B0AB50A9C34344F0 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + D1A69703A518C37D73BF8B91 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + F0609304FBCAEC2289164BD5 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 33E20B2F26EFCDFC00A4A191 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 50EB54C1FE43DB743F5DEC7C /* libPods-RunnerTests.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 686BE82A25E58CCF00862533 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 83C36CAF23D629E5ABE75B2A /* libPods-Runner.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 33E20B3326EFCDFC00A4A191 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 33E20B3426EFCDFC00A4A191 /* RunnerTests.m */, + 33E20B3626EFCDFC00A4A191 /* Info.plist */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 686BE82E25E58CCF00862533 /* RunnerUITests */ = { + isa = PBXGroup; + children = ( + 686BE82F25E58CCF00862533 /* RunnerUITests.m */, + 686BE83125E58CCF00862533 /* Info.plist */, + ); + path = RunnerUITests; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 686BE82E25E58CCF00862533 /* RunnerUITests */, + 33E20B3326EFCDFC00A4A191 /* RunnerTests */, + 97C146EF1CF9000F007C117D /* Products */, + D0FE95BE2380323DD75CB891 /* Pods */, + A44AD0D63DEF785A2A2DEE28 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 686BE82D25E58CCF00862533 /* RunnerUITests.xctest */, + 33E20B3226EFCDFC00A4A191 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 97C146F11CF9000F007C117D /* Supporting Files */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + ); + path = Runner; + sourceTree = ""; + }; + 97C146F11CF9000F007C117D /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 97C146F21CF9000F007C117D /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + A44AD0D63DEF785A2A2DEE28 /* Frameworks */ = { + isa = PBXGroup; + children = ( + CCC799F2B0AB50A9C34344F0 /* libPods-Runner.a */, + D1A69703A518C37D73BF8B91 /* libPods-RunnerTests.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + D0FE95BE2380323DD75CB891 /* Pods */ = { + isa = PBXGroup; + children = ( + 5278439583922091276A37C9 /* Pods-Runner.debug.xcconfig */, + F0609304FBCAEC2289164BD5 /* Pods-Runner.release.xcconfig */, + 9D27FE1F0F21D4D47DDA16DE /* Pods-RunnerTests.debug.xcconfig */, + 96F949A6B78E2DC62B93C4F8 /* Pods-RunnerTests.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 33E20B3126EFCDFC00A4A191 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33E20B3B26EFCDFC00A4A191 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 3B2E8279C112D7129C8D23F1 /* [CP] Check Pods Manifest.lock */, + 33E20B2E26EFCDFC00A4A191 /* Sources */, + 33E20B2F26EFCDFC00A4A191 /* Frameworks */, + 33E20B3026EFCDFC00A4A191 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 33E20B3826EFCDFC00A4A191 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 33E20B3226EFCDFC00A4A191 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 686BE82C25E58CCF00862533 /* RunnerUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 686BE83625E58CCF00862533 /* Build configuration list for PBXNativeTarget "RunnerUITests" */; + buildPhases = ( + 686BE82925E58CCF00862533 /* Sources */, + 686BE82A25E58CCF00862533 /* Frameworks */, + 686BE82B25E58CCF00862533 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 686BE83325E58CCF00862533 /* PBXTargetDependency */, + ); + name = RunnerUITests; + productName = RunnerUITests; + productReference = 686BE82D25E58CCF00862533 /* RunnerUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + C6989ECD8FF0836301D734B4 /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = "The Flutter Authors"; + TargetAttributes = { + 33E20B3126EFCDFC00A4A191 = { + CreatedOnToolsVersion = 12.5; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 686BE82C25E58CCF00862533 = { + CreatedOnToolsVersion = 12.4; + ProvisioningStyle = Automatic; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 686BE82C25E58CCF00862533 /* RunnerUITests */, + 33E20B3126EFCDFC00A4A191 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33E20B3026EFCDFC00A4A191 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 686BE82B25E58CCF00862533 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 3B2E8279C112D7129C8D23F1 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + C6989ECD8FF0836301D734B4 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 33E20B2E26EFCDFC00A4A191 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33E20B3526EFCDFC00A4A191 /* RunnerTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 686BE82925E58CCF00862533 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 686BE83025E58CCF00862533 /* RunnerUITests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, + 97C146F31CF9000F007C117D /* main.m in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33E20B3826EFCDFC00A4A191 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 33E20B3726EFCDFC00A4A191 /* PBXContainerItemProxy */; + }; + 686BE83325E58CCF00862533 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 686BE83225E58CCF00862533 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 33E20B3926EFCDFC00A4A191 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9D27FE1F0F21D4D47DDA16DE /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + INFOPLIST_FILE = RunnerTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Debug; + }; + 33E20B3A26EFCDFC00A4A191 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 96F949A6B78E2DC62B93C4F8 /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + INFOPLIST_FILE = RunnerTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Release; + }; + 686BE83425E58CCF00862533 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = RunnerUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.google.RunnerUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = Runner; + }; + name = Debug; + }; + 686BE83525E58CCF00862533 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = RunnerUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.google.RunnerUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = Runner; + }; + name = Release; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.quickActionsExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.quickActionsExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 33E20B3B26EFCDFC00A4A191 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33E20B3926EFCDFC00A4A191 /* Debug */, + 33E20B3A26EFCDFC00A4A191 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 686BE83625E58CCF00862533 /* Build configuration list for PBXNativeTarget "RunnerUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 686BE83425E58CCF00862533 /* Debug */, + 686BE83525E58CCF00862533 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000000..1ba2b47c79f1 --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/RunnerUITests.xcscheme b/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/RunnerUITests.xcscheme new file mode 100644 index 000000000000..0164e94407dd --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/RunnerUITests.xcscheme @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..21a3cc14c74e --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Runner/AppDelegate.h b/packages/quick_actions/quick_actions_ios/example/ios/Runner/AppDelegate.h new file mode 100644 index 000000000000..0681d288bb70 --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/example/ios/Runner/AppDelegate.h @@ -0,0 +1,10 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import + +@interface AppDelegate : FlutterAppDelegate + +@end diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Runner/AppDelegate.m b/packages/quick_actions/quick_actions_ios/example/ios/Runner/AppDelegate.m new file mode 100644 index 000000000000..a89d86c28c6f --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/example/ios/Runner/AppDelegate.m @@ -0,0 +1,17 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "AppDelegate.h" +#include "GeneratedPluginRegistrant.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [GeneratedPluginRegistrant registerWithRegistry:self]; + // Override point for customization after application launch. + [super application:application didFinishLaunchingWithOptions:launchOptions]; + return NO; +} +@end diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000000..d22f10b2ab63 --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..28c6bf03016f6c994b70f38d1b7346e5831b531f GIT binary patch literal 564 zcmV-40?Yl0P)Px$?ny*JR5%f>l)FnDQ543{x%ZCiu33$Wg!pQFfT_}?5Q|_VSlIbLC`dpoMXL}9 zHfd9&47Mo(7D231gb+kjFxZHS4-m~7WurTH&doVX2KI5sU4v(sJ1@T9eCIKPjsqSr z)C01LsCxk=72-vXmX}CQD#BD;Cthymh&~=f$Q8nn0J<}ZrusBy4PvRNE}+1ceuj8u z0mW5k8fmgeLnTbWHGwfKA3@PdZxhn|PypR&^p?weGftrtCbjF#+zk_5BJh7;0`#Wr zgDpM_;Ax{jO##IrT`Oz;MvfwGfV$zD#c2xckpcXC6oou4ML~ezCc2EtnsQTB4tWNg z?4bkf;hG7IMfhgNI(FV5Gs4|*GyMTIY0$B=_*mso9Ityq$m^S>15>-?0(zQ<8Qy<_TjHE33(?_M8oaM zyc;NxzRVK@DL6RJnX%U^xW0Gpg(lXp(!uK1v0YgHjs^ZXSQ|m#lV7ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 literal 0 HcmV?d00001 diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..f091b6b0bca859a3f474b03065bef75ba58a9e4c GIT binary patch literal 1588 zcmV-42Fv-0P)C1SqPt}wig>|5Crh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)y zRAZ>eDe#*r`yDAVgB_R*LB*MAc)8(b{g{9McCXW!lq7r(btRoB9!8B-#AI6JMb~YFBEvdsV)`mEQO^&#eRKx@b&x- z5lZm*!WfD8oCLzfHGz#u7sT0^VLMI1MqGxF^v+`4YYnVYgk*=kU?HsSz{v({E3lb9 z>+xILjBN)t6`=g~IBOelGQ(O990@BfXf(DRI5I$qN$0Gkz-FSc$3a+2fX$AedL4u{ z4V+5Ong(9LiGcIKW?_352sR;LtDPmPJXI{YtT=O8=76o9;*n%_m|xo!i>7$IrZ-{l z-x3`7M}qzHsPV@$v#>H-TpjDh2UE$9g6sysUREDy_R(a)>=eHw-WAyfIN z*qb!_hW>G)Tu8nSw9yn#3wFMiLcfc4pY0ek1}8(NqkBR@t4{~oC>ryc-h_ByH(Cg5 z>ao-}771+xE3um9lWAY1FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zm zZQj(aA_HeBY&OC^jj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5Kh zX*|AU4QE#~SgPzOXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&g3j|zDgC+}2Q_v%YfDax z!?umcN^n}KYQ|a$Lr+51Nf9dkkYFSjZZjkma$0KOj+;aQ&721~t7QUKx61J3(P4P1 zstI~7-wOACnWP4=8oGOwz%vNDqD8w&Q`qcNGGrbbf&0s9L0De{4{mRS?o0MU+nR_! zrvshUau0G^DeMhM_v{5BuLjb#Hh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG z;P%vktHcXwTj~+IE(~px)3*MY77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC z(zYC$g|@+I+kY;dg_dE>scBf&bP1Nc@Hz<3R)V`=AGkc;8CXqdi=B4l2k|g;2%#m& z*jfX^%b!A8#bI!j9-0Fi0bOXl(-c^AB9|nQaE`*)Hw+o&jS9@7&Gov#HbD~#d{twV zXd^Tr^mWLfFh$@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)y zI9C9*oUga6=hxw6QasLPnee@3^Rr*M{CdaL5=R41nLs(AHk_=Y+A9$2&H(B7!_pURs&8aNw7?`&Z&xY_Ye z)~D5Bog^td-^QbUtkTirdyK^mTHAOuptDflut!#^lnKqU md>ggs(5nOWAqO?umG&QVYK#ibz}*4>0000U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~cTNYR~@Y9KepMPrrB8cABapAFa=`H+UGhkXUZV1GnwR1*lPyZ;*K(i~2gp|@bzp8}og7e*#% zEnr|^CWdVV!-4*Y_7rFvlww2Ze+>j*!Z!pQ?2l->4q#nqRu9`ELo6RMS5=br47g_X zRw}P9a7RRYQ%2Vsd0Me{_(EggTnuN6j=-?uFS6j^u69elMypu?t>op*wBx<=Wx8?( ztpe^(fwM6jJX7M-l*k3kEpWOl_Vk3@(_w4oc}4YF4|Rt=2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX?F&rExl^W|G8Z8;`5z-k}OGNZ0#WVb$WCpQu-_YsiqKP?BB# vzVHS-CTUF4Ozn5G+mq_~Qqto~ahA+K`|lyv3(-e}00000NkvXXu0mjfd`9t{ literal 0 HcmV?d00001 diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d0ef06e7edb86cdfe0d15b4b0d98334a86163658 GIT binary patch literal 1716 zcmds$`#;kQ7{|XelZftyR5~xW7?MLxS4^|Hw3&P7^y)@A9Fj{Xm1~_CIV^XZ%SLBn zA;!r`GqGHg=7>xrB{?psZQs88ZaedDoagm^KF{a*>G|dJWRSe^I$DNW008I^+;Kjt z>9p3GNR^I;v>5_`+91i(*G;u5|L+Bu6M=(afLjtkya#yZ175|z$pU~>2#^Z_pCZ7o z1c6UNcv2B3?; zX%qdxCXQpdKRz=#b*q0P%b&o)5ZrNZt7$fiETSK_VaY=mb4GK`#~0K#~9^ zcY!`#Af+4h?UMR-gMKOmpuYeN5P*RKF!(tb`)oe0j2BH1l?=>y#S5pMqkx6i{*=V9JF%>N8`ewGhRE(|WohnD59R^$_36{4>S zDFlPC5|k?;SPsDo87!B{6*7eqmMdU|QZ84>6)Kd9wNfh90=y=TFQay-0__>=<4pk& zYDjgIhL-jQ9o>z32K)BgAH+HxamL{ZL~ozu)Qqe@a`FpH=oQRA8=L-m-1dam(Ix2V z?du;LdMO+ooBelr^_y4{|44tmgH^2hSzPFd;U^!1p>6d|o)(-01z{i&Kj@)z-yfWQ)V#3Uo!_U}q3u`(fOs`_f^ueFii1xBNUB z6MecwJN$CqV&vhc+)b(p4NzGGEgwWNs z@*lUV6LaduZH)4_g!cE<2G6#+hJrWd5(|p1Z;YJ7ifVHv+n49btR}dq?HHDjl{m$T z!jLZcGkb&XS2OG~u%&R$(X+Z`CWec%QKt>NGYvd5g20)PU(dOn^7%@6kQb}C(%=vr z{?RP(z~C9DPnL{q^@pVw@|Vx~@3v!9dCaBtbh2EdtoNHm4kGxp>i#ct)7p|$QJs+U z-a3qtcPvhihub?wnJqEt>zC@)2suY?%-96cYCm$Q8R%-8$PZYsx3~QOLMDf(piXMm zB=<63yQk1AdOz#-qsEDX>>c)EES%$owHKue;?B3)8aRd}m~_)>SL3h2(9X;|+2#7X z+#2)NpD%qJvCQ0a-uzZLmz*ms+l*N}w)3LRQ*6>|Ub-fyptY(keUxw+)jfwF5K{L9 z|Cl_w=`!l_o><384d&?)$6Nh(GAm=4p_;{qVn#hI8lqewW7~wUlyBM-4Z|)cZr?Rh z=xZ&Ol>4(CU85ea(CZ^aO@2N18K>ftl8>2MqetAR53_JA>Fal`^)1Y--Am~UDa4th zKfCYpcXky$XSFDWBMIl(q=Mxj$iMBX=|j9P)^fDmF(5(5$|?Cx}DKEJa&XZP%OyE`*GvvYQ4PV&!g2|L^Q z?YG}tx;sY@GzMmsY`7r$P+F_YLz)(e}% zyakqFB<6|x9R#TdoP{R$>o7y(-`$$p0NxJ6?2B8tH)4^yF(WhqGZlM3=9Ibs$%U1w zWzcss*_c0=v_+^bfb`kBFsI`d;ElwiU%frgRB%qBjn@!0U2zZehBn|{%uNIKBA7n= zzE`nnwTP85{g;8AkYxA68>#muXa!G>xH22D1I*SiD~7C?7Za+9y7j1SHiuSkKK*^O zsZ==KO(Ua#?YUpXl{ViynyT#Hzk=}5X$e04O@fsMQjb}EMuPWFO0e&8(2N(29$@Vd zn1h8Yd>6z(*p^E{c(L0Lg=wVdupg!z@WG;E0k|4a%s7Up5C0c)55XVK*|x9RQeZ1J@1v9MX;>n34(i>=YE@Iur`0Vah(inE3VUFZNqf~tSz{1fz3Fsn_x4F>o(Yo;kpqvBe-sbwH(*Y zu$JOl0b83zu$JMvy<#oH^Wl>aWL*?aDwnS0iEAwC?DK@aT)GHRLhnz2WCvf3Ba;o=aY7 z2{Asu5MEjGOY4O#Ggz@@J;q*0`kd2n8I3BeNuMmYZf{}pg=jTdTCrIIYuW~luKecn z+E-pHY%ohj@uS0%^ z&(OxwPFPD$+#~`H?fMvi9geVLci(`K?Kj|w{rZ9JgthFHV+=6vMbK~0)Ea<&WY-NC zy-PnZft_k2tfeQ*SuC=nUj4H%SQ&Y$gbH4#2sT0cU0SdFs=*W*4hKGpuR1{)mV;Qf5pw4? zfiQgy0w3fC*w&Bj#{&=7033qFR*<*61B4f9K%CQvxEn&bsWJ{&winp;FP!KBj=(P6 z4Z_n4L7cS;ao2)ax?Tm|I1pH|uLpDSRVghkA_UtFFuZ0b2#>!8;>-_0ELjQSD-DRd z4im;599VHDZYtnWZGAB25W-e(2VrzEh|etsv2YoP#VbIZ{aFkwPrzJ#JvCvA*mXS& z`}Q^v9(W4GiSs}#s7BaN!WA2bniM$0J(#;MR>uIJ^uvgD3GS^%*ikdW6-!VFUU?JV zZc2)4cMsX@j z5HQ^e3BUzOdm}yC-xA%SY``k$rbfk z;CHqifhU*jfGM@DkYCecD9vl*qr58l6x<8URB=&%{!Cu3RO*MrKZ4VO}V6R0a zZw3Eg^0iKWM1dcTYZ0>N899=r6?+adUiBKPciJw}L$=1f4cs^bio&cr9baLF>6#BM z(F}EXe-`F=f_@`A7+Q&|QaZ??Txp_dB#lg!NH=t3$G8&06MFhwR=Iu*Im0s_b2B@| znW>X}sy~m#EW)&6E&!*0%}8UAS)wjt+A(io#wGI@Z2S+Ms1Cxl%YVE800007ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 literal 0 HcmV?d00001 diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c8f9ed8f5cee1c98386d13b17e89f719e83555b2 GIT binary patch literal 1895 zcmV-t2blPYP)FQtfgmafE#=YDCq`qUBt#QpG%*H6QHY765~R=q zZ6iudfM}q!Pz#~9JgOi8QJ|DSu?1-*(kSi1K4#~5?#|rh?sS)(-JQqX*}ciXJ56_H zdw=^s_srbAdqxlvGyrgGet#6T7_|j;95sL%MtM;q86vOxKM$f#puR)Bjv9Zvz9-di zXOTSsZkM83)E9PYBXC<$6(|>lNLVBb&&6y{NByFCp%6+^ALR@NCTse_wqvNmSWI-m z!$%KlHFH2omF!>#%1l3LTZg(s7eof$7*xB)ZQ0h?ejh?Ta9fDv59+u#MokW+1t8Zb zgHv%K(u9G^Lv`lh#f3<6!JVTL3(dCpxHbnbA;kKqQyd1~^Xe0VIaYBSWm6nsr;dFj z4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3QU4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKubPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`Q zykI#*+rQ=z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRe zt3L_uNyQ*cE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=ky zx=~RKa4{iTm{_>_vSCm?$Ej=i6@=m%@VvAITnigVg{&@!7CDgs908761meDK5azA} z4?=NOH|PdvabgJ&fW2{Mo$Q0CcD8Qc84%{JPYt5EiG{MdLIAeX%T=D7NIP4%Hw}p9 zg)==!2Lbp#j{u_}hMiao9=!VSyx0gHbeCS`;q&vzeq|fs`y&^X-lso(Ls@-706qmA z7u*T5PMo_w3{se1t2`zWeO^hOvTsohG_;>J0wVqVe+n)AbQCx)yh9;w+J6?NF5Lmo zecS@ieAKL8%bVd@+-KT{yI|S}O>pYckUFs;ry9Ow$CD@ztz5K-*D$^{i(_1llhSh^ zEkL$}tsQt5>QA^;QgjgIfBDmcOgi5YDyu?t6vSnbp=1+@6D& z5MJ}B8q;bRlVoxasyhcUF1+)o`&3r0colr}QJ3hcSdLu;9;td>kf@Tcn<@9sIx&=m z;AD;SCh95=&p;$r{Xz3iWCO^MX83AGJ(yH&eTXgv|0=34#-&WAmw{)U7OU9!Wz^!7 zZ%jZFi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i h0WYBP*#0Ks^FNSabJA*5${_#%002ovPDHLkV1oKhTl@e3 literal 0 HcmV?d00001 diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..75b2d164a5a98e212cca15ea7bf2ab5de5108680 GIT binary patch literal 3831 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 literal 0 HcmV?d00001 diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/quick_actions/quick_actions_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..c4df70d39da7941ef3f6dcb7f06a192d8dcb308d GIT binary patch literal 1888 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 iW8 + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Runner/Base.lproj/Main.storyboard b/packages/quick_actions/quick_actions_ios/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 000000000000..f3c28516fb38 --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Runner/Info.plist b/packages/quick_actions/quick_actions_ios/example/ios/Runner/Info.plist new file mode 100644 index 000000000000..d6bca84ca23d --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/example/ios/Runner/Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + quick_actions_example + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Runner/main.m b/packages/quick_actions/quick_actions_ios/example/ios/Runner/main.m new file mode 100644 index 000000000000..f143297b30d6 --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/example/ios/Runner/main.m @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import +#import "AppDelegate.h" + +int main(int argc, char *argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/Info.plist b/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/Info.plist new file mode 100644 index 000000000000..64d65ca49577 --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/RunnerTests.m b/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/RunnerTests.m new file mode 100644 index 000000000000..4a96d05acb58 --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/RunnerTests.m @@ -0,0 +1,18 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import quick_actions_ios; +@import XCTest; + +@interface QuickActionsTests : XCTestCase +@end + +@implementation QuickActionsTests + +- (void)testPlugin { + FLTQuickActionsPlugin *plugin = [[FLTQuickActionsPlugin alloc] init]; + XCTAssertNotNil(plugin); +} + +@end diff --git a/packages/quick_actions/quick_actions_ios/example/ios/RunnerUITests/Info.plist b/packages/quick_actions/quick_actions_ios/example/ios/RunnerUITests/Info.plist new file mode 100644 index 000000000000..64d65ca49577 --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/example/ios/RunnerUITests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/packages/quick_actions/quick_actions_ios/example/ios/RunnerUITests/RunnerUITests.m b/packages/quick_actions/quick_actions_ios/example/ios/RunnerUITests/RunnerUITests.m new file mode 100644 index 000000000000..0bad57f886de --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/example/ios/RunnerUITests/RunnerUITests.m @@ -0,0 +1,99 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import + +static const int kElementWaitingTime = 30; + +@interface RunnerUITests : XCTestCase + +@end + +@implementation RunnerUITests { + XCUIApplication *_exampleApp; +} + +- (void)setUp { + [super setUp]; + self.continueAfterFailure = NO; + _exampleApp = [[XCUIApplication alloc] init]; +} + +- (void)tearDown { + [super tearDown]; + [_exampleApp terminate]; + _exampleApp = nil; +} + +- (void)testQuickActionWithFreshStart { + XCUIApplication *springboard = + [[XCUIApplication alloc] initWithBundleIdentifier:@"com.apple.springboard"]; + XCUIElement *quickActionsAppIcon = springboard.icons[@"quick_actions_example"]; + if (![quickActionsAppIcon waitForExistenceWithTimeout:kElementWaitingTime]) { + os_log_error(OS_LOG_DEFAULT, "%@", springboard.debugDescription); + XCTFail(@"Failed due to not able to find the example app from springboard with %@ seconds", + @(kElementWaitingTime)); + } + + [quickActionsAppIcon pressForDuration:2]; + XCUIElement *actionTwo = springboard.buttons[@"Action two"]; + if (![actionTwo waitForExistenceWithTimeout:kElementWaitingTime]) { + os_log_error(OS_LOG_DEFAULT, "%@", springboard.debugDescription); + XCTFail(@"Failed due to not able to find the actionTwo button from springboard with %@ seconds", + @(kElementWaitingTime)); + } + + [actionTwo tap]; + + XCUIElement *actionTwoConfirmation = _exampleApp.otherElements[@"action_two"]; + if (![actionTwoConfirmation waitForExistenceWithTimeout:kElementWaitingTime]) { + os_log_error(OS_LOG_DEFAULT, "%@", springboard.debugDescription); + XCTFail(@"Failed due to not able to find the actionTwoConfirmation in the app with %@ seconds", + @(kElementWaitingTime)); + } + XCTAssertTrue(actionTwoConfirmation.exists); +} + +- (void)testQuickActionWhenAppIsInBackground { + [_exampleApp launch]; + + XCUIElement *actionsReady = _exampleApp.otherElements[@"actions ready"]; + if (![actionsReady waitForExistenceWithTimeout:kElementWaitingTime]) { + os_log_error(OS_LOG_DEFAULT, "%@", _exampleApp.debugDescription); + XCTFail(@"Failed due to not able to find the actionsReady in the app with %@ seconds", + @(kElementWaitingTime)); + } + + [[XCUIDevice sharedDevice] pressButton:XCUIDeviceButtonHome]; + + XCUIApplication *springboard = + [[XCUIApplication alloc] initWithBundleIdentifier:@"com.apple.springboard"]; + XCUIElement *quickActionsAppIcon = springboard.icons[@"quick_actions_example"]; + if (![quickActionsAppIcon waitForExistenceWithTimeout:kElementWaitingTime]) { + os_log_error(OS_LOG_DEFAULT, "%@", springboard.debugDescription); + XCTFail(@"Failed due to not able to find the example app from springboard with %@ seconds", + @(kElementWaitingTime)); + } + + [quickActionsAppIcon pressForDuration:2]; + XCUIElement *actionOne = springboard.buttons[@"Action one"]; + if (![actionOne waitForExistenceWithTimeout:kElementWaitingTime]) { + os_log_error(OS_LOG_DEFAULT, "%@", springboard.debugDescription); + XCTFail(@"Failed due to not able to find the actionOne button from springboard with %@ seconds", + @(kElementWaitingTime)); + } + + [actionOne tap]; + + XCUIElement *actionOneConfirmation = _exampleApp.otherElements[@"action_one"]; + if (![actionOneConfirmation waitForExistenceWithTimeout:kElementWaitingTime]) { + os_log_error(OS_LOG_DEFAULT, "%@", springboard.debugDescription); + XCTFail(@"Failed due to not able to find the actionOneConfirmation in the app with %@ seconds", + @(kElementWaitingTime)); + } + XCTAssertTrue(actionOneConfirmation.exists); +} + +@end diff --git a/packages/quick_actions/quick_actions_ios/example/lib/main.dart b/packages/quick_actions/quick_actions_ios/example/lib/main.dart new file mode 100644 index 000000000000..5173d952d623 --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/example/lib/main.dart @@ -0,0 +1,82 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; +import 'package:quick_actions_ios/quick_actions_ios.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Quick Actions Demo', + theme: ThemeData( + primarySwatch: Colors.blue, + ), + home: const MyHomePage(), + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({Key? key}) : super(key: key); + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + String shortcut = 'no action set'; + + @override + void initState() { + super.initState(); + + final QuickActionsIos quickActions = QuickActionsIos(); + quickActions.initialize((String shortcutType) { + setState(() { + if (shortcutType != null) { + shortcut = shortcutType; + } + }); + }); + + quickActions.setShortcutItems([ + const ShortcutItem( + type: 'action_one', + localizedTitle: 'Action one', + icon: 'AppIcon', + ), + const ShortcutItem( + type: 'action_two', + localizedTitle: 'Action two', + icon: 'ic_launcher'), + ]).then((void _) { + setState(() { + if (shortcut == 'no action set') { + shortcut = 'actions ready'; + } + }); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(shortcut), + ), + body: const Center( + child: Text('On home screen, long press the app icon to ' + 'get Action one or Action two options. Tapping on that action should ' + 'set the toolbar title.'), + ), + ); + } +} diff --git a/packages/quick_actions/quick_actions_ios/example/pubspec.yaml b/packages/quick_actions/quick_actions_ios/example/pubspec.yaml new file mode 100644 index 000000000000..49c6b5e89ed4 --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/example/pubspec.yaml @@ -0,0 +1,27 @@ +name: quick_actions_example +description: Demonstrates how to use the quick_actions plugin. +publish_to: none + +environment: + sdk: ">=2.15.0 <3.0.0" + flutter: ">=2.8.0" + +dependencies: + flutter: + sdk: flutter + quick_actions_ios: + # When depending on this package from a real application you should use: + # quick_actions_ios: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + +dev_dependencies: + flutter_driver: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/quick_actions/quick_actions_ios/example/test_driver/integration_test.dart b/packages/quick_actions/quick_actions_ios/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4f10f2a522f3 --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/example/test_driver/integration_test.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); diff --git a/packages/quick_actions/quick_actions/ios/Assets/.gitkeep b/packages/quick_actions/quick_actions_ios/ios/Assets/.gitkeep similarity index 100% rename from packages/quick_actions/quick_actions/ios/Assets/.gitkeep rename to packages/quick_actions/quick_actions_ios/ios/Assets/.gitkeep diff --git a/packages/quick_actions/quick_actions/ios/Classes/FLTQuickActionsPlugin.h b/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.h similarity index 100% rename from packages/quick_actions/quick_actions/ios/Classes/FLTQuickActionsPlugin.h rename to packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.h diff --git a/packages/quick_actions/quick_actions/ios/Classes/FLTQuickActionsPlugin.m b/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.m similarity index 96% rename from packages/quick_actions/quick_actions/ios/Classes/FLTQuickActionsPlugin.m rename to packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.m index a099b696387c..883352c2ac1d 100644 --- a/packages/quick_actions/quick_actions/ios/Classes/FLTQuickActionsPlugin.m +++ b/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.m @@ -4,7 +4,7 @@ #import "FLTQuickActionsPlugin.h" -static NSString *const CHANNEL_NAME = @"plugins.flutter.io/quick_actions"; +static NSString *const kChannelName = @"plugins.flutter.io/quick_actions_ios"; @interface FLTQuickActionsPlugin () @property(nonatomic, retain) FlutterMethodChannel *channel; @@ -15,7 +15,7 @@ @implementation FLTQuickActionsPlugin + (void)registerWithRegistrar:(NSObject *)registrar { FlutterMethodChannel *channel = - [FlutterMethodChannel methodChannelWithName:CHANNEL_NAME + [FlutterMethodChannel methodChannelWithName:kChannelName binaryMessenger:[registrar messenger]]; FLTQuickActionsPlugin *instance = [[FLTQuickActionsPlugin alloc] init]; instance.channel = channel; diff --git a/packages/quick_actions/quick_actions/ios/quick_actions.podspec b/packages/quick_actions/quick_actions_ios/ios/quick_actions_ios.podspec similarity index 95% rename from packages/quick_actions/quick_actions/ios/quick_actions.podspec rename to packages/quick_actions/quick_actions_ios/ios/quick_actions_ios.podspec index 7bc36f9d535c..e8485f9b4436 100644 --- a/packages/quick_actions/quick_actions/ios/quick_actions.podspec +++ b/packages/quick_actions/quick_actions_ios/ios/quick_actions_ios.podspec @@ -2,7 +2,7 @@ # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html # Pod::Spec.new do |s| - s.name = 'quick_actions' + s.name = 'quick_actions_ios' s.version = '0.0.1' s.summary = 'Flutter Quick Actions' s.description = <<-DESC diff --git a/packages/quick_actions/quick_actions_ios/lib/quick_actions_ios.dart b/packages/quick_actions/quick_actions_ios/lib/quick_actions_ios.dart new file mode 100644 index 000000000000..d19c9ee371bf --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/lib/quick_actions_ios.dart @@ -0,0 +1,56 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:quick_actions_platform_interface/quick_actions_platform_interface.dart'; + +export 'package:quick_actions_platform_interface/types/types.dart'; + +const MethodChannel _channel = + MethodChannel('plugins.flutter.io/quick_actions_ios'); + +/// An implementation of [QuickActionsPlatform] for iOS. +class QuickActionsIos extends QuickActionsPlatform { + /// Registers this class as the default instance of [QuickActionsPlatform]. + static void registerWith() { + QuickActionsPlatform.instance = QuickActionsIos(); + } + + /// The MethodChannel that is being used by this implementation of the plugin. + @visibleForTesting + MethodChannel get channel => _channel; + + @override + Future initialize(QuickActionHandler handler) async { + channel.setMethodCallHandler((MethodCall call) async { + assert(call.method == 'launch'); + handler(call.arguments as String); + }); + final String? action = + await channel.invokeMethod('getLaunchAction'); + if (action != null) { + handler(action); + } + } + + @override + Future setShortcutItems(List items) async { + final List> itemsList = + items.map(_serializeItem).toList(); + await channel.invokeMethod('setShortcutItems', itemsList); + } + + @override + Future clearShortcutItems() => + channel.invokeMethod('clearShortcutItems'); + + Map _serializeItem(ShortcutItem item) { + return { + 'type': item.type, + 'localizedTitle': item.localizedTitle, + 'icon': item.icon, + }; + } +} diff --git a/packages/quick_actions/quick_actions_ios/pubspec.yaml b/packages/quick_actions/quick_actions_ios/pubspec.yaml new file mode 100644 index 000000000000..26644ba12fde --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/pubspec.yaml @@ -0,0 +1,30 @@ +name: quick_actions_ios +description: An implementation for the iOS platform of the Flutter `quick_actions` plugin. +repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions_ios +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 +version: 0.6.0+9 + +environment: + sdk: ">=2.15.0 <3.0.0" + flutter: ">=2.8.0" + +flutter: + plugin: + implements: quick_actions + platforms: + ios: + pluginClass: FLTQuickActionsPlugin + dartPluginClass: QuickActionsIos + +dependencies: + flutter: + sdk: flutter + quick_actions_platform_interface: ^1.0.0 + +dev_dependencies: + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + plugin_platform_interface: ^2.1.2 + \ No newline at end of file diff --git a/packages/quick_actions/quick_actions_ios/test/quick_actions_ios_test.dart b/packages/quick_actions/quick_actions_ios/test/quick_actions_ios_test.dart new file mode 100644 index 000000000000..36827a5c6d8c --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/test/quick_actions_ios_test.dart @@ -0,0 +1,164 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:quick_actions_ios/quick_actions_ios.dart'; +import 'package:quick_actions_platform_interface/quick_actions_platform_interface.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('$QuickActionsIos', () { + late List log; + + setUp(() { + log = []; + }); + + QuickActionsIos buildQuickActionsPlugin() { + final QuickActionsIos quickActions = QuickActionsIos(); + quickActions.channel + .setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + return ''; + }); + + return quickActions; + } + + test('registerWith() registers correct instance', () { + QuickActionsIos.registerWith(); + expect(QuickActionsPlatform.instance, isA()); + }); + + group('#initialize', () { + test('passes getLaunchAction on launch method', () { + final QuickActionsIos quickActions = buildQuickActionsPlugin(); + quickActions.initialize((String type) {}); + + expect( + log, + [ + isMethodCall('getLaunchAction', arguments: null), + ], + ); + }); + + test('initialize', () async { + final QuickActionsIos quickActions = buildQuickActionsPlugin(); + final Completer quickActionsHandler = Completer(); + await quickActions + .initialize((_) => quickActionsHandler.complete(true)); + expect( + log, + [ + isMethodCall('getLaunchAction', arguments: null), + ], + ); + log.clear(); + + expect(quickActionsHandler.future, completion(isTrue)); + }); + }); + + group('#setShortCutItems', () { + test('passes shortcutItem through channel', () { + final QuickActionsIos quickActions = buildQuickActionsPlugin(); + quickActions.initialize((String type) {}); + quickActions.setShortcutItems([ + const ShortcutItem( + type: 'test', localizedTitle: 'title', icon: 'icon.svg') + ]); + + expect( + log, + [ + isMethodCall('getLaunchAction', arguments: null), + isMethodCall('setShortcutItems', arguments: >[ + { + 'type': 'test', + 'localizedTitle': 'title', + 'icon': 'icon.svg', + } + ]), + ], + ); + }); + + test('setShortcutItems with demo data', () async { + const String type = 'type'; + const String localizedTitle = 'localizedTitle'; + const String icon = 'icon'; + final QuickActionsIos quickActions = buildQuickActionsPlugin(); + await quickActions.setShortcutItems( + const [ + ShortcutItem(type: type, localizedTitle: localizedTitle, icon: icon) + ], + ); + expect( + log, + [ + isMethodCall( + 'setShortcutItems', + arguments: >[ + { + 'type': type, + 'localizedTitle': localizedTitle, + 'icon': icon, + } + ], + ), + ], + ); + log.clear(); + }); + }); + + group('#clearShortCutItems', () { + test('send clearShortcutItems through channel', () { + final QuickActionsIos quickActions = buildQuickActionsPlugin(); + quickActions.initialize((String type) {}); + quickActions.clearShortcutItems(); + + expect( + log, + [ + isMethodCall('getLaunchAction', arguments: null), + isMethodCall('clearShortcutItems', arguments: null), + ], + ); + }); + + test('clearShortcutItems', () { + final QuickActionsIos quickActions = buildQuickActionsPlugin(); + quickActions.clearShortcutItems(); + expect( + log, + [ + isMethodCall('clearShortcutItems', arguments: null), + ], + ); + log.clear(); + }); + }); + }); + + group('$ShortcutItem', () { + test('Shortcut item can be constructed', () { + const String type = 'type'; + const String localizedTitle = 'title'; + const String icon = 'foo'; + + const ShortcutItem item = + ShortcutItem(type: type, localizedTitle: localizedTitle, icon: icon); + + expect(item.type, type); + expect(item.localizedTitle, localizedTitle); + expect(item.icon, icon); + }); + }); +} From a3fd3e92088ab6912e6cc82082d223039031c478 Mon Sep 17 00:00:00 2001 From: Bart Selwesiuk Date: Wed, 9 Feb 2022 18:05:14 +0100 Subject: [PATCH 155/600] [camera_web] Release the camera stream of each available video input device (#4621) --- packages/camera/camera_web/CHANGELOG.md | 3 +- .../integration_test/camera_web_test.dart | 31 +++++++++++++++++++ .../camera/camera_web/lib/src/camera_web.dart | 3 ++ packages/camera/camera_web/pubspec.yaml | 2 +- 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index 4ac140ec4386..cfb980a602c1 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.2.1+2 +* Fixes cameraNotReadable error that prevented access to the camera on some Android devices when initializing a camera. * Implemented support for new Dart SDKs with an async requestFullscreen API. ## 0.2.1+1 diff --git a/packages/camera/camera_web/example/integration_test/camera_web_test.dart b/packages/camera/camera_web/example/integration_test/camera_web_test.dart index 9749559ed8c6..0943d7a94ea9 100644 --- a/packages/camera/camera_web/example/integration_test/camera_web_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_web_test.dart @@ -374,6 +374,37 @@ void main() { ); }); + testWidgets( + 'releases the video stream ' + 'of a video input device', (tester) async { + final videoDevice = FakeMediaDeviceInfo( + '1', + 'Camera 1', + MediaDeviceKind.videoInput, + ); + + final videoStream = + FakeMediaStream([MockMediaStreamTrack(), MockMediaStreamTrack()]); + + when(mediaDevices.enumerateDevices).thenAnswer( + (_) => Future.value([videoDevice]), + ); + + when( + () => cameraService.getMediaStreamForOptions( + CameraOptions( + video: VideoConstraints(deviceId: videoDevice.deviceId), + ), + ), + ).thenAnswer((_) => Future.value(videoStream)); + + final _ = await CameraPlatform.instance.availableCameras(); + + for (var videoTrack in videoStream.getVideoTracks()) { + verify(videoTrack.stop).called(1); + } + }); + group('throws CameraException', () { testWidgets( 'with notSupported error ' diff --git a/packages/camera/camera_web/lib/src/camera_web.dart b/packages/camera/camera_web/lib/src/camera_web.dart index 5fbd820103c7..f183b787fb6c 100644 --- a/packages/camera/camera_web/lib/src/camera_web.dart +++ b/packages/camera/camera_web/lib/src/camera_web.dart @@ -163,6 +163,9 @@ class CameraPlugin extends CameraPlatform { cameras.add(camera); camerasMetadata[camera] = cameraMetadata; + + // Release the camera stream of the current video input device. + videoTracks.forEach((videoTrack) => videoTrack.stop()); } else { // Ignore as no video tracks exist in the current video input device. continue; diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index c75183de1e12..2839710af2f5 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_web description: A Flutter plugin for getting information about and controlling the camera on Web. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.2.1+1 +version: 0.2.1+2 environment: sdk: ">=2.12.0 <3.0.0" From 049ae071e9793363e30238a47b23840fb381615b Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Wed, 9 Feb 2022 18:45:25 +0100 Subject: [PATCH 156/600] [quick_actions] Extract the Android implementation into a separate package (#4698) --- .../quick_actions/quick_actions/pubspec.yaml | 5 +- .../quick_actions_android/AUTHORS | 68 ++++++++ .../quick_actions_android/CHANGELOG.md | 3 + .../quick_actions_android/LICENSE | 25 +++ .../quick_actions_android/README.md | 17 ++ .../android/build.gradle | 0 .../android/settings.gradle | 0 .../android/src/main/AndroidManifest.xml | 0 .../quickactions/MethodCallHandlerImpl.java | 2 +- .../quickactions/QuickActionsPlugin.java | 2 +- .../quickactions/QuickActionsTest.java | 2 +- .../quick_actions_android/example/README.md | 8 + .../example/android/app/build.gradle | 59 +++++++ .../gradle/wrapper/gradle-wrapper.properties | 5 + .../flutter/plugins/DartIntegrationTest.java | 14 ++ .../FlutterActivityTest.java | 19 ++ .../quickactionsexample/QuickActionsTest.java | 0 .../android/app/src/debug/AndroidManifest.xml | 17 ++ .../android/app/src/main/AndroidManifest.xml | 22 +++ .../QuickActionsTestActivity.java | 20 +++ .../res/drawable/ic_launcher_background.xml | 74 ++++++++ .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values/styles.xml | 7 + .../example/android/build.gradle | 29 ++++ .../example/android/gradle.properties | 4 + .../gradle/wrapper/gradle-wrapper.properties | 5 + .../example/android/settings.gradle | 15 ++ .../integration_test/quick_actions_test.dart | 24 +++ .../example/lib/main.dart | 82 +++++++++ .../example/pubspec.yaml | 28 +++ .../example/test_driver/integration_test.dart | 7 + .../lib/quick_actions_android.dart | 56 ++++++ .../quick_actions_android/pubspec.yaml | 30 ++++ .../test/quick_actions_android_test.dart | 164 ++++++++++++++++++ 38 files changed, 808 insertions(+), 5 deletions(-) create mode 100644 packages/quick_actions/quick_actions_android/AUTHORS create mode 100644 packages/quick_actions/quick_actions_android/CHANGELOG.md create mode 100644 packages/quick_actions/quick_actions_android/LICENSE create mode 100644 packages/quick_actions/quick_actions_android/README.md rename packages/quick_actions/{quick_actions => quick_actions_android}/android/build.gradle (100%) rename packages/quick_actions/{quick_actions => quick_actions_android}/android/settings.gradle (100%) rename packages/quick_actions/{quick_actions => quick_actions_android}/android/src/main/AndroidManifest.xml (100%) rename packages/quick_actions/{quick_actions => quick_actions_android}/android/src/main/java/io/flutter/plugins/quickactions/MethodCallHandlerImpl.java (99%) rename packages/quick_actions/{quick_actions => quick_actions_android}/android/src/main/java/io/flutter/plugins/quickactions/QuickActionsPlugin.java (99%) rename packages/quick_actions/{quick_actions => quick_actions_android}/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java (98%) create mode 100644 packages/quick_actions/quick_actions_android/example/README.md create mode 100644 packages/quick_actions/quick_actions_android/example/android/app/build.gradle create mode 100644 packages/quick_actions/quick_actions_android/example/android/app/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/quick_actions/quick_actions_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java create mode 100644 packages/quick_actions/quick_actions_android/example/android/app/src/androidTest/java/io/flutter/plugins/quickactionsexample/FlutterActivityTest.java rename packages/quick_actions/{quick_actions => quick_actions_android}/example/android/app/src/androidTest/java/io/flutter/plugins/quickactionsexample/QuickActionsTest.java (100%) create mode 100644 packages/quick_actions/quick_actions_android/example/android/app/src/debug/AndroidManifest.xml create mode 100644 packages/quick_actions/quick_actions_android/example/android/app/src/main/AndroidManifest.xml create mode 100644 packages/quick_actions/quick_actions_android/example/android/app/src/main/java/io/flutter/plugins/quickactionsexample/QuickActionsTestActivity.java create mode 100644 packages/quick_actions/quick_actions_android/example/android/app/src/main/res/drawable/ic_launcher_background.xml create mode 100644 packages/quick_actions/quick_actions_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 packages/quick_actions/quick_actions_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 packages/quick_actions/quick_actions_android/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 packages/quick_actions/quick_actions_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 packages/quick_actions/quick_actions_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 packages/quick_actions/quick_actions_android/example/android/app/src/main/res/values/styles.xml create mode 100644 packages/quick_actions/quick_actions_android/example/android/build.gradle create mode 100644 packages/quick_actions/quick_actions_android/example/android/gradle.properties create mode 100644 packages/quick_actions/quick_actions_android/example/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/quick_actions/quick_actions_android/example/android/settings.gradle create mode 100644 packages/quick_actions/quick_actions_android/example/integration_test/quick_actions_test.dart create mode 100644 packages/quick_actions/quick_actions_android/example/lib/main.dart create mode 100644 packages/quick_actions/quick_actions_android/example/pubspec.yaml create mode 100644 packages/quick_actions/quick_actions_android/example/test_driver/integration_test.dart create mode 100644 packages/quick_actions/quick_actions_android/lib/quick_actions_android.dart create mode 100644 packages/quick_actions/quick_actions_android/pubspec.yaml create mode 100644 packages/quick_actions/quick_actions_android/test/quick_actions_android_test.dart diff --git a/packages/quick_actions/quick_actions/pubspec.yaml b/packages/quick_actions/quick_actions/pubspec.yaml index 51cf642d59a7..b2a9f7498a95 100644 --- a/packages/quick_actions/quick_actions/pubspec.yaml +++ b/packages/quick_actions/quick_actions/pubspec.yaml @@ -17,8 +17,7 @@ flutter: plugin: platforms: android: - package: io.flutter.plugins.quickactions - pluginClass: QuickActionsPlugin + default_package: quick_actions_android ios: default_package: quick_actions_ios @@ -26,6 +25,8 @@ dependencies: flutter: sdk: flutter # Temporary path dependencies to allow moving Android and iOS implementations. + quick_actions_android: + path: ../quick_actions_android quick_actions_ios: path: ../quick_actions_ios quick_actions_platform_interface: ^1.0.0 diff --git a/packages/quick_actions/quick_actions_android/AUTHORS b/packages/quick_actions/quick_actions_android/AUTHORS new file mode 100644 index 000000000000..5f17b78d134f --- /dev/null +++ b/packages/quick_actions/quick_actions_android/AUTHORS @@ -0,0 +1,68 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> +Daniel Roek +Maurits van Beusekom diff --git a/packages/quick_actions/quick_actions_android/CHANGELOG.md b/packages/quick_actions/quick_actions_android/CHANGELOG.md new file mode 100644 index 000000000000..98e8cf5e333b --- /dev/null +++ b/packages/quick_actions/quick_actions_android/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.6.0+9 + +* Switches to a package-internal implementation of the platform interface. \ No newline at end of file diff --git a/packages/quick_actions/quick_actions_android/LICENSE b/packages/quick_actions/quick_actions_android/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/quick_actions/quick_actions_android/LICENSE @@ -0,0 +1,25 @@ +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/quick_actions/quick_actions_android/README.md b/packages/quick_actions/quick_actions_android/README.md new file mode 100644 index 000000000000..caeb94374398 --- /dev/null +++ b/packages/quick_actions/quick_actions_android/README.md @@ -0,0 +1,17 @@ +# quick\_actions\_android + +The Android implementation of [`quick_actions`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use `quick_actions` +normally. This package will be automatically included in your app when you do. + +## Contributing + +If you would like to contribute to the plugin, check out our [contribution guide][3]. + +[1]: https://pub.dev/packages/quick_actions +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin +[3]: https://github.com/flutter/plugins/blob/master/CONTRIBUTING.md + diff --git a/packages/quick_actions/quick_actions/android/build.gradle b/packages/quick_actions/quick_actions_android/android/build.gradle similarity index 100% rename from packages/quick_actions/quick_actions/android/build.gradle rename to packages/quick_actions/quick_actions_android/android/build.gradle diff --git a/packages/quick_actions/quick_actions/android/settings.gradle b/packages/quick_actions/quick_actions_android/android/settings.gradle similarity index 100% rename from packages/quick_actions/quick_actions/android/settings.gradle rename to packages/quick_actions/quick_actions_android/android/settings.gradle diff --git a/packages/quick_actions/quick_actions/android/src/main/AndroidManifest.xml b/packages/quick_actions/quick_actions_android/android/src/main/AndroidManifest.xml similarity index 100% rename from packages/quick_actions/quick_actions/android/src/main/AndroidManifest.xml rename to packages/quick_actions/quick_actions_android/android/src/main/AndroidManifest.xml diff --git a/packages/quick_actions/quick_actions/android/src/main/java/io/flutter/plugins/quickactions/MethodCallHandlerImpl.java b/packages/quick_actions/quick_actions_android/android/src/main/java/io/flutter/plugins/quickactions/MethodCallHandlerImpl.java similarity index 99% rename from packages/quick_actions/quick_actions/android/src/main/java/io/flutter/plugins/quickactions/MethodCallHandlerImpl.java rename to packages/quick_actions/quick_actions_android/android/src/main/java/io/flutter/plugins/quickactions/MethodCallHandlerImpl.java index 34563cbefeda..6316e8428288 100644 --- a/packages/quick_actions/quick_actions/android/src/main/java/io/flutter/plugins/quickactions/MethodCallHandlerImpl.java +++ b/packages/quick_actions/quick_actions_android/android/src/main/java/io/flutter/plugins/quickactions/MethodCallHandlerImpl.java @@ -27,7 +27,7 @@ class MethodCallHandlerImpl implements MethodChannel.MethodCallHandler { protected static final String EXTRA_ACTION = "some unique action key"; - private static final String CHANNEL_ID = "plugins.flutter.io/quick_actions"; + private static final String CHANNEL_ID = "plugins.flutter.io/quick_actions_android"; private final Context context; private Activity activity; diff --git a/packages/quick_actions/quick_actions/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 similarity index 99% rename from packages/quick_actions/quick_actions/android/src/main/java/io/flutter/plugins/quickactions/QuickActionsPlugin.java rename to packages/quick_actions/quick_actions_android/android/src/main/java/io/flutter/plugins/quickactions/QuickActionsPlugin.java index b2f80ad0a271..99ce0f8426a0 100644 --- a/packages/quick_actions/quick_actions/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 @@ -17,7 +17,7 @@ /** QuickActionsPlugin */ public class QuickActionsPlugin implements FlutterPlugin, ActivityAware, NewIntentListener { - private static final String CHANNEL_ID = "plugins.flutter.io/quick_actions"; + private static final String CHANNEL_ID = "plugins.flutter.io/quick_actions_android"; private MethodChannel channel; private MethodCallHandlerImpl handler; diff --git a/packages/quick_actions/quick_actions/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 similarity index 98% rename from packages/quick_actions/quick_actions/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java rename to packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java index 208a119efafe..d2e63b62f229 100644 --- a/packages/quick_actions/quick_actions/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 @@ -43,7 +43,7 @@ public void send( @NonNull String channel, @Nullable ByteBuffer message, @Nullable final BinaryReply callback) { - if (channel.equals("plugins.flutter.io/quick_actions")) { + if (channel.equals("plugins.flutter.io/quick_actions_android")) { lastMethodCall = StandardMethodCodec.INSTANCE.decodeMethodCall((ByteBuffer) message.position(0)); } diff --git a/packages/quick_actions/quick_actions_android/example/README.md b/packages/quick_actions/quick_actions_android/example/README.md new file mode 100644 index 000000000000..d1b72891de9e --- /dev/null +++ b/packages/quick_actions/quick_actions_android/example/README.md @@ -0,0 +1,8 @@ +# quick_actions_example + +Demonstrates how to use the quick_actions plugin. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](https://flutter.dev/). diff --git a/packages/quick_actions/quick_actions_android/example/android/app/build.gradle b/packages/quick_actions/quick_actions_android/example/android/app/build.gradle new file mode 100644 index 000000000000..54f2e59bacf7 --- /dev/null +++ b/packages/quick_actions/quick_actions_android/example/android/app/build.gradle @@ -0,0 +1,59 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 31 + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + applicationId "io.flutter.plugins.quickactionsexample" + minSdkVersion 21 + targetSdkVersion 28 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + api 'androidx.test:core:1.2.0' +} diff --git a/packages/quick_actions/quick_actions_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/quick_actions/quick_actions_android/example/android/app/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..9a4163a4f5ee --- /dev/null +++ b/packages/quick_actions/quick_actions_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/packages/quick_actions/quick_actions_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java b/packages/quick_actions/quick_actions_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java new file mode 100644 index 000000000000..0f4298dca155 --- /dev/null +++ b/packages/quick_actions/quick_actions_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java @@ -0,0 +1,14 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface DartIntegrationTest {} diff --git a/packages/quick_actions/quick_actions_android/example/android/app/src/androidTest/java/io/flutter/plugins/quickactionsexample/FlutterActivityTest.java b/packages/quick_actions/quick_actions_android/example/android/app/src/androidTest/java/io/flutter/plugins/quickactionsexample/FlutterActivityTest.java new file mode 100644 index 000000000000..e96548da291a --- /dev/null +++ b/packages/quick_actions/quick_actions_android/example/android/app/src/androidTest/java/io/flutter/plugins/quickactionsexample/FlutterActivityTest.java @@ -0,0 +1,19 @@ +// 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.quickactionsexample; + +import androidx.test.rule.ActivityTestRule; +import dev.flutter.plugins.integration_test.FlutterTestRunner; +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.plugins.DartIntegrationTest; +import org.junit.Rule; +import org.junit.runner.RunWith; + +@DartIntegrationTest +@RunWith(FlutterTestRunner.class) +public class FlutterActivityTest { + @Rule + public ActivityTestRule rule = new ActivityTestRule<>(FlutterActivity.class); +} diff --git a/packages/quick_actions/quick_actions/example/android/app/src/androidTest/java/io/flutter/plugins/quickactionsexample/QuickActionsTest.java b/packages/quick_actions/quick_actions_android/example/android/app/src/androidTest/java/io/flutter/plugins/quickactionsexample/QuickActionsTest.java similarity index 100% rename from packages/quick_actions/quick_actions/example/android/app/src/androidTest/java/io/flutter/plugins/quickactionsexample/QuickActionsTest.java rename to packages/quick_actions/quick_actions_android/example/android/app/src/androidTest/java/io/flutter/plugins/quickactionsexample/QuickActionsTest.java diff --git a/packages/quick_actions/quick_actions_android/example/android/app/src/debug/AndroidManifest.xml b/packages/quick_actions/quick_actions_android/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 000000000000..bee689df1735 --- /dev/null +++ b/packages/quick_actions/quick_actions_android/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/packages/quick_actions/quick_actions_android/example/android/app/src/main/AndroidManifest.xml b/packages/quick_actions/quick_actions_android/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..4f384b7c6b13 --- /dev/null +++ b/packages/quick_actions/quick_actions_android/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + diff --git a/packages/quick_actions/quick_actions_android/example/android/app/src/main/java/io/flutter/plugins/quickactionsexample/QuickActionsTestActivity.java b/packages/quick_actions/quick_actions_android/example/android/app/src/main/java/io/flutter/plugins/quickactionsexample/QuickActionsTestActivity.java new file mode 100644 index 000000000000..4ff3a27cd5c0 --- /dev/null +++ b/packages/quick_actions/quick_actions_android/example/android/app/src/main/java/io/flutter/plugins/quickactionsexample/QuickActionsTestActivity.java @@ -0,0 +1,20 @@ +// 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.quickactionsexample; + +import androidx.annotation.NonNull; +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.embedding.engine.FlutterEngine; + +// Makes the FlutterEngine accessible for testing. +public class QuickActionsTestActivity extends FlutterActivity { + public FlutterEngine engine; + + @Override + public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { + super.configureFlutterEngine(flutterEngine); + engine = flutterEngine; + } +} diff --git a/packages/quick_actions/quick_actions_android/example/android/app/src/main/res/drawable/ic_launcher_background.xml b/packages/quick_actions/quick_actions_android/example/android/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 000000000000..9ed346888001 --- /dev/null +++ b/packages/quick_actions/quick_actions_android/example/android/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/quick_actions/quick_actions_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/quick_actions/quick_actions_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/packages/quick_actions/quick_actions_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/quick_actions/quick_actions_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/packages/quick_actions/quick_actions_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/quick_actions/quick_actions_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/packages/quick_actions/quick_actions_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/quick_actions/quick_actions_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/packages/quick_actions/quick_actions_android/example/android/app/src/main/res/values/styles.xml b/packages/quick_actions/quick_actions_android/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 000000000000..6c1d1ec695c9 --- /dev/null +++ b/packages/quick_actions/quick_actions_android/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/packages/quick_actions/quick_actions_android/example/android/build.gradle b/packages/quick_actions/quick_actions_android/example/android/build.gradle new file mode 100644 index 000000000000..e101ac08df55 --- /dev/null +++ b/packages/quick_actions/quick_actions_android/example/android/build.gradle @@ -0,0 +1,29 @@ +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.3.0' + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/packages/quick_actions/quick_actions_android/example/android/gradle.properties b/packages/quick_actions/quick_actions_android/example/android/gradle.properties new file mode 100644 index 000000000000..38c8d4544ff1 --- /dev/null +++ b/packages/quick_actions/quick_actions_android/example/android/gradle.properties @@ -0,0 +1,4 @@ +org.gradle.jvmargs=-Xmx1536M +android.enableR8=true +android.useAndroidX=true +android.enableJetifier=true diff --git a/packages/quick_actions/quick_actions_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/quick_actions/quick_actions_android/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..019065d1d650 --- /dev/null +++ b/packages/quick_actions/quick_actions_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip diff --git a/packages/quick_actions/quick_actions_android/example/android/settings.gradle b/packages/quick_actions/quick_actions_android/example/android/settings.gradle new file mode 100644 index 000000000000..115da6cb4f4d --- /dev/null +++ b/packages/quick_actions/quick_actions_android/example/android/settings.gradle @@ -0,0 +1,15 @@ +include ':app' + +def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + +def plugins = new Properties() +def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') +if (pluginsFile.exists()) { + pluginsFile.withInputStream { stream -> plugins.load(stream) } +} + +plugins.each { name, path -> + def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() + include ":$name" + project(":$name").projectDir = pluginDirectory +} diff --git a/packages/quick_actions/quick_actions_android/example/integration_test/quick_actions_test.dart b/packages/quick_actions/quick_actions_android/example/integration_test/quick_actions_test.dart new file mode 100644 index 000000000000..f9c42ad109e7 --- /dev/null +++ b/packages/quick_actions/quick_actions_android/example/integration_test/quick_actions_test.dart @@ -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. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:quick_actions_platform_interface/quick_actions_platform_interface.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('Can set shortcuts', (WidgetTester tester) async { + final QuickActionsPlatform quickActions = QuickActionsPlatform.instance; + await quickActions.initialize((String value) {}); + + const ShortcutItem shortCutItem = ShortcutItem( + type: 'action_one', + localizedTitle: 'Action one', + icon: 'AppIcon', + ); + expect( + quickActions.setShortcutItems([shortCutItem]), completes); + }); +} diff --git a/packages/quick_actions/quick_actions_android/example/lib/main.dart b/packages/quick_actions/quick_actions_android/example/lib/main.dart new file mode 100644 index 000000000000..06f141073b33 --- /dev/null +++ b/packages/quick_actions/quick_actions_android/example/lib/main.dart @@ -0,0 +1,82 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; +import 'package:quick_actions_android/quick_actions_android.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Quick Actions Demo', + theme: ThemeData( + primarySwatch: Colors.blue, + ), + home: const MyHomePage(), + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({Key? key}) : super(key: key); + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + String shortcut = 'no action set'; + + @override + void initState() { + super.initState(); + + final QuickActionsAndroid quickActions = QuickActionsAndroid(); + quickActions.initialize((String shortcutType) { + setState(() { + if (shortcutType != null) { + shortcut = shortcutType; + } + }); + }); + + quickActions.setShortcutItems([ + const ShortcutItem( + type: 'action_one', + localizedTitle: 'Action one', + icon: 'AppIcon', + ), + const ShortcutItem( + type: 'action_two', + localizedTitle: 'Action two', + icon: 'ic_launcher'), + ]).then((void _) { + setState(() { + if (shortcut == 'no action set') { + shortcut = 'actions ready'; + } + }); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(shortcut), + ), + body: const Center( + child: Text('On home screen, long press the app icon to ' + 'get Action one or Action two options. Tapping on that action should ' + 'set the toolbar title.'), + ), + ); + } +} diff --git a/packages/quick_actions/quick_actions_android/example/pubspec.yaml b/packages/quick_actions/quick_actions_android/example/pubspec.yaml new file mode 100644 index 000000000000..53170971d136 --- /dev/null +++ b/packages/quick_actions/quick_actions_android/example/pubspec.yaml @@ -0,0 +1,28 @@ +name: quick_actions_example +description: Demonstrates how to use the quick_actions plugin. +publish_to: none + +environment: + sdk: ">=2.15.0 <3.0.0" + flutter: ">=2.8.0" + +dependencies: + flutter: + sdk: flutter + quick_actions_android: + # When depending on this package from a real application you should use: + # quick_actions_android: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + +dev_dependencies: + espresso: ^0.1.0+2 + flutter_driver: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/quick_actions/quick_actions_android/example/test_driver/integration_test.dart b/packages/quick_actions/quick_actions_android/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4f10f2a522f3 --- /dev/null +++ b/packages/quick_actions/quick_actions_android/example/test_driver/integration_test.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); diff --git a/packages/quick_actions/quick_actions_android/lib/quick_actions_android.dart b/packages/quick_actions/quick_actions_android/lib/quick_actions_android.dart new file mode 100644 index 000000000000..99a54e9866af --- /dev/null +++ b/packages/quick_actions/quick_actions_android/lib/quick_actions_android.dart @@ -0,0 +1,56 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:quick_actions_platform_interface/quick_actions_platform_interface.dart'; + +export 'package:quick_actions_platform_interface/types/types.dart'; + +const MethodChannel _channel = + MethodChannel('plugins.flutter.io/quick_actions_android'); + +/// An implementation of [QuickActionsPlatform] that for Android. +class QuickActionsAndroid extends QuickActionsPlatform { + /// Registers this class as the default instance of [QuickActionsPlatform]. + static void registerWith() { + QuickActionsPlatform.instance = QuickActionsAndroid(); + } + + /// The MethodChannel that is being used by this implementation of the plugin. + @visibleForTesting + MethodChannel get channel => _channel; + + @override + Future initialize(QuickActionHandler handler) async { + channel.setMethodCallHandler((MethodCall call) async { + assert(call.method == 'launch'); + handler(call.arguments as String); + }); + final String? action = + await channel.invokeMethod('getLaunchAction'); + if (action != null) { + handler(action); + } + } + + @override + Future setShortcutItems(List items) async { + final List> itemsList = + items.map(_serializeItem).toList(); + await channel.invokeMethod('setShortcutItems', itemsList); + } + + @override + Future clearShortcutItems() => + channel.invokeMethod('clearShortcutItems'); + + Map _serializeItem(ShortcutItem item) { + return { + 'type': item.type, + 'localizedTitle': item.localizedTitle, + 'icon': item.icon, + }; + } +} diff --git a/packages/quick_actions/quick_actions_android/pubspec.yaml b/packages/quick_actions/quick_actions_android/pubspec.yaml new file mode 100644 index 000000000000..cf9971dca945 --- /dev/null +++ b/packages/quick_actions/quick_actions_android/pubspec.yaml @@ -0,0 +1,30 @@ +name: quick_actions_android +description: An implementation for the Android platform of the Flutter `quick_actions` plugin. +repository: https://github.com/flutter/plugins/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: 0.6.0+9 + +environment: + sdk: ">=2.15.0 <3.0.0" + flutter: ">=2.8.0" + +flutter: + plugin: + implements: quick_actions + platforms: + android: + package: io.flutter.plugins.quickactions + pluginClass: QuickActionsPlugin + dartPluginClass: QuickActionsAndroid + +dependencies: + flutter: + sdk: flutter + quick_actions_platform_interface: ^1.0.0 + +dev_dependencies: + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + plugin_platform_interface: ^2.1.2 diff --git a/packages/quick_actions/quick_actions_android/test/quick_actions_android_test.dart b/packages/quick_actions/quick_actions_android/test/quick_actions_android_test.dart new file mode 100644 index 000000000000..40cfe458615d --- /dev/null +++ b/packages/quick_actions/quick_actions_android/test/quick_actions_android_test.dart @@ -0,0 +1,164 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:quick_actions_android/quick_actions_android.dart'; +import 'package:quick_actions_platform_interface/quick_actions_platform_interface.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('$QuickActionsAndroid', () { + late List log; + + setUp(() { + log = []; + }); + + QuickActionsAndroid buildQuickActionsPlugin() { + final QuickActionsAndroid quickActions = QuickActionsAndroid(); + quickActions.channel + .setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + return ''; + }); + + return quickActions; + } + + test('registerWith() registers correct instance', () { + QuickActionsAndroid.registerWith(); + expect(QuickActionsPlatform.instance, isA()); + }); + + group('#initialize', () { + test('passes getLaunchAction on launch method', () { + final QuickActionsAndroid quickActions = buildQuickActionsPlugin(); + quickActions.initialize((String type) {}); + + expect( + log, + [ + isMethodCall('getLaunchAction', arguments: null), + ], + ); + }); + + test('initialize', () async { + final QuickActionsAndroid quickActions = buildQuickActionsPlugin(); + final Completer quickActionsHandler = Completer(); + await quickActions + .initialize((_) => quickActionsHandler.complete(true)); + expect( + log, + [ + isMethodCall('getLaunchAction', arguments: null), + ], + ); + log.clear(); + + expect(quickActionsHandler.future, completion(isTrue)); + }); + }); + + group('#setShortCutItems', () { + test('passes shortcutItem through channel', () { + final QuickActionsAndroid quickActions = buildQuickActionsPlugin(); + quickActions.initialize((String type) {}); + quickActions.setShortcutItems([ + const ShortcutItem( + type: 'test', localizedTitle: 'title', icon: 'icon.svg') + ]); + + expect( + log, + [ + isMethodCall('getLaunchAction', arguments: null), + isMethodCall('setShortcutItems', arguments: >[ + { + 'type': 'test', + 'localizedTitle': 'title', + 'icon': 'icon.svg', + } + ]), + ], + ); + }); + + test('setShortcutItems with demo data', () async { + const String type = 'type'; + const String localizedTitle = 'localizedTitle'; + const String icon = 'icon'; + final QuickActionsAndroid quickActions = buildQuickActionsPlugin(); + await quickActions.setShortcutItems( + const [ + ShortcutItem(type: type, localizedTitle: localizedTitle, icon: icon) + ], + ); + expect( + log, + [ + isMethodCall( + 'setShortcutItems', + arguments: >[ + { + 'type': type, + 'localizedTitle': localizedTitle, + 'icon': icon, + } + ], + ), + ], + ); + log.clear(); + }); + }); + + group('#clearShortCutItems', () { + test('send clearShortcutItems through channel', () { + final QuickActionsAndroid quickActions = buildQuickActionsPlugin(); + quickActions.initialize((String type) {}); + quickActions.clearShortcutItems(); + + expect( + log, + [ + isMethodCall('getLaunchAction', arguments: null), + isMethodCall('clearShortcutItems', arguments: null), + ], + ); + }); + + test('clearShortcutItems', () { + final QuickActionsAndroid quickActions = buildQuickActionsPlugin(); + quickActions.clearShortcutItems(); + expect( + log, + [ + isMethodCall('clearShortcutItems', arguments: null), + ], + ); + log.clear(); + }); + }); + }); + + group('$ShortcutItem', () { + test('Shortcut item can be constructed', () { + const String type = 'type'; + const String localizedTitle = 'title'; + const String icon = 'foo'; + + const ShortcutItem item = + ShortcutItem(type: type, localizedTitle: localizedTitle, icon: icon); + + expect(item.type, type); + expect(item.localizedTitle, localizedTitle); + expect(item.icon, icon); + }); + }); +} From f2944b58dc4282e512d4b9498f1f44b4e37cadf8 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Wed, 9 Feb 2022 10:24:58 -0800 Subject: [PATCH 157/600] [webview_flutter_wkwebview] Start work on new ios implementation (#4626) --- .../lib/src/web_kit/web_kit.dart | 69 ++++++++ .../lib/src/web_kit_webview_widget.dart | 157 ++++++++++++++++++ .../webview_flutter_wkwebview/pubspec.yaml | 2 + .../test/src/web_kit_webview_widget_test.dart | 134 +++++++++++++++ .../web_kit_webview_widget_test.mocks.dart | 129 ++++++++++++++ 5 files changed, 491 insertions(+) create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart new file mode 100644 index 000000000000..562957f34eac --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart @@ -0,0 +1,69 @@ +// 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. + +/// The media types that require a user gesture to begin playing. +/// +/// Wraps [WKAudiovisualMediaTypes](https://developer.apple.com/documentation/webkit/wkaudiovisualmediatypes?language=objc). +enum WKAudiovisualMediaType { + /// No media types require a user gesture to begin playing. + /// + /// See https://developer.apple.com/documentation/webkit/wkaudiovisualmediatypes/wkaudiovisualmediatypenone?language=objc. + none, + + /// Media types that contain audio require a user gesture to begin playing. + /// + /// See https://developer.apple.com/documentation/webkit/wkaudiovisualmediatypes/wkaudiovisualmediatypeaudio?language=objc. + audio, + + /// Media types that contain video require a user gesture to begin playing. + /// + /// See https://developer.apple.com/documentation/webkit/wkaudiovisualmediatypes/wkaudiovisualmediatypevideo?language=objc. + video, + + /// All media types require a user gesture to begin playing. + /// + /// See https://developer.apple.com/documentation/webkit/wkaudiovisualmediatypes/wkaudiovisualmediatypeall?language=objc. + all, +} + +/// A collection of properties that you use to initialize a web view. +/// +/// Wraps [WKWebViewConfiguration](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration?language=objc) +class WKWebViewConfiguration { + /// Indicates whether HTML5 videos play inline or use the native full-screen controller. + set allowsInlineMediaPlayback(bool allow) { + throw UnimplementedError(); + } + + /// The media types that require a user gesture to begin playing. + /// + /// Use [WKAudiovisualMediaType.none] to indicate that no user gestures are + /// required to begin playing media. + set mediaTypesRequiringUserActionForPlayback( + Set types, + ) { + assert(types.isNotEmpty); + throw UnimplementedError(); + } +} + +/// Object that displays interactive web content, such as for an in-app browser. +/// +/// Wraps [WKWebView](https://developer.apple.com/documentation/webkit/wkwebview?language=objc). +class WKWebView { + /// Construct a [WKWebView]. + /// + /// [configuration] contains the configuration details for the web view. This + /// method saves a copy of your configuration object. Changes you make to your + /// original object after calling this method have no effect on the web view’s + /// configuration. For a list of configuration options and their default + /// values, see [WKWebViewConfiguration]. If you didn’t create your web view + /// using the `configuration` parameter, this value uses a default + /// configuration object. + // TODO(bparrishMines): Remove ignore once constructor is implemented. + // ignore: avoid_unused_constructor_parameters + WKWebView([WKWebViewConfiguration? configuration]) { + throw UnimplementedError(); + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart new file mode 100644 index 000000000000..38cc8619f9b4 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -0,0 +1,157 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; + +import 'web_kit/web_kit.dart'; + +/// A [Widget] that displays a [WKWebView]. +class WebKitWebViewWidget extends StatefulWidget { + /// Constructs a [WebKitWebViewWidget]. + const WebKitWebViewWidget({ + required this.creationParams, + required this.callbacksHandler, + required this.javascriptChannelRegistry, + required this.onBuildWidget, + this.configuration, + @visibleForTesting this.webViewProxy = const WebViewProxy(), + }); + + /// The initial parameters used to setup the WebView. + final CreationParams creationParams; + + /// The handler of callbacks made made by [NavigationDelegate]. + final WebViewPlatformCallbacksHandler callbacksHandler; + + /// Manager of named JavaScript channels and forwarding incoming messages on the correct channel. + final JavascriptChannelRegistry javascriptChannelRegistry; + + /// A collection of properties used to initialize a web view. + /// + /// If null, a default configuration is used. + final WKWebViewConfiguration? configuration; + + /// The handler for constructing [WKWebView]s and calling static methods. + /// + /// This should only be changed for testing purposes. + final WebViewProxy webViewProxy; + + /// A callback to build a widget once [WKWebView] has been initialized. + final Widget Function(WebKitWebViewPlatformController controller) + onBuildWidget; + + @override + State createState() => _WebKitWebViewWidgetState(); +} + +class _WebKitWebViewWidgetState extends State { + late final WebKitWebViewPlatformController controller; + + @override + void initState() { + super.initState(); + controller = WebKitWebViewPlatformController( + creationParams: widget.creationParams, + callbacksHandler: widget.callbacksHandler, + javascriptChannelRegistry: widget.javascriptChannelRegistry, + configuration: widget.configuration, + webViewProxy: widget.webViewProxy, + ); + } + + @override + Widget build(BuildContext context) { + return widget.onBuildWidget(controller); + } +} + +/// An implementation of [WebViewPlatformController] with the WebKit api. +class WebKitWebViewPlatformController extends WebViewPlatformController { + /// Construct a [WebKitWebViewPlatformController]. + WebKitWebViewPlatformController({ + required CreationParams creationParams, + required this.callbacksHandler, + required this.javascriptChannelRegistry, + WKWebViewConfiguration? configuration, + @visibleForTesting this.webViewProxy = const WebViewProxy(), + }) : super(callbacksHandler) { + _setCreationParams( + creationParams, + configuration: configuration ?? WKWebViewConfiguration(), + ).then((_) => _initializationCompleter.complete()); + } + + final Completer _initializationCompleter = Completer(); + + /// Handles callbacks that are made by navigation. + final WebViewPlatformCallbacksHandler callbacksHandler; + + /// Manages named JavaScript channels and forwarding incoming messages on the correct channel. + final JavascriptChannelRegistry javascriptChannelRegistry; + + /// Handles constructing a [WKWebView]. + /// + /// This should only be changed when used for testing. + final WebViewProxy webViewProxy; + + /// Represents the WebView maintained by platform code. + late final WKWebView webView; + + Future _setCreationParams( + CreationParams params, { + required WKWebViewConfiguration configuration, + }) async { + _setWebViewConfiguration( + configuration, + allowsInlineMediaPlayback: params.webSettings?.allowsInlineMediaPlayback, + autoMediaPlaybackPolicy: params.autoMediaPlaybackPolicy, + ); + + webView = webViewProxy.createWebView(configuration); + } + + void _setWebViewConfiguration( + WKWebViewConfiguration configuration, { + required bool? allowsInlineMediaPlayback, + required AutoMediaPlaybackPolicy autoMediaPlaybackPolicy, + }) { + if (allowsInlineMediaPlayback != null) { + configuration.allowsInlineMediaPlayback = allowsInlineMediaPlayback; + } + + late final bool requiresUserAction; + switch (autoMediaPlaybackPolicy) { + case AutoMediaPlaybackPolicy.require_user_action_for_all_media_types: + requiresUserAction = true; + break; + case AutoMediaPlaybackPolicy.always_allow: + requiresUserAction = false; + break; + } + + configuration.mediaTypesRequiringUserActionForPlayback = + { + if (requiresUserAction) WKAudiovisualMediaType.all, + if (!requiresUserAction) WKAudiovisualMediaType.none, + }; + } +} + +/// Handles constructing [WKWebView]s and calling static methods. +/// +/// This should only be used for testing purposes. +@visibleForTesting +class WebViewProxy { + /// Creates a [WebViewProxy]. + const WebViewProxy(); + + /// Constructs a [WKWebView]. + WKWebView createWebView(WKWebViewConfiguration configuration) { + return WKWebView(configuration); + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index e61eac3faf22..26d14ea9d468 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -21,8 +21,10 @@ dependencies: webview_flutter_platform_interface: ^1.8.0 dev_dependencies: + build_runner: ^2.1.5 flutter_driver: sdk: flutter flutter_test: sdk: flutter + mockito: ^5.0.16 pedantic: ^1.10.0 diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart new file mode 100644 index 000000000000..6ddec48182a2 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -0,0 +1,134 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; +import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart'; +import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart'; + +import 'web_kit_webview_widget_test.mocks.dart'; + +@GenerateMocks([ + WKWebView, + WKWebViewConfiguration, + JavascriptChannelRegistry, + WebViewPlatformCallbacksHandler, + WebViewProxy, +]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('$WebKitWebViewWidget', () { + late MockWKWebView mockWebView; + late MockWebViewProxy mockWebViewProxy; + late MockWKWebViewConfiguration mockWebViewConfiguration; + + late MockWebViewPlatformCallbacksHandler mockCallbacksHandler; + late MockJavascriptChannelRegistry mockJavascriptChannelRegistry; + + setUp(() { + mockWebView = MockWKWebView(); + mockWebViewConfiguration = MockWKWebViewConfiguration(); + mockWebViewProxy = MockWebViewProxy(); + + when(mockWebViewProxy.createWebView(any)).thenReturn(mockWebView); + + mockCallbacksHandler = MockWebViewPlatformCallbacksHandler(); + mockJavascriptChannelRegistry = MockJavascriptChannelRegistry(); + }); + + // Builds a WebViewCupertinoWidget with default parameters. + Future buildWidget( + WidgetTester tester, { + CreationParams? creationParams, + bool hasNavigationDelegate = false, + bool hasProgressTracking = false, + bool useHybridComposition = false, + }) async { + await tester.pumpWidget(WebKitWebViewWidget( + creationParams: creationParams ?? + CreationParams( + webSettings: WebSettings( + userAgent: const WebSetting.absent(), + hasNavigationDelegate: hasNavigationDelegate, + hasProgressTracking: hasProgressTracking, + )), + callbacksHandler: mockCallbacksHandler, + javascriptChannelRegistry: mockJavascriptChannelRegistry, + webViewProxy: mockWebViewProxy, + configuration: mockWebViewConfiguration, + onBuildWidget: (WebKitWebViewPlatformController controller) { + return Container(); + }, + )); + await tester.pumpAndSettle(); + } + + testWidgets('build $WebKitWebViewWidget', (WidgetTester tester) async { + await buildWidget(tester); + }); + + group('$CreationParams', () { + testWidgets('autoMediaPlaybackPolicy true', (WidgetTester tester) async { + await buildWidget( + tester, + creationParams: CreationParams( + autoMediaPlaybackPolicy: + AutoMediaPlaybackPolicy.require_user_action_for_all_media_types, + webSettings: WebSettings( + userAgent: const WebSetting.absent(), + hasNavigationDelegate: false, + ), + ), + ); + + verify( + mockWebViewConfiguration.mediaTypesRequiringUserActionForPlayback = + { + WKAudiovisualMediaType.all, + }); + }); + + testWidgets('autoMediaPlaybackPolicy false', (WidgetTester tester) async { + await buildWidget( + tester, + creationParams: CreationParams( + autoMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow, + webSettings: WebSettings( + userAgent: const WebSetting.absent(), + hasNavigationDelegate: false, + ), + ), + ); + + verify( + mockWebViewConfiguration.mediaTypesRequiringUserActionForPlayback = + { + WKAudiovisualMediaType.none, + }); + }); + + group('$WebSettings', () { + testWidgets('allowsInlineMediaPlayback', (WidgetTester tester) async { + await buildWidget( + tester, + creationParams: CreationParams( + webSettings: WebSettings( + userAgent: const WebSetting.absent(), + allowsInlineMediaPlayback: true, + ), + ), + ); + + verify(mockWebViewConfiguration.allowsInlineMediaPlayback = true); + }); + }); + }); + }); +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart new file mode 100644 index 000000000000..1ec05313716c --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -0,0 +1,129 @@ +// Mocks generated by Mockito 5.0.17 from annotations +// in webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. +// Do not manually edit this file. + +import 'dart:async' as _i5; + +import 'package:mockito/mockito.dart' as _i1; +import 'package:webview_flutter_platform_interface/src/types/javascript_channel.dart' + as _i4; +import 'package:webview_flutter_platform_interface/src/types/types.dart' as _i6; +import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart' + as _i3; +import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart' as _i2; +import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart' + as _i7; + +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types + +class _FakeWKWebView_0 extends _i1.Fake implements _i2.WKWebView {} + +/// A class which mocks [WKWebView]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockWKWebView extends _i1.Mock implements _i2.WKWebView { + MockWKWebView() { + _i1.throwOnMissingStub(this); + } +} + +/// A class which mocks [WKWebViewConfiguration]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockWKWebViewConfiguration extends _i1.Mock + implements _i2.WKWebViewConfiguration { + MockWKWebViewConfiguration() { + _i1.throwOnMissingStub(this); + } + + @override + set allowsInlineMediaPlayback(bool? allow) => + super.noSuchMethod(Invocation.setter(#allowsInlineMediaPlayback, allow), + returnValueForMissingStub: null); + @override + set mediaTypesRequiringUserActionForPlayback( + Set<_i2.WKAudiovisualMediaType>? types) => + super.noSuchMethod( + Invocation.setter(#mediaTypesRequiringUserActionForPlayback, types), + returnValueForMissingStub: null); +} + +/// A class which mocks [JavascriptChannelRegistry]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockJavascriptChannelRegistry extends _i1.Mock + implements _i3.JavascriptChannelRegistry { + MockJavascriptChannelRegistry() { + _i1.throwOnMissingStub(this); + } + + @override + Map get channels => + (super.noSuchMethod(Invocation.getter(#channels), + returnValue: {}) + as Map); + @override + void onJavascriptChannelMessage(String? channel, String? message) => + super.noSuchMethod( + Invocation.method(#onJavascriptChannelMessage, [channel, message]), + returnValueForMissingStub: null); + @override + void updateJavascriptChannelsFromSet(Set<_i4.JavascriptChannel>? channels) => + super.noSuchMethod( + Invocation.method(#updateJavascriptChannelsFromSet, [channels]), + returnValueForMissingStub: null); +} + +/// A class which mocks [WebViewPlatformCallbacksHandler]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockWebViewPlatformCallbacksHandler extends _i1.Mock + implements _i3.WebViewPlatformCallbacksHandler { + MockWebViewPlatformCallbacksHandler() { + _i1.throwOnMissingStub(this); + } + + @override + _i5.FutureOr onNavigationRequest({String? url, bool? isForMainFrame}) => + (super.noSuchMethod( + Invocation.method(#onNavigationRequest, [], + {#url: url, #isForMainFrame: isForMainFrame}), + returnValue: Future.value(false)) as _i5.FutureOr); + @override + void onPageStarted(String? url) => + super.noSuchMethod(Invocation.method(#onPageStarted, [url]), + returnValueForMissingStub: null); + @override + void onPageFinished(String? url) => + super.noSuchMethod(Invocation.method(#onPageFinished, [url]), + returnValueForMissingStub: null); + @override + void onProgress(int? progress) => + super.noSuchMethod(Invocation.method(#onProgress, [progress]), + returnValueForMissingStub: null); + @override + void onWebResourceError(_i6.WebResourceError? error) => + super.noSuchMethod(Invocation.method(#onWebResourceError, [error]), + returnValueForMissingStub: null); +} + +/// A class which mocks [WebViewProxy]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockWebViewProxy extends _i1.Mock implements _i7.WebViewProxy { + MockWebViewProxy() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.WKWebView createWebView(_i2.WKWebViewConfiguration? configuration) => + (super.noSuchMethod(Invocation.method(#createWebView, [configuration]), + returnValue: _FakeWKWebView_0()) as _i2.WKWebView); +} From 08ba6bdd1e2bbfcbbcb888d06c370fff30b87fa7 Mon Sep 17 00:00:00 2001 From: BeMacized Date: Wed, 9 Feb 2022 20:05:23 +0100 Subject: [PATCH 158/600] [shared_preferences] Move away from shared method channel implementation in native packages. (#4723) --- .../test/shared_preferences_test.dart | 5 - .../shared_preferences_android/CHANGELOG.md | 4 + .../SharedPreferencesPlugin.java | 2 +- .../lib/shared_preferences_android.dart | 53 ++++++++ .../shared_preferences_android/pubspec.yaml | 6 +- .../test/shared_preferences_android_test.dart | 117 ++++++++++++++++++ .../shared_preferences_ios/CHANGELOG.md | 4 + .../ios/Classes/FLTSharedPreferencesPlugin.m | 2 +- .../lib/shared_preferences_ios.dart | 53 ++++++++ .../shared_preferences_ios/pubspec.yaml | 6 +- .../test/shared_preferences_ios_test.dart | 117 ++++++++++++++++++ .../shared_preferences_linux/CHANGELOG.md | 4 + .../shared_preferences_test.dart | 2 +- .../example/lib/main.dart | 2 +- .../lib/shared_preferences_linux.dart | 13 +- .../shared_preferences_linux/pubspec.yaml | 6 +- .../test/shared_preferences_linux_test.dart | 29 ++++- .../shared_preferences_macos/CHANGELOG.md | 3 +- .../lib/shared_preferences_macos.dart | 53 ++++++++ .../Classes/SharedPreferencesPlugin.swift | 2 +- .../shared_preferences_macos/pubspec.yaml | 8 +- .../test/shared_preferences_macos_test.dart | 117 ++++++++++++++++++ .../shared_preferences_windows/CHANGELOG.md | 4 + .../shared_preferences_test.dart | 38 +++--- .../example/lib/main.dart | 2 +- .../lib/shared_preferences_windows.dart | 8 +- .../shared_preferences_windows/pubspec.yaml | 5 +- 27 files changed, 606 insertions(+), 59 deletions(-) create mode 100644 packages/shared_preferences/shared_preferences_android/lib/shared_preferences_android.dart create mode 100644 packages/shared_preferences/shared_preferences_android/test/shared_preferences_android_test.dart create mode 100644 packages/shared_preferences/shared_preferences_ios/lib/shared_preferences_ios.dart create mode 100644 packages/shared_preferences/shared_preferences_ios/test/shared_preferences_ios_test.dart create mode 100644 packages/shared_preferences/shared_preferences_macos/lib/shared_preferences_macos.dart create mode 100644 packages/shared_preferences/shared_preferences_macos/test/shared_preferences_macos_test.dart diff --git a/packages/shared_preferences/shared_preferences/test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences/test/shared_preferences_test.dart index 1938a04ae84b..11498cfa5dcb 100755 --- a/packages/shared_preferences/shared_preferences/test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences/test/shared_preferences_test.dart @@ -47,11 +47,6 @@ void main() { store.log.clear(); }); - tearDown(() async { - await preferences.clear(); - await store.clear(); - }); - test('reading', () async { expect(preferences.get('String'), testString); expect(preferences.get('bool'), testBool); diff --git a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md index 527d64d7b007..5321e869c497 100644 --- a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.11 + +* Switches to an in-package method channel implementation. + ## 2.0.10 * Removes dependency on `meta`. diff --git a/packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/SharedPreferencesPlugin.java b/packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/SharedPreferencesPlugin.java index d41328ee6202..9545fe95c54b 100644 --- a/packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/SharedPreferencesPlugin.java +++ b/packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/SharedPreferencesPlugin.java @@ -11,7 +11,7 @@ /** SharedPreferencesPlugin */ public class SharedPreferencesPlugin implements FlutterPlugin { - private static final String CHANNEL_NAME = "plugins.flutter.io/shared_preferences"; + private static final String CHANNEL_NAME = "plugins.flutter.io/shared_preferences_android"; private MethodChannel channel; private MethodCallHandlerImpl handler; diff --git a/packages/shared_preferences/shared_preferences_android/lib/shared_preferences_android.dart b/packages/shared_preferences/shared_preferences_android/lib/shared_preferences_android.dart new file mode 100644 index 000000000000..86f447b8959c --- /dev/null +++ b/packages/shared_preferences/shared_preferences_android/lib/shared_preferences_android.dart @@ -0,0 +1,53 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; + +const MethodChannel _kChannel = + MethodChannel('plugins.flutter.io/shared_preferences_android'); + +/// The macOS implementation of [SharedPreferencesStorePlatform]. +/// +/// This class implements the `package:shared_preferences` functionality for Android. +class SharedPreferencesAndroid extends SharedPreferencesStorePlatform { + /// Registers this class as the default instance of [SharedPreferencesStorePlatform]. + static void registerWith() { + SharedPreferencesStorePlatform.instance = SharedPreferencesAndroid(); + } + + @override + Future remove(String key) async { + return (await _kChannel.invokeMethod( + 'remove', + {'key': key}, + ))!; + } + + @override + Future setValue(String valueType, String key, Object value) async { + return (await _kChannel.invokeMethod( + 'set$valueType', + {'key': key, 'value': value}, + ))!; + } + + @override + Future clear() async { + return (await _kChannel.invokeMethod('clear'))!; + } + + @override + Future> getAll() async { + final Map? preferences = + await _kChannel.invokeMapMethod('getAll'); + + if (preferences == null) { + return {}; + } + return preferences; + } +} diff --git a/packages/shared_preferences/shared_preferences_android/pubspec.yaml b/packages/shared_preferences/shared_preferences_android/pubspec.yaml index 8adb2402cb51..7eb180f3ab48 100644 --- a/packages/shared_preferences/shared_preferences_android/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_android/pubspec.yaml @@ -2,11 +2,11 @@ name: shared_preferences_android description: Android implementation of the shared_preferences plugin repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.10 +version: 2.0.11 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: @@ -15,6 +15,7 @@ flutter: android: package: io.flutter.plugins.sharedpreferences pluginClass: SharedPreferencesPlugin + dartPluginClass: SharedPreferencesAndroid dependencies: flutter: @@ -24,4 +25,3 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - pedantic: ^1.10.0 diff --git a/packages/shared_preferences/shared_preferences_android/test/shared_preferences_android_test.dart b/packages/shared_preferences/shared_preferences_android/test/shared_preferences_android_test.dart new file mode 100644 index 000000000000..92d9e8f53be1 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_android/test/shared_preferences_android_test.dart @@ -0,0 +1,117 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:shared_preferences_android/shared_preferences_android.dart'; +import 'package:shared_preferences_platform_interface/method_channel_shared_preferences.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group(MethodChannelSharedPreferencesStore, () { + const MethodChannel channel = MethodChannel( + 'plugins.flutter.io/shared_preferences_android', + ); + + const Map kTestValues = { + 'flutter.String': 'hello world', + 'flutter.Bool': true, + 'flutter.Int': 42, + 'flutter.Double': 3.14159, + 'flutter.StringList': ['foo', 'bar'], + }; + // Create a dummy in-memory implementation to back the mocked method channel + // API to simplify validation of the expected calls. + late InMemorySharedPreferencesStore testData; + + final List log = []; + late SharedPreferencesStorePlatform store; + + setUp(() async { + testData = InMemorySharedPreferencesStore.empty(); + + channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + if (methodCall.method == 'getAll') { + return await testData.getAll(); + } + if (methodCall.method == 'remove') { + final String key = methodCall.arguments['key']! as String; + return await testData.remove(key); + } + if (methodCall.method == 'clear') { + return await testData.clear(); + } + final RegExp setterRegExp = RegExp(r'set(.*)'); + final Match? match = setterRegExp.matchAsPrefix(methodCall.method); + if (match?.groupCount == 1) { + final String valueType = match!.group(1)!; + final String key = methodCall.arguments['key'] as String; + final Object value = methodCall.arguments['value'] as Object; + return await testData.setValue(valueType, key, value); + } + fail('Unexpected method call: ${methodCall.method}'); + }); + log.clear(); + }); + + test('registered instance', () { + SharedPreferencesAndroid.registerWith(); + expect(SharedPreferencesStorePlatform.instance, + isA()); + }); + + test('getAll', () async { + store = SharedPreferencesAndroid(); + testData = InMemorySharedPreferencesStore.withData(kTestValues); + expect(await store.getAll(), kTestValues); + expect(log.single.method, 'getAll'); + }); + + test('remove', () async { + store = SharedPreferencesAndroid(); + testData = InMemorySharedPreferencesStore.withData(kTestValues); + expect(await store.remove('flutter.String'), true); + expect(await store.remove('flutter.Bool'), true); + expect(await store.remove('flutter.Int'), true); + expect(await store.remove('flutter.Double'), true); + expect(await testData.getAll(), { + 'flutter.StringList': ['foo', 'bar'], + }); + + expect(log, hasLength(4)); + for (final MethodCall call in log) { + expect(call.method, 'remove'); + } + }); + + test('setValue', () async { + store = SharedPreferencesAndroid(); + expect(await testData.getAll(), isEmpty); + for (final String key in kTestValues.keys) { + final Object value = kTestValues[key]!; + expect(await store.setValue(key.split('.').last, key, value), true); + } + expect(await testData.getAll(), kTestValues); + + expect(log, hasLength(5)); + expect(log[0].method, 'setString'); + expect(log[1].method, 'setBool'); + expect(log[2].method, 'setInt'); + expect(log[3].method, 'setDouble'); + expect(log[4].method, 'setStringList'); + }); + + test('clear', () async { + store = SharedPreferencesAndroid(); + testData = InMemorySharedPreferencesStore.withData(kTestValues); + expect(await testData.getAll(), isNotEmpty); + expect(await store.clear(), true); + expect(await testData.getAll(), isEmpty); + expect(log.single.method, 'clear'); + }); + }); +} diff --git a/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md b/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md index 84fe4f8879f9..b2a9f141ae68 100644 --- a/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.10 + +* Switches to an in-package method channel implementation. + ## 2.0.9 * Removes dependency on `meta`. diff --git a/packages/shared_preferences/shared_preferences_ios/ios/Classes/FLTSharedPreferencesPlugin.m b/packages/shared_preferences/shared_preferences_ios/ios/Classes/FLTSharedPreferencesPlugin.m index 09308d42d762..4d49e3b14619 100644 --- a/packages/shared_preferences/shared_preferences_ios/ios/Classes/FLTSharedPreferencesPlugin.m +++ b/packages/shared_preferences/shared_preferences_ios/ios/Classes/FLTSharedPreferencesPlugin.m @@ -4,7 +4,7 @@ #import "FLTSharedPreferencesPlugin.h" -static NSString *const CHANNEL_NAME = @"plugins.flutter.io/shared_preferences"; +static NSString *const CHANNEL_NAME = @"plugins.flutter.io/shared_preferences_ios"; @implementation FLTSharedPreferencesPlugin diff --git a/packages/shared_preferences/shared_preferences_ios/lib/shared_preferences_ios.dart b/packages/shared_preferences/shared_preferences_ios/lib/shared_preferences_ios.dart new file mode 100644 index 000000000000..15f1e2f9d94d --- /dev/null +++ b/packages/shared_preferences/shared_preferences_ios/lib/shared_preferences_ios.dart @@ -0,0 +1,53 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; + +const MethodChannel _kChannel = + MethodChannel('plugins.flutter.io/shared_preferences_ios'); + +/// The macOS implementation of [SharedPreferencesStorePlatform]. +/// +/// This class implements the `package:shared_preferences` functionality for iOS. +class SharedPreferencesIOS extends SharedPreferencesStorePlatform { + /// Registers this class as the default instance of [SharedPreferencesStorePlatform]. + static void registerWith() { + SharedPreferencesStorePlatform.instance = SharedPreferencesIOS(); + } + + @override + Future remove(String key) async { + return (await _kChannel.invokeMethod( + 'remove', + {'key': key}, + ))!; + } + + @override + Future setValue(String valueType, String key, Object value) async { + return (await _kChannel.invokeMethod( + 'set$valueType', + {'key': key, 'value': value}, + ))!; + } + + @override + Future clear() async { + return (await _kChannel.invokeMethod('clear'))!; + } + + @override + Future> getAll() async { + final Map? preferences = + await _kChannel.invokeMapMethod('getAll'); + + if (preferences == null) { + return {}; + } + return preferences; + } +} diff --git a/packages/shared_preferences/shared_preferences_ios/pubspec.yaml b/packages/shared_preferences/shared_preferences_ios/pubspec.yaml index ad3f20bf97c0..68ab03523841 100644 --- a/packages/shared_preferences/shared_preferences_ios/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_ios/pubspec.yaml @@ -2,11 +2,11 @@ name: shared_preferences_ios description: iOS implementation of the shared_preferences plugin repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.9 +version: 2.0.10 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: @@ -14,6 +14,7 @@ flutter: platforms: ios: pluginClass: FLTSharedPreferencesPlugin + dartPluginClass: SharedPreferencesIOS dependencies: flutter: @@ -23,4 +24,3 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - pedantic: ^1.10.0 diff --git a/packages/shared_preferences/shared_preferences_ios/test/shared_preferences_ios_test.dart b/packages/shared_preferences/shared_preferences_ios/test/shared_preferences_ios_test.dart new file mode 100644 index 000000000000..8eb23f28c424 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_ios/test/shared_preferences_ios_test.dart @@ -0,0 +1,117 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:shared_preferences_ios/shared_preferences_ios.dart'; +import 'package:shared_preferences_platform_interface/method_channel_shared_preferences.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group(MethodChannelSharedPreferencesStore, () { + const MethodChannel channel = MethodChannel( + 'plugins.flutter.io/shared_preferences_ios', + ); + + const Map kTestValues = { + 'flutter.String': 'hello world', + 'flutter.Bool': true, + 'flutter.Int': 42, + 'flutter.Double': 3.14159, + 'flutter.StringList': ['foo', 'bar'], + }; + // Create a dummy in-memory implementation to back the mocked method channel + // API to simplify validation of the expected calls. + late InMemorySharedPreferencesStore testData; + + final List log = []; + late SharedPreferencesStorePlatform store; + + setUp(() async { + testData = InMemorySharedPreferencesStore.empty(); + + channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + if (methodCall.method == 'getAll') { + return await testData.getAll(); + } + if (methodCall.method == 'remove') { + final String key = methodCall.arguments['key'] as String; + return await testData.remove(key); + } + if (methodCall.method == 'clear') { + return await testData.clear(); + } + final RegExp setterRegExp = RegExp(r'set(.*)'); + final Match? match = setterRegExp.matchAsPrefix(methodCall.method); + if (match?.groupCount == 1) { + final String valueType = match!.group(1)!; + final String key = methodCall.arguments['key'] as String; + final Object value = methodCall.arguments['value'] as Object; + return await testData.setValue(valueType, key, value); + } + fail('Unexpected method call: ${methodCall.method}'); + }); + log.clear(); + }); + + test('registered instance', () { + SharedPreferencesIOS.registerWith(); + expect( + SharedPreferencesStorePlatform.instance, isA()); + }); + + test('getAll', () async { + store = SharedPreferencesIOS(); + testData = InMemorySharedPreferencesStore.withData(kTestValues); + expect(await store.getAll(), kTestValues); + expect(log.single.method, 'getAll'); + }); + + test('remove', () async { + store = SharedPreferencesIOS(); + testData = InMemorySharedPreferencesStore.withData(kTestValues); + expect(await store.remove('flutter.String'), true); + expect(await store.remove('flutter.Bool'), true); + expect(await store.remove('flutter.Int'), true); + expect(await store.remove('flutter.Double'), true); + expect(await testData.getAll(), { + 'flutter.StringList': ['foo', 'bar'], + }); + + expect(log, hasLength(4)); + for (final MethodCall call in log) { + expect(call.method, 'remove'); + } + }); + + test('setValue', () async { + store = SharedPreferencesIOS(); + expect(await testData.getAll(), isEmpty); + for (final String key in kTestValues.keys) { + final Object value = kTestValues[key]!; + expect(await store.setValue(key.split('.').last, key, value), true); + } + expect(await testData.getAll(), kTestValues); + + expect(log, hasLength(5)); + expect(log[0].method, 'setString'); + expect(log[1].method, 'setBool'); + expect(log[2].method, 'setInt'); + expect(log[3].method, 'setDouble'); + expect(log[4].method, 'setStringList'); + }); + + test('clear', () async { + store = SharedPreferencesIOS(); + testData = InMemorySharedPreferencesStore.withData(kTestValues); + expect(await testData.getAll(), isNotEmpty); + expect(await store.clear(), true); + expect(await testData.getAll(), isEmpty); + expect(log.single.method, 'clear'); + }); + }); +} diff --git a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md index bfc48b705e39..7c86b3c80dc8 100644 --- a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.0 + +* Deprecated `SharedPreferencesWindows.instance` in favor of `SharedPreferencesStorePlatform.instance`. + ## 2.0.4 * Removes dependency on `meta`. diff --git a/packages/shared_preferences/shared_preferences_linux/example/integration_test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences_linux/example/integration_test/shared_preferences_test.dart index b33d6011f5c4..1d83eead9f25 100644 --- a/packages/shared_preferences/shared_preferences_linux/example/integration_test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences_linux/example/integration_test/shared_preferences_test.dart @@ -31,7 +31,7 @@ void main() { late SharedPreferencesLinux preferences; setUp(() async { - preferences = SharedPreferencesLinux.instance; + preferences = SharedPreferencesLinux(); }); tearDown(() { diff --git a/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart b/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart index b4a7ea3f2c1e..4b71c7ea3beb 100644 --- a/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart @@ -31,7 +31,7 @@ class SharedPreferencesDemo extends StatefulWidget { } class SharedPreferencesDemoState extends State { - final SharedPreferencesLinux prefs = SharedPreferencesLinux.instance; + final SharedPreferencesLinux prefs = SharedPreferencesLinux(); late Future _counter; Future _incrementCounter() async { diff --git a/packages/shared_preferences/shared_preferences_linux/lib/shared_preferences_linux.dart b/packages/shared_preferences/shared_preferences_linux/lib/shared_preferences_linux.dart index d6dcceb47af6..b6a9a5bca4ff 100644 --- a/packages/shared_preferences/shared_preferences_linux/lib/shared_preferences_linux.dart +++ b/packages/shared_preferences/shared_preferences_linux/lib/shared_preferences_linux.dart @@ -16,14 +16,14 @@ import 'package:shared_preferences_platform_interface/shared_preferences_platfor /// /// This class implements the `package:shared_preferences` functionality for Linux. class SharedPreferencesLinux extends SharedPreferencesStorePlatform { - /// The default instance of [SharedPreferencesLinux] to use. - // TODO(egarciad): Remove when the Dart plugin registrant lands on Flutter stable. - // https://github.com/flutter/flutter/issues/81421 + /// Deprecated instance of [SharedPreferencesLinux]. + /// Use [SharedPreferencesStorePlatform.instance] instead. + @Deprecated('Use `SharedPreferencesStorePlatform.instance` instead.') static SharedPreferencesLinux instance = SharedPreferencesLinux(); /// Registers the Linux implementation. static void registerWith() { - SharedPreferencesStorePlatform.instance = instance; + SharedPreferencesStorePlatform.instance = SharedPreferencesLinux(); } /// Local copy of preferences @@ -33,9 +33,12 @@ class SharedPreferencesLinux extends SharedPreferencesStorePlatform { @visibleForTesting FileSystem fs = const LocalFileSystem(); + /// The path_provider_linux instance used to find the support directory. + @visibleForTesting + PathProviderLinux pathProvider = PathProviderLinux(); + /// Gets the file where the preferences are stored. Future _getLocalDataFile() async { - final PathProviderLinux pathProvider = PathProviderLinux(); final String? directory = await pathProvider.getApplicationSupportPath(); if (directory == null) { return null; diff --git a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml index 1b84644b9233..8ab692a613e2 100644 --- a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml @@ -2,11 +2,11 @@ name: shared_preferences_linux description: Linux implementation of the shared_preferences plugin repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.4 +version: 2.1.0 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.5.0" flutter: plugin: @@ -21,9 +21,9 @@ dependencies: sdk: flutter path: ^1.8.0 path_provider_linux: ^2.0.0 + path_provider_platform_interface: ^2.0.0 shared_preferences_platform_interface: ^2.0.0 dev_dependencies: flutter_test: sdk: flutter - pedantic: ^1.10.0 diff --git a/packages/shared_preferences/shared_preferences_linux/test/shared_preferences_linux_test.dart b/packages/shared_preferences/shared_preferences_linux/test/shared_preferences_linux_test.dart index 40dbdd58afc1..57acd17f5094 100644 --- a/packages/shared_preferences/shared_preferences_linux/test/shared_preferences_linux_test.dart +++ b/packages/shared_preferences/shared_preferences_linux/test/shared_preferences_linux_test.dart @@ -5,20 +5,22 @@ import 'package:file/memory.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:path/path.dart' as path; import 'package:path_provider_linux/path_provider_linux.dart'; +import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; import 'package:shared_preferences_linux/shared_preferences_linux.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; void main() { late MemoryFileSystem fs; + late PathProviderLinux pathProvider; SharedPreferencesLinux.registerWith(); setUp(() { fs = MemoryFileSystem.test(); + pathProvider = FakePathProviderLinux(); }); Future _getFilePath() async { - final PathProviderLinux pathProvider = PathProviderLinux(); final String? directory = await pathProvider.getApplicationSupportPath(); return path.join(directory!, 'shared_preferences.json'); } @@ -36,10 +38,12 @@ void main() { SharedPreferencesLinux _getPreferences() { final SharedPreferencesLinux prefs = SharedPreferencesLinux(); prefs.fs = fs; + prefs.pathProvider = pathProvider; return prefs; } test('registered instance', () { + SharedPreferencesLinux.registerWith(); expect( SharedPreferencesStorePlatform.instance, isA()); }); @@ -81,3 +85,26 @@ void main() { expect(await _readTestFile(), '{}'); }); } + +/// Fake implementation of PathProviderLinux that returns hard-coded paths, +/// allowing tests to run on any platform. +/// +/// Note that this should only be used with an in-memory filesystem, as the +/// path it returns is a root path that does not actually exist on Linux. +class FakePathProviderLinux extends PathProviderPlatform + implements PathProviderLinux { + @override + Future getApplicationSupportPath() async => r'/appsupport'; + + @override + Future getTemporaryPath() async => null; + + @override + Future getLibraryPath() async => null; + + @override + Future getApplicationDocumentsPath() async => null; + + @override + Future getDownloadsPath() async => null; +} diff --git a/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md b/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md index 7a2e3ab74e72..1f586a2a9581 100644 --- a/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.0.3 +* Switches to an in-package method channel implementation. * Fixes newly enabled analyzer options. ## 2.0.2 diff --git a/packages/shared_preferences/shared_preferences_macos/lib/shared_preferences_macos.dart b/packages/shared_preferences/shared_preferences_macos/lib/shared_preferences_macos.dart new file mode 100644 index 000000000000..a97fe131af5c --- /dev/null +++ b/packages/shared_preferences/shared_preferences_macos/lib/shared_preferences_macos.dart @@ -0,0 +1,53 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; + +const MethodChannel _kChannel = + MethodChannel('plugins.flutter.io/shared_preferences_macos'); + +/// The macOS implementation of [SharedPreferencesStorePlatform]. +/// +/// This class implements the `package:shared_preferences` functionality for macOS. +class SharedPreferencesMacOS extends SharedPreferencesStorePlatform { + /// Registers this class as the default instance of [SharedPreferencesStorePlatform]. + static void registerWith() { + SharedPreferencesStorePlatform.instance = SharedPreferencesMacOS(); + } + + @override + Future remove(String key) async { + return (await _kChannel.invokeMethod( + 'remove', + {'key': key}, + ))!; + } + + @override + Future setValue(String valueType, String key, Object value) async { + return (await _kChannel.invokeMethod( + 'set$valueType', + {'key': key, 'value': value}, + ))!; + } + + @override + Future clear() async { + return (await _kChannel.invokeMethod('clear'))!; + } + + @override + Future> getAll() async { + final Map? preferences = + await _kChannel.invokeMapMethod('getAll'); + + if (preferences == null) { + return {}; + } + return preferences; + } +} diff --git a/packages/shared_preferences/shared_preferences_macos/macos/Classes/SharedPreferencesPlugin.swift b/packages/shared_preferences/shared_preferences_macos/macos/Classes/SharedPreferencesPlugin.swift index 2cf345cf0b04..91b42441adda 100644 --- a/packages/shared_preferences/shared_preferences_macos/macos/Classes/SharedPreferencesPlugin.swift +++ b/packages/shared_preferences/shared_preferences_macos/macos/Classes/SharedPreferencesPlugin.swift @@ -8,7 +8,7 @@ import Foundation public class SharedPreferencesPlugin: NSObject, FlutterPlugin { public static func register(with registrar: FlutterPluginRegistrar) { let channel = FlutterMethodChannel( - name: "plugins.flutter.io/shared_preferences", + name: "plugins.flutter.io/shared_preferences_macos", binaryMessenger: registrar.messenger) let instance = SharedPreferencesPlugin() registrar.addMethodCallDelegate(instance, channel: channel) diff --git a/packages/shared_preferences/shared_preferences_macos/pubspec.yaml b/packages/shared_preferences/shared_preferences_macos/pubspec.yaml index 4f06c52dc652..0873696e6d4f 100644 --- a/packages/shared_preferences/shared_preferences_macos/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_macos/pubspec.yaml @@ -2,11 +2,11 @@ name: shared_preferences_macos description: macOS implementation of the shared_preferences plugin. repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.2 +version: 2.0.3 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.5.0" flutter: plugin: @@ -14,6 +14,7 @@ flutter: platforms: macos: pluginClass: SharedPreferencesPlugin + dartPluginClass: SharedPreferencesMacOS dependencies: flutter: @@ -21,4 +22,5 @@ dependencies: shared_preferences_platform_interface: ^2.0.0 dev_dependencies: - pedantic: ^1.10.0 + flutter_test: + sdk: flutter diff --git a/packages/shared_preferences/shared_preferences_macos/test/shared_preferences_macos_test.dart b/packages/shared_preferences/shared_preferences_macos/test/shared_preferences_macos_test.dart new file mode 100644 index 000000000000..cd858f48179d --- /dev/null +++ b/packages/shared_preferences/shared_preferences_macos/test/shared_preferences_macos_test.dart @@ -0,0 +1,117 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:shared_preferences_macos/shared_preferences_macos.dart'; +import 'package:shared_preferences_platform_interface/method_channel_shared_preferences.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group(MethodChannelSharedPreferencesStore, () { + const MethodChannel channel = MethodChannel( + 'plugins.flutter.io/shared_preferences_macos', + ); + + const Map kTestValues = { + 'flutter.String': 'hello world', + 'flutter.Bool': true, + 'flutter.Int': 42, + 'flutter.Double': 3.14159, + 'flutter.StringList': ['foo', 'bar'], + }; + // Create a dummy in-memory implementation to back the mocked method channel + // API to simplify validation of the expected calls. + late InMemorySharedPreferencesStore testData; + + final List log = []; + late SharedPreferencesStorePlatform store; + + setUp(() async { + testData = InMemorySharedPreferencesStore.empty(); + + channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + if (methodCall.method == 'getAll') { + return await testData.getAll(); + } + if (methodCall.method == 'remove') { + final String key = (methodCall.arguments['key'] as String?)!; + return await testData.remove(key); + } + if (methodCall.method == 'clear') { + return await testData.clear(); + } + final RegExp setterRegExp = RegExp(r'set(.*)'); + final Match? match = setterRegExp.matchAsPrefix(methodCall.method); + if (match?.groupCount == 1) { + final String valueType = match!.group(1)!; + final String key = (methodCall.arguments['key'] as String?)!; + final Object value = (methodCall.arguments['value'] as Object?)!; + return await testData.setValue(valueType, key, value); + } + fail('Unexpected method call: ${methodCall.method}'); + }); + log.clear(); + }); + + test('registers instance', () { + SharedPreferencesMacOS.registerWith(); + expect(SharedPreferencesStorePlatform.instance, + isA()); + }); + + test('getAll', () async { + store = SharedPreferencesMacOS(); + testData = InMemorySharedPreferencesStore.withData(kTestValues); + expect(await store.getAll(), kTestValues); + expect(log.single.method, 'getAll'); + }); + + test('remove', () async { + store = SharedPreferencesMacOS(); + testData = InMemorySharedPreferencesStore.withData(kTestValues); + expect(await store.remove('flutter.String'), true); + expect(await store.remove('flutter.Bool'), true); + expect(await store.remove('flutter.Int'), true); + expect(await store.remove('flutter.Double'), true); + expect(await testData.getAll(), { + 'flutter.StringList': ['foo', 'bar'], + }); + + expect(log, hasLength(4)); + for (final MethodCall call in log) { + expect(call.method, 'remove'); + } + }); + + test('setValue', () async { + store = SharedPreferencesMacOS(); + expect(await testData.getAll(), isEmpty); + for (final String key in kTestValues.keys) { + final Object value = kTestValues[key]!; + expect(await store.setValue(key.split('.').last, key, value), true); + } + expect(await testData.getAll(), kTestValues); + + expect(log, hasLength(5)); + expect(log[0].method, 'setString'); + expect(log[1].method, 'setBool'); + expect(log[2].method, 'setInt'); + expect(log[3].method, 'setDouble'); + expect(log[4].method, 'setStringList'); + }); + + test('clear', () async { + store = SharedPreferencesMacOS(); + testData = InMemorySharedPreferencesStore.withData(kTestValues); + expect(await testData.getAll(), isNotEmpty); + expect(await store.clear(), true); + expect(await testData.getAll(), isEmpty); + expect(log.single.method, 'clear'); + }); + }); +} diff --git a/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md b/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md index 18ae9d125ed9..6c96681ce5b7 100644 --- a/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.0 + +* Deprecated `SharedPreferencesWindows.instance` in favor of `SharedPreferencesStorePlatform.instance`. + ## 2.0.4 * Removes dependency on `meta`. diff --git a/packages/shared_preferences/shared_preferences_windows/example/integration_test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences_windows/example/integration_test/shared_preferences_test.dart index ac138666518a..92a34fc2a255 100644 --- a/packages/shared_preferences/shared_preferences_windows/example/integration_test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences_windows/example/integration_test/shared_preferences_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; - import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:shared_preferences_windows/shared_preferences_windows.dart'; @@ -28,17 +26,9 @@ void main() { 'flutter.List': ['baz', 'quox'], }; - late SharedPreferencesWindows preferences; - - setUp(() async { - preferences = SharedPreferencesWindows.instance; - }); - - tearDown(() { - preferences.clear(); - }); - testWidgets('reading', (WidgetTester _) async { + final SharedPreferencesWindows preferences = SharedPreferencesWindows(); + preferences.clear(); final Map values = await preferences.getAll(); expect(values['String'], isNull); expect(values['bool'], isNull); @@ -48,16 +38,16 @@ void main() { }); testWidgets('writing', (WidgetTester _) async { - await Future.wait(>[ - preferences.setValue( - 'String', 'String', kTestValues2['flutter.String']!), - preferences.setValue('Bool', 'bool', kTestValues2['flutter.bool']!), - preferences.setValue('Int', 'int', kTestValues2['flutter.int']!), - preferences.setValue( - 'Double', 'double', kTestValues2['flutter.double']!), - preferences.setValue( - 'StringList', 'List', kTestValues2['flutter.List']!) - ]); + final SharedPreferencesWindows preferences = SharedPreferencesWindows(); + preferences.clear(); + await preferences.setValue( + 'String', 'String', kTestValues2['flutter.String']!); + await preferences.setValue('Bool', 'bool', kTestValues2['flutter.bool']!); + await preferences.setValue('Int', 'int', kTestValues2['flutter.int']!); + await preferences.setValue( + 'Double', 'double', kTestValues2['flutter.double']!); + await preferences.setValue( + 'StringList', 'List', kTestValues2['flutter.List']!); final Map values = await preferences.getAll(); expect(values['String'], kTestValues2['flutter.String']); expect(values['bool'], kTestValues2['flutter.bool']); @@ -67,6 +57,8 @@ void main() { }); testWidgets('removing', (WidgetTester _) async { + final SharedPreferencesWindows preferences = SharedPreferencesWindows(); + preferences.clear(); const String key = 'testKey'; await preferences.setValue('String', key, kTestValues['flutter.String']!); await preferences.setValue('Bool', key, kTestValues['flutter.bool']!); @@ -80,6 +72,8 @@ void main() { }); testWidgets('clearing', (WidgetTester _) async { + final SharedPreferencesWindows preferences = SharedPreferencesWindows(); + preferences.clear(); await preferences.setValue( 'String', 'String', kTestValues['flutter.String']!); await preferences.setValue('Bool', 'bool', kTestValues['flutter.bool']!); diff --git a/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart b/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart index 423a727042cc..40a9159cee70 100644 --- a/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart @@ -31,7 +31,7 @@ class SharedPreferencesDemo extends StatefulWidget { } class SharedPreferencesDemoState extends State { - final SharedPreferencesWindows prefs = SharedPreferencesWindows.instance; + final SharedPreferencesWindows prefs = SharedPreferencesWindows(); late Future _counter; Future _incrementCounter() async { diff --git a/packages/shared_preferences/shared_preferences_windows/lib/shared_preferences_windows.dart b/packages/shared_preferences/shared_preferences_windows/lib/shared_preferences_windows.dart index e3551d2dd079..a60d2ed09926 100644 --- a/packages/shared_preferences/shared_preferences_windows/lib/shared_preferences_windows.dart +++ b/packages/shared_preferences/shared_preferences_windows/lib/shared_preferences_windows.dart @@ -16,14 +16,14 @@ import 'package:shared_preferences_platform_interface/shared_preferences_platfor /// /// This class implements the `package:shared_preferences` functionality for Windows. class SharedPreferencesWindows extends SharedPreferencesStorePlatform { - /// The default instance of [SharedPreferencesWindows] to use. - // TODO(egarciad): Remove when the Dart plugin registrant lands on Flutter stable. - // https://github.com/flutter/flutter/issues/81421 + /// Deprecated instance of [SharedPreferencesWindows]. + /// Use [SharedPreferencesStorePlatform.instance] instead. + @Deprecated('Use `SharedPreferencesStorePlatform.instance` instead.') static SharedPreferencesWindows instance = SharedPreferencesWindows(); /// Registers the Windows implementation. static void registerWith() { - SharedPreferencesStorePlatform.instance = instance; + SharedPreferencesStorePlatform.instance = SharedPreferencesWindows(); } /// File system used to store to disk. Exposed for testing only. diff --git a/packages/shared_preferences/shared_preferences_windows/pubspec.yaml b/packages/shared_preferences/shared_preferences_windows/pubspec.yaml index a3938ba461b1..6dcb5997f131 100644 --- a/packages/shared_preferences/shared_preferences_windows/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_windows/pubspec.yaml @@ -2,11 +2,11 @@ name: shared_preferences_windows description: Windows implementation of shared_preferences repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.4 +version: 2.1.0 environment: sdk: '>=2.12.0 <3.0.0' - flutter: ">=2.0.0" + flutter: ">=2.5.0" flutter: plugin: @@ -27,4 +27,3 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - pedantic: ^1.10.0 From a80f9830e0b72aecd054ffd8abe713e34a6844f8 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 9 Feb 2022 15:17:43 -0500 Subject: [PATCH 159/600] [url_launcher] Major version bump for desktop Dart-based url_launcher implementations (#4777) https://github.com/flutter/plugins/pull/4719 converted the url_launcher implementations to Dart-based versions. This should have been completely transparent to clients, but there is a latent bug in `url_launcher` where the `default_package` entries for Linux, macOS, and Windows are incorrect. This went unnoticed until now because currently the only client-facing effect of `default_package` is in resolving which implementation to run Dart registration for. Because of the typos in the `default_package` entries, the resolution in the `flutter` tool doesn't recognize the registration for these packages as needing to run, causing them to be broken at runtime. The affected versions have been retracted to fix the breakage in the short term. This is PR is the first half of the long-term fix; it re-releases them as a major version bump so that existing versions of `url_launcher` will not pick them up. Once they are published, a follow-up PR will update `url_launcher` to fix the typos and relax the version constraints for the dependencies on these implementation packages to allow 2.x or 3.x. (In theory this could be one PR, but doing it as two ensures that we get real testing on the `url_launcher` change, so is safer.) Part of a long-term fix for https://github.com/flutter/flutter/issues/98097 --- packages/url_launcher/url_launcher_linux/CHANGELOG.md | 9 ++++++++- packages/url_launcher/url_launcher_linux/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_macos/CHANGELOG.md | 9 ++++++++- packages/url_launcher/url_launcher_macos/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_windows/CHANGELOG.md | 9 +++++++++ packages/url_launcher/url_launcher_windows/pubspec.yaml | 2 +- 6 files changed, 28 insertions(+), 5 deletions(-) diff --git a/packages/url_launcher/url_launcher_linux/CHANGELOG.md b/packages/url_launcher/url_launcher_linux/CHANGELOG.md index f3460947099e..0fc373f2ebb1 100644 --- a/packages/url_launcher/url_launcher_linux/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_linux/CHANGELOG.md @@ -1,6 +1,13 @@ +## 3.0.0 + +* Changes the major version since, due to a typo in `default_package` in + existing versions of `url_launcher`, requiring Dart registration in this + package is in practice a breaking change. + * Does not include any API changes; clients can allow both 2.x or 3.x. + ## 2.0.4 -* Switches to an in-package method channel implementation. +* **\[Retracted\]** Switches to an in-package method channel implementation. ## 2.0.3 diff --git a/packages/url_launcher/url_launcher_linux/pubspec.yaml b/packages/url_launcher/url_launcher_linux/pubspec.yaml index ee559209c2c5..cb9a0be0aa41 100644 --- a/packages/url_launcher/url_launcher_linux/pubspec.yaml +++ b/packages/url_launcher/url_launcher_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_linux description: Linux implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 2.0.4 +version: 3.0.0 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_macos/CHANGELOG.md b/packages/url_launcher/url_launcher_macos/CHANGELOG.md index 2bf9de938956..082bc45fc2e8 100644 --- a/packages/url_launcher/url_launcher_macos/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_macos/CHANGELOG.md @@ -1,6 +1,13 @@ +## 3.0.0 + +* Changes the major version since, due to a typo in `default_package` in + existing versions of `url_launcher`, requiring Dart registration in this + package is in practice a breaking change. + * Does not include any API changes; clients can allow both 2.x or 3.x. + ## 2.0.4 -* Switches to an in-package method channel implementation. +* **\[Retracted\]** Switches to an in-package method channel implementation. ## 2.0.3 diff --git a/packages/url_launcher/url_launcher_macos/pubspec.yaml b/packages/url_launcher/url_launcher_macos/pubspec.yaml index 0cd25d2fc3ad..8b5183b1914d 100644 --- a/packages/url_launcher/url_launcher_macos/pubspec.yaml +++ b/packages/url_launcher/url_launcher_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_macos description: macOS implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 2.0.4 +version: 3.0.0 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_windows/CHANGELOG.md b/packages/url_launcher/url_launcher_windows/CHANGELOG.md index a562da1db97d..e02f5a2288e1 100644 --- a/packages/url_launcher/url_launcher_windows/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_windows/CHANGELOG.md @@ -1,5 +1,14 @@ +## 3.0.0 + +* Changes the major version since, due to a typo in `default_package` in + existing versions of `url_launcher`, requiring Dart registration in this + package is in practice a breaking change. + * Does not include any API changes; clients can allow both 2.x or 3.x. + ## 2.0.3 +**\[Retracted\]** + * Switches to an in-package method channel implementation. * Adds unit tests. * Updates code for new analysis options. diff --git a/packages/url_launcher/url_launcher_windows/pubspec.yaml b/packages/url_launcher/url_launcher_windows/pubspec.yaml index 506e9958b72c..95f17ad9e921 100644 --- a/packages/url_launcher/url_launcher_windows/pubspec.yaml +++ b/packages/url_launcher/url_launcher_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_windows description: Windows implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 2.0.3 +version: 3.0.0 environment: sdk: ">=2.12.0 <3.0.0" From 9bf17372d428a1045e3fc2c29a146332c3f428d2 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 9 Feb 2022 17:05:26 -0500 Subject: [PATCH 160/600] Roll Flutter from f483b7d80831 to bb3275b9340f (51 revisions) (#4778) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index d38074fc0ff2..3d4a625e14c4 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -f483b7d808315411fc6ccba66cab8807cee2d371 +bb3275b9340f6820ac8ed2161635765ed5b14202 From a9316552863be1b73e9c7344bc3bbdaf6cdf019f Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Wed, 9 Feb 2022 14:50:23 -0800 Subject: [PATCH 161/600] [camera]skip app delegate during unit test (#4729) --- packages/camera/camera/CHANGELOG.md | 4 ++++ packages/camera/camera/example/ios/Runner/main.m | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index c05ea245b9c8..e92a7a815625 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Skips unnecessary AppDelegate setup for unit tests on iOS. + ## 0.9.4+11 * Manages iOS camera's orientation-related states on a background queue to prevent potential race conditions. diff --git a/packages/camera/camera/example/ios/Runner/main.m b/packages/camera/camera/example/ios/Runner/main.m index f143297b30d6..8b3fb8d5b361 100644 --- a/packages/camera/camera/example/ios/Runner/main.m +++ b/packages/camera/camera/example/ios/Runner/main.m @@ -8,6 +8,12 @@ int main(int argc, char *argv[]) { @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + // The setup logic in `AppDelegate::didFinishLaunchingWithOptions:` eventually sends camera + // operations on the background queue, which would run concurrently with the test cases during + // unit tests, making the debugging process confusing. This setup is actually not necessary for + // the unit tests, so here we want to skip the AppDelegate when running unit tests. + BOOL isTesting = NSClassFromString(@"XCTestCase") != nil; + return UIApplicationMain(argc, argv, nil, + isTesting ? nil : NSStringFromClass([AppDelegate class])); } } From 880bfbf923a09780c7e7e85fb5d3c47f7a32b316 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 9 Feb 2022 18:45:21 -0500 Subject: [PATCH 162/600] Roll Flutter from bb3275b9340f to 5f3bee55ad48 (2 revisions) (#4781) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 3d4a625e14c4..5050a5cbf5b0 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -bb3275b9340f6820ac8ed2161635765ed5b14202 +5f3bee55ad4804984256dc6bfd762fd0911db901 From 7169829e41944fb6b221b3acf2e903cb99ff43ac Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 9 Feb 2022 19:50:23 -0500 Subject: [PATCH 163/600] Roll Flutter from 5f3bee55ad48 to 0ce527eb91a3 (1 revision) (#4783) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 5050a5cbf5b0..6533860d9a3d 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -5f3bee55ad4804984256dc6bfd762fd0911db901 +0ce527eb91a37ef31b60ed6d4a9613310f2107d4 From aa36a8a606e1e04e80e5a52b372c8eeb43935453 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Thu, 10 Feb 2022 09:35:21 +0100 Subject: [PATCH 164/600] [image_picker] Default to gallery instead of camera when picking multiple images on pre-iOS 14 devices. (#4718) --- .../image_picker/image_picker/CHANGELOG.md | 5 + .../ios/RunnerTests/ImagePickerPluginTests.m | 103 +++++++---- .../ios/Classes/FLTImagePickerPlugin.h | 1 - .../ios/Classes/FLTImagePickerPlugin.m | 165 ++++++++++-------- .../ios/Classes/FLTImagePickerPlugin_Test.h | 11 ++ .../image_picker/image_picker/pubspec.yaml | 2 +- 6 files changed, 182 insertions(+), 105 deletions(-) diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index 3dbf2c51690e..992c8b299345 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.8.4+8 + +* Configures the `UIImagePicker` to default to gallery instead of camera when +picking multiple images on pre-iOS 14 devices. + ## 0.8.4+7 * Refactors unit test to expose private interface via a separate test header instead of the inline declaration. diff --git a/packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerPluginTests.m b/packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerPluginTests.m index bcaf2d1da37b..5f3287400c5e 100644 --- a/packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerPluginTests.m +++ b/packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerPluginTests.m @@ -23,30 +23,26 @@ - (UIViewController *)presentedViewController { @end @interface ImagePickerPluginTests : XCTestCase -@property(readonly, nonatomic) id mockUIImagePicker; -@property(readonly, nonatomic) id mockAVCaptureDevice; + @end @implementation ImagePickerPluginTests -- (void)setUp { - _mockUIImagePicker = OCMClassMock([UIImagePickerController class]); - _mockAVCaptureDevice = OCMClassMock([AVCaptureDevice class]); -} - - (void)testPluginPickImageDeviceBack { + id mockUIImagePicker = OCMClassMock([UIImagePickerController class]); + id mockAVCaptureDevice = OCMClassMock([AVCaptureDevice class]); // UIImagePickerControllerSourceTypeCamera is supported OCMStub(ClassMethod( - [_mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])) + [mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])) .andReturn(YES); // UIImagePickerControllerCameraDeviceRear is supported OCMStub(ClassMethod( - [_mockUIImagePicker isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear])) + [mockUIImagePicker isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear])) .andReturn(YES); // AVAuthorizationStatusAuthorized is supported - OCMStub([_mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]) + OCMStub([mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]) .andReturn(AVAuthorizationStatusAuthorized); // Run test @@ -54,27 +50,30 @@ - (void)testPluginPickImageDeviceBack { FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"pickImage" arguments:@{@"source" : @(0), @"cameraDevice" : @(0)}]; + UIImagePickerController *controller = [[UIImagePickerController alloc] init]; + [plugin setImagePickerControllerOverrides:@[ controller ]]; [plugin handleMethodCall:call result:^(id _Nullable r){ }]; - XCTAssertEqual([plugin getImagePickerController].cameraDevice, - UIImagePickerControllerCameraDeviceRear); + XCTAssertEqual(controller.cameraDevice, UIImagePickerControllerCameraDeviceRear); } - (void)testPluginPickImageDeviceFront { + id mockUIImagePicker = OCMClassMock([UIImagePickerController class]); + id mockAVCaptureDevice = OCMClassMock([AVCaptureDevice class]); // UIImagePickerControllerSourceTypeCamera is supported OCMStub(ClassMethod( - [_mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])) + [mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])) .andReturn(YES); // UIImagePickerControllerCameraDeviceFront is supported - OCMStub(ClassMethod([_mockUIImagePicker - isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront])) + OCMStub(ClassMethod( + [mockUIImagePicker isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront])) .andReturn(YES); // AVAuthorizationStatusAuthorized is supported - OCMStub([_mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]) + OCMStub([mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]) .andReturn(AVAuthorizationStatusAuthorized); // Run test @@ -82,27 +81,30 @@ - (void)testPluginPickImageDeviceFront { FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"pickImage" arguments:@{@"source" : @(0), @"cameraDevice" : @(1)}]; + UIImagePickerController *controller = [[UIImagePickerController alloc] init]; + [plugin setImagePickerControllerOverrides:@[ controller ]]; [plugin handleMethodCall:call result:^(id _Nullable r){ }]; - XCTAssertEqual([plugin getImagePickerController].cameraDevice, - UIImagePickerControllerCameraDeviceFront); + XCTAssertEqual(controller.cameraDevice, UIImagePickerControllerCameraDeviceFront); } - (void)testPluginPickVideoDeviceBack { + id mockUIImagePicker = OCMClassMock([UIImagePickerController class]); + id mockAVCaptureDevice = OCMClassMock([AVCaptureDevice class]); // UIImagePickerControllerSourceTypeCamera is supported OCMStub(ClassMethod( - [_mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])) + [mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])) .andReturn(YES); // UIImagePickerControllerCameraDeviceRear is supported OCMStub(ClassMethod( - [_mockUIImagePicker isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear])) + [mockUIImagePicker isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear])) .andReturn(YES); // AVAuthorizationStatusAuthorized is supported - OCMStub([_mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]) + OCMStub([mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]) .andReturn(AVAuthorizationStatusAuthorized); // Run test @@ -110,27 +112,31 @@ - (void)testPluginPickVideoDeviceBack { FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"pickVideo" arguments:@{@"source" : @(0), @"cameraDevice" : @(0)}]; + UIImagePickerController *controller = [[UIImagePickerController alloc] init]; + [plugin setImagePickerControllerOverrides:@[ controller ]]; [plugin handleMethodCall:call result:^(id _Nullable r){ }]; - XCTAssertEqual([plugin getImagePickerController].cameraDevice, - UIImagePickerControllerCameraDeviceRear); + XCTAssertEqual(controller.cameraDevice, UIImagePickerControllerCameraDeviceRear); } - (void)testPluginPickVideoDeviceFront { + id mockUIImagePicker = OCMClassMock([UIImagePickerController class]); + id mockAVCaptureDevice = OCMClassMock([AVCaptureDevice class]); + // UIImagePickerControllerSourceTypeCamera is supported OCMStub(ClassMethod( - [_mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])) + [mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])) .andReturn(YES); // UIImagePickerControllerCameraDeviceFront is supported - OCMStub(ClassMethod([_mockUIImagePicker - isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront])) + OCMStub(ClassMethod( + [mockUIImagePicker isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront])) .andReturn(YES); // AVAuthorizationStatusAuthorized is supported - OCMStub([_mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]) + OCMStub([mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]) .andReturn(AVAuthorizationStatusAuthorized); // Run test @@ -138,12 +144,40 @@ - (void)testPluginPickVideoDeviceFront { FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"pickVideo" arguments:@{@"source" : @(0), @"cameraDevice" : @(1)}]; + UIImagePickerController *controller = [[UIImagePickerController alloc] init]; + [plugin setImagePickerControllerOverrides:@[ controller ]]; [plugin handleMethodCall:call result:^(id _Nullable r){ }]; - XCTAssertEqual([plugin getImagePickerController].cameraDevice, - UIImagePickerControllerCameraDeviceFront); + XCTAssertEqual(controller.cameraDevice, UIImagePickerControllerCameraDeviceFront); +} + +- (void)testPickMultiImageShouldUseUIImagePickerControllerOnPreiOS14 { + if (@available(iOS 14, *)) { + return; + } + + id mockUIImagePicker = OCMClassMock([UIImagePickerController class]); + id photoLibrary = OCMClassMock([PHPhotoLibrary class]); + OCMStub(ClassMethod([photoLibrary authorizationStatus])) + .andReturn(PHAuthorizationStatusAuthorized); + + FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; + [plugin setImagePickerControllerOverrides:@[ mockUIImagePicker ]]; + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"pickMultiImage" + arguments:@{ + @"maxWidth" : @(100), + @"maxHeight" : @(200), + @"imageQuality" : @(50), + }]; + + [plugin handleMethodCall:call + result:^(id _Nullable r){ + }]; + + OCMVerify(times(1), + [mockUIImagePicker setSourceType:UIImagePickerControllerSourceTypePhotoLibrary]); } #pragma mark - Test camera devices, no op on simulators @@ -156,15 +190,18 @@ - (void)testPluginPickImageDeviceCancelClickMultipleTimes { FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"pickImage" arguments:@{@"source" : @(0), @"cameraDevice" : @(1)}]; + UIImagePickerController *controller = [[UIImagePickerController alloc] init]; + plugin.imagePickerControllerOverrides = @[ controller ]; [plugin handleMethodCall:call result:^(id _Nullable r){ }]; plugin.result = ^(id result) { }; + // To ensure the flow does not crash by multiple cancel call - [plugin imagePickerControllerDidCancel:[plugin getImagePickerController]]; - [plugin imagePickerControllerDidCancel:[plugin getImagePickerController]]; + [plugin imagePickerControllerDidCancel:controller]; + [plugin imagePickerControllerDidCancel:controller]; } #pragma mark - Test video duration @@ -174,10 +211,12 @@ - (void)testPickingVideoWithDuration { FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"pickVideo" arguments:@{@"source" : @(0), @"cameraDevice" : @(0), @"maxDuration" : @95}]; + UIImagePickerController *controller = [[UIImagePickerController alloc] init]; + [plugin setImagePickerControllerOverrides:@[ controller ]]; [plugin handleMethodCall:call result:^(id _Nullable r){ }]; - XCTAssertEqual([plugin getImagePickerController].videoMaximumDuration, 95); + XCTAssertEqual(controller.videoMaximumDuration, 95); } - (void)testViewController { diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.h b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.h index ffd23cd3df6a..c88db0bad72f 100644 --- a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.h +++ b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.h @@ -8,7 +8,6 @@ @interface FLTImagePickerPlugin : NSObject // For testing only. -- (UIImagePickerController *)getImagePickerController; - (UIViewController *)viewControllerWithWindow:(UIWindow *)window; @end diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m index 1301398fb0c1..cc841d6db447 100644 --- a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m @@ -31,12 +31,31 @@ @interface FLTImagePickerPlugin () +/** + * The maximum amount of images that are allowed to be picked. + */ @property(assign, nonatomic) int maxImagesAllowed; +/** + * The arguments that are passed in from the Flutter method call. + */ @property(copy, nonatomic) NSDictionary *arguments; +/** + * The PHPickerViewController instance used to pick multiple + * images. + */ @property(strong, nonatomic) PHPickerViewController *pickerViewController API_AVAILABLE(ios(14)); +/** + * The UIImagePickerController instances that will be used when a new + * controller would normally be created. Each call to + * createImagePickerController will remove the current first element from + * the array. + */ +@property(strong, nonatomic) + NSMutableArray *imagePickerControllerOverrides; + @end static const int SOURCE_CAMERA = 0; @@ -44,9 +63,7 @@ @interface FLTImagePickerPlugin () *)registrar { FlutterMethodChannel *channel = @@ -56,8 +73,19 @@ + (void)registerWithRegistrar:(NSObject *)registrar { [registrar addMethodCallDelegate:instance channel:channel]; } -- (UIImagePickerController *)getImagePickerController { - return _imagePickerController; +- (UIImagePickerController *)createImagePickerController { + if ([self.imagePickerControllerOverrides count] > 0) { + UIImagePickerController *controller = [self.imagePickerControllerOverrides firstObject]; + [self.imagePickerControllerOverrides removeObjectAtIndex:0]; + return controller; + } + + return [[UIImagePickerController alloc] init]; +} + +- (void)setImagePickerControllerOverrides: + (NSArray *)imagePickerControllers { + _imagePickerControllerOverrides = [imagePickerControllers mutableCopy]; } - (UIViewController *)viewControllerWithWindow:(UIWindow *)window { @@ -88,7 +116,7 @@ - (UIViewController *)viewControllerWithWindow:(UIWindow *)window { * @param arguments that should be used to get cameraDevice value. */ - (UIImagePickerControllerCameraDevice)getCameraDeviceFromArguments:(NSDictionary *)arguments { - NSInteger cameraDevice = [[arguments objectForKey:@"cameraDevice"] intValue]; + NSInteger cameraDevice = [arguments[@"cameraDevice"] intValue]; return (cameraDevice == 1) ? UIImagePickerControllerCameraDeviceFront : UIImagePickerControllerCameraDeviceRear; } @@ -108,22 +136,20 @@ - (void)pickImageWithPHPicker:(int)maxImagesAllowed API_AVAILABLE(ios(14)) { [self checkPhotoAuthorizationForAccessLevel]; } -- (void)pickImageWithUIImagePicker { - _imagePickerController = [[UIImagePickerController alloc] init]; - _imagePickerController.modalPresentationStyle = UIModalPresentationCurrentContext; - _imagePickerController.delegate = self; - _imagePickerController.mediaTypes = @[ (NSString *)kUTTypeImage ]; - - int imageSource = [[_arguments objectForKey:@"source"] intValue]; +- (void)launchUIImagePickerWithSource:(int)imageSource { + UIImagePickerController *imagePickerController = [self createImagePickerController]; + imagePickerController.modalPresentationStyle = UIModalPresentationCurrentContext; + imagePickerController.delegate = self; + imagePickerController.mediaTypes = @[ (NSString *)kUTTypeImage ]; self.maxImagesAllowed = 1; switch (imageSource) { case SOURCE_CAMERA: - [self checkCameraAuthorization]; + [self checkCameraAuthorizationWithImagePicker:imagePickerController]; break; case SOURCE_GALLERY: - [self checkPhotoAuthorization]; + [self checkPhotoAuthorizationWithImagePicker:imagePickerController]; break; default: self.result([FlutterError errorWithCode:@"invalid_source" @@ -141,10 +167,11 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result self.result = nil; } + self.result = result; + _arguments = call.arguments; + if ([@"pickImage" isEqualToString:call.method]) { - self.result = result; - _arguments = call.arguments; - int imageSource = [[_arguments objectForKey:@"source"] intValue]; + int imageSource = [call.arguments[@"source"] intValue]; if (imageSource == SOURCE_GALLERY) { // Capture is not possible with PHPicker if (@available(iOS 14, *)) { @@ -152,44 +179,39 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result [self pickImageWithPHPicker:1]; } else { // UIImagePicker is used - [self pickImageWithUIImagePicker]; + [self launchUIImagePickerWithSource:imageSource]; } } else { - [self pickImageWithUIImagePicker]; + [self launchUIImagePickerWithSource:imageSource]; } } else if ([@"pickMultiImage" isEqualToString:call.method]) { if (@available(iOS 14, *)) { - self.result = result; - _arguments = call.arguments; [self pickImageWithPHPicker:0]; } else { - [self pickImageWithUIImagePicker]; + [self launchUIImagePickerWithSource:SOURCE_GALLERY]; } } else if ([@"pickVideo" isEqualToString:call.method]) { - _imagePickerController = [[UIImagePickerController alloc] init]; - _imagePickerController.modalPresentationStyle = UIModalPresentationCurrentContext; - _imagePickerController.delegate = self; - _imagePickerController.mediaTypes = @[ + UIImagePickerController *imagePickerController = [self createImagePickerController]; + imagePickerController.modalPresentationStyle = UIModalPresentationCurrentContext; + imagePickerController.delegate = self; + imagePickerController.mediaTypes = @[ (NSString *)kUTTypeMovie, (NSString *)kUTTypeAVIMovie, (NSString *)kUTTypeVideo, (NSString *)kUTTypeMPEG4 ]; - _imagePickerController.videoQuality = UIImagePickerControllerQualityTypeHigh; - - self.result = result; - _arguments = call.arguments; + imagePickerController.videoQuality = UIImagePickerControllerQualityTypeHigh; - int imageSource = [[_arguments objectForKey:@"source"] intValue]; - if ([[_arguments objectForKey:@"maxDuration"] isKindOfClass:[NSNumber class]]) { - NSTimeInterval max = [[_arguments objectForKey:@"maxDuration"] doubleValue]; - _imagePickerController.videoMaximumDuration = max; + int imageSource = [call.arguments[@"source"] intValue]; + if ([call.arguments[@"maxDuration"] isKindOfClass:[NSNumber class]]) { + NSTimeInterval max = [call.arguments[@"maxDuration"] doubleValue]; + imagePickerController.videoMaximumDuration = max; } switch (imageSource) { case SOURCE_CAMERA: - [self checkCameraAuthorization]; + [self checkCameraAuthorizationWithImagePicker:imagePickerController]; break; case SOURCE_GALLERY: - [self checkPhotoAuthorization]; + [self checkPhotoAuthorizationWithImagePicker:imagePickerController]; break; default: result([FlutterError errorWithCode:@"invalid_source" @@ -202,9 +224,9 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result } } -- (void)showCamera { +- (void)showCameraWithImagePicker:(UIImagePickerController *)imagePickerController { @synchronized(self) { - if (_imagePickerController.beingPresented) { + if (imagePickerController.beingPresented) { return; } } @@ -212,9 +234,9 @@ - (void)showCamera { // Camera is not available on simulators if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera] && [UIImagePickerController isCameraDeviceAvailable:device]) { - _imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera; - _imagePickerController.cameraDevice = device; - [[self viewControllerWithWindow:nil] presentViewController:_imagePickerController + imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera; + imagePickerController.cameraDevice = device; + [[self viewControllerWithWindow:nil] presentViewController:imagePickerController animated:YES completion:nil]; } else { @@ -238,19 +260,19 @@ - (void)showCamera { } } -- (void)checkCameraAuthorization { +- (void)checkCameraAuthorizationWithImagePicker:(UIImagePickerController *)imagePickerController { AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; switch (status) { case AVAuthorizationStatusAuthorized: - [self showCamera]; + [self showCameraWithImagePicker:imagePickerController]; break; case AVAuthorizationStatusNotDetermined: { [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) { dispatch_async(dispatch_get_main_queue(), ^{ if (granted) { - [self showCamera]; + [self showCameraWithImagePicker:imagePickerController]; } else { [self errorNoCameraAccess:AVAuthorizationStatusDenied]; } @@ -266,14 +288,14 @@ - (void)checkCameraAuthorization { } } -- (void)checkPhotoAuthorization { +- (void)checkPhotoAuthorizationWithImagePicker:(UIImagePickerController *)imagePickerController { PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus]; switch (status) { case PHAuthorizationStatusNotDetermined: { [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) { dispatch_async(dispatch_get_main_queue(), ^{ if (status == PHAuthorizationStatusAuthorized) { - [self showPhotoLibrary:UIImagePickerClassType]; + [self showPhotoLibraryWithImagePicker:imagePickerController]; } else { [self errorNoPhotoAccess:status]; } @@ -282,7 +304,7 @@ - (void)checkPhotoAuthorization { break; } case PHAuthorizationStatusAuthorized: - [self showPhotoLibrary:UIImagePickerClassType]; + [self showPhotoLibraryWithImagePicker:imagePickerController]; break; case PHAuthorizationStatusDenied: case PHAuthorizationStatusRestricted: @@ -301,9 +323,13 @@ - (void)checkPhotoAuthorizationForAccessLevel API_AVAILABLE(ios(14)) { handler:^(PHAuthorizationStatus status) { dispatch_async(dispatch_get_main_queue(), ^{ if (status == PHAuthorizationStatusAuthorized) { - [self showPhotoLibrary:PHPickerClassType]; + [self + showPhotoLibraryWithPHPicker:self-> + _pickerViewController]; } else if (status == PHAuthorizationStatusLimited) { - [self showPhotoLibrary:PHPickerClassType]; + [self + showPhotoLibraryWithPHPicker:self-> + _pickerViewController]; } else { [self errorNoPhotoAccess:status]; } @@ -313,7 +339,7 @@ - (void)checkPhotoAuthorizationForAccessLevel API_AVAILABLE(ios(14)) { } case PHAuthorizationStatusAuthorized: case PHAuthorizationStatusLimited: - [self showPhotoLibrary:PHPickerClassType]; + [self showPhotoLibraryWithPHPicker:_pickerViewController]; break; case PHAuthorizationStatusDenied: case PHAuthorizationStatusRestricted: @@ -355,21 +381,18 @@ - (void)errorNoPhotoAccess:(PHAuthorizationStatus)status { } } -- (void)showPhotoLibrary:(ImagePickerClassType)imagePickerClassType { - // No need to check if SourceType is available. It always is. - switch (imagePickerClassType) { - case PHPickerClassType: - [[self viewControllerWithWindow:nil] presentViewController:_pickerViewController - animated:YES - completion:nil]; - break; - case UIImagePickerClassType: - _imagePickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; - [[self viewControllerWithWindow:nil] presentViewController:_imagePickerController - animated:YES - completion:nil]; - break; - } +- (void)showPhotoLibraryWithPHPicker:(PHPickerViewController *)pickerViewController + API_AVAILABLE(ios(14)) { + [[self viewControllerWithWindow:nil] presentViewController:pickerViewController + animated:YES + completion:nil]; +} + +- (void)showPhotoLibraryWithImagePicker:(UIImagePickerController *)imagePickerController { + imagePickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; + [[self viewControllerWithWindow:nil] presentViewController:imagePickerController + animated:YES + completion:nil]; } - (NSNumber *)getDesiredImageQuality:(NSNumber *)imageQuality { @@ -449,8 +472,8 @@ - (NSMutableArray *)createNSMutableArrayWithSize:(NSUInteger)size { - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { - NSURL *videoURL = [info objectForKey:UIImagePickerControllerMediaURL]; - [_imagePickerController dismissViewControllerAnimated:YES completion:nil]; + NSURL *videoURL = info[UIImagePickerControllerMediaURL]; + [picker dismissViewControllerAnimated:YES completion:nil]; // The method dismissViewControllerAnimated does not immediately prevent // further didFinishPickingMediaWithInfo invocations. A nil check is necessary // to prevent below code to be unwantly executed multiple times and cause a @@ -484,9 +507,9 @@ - (void)imagePickerController:(UIImagePickerController *)picker self.result = nil; _arguments = nil; } else { - UIImage *image = [info objectForKey:UIImagePickerControllerEditedImage]; + UIImage *image = info[UIImagePickerControllerEditedImage]; if (image == nil) { - image = [info objectForKey:UIImagePickerControllerOriginalImage]; + image = info[UIImagePickerControllerOriginalImage]; } NSNumber *maxWidth = GetNullableValueForKey(_arguments, @"maxWidth"); NSNumber *maxHeight = GetNullableValueForKey(_arguments, @"maxHeight"); @@ -523,7 +546,7 @@ - (void)imagePickerController:(UIImagePickerController *)picker } - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker { - [_imagePickerController dismissViewControllerAnimated:YES completion:nil]; + [picker dismissViewControllerAnimated:YES completion:nil]; if (!self.result) { return; } diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin_Test.h b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin_Test.h index 94eca425eb5e..5442f7d089c6 100644 --- a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin_Test.h +++ b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin_Test.h @@ -40,4 +40,15 @@ */ - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker; +/** + * Sets UIImagePickerController instances that will be used when a new + * controller would normally be created. Each call to + * createImagePickerController will remove the current first element from + * the array. + * + * Should be used for testing purposes only. + */ +- (void)setImagePickerControllerOverrides: + (NSArray *)imagePickerControllers; + @end diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 7142d8238b30..43143f1dbd57 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.4+7 +version: 0.8.4+8 environment: sdk: ">=2.14.0 <3.0.0" From 839af29ce1a121eea37fe951f49a39801b62909b Mon Sep 17 00:00:00 2001 From: Jukka-Pekka Siitonen Date: Thu, 10 Feb 2022 18:00:25 +0200 Subject: [PATCH 165/600] [url_launcher] Documentation improvement for file scheme to readme (#4711) --- .../url_launcher/url_launcher/CHANGELOG.md | 6 +++ packages/url_launcher/url_launcher/README.md | 42 +++++++++++++++---- .../url_launcher/url_launcher/pubspec.yaml | 2 +- 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index 80065cd4476c..5eb5b4af414b 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,3 +1,9 @@ +## 6.0.19 + +* Updates README: + * Adds description for `file` scheme usage. + * Updates `Uri` class link to SDK documentation. + ## 6.0.18 * Removes dependency on `meta`. diff --git a/packages/url_launcher/url_launcher/README.md b/packages/url_launcher/url_launcher/README.md index 393bbd904158..a4ffb6241f6c 100644 --- a/packages/url_launcher/url_launcher/README.md +++ b/packages/url_launcher/url_launcher/README.md @@ -96,16 +96,18 @@ takes a string argument containing a URL. This URL can be formatted using a number of different URL schemes. The supported URL schemes depend on the underlying platform and installed apps. -Common schemes supported by both iOS and Android: +Commonly used schemes include: -| Scheme | Action | -|---|---| -| `http:` , `https:`, e.g. `http://flutter.dev` | Open URL in the default browser | -| `mailto:?subject=&body=`, e.g. `mailto:smith@example.org?subject=News&body=New%20plugin` | Create email to in the default email app | -| `tel:`, e.g. `tel:+1 555 010 999` | Make a phone call to using the default phone app | -| `sms:`, e.g. `sms:5550101234` | Send an SMS message to using the default messaging app | +| Scheme | Example | Action | +|:---|:---|:---| +| `https:` | `https://flutter.dev` | Open URL in the default browser | +| `mailto:?subject=&body=` | `mailto:smith@example.org?subject=News&body=New%20plugin` | Create email to in the default email app | +| `tel:` | `tel:+1-555-010-999` | Make a phone call to using the default phone app | +| `sms:` | `sms:5550101234` | Send an SMS message to using the default messaging app | +| `file:` | `file:/home` | Open file or folder using default app association, supported on desktop platforms | -More details can be found here for [iOS](https://developer.apple.com/library/content/featuredarticles/iPhoneURLScheme_Reference/Introduction/Introduction.html) and [Android](https://developer.android.com/guide/components/intents-common.html) +More details can be found here for [iOS](https://developer.apple.com/library/content/featuredarticles/iPhoneURLScheme_Reference/Introduction/Introduction.html) +and [Android](https://developer.android.com/guide/components/intents-common.html) **Note**: URL schemes are only supported if there are apps installed on the device that can support them. For example, iOS simulators don't have a default email or phone @@ -115,7 +117,7 @@ apps installed, so can't open `tel:` or `mailto:` links. URLs must be properly encoded, especially when including spaces or other special characters. This can be done using the -[`Uri` class](https://api.dart.dev/stable/2.7.1/dart-core/Uri-class.html). +[`Uri` class](https://api.dart.dev/dart-core/Uri-class.html). For example: ```dart String? encodeQueryParameters(Map params) { @@ -164,3 +166,25 @@ If you do this for a URL of a page containing JavaScript, make sure to pass in `enableJavaScript: true`, or else the launch method will not work properly. On iOS, the default behavior is to open all web URLs within the app. Everything else is redirected to the app handler. + +## File scheme handling +`file:` scheme can be used on desktop platforms: `macOS`, `Linux` and `Windows`. + +We recommend checking first whether the directory or file exists before calling `launch`. + +Example: +```dart +var filePath = '/path/to/file'; +final Uri uri = Uri.file(filePath); + +if (await File(uri.toFilePath()).exists()) { + if (!await launch(uri.toString())) { + throw 'Could not launch $uri'; + } +} +``` + +### macOS file access configuration + +If you need to access files outside of your application's sandbox, you will need to have the necessary +[entitlements](https://docs.flutter.dev/desktop#entitlements-and-the-app-sandbox). diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index b0aac361f709..5d6548a6399a 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.0.18 +version: 6.0.19 environment: sdk: ">=2.14.0 <3.0.0" From e2761fd9cc1ce6b1e67dd856c158de1a6bb595bc Mon Sep 17 00:00:00 2001 From: Jukka-Pekka Siitonen Date: Thu, 10 Feb 2022 19:00:23 +0200 Subject: [PATCH 166/600] [path_provider] Documentation update for readme.md and example app (#4686) --- .../path_provider/path_provider/CHANGELOG.md | 5 + .../path_provider/path_provider/README.md | 28 ++- .../integration_test/path_provider_test.dart | 10 + .../path_provider/example/lib/main.dart | 238 ++++++++++++------ .../macos/Runner/DebugProfile.entitlements | 2 + .../example/macos/Runner/Release.entitlements | 2 + .../path_provider/path_provider/pubspec.yaml | 2 +- 7 files changed, 202 insertions(+), 85 deletions(-) diff --git a/packages/path_provider/path_provider/CHANGELOG.md b/packages/path_provider/path_provider/CHANGELOG.md index bc349f7b86fa..d71ebf70c796 100644 --- a/packages/path_provider/path_provider/CHANGELOG.md +++ b/packages/path_provider/path_provider/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.9 + +* Updates documentation on README.md. +* Updates example application. + ## 2.0.8 * Updates example app Android compileSdkVersion to 31. diff --git a/packages/path_provider/path_provider/README.md b/packages/path_provider/path_provider/README.md index 47ae6891d294..20d888ff8d73 100644 --- a/packages/path_provider/path_provider/README.md +++ b/packages/path_provider/path_provider/README.md @@ -2,16 +2,16 @@ [![pub package](https://img.shields.io/pub/v/path_provider.svg)](https://pub.dev/packages/path_provider) -A Flutter plugin for finding commonly used locations on the filesystem. Supports Android, iOS, Linux, macOS and Windows. +A Flutter plugin for finding commonly used locations on the filesystem. +Supports Android, iOS, Linux, macOS and Windows. Not all methods are supported on all platforms. ## Usage To use this plugin, add `path_provider` as a [dependency in your pubspec.yaml file](https://flutter.dev/docs/development/platform-integration/platform-channels). -### Example - -``` dart +## Example +```dart Directory tempDir = await getTemporaryDirectory(); String tempPath = tempDir.path; @@ -19,11 +19,25 @@ Directory appDocDir = await getApplicationDocumentsDirectory(); String appDocPath = appDocDir.path; ``` -Please see the example app of this plugin for a full example. +## Supported platforms and paths + +Directories support by platform: -### Usage in tests +| Directory | Android | iOS | Linux | macOS | Windows | +| :--- | :---: | :---: | :---: | :---: | :---: | +| Temporary | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | +| Application Support | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | +| Application Library | ❌️ | ✔️ | ❌️ | ✔️ | ❌️ | +| Application Documents | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | +| External Storage | ✔️ | ❌ | ❌ | ❌️ | ❌️ | +| External Cache Directories | ✔️ | ❌ | ❌ | ❌️ | ❌️ | +| External Storage Directories | ✔️ | ❌ | ❌ | ❌️ | ❌️ | +| Downloads | ❌ | ❌ | ✔️ | ✔️ | ✔️ | -`path_provider` now uses a `PlatformInterface`, meaning that not all platforms share the a single `PlatformChannel`-based implementation. +## Testing + +`path_provider` now uses a `PlatformInterface`, meaning that not all platforms share a single `PlatformChannel`-based implementation. With that change, tests should be updated to mock `PathProviderPlatform` rather than `PlatformChannel`. See this `path_provider` [test](https://github.com/flutter/plugins/blob/master/packages/path_provider/path_provider/test/path_provider_test.dart) for an example. + diff --git a/packages/path_provider/path_provider/example/integration_test/path_provider_test.dart b/packages/path_provider/path_provider/example/integration_test/path_provider_test.dart index 9f8feee99ee2..27493a76012c 100644 --- a/packages/path_provider/path_provider/example/integration_test/path_provider_test.dart +++ b/packages/path_provider/path_provider/example/integration_test/path_provider_test.dart @@ -85,6 +85,16 @@ void main() { } }); } + + testWidgets('getDownloadsDirectory', (WidgetTester tester) async { + if (Platform.isIOS || Platform.isAndroid) { + final Future result = getDownloadsDirectory(); + expect(result, throwsA(isInstanceOf())); + } else { + final Directory? result = await getDownloadsDirectory(); + _verifySampleFile(result, 'downloads'); + } + }); } /// Verify a file called [name] in [directory] by recreating it with test diff --git a/packages/path_provider/path_provider/example/lib/main.dart b/packages/path_provider/path_provider/example/lib/main.dart index c0ac126b2a00..90c2ccb93154 100644 --- a/packages/path_provider/path_provider/example/lib/main.dart +++ b/packages/path_provider/path_provider/example/lib/main.dart @@ -42,6 +42,7 @@ class _MyHomePageState extends State { Future? _externalDocumentsDirectory; Future?>? _externalStorageDirectories; Future?>? _externalCacheDirectories; + Future? _downloadsDirectory; void _requestTempDirectory() { setState(() { @@ -117,6 +118,12 @@ class _MyHomePageState extends State { }); } + void _requestDownloadsDirectory() { + setState(() { + _downloadsDirectory = getDownloadsDirectory(); + }); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -126,88 +133,165 @@ class _MyHomePageState extends State { body: Center( child: ListView( children: [ - Padding( - padding: const EdgeInsets.all(16.0), - child: ElevatedButton( - child: const Text('Get Temporary Directory'), - onPressed: _requestTempDirectory, - ), + Column( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: ElevatedButton( + child: const Text( + 'Get Temporary Directory', + ), + onPressed: _requestTempDirectory, + ), + ), + FutureBuilder( + future: _tempDirectory, + builder: _buildDirectory, + ), + ], ), - FutureBuilder( - future: _tempDirectory, builder: _buildDirectory), - Padding( - padding: const EdgeInsets.all(16.0), - child: ElevatedButton( - child: const Text('Get Application Documents Directory'), - onPressed: _requestAppDocumentsDirectory, - ), + Column( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: ElevatedButton( + child: const Text( + 'Get Application Documents Directory', + ), + onPressed: _requestAppDocumentsDirectory, + ), + ), + FutureBuilder( + future: _appDocumentsDirectory, + builder: _buildDirectory, + ), + ], ), - FutureBuilder( - future: _appDocumentsDirectory, builder: _buildDirectory), - Padding( - padding: const EdgeInsets.all(16.0), - child: ElevatedButton( - child: const Text('Get Application Support Directory'), - onPressed: _requestAppSupportDirectory, - ), + Column( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: ElevatedButton( + child: const Text( + 'Get Application Support Directory', + ), + onPressed: _requestAppSupportDirectory, + ), + ), + FutureBuilder( + future: _appSupportDirectory, + builder: _buildDirectory, + ), + ], ), - FutureBuilder( - future: _appSupportDirectory, builder: _buildDirectory), - Padding( - padding: const EdgeInsets.all(16.0), - child: ElevatedButton( - child: const Text('Get Application Library Directory'), - onPressed: _requestAppLibraryDirectory, - ), + Column( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: ElevatedButton( + child: Text( + Platform.isAndroid + ? 'Application Library Directory unavailable' + : 'Get Application Library Directory', + ), + onPressed: + Platform.isAndroid ? null : _requestAppLibraryDirectory, + ), + ), + FutureBuilder( + future: _appLibraryDirectory, + builder: _buildDirectory, + ), + ], ), - FutureBuilder( - future: _appLibraryDirectory, builder: _buildDirectory), - Padding( - padding: const EdgeInsets.all(16.0), - child: ElevatedButton( - child: Text(Platform.isIOS - ? 'External directories are unavailable on iOS' - : 'Get External Storage Directory'), - onPressed: - Platform.isIOS ? null : _requestExternalStorageDirectory, - ), + Column( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: ElevatedButton( + child: Text( + !Platform.isAndroid + ? 'External storage is unavailable' + : 'Get External Storage Directory', + ), + onPressed: !Platform.isAndroid + ? null + : _requestExternalStorageDirectory, + ), + ), + FutureBuilder( + future: _externalDocumentsDirectory, + builder: _buildDirectory, + ), + ], + ), + Column( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: ElevatedButton( + child: Text( + !Platform.isAndroid + ? 'External directories are unavailable' + : 'Get External Storage Directories', + ), + onPressed: !Platform.isAndroid + ? null + : () { + _requestExternalStorageDirectories( + StorageDirectory.music, + ); + }, + ), + ), + FutureBuilder?>( + future: _externalStorageDirectories, + builder: _buildDirectories, + ), + ], + ), + Column( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: ElevatedButton( + child: Text( + !Platform.isAndroid + ? 'External directories are unavailable' + : 'Get External Cache Directories', + ), + onPressed: !Platform.isAndroid + ? null + : _requestExternalCacheDirectories, + ), + ), + FutureBuilder?>( + future: _externalCacheDirectories, + builder: _buildDirectories, + ), + ], + ), + Column( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: ElevatedButton( + child: Text( + Platform.isAndroid || Platform.isIOS + ? 'Downloads directory is unavailable' + : 'Get Downloads Directory', + ), + onPressed: Platform.isAndroid || Platform.isIOS + ? null + : _requestDownloadsDirectory, + ), + ), + FutureBuilder( + future: _downloadsDirectory, + builder: _buildDirectory, + ), + ], ), - FutureBuilder( - future: _externalDocumentsDirectory, builder: _buildDirectory), - Column(children: [ - Padding( - padding: const EdgeInsets.all(16.0), - child: ElevatedButton( - child: Text(Platform.isIOS - ? 'External directories are unavailable on iOS' - : 'Get External Storage Directories'), - onPressed: Platform.isIOS - ? null - : () { - _requestExternalStorageDirectories( - StorageDirectory.music, - ); - }, - ), - ), - ]), - FutureBuilder?>( - future: _externalStorageDirectories, - builder: _buildDirectories), - Column(children: [ - Padding( - padding: const EdgeInsets.all(16.0), - child: ElevatedButton( - child: Text(Platform.isIOS - ? 'External directories are unavailable on iOS' - : 'Get External Cache Directories'), - onPressed: - Platform.isIOS ? null : _requestExternalCacheDirectories, - ), - ), - ]), - FutureBuilder?>( - future: _externalCacheDirectories, builder: _buildDirectories), ], ), ), diff --git a/packages/path_provider/path_provider/example/macos/Runner/DebugProfile.entitlements b/packages/path_provider/path_provider/example/macos/Runner/DebugProfile.entitlements index dddb8a30c851..f83e1f42d120 100644 --- a/packages/path_provider/path_provider/example/macos/Runner/DebugProfile.entitlements +++ b/packages/path_provider/path_provider/example/macos/Runner/DebugProfile.entitlements @@ -8,5 +8,7 @@ com.apple.security.network.server + com.apple.security.files.downloads.read-write + diff --git a/packages/path_provider/path_provider/example/macos/Runner/Release.entitlements b/packages/path_provider/path_provider/example/macos/Runner/Release.entitlements index 852fa1a4728a..9d379927fbcb 100644 --- a/packages/path_provider/path_provider/example/macos/Runner/Release.entitlements +++ b/packages/path_provider/path_provider/example/macos/Runner/Release.entitlements @@ -4,5 +4,7 @@ com.apple.security.app-sandbox + com.apple.security.files.downloads.read-write + diff --git a/packages/path_provider/path_provider/pubspec.yaml b/packages/path_provider/path_provider/pubspec.yaml index 0c2b391ca0de..ea08369f1ac7 100644 --- a/packages/path_provider/path_provider/pubspec.yaml +++ b/packages/path_provider/path_provider/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider description: Flutter plugin for getting commonly used locations on host platform file systems, such as the temp and app data directories. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.8 +version: 2.0.9 environment: sdk: ">=2.14.0 <3.0.0" From 7e73aae20006c1ea680a1be846015f90019e8996 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 10 Feb 2022 15:20:21 -0500 Subject: [PATCH 167/600] [ios_platform_images] Switch to new analysis options (#4784) --- packages/ios_platform_images/CHANGELOG.md | 4 ++ .../ios_platform_images/analysis_options.yaml | 1 - .../ios_platform_images/example/lib/main.dart | 5 +- .../ios_platform_images/example/pubspec.yaml | 4 +- .../example/test/widget_test.dart | 3 +- .../lib/ios_platform_images.dart | 46 +++++++++++-------- packages/ios_platform_images/pubspec.yaml | 2 +- .../test/ios_platform_images_test.dart | 2 +- script/configs/custom_analysis.yaml | 1 - 9 files changed, 37 insertions(+), 31 deletions(-) delete mode 100644 packages/ios_platform_images/analysis_options.yaml diff --git a/packages/ios_platform_images/CHANGELOG.md b/packages/ios_platform_images/CHANGELOG.md index 443be3f7bfde..416f93661c8d 100644 --- a/packages/ios_platform_images/CHANGELOG.md +++ b/packages/ios_platform_images/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.0+4 + +* Internal code cleanup for stricter analysis options. + ## 0.2.0+3 * Internal fix for unused field formal parameter. diff --git a/packages/ios_platform_images/analysis_options.yaml b/packages/ios_platform_images/analysis_options.yaml deleted file mode 100644 index cda4f6e153e6..000000000000 --- a/packages/ios_platform_images/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../analysis_options_legacy.yaml diff --git a/packages/ios_platform_images/example/lib/main.dart b/packages/ios_platform_images/example/lib/main.dart index 1546edca8c90..ecdfeb2cba01 100644 --- a/packages/ios_platform_images/example/lib/main.dart +++ b/packages/ios_platform_images/example/lib/main.dart @@ -18,7 +18,8 @@ class _MyAppState extends State { void initState() { super.initState(); - IosPlatformImages.resolveURL("textfile").then((value) => print(value)); + IosPlatformImages.resolveURL('textfile') + .then((String? value) => print(value)); } @override @@ -31,7 +32,7 @@ class _MyAppState extends State { body: Center( // "flutter" is a resource in Assets.xcassets. child: Image( - image: IosPlatformImages.load("flutter"), + image: IosPlatformImages.load('flutter'), semanticLabel: 'Flutter logo', ), ), diff --git a/packages/ios_platform_images/example/pubspec.yaml b/packages/ios_platform_images/example/pubspec.yaml index 97241b677295..aa8fea54b287 100644 --- a/packages/ios_platform_images/example/pubspec.yaml +++ b/packages/ios_platform_images/example/pubspec.yaml @@ -7,11 +7,10 @@ environment: flutter: ">=2.5.0" dependencies: + cupertino_icons: ^1.0.2 flutter: sdk: flutter - cupertino_icons: ^1.0.2 - dev_dependencies: flutter_test: sdk: flutter @@ -22,7 +21,6 @@ dev_dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - pedantic: ^1.10.0 flutter: uses-material-design: true diff --git a/packages/ios_platform_images/example/test/widget_test.dart b/packages/ios_platform_images/example/test/widget_test.dart index 09fa35c27a6b..18e9e657ddb9 100644 --- a/packages/ios_platform_images/example/test/widget_test.dart +++ b/packages/ios_platform_images/example/test/widget_test.dart @@ -14,11 +14,10 @@ void main() { // Build our app and trigger a frame. await tester.pumpWidget(MyApp()); - // Verify that platform version is retrieved. expect( find.byWidgetPredicate( (Widget widget) => - widget is Image && (Platform.isIOS ? widget.image != null : true), + widget is Image && (!Platform.isIOS || widget.image != null), ), findsOneWidget, ); diff --git a/packages/ios_platform_images/lib/ios_platform_images.dart b/packages/ios_platform_images/lib/ios_platform_images.dart index 856562c7598f..23a437d775ef 100644 --- a/packages/ios_platform_images/lib/ios_platform_images.dart +++ b/packages/ios_platform_images/lib/ios_platform_images.dart @@ -6,18 +6,18 @@ import 'dart:async'; import 'dart:typed_data'; import 'dart:ui' as ui; +import 'package:flutter/foundation.dart' + show SynchronousFuture, describeIdentity, immutable, objectRuntimeType; +import 'package:flutter/painting.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; -import 'package:flutter/painting.dart'; -import 'package:flutter/foundation.dart' - show SynchronousFuture, describeIdentity; class _FutureImageStreamCompleter extends ImageStreamCompleter { _FutureImageStreamCompleter({ required Future codec, required this.futureScale, }) { - codec.then(_onCodecReady, onError: (dynamic error, StackTrace stack) { + codec.then(_onCodecReady, onError: (Object error, StackTrace stack) { reportError( context: ErrorDescription('resolving a single-frame image stream'), exception: error, @@ -31,8 +31,8 @@ class _FutureImageStreamCompleter extends ImageStreamCompleter { Future _onCodecReady(ui.Codec codec) async { try { - ui.FrameInfo nextFrame = await codec.getNextFrame(); - double scale = await futureScale; + final ui.FrameInfo nextFrame = await codec.getNextFrame(); + final double scale = await futureScale; setImage(ImageInfo(image: nextFrame.image, scale: scale)); } catch (exception, stack) { reportError( @@ -47,6 +47,7 @@ class _FutureImageStreamCompleter extends ImageStreamCompleter { /// Performs exactly like a [MemoryImage] but instead of taking in bytes it takes /// in a future that represents bytes. +@immutable class _FutureMemoryImage extends ImageProvider<_FutureMemoryImage> { /// Constructor for FutureMemoryImage. [_futureBytes] is the bytes that will /// be loaded into an image and [_futureScale] is the scale that will be applied to @@ -83,11 +84,13 @@ class _FutureMemoryImage extends ImageProvider<_FutureMemoryImage> { /// See [ImageProvider.operator==]. @override - bool operator ==(dynamic other) { - if (other.runtimeType != runtimeType) return false; - final _FutureMemoryImage typedOther = other; - return _futureBytes == typedOther._futureBytes && - _futureScale == typedOther._futureScale; + bool operator ==(Object other) { + if (other.runtimeType != runtimeType) { + return false; + } + return other is _FutureMemoryImage && + _futureBytes == other._futureBytes && + _futureScale == other._futureScale; } /// See [ImageProvider.hashCode]. @@ -96,10 +99,11 @@ class _FutureMemoryImage extends ImageProvider<_FutureMemoryImage> { /// See [ImageProvider.toString]. @override - String toString() => - '$runtimeType(${describeIdentity(_futureBytes)}, scale: $_futureScale)'; + String toString() => '${objectRuntimeType(this, '_FutureMemoryImage')}' + '(${describeIdentity(_futureBytes)}, scale: $_futureScale)'; } +// ignore: avoid_classes_with_only_static_members /// Class to help loading of iOS platform images into Flutter. /// /// For example, loading an image that is in `Assets.xcassts`. @@ -114,10 +118,11 @@ class IosPlatformImages { /// /// See [https://developer.apple.com/documentation/uikit/uiimage/1624146-imagenamed?language=objc] static ImageProvider load(String name) { - Future loadInfo = _channel.invokeMapMethod('loadImage', name); - Completer bytesCompleter = Completer(); - Completer scaleCompleter = Completer(); - loadInfo.then((map) { + final Future?> loadInfo = + _channel.invokeMapMethod('loadImage', name); + final Completer bytesCompleter = Completer(); + final Completer scaleCompleter = Completer(); + loadInfo.then((Map? map) { if (map == null) { scaleCompleter.completeError( Exception("Image couldn't be found: $name"), @@ -127,8 +132,8 @@ class IosPlatformImages { ); return; } - scaleCompleter.complete(map["scale"]); - bytesCompleter.complete(map["data"]); + scaleCompleter.complete(map['scale']! as double); + bytesCompleter.complete(map['data']! as Uint8List); }); return _FutureMemoryImage(bytesCompleter.future, scaleCompleter.future); } @@ -140,6 +145,7 @@ class IosPlatformImages { /// /// See [https://developer.apple.com/documentation/foundation/nsbundle/1411540-urlforresource?language=objc] static Future resolveURL(String name, {String? extension}) { - return _channel.invokeMethod('resolveURL', [name, extension]); + return _channel + .invokeMethod('resolveURL', [name, extension]); } } diff --git a/packages/ios_platform_images/pubspec.yaml b/packages/ios_platform_images/pubspec.yaml index d5aa9eefda2c..fe7952abbc3b 100644 --- a/packages/ios_platform_images/pubspec.yaml +++ b/packages/ios_platform_images/pubspec.yaml @@ -2,7 +2,7 @@ name: ios_platform_images description: A plugin to share images between Flutter and iOS in add-to-app setups. repository: https://github.com/flutter/plugins/tree/main/packages/ios_platform_images issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+ios_platform_images%22 -version: 0.2.0+3 +version: 0.2.0+4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/ios_platform_images/test/ios_platform_images_test.dart b/packages/ios_platform_images/test/ios_platform_images_test.dart index a896e3d835af..76b012002dfa 100644 --- a/packages/ios_platform_images/test/ios_platform_images_test.dart +++ b/packages/ios_platform_images/test/ios_platform_images_test.dart @@ -23,6 +23,6 @@ void main() { }); test('resolveURL', () async { - expect(await IosPlatformImages.resolveURL("foobar"), '42'); + expect(await IosPlatformImages.resolveURL('foobar'), '42'); }); } diff --git a/script/configs/custom_analysis.yaml b/script/configs/custom_analysis.yaml index 721e2ef10e53..7ef79694442d 100644 --- a/script/configs/custom_analysis.yaml +++ b/script/configs/custom_analysis.yaml @@ -25,7 +25,6 @@ - in_app_purchase/in_app_purchase - in_app_purchase/in_app_purchase_android - in_app_purchase/in_app_purchase_storekit -- ios_platform_images - video_player/video_player - video_player/video_player_platform_interface - video_player/video_player_web From 09ab5d74c3a47ce1a6ea9802e0d1a4094fe7bad3 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Fri, 11 Feb 2022 10:05:23 +1300 Subject: [PATCH 168/600] [path_provider] Remove use of deprecated pedantic package (#4764) --- packages/path_provider/path_provider/example/pubspec.yaml | 1 - packages/path_provider/path_provider/pubspec.yaml | 1 - .../path_provider/path_provider_android/example/pubspec.yaml | 1 - packages/path_provider/path_provider_android/pubspec.yaml | 1 - packages/path_provider/path_provider_ios/example/pubspec.yaml | 1 - packages/path_provider/path_provider_ios/pubspec.yaml | 1 - packages/path_provider/path_provider_linux/pubspec.yaml | 1 - packages/path_provider/path_provider_macos/pubspec.yaml | 1 - .../path_provider/path_provider_platform_interface/pubspec.yaml | 1 - .../path_provider/path_provider_windows/example/pubspec.yaml | 1 - packages/path_provider/path_provider_windows/pubspec.yaml | 1 - 11 files changed, 11 deletions(-) diff --git a/packages/path_provider/path_provider/example/pubspec.yaml b/packages/path_provider/path_provider/example/pubspec.yaml index 0001fe580e78..a279bbade6bf 100644 --- a/packages/path_provider/path_provider/example/pubspec.yaml +++ b/packages/path_provider/path_provider/example/pubspec.yaml @@ -22,7 +22,6 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.10.0 flutter: uses-material-design: true diff --git a/packages/path_provider/path_provider/pubspec.yaml b/packages/path_provider/path_provider/pubspec.yaml index ea08369f1ac7..30ddfdcfb119 100644 --- a/packages/path_provider/path_provider/pubspec.yaml +++ b/packages/path_provider/path_provider/pubspec.yaml @@ -39,6 +39,5 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.10.0 plugin_platform_interface: ^2.0.0 test: ^1.16.0 diff --git a/packages/path_provider/path_provider_android/example/pubspec.yaml b/packages/path_provider/path_provider_android/example/pubspec.yaml index 08f2c6d4b5b4..75617d8f9747 100644 --- a/packages/path_provider/path_provider_android/example/pubspec.yaml +++ b/packages/path_provider/path_provider_android/example/pubspec.yaml @@ -23,7 +23,6 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.10.0 flutter: uses-material-design: true diff --git a/packages/path_provider/path_provider_android/pubspec.yaml b/packages/path_provider/path_provider_android/pubspec.yaml index 24632ed18eb6..e259fdc958cc 100644 --- a/packages/path_provider/path_provider_android/pubspec.yaml +++ b/packages/path_provider/path_provider_android/pubspec.yaml @@ -29,6 +29,5 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.10.0 plugin_platform_interface: ^2.0.0 test: ^1.16.0 diff --git a/packages/path_provider/path_provider_ios/example/pubspec.yaml b/packages/path_provider/path_provider_ios/example/pubspec.yaml index ebe846e3c502..2166076db2b9 100644 --- a/packages/path_provider/path_provider_ios/example/pubspec.yaml +++ b/packages/path_provider/path_provider_ios/example/pubspec.yaml @@ -23,7 +23,6 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.10.0 flutter: uses-material-design: true diff --git a/packages/path_provider/path_provider_ios/pubspec.yaml b/packages/path_provider/path_provider_ios/pubspec.yaml index dc64972dce21..0e05121f3052 100644 --- a/packages/path_provider/path_provider_ios/pubspec.yaml +++ b/packages/path_provider/path_provider_ios/pubspec.yaml @@ -27,6 +27,5 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.10.0 plugin_platform_interface: ^2.0.0 test: ^1.16.0 diff --git a/packages/path_provider/path_provider_linux/pubspec.yaml b/packages/path_provider/path_provider_linux/pubspec.yaml index b652e8d01845..91304fc0b268 100644 --- a/packages/path_provider/path_provider_linux/pubspec.yaml +++ b/packages/path_provider/path_provider_linux/pubspec.yaml @@ -26,4 +26,3 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - pedantic: ^1.10.0 diff --git a/packages/path_provider/path_provider_macos/pubspec.yaml b/packages/path_provider/path_provider_macos/pubspec.yaml index e0948fed5ea8..2451b6dedf80 100644 --- a/packages/path_provider/path_provider_macos/pubspec.yaml +++ b/packages/path_provider/path_provider_macos/pubspec.yaml @@ -25,4 +25,3 @@ dev_dependencies: flutter_test: sdk: flutter path: ^1.8.0 - pedantic: ^1.10.0 diff --git a/packages/path_provider/path_provider_platform_interface/pubspec.yaml b/packages/path_provider/path_provider_platform_interface/pubspec.yaml index 68f3f8a4d8b2..d1b0b3821e21 100644 --- a/packages/path_provider/path_provider_platform_interface/pubspec.yaml +++ b/packages/path_provider/path_provider_platform_interface/pubspec.yaml @@ -19,4 +19,3 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - pedantic: ^1.10.0 diff --git a/packages/path_provider/path_provider_windows/example/pubspec.yaml b/packages/path_provider/path_provider_windows/example/pubspec.yaml index 26a796fca90c..d943347df1ff 100644 --- a/packages/path_provider/path_provider_windows/example/pubspec.yaml +++ b/packages/path_provider/path_provider_windows/example/pubspec.yaml @@ -22,7 +22,6 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.10.0 flutter: uses-material-design: true diff --git a/packages/path_provider/path_provider_windows/pubspec.yaml b/packages/path_provider/path_provider_windows/pubspec.yaml index ce0b17f57cde..873fa0a6861b 100644 --- a/packages/path_provider/path_provider_windows/pubspec.yaml +++ b/packages/path_provider/path_provider_windows/pubspec.yaml @@ -26,4 +26,3 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - pedantic: ^1.10.0 From 8dc94de384687f5b5224c89fa7b8a89ad94bd43c Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 10 Feb 2022 16:55:21 -0500 Subject: [PATCH 169/600] [video_player] Switch platform interface to new analysis options (#4785) --- .../CHANGELOG.md | 3 ++- .../analysis_options.yaml | 1 - .../lib/messages.dart | 2 +- .../lib/method_channel_video_player.dart | 24 ++++++++--------- .../lib/video_player_platform_interface.dart | 26 +++++++++++++++---- .../pubspec.yaml | 2 +- .../method_channel_video_player_test.dart | 21 ++++++++------- script/configs/custom_analysis.yaml | 1 - 8 files changed, 48 insertions(+), 32 deletions(-) delete mode 100644 packages/video_player/video_player_platform_interface/analysis_options.yaml diff --git a/packages/video_player/video_player_platform_interface/CHANGELOG.md b/packages/video_player/video_player_platform_interface/CHANGELOG.md index 8d295aa06a0a..353f2734642c 100644 --- a/packages/video_player/video_player_platform_interface/CHANGELOG.md +++ b/packages/video_player/video_player_platform_interface/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 5.0.2 * Adds the Pigeon definitions used to create the method channel implementation. +* Internal code cleanup for stricter analysis options. ## 5.0.1 diff --git a/packages/video_player/video_player_platform_interface/analysis_options.yaml b/packages/video_player/video_player_platform_interface/analysis_options.yaml deleted file mode 100644 index 5aeb4e7c5e21..000000000000 --- a/packages/video_player/video_player_platform_interface/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../../analysis_options_legacy.yaml diff --git a/packages/video_player/video_player_platform_interface/lib/messages.dart b/packages/video_player/video_player_platform_interface/lib/messages.dart index 0ddbfaeaf247..831f4e3755d9 100644 --- a/packages/video_player/video_player_platform_interface/lib/messages.dart +++ b/packages/video_player/video_player_platform_interface/lib/messages.dart @@ -4,7 +4,7 @@ // Autogenerated from Pigeon (v0.1.21), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, cast_nullable_to_non_nullable // @dart = 2.12 import 'dart:async'; import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; diff --git a/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart b/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart index 31d1839595e3..2aa7fb30e5f2 100644 --- a/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart +++ b/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart @@ -17,7 +17,7 @@ import 'video_player_platform_interface.dart'; /// third-party implementations. It is not used by other implementations in /// this repository. class MethodChannelVideoPlayer extends VideoPlayerPlatform { - VideoPlayerApi _api = VideoPlayerApi(); + final VideoPlayerApi _api = VideoPlayerApi(); @override Future init() { @@ -31,7 +31,7 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { @override Future create(DataSource dataSource) async { - CreateMessage message = CreateMessage(); + final CreateMessage message = CreateMessage(); switch (dataSource.sourceType) { case DataSourceType.asset: @@ -51,7 +51,7 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { break; } - TextureMessage response = await _api.create(message); + final TextureMessage response = await _api.create(message); return response.textureId; } @@ -97,7 +97,7 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { @override Future getPosition(int textureId) async { - PositionMessage response = + final PositionMessage response = await _api.position(TextureMessage()..textureId = textureId); return Duration(milliseconds: response.position!); } @@ -107,21 +107,21 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { return _eventChannelFor(textureId) .receiveBroadcastStream() .map((dynamic event) { - final Map map = event; + final Map map = event as Map; switch (map['event']) { case 'initialized': return VideoEvent( eventType: VideoEventType.initialized, - duration: Duration(milliseconds: map['duration']), - size: Size(map['width']?.toDouble() ?? 0.0, - map['height']?.toDouble() ?? 0.0), + duration: Duration(milliseconds: map['duration']! as int), + size: Size((map['width'] as num?)?.toDouble() ?? 0.0, + (map['height'] as num?)?.toDouble() ?? 0.0), ); case 'completed': return VideoEvent( eventType: VideoEventType.completed, ); case 'bufferingUpdate': - final List values = map['values']; + final List values = map['values']! as List; return VideoEvent( buffered: values.map(_toDurationRange).toList(), @@ -162,10 +162,10 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { }; DurationRange _toDurationRange(dynamic value) { - final List pair = value; + final List pair = value as List; return DurationRange( - Duration(milliseconds: pair[0]), - Duration(milliseconds: pair[1]), + Duration(milliseconds: pair[0]! as int), + Duration(milliseconds: pair[1]! as int), ); } } diff --git a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart index 451537ad1f12..f10fe667f0ea 100644 --- a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart +++ b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart @@ -127,7 +127,7 @@ class DataSource { this.formatHint, this.asset, this.package, - this.httpHeaders = const {}, + this.httpHeaders = const {}, }); /// The way in which the video was originally loaded. @@ -193,6 +193,7 @@ enum VideoFormat { } /// Event emitted from the platform implementation. +@immutable class VideoEvent { /// Creates an instance of [VideoEvent]. /// @@ -200,6 +201,10 @@ class VideoEvent { /// /// Depending on the [eventType], the [duration], [size] and [buffered] /// arguments can be null. + // TODO(stuartmorgan): Temporarily suppress warnings about not using const + // in all of the other video player packages, fix this, and then update + // the other packages to use const. + // ignore: prefer_const_constructors_in_immutables VideoEvent({ required this.eventType, this.duration, @@ -270,9 +275,14 @@ enum VideoEventType { /// Describes a discrete segment of time within a video using a [start] and /// [end] [Duration]. +@immutable class DurationRange { /// Trusts that the given [start] and [end] are actually in order. They should /// both be non-null. + // TODO(stuartmorgan): Temporarily suppress warnings about not using const + // in all of the other video player packages, fix this, and then update + // the other packages to use const. + // ignore: prefer_const_constructors_in_immutables DurationRange(this.start, this.end); /// The beginning of the segment described relative to the beginning of the @@ -313,7 +323,8 @@ class DurationRange { } @override - String toString() => '$runtimeType(start: $start, end: $end)'; + String toString() => + '${objectRuntimeType(this, 'DurationRange')}(start: $start, end: $end)'; @override bool operator ==(Object other) => @@ -328,14 +339,19 @@ class DurationRange { } /// [VideoPlayerOptions] can be optionally used to set additional player settings +@immutable class VideoPlayerOptions { + /// set additional optional player settings + // TODO(stuartmorgan): Temporarily suppress warnings about not using const + // in all of the other video player packages, fix this, and then update + // the other packages to use const. + // ignore: prefer_const_constructors_in_immutables + VideoPlayerOptions({this.mixWithOthers = false}); + /// Set this to true to mix the video players audio with other audio sources. /// The default value is false /// /// Note: This option will be silently ignored in the web platform (there is /// currently no way to implement this feature in this platform). final bool mixWithOthers; - - /// set additional optional player settings - VideoPlayerOptions({this.mixWithOthers = false}); } diff --git a/packages/video_player/video_player_platform_interface/pubspec.yaml b/packages/video_player/video_player_platform_interface/pubspec.yaml index b7d5745b3525..7d5bd83278c0 100644 --- a/packages/video_player/video_player_platform_interface/pubspec.yaml +++ b/packages/video_player/video_player_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/video_player/v issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 5.0.1 +version: 5.0.2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart b/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart index 4d1c9b78fc34..75baba45f763 100644 --- a/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart +++ b/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart @@ -13,7 +13,7 @@ import 'package:video_player_platform_interface/video_player_platform_interface. import 'test.dart'; class _ApiLogger implements TestHostVideoPlayerApi { - final List log = []; + final List log = []; TextureMessage? textureMessage; CreateMessage? createMessage; PositionMessage? positionMessage; @@ -148,7 +148,7 @@ void main() { expect(log.createMessage?.uri, 'someUri'); expect(log.createMessage?.packageName, null); expect(log.createMessage?.formatHint, 'dash'); - expect(log.createMessage?.httpHeaders, {}); + expect(log.createMessage?.httpHeaders, {}); expect(textureId, 3); }); @@ -156,14 +156,15 @@ void main() { final int? textureId = await player.create(DataSource( sourceType: DataSourceType.network, uri: 'someUri', - httpHeaders: {'Authorization': 'Bearer token'}, + httpHeaders: {'Authorization': 'Bearer token'}, )); expect(log.log.last, 'create'); expect(log.createMessage?.asset, null); expect(log.createMessage?.uri, 'someUri'); expect(log.createMessage?.packageName, null); expect(log.createMessage?.formatHint, null); - expect(log.createMessage?.httpHeaders, {'Authorization': 'Bearer token'}); + expect(log.createMessage?.httpHeaders, + {'Authorization': 'Bearer token'}); expect(textureId, 3); }); @@ -238,7 +239,7 @@ void main() { _ambiguate(ServicesBinding.instance) ?.defaultBinaryMessenger .setMockMessageHandler( - "flutter.io/videoPlayer/videoEvents123", + 'flutter.io/videoPlayer/videoEvents123', (ByteData? message) async { final MethodCall methodCall = const StandardMethodCodec().decodeMethodCall(message); @@ -246,7 +247,7 @@ void main() { await _ambiguate(ServicesBinding.instance) ?.defaultBinaryMessenger .handlePlatformMessage( - "flutter.io/videoPlayer/videoEvents123", + 'flutter.io/videoPlayer/videoEvents123', const StandardMethodCodec() .encodeSuccessEnvelope({ 'event': 'initialized', @@ -259,7 +260,7 @@ void main() { await _ambiguate(ServicesBinding.instance) ?.defaultBinaryMessenger .handlePlatformMessage( - "flutter.io/videoPlayer/videoEvents123", + 'flutter.io/videoPlayer/videoEvents123', const StandardMethodCodec() .encodeSuccessEnvelope({ 'event': 'completed', @@ -269,7 +270,7 @@ void main() { await _ambiguate(ServicesBinding.instance) ?.defaultBinaryMessenger .handlePlatformMessage( - "flutter.io/videoPlayer/videoEvents123", + 'flutter.io/videoPlayer/videoEvents123', const StandardMethodCodec() .encodeSuccessEnvelope({ 'event': 'bufferingUpdate', @@ -283,7 +284,7 @@ void main() { await _ambiguate(ServicesBinding.instance) ?.defaultBinaryMessenger .handlePlatformMessage( - "flutter.io/videoPlayer/videoEvents123", + 'flutter.io/videoPlayer/videoEvents123', const StandardMethodCodec() .encodeSuccessEnvelope({ 'event': 'bufferingStart', @@ -293,7 +294,7 @@ void main() { await _ambiguate(ServicesBinding.instance) ?.defaultBinaryMessenger .handlePlatformMessage( - "flutter.io/videoPlayer/videoEvents123", + 'flutter.io/videoPlayer/videoEvents123', const StandardMethodCodec() .encodeSuccessEnvelope({ 'event': 'bufferingEnd', diff --git a/script/configs/custom_analysis.yaml b/script/configs/custom_analysis.yaml index 7ef79694442d..0dbeae42289b 100644 --- a/script/configs/custom_analysis.yaml +++ b/script/configs/custom_analysis.yaml @@ -26,5 +26,4 @@ - in_app_purchase/in_app_purchase_android - in_app_purchase/in_app_purchase_storekit - video_player/video_player -- video_player/video_player_platform_interface - video_player/video_player_web From 04d5fa969f74e45713aa7659f8276257b58ae8ea Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 10 Feb 2022 17:35:22 -0500 Subject: [PATCH 170/600] [url_launcher] Support new desktop implementation versions (#4779) --- .../url_launcher/url_launcher/CHANGELOG.md | 4 + .../url_launcher/url_launcher/pubspec.yaml | 16 ++-- script/tool/CHANGELOG.md | 1 + .../tool/lib/src/pubspec_check_command.dart | 58 +++++++++++- .../tool/test/pubspec_check_command_test.dart | 93 ++++++++++++++++++- 5 files changed, 161 insertions(+), 11 deletions(-) diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index 5eb5b4af414b..1634dcdab0f9 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.0.20 + +* Fixes a typo in `default_package` registration for Windows, macOS, and Linux. + ## 6.0.19 * Updates README: diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index 5d6548a6399a..feb0a2c9ad95 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.0.19 +version: 6.0.20 environment: sdk: ">=2.14.0 <3.0.0" @@ -17,24 +17,26 @@ flutter: ios: default_package: url_launcher_ios linux: - default_package: url_laucher_linux + default_package: url_launcher_linux macos: - default_package: url_laucher_macos + default_package: url_launcher_macos web: default_package: url_launcher_web windows: - default_package: url_laucher_windows + default_package: url_launcher_windows dependencies: flutter: sdk: flutter url_launcher_android: ^6.0.13 url_launcher_ios: ^6.0.13 - url_launcher_linux: ^2.0.0 - url_launcher_macos: ^2.0.0 + # Allow either the pure-native or Dart/native hybrid versions of the desktop + # implementations, as both are compatible. + url_launcher_linux: ">=2.0.0 <4.0.0" + url_launcher_macos: ">=2.0.0 <4.0.0" url_launcher_platform_interface: ^2.0.3 url_launcher_web: ^2.0.0 - url_launcher_windows: ^2.0.0 + url_launcher_windows: ">=2.0.0 <4.0.0" dev_dependencies: flutter_test: diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 101bfac2b9c8..fbf7610b3ea3 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -17,6 +17,7 @@ for flake issues. - Adds support for `CHROME_EXECUTABLE` in `drive-examples` to match similar `flutter` behavior. +- Validates `default_package` entries in plugins. ## 0.7.3 diff --git a/script/tool/lib/src/pubspec_check_command.dart b/script/tool/lib/src/pubspec_check_command.dart index 4f2b208524d9..2c27c91e0490 100644 --- a/script/tool/lib/src/pubspec_check_command.dart +++ b/script/tool/lib/src/pubspec_check_command.dart @@ -6,6 +6,7 @@ import 'package:file/file.dart'; import 'package:git/git.dart'; import 'package:platform/platform.dart'; import 'package:pubspec_parse/pubspec_parse.dart'; +import 'package:yaml/yaml.dart'; import 'common/core.dart'; import 'common/package_looping_command.dart'; @@ -100,9 +101,17 @@ class PubspecCheckCommand extends PackageLoopingCommand { } if (isPlugin) { - final String? error = _checkForImplementsError(pubspec, package: package); - if (error != null) { - printError('$indentation$error'); + final String? implementsError = + _checkForImplementsError(pubspec, package: package); + if (implementsError != null) { + printError('$indentation$implementsError'); + passing = false; + } + + final String? defaultPackageError = + _checkForDefaultPackageError(pubspec, package: package); + if (defaultPackageError != null) { + printError('$indentation$defaultPackageError'); passing = false; } } @@ -243,6 +252,49 @@ class PubspecCheckCommand extends PackageLoopingCommand { return null; } + // Validates any "default_package" entries a plugin, returning an error + // string if there are any issues. + // + // Should only be called on plugin packages. + String? _checkForDefaultPackageError( + Pubspec pubspec, { + required RepositoryPackage package, + }) { + final dynamic platformsEntry = pubspec.flutter!['plugin']!['platforms']; + if (platformsEntry == null) { + logWarning('Does not implement any platforms'); + return null; + } + final YamlMap platforms = platformsEntry as YamlMap; + final String packageName = package.directory.basename; + + // Validate that the default_package entries look correct (e.g., no typos). + final Set defaultPackages = {}; + for (final MapEntry platformEntry in platforms.entries) { + final String? defaultPackage = + platformEntry.value['default_package'] as String?; + if (defaultPackage != null) { + defaultPackages.add(defaultPackage); + if (!defaultPackage.startsWith('${packageName}_')) { + return '"$defaultPackage" is not an expected implementation name ' + 'for "$packageName"'; + } + } + } + + // Validate that all default_packages are also dependencies. + final Iterable dependencies = pubspec.dependencies.keys; + final Iterable missingPackages = defaultPackages + .where((String package) => !dependencies.contains(package)); + if (missingPackages.isNotEmpty) { + return 'The following default_packages are missing ' + 'corresponding dependencies:\n ' + + missingPackages.join('\n '); + } + + return null; + } + // Returns true if [packageName] appears to be an implementation package // according to repository conventions. bool _isImplementationPackage(RepositoryPackage package) { diff --git a/script/tool/test/pubspec_check_command_test.dart b/script/tool/test/pubspec_check_command_test.dart index 9ad1eaa620c4..42d20240da87 100644 --- a/script/tool/test/pubspec_check_command_test.dart +++ b/script/tool/test/pubspec_check_command_test.dart @@ -70,12 +70,27 @@ environment: String _flutterSection({ bool isPlugin = false, String? implementedPackage, + Map> pluginPlatformDetails = + const >{}, }) { - final String pluginEntry = ''' + String pluginEntry = ''' plugin: ${implementedPackage == null ? '' : ' implements: $implementedPackage'} platforms: '''; + + for (final MapEntry> platform + in pluginPlatformDetails.entries) { + pluginEntry += ''' + ${platform.key}: +'''; + for (final MapEntry detail in platform.value.entries) { + pluginEntry += ''' + ${detail.key}: ${detail.value} +'''; + } + } + return ''' flutter: ${isPlugin ? pluginEntry : ''} @@ -647,6 +662,82 @@ ${_devDependenciesSection()} ); }); + test('fails when a "default_package" looks incorrect', () async { + final Directory pluginDirectory = + createFakePlugin('plugin_a', packagesDir.childDirectory('plugin_a')); + + pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' +${_headerSection( + 'plugin_a', + isPlugin: true, + repositoryPackagesDirRelativePath: 'plugin_a/plugin_a', + )} +${_environmentSection()} +${_flutterSection( + isPlugin: true, + pluginPlatformDetails: >{ + 'android': {'default_package': 'plugin_b_android'} + }, + )} +${_dependenciesSection()} +${_devDependenciesSection()} +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['pubspec-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains( + '"plugin_b_android" is not an expected implementation name for "plugin_a"'), + ]), + ); + }); + + test( + 'fails when a "default_package" does not have a corresponding dependency', + () async { + final Directory pluginDirectory = + createFakePlugin('plugin_a', packagesDir.childDirectory('plugin_a')); + + pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' +${_headerSection( + 'plugin_a', + isPlugin: true, + repositoryPackagesDirRelativePath: 'plugin_a/plugin_a', + )} +${_environmentSection()} +${_flutterSection( + isPlugin: true, + pluginPlatformDetails: >{ + 'android': {'default_package': 'plugin_a_android'} + }, + )} +${_dependenciesSection()} +${_devDependenciesSection()} +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['pubspec-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The following default_packages are missing corresponding ' + 'dependencies:\n plugin_a_android'), + ]), + ); + }); + test('passes for an app-facing package without "implements"', () async { final Directory pluginDirectory = createFakePlugin('plugin_a', packagesDir.childDirectory('plugin_a')); From 5bb47889f8ffd11dca7cd3393d05fd1a009f8278 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 10 Feb 2022 18:20:19 -0500 Subject: [PATCH 171/600] [video_player] Update web analysis options (#4788) --- .../video_player_web/CHANGELOG.md | 4 +++ .../video_player_web/analysis_options.yaml | 1 - .../video_player_web_test.dart | 8 ++--- .../video_player_web/example/lib/main.dart | 2 +- .../video_player_web/example/pubspec.yaml | 8 ++--- .../lib/src/shims/dart_ui.dart | 4 +-- .../lib/src/shims/dart_ui_fake.dart | 11 +++++-- .../lib/video_player_web.dart | 30 ++++++++++--------- .../video_player_web/pubspec.yaml | 3 +- script/configs/custom_analysis.yaml | 1 - 10 files changed, 40 insertions(+), 32 deletions(-) delete mode 100644 packages/video_player/video_player_web/analysis_options.yaml diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md index 246cd229b364..1cd428c4deea 100644 --- a/packages/video_player/video_player_web/CHANGELOG.md +++ b/packages/video_player/video_player_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.7 + +* Internal code cleanup for stricter analysis options. + ## 2.0.6 * Removes dependency on `meta`. diff --git a/packages/video_player/video_player_web/analysis_options.yaml b/packages/video_player/video_player_web/analysis_options.yaml deleted file mode 100644 index 5aeb4e7c5e21..000000000000 --- a/packages/video_player/video_player_web/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../../analysis_options_legacy.yaml diff --git a/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart b/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart index 2a830c9c573d..97b03642cd07 100644 --- a/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart +++ b/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart @@ -27,7 +27,7 @@ void main() { 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4', ), ) - .then((textureId) => textureId!); + .then((int? textureId) => textureId!); }); testWidgets('can init', (WidgetTester tester) async { @@ -98,14 +98,14 @@ void main() { testWidgets('throws PlatformException when playing bad media', (WidgetTester tester) async { - int videoPlayerId = (await VideoPlayerPlatform.instance.create( + final int videoPlayerId = (await VideoPlayerPlatform.instance.create( DataSource( sourceType: DataSourceType.network, uri: 'https://flutter.github.io/assets-for-api-docs/assets/videos/_non_existent_video.mp4'), ))!; - Stream eventStream = + final Stream eventStream = VideoPlayerPlatform.instance.videoEventsFor(videoPlayerId); // Mute video to allow autoplay (See https://goo.gl/xX8pDD) @@ -139,7 +139,7 @@ void main() { expect( VideoPlayerPlatform.instance.seekTo( await textureId, - Duration(seconds: 1), + const Duration(seconds: 1), ), completes, ); diff --git a/packages/video_player/video_player_web/example/lib/main.dart b/packages/video_player/video_player_web/example/lib/main.dart index e1a38dcdcd46..341913a18490 100644 --- a/packages/video_player/video_player_web/example/lib/main.dart +++ b/packages/video_player/video_player_web/example/lib/main.dart @@ -17,7 +17,7 @@ class MyApp extends StatefulWidget { class _MyAppState extends State { @override Widget build(BuildContext context) { - return Directionality( + return const Directionality( textDirection: TextDirection.ltr, child: Text('Testing... Look at the console output for results!'), ); diff --git a/packages/video_player/video_player_web/example/pubspec.yaml b/packages/video_player/video_player_web/example/pubspec.yaml index 4ec938bebce2..bc70b7d21aca 100644 --- a/packages/video_player/video_player_web/example/pubspec.yaml +++ b/packages/video_player/video_player_web/example/pubspec.yaml @@ -6,15 +6,15 @@ environment: flutter: ">=2.2.0" dependencies: - video_player_web: - path: ../ flutter: sdk: flutter + video_player_web: + path: ../ dev_dependencies: - flutter_test: - sdk: flutter flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/video_player/video_player_web/lib/src/shims/dart_ui.dart b/packages/video_player/video_player_web/lib/src/shims/dart_ui.dart index 5eacec5fe867..bd28793f190d 100644 --- a/packages/video_player/video_player_web/lib/src/shims/dart_ui.dart +++ b/packages/video_player/video_player_web/lib/src/shims/dart_ui.dart @@ -5,6 +5,6 @@ /// This file shims dart:ui in web-only scenarios, getting rid of the need to /// suppress analyzer warnings. -// TODO(flutter/flutter#55000) Remove this file once web-only dart:ui APIs -// are exposed from a dedicated place. +// TODO(ditman): Remove this file once web-only dart:ui APIs are exposed from +// a dedicated place, https://github.com/flutter/flutter/issues/55000 export 'dart_ui_fake.dart' if (dart.library.html) 'dart_ui_real.dart'; diff --git a/packages/video_player/video_player_web/lib/src/shims/dart_ui_fake.dart b/packages/video_player/video_player_web/lib/src/shims/dart_ui_fake.dart index f2862af8b704..8757ca22be17 100644 --- a/packages/video_player/video_player_web/lib/src/shims/dart_ui_fake.dart +++ b/packages/video_player/video_player_web/lib/src/shims/dart_ui_fake.dart @@ -7,13 +7,18 @@ import 'dart:html' as html; // Fake interface for the logic that this package needs from (web-only) dart:ui. // This is conditionally exported so the analyzer sees these methods as available. +// ignore_for_file: avoid_classes_with_only_static_members +// ignore_for_file: camel_case_types + /// Shim for web_ui engine.PlatformViewRegistry /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L62 class platformViewRegistry { /// Shim for registerViewFactory /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L72 - static registerViewFactory( - String viewTypeId, html.Element Function(int viewId) viewFactory) {} + static bool registerViewFactory( + String viewTypeId, html.Element Function(int viewId) viewFactory) { + return false; + } } /// Shim for web_ui engine.AssetManager. @@ -21,7 +26,7 @@ class platformViewRegistry { class webOnlyAssetManager { /// Shim for getAssetUrl. /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/src/engine/assets.dart#L45 - static getAssetUrl(String asset) {} + static String getAssetUrl(String asset) => ''; } /// Signature of callbacks that have no arguments and return no data. diff --git a/packages/video_player/video_player_web/lib/video_player_web.dart b/packages/video_player/video_player_web/lib/video_player_web.dart index 612d22d2eb3f..a676850f3488 100644 --- a/packages/video_player/video_player_web/lib/video_player_web.dart +++ b/packages/video_player/video_player_web/lib/video_player_web.dart @@ -4,16 +4,17 @@ import 'dart:async'; import 'dart:html'; -import 'src/shims/dart_ui.dart' as ui; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; +import 'src/shims/dart_ui.dart' as ui; + // An error code value to error name Map. // See: https://developer.mozilla.org/en-US/docs/Web/API/MediaError/code -const Map _kErrorValueToErrorName = { +const Map _kErrorValueToErrorName = { 1: 'MEDIA_ERR_ABORTED', 2: 'MEDIA_ERR_NETWORK', 3: 'MEDIA_ERR_DECODE', @@ -22,7 +23,7 @@ const Map _kErrorValueToErrorName = { // An error code value to description Map. // See: https://developer.mozilla.org/en-US/docs/Web/API/MediaError/code -const Map _kErrorValueToErrorDescription = { +const Map _kErrorValueToErrorDescription = { 1: 'The user canceled the fetching of the video.', 2: 'A network error occurred while fetching the video, despite having previously been available.', 3: 'An error occurred while trying to decode the video, despite having previously been determined to be usable.', @@ -43,7 +44,7 @@ class VideoPlayerPlugin extends VideoPlayerPlatform { VideoPlayerPlatform.instance = VideoPlayerPlugin(); } - Map _videoPlayers = {}; + final Map _videoPlayers = {}; int _textureCounter = 1; @@ -56,12 +57,13 @@ class VideoPlayerPlugin extends VideoPlayerPlatform { Future dispose(int textureId) async { _videoPlayers[textureId]!.dispose(); _videoPlayers.remove(textureId); - return null; + return; } void _disposeAllPlayers() { - _videoPlayers.values - .forEach((_VideoPlayer videoPlayer) => videoPlayer.dispose()); + for (final _VideoPlayer videoPlayer in _videoPlayers.values) { + videoPlayer.dispose(); + } _videoPlayers.clear(); } @@ -86,10 +88,10 @@ class VideoPlayerPlugin extends VideoPlayerPlatform { uri = assetUrl; break; case DataSourceType.file: - return Future.error(UnimplementedError( + return Future.error(UnimplementedError( 'web implementation of video_player cannot play local files')); case DataSourceType.contentUri: - return Future.error(UnimplementedError( + return Future.error(UnimplementedError( 'web implementation of video_player cannot play content uri')); } @@ -225,7 +227,7 @@ class _VideoPlayer { // The Event itself (_) doesn't contain info about the actual error. // We need to look at the HTMLMediaElement.error. // See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/error - MediaError error = videoElement.error!; + final MediaError error = videoElement.error!; eventController.addError(PlatformException( code: _kErrorValueToErrorName[error.code]!, message: error.message != '' ? error.message : _kDefaultErrorMessage, @@ -247,18 +249,18 @@ class _VideoPlayer { } Future play() { - return videoElement.play().catchError((e) { + return videoElement.play().catchError((Object e) { // play() attempts to begin playback of the media. It returns // a Promise which can get rejected in case of failure to begin // playback for any reason, such as permission issues. // The rejection handler is called with a DomException. // See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play - DomException exception = e; + final DomException exception = e as DomException; eventController.addError(PlatformException( code: exception.name, message: exception.message, )); - }, test: (e) => e is DomException); + }, test: (Object e) => e is DomException); } void pause() { @@ -270,7 +272,7 @@ class _VideoPlayer { } void setVolume(double value) { - // TODO: Do we need to expose a "muted" API? https://github.com/flutter/flutter/issues/60721 + // TODO(ditman): Do we need to expose a "muted" API? https://github.com/flutter/flutter/issues/60721 if (value > 0.0) { videoElement.muted = false; } else { diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml index 353253bbcc46..69a2df4e99e4 100644 --- a/packages/video_player/video_player_web/pubspec.yaml +++ b/packages/video_player/video_player_web/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_web description: Web platform implementation of video_player. repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.0.6 +version: 2.0.7 environment: sdk: ">=2.12.0 <3.0.0" @@ -21,7 +21,6 @@ dependencies: sdk: flutter flutter_web_plugins: sdk: flutter - pedantic: ^1.10.0 video_player_platform_interface: ">=4.2.0 <6.0.0" dev_dependencies: diff --git a/script/configs/custom_analysis.yaml b/script/configs/custom_analysis.yaml index 0dbeae42289b..493b6e69a54c 100644 --- a/script/configs/custom_analysis.yaml +++ b/script/configs/custom_analysis.yaml @@ -26,4 +26,3 @@ - in_app_purchase/in_app_purchase_android - in_app_purchase/in_app_purchase_storekit - video_player/video_player -- video_player/video_player_web From f6495091d47070ec7e1aa456b236a04b674fc378 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 11 Feb 2022 01:10:25 -0500 Subject: [PATCH 172/600] Roll Flutter from 0ce527eb91a3 to 6656356bbf23 (8 revisions) (#4793) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 6533860d9a3d..076b6401fd9e 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -0ce527eb91a37ef31b60ed6d4a9613310f2107d4 +6656356bbf23dad8935e1ba7679ccc3dabb7d487 From f45bf32f9f5308ccff27f08627486cfb618e06c3 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 11 Feb 2022 10:40:26 -0500 Subject: [PATCH 173/600] Roll Flutter from 6656356bbf23 to 8cd540f449a7 (11 revisions) (#4800) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 076b6401fd9e..d3a01e1b7ede 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -6656356bbf23dad8935e1ba7679ccc3dabb7d487 +8cd540f449a717b33a5500fca7dedf86e2204d62 From 36df4c59f8401fc2b50a923196d7644d83f0f435 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Fri, 11 Feb 2022 11:23:56 -0800 Subject: [PATCH 174/600] Adds support for JavaScript channels to the new iOS implementation of `webview_flutter_wkwebview`. (#4782) --- .../lib/src/web_kit/web_kit.dart | 168 ++++++++++++++++- .../lib/src/web_kit_webview_widget.dart | 99 ++++++++-- .../test/src/web_kit_webview_widget_test.dart | 170 +++++++++++++++++- .../web_kit_webview_widget_test.mocks.dart | 122 +++++++++++-- 4 files changed, 525 insertions(+), 34 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart index 562957f34eac..9f4f8edfc1f2 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart @@ -2,6 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +/// Times at which to inject script content into a webpage. +/// +/// Wraps [WKUserScriptInjectionTime](https://developer.apple.com/documentation/webkit/wkuserscriptinjectiontime?language=objc). +enum WKUserScriptInjectionTime { + /// Inject the script after the creation of the webpage’s document element, but before loading any other content. + /// + /// See https://developer.apple.com/documentation/webkit/wkuserscriptinjectiontime/wkuserscriptinjectiontimeatdocumentstart?language=objc. + atDocumentStart, + + /// Inject the script after the document finishes loading, but before loading any other subresources. + /// + /// See https://developer.apple.com/documentation/webkit/wkuserscriptinjectiontime/wkuserscriptinjectiontimeatdocumentend?language=objc. + atDocumentEnd, +} + /// The media types that require a user gesture to begin playing. /// /// Wraps [WKAudiovisualMediaTypes](https://developer.apple.com/documentation/webkit/wkaudiovisualmediatypes?language=objc). @@ -27,10 +42,149 @@ enum WKAudiovisualMediaType { all, } +/// A script that the web view injects into a webpage. +/// +/// Wraps [WKUserScript](https://developer.apple.com/documentation/webkit/wkuserscript?language=objc). +class WKUserScript { + /// Constructs a [UserScript]. + WKUserScript( + this.source, + this.injectionTime, { + required this.isMainFrameOnly, + }); + + /// The script’s source code. + final String source; + + /// The time at which to inject the script into the webpage. + final WKUserScriptInjectionTime injectionTime; + + /// Indicates whether to inject the script into the main frame or all frames. + final bool isMainFrameOnly; +} + +/// An object that encapsulates a message sent by JavaScript code from a webpage. +/// +/// Wraps [WKScriptMessage](https://developer.apple.com/documentation/webkit/wkscriptmessage?language=objc). +class WKScriptMessage { + /// Constructs a [WKScriptMessage]. + WKScriptMessage({required this.name, this.body}); + + /// The name of the message handler to which the message is sent. + final String name; + + /// The body of the message. + /// + /// Allowed types are [num], [String], [List], [Map], and `null`. + final Object? body; +} + +/// An interface for receiving messages from JavaScript code running in a webpage. +/// +/// Wraps [WKScriptMessageHandler](https://developer.apple.com/documentation/webkit/wkscriptmessagehandler?language=objc) +class WKScriptMessageHandler { + /// Tells the handler that a webpage sent a script message. + /// + /// Use this method to respond to a message sent from the webpage’s + /// JavaScript code. Use the [message] parameter to get the message contents and + /// to determine the originating web view. + Future setDidReceiveScriptMessage( + void Function( + WKUserContentController userContentController, + WKScriptMessage message, + ) + didReceiveScriptMessage, + ) { + throw UnimplementedError(); + } +} + +/// Manages interactions between JavaScript code and your web view. +/// +/// Use this object to do the following: +/// +/// * Inject JavaScript code into webpages running in your web view. +/// * Install custom JavaScript functions that call through to your app’s native +/// code. +/// +/// Wraps [WKUserContentController](https://developer.apple.com/documentation/webkit/wkusercontentcontroller?language=objc). +class WKUserContentController { + /// Constructs a [WKUserContentController]. + WKUserContentController(); + + // A WKUserContentController that is owned by configuration. + WKUserContentController._fromWebViewConfiguretion( + // TODO(bparrishMines): Remove ignore once constructor is implemented. + // ignore: avoid_unused_constructor_parameters + WKWebViewConfiguration configuration, + ); + + /// Installs a message handler that you can call from your JavaScript code. + /// + /// This name of the parameter must be unique within the user content + /// controller and must not be an empty string. The user content controller + /// uses this parameter to define a JavaScript function for your message + /// handler in the page’s main content world. The name of this function is + /// `window.webkit.messageHandlers..postMessage()`, where + /// `` corresponds to the value of this parameter. For example, if you + /// specify the string `MyFunction`, the user content controller defines the ` + /// `window.webkit.messageHandlers.MyFunction.postMessage()` function in + /// JavaScript. + Future addScriptMessageHandler( + WKScriptMessageHandler handler, + String name, + ) { + assert(name.isNotEmpty); + throw UnimplementedError(); + } + + /// Uninstalls the custom message handler with the specified name from your JavaScript code. + /// + /// If no message handler with this name exists in the user content + /// controller, this method does nothing. + /// + /// Use this method to remove a message handler that you previously installed + /// using the [addScriptMessageHandler] method. This method removes the + /// message handler from the page content world. If you installed the message + /// handler in a different content world, this method doesn’t remove it. + Future removeScriptMessageHandler(String name) { + throw UnimplementedError(); + } + + /// Uninstalls all custom message handlers associated with the user content controller. + Future removeAllScriptMessageHandlers() { + throw UnimplementedError(); + } + + /// Injects the specified script into the webpage’s content. + Future addUserScript(WKUserScript userScript) { + throw UnimplementedError(); + } + + /// Removes all user scripts from the web view. + Future removeAllUserScripts() { + throw UnimplementedError(); + } +} + /// A collection of properties that you use to initialize a web view. /// /// Wraps [WKWebViewConfiguration](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration?language=objc) class WKWebViewConfiguration { + /// Constructs a [WKWebViewConfiguration]. + WKWebViewConfiguration({required this.userContentController}); + + // A WKWebViewConfiguration that is owned by webView. + // TODO(bparrishMines): Remove ignore once constructor is implemented. + // ignore: avoid_unused_constructor_parameters + WKWebViewConfiguration._fromWebView(WKWebView webView) { + userContentController = + WKUserContentController._fromWebViewConfiguretion(this); + } + + /// Coordinates interactions between your app’s code and the webpage’s scripts and other content. + late final WKUserContentController userContentController; + /// Indicates whether HTML5 videos play inline or use the native full-screen controller. set allowsInlineMediaPlayback(bool allow) { throw UnimplementedError(); @@ -52,7 +206,7 @@ class WKWebViewConfiguration { /// /// Wraps [WKWebView](https://developer.apple.com/documentation/webkit/wkwebview?language=objc). class WKWebView { - /// Construct a [WKWebView]. + /// Constructs a [WKWebView]. /// /// [configuration] contains the configuration details for the web view. This /// method saves a copy of your configuration object. Changes you make to your @@ -66,4 +220,16 @@ class WKWebView { WKWebView([WKWebViewConfiguration? configuration]) { throw UnimplementedError(); } + + /// Contains the configuration details for the web view. + /// + /// Use the object in this property to obtain information about your web + /// view’s configuration. Because this property returns a copy of the + /// configuration object, changes you make to that object don’t affect the web + /// view’s configuration. + /// + /// If you didn’t create your web view with a [WKWebViewConfiguration] this + /// property contains a default configuration object. + late final WKWebViewConfiguration configuration = + WKWebViewConfiguration._fromWebView(this); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 38cc8619f9b4..25137c9c0af6 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -19,7 +19,7 @@ class WebKitWebViewWidget extends StatefulWidget { required this.javascriptChannelRegistry, required this.onBuildWidget, this.configuration, - @visibleForTesting this.webViewProxy = const WebViewProxy(), + @visibleForTesting this.webViewProxy = const WebViewWidgetProxy(), }); /// The initial parameters used to setup the WebView. @@ -39,7 +39,7 @@ class WebKitWebViewWidget extends StatefulWidget { /// The handler for constructing [WKWebView]s and calling static methods. /// /// This should only be changed for testing purposes. - final WebViewProxy webViewProxy; + final WebViewWidgetProxy webViewProxy; /// A callback to build a widget once [WKWebView] has been initialized. final Widget Function(WebKitWebViewPlatformController controller) @@ -78,15 +78,19 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { required this.callbacksHandler, required this.javascriptChannelRegistry, WKWebViewConfiguration? configuration, - @visibleForTesting this.webViewProxy = const WebViewProxy(), + @visibleForTesting this.webViewProxy = const WebViewWidgetProxy(), }) : super(callbacksHandler) { _setCreationParams( creationParams, - configuration: configuration ?? WKWebViewConfiguration(), - ).then((_) => _initializationCompleter.complete()); + configuration: configuration ?? + WKWebViewConfiguration( + userContentController: WKUserContentController(), + ), + ); } - final Completer _initializationCompleter = Completer(); + final Map _scriptMessageHandlers = + {}; /// Handles callbacks that are made by navigation. final WebViewPlatformCallbacksHandler callbacksHandler; @@ -97,7 +101,7 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { /// Handles constructing a [WKWebView]. /// /// This should only be changed when used for testing. - final WebViewProxy webViewProxy; + final WebViewWidgetProxy webViewProxy; /// Represents the WebView maintained by platform code. late final WKWebView webView; @@ -113,6 +117,8 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { ); webView = webViewProxy.createWebView(configuration); + + await addJavascriptChannels(params.javascriptChannelNames); } void _setWebViewConfiguration( @@ -140,18 +146,89 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { if (!requiresUserAction) WKAudiovisualMediaType.none, }; } + + @override + Future addJavascriptChannels(Set javascriptChannelNames) async { + await Future.wait( + javascriptChannelNames.where( + (String channelName) { + return !_scriptMessageHandlers.containsKey(channelName); + }, + ).map>( + (String channelName) { + final WKScriptMessageHandler handler = + webViewProxy.createScriptMessageHandler() + ..setDidReceiveScriptMessage( + ( + WKUserContentController userContentController, + WKScriptMessage message, + ) { + javascriptChannelRegistry.onJavascriptChannelMessage( + message.name, + message.body!.toString(), + ); + }, + ); + _scriptMessageHandlers[channelName] = handler; + + final String wrapperSource = + 'window.$channelName = webkit.messageHandlers.$channelName;'; + final WKUserScript wrapperScript = WKUserScript( + wrapperSource, + WKUserScriptInjectionTime.atDocumentStart, + isMainFrameOnly: false, + ); + webView.configuration.userContentController + .addUserScript(wrapperScript); + return webView.configuration.userContentController + .addScriptMessageHandler( + handler, + channelName, + ); + }, + ), + ); + } + + @override + Future removeJavascriptChannels( + Set javascriptChannelNames, + ) async { + if (javascriptChannelNames.isEmpty) { + return; + } + + // WKWebView does not support removing a single user script, so this removes + // all user scripts and all message handlers and re-registers channels that + // shouldn't be removed. Note that this workaround could interfere with + // exposing support for custom scripts from applications. + webView.configuration.userContentController.removeAllUserScripts(); + webView.configuration.userContentController + .removeAllScriptMessageHandlers(); + + javascriptChannelNames.forEach(_scriptMessageHandlers.remove); + final Set remainingNames = _scriptMessageHandlers.keys.toSet(); + _scriptMessageHandlers.clear(); + + await addJavascriptChannels(remainingNames); + } } -/// Handles constructing [WKWebView]s and calling static methods. +/// Handles constructing objects and calling static methods. /// /// This should only be used for testing purposes. @visibleForTesting -class WebViewProxy { - /// Creates a [WebViewProxy]. - const WebViewProxy(); +class WebViewWidgetProxy { + /// Constructs a [WebViewWidgetProxy]. + const WebViewWidgetProxy(); /// Constructs a [WKWebView]. WKWebView createWebView(WKWebViewConfiguration configuration) { return WKWebView(configuration); } + + /// Constructs a [WKScriptMessageHandler]. + WKScriptMessageHandler createScriptMessageHandler() { + return WKScriptMessageHandler(); + } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index 6ddec48182a2..95d5e2cc5fa6 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -15,29 +15,39 @@ import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart'; import 'web_kit_webview_widget_test.mocks.dart'; @GenerateMocks([ + WKScriptMessageHandler, WKWebView, WKWebViewConfiguration, + WKUserContentController, JavascriptChannelRegistry, WebViewPlatformCallbacksHandler, - WebViewProxy, + WebViewWidgetProxy, ]) void main() { TestWidgetsFlutterBinding.ensureInitialized(); group('$WebKitWebViewWidget', () { late MockWKWebView mockWebView; - late MockWebViewProxy mockWebViewProxy; + late MockWebViewWidgetProxy mockWebViewWidgetProxy; + late MockWKUserContentController mockUserContentController; late MockWKWebViewConfiguration mockWebViewConfiguration; late MockWebViewPlatformCallbacksHandler mockCallbacksHandler; late MockJavascriptChannelRegistry mockJavascriptChannelRegistry; + late WebKitWebViewPlatformController testController; + setUp(() { mockWebView = MockWKWebView(); mockWebViewConfiguration = MockWKWebViewConfiguration(); - mockWebViewProxy = MockWebViewProxy(); + mockUserContentController = MockWKUserContentController(); + mockWebViewWidgetProxy = MockWebViewWidgetProxy(); - when(mockWebViewProxy.createWebView(any)).thenReturn(mockWebView); + when(mockWebViewWidgetProxy.createWebView(any)).thenReturn(mockWebView); + when(mockWebView.configuration).thenReturn(mockWebViewConfiguration); + when(mockWebViewConfiguration.userContentController).thenReturn( + mockUserContentController, + ); mockCallbacksHandler = MockWebViewPlatformCallbacksHandler(); mockJavascriptChannelRegistry = MockJavascriptChannelRegistry(); @@ -49,7 +59,6 @@ void main() { CreationParams? creationParams, bool hasNavigationDelegate = false, bool hasProgressTracking = false, - bool useHybridComposition = false, }) async { await tester.pumpWidget(WebKitWebViewWidget( creationParams: creationParams ?? @@ -61,9 +70,10 @@ void main() { )), callbacksHandler: mockCallbacksHandler, javascriptChannelRegistry: mockJavascriptChannelRegistry, - webViewProxy: mockWebViewProxy, + webViewProxy: mockWebViewWidgetProxy, configuration: mockWebViewConfiguration, onBuildWidget: (WebKitWebViewPlatformController controller) { + testController = controller; return Container(); }, )); @@ -114,6 +124,40 @@ void main() { }); }); + testWidgets('javascriptChannelNames', (WidgetTester tester) async { + when(mockWebViewWidgetProxy.createScriptMessageHandler()).thenReturn( + MockWKScriptMessageHandler(), + ); + + await buildWidget( + tester, + creationParams: CreationParams( + javascriptChannelNames: {'a', 'b'}, + webSettings: WebSettings( + userAgent: const WebSetting.absent(), + hasNavigationDelegate: false, + ), + ), + ); + + final List javaScriptChannels = verify( + mockUserContentController.addScriptMessageHandler( + captureAny, + captureAny, + ), + ).captured; + expect( + javaScriptChannels[0], + isA(), + ); + expect(javaScriptChannels[1], 'a'); + expect( + javaScriptChannels[2], + isA(), + ); + expect(javaScriptChannels[3], 'b'); + }); + group('$WebSettings', () { testWidgets('allowsInlineMediaPlayback', (WidgetTester tester) async { await buildWidget( @@ -130,5 +174,119 @@ void main() { }); }); }); + + group('$WebKitWebViewPlatformController', () { + testWidgets('addJavascriptChannels', (WidgetTester tester) async { + when(mockWebViewWidgetProxy.createScriptMessageHandler()).thenReturn( + MockWKScriptMessageHandler(), + ); + + await buildWidget(tester); + + await testController.addJavascriptChannels({'c', 'd'}); + final List javaScriptChannels = verify( + mockUserContentController.addScriptMessageHandler( + captureAny, captureAny), + ).captured; + expect( + javaScriptChannels[0], + isA(), + ); + expect(javaScriptChannels[1], 'c'); + expect( + javaScriptChannels[2], + isA(), + ); + expect(javaScriptChannels[3], 'd'); + + final List userScripts = + verify(mockUserContentController.addUserScript(captureAny)) + .captured + .cast(); + expect(userScripts[0].source, 'window.c = webkit.messageHandlers.c;'); + expect( + userScripts[0].injectionTime, + WKUserScriptInjectionTime.atDocumentStart, + ); + expect(userScripts[0].isMainFrameOnly, false); + expect(userScripts[1].source, 'window.d = webkit.messageHandlers.d;'); + expect( + userScripts[1].injectionTime, + WKUserScriptInjectionTime.atDocumentStart, + ); + expect(userScripts[0].isMainFrameOnly, false); + }); + + testWidgets('removeJavascriptChannels', (WidgetTester tester) async { + when(mockWebViewWidgetProxy.createScriptMessageHandler()).thenReturn( + MockWKScriptMessageHandler(), + ); + + await buildWidget(tester); + + await testController.addJavascriptChannels({'c', 'd'}); + reset(mockUserContentController); + + await testController.removeJavascriptChannels({'c'}); + + verify(mockUserContentController.removeAllScriptMessageHandlers()); + verify(mockUserContentController.removeAllUserScripts()); + + final List javaScriptChannels = verify( + mockUserContentController.addScriptMessageHandler( + captureAny, captureAny), + ).captured; + expect( + javaScriptChannels[0], + isA(), + ); + expect(javaScriptChannels[1], 'd'); + + final List userScripts = + verify(mockUserContentController.addUserScript(captureAny)) + .captured + .cast(); + expect(userScripts[0].source, 'window.d = webkit.messageHandlers.d;'); + expect( + userScripts[0].injectionTime, + WKUserScriptInjectionTime.atDocumentStart, + ); + expect(userScripts[0].isMainFrameOnly, false); + }); + }); + + group('$JavascriptChannelRegistry', () { + testWidgets('onJavascriptChannelMessage', (WidgetTester tester) async { + when(mockWebViewWidgetProxy.createScriptMessageHandler()).thenReturn( + MockWKScriptMessageHandler(), + ); + + await buildWidget(tester); + await testController.addJavascriptChannels({'hello'}); + + final MockWKScriptMessageHandler messageHandler = verify( + mockUserContentController.addScriptMessageHandler( + captureAny, 'hello')) + .captured + .single as MockWKScriptMessageHandler; + + final dynamic didReceiveScriptMessage = + verify(messageHandler.setDidReceiveScriptMessage(captureAny)) + .captured + .single as void Function( + WKUserContentController userContentController, + WKScriptMessage message, + ); + + didReceiveScriptMessage( + mockUserContentController, + WKScriptMessage(name: 'hello', body: 'A message.'), + ); + verify(mockJavascriptChannelRegistry.onJavascriptChannelMessage( + 'hello', + 'A message.', + )); + }); + }); }); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index 1ec05313716c..6c177b87e953 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -2,14 +2,14 @@ // in webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. // Do not manually edit this file. -import 'dart:async' as _i5; +import 'dart:async' as _i3; import 'package:mockito/mockito.dart' as _i1; import 'package:webview_flutter_platform_interface/src/types/javascript_channel.dart' - as _i4; + as _i5; import 'package:webview_flutter_platform_interface/src/types/types.dart' as _i6; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart' - as _i3; + as _i4; import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart' as _i2; import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart' as _i7; @@ -23,7 +23,36 @@ import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart' // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types -class _FakeWKWebView_0 extends _i1.Fake implements _i2.WKWebView {} +class _FakeWKWebViewConfiguration_0 extends _i1.Fake + implements _i2.WKWebViewConfiguration {} + +class _FakeWKUserContentController_1 extends _i1.Fake + implements _i2.WKUserContentController {} + +class _FakeWKWebView_2 extends _i1.Fake implements _i2.WKWebView {} + +class _FakeWKScriptMessageHandler_3 extends _i1.Fake + implements _i2.WKScriptMessageHandler {} + +/// A class which mocks [WKScriptMessageHandler]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockWKScriptMessageHandler extends _i1.Mock + implements _i2.WKScriptMessageHandler { + MockWKScriptMessageHandler() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.Future setDidReceiveScriptMessage( + void Function(_i2.WKUserContentController, _i2.WKScriptMessage)? + didReceiveScriptMessage) => + (super.noSuchMethod( + Invocation.method( + #setDidReceiveScriptMessage, [didReceiveScriptMessage]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i3.Future); +} /// A class which mocks [WKWebView]. /// @@ -32,6 +61,12 @@ class MockWKWebView extends _i1.Mock implements _i2.WKWebView { MockWKWebView() { _i1.throwOnMissingStub(this); } + + @override + _i2.WKWebViewConfiguration get configuration => + (super.noSuchMethod(Invocation.getter(#configuration), + returnValue: _FakeWKWebViewConfiguration_0()) + as _i2.WKWebViewConfiguration); } /// A class which mocks [WKWebViewConfiguration]. @@ -43,6 +78,17 @@ class MockWKWebViewConfiguration extends _i1.Mock _i1.throwOnMissingStub(this); } + @override + _i2.WKUserContentController get userContentController => + (super.noSuchMethod(Invocation.getter(#userContentController), + returnValue: _FakeWKUserContentController_1()) + as _i2.WKUserContentController); + @override + set userContentController( + _i2.WKUserContentController? _userContentController) => + super.noSuchMethod( + Invocation.setter(#userContentController, _userContentController), + returnValueForMissingStub: null); @override set allowsInlineMediaPlayback(bool? allow) => super.noSuchMethod(Invocation.setter(#allowsInlineMediaPlayback, allow), @@ -55,27 +101,65 @@ class MockWKWebViewConfiguration extends _i1.Mock returnValueForMissingStub: null); } +/// A class which mocks [WKUserContentController]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockWKUserContentController extends _i1.Mock + implements _i2.WKUserContentController { + MockWKUserContentController() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.Future addScriptMessageHandler( + _i2.WKScriptMessageHandler? handler, String? name) => + (super.noSuchMethod( + Invocation.method(#addScriptMessageHandler, [handler, name]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i3.Future); + @override + _i3.Future removeScriptMessageHandler(String? name) => (super + .noSuchMethod(Invocation.method(#removeScriptMessageHandler, [name]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i3.Future); + @override + _i3.Future removeAllScriptMessageHandlers() => (super.noSuchMethod( + Invocation.method(#removeAllScriptMessageHandlers, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i3.Future); + @override + _i3.Future addUserScript(_i2.WKUserScript? userScript) => + (super.noSuchMethod(Invocation.method(#addUserScript, [userScript]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i3.Future); + @override + _i3.Future removeAllUserScripts() => + (super.noSuchMethod(Invocation.method(#removeAllUserScripts, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i3.Future); +} + /// A class which mocks [JavascriptChannelRegistry]. /// /// See the documentation for Mockito's code generation for more information. class MockJavascriptChannelRegistry extends _i1.Mock - implements _i3.JavascriptChannelRegistry { + implements _i4.JavascriptChannelRegistry { MockJavascriptChannelRegistry() { _i1.throwOnMissingStub(this); } @override - Map get channels => + Map get channels => (super.noSuchMethod(Invocation.getter(#channels), - returnValue: {}) - as Map); + returnValue: {}) + as Map); @override void onJavascriptChannelMessage(String? channel, String? message) => super.noSuchMethod( Invocation.method(#onJavascriptChannelMessage, [channel, message]), returnValueForMissingStub: null); @override - void updateJavascriptChannelsFromSet(Set<_i4.JavascriptChannel>? channels) => + void updateJavascriptChannelsFromSet(Set<_i5.JavascriptChannel>? channels) => super.noSuchMethod( Invocation.method(#updateJavascriptChannelsFromSet, [channels]), returnValueForMissingStub: null); @@ -85,17 +169,17 @@ class MockJavascriptChannelRegistry extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockWebViewPlatformCallbacksHandler extends _i1.Mock - implements _i3.WebViewPlatformCallbacksHandler { + implements _i4.WebViewPlatformCallbacksHandler { MockWebViewPlatformCallbacksHandler() { _i1.throwOnMissingStub(this); } @override - _i5.FutureOr onNavigationRequest({String? url, bool? isForMainFrame}) => + _i3.FutureOr onNavigationRequest({String? url, bool? isForMainFrame}) => (super.noSuchMethod( Invocation.method(#onNavigationRequest, [], {#url: url, #isForMainFrame: isForMainFrame}), - returnValue: Future.value(false)) as _i5.FutureOr); + returnValue: Future.value(false)) as _i3.FutureOr); @override void onPageStarted(String? url) => super.noSuchMethod(Invocation.method(#onPageStarted, [url]), @@ -114,16 +198,22 @@ class MockWebViewPlatformCallbacksHandler extends _i1.Mock returnValueForMissingStub: null); } -/// A class which mocks [WebViewProxy]. +/// A class which mocks [WebViewWidgetProxy]. /// /// See the documentation for Mockito's code generation for more information. -class MockWebViewProxy extends _i1.Mock implements _i7.WebViewProxy { - MockWebViewProxy() { +class MockWebViewWidgetProxy extends _i1.Mock + implements _i7.WebViewWidgetProxy { + MockWebViewWidgetProxy() { _i1.throwOnMissingStub(this); } @override _i2.WKWebView createWebView(_i2.WKWebViewConfiguration? configuration) => (super.noSuchMethod(Invocation.method(#createWebView, [configuration]), - returnValue: _FakeWKWebView_0()) as _i2.WKWebView); + returnValue: _FakeWKWebView_2()) as _i2.WKWebView); + @override + _i2.WKScriptMessageHandler createScriptMessageHandler() => + (super.noSuchMethod(Invocation.method(#createScriptMessageHandler, []), + returnValue: _FakeWKScriptMessageHandler_3()) + as _i2.WKScriptMessageHandler); } From 82be1732799e986b9a728c1c4ff1846db1445aeb Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 11 Feb 2022 17:55:23 -0500 Subject: [PATCH 175/600] Roll Flutter from 8cd540f449a7 to 61ac285608cd (5 revisions) (#4810) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index d3a01e1b7ede..bae62fa60a18 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -8cd540f449a717b33a5500fca7dedf86e2204d62 +61ac285608cd3c3c003507a572b34fad893a21d4 From ce42c4a5526aaf3bbfc5b0471eadd6f587838cef Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 11 Feb 2022 20:05:19 -0500 Subject: [PATCH 176/600] Roll Flutter from 61ac285608cd to 3870548fa6b1 (5 revisions) (#4812) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index bae62fa60a18..8c4a305d89e6 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -61ac285608cd3c3c003507a572b34fad893a21d4 +3870548fa6b19eb616516d4f5dc649a71c021a2c From b211e9ea963191bc5fe3dab5f451065895c6bba3 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 11 Feb 2022 23:00:14 -0500 Subject: [PATCH 177/600] Roll Flutter from 3870548fa6b1 to f54790b7b03f (2 revisions) (#4814) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 8c4a305d89e6..e3a44c789b9b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -3870548fa6b19eb616516d4f5dc649a71c021a2c +f54790b7b03fdafe52eadfca8e888cb39b5a8737 From b108c79471e654a54fe67d6e3dca9dff759b688c Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 12 Feb 2022 03:10:19 -0500 Subject: [PATCH 178/600] Roll Flutter from f54790b7b03f to 350e3ee46d74 (3 revisions) (#4817) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index e3a44c789b9b..e8c702620360 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -f54790b7b03fdafe52eadfca8e888cb39b5a8737 +350e3ee46d7422c4a17001319b5b5d680b11f7a0 From 38235d998ffadf3f6ba420fd91a50f285b02e8d7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 12 Feb 2022 19:30:06 -0500 Subject: [PATCH 179/600] Roll Flutter from 350e3ee46d74 to f523ddd2d6ab (1 revision) (#4820) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index e8c702620360..8de682966f44 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -350e3ee46d7422c4a17001319b5b5d680b11f7a0 +f523ddd2d6ab0eb403aea5b3726c23b90733e0d2 From b634a426551efdc90109a396ec5b690231ed123c Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 13 Feb 2022 09:25:23 -0500 Subject: [PATCH 180/600] Roll Flutter from f523ddd2d6ab to 0f33a369bfb0 (1 revision) (#4823) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 8de682966f44..cacea1faa801 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -f523ddd2d6ab0eb403aea5b3726c23b90733e0d2 +0f33a369bfb002226aa9d2cc07ed751cb057561b From 484bdfa399f5a42047a34b25de2532d871b896a6 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 13 Feb 2022 19:35:21 -0500 Subject: [PATCH 181/600] Roll Flutter from 0f33a369bfb0 to 5515400a1b02 (3 revisions) (#4828) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index cacea1faa801..593f428e2312 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -0f33a369bfb002226aa9d2cc07ed751cb057561b +5515400a1b0285f308a3e50a754bf9d49358bc42 From 07578ae66c249d590db1b12876521c446bbb1596 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 14 Feb 2022 00:40:16 -0500 Subject: [PATCH 182/600] Roll Flutter from 5515400a1b02 to ad612b59e5b6 (1 revision) (#4830) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 593f428e2312..4117012f618b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -5515400a1b0285f308a3e50a754bf9d49358bc42 +ad612b59e5b6e94f6a23bc89415c4ce606ba8019 From 2efdee1e7368cebdfccf88bbab6887156d2f4f7d Mon Sep 17 00:00:00 2001 From: BeMacized Date: Mon, 14 Feb 2022 11:20:23 +0100 Subject: [PATCH 183/600] [local_auth] Add platform interface to prepare for migration to federated architecture (#4697) --- .../local_auth_platform_interface/AUTHORS | 67 +++++++ .../CHANGELOG.md | 3 + .../local_auth_platform_interface/LICENSE | 25 +++ .../local_auth_platform_interface/README.md | 26 +++ .../lib/default_method_channel_platform.dart | 78 ++++++++ .../lib/local_auth_platform_interface.dart | 99 ++++++++++ .../lib/types/auth_messages.dart | 12 ++ .../lib/types/auth_options.dart | 60 ++++++ .../lib/types/biometric_type.dart | 27 +++ .../pubspec.yaml | 22 +++ .../default_method_channel_platform_test.dart | 180 ++++++++++++++++++ 11 files changed, 599 insertions(+) create mode 100644 packages/local_auth/local_auth_platform_interface/AUTHORS create mode 100644 packages/local_auth/local_auth_platform_interface/CHANGELOG.md create mode 100644 packages/local_auth/local_auth_platform_interface/LICENSE create mode 100644 packages/local_auth/local_auth_platform_interface/README.md create mode 100644 packages/local_auth/local_auth_platform_interface/lib/default_method_channel_platform.dart create mode 100644 packages/local_auth/local_auth_platform_interface/lib/local_auth_platform_interface.dart create mode 100644 packages/local_auth/local_auth_platform_interface/lib/types/auth_messages.dart create mode 100644 packages/local_auth/local_auth_platform_interface/lib/types/auth_options.dart create mode 100644 packages/local_auth/local_auth_platform_interface/lib/types/biometric_type.dart create mode 100644 packages/local_auth/local_auth_platform_interface/pubspec.yaml create mode 100644 packages/local_auth/local_auth_platform_interface/test/default_method_channel_platform_test.dart diff --git a/packages/local_auth/local_auth_platform_interface/AUTHORS b/packages/local_auth/local_auth_platform_interface/AUTHORS new file mode 100644 index 000000000000..d5694690c247 --- /dev/null +++ b/packages/local_auth/local_auth_platform_interface/AUTHORS @@ -0,0 +1,67 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> +Bodhi Mulders diff --git a/packages/local_auth/local_auth_platform_interface/CHANGELOG.md b/packages/local_auth/local_auth_platform_interface/CHANGELOG.md new file mode 100644 index 000000000000..0d8803f93540 --- /dev/null +++ b/packages/local_auth/local_auth_platform_interface/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +* Initial release. diff --git a/packages/local_auth/local_auth_platform_interface/LICENSE b/packages/local_auth/local_auth_platform_interface/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/local_auth/local_auth_platform_interface/LICENSE @@ -0,0 +1,25 @@ +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/local_auth/local_auth_platform_interface/README.md b/packages/local_auth/local_auth_platform_interface/README.md new file mode 100644 index 000000000000..3b01ced7b93b --- /dev/null +++ b/packages/local_auth/local_auth_platform_interface/README.md @@ -0,0 +1,26 @@ +# local_auth_platform_interface + +A common platform interface for the [`local_auth`][1] plugin. + +This interface allows platform-specific implementations of the `local_auth` +plugin, as well as the plugin itself, to ensure they are supporting the +same interface. + +# Usage + +To implement a new platform-specific implementation of `local_auth`, extend +[`LocalAuthPlatform`][2] with an implementation that performs the +platform-specific behavior, and when you register your plugin, set the default +`LocalAuthPlatform` by calling +`LocalAuthPlatform.instance = MyLocalAuthPlatform()`. + +# Note on breaking changes + +Strongly prefer non-breaking changes (such as adding a method to the interface) +over breaking changes for this package. + +See https://flutter.dev/go/platform-interface-breaking-changes for a discussion +on why a less-clean interface is preferable to a breaking change. + +[1]: ../local_auth +[2]: lib/local_auth_platform_interface.dart diff --git a/packages/local_auth/local_auth_platform_interface/lib/default_method_channel_platform.dart b/packages/local_auth/local_auth_platform_interface/lib/default_method_channel_platform.dart new file mode 100644 index 000000000000..c68a3bfb8371 --- /dev/null +++ b/packages/local_auth/local_auth_platform_interface/lib/default_method_channel_platform.dart @@ -0,0 +1,78 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; +import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; +import 'package:local_auth_platform_interface/types/auth_messages.dart'; +import 'package:local_auth_platform_interface/types/auth_options.dart'; +import 'package:local_auth_platform_interface/types/biometric_type.dart'; + +const MethodChannel _channel = MethodChannel('plugins.flutter.io/local_auth'); + +/// The default interface implementation acting as a placeholder for +/// the native implementation to be set. +/// +/// This implementation is not used by any of the implementations in this +/// repository, and exists only for backward compatibility with any +/// clients that were relying on internal details of the method channel +/// in the pre-federated plugin. +class DefaultLocalAuthPlatform extends LocalAuthPlatform { + @override + Future authenticate({ + required String localizedReason, + required Iterable authMessages, + AuthenticationOptions options = const AuthenticationOptions(), + }) async { + assert(localizedReason.isNotEmpty); + final Map args = { + 'localizedReason': localizedReason, + 'useErrorDialogs': options.useErrorDialogs, + 'stickyAuth': options.stickyAuth, + 'sensitiveTransaction': options.sensitiveTransaction, + 'biometricOnly': options.biometricOnly, + }; + for (final AuthMessages messages in authMessages) { + args.addAll(messages.args); + } + return (await _channel.invokeMethod('authenticate', args)) ?? false; + } + + @override + Future> getEnrolledBiometrics() async { + final List result = (await _channel.invokeListMethod( + 'getAvailableBiometrics', + )) ?? + []; + final List biometrics = []; + for (final String value in result) { + switch (value) { + case 'face': + biometrics.add(BiometricType.face); + break; + case 'fingerprint': + biometrics.add(BiometricType.fingerprint); + break; + case 'iris': + biometrics.add(BiometricType.iris); + break; + case 'undefined': + break; + } + } + return biometrics; + } + + @override + Future deviceSupportsBiometrics() async { + return (await getEnrolledBiometrics()).isNotEmpty; + } + + @override + Future isDeviceSupported() async => + (await _channel.invokeMethod('isDeviceSupported')) ?? false; + + @override + Future stopAuthentication() async => + await _channel.invokeMethod('stopAuthentication') ?? false; +} diff --git a/packages/local_auth/local_auth_platform_interface/lib/local_auth_platform_interface.dart b/packages/local_auth/local_auth_platform_interface/lib/local_auth_platform_interface.dart new file mode 100644 index 000000000000..b909ee90d12b --- /dev/null +++ b/packages/local_auth/local_auth_platform_interface/lib/local_auth_platform_interface.dart @@ -0,0 +1,99 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:local_auth_platform_interface/default_method_channel_platform.dart'; +import 'package:local_auth_platform_interface/types/auth_messages.dart'; +import 'package:local_auth_platform_interface/types/auth_options.dart'; +import 'package:local_auth_platform_interface/types/biometric_type.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +/// The interface that implementations of local_auth must implement. +/// +/// Platform implementations should extend this class rather than implement it as `local_auth` +/// does not consider newly added methods to be breaking changes. Extending this class +/// (using `extends`) ensures that the subclass will get the default implementation, while +/// platform implementations that `implements` this interface will be broken by newly added +/// [LocalAuthPlatform] methods. +abstract class LocalAuthPlatform extends PlatformInterface { + /// Constructs a LocalAuthPlatform. + LocalAuthPlatform() : super(token: _token); + + static final Object _token = Object(); + + static LocalAuthPlatform _instance = DefaultLocalAuthPlatform(); + + /// The default instance of [LocalAuthPlatform] to use. + /// + /// Defaults to [DefaultLocalAuthPlatform]. + static LocalAuthPlatform get instance => _instance; + + /// Platform-specific implementations should set this with their own + /// platform-specific class that extends [LocalAuthPlatform] when they + /// register themselves. + static set instance(LocalAuthPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + /// Authenticates the user with biometrics available on the device while also + /// allowing the user to use device authentication - pin, pattern, passcode. + /// + /// Returns true if the user successfully authenticated, false otherwise. + /// + /// [localizedReason] is the message to show to user while prompting them + /// for authentication. This is typically along the lines of: 'Please scan + /// your finger to access MyApp.'. This must not be empty. + /// + /// Provide [authMessages] if you want to + /// customize messages in the dialogs. + /// + /// Provide [options] for configuring further authentication related options. + /// + /// Throws a [PlatformException] if there were technical problems with local + /// authentication (e.g. lack of relevant hardware). This might throw + /// [PlatformException] with error code [otherOperatingSystem] on the iOS + /// simulator. + Future authenticate({ + required String localizedReason, + required Iterable authMessages, + AuthenticationOptions options = const AuthenticationOptions(), + }) async { + throw UnimplementedError('authenticate() has not been implemented.'); + } + + /// Returns true if the device is capable of checking biometrics. + /// + /// This will return true even if there are no biometrics currently enrolled. + Future deviceSupportsBiometrics() async { + throw UnimplementedError('canCheckBiometrics() has not been implemented.'); + } + + /// Returns a list of enrolled biometrics. + /// + /// Possible values include: + /// - BiometricType.face + /// - BiometricType.fingerprint + /// - BiometricType.iris (not yet implemented) + /// - BiometricType.strong + /// - BiometricType.weak + Future> getEnrolledBiometrics() async { + throw UnimplementedError( + 'getAvailableBiometrics() has not been implemented.'); + } + + /// Returns true if device is capable of checking biometrics or is able to + /// fail over to device credentials. + Future isDeviceSupported() async { + throw UnimplementedError('isDeviceSupported() has not been implemented.'); + } + + /// Cancels any authentication currently in progress. + /// + /// Returns true if auth was cancelled successfully. + /// Returns false if there was no authentication in progress, + /// or an error occurred. + Future stopAuthentication() async { + throw UnimplementedError('stopAuthentication() has not been implemented.'); + } +} diff --git a/packages/local_auth/local_auth_platform_interface/lib/types/auth_messages.dart b/packages/local_auth/local_auth_platform_interface/lib/types/auth_messages.dart new file mode 100644 index 000000000000..d51980d575cf --- /dev/null +++ b/packages/local_auth/local_auth_platform_interface/lib/types/auth_messages.dart @@ -0,0 +1,12 @@ +// 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. + +/// Abstract class for storing platform specific strings. +abstract class AuthMessages { + /// Constructs an instance of [AuthMessages]. + const AuthMessages(); + + /// Returns all platform-specific messages as a map. + Map get args; +} diff --git a/packages/local_auth/local_auth_platform_interface/lib/types/auth_options.dart b/packages/local_auth/local_auth_platform_interface/lib/types/auth_options.dart new file mode 100644 index 000000000000..c4b646c0b97a --- /dev/null +++ b/packages/local_auth/local_auth_platform_interface/lib/types/auth_options.dart @@ -0,0 +1,60 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; + +/// Options wrapper for [LocalAuthPlatform.authenticate] parameters. +@immutable +class AuthenticationOptions { + /// Constructs a new instance. + const AuthenticationOptions({ + this.useErrorDialogs = true, + this.stickyAuth = false, + this.sensitiveTransaction = true, + this.biometricOnly = false, + }); + + /// Whether the system will attempt to handle user-fixable issues encountered + /// while authenticating. For instance, if a fingerprint reader exists on the + /// device but there's no fingerprint registered, the plugin might attempt to + /// take the user to settings to add one. Anything that is not user fixable, + /// such as no biometric sensor on device, will still result in + /// a [PlatformException]. + final bool useErrorDialogs; + + /// Used when the application goes into background for any reason while the + /// authentication is in progress. Due to security reasons, the + /// authentication has to be stopped at that time. If stickyAuth is set to + /// true, authentication resumes when the app is resumed. If it is set to + /// false (default), then as soon as app is paused a failure message is sent + /// back to Dart and it is up to the client app to restart authentication or + /// do something else. + final bool stickyAuth; + + /// Whether platform specific precautions are enabled. For instance, on face + /// unlock, Android opens a confirmation dialog after the face is recognized + /// to make sure the user meant to unlock their device. + final bool sensitiveTransaction; + + /// Prevent authentications from using non-biometric local authentication + /// such as pin, passcode, or pattern. + final bool biometricOnly; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is AuthenticationOptions && + runtimeType == other.runtimeType && + useErrorDialogs == other.useErrorDialogs && + stickyAuth == other.stickyAuth && + sensitiveTransaction == other.sensitiveTransaction && + biometricOnly == other.biometricOnly; + + @override + int get hashCode => + useErrorDialogs.hashCode ^ + stickyAuth.hashCode ^ + sensitiveTransaction.hashCode ^ + biometricOnly.hashCode; +} diff --git a/packages/local_auth/local_auth_platform_interface/lib/types/biometric_type.dart b/packages/local_auth/local_auth_platform_interface/lib/types/biometric_type.dart new file mode 100644 index 000000000000..9c335e25624a --- /dev/null +++ b/packages/local_auth/local_auth_platform_interface/lib/types/biometric_type.dart @@ -0,0 +1,27 @@ +// 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. + +/// Various types of biometric authentication. +/// Some platforms report specific biometric types, while others report only +/// classifications like strong and weak. +enum BiometricType { + /// Face authentication. + face, + + /// Fingerprint authentication. + fingerprint, + + /// Iris authentication. + iris, + + /// Any biometric (e.g. fingerprint, iris, or face) on the device that the + /// platform API considers to be strong. For example, on Android this + /// corresponds to Class 3. + strong, + + /// Any biometric (e.g. fingerprint, iris, or face) on the device that the + /// platform API considers to be weak. For example, on Android this + /// corresponds to Class 2. + weak, +} diff --git a/packages/local_auth/local_auth_platform_interface/pubspec.yaml b/packages/local_auth/local_auth_platform_interface/pubspec.yaml new file mode 100644 index 000000000000..f04268926ebd --- /dev/null +++ b/packages/local_auth/local_auth_platform_interface/pubspec.yaml @@ -0,0 +1,22 @@ +name: local_auth_platform_interface +description: A common platform interface for the local_auth plugin. +repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_platform_interface +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 +# NOTE: We strongly prefer non-breaking changes, even at the expense of a +# less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes +version: 1.0.0 + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.8.0" + +dependencies: + flutter: + sdk: flutter + intl: ^0.17.0 + plugin_platform_interface: ^2.1.2 + +dev_dependencies: + flutter_test: + sdk: flutter + mockito: ^5.0.0 \ No newline at end of file diff --git a/packages/local_auth/local_auth_platform_interface/test/default_method_channel_platform_test.dart b/packages/local_auth/local_auth_platform_interface/test/default_method_channel_platform_test.dart new file mode 100644 index 000000000000..3853fd84c6fc --- /dev/null +++ b/packages/local_auth/local_auth_platform_interface/test/default_method_channel_platform_test.dart @@ -0,0 +1,180 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:local_auth_platform_interface/default_method_channel_platform.dart'; +import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; +import 'package:local_auth_platform_interface/types/auth_messages.dart'; +import 'package:local_auth_platform_interface/types/auth_options.dart'; +import 'package:local_auth_platform_interface/types/biometric_type.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + const MethodChannel channel = MethodChannel( + 'plugins.flutter.io/local_auth', + ); + + final List log = []; + late LocalAuthPlatform localAuthentication; + + test( + 'DefaultLocalAuthPlatform is registered as the default platform implementation', + () async { + expect(LocalAuthPlatform.instance, + const TypeMatcher()); + }); + + test('getAvailableBiometrics', () async { + channel.setMockMethodCallHandler((MethodCall methodCall) { + log.add(methodCall); + return Future.value([]); + }); + localAuthentication = DefaultLocalAuthPlatform(); + log.clear(); + await localAuthentication.getEnrolledBiometrics(); + expect( + log, + [ + isMethodCall('getAvailableBiometrics', arguments: null), + ], + ); + }); + + group('Boolean returning methods', () { + setUp(() { + channel.setMockMethodCallHandler((MethodCall methodCall) { + log.add(methodCall); + return Future.value(true); + }); + localAuthentication = DefaultLocalAuthPlatform(); + log.clear(); + }); + + test('isDeviceSupported', () async { + await localAuthentication.isDeviceSupported(); + expect( + log, + [ + isMethodCall('isDeviceSupported', arguments: null), + ], + ); + }); + + test('stopAuthentication', () async { + await localAuthentication.stopAuthentication(); + expect( + log, + [ + isMethodCall('stopAuthentication', arguments: null), + ], + ); + }); + + group('authenticate with device auth fail over', () { + test('authenticate with no args.', () async { + await localAuthentication.authenticate( + authMessages: [], + localizedReason: 'Needs secure', + options: const AuthenticationOptions(biometricOnly: true), + ); + expect( + log, + [ + isMethodCall( + 'authenticate', + arguments: { + 'localizedReason': 'Needs secure', + 'useErrorDialogs': true, + 'stickyAuth': false, + 'sensitiveTransaction': true, + 'biometricOnly': true, + }, + ), + ], + ); + }); + + test('authenticate with no sensitive transaction.', () async { + await localAuthentication.authenticate( + authMessages: [], + localizedReason: 'Insecure', + options: const AuthenticationOptions( + sensitiveTransaction: false, + useErrorDialogs: false, + biometricOnly: true, + ), + ); + expect( + log, + [ + isMethodCall( + 'authenticate', + arguments: { + 'localizedReason': 'Insecure', + 'useErrorDialogs': false, + 'stickyAuth': false, + 'sensitiveTransaction': false, + 'biometricOnly': true, + }, + ), + ], + ); + }); + }); + + group('authenticate with biometrics only', () { + test('authenticate with no args.', () async { + await localAuthentication.authenticate( + authMessages: [], + localizedReason: 'Needs secure', + ); + expect( + log, + [ + isMethodCall( + 'authenticate', + arguments: { + 'localizedReason': 'Needs secure', + 'useErrorDialogs': true, + 'stickyAuth': false, + 'sensitiveTransaction': true, + 'biometricOnly': false, + }, + ), + ], + ); + }); + + test('authenticate with no sensitive transaction.', () async { + await localAuthentication.authenticate( + authMessages: [], + localizedReason: 'Insecure', + options: const AuthenticationOptions( + sensitiveTransaction: false, + useErrorDialogs: false, + ), + ); + expect( + log, + [ + isMethodCall( + 'authenticate', + arguments: { + 'localizedReason': 'Insecure', + 'useErrorDialogs': false, + 'stickyAuth': false, + 'sensitiveTransaction': false, + 'biometricOnly': false, + }, + ), + ], + ); + }); + }); + }); +} From 6b0d4549eaa587db6ff42b10532b1b253598e936 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 14 Feb 2022 09:35:24 -0500 Subject: [PATCH 184/600] Roll Flutter from ad612b59e5b6 to 19586569438a (1 revision) (#4832) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 4117012f618b..4765e192a2f0 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ad612b59e5b6e94f6a23bc89415c4ce606ba8019 +19586569438afdc52fcc0ab7d95edeeb7a2d961f From e332159595c260dd4bac2b85ab66cd23b93ff42c Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 14 Feb 2022 14:25:25 -0500 Subject: [PATCH 185/600] [camera] Switch app-facing package to new analysis options (#4808) --- packages/camera/camera/CHANGELOG.md | 17 +- packages/camera/camera/analysis_options.yaml | 1 - .../example/integration_test/camera_test.dart | 28 +- packages/camera/camera/example/lib/main.dart | 207 +++++---- packages/camera/camera/example/pubspec.yaml | 7 +- .../example/test_driver/integration_test.dart | 4 +- packages/camera/camera/lib/camera.dart | 8 +- .../camera/lib/src/camera_controller.dart | 101 ++-- .../camera/camera/lib/src/camera_image.dart | 27 +- .../camera/camera/lib/src/camera_preview.dart | 14 +- packages/camera/camera/pubspec.yaml | 9 +- .../camera/test/camera_image_stream_test.dart | 82 ++-- .../camera/camera/test/camera_image_test.dart | 45 +- .../camera/test/camera_preview_test.dart | 38 +- packages/camera/camera/test/camera_test.dart | 434 +++++++++--------- .../camera/camera/test/camera_value_test.dart | 26 +- .../test/utils/method_channel_mock.dart | 18 +- script/configs/custom_analysis.yaml | 1 - 18 files changed, 563 insertions(+), 504 deletions(-) delete mode 100644 packages/camera/camera/analysis_options.yaml diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index e92a7a815625..d2b2f907a6fb 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,19 +1,20 @@ -## NEXT +## 0.9.4+12 * Skips unnecessary AppDelegate setup for unit tests on iOS. - +* Internal code cleanup for stricter analysis options. + ## 0.9.4+11 -* Manages iOS camera's orientation-related states on a background queue to prevent potential race conditions. +* Manages iOS camera's orientation-related states on a background queue to prevent potential race conditions. ## 0.9.4+10 -* iOS performance improvement by moving file writing from the main queue to a background IO queue. +* iOS performance improvement by moving file writing from the main queue to a background IO queue. ## 0.9.4+9 -* iOS performance improvement by moving sample buffer handling from the main queue to a background session queue. -* Minor iOS internal code cleanup related to camera class and its delegate. +* iOS performance improvement by moving sample buffer handling from the main queue to a background session queue. +* Minor iOS internal code cleanup related to camera class and its delegate. * Minor iOS internal code cleanup related to resolution preset, video format, focus mode, exposure mode and device orientation. * Minor iOS internal code cleanup related to flash mode. @@ -23,12 +24,12 @@ ## 0.9.4+7 -* Fixes a crash in iOS when passing null queue pointer into AVFoundation API due to race condition. +* Fixes a crash in iOS when passing null queue pointer into AVFoundation API due to race condition. * Minor iOS internal code cleanup related to dispatch queue. ## 0.9.4+6 -* Fixes a crash in iOS when using image stream due to calling Flutter engine API on non-main thread. +* Fixes a crash in iOS when using image stream due to calling Flutter engine API on non-main thread. ## 0.9.4+5 diff --git a/packages/camera/camera/analysis_options.yaml b/packages/camera/camera/analysis_options.yaml deleted file mode 100644 index 5aeb4e7c5e21..000000000000 --- a/packages/camera/camera/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../../analysis_options_legacy.yaml diff --git a/packages/camera/camera/example/integration_test/camera_test.dart b/packages/camera/camera/example/integration_test/camera_test.dart index 3af291afe63b..557f4858acab 100644 --- a/packages/camera/camera/example/integration_test/camera_test.dart +++ b/packages/camera/camera/example/integration_test/camera_test.dart @@ -9,9 +9,9 @@ import 'dart:ui'; import 'package:camera/camera.dart'; import 'package:flutter/painting.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; import 'package:path_provider/path_provider.dart'; import 'package:video_player/video_player.dart'; -import 'package:integration_test/integration_test.dart'; void main() { late Directory testDir; @@ -59,7 +59,7 @@ void main() { 'Capturing photo at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.description.name}'); // Take Picture - final file = await controller.takePicture(); + final XFile file = await controller.takePicture(); // Load picture final File fileImage = File(file.path); @@ -78,9 +78,9 @@ void main() { if (cameras.isEmpty) { return; } - for (CameraDescription cameraDescription in cameras) { + for (final CameraDescription cameraDescription in cameras) { bool previousPresetExactlySupported = true; - for (MapEntry preset + for (final MapEntry preset in presetExpectedSizes.entries) { final CameraController controller = CameraController(cameraDescription, preset.key); @@ -110,7 +110,7 @@ void main() { // Take Video await controller.startVideoRecording(); sleep(const Duration(milliseconds: 300)); - final file = await controller.stopVideoRecording(); + final XFile file = await controller.stopVideoRecording(); // Load video metadata final File videoFile = File(file.path); @@ -132,9 +132,9 @@ void main() { if (cameras.isEmpty) { return; } - for (CameraDescription cameraDescription in cameras) { + for (final CameraDescription cameraDescription in cameras) { bool previousPresetExactlySupported = true; - for (MapEntry preset + for (final MapEntry preset in presetExpectedSizes.entries) { final CameraController controller = CameraController(cameraDescription, preset.key); @@ -191,7 +191,7 @@ void main() { sleep(const Duration(milliseconds: 500)); - final file = await controller.stopVideoRecording(); + final XFile file = await controller.stopVideoRecording(); final int recordingTime = DateTime.now().millisecondsSinceEpoch - recordingStart; @@ -224,7 +224,9 @@ void main() { bool _isDetecting = false; await controller.startImageStream((CameraImage image) { - if (_isDetecting) return; + if (_isDetecting) { + return; + } _isDetecting = true; @@ -252,14 +254,14 @@ void main() { ); await controller.initialize(); - final _completer = Completer(); + final Completer _completer = Completer(); await controller.startImageStream((CameraImage image) { if (!_completer.isCompleted) { - Future(() async { + Future(() async { await controller.stopImageStream(); await controller.dispose(); - }).then((value) { + }).then((Object? value) { _completer.complete(image); }); } @@ -275,7 +277,7 @@ void main() { return; } - var _image = await startStreaming(cameras, null); + CameraImage _image = await startStreaming(cameras, null); expect(_image, isNotNull); expect(_image.format.group, ImageFormatGroup.bgra8888); expect(_image.planes.length, 1); diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index a3a5d1d46391..d47edfed69e2 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -194,7 +194,8 @@ class _CameraExampleHomeState extends State behavior: HitTestBehavior.opaque, onScaleStart: _handleScaleStart, onScaleUpdate: _handleScaleUpdate, - onTapDown: (details) => onViewFinderTap(details, constraints), + onTapDown: (TapDownDetails details) => + onViewFinderTap(details, constraints), ); }), ), @@ -228,34 +229,34 @@ class _CameraExampleHomeState extends State child: Row( mainAxisSize: MainAxisSize.min, children: [ - localVideoController == null && imageFile == null - ? Container() - : SizedBox( - child: (localVideoController == null) - ? ( - // The captured image on the web contains a network-accessible URL - // pointing to a location within the browser. It may be displayed - // either with Image.network or Image.memory after loading the image - // bytes to memory. - kIsWeb - ? Image.network(imageFile!.path) - : Image.file(File(imageFile!.path))) - : Container( - child: Center( - child: AspectRatio( - aspectRatio: - localVideoController.value.size != null - ? localVideoController - .value.aspectRatio - : 1.0, - child: VideoPlayer(localVideoController)), - ), - decoration: BoxDecoration( - border: Border.all(color: Colors.pink)), - ), - width: 64.0, - height: 64.0, - ), + if (localVideoController == null && imageFile == null) + Container() + else + SizedBox( + child: (localVideoController == null) + ? ( + // The captured image on the web contains a network-accessible URL + // pointing to a location within the browser. It may be displayed + // either with Image.network or Image.memory after loading the image + // bytes to memory. + kIsWeb + ? Image.network(imageFile!.path) + : Image.file(File(imageFile!.path))) + : Container( + child: Center( + child: AspectRatio( + aspectRatio: + localVideoController.value.size != null + ? localVideoController.value.aspectRatio + : 1.0, + child: VideoPlayer(localVideoController)), + ), + decoration: BoxDecoration( + border: Border.all(color: Colors.pink)), + ), + width: 64.0, + height: 64.0, + ), ], ), ), @@ -265,34 +266,34 @@ class _CameraExampleHomeState extends State /// Display a bar with buttons to change the flash and exposure modes Widget _modeControlRowWidget() { return Column( - children: [ + children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisSize: MainAxisSize.max, children: [ IconButton( - icon: Icon(Icons.flash_on), + icon: const Icon(Icons.flash_on), color: Colors.blue, onPressed: controller != null ? onFlashModeButtonPressed : null, ), // The exposure and focus mode are currently not supported on the web. - ...(!kIsWeb - ? [ + ...!kIsWeb + ? [ IconButton( - icon: Icon(Icons.exposure), + icon: const Icon(Icons.exposure), color: Colors.blue, onPressed: controller != null ? onExposureModeButtonPressed : null, ), IconButton( - icon: Icon(Icons.filter_center_focus), + icon: const Icon(Icons.filter_center_focus), color: Colors.blue, onPressed: controller != null ? onFocusModeButtonPressed : null, ) ] - : []), + : [], IconButton( icon: Icon(enableAudio ? Icons.volume_up : Icons.volume_mute), color: Colors.blue, @@ -323,9 +324,9 @@ class _CameraExampleHomeState extends State child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisSize: MainAxisSize.max, - children: [ + children: [ IconButton( - icon: Icon(Icons.flash_off), + icon: const Icon(Icons.flash_off), color: controller?.value.flashMode == FlashMode.off ? Colors.orange : Colors.blue, @@ -334,7 +335,7 @@ class _CameraExampleHomeState extends State : null, ), IconButton( - icon: Icon(Icons.flash_auto), + icon: const Icon(Icons.flash_auto), color: controller?.value.flashMode == FlashMode.auto ? Colors.orange : Colors.blue, @@ -343,7 +344,7 @@ class _CameraExampleHomeState extends State : null, ), IconButton( - icon: Icon(Icons.flash_on), + icon: const Icon(Icons.flash_on), color: controller?.value.flashMode == FlashMode.always ? Colors.orange : Colors.blue, @@ -352,7 +353,7 @@ class _CameraExampleHomeState extends State : null, ), IconButton( - icon: Icon(Icons.highlight), + icon: const Icon(Icons.highlight), color: controller?.value.flashMode == FlashMode.torch ? Colors.orange : Colors.blue, @@ -384,16 +385,16 @@ class _CameraExampleHomeState extends State child: Container( color: Colors.grey.shade50, child: Column( - children: [ - Center( - child: Text("Exposure Mode"), + children: [ + const Center( + child: Text('Exposure Mode'), ), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisSize: MainAxisSize.max, - children: [ + children: [ TextButton( - child: Text('AUTO'), + child: const Text('AUTO'), style: styleAuto, onPressed: controller != null ? () => @@ -407,7 +408,7 @@ class _CameraExampleHomeState extends State }, ), TextButton( - child: Text('LOCKED'), + child: const Text('LOCKED'), style: styleLocked, onPressed: controller != null ? () => @@ -415,7 +416,7 @@ class _CameraExampleHomeState extends State : null, ), TextButton( - child: Text('RESET OFFSET'), + child: const Text('RESET OFFSET'), style: styleLocked, onPressed: controller != null ? () => controller!.setExposureOffset(0.0) @@ -423,13 +424,13 @@ class _CameraExampleHomeState extends State ), ], ), - Center( - child: Text("Exposure Offset"), + const Center( + child: Text('Exposure Offset'), ), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisSize: MainAxisSize.max, - children: [ + children: [ Text(_minAvailableExposureOffset.toString()), Slider( value: _currentExposureOffset, @@ -469,27 +470,29 @@ class _CameraExampleHomeState extends State child: Container( color: Colors.grey.shade50, child: Column( - children: [ - Center( - child: Text("Focus Mode"), + children: [ + const Center( + child: Text('Focus Mode'), ), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisSize: MainAxisSize.max, - children: [ + children: [ TextButton( - child: Text('AUTO'), + child: const Text('AUTO'), style: styleAuto, onPressed: controller != null ? () => onSetFocusModeButtonPressed(FocusMode.auto) : null, onLongPress: () { - if (controller != null) controller!.setFocusPoint(null); + if (controller != null) { + controller!.setFocusPoint(null); + } showInSnackBar('Resetting focus point'); }, ), TextButton( - child: Text('LOCKED'), + child: const Text('LOCKED'), style: styleLocked, onPressed: controller != null ? () => onSetFocusModeButtonPressed(FocusMode.locked) @@ -533,8 +536,8 @@ class _CameraExampleHomeState extends State IconButton( icon: cameraController != null && cameraController.value.isRecordingPaused - ? Icon(Icons.play_arrow) - : Icon(Icons.pause), + ? const Icon(Icons.play_arrow) + : const Icon(Icons.pause), color: Colors.blue, onPressed: cameraController != null && cameraController.value.isInitialized && @@ -570,7 +573,8 @@ class _CameraExampleHomeState extends State Widget _cameraTogglesRowWidget() { final List toggles = []; - final onChanged = (CameraDescription? description) { + final Null Function(CameraDescription? description) onChanged = + (CameraDescription? description) { if (description == null) { return; } @@ -581,7 +585,7 @@ class _CameraExampleHomeState extends State if (cameras.isEmpty) { return const Text('No camera found'); } else { - for (CameraDescription cameraDescription in cameras) { + for (final CameraDescription cameraDescription in cameras) { toggles.add( SizedBox( width: 90.0, @@ -616,7 +620,7 @@ class _CameraExampleHomeState extends State final CameraController cameraController = controller!; - final offset = Offset( + final Offset offset = Offset( details.localPosition.dx / constraints.maxWidth, details.localPosition.dy / constraints.maxHeight, ); @@ -624,7 +628,7 @@ class _CameraExampleHomeState extends State cameraController.setFocusPoint(offset); } - void onNewCameraSelected(CameraDescription cameraDescription) async { + Future onNewCameraSelected(CameraDescription cameraDescription) async { if (controller != null) { await controller!.dispose(); } @@ -640,7 +644,9 @@ class _CameraExampleHomeState extends State // If the controller is updated then update the UI. cameraController.addListener(() { - if (mounted) setState(() {}); + if (mounted) { + setState(() {}); + } if (cameraController.value.hasError) { showInSnackBar( 'Camera error ${cameraController.value.errorDescription}'); @@ -649,24 +655,23 @@ class _CameraExampleHomeState extends State try { await cameraController.initialize(); - await Future.wait([ + await Future.wait(>[ // The exposure mode is currently not supported on the web. - ...(!kIsWeb - ? [ - cameraController - .getMinExposureOffset() - .then((value) => _minAvailableExposureOffset = value), + ...!kIsWeb + ? >[ + cameraController.getMinExposureOffset().then( + (double value) => _minAvailableExposureOffset = value), cameraController .getMaxExposureOffset() - .then((value) => _maxAvailableExposureOffset = value) + .then((double value) => _maxAvailableExposureOffset = value) ] - : []), + : >[], cameraController .getMaxZoomLevel() - .then((value) => _maxAvailableZoom = value), + .then((double value) => _maxAvailableZoom = value), cameraController .getMinZoomLevel() - .then((value) => _minAvailableZoom = value), + .then((double value) => _minAvailableZoom = value), ]); } on CameraException catch (e) { _showCameraException(e); @@ -685,7 +690,9 @@ class _CameraExampleHomeState extends State videoController?.dispose(); videoController = null; }); - if (file != null) showInSnackBar('Picture saved to ${file.path}'); + if (file != null) { + showInSnackBar('Picture saved to ${file.path}'); + } } }); } @@ -727,7 +734,7 @@ class _CameraExampleHomeState extends State } } - void onCaptureOrientationLockButtonPressed() async { + Future onCaptureOrientationLockButtonPressed() async { try { if (controller != null) { final CameraController cameraController = controller!; @@ -747,34 +754,44 @@ class _CameraExampleHomeState extends State void onSetFlashModeButtonPressed(FlashMode mode) { setFlashMode(mode).then((_) { - if (mounted) setState(() {}); + if (mounted) { + setState(() {}); + } showInSnackBar('Flash mode set to ${mode.toString().split('.').last}'); }); } void onSetExposureModeButtonPressed(ExposureMode mode) { setExposureMode(mode).then((_) { - if (mounted) setState(() {}); + if (mounted) { + setState(() {}); + } showInSnackBar('Exposure mode set to ${mode.toString().split('.').last}'); }); } void onSetFocusModeButtonPressed(FocusMode mode) { setFocusMode(mode).then((_) { - if (mounted) setState(() {}); + if (mounted) { + setState(() {}); + } showInSnackBar('Focus mode set to ${mode.toString().split('.').last}'); }); } void onVideoRecordButtonPressed() { startVideoRecording().then((_) { - if (mounted) setState(() {}); + if (mounted) { + setState(() {}); + } }); } void onStopButtonPressed() { - stopVideoRecording().then((file) { - if (mounted) setState(() {}); + stopVideoRecording().then((XFile? file) { + if (mounted) { + setState(() {}); + } if (file != null) { showInSnackBar('Video recorded to ${file.path}'); videoFile = file; @@ -797,19 +814,25 @@ class _CameraExampleHomeState extends State await cameraController.pausePreview(); } - if (mounted) setState(() {}); + if (mounted) { + setState(() {}); + } } void onPauseButtonPressed() { pauseVideoRecording().then((_) { - if (mounted) setState(() {}); + if (mounted) { + setState(() {}); + } showInSnackBar('Video recording paused'); }); } void onResumeButtonPressed() { resumeVideoRecording().then((_) { - if (mounted) setState(() {}); + if (mounted) { + setState(() {}); + } showInSnackBar('Video recording resumed'); }); } @@ -854,7 +877,7 @@ class _CameraExampleHomeState extends State final CameraController? cameraController = controller; if (cameraController == null || !cameraController.value.isRecordingVideo) { - return null; + return; } try { @@ -869,7 +892,7 @@ class _CameraExampleHomeState extends State final CameraController? cameraController = controller; if (cameraController == null || !cameraController.value.isRecordingVideo) { - return null; + return; } try { @@ -947,7 +970,9 @@ class _CameraExampleHomeState extends State videoPlayerListener = () { if (videoController != null && videoController!.value.size != null) { // Refreshing the state to update video player with the correct ratio. - if (mounted) setState(() {}); + if (mounted) { + setState(() {}); + } videoController!.removeListener(videoPlayerListener!); } }; @@ -977,7 +1002,7 @@ class _CameraExampleHomeState extends State } try { - XFile file = await cameraController.takePicture(); + final XFile file = await cameraController.takePicture(); return file; } on CameraException catch (e) { _showCameraException(e); @@ -1000,7 +1025,7 @@ class CameraApp extends StatelessWidget { } } -List cameras = []; +List cameras = []; Future main() async { // Fetch the available cameras before initializing the app. diff --git a/packages/camera/camera/example/pubspec.yaml b/packages/camera/camera/example/pubspec.yaml index 1899835aca50..1700074f1f88 100644 --- a/packages/camera/camera/example/pubspec.yaml +++ b/packages/camera/camera/example/pubspec.yaml @@ -14,19 +14,18 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - path_provider: ^2.0.0 flutter: sdk: flutter + path_provider: ^2.0.0 video_player: ^2.1.4 dev_dependencies: - flutter_test: - sdk: flutter flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter - pedantic: ^1.10.0 flutter: uses-material-design: true diff --git a/packages/camera/camera/example/test_driver/integration_test.dart b/packages/camera/camera/example/test_driver/integration_test.dart index dedb2537fb88..4ec97e66d36c 100644 --- a/packages/camera/camera/example/test_driver/integration_test.dart +++ b/packages/camera/camera/example/test_driver/integration_test.dart @@ -18,7 +18,7 @@ Future main() async { final bool adbExists = Process.runSync('which', ['adb']).exitCode == 0; if (!adbExists) { - print('This test needs ADB to exist on the \$PATH. Skipping...'); + print(r'This test needs ADB to exist on the $PATH. Skipping...'); exit(0); } print('Granting camera permissions...'); @@ -59,6 +59,6 @@ Future main() async { 'android.permission.RECORD_AUDIO' ]); - final Map result = jsonDecode(data); + final Map result = jsonDecode(data) as Map; exit(result['result'] == 'true' ? 0 : 1); } diff --git a/packages/camera/camera/lib/camera.dart b/packages/camera/camera/lib/camera.dart index 1e24efbd3dc6..900c2633a5d7 100644 --- a/packages/camera/camera/lib/camera.dart +++ b/packages/camera/camera/lib/camera.dart @@ -2,10 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -export 'src/camera_controller.dart'; -export 'src/camera_image.dart'; -export 'src/camera_preview.dart'; - export 'package:camera_platform_interface/camera_platform_interface.dart' show CameraDescription, @@ -17,3 +13,7 @@ export 'package:camera_platform_interface/camera_platform_interface.dart' ResolutionPreset, XFile, ImageFormatGroup; + +export 'src/camera_controller.dart'; +export 'src/camera_image.dart'; +export 'src/camera_preview.dart'; diff --git a/packages/camera/camera/lib/src/camera_controller.dart b/packages/camera/camera/lib/src/camera_controller.dart index 8cf1e90e36c1..835ac1680c6e 100644 --- a/packages/camera/camera/lib/src/camera_controller.dart +++ b/packages/camera/camera/lib/src/camera_controller.dart @@ -10,15 +10,16 @@ import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:pedantic/pedantic.dart'; import 'package:quiver/core.dart'; -final MethodChannel _channel = const MethodChannel('plugins.flutter.io/camera'); +const MethodChannel _channel = MethodChannel('plugins.flutter.io/camera'); /// Signature for a callback receiving the a camera image. /// /// This is used by [CameraController.startImageStream]. -// ignore: inference_failure_on_function_return_type +// TODO(stuartmorgan): Fix this naming the next time there's a breaking change +// to this package. +// ignore: camel_case_types typedef onLatestImageAvailable = Function(CameraImage image); /// Completes with a list of available cameras. @@ -192,7 +193,7 @@ class CameraValue { @override String toString() { - return '$runtimeType(' + return '${objectRuntimeType(this, 'CameraValue')}(' 'isRecordingVideo: $isRecordingVideo, ' 'isInitialized: $isInitialized, ' 'errorDescription: $errorDescription, ' @@ -254,7 +255,8 @@ class CameraController extends ValueNotifier { bool _isDisposed = false; StreamSubscription? _imageStreamSubscription; FutureOr? _initCalled; - StreamSubscription? _deviceOrientationSubscription; + StreamSubscription? + _deviceOrientationSubscription; /// Checks whether [CameraController.dispose] has completed successfully. /// @@ -277,10 +279,12 @@ class CameraController extends ValueNotifier { ); } try { - Completer _initializeCompleter = Completer(); + final Completer _initializeCompleter = + Completer(); - _deviceOrientationSubscription = - CameraPlatform.instance.onDeviceOrientationChanged().listen((event) { + _deviceOrientationSubscription = CameraPlatform.instance + .onDeviceOrientationChanged() + .listen((DeviceOrientationChangedEvent event) { value = value.copyWith( deviceOrientation: event.orientation, ); @@ -295,7 +299,7 @@ class CameraController extends ValueNotifier { unawaited(CameraPlatform.instance .onCameraInitialized(_cameraId) .first - .then((event) { + .then((CameraInitializedEvent event) { _initializeCompleter.complete(event); })); @@ -312,13 +316,13 @@ class CameraController extends ValueNotifier { event.previewHeight, )), exposureMode: await _initializeCompleter.future - .then((event) => event.exposureMode), - focusMode: - await _initializeCompleter.future.then((event) => event.focusMode), - exposurePointSupported: await _initializeCompleter.future - .then((event) => event.exposurePointSupported), + .then((CameraInitializedEvent event) => event.exposureMode), + focusMode: await _initializeCompleter.future + .then((CameraInitializedEvent event) => event.focusMode), + exposurePointSupported: await _initializeCompleter.future.then( + (CameraInitializedEvent event) => event.exposurePointSupported), focusPointSupported: await _initializeCompleter.future - .then((event) => event.focusPointSupported), + .then((CameraInitializedEvent event) => event.focusPointSupported), ); } on PlatformException catch (e) { throw CameraException(e.code, e.message); @@ -351,7 +355,8 @@ class CameraController extends ValueNotifier { await CameraPlatform.instance.pausePreview(_cameraId); value = value.copyWith( isPreviewPaused: true, - previewPauseOrientation: Optional.of(this.value.deviceOrientation)); + previewPauseOrientation: + Optional.of(value.deviceOrientation)); } on PlatformException catch (e) { throw CameraException(e.code, e.message); } @@ -365,7 +370,8 @@ class CameraController extends ValueNotifier { try { await CameraPlatform.instance.resumePreview(_cameraId); value = value.copyWith( - isPreviewPaused: false, previewPauseOrientation: Optional.absent()); + isPreviewPaused: false, + previewPauseOrientation: const Optional.absent()); } on PlatformException catch (e) { throw CameraException(e.code, e.message); } @@ -375,7 +381,7 @@ class CameraController extends ValueNotifier { /// /// Throws a [CameraException] if the capture fails. Future takePicture() async { - _throwIfNotInitialized("takePicture"); + _throwIfNotInitialized('takePicture'); if (value.isTakingPicture) { throw CameraException( 'Previous capture has not returned yet.', @@ -384,7 +390,7 @@ class CameraController extends ValueNotifier { } try { value = value.copyWith(isTakingPicture: true); - XFile file = await CameraPlatform.instance.takePicture(_cameraId); + final XFile file = await CameraPlatform.instance.takePicture(_cameraId); value = value.copyWith(isTakingPicture: false); return file; } on PlatformException catch (e) { @@ -413,7 +419,7 @@ class CameraController extends ValueNotifier { Future startImageStream(onLatestImageAvailable onAvailable) async { assert(defaultTargetPlatform == TargetPlatform.android || defaultTargetPlatform == TargetPlatform.iOS); - _throwIfNotInitialized("startImageStream"); + _throwIfNotInitialized('startImageStream'); if (value.isRecordingVideo) { throw CameraException( 'A video recording is already started.', @@ -438,7 +444,8 @@ class CameraController extends ValueNotifier { _imageStreamSubscription = cameraEventChannel.receiveBroadcastStream().listen( (dynamic imageData) { - onAvailable(CameraImage.fromPlatformData(imageData)); + onAvailable( + CameraImage.fromPlatformData(imageData as Map)); }, ); } @@ -453,7 +460,7 @@ class CameraController extends ValueNotifier { Future stopImageStream() async { assert(defaultTargetPlatform == TargetPlatform.android || defaultTargetPlatform == TargetPlatform.iOS); - _throwIfNotInitialized("stopImageStream"); + _throwIfNotInitialized('stopImageStream'); if (value.isRecordingVideo) { throw CameraException( 'A video recording is already started.', @@ -483,7 +490,7 @@ class CameraController extends ValueNotifier { /// The video is returned as a [XFile] after calling [stopVideoRecording]. /// Throws a [CameraException] if the capture fails. Future startVideoRecording() async { - _throwIfNotInitialized("startVideoRecording"); + _throwIfNotInitialized('startVideoRecording'); if (value.isRecordingVideo) { throw CameraException( 'A video recording is already started.', @@ -502,7 +509,7 @@ class CameraController extends ValueNotifier { value = value.copyWith( isRecordingVideo: true, isRecordingPaused: false, - recordingOrientation: Optional.fromNullable( + recordingOrientation: Optional.fromNullable( value.lockedCaptureOrientation ?? value.deviceOrientation)); } on PlatformException catch (e) { throw CameraException(e.code, e.message); @@ -513,7 +520,7 @@ class CameraController extends ValueNotifier { /// /// Throws a [CameraException] if the capture failed. Future stopVideoRecording() async { - _throwIfNotInitialized("stopVideoRecording"); + _throwIfNotInitialized('stopVideoRecording'); if (!value.isRecordingVideo) { throw CameraException( 'No video is recording', @@ -521,10 +528,11 @@ class CameraController extends ValueNotifier { ); } try { - XFile file = await CameraPlatform.instance.stopVideoRecording(_cameraId); + final XFile file = + await CameraPlatform.instance.stopVideoRecording(_cameraId); value = value.copyWith( isRecordingVideo: false, - recordingOrientation: Optional.absent(), + recordingOrientation: const Optional.absent(), ); return file; } on PlatformException catch (e) { @@ -536,7 +544,7 @@ class CameraController extends ValueNotifier { /// /// This feature is only available on iOS and Android sdk 24+. Future pauseVideoRecording() async { - _throwIfNotInitialized("pauseVideoRecording"); + _throwIfNotInitialized('pauseVideoRecording'); if (!value.isRecordingVideo) { throw CameraException( 'No video is recording', @@ -555,7 +563,7 @@ class CameraController extends ValueNotifier { /// /// This feature is only available on iOS and Android sdk 24+. Future resumeVideoRecording() async { - _throwIfNotInitialized("resumeVideoRecording"); + _throwIfNotInitialized('resumeVideoRecording'); if (!value.isRecordingVideo) { throw CameraException( 'No video is recording', @@ -572,7 +580,7 @@ class CameraController extends ValueNotifier { /// Returns a widget showing a live camera preview. Widget buildPreview() { - _throwIfNotInitialized("buildPreview"); + _throwIfNotInitialized('buildPreview'); try { return CameraPlatform.instance.buildPreview(_cameraId); } on PlatformException catch (e) { @@ -582,7 +590,7 @@ class CameraController extends ValueNotifier { /// Gets the maximum supported zoom level for the selected camera. Future getMaxZoomLevel() { - _throwIfNotInitialized("getMaxZoomLevel"); + _throwIfNotInitialized('getMaxZoomLevel'); try { return CameraPlatform.instance.getMaxZoomLevel(_cameraId); } on PlatformException catch (e) { @@ -592,7 +600,7 @@ class CameraController extends ValueNotifier { /// Gets the minimum supported zoom level for the selected camera. Future getMinZoomLevel() { - _throwIfNotInitialized("getMinZoomLevel"); + _throwIfNotInitialized('getMinZoomLevel'); try { return CameraPlatform.instance.getMinZoomLevel(_cameraId); } on PlatformException catch (e) { @@ -606,7 +614,7 @@ class CameraController extends ValueNotifier { /// zoom level returned by the `getMaxZoomLevel`. Throws an `CameraException` /// when an illegal zoom level is suplied. Future setZoomLevel(double zoom) { - _throwIfNotInitialized("setZoomLevel"); + _throwIfNotInitialized('setZoomLevel'); try { return CameraPlatform.instance.setZoomLevel(_cameraId, zoom); } on PlatformException catch (e) { @@ -662,7 +670,7 @@ class CameraController extends ValueNotifier { /// Gets the minimum supported exposure offset for the selected camera in EV units. Future getMinExposureOffset() async { - _throwIfNotInitialized("getMinExposureOffset"); + _throwIfNotInitialized('getMinExposureOffset'); try { return CameraPlatform.instance.getMinExposureOffset(_cameraId); } on PlatformException catch (e) { @@ -672,7 +680,7 @@ class CameraController extends ValueNotifier { /// Gets the maximum supported exposure offset for the selected camera in EV units. Future getMaxExposureOffset() async { - _throwIfNotInitialized("getMaxExposureOffset"); + _throwIfNotInitialized('getMaxExposureOffset'); try { return CameraPlatform.instance.getMaxExposureOffset(_cameraId); } on PlatformException catch (e) { @@ -684,7 +692,7 @@ class CameraController extends ValueNotifier { /// /// Returns 0 when the camera supports using a free value without stepping. Future getExposureOffsetStepSize() async { - _throwIfNotInitialized("getExposureOffsetStepSize"); + _throwIfNotInitialized('getExposureOffsetStepSize'); try { return CameraPlatform.instance.getExposureOffsetStepSize(_cameraId); } on PlatformException catch (e) { @@ -704,21 +712,21 @@ class CameraController extends ValueNotifier { /// /// Returns the (rounded) offset value that was set. Future setExposureOffset(double offset) async { - _throwIfNotInitialized("setExposureOffset"); + _throwIfNotInitialized('setExposureOffset'); // Check if offset is in range - List range = - await Future.wait([getMinExposureOffset(), getMaxExposureOffset()]); + final List range = await Future.wait( + >[getMinExposureOffset(), getMaxExposureOffset()]); if (offset < range[0] || offset > range[1]) { throw CameraException( - "exposureOffsetOutOfBounds", - "The provided exposure offset was outside the supported range for this device.", + 'exposureOffsetOutOfBounds', + 'The provided exposure offset was outside the supported range for this device.', ); } // Round to the closest step if needed - double stepSize = await getExposureOffsetStepSize(); + final double stepSize = await getExposureOffsetStepSize(); if (stepSize > 0) { - double inv = 1.0 / stepSize; + final double inv = 1.0 / stepSize; double roundedOffset = (offset * inv).roundToDouble() / inv; if (roundedOffset > range[1]) { roundedOffset = (offset * inv).floorToDouble() / inv; @@ -743,8 +751,8 @@ class CameraController extends ValueNotifier { await CameraPlatform.instance.lockCaptureOrientation( _cameraId, orientation ?? value.deviceOrientation); value = value.copyWith( - lockedCaptureOrientation: - Optional.fromNullable(orientation ?? value.deviceOrientation)); + lockedCaptureOrientation: Optional.fromNullable( + orientation ?? value.deviceOrientation)); } on PlatformException catch (e) { throw CameraException(e.code, e.message); } @@ -764,7 +772,8 @@ class CameraController extends ValueNotifier { Future unlockCaptureOrientation() async { try { await CameraPlatform.instance.unlockCaptureOrientation(_cameraId); - value = value.copyWith(lockedCaptureOrientation: Optional.absent()); + value = value.copyWith( + lockedCaptureOrientation: const Optional.absent()); } on PlatformException catch (e) { throw CameraException(e.code, e.message); } diff --git a/packages/camera/camera/lib/src/camera_image.dart b/packages/camera/camera/lib/src/camera_image.dart index 43fa763bed48..fd3a3d6233bc 100644 --- a/packages/camera/camera/lib/src/camera_image.dart +++ b/packages/camera/camera/lib/src/camera_image.dart @@ -4,9 +4,9 @@ import 'dart:typed_data'; +import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:camera_platform_interface/camera_platform_interface.dart'; /// A single color plane of image data. /// @@ -14,11 +14,11 @@ import 'package:camera_platform_interface/camera_platform_interface.dart'; /// format of the Image. class Plane { Plane._fromPlatformData(Map data) - : bytes = data['bytes'], - bytesPerPixel = data['bytesPerPixel'], - bytesPerRow = data['bytesPerRow'], - height = data['height'], - width = data['width']; + : bytes = data['bytes'] as Uint8List, + bytesPerPixel = data['bytesPerPixel'] as int?, + bytesPerRow = data['bytesPerRow'] as int, + height = data['height'] as int?, + width = data['width'] as int?; /// Bytes representing this plane. final Uint8List bytes; @@ -98,13 +98,14 @@ class CameraImage { /// CameraImage Constructor CameraImage.fromPlatformData(Map data) : format = ImageFormat._fromPlatformData(data['format']), - height = data['height'], - width = data['width'], - lensAperture = data['lensAperture'], - sensorExposureTime = data['sensorExposureTime'], - sensorSensitivity = data['sensorSensitivity'], - planes = List.unmodifiable(data['planes'] - .map((dynamic planeData) => Plane._fromPlatformData(planeData))); + height = data['height'] as int, + width = data['width'] as int, + lensAperture = data['lensAperture'] as double?, + sensorExposureTime = data['sensorExposureTime'] as int?, + sensorSensitivity = data['sensorSensitivity'] as double?, + planes = List.unmodifiable((data['planes'] as List) + .map((dynamic planeData) => + Plane._fromPlatformData(planeData as Map))); /// Format of the image provided. /// diff --git a/packages/camera/camera/lib/src/camera_preview.dart b/packages/camera/camera/lib/src/camera_preview.dart index 5faa69f3cb9d..a9b3f2143b49 100644 --- a/packages/camera/camera/lib/src/camera_preview.dart +++ b/packages/camera/camera/lib/src/camera_preview.dart @@ -21,16 +21,16 @@ class CameraPreview extends StatelessWidget { @override Widget build(BuildContext context) { return controller.value.isInitialized - ? ValueListenableBuilder( + ? ValueListenableBuilder( valueListenable: controller, - builder: (context, value, child) { + builder: (BuildContext context, Object? value, Widget? child) { return AspectRatio( aspectRatio: _isLandscape() ? controller.value.aspectRatio : (1 / controller.value.aspectRatio), child: Stack( fit: StackFit.expand, - children: [ + children: [ _wrapInRotatedBox(child: controller.buildPreview()), child ?? Container(), ], @@ -54,12 +54,14 @@ class CameraPreview extends StatelessWidget { } bool _isLandscape() { - return [DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight] - .contains(_getApplicableOrientation()); + return [ + DeviceOrientation.landscapeLeft, + DeviceOrientation.landscapeRight + ].contains(_getApplicableOrientation()); } int _getQuarterTurns() { - Map turns = { + final Map turns = { DeviceOrientation.portraitUp: 0, DeviceOrientation.landscapeRight: 1, DeviceOrientation.portraitDown: 2, diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index f522608f8744..7421a7738ddd 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+11 +version: 0.9.4+12 environment: sdk: ">=2.14.0 <3.0.0" @@ -26,15 +26,14 @@ dependencies: camera_web: ^0.2.1 flutter: sdk: flutter - pedantic: ^1.10.0 - quiver: ^3.0.0 flutter_plugin_android_lifecycle: ^2.0.2 + quiver: ^3.0.0 dev_dependencies: - flutter_test: - sdk: flutter flutter_driver: sdk: flutter + flutter_test: + sdk: flutter mockito: ^5.0.0 plugin_platform_interface: ^2.0.0 video_player: ^2.0.0 diff --git a/packages/camera/camera/test/camera_image_stream_test.dart b/packages/camera/camera/test/camera_image_stream_test.dart index 840770d1eed7..7055b2239a5a 100644 --- a/packages/camera/camera/test/camera_image_stream_test.dart +++ b/packages/camera/camera/test/camera_image_stream_test.dart @@ -17,24 +17,24 @@ void main() { }); test('startImageStream() throws $CameraException when uninitialized', () { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), ResolutionPreset.max); expect( - () => cameraController.startImageStream((image) => null), + () => cameraController.startImageStream((CameraImage image) => null), throwsA( isA() .having( - (error) => error.code, + (CameraException error) => error.code, 'code', 'Uninitialized CameraController', ) .having( - (error) => error.description, + (CameraException error) => error.description, 'description', 'startImageStream() was called on an uninitialized CameraController.', ), @@ -44,8 +44,8 @@ void main() { test('startImageStream() throws $CameraException when recording videos', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -57,9 +57,9 @@ void main() { cameraController.value.copyWith(isRecordingVideo: true); expect( - () => cameraController.startImageStream((image) => null), + () => cameraController.startImageStream((CameraImage image) => null), throwsA(isA().having( - (error) => error.description, + (CameraException error) => error.description, 'A video recording is already started.', 'startImageStream was called while a video is being recorded.', ))); @@ -67,8 +67,8 @@ void main() { test( 'startImageStream() throws $CameraException when already streaming images', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -78,31 +78,31 @@ void main() { cameraController.value = cameraController.value.copyWith(isStreamingImages: true); expect( - () => cameraController.startImageStream((image) => null), + () => cameraController.startImageStream((CameraImage image) => null), throwsA(isA().having( - (error) => error.description, + (CameraException error) => error.description, 'A camera has started streaming images.', 'startImageStream was called while a camera was streaming images.', ))); }); test('startImageStream() calls CameraPlatform', () async { - MethodChannelMock cameraChannelMock = MethodChannelMock( + final MethodChannelMock cameraChannelMock = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: {'startImageStream': {}}); - MethodChannelMock streamChannelMock = MethodChannelMock( + methods: {'startImageStream': {}}); + final MethodChannelMock streamChannelMock = MethodChannelMock( channelName: 'plugins.flutter.io/camera/imageStream', - methods: {'listen': {}}); + methods: {'listen': {}}); - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), ResolutionPreset.max); await cameraController.initialize(); - await cameraController.startImageStream((image) => null); + await cameraController.startImageStream((CameraImage image) => null); expect(cameraChannelMock.log, [isMethodCall('startImageStream', arguments: null)]); @@ -111,8 +111,8 @@ void main() { }); test('stopImageStream() throws $CameraException when uninitialized', () { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -123,12 +123,12 @@ void main() { throwsA( isA() .having( - (error) => error.code, + (CameraException error) => error.code, 'code', 'Uninitialized CameraController', ) .having( - (error) => error.description, + (CameraException error) => error.description, 'description', 'stopImageStream() was called on an uninitialized CameraController.', ), @@ -138,21 +138,21 @@ void main() { test('stopImageStream() throws $CameraException when recording videos', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), ResolutionPreset.max); await cameraController.initialize(); - await cameraController.startImageStream((image) => null); + await cameraController.startImageStream((CameraImage image) => null); cameraController.value = cameraController.value.copyWith(isRecordingVideo: true); expect( cameraController.stopImageStream, throwsA(isA().having( - (error) => error.description, + (CameraException error) => error.description, 'A video recording is already started.', 'stopImageStream was called while a video is being recorded.', ))); @@ -160,8 +160,8 @@ void main() { test('stopImageStream() throws $CameraException when not streaming images', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -171,28 +171,34 @@ void main() { expect( cameraController.stopImageStream, throwsA(isA().having( - (error) => error.description, + (CameraException error) => error.description, 'No camera is streaming images', 'stopImageStream was called when no camera is streaming images.', ))); }); test('stopImageStream() intended behaviour', () async { - MethodChannelMock cameraChannelMock = MethodChannelMock( + final MethodChannelMock cameraChannelMock = MethodChannelMock( channelName: 'plugins.flutter.io/camera', - methods: {'startImageStream': {}, 'stopImageStream': {}}); - MethodChannelMock streamChannelMock = MethodChannelMock( + methods: { + 'startImageStream': {}, + 'stopImageStream': {} + }); + final MethodChannelMock streamChannelMock = MethodChannelMock( channelName: 'plugins.flutter.io/camera/imageStream', - methods: {'listen': {}, 'cancel': {}}); + methods: { + 'listen': {}, + 'cancel': {} + }); - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), ResolutionPreset.max); await cameraController.initialize(); - await cameraController.startImageStream((image) => null); + await cameraController.startImageStream((CameraImage image) => null); await cameraController.stopImageStream(); expect(cameraChannelMock.log, [ diff --git a/packages/camera/camera/test/camera_image_test.dart b/packages/camera/camera/test/camera_image_test.dart index 85d613f41485..b09a14177121 100644 --- a/packages/camera/camera/test/camera_image_test.dart +++ b/packages/camera/camera/test/camera_image_test.dart @@ -14,16 +14,17 @@ void main() { group('$CameraImage tests', () { test('$CameraImage can be created', () { debugDefaultTargetPlatformOverride = TargetPlatform.android; - CameraImage cameraImage = CameraImage.fromPlatformData({ + final CameraImage cameraImage = + CameraImage.fromPlatformData({ 'format': 35, 'height': 1, 'width': 4, 'lensAperture': 1.8, 'sensorExposureTime': 9991324, 'sensorSensitivity': 92.0, - 'planes': [ - { - 'bytes': Uint8List.fromList([1, 2, 3, 4]), + 'planes': [ + { + 'bytes': Uint8List.fromList([1, 2, 3, 4]), 'bytesPerPixel': 1, 'bytesPerRow': 4, 'height': 1, @@ -40,16 +41,17 @@ void main() { test('$CameraImage has ImageFormatGroup.yuv420 for iOS', () { debugDefaultTargetPlatformOverride = TargetPlatform.iOS; - CameraImage cameraImage = CameraImage.fromPlatformData({ + final CameraImage cameraImage = + CameraImage.fromPlatformData({ 'format': 875704438, 'height': 1, 'width': 4, 'lensAperture': 1.8, 'sensorExposureTime': 9991324, 'sensorSensitivity': 92.0, - 'planes': [ - { - 'bytes': Uint8List.fromList([1, 2, 3, 4]), + 'planes': [ + { + 'bytes': Uint8List.fromList([1, 2, 3, 4]), 'bytesPerPixel': 1, 'bytesPerRow': 4, 'height': 1, @@ -63,16 +65,17 @@ void main() { test('$CameraImage has ImageFormatGroup.yuv420 for Android', () { debugDefaultTargetPlatformOverride = TargetPlatform.android; - CameraImage cameraImage = CameraImage.fromPlatformData({ + final CameraImage cameraImage = + CameraImage.fromPlatformData({ 'format': 35, 'height': 1, 'width': 4, 'lensAperture': 1.8, 'sensorExposureTime': 9991324, 'sensorSensitivity': 92.0, - 'planes': [ - { - 'bytes': Uint8List.fromList([1, 2, 3, 4]), + 'planes': [ + { + 'bytes': Uint8List.fromList([1, 2, 3, 4]), 'bytesPerPixel': 1, 'bytesPerRow': 4, 'height': 1, @@ -86,16 +89,17 @@ void main() { test('$CameraImage has ImageFormatGroup.bgra8888 for iOS', () { debugDefaultTargetPlatformOverride = TargetPlatform.iOS; - CameraImage cameraImage = CameraImage.fromPlatformData({ + final CameraImage cameraImage = + CameraImage.fromPlatformData({ 'format': 1111970369, 'height': 1, 'width': 4, 'lensAperture': 1.8, 'sensorExposureTime': 9991324, 'sensorSensitivity': 92.0, - 'planes': [ - { - 'bytes': Uint8List.fromList([1, 2, 3, 4]), + 'planes': [ + { + 'bytes': Uint8List.fromList([1, 2, 3, 4]), 'bytesPerPixel': 1, 'bytesPerRow': 4, 'height': 1, @@ -106,16 +110,17 @@ void main() { expect(cameraImage.format.group, ImageFormatGroup.bgra8888); }); test('$CameraImage has ImageFormatGroup.unknown', () { - CameraImage cameraImage = CameraImage.fromPlatformData({ + final CameraImage cameraImage = + CameraImage.fromPlatformData({ 'format': null, 'height': 1, 'width': 4, 'lensAperture': 1.8, 'sensorExposureTime': 9991324, 'sensorSensitivity': 92.0, - 'planes': [ - { - 'bytes': Uint8List.fromList([1, 2, 3, 4]), + 'planes': [ + { + 'bytes': Uint8List.fromList([1, 2, 3, 4]), 'bytesPerPixel': 1, 'bytesPerRow': 4, 'height': 1, diff --git a/packages/camera/camera/test/camera_preview_test.dart b/packages/camera/camera/test/camera_preview_test.dart index 32718f4d5169..76bfe40605d7 100644 --- a/packages/camera/camera/test/camera_preview_test.dart +++ b/packages/camera/camera/test/camera_preview_test.dart @@ -23,7 +23,7 @@ class FakeController extends ValueNotifier @override Widget buildPreview() { - return Texture(textureId: CameraController.kUninitializedCameraId); + return const Texture(textureId: CameraController.kUninitializedCameraId); } @override @@ -33,7 +33,7 @@ class FakeController extends ValueNotifier void debugCheckIsDisposed() {} @override - CameraDescription get description => CameraDescription( + CameraDescription get description => const CameraDescription( name: '', lensDirection: CameraLensDirection.back, sensorOrientation: 0); @override @@ -97,7 +97,7 @@ class FakeController extends ValueNotifier Future setZoomLevel(double zoom) async {} @override - Future startImageStream(onAvailable) async {} + Future startImageStream(onLatestImageAvailable onAvailable) async {} @override Future startVideoRecording() async {} @@ -136,10 +136,11 @@ void main() { isRecordingVideo: true, deviceOrientation: DeviceOrientation.portraitUp, lockedCaptureOrientation: - Optional.fromNullable(DeviceOrientation.landscapeRight), - recordingOrientation: - Optional.fromNullable(DeviceOrientation.landscapeLeft), - previewSize: Size(480, 640), + const Optional.fromNullable( + DeviceOrientation.landscapeRight), + recordingOrientation: const Optional.fromNullable( + DeviceOrientation.landscapeLeft), + previewSize: const Size(480, 640), ); await tester.pumpWidget( @@ -150,7 +151,7 @@ void main() { ); expect(find.byType(RotatedBox), findsOneWidget); - RotatedBox rotatedBox = + final RotatedBox rotatedBox = tester.widget(find.byType(RotatedBox)); expect(rotatedBox.quarterTurns, 3); @@ -169,10 +170,11 @@ void main() { isInitialized: true, deviceOrientation: DeviceOrientation.portraitUp, lockedCaptureOrientation: - Optional.fromNullable(DeviceOrientation.landscapeRight), - recordingOrientation: - Optional.fromNullable(DeviceOrientation.landscapeLeft), - previewSize: Size(480, 640), + const Optional.fromNullable( + DeviceOrientation.landscapeRight), + recordingOrientation: const Optional.fromNullable( + DeviceOrientation.landscapeLeft), + previewSize: const Size(480, 640), ); await tester.pumpWidget( @@ -183,7 +185,7 @@ void main() { ); expect(find.byType(RotatedBox), findsOneWidget); - RotatedBox rotatedBox = + final RotatedBox rotatedBox = tester.widget(find.byType(RotatedBox)); expect(rotatedBox.quarterTurns, 1); @@ -202,9 +204,9 @@ void main() { isInitialized: true, deviceOrientation: DeviceOrientation.portraitUp, lockedCaptureOrientation: null, - recordingOrientation: - Optional.fromNullable(DeviceOrientation.landscapeLeft), - previewSize: Size(480, 640), + recordingOrientation: const Optional.fromNullable( + DeviceOrientation.landscapeLeft), + previewSize: const Size(480, 640), ); await tester.pumpWidget( @@ -215,7 +217,7 @@ void main() { ); expect(find.byType(RotatedBox), findsOneWidget); - RotatedBox rotatedBox = + final RotatedBox rotatedBox = tester.widget(find.byType(RotatedBox)); expect(rotatedBox.quarterTurns, 0); @@ -229,7 +231,7 @@ void main() { final FakeController controller = FakeController(); controller.value = controller.value.copyWith( isInitialized: true, - previewSize: Size(480, 640), + previewSize: const Size(480, 640), ); await tester.pumpWidget( diff --git a/packages/camera/camera/test/camera_test.dart b/packages/camera/camera/test/camera_test.dart index 6904e68ef89f..c4e0c9388231 100644 --- a/packages/camera/camera/test/camera_test.dart +++ b/packages/camera/camera/test/camera_test.dart @@ -15,20 +15,21 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -get mockAvailableCameras => [ - CameraDescription( +List get mockAvailableCameras => [ + const CameraDescription( name: 'camBack', lensDirection: CameraLensDirection.back, sensorOrientation: 90), - CameraDescription( + const CameraDescription( name: 'camFront', lensDirection: CameraLensDirection.front, sensorOrientation: 180), ]; -get mockInitializeCamera => 13; +int get mockInitializeCamera => 13; -get mockOnCameraInitializedEvent => CameraInitializedEvent( +CameraInitializedEvent get mockOnCameraInitializedEvent => + const CameraInitializedEvent( 13, 75, 75, @@ -38,16 +39,17 @@ get mockOnCameraInitializedEvent => CameraInitializedEvent( true, ); -get mockOnDeviceOrientationChangedEvent => - DeviceOrientationChangedEvent(DeviceOrientation.portraitUp); +DeviceOrientationChangedEvent get mockOnDeviceOrientationChangedEvent => + const DeviceOrientationChangedEvent(DeviceOrientation.portraitUp); -get mockOnCameraClosingEvent => null; +CameraClosingEvent get mockOnCameraClosingEvent => const CameraClosingEvent(13); -get mockOnCameraErrorEvent => CameraErrorEvent(13, 'closing'); +CameraErrorEvent get mockOnCameraErrorEvent => + const CameraErrorEvent(13, 'closing'); XFile mockTakePicture = XFile('foo/bar.png'); -get mockVideoRecordingXFile => null; +XFile mockVideoRecordingXFile = XFile('foo/bar.mpeg'); bool mockPlatformException = false; @@ -57,7 +59,7 @@ void main() { group('camera', () { test('debugCheckIsDisposed should not throw assertion error when disposed', () { - final MockCameraDescription description = MockCameraDescription(); + const MockCameraDescription description = MockCameraDescription(); final CameraController controller = CameraController( description, ResolutionPreset.low, @@ -70,7 +72,7 @@ void main() { test('debugCheckIsDisposed should throw assertion error when not disposed', () { - final MockCameraDescription description = MockCameraDescription(); + const MockCameraDescription description = MockCameraDescription(); final CameraController controller = CameraController( description, ResolutionPreset.low, @@ -85,7 +87,7 @@ void main() { test('availableCameras() has camera', () async { CameraPlatform.instance = MockCameraPlatform(); - var camList = await availableCameras(); + final List camList = await availableCameras(); expect(camList, equals(mockAvailableCameras)); }); @@ -97,8 +99,8 @@ void main() { }); test('Can be initialized', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -106,13 +108,13 @@ void main() { await cameraController.initialize(); expect(cameraController.value.aspectRatio, 1); - expect(cameraController.value.previewSize, Size(75, 75)); + expect(cameraController.value.previewSize, const Size(75, 75)); expect(cameraController.value.isInitialized, isTrue); }); test('can be disposed', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -120,7 +122,7 @@ void main() { await cameraController.initialize(); expect(cameraController.value.aspectRatio, 1); - expect(cameraController.value.previewSize, Size(75, 75)); + expect(cameraController.value.previewSize, const Size(75, 75)); expect(cameraController.value.isInitialized, isTrue); await cameraController.dispose(); @@ -129,8 +131,8 @@ void main() { }); test('initialize() throws CameraException when disposed', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -138,7 +140,7 @@ void main() { await cameraController.initialize(); expect(cameraController.value.aspectRatio, 1); - expect(cameraController.value.previewSize, Size(75, 75)); + expect(cameraController.value.previewSize, const Size(75, 75)); expect(cameraController.value.isInitialized, isTrue); await cameraController.dispose(); @@ -148,7 +150,7 @@ void main() { expect( cameraController.initialize, throwsA(isA().having( - (error) => error.description, + (CameraException error) => error.description, 'Error description', 'initialize was called on a disposed CameraController', ))); @@ -156,8 +158,8 @@ void main() { test('initialize() throws $CameraException on $PlatformException ', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -168,7 +170,7 @@ void main() { expect( cameraController.initialize, throwsA(isA().having( - (error) => error.description, + (CameraException error) => error.description, 'foo', 'bar', ))); @@ -177,8 +179,8 @@ void main() { test('initialize() sets imageFormat', () async { debugDefaultTargetPlatformOverride = TargetPlatform.android; - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -192,8 +194,8 @@ void main() { }); test('prepareForVideoRecording() calls $CameraPlatform ', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -206,8 +208,8 @@ void main() { }); test('takePicture() throws $CameraException when uninitialized ', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -217,12 +219,12 @@ void main() { throwsA( isA() .having( - (error) => error.code, + (CameraException error) => error.code, 'code', 'Uninitialized CameraController', ) .having( - (error) => error.description, + (CameraException error) => error.description, 'description', 'takePicture() was called on an uninitialized CameraController.', ), @@ -232,8 +234,8 @@ void main() { test('takePicture() throws $CameraException when takePicture is true', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -245,29 +247,29 @@ void main() { expect( cameraController.takePicture(), throwsA(isA().having( - (error) => error.description, + (CameraException error) => error.description, 'Previous capture has not returned yet.', 'takePicture was called before the previous capture returned.', ))); }); test('takePicture() returns $XFile', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), ResolutionPreset.max); await cameraController.initialize(); - XFile xFile = await cameraController.takePicture(); + final XFile xFile = await cameraController.takePicture(); expect(xFile.path, mockTakePicture.path); }); test('takePicture() throws $CameraException on $PlatformException', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -278,7 +280,7 @@ void main() { expect( cameraController.takePicture(), throwsA(isA().having( - (error) => error.description, + (CameraException error) => error.description, 'foo', 'bar', ))); @@ -287,8 +289,8 @@ void main() { test('startVideoRecording() throws $CameraException when uninitialized', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -299,12 +301,12 @@ void main() { throwsA( isA() .having( - (error) => error.code, + (CameraException error) => error.code, 'code', 'Uninitialized CameraController', ) .having( - (error) => error.description, + (CameraException error) => error.description, 'description', 'startVideoRecording() was called on an uninitialized CameraController.', ), @@ -313,8 +315,8 @@ void main() { }); test('startVideoRecording() throws $CameraException when recording videos', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -328,7 +330,7 @@ void main() { expect( cameraController.startVideoRecording(), throwsA(isA().having( - (error) => error.description, + (CameraException error) => error.description, 'A video recording is already started.', 'startVideoRecording was called when a recording is already started.', ))); @@ -337,8 +339,8 @@ void main() { test( 'startVideoRecording() throws $CameraException when already streaming images', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -352,7 +354,7 @@ void main() { expect( cameraController.startVideoRecording(), throwsA(isA().having( - (error) => error.description, + (CameraException error) => error.description, 'A camera has started streaming images.', 'startVideoRecording was called while a camera was streaming images.', ))); @@ -360,8 +362,8 @@ void main() { test('getMaxZoomLevel() throws $CameraException when uninitialized', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -372,12 +374,12 @@ void main() { throwsA( isA() .having( - (error) => error.code, + (CameraException error) => error.code, 'code', 'Uninitialized CameraController', ) .having( - (error) => error.description, + (CameraException error) => error.description, 'description', 'getMaxZoomLevel() was called on an uninitialized CameraController.', ), @@ -386,8 +388,8 @@ void main() { }); test('getMaxZoomLevel() throws $CameraException when disposed', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -401,12 +403,12 @@ void main() { throwsA( isA() .having( - (error) => error.code, + (CameraException error) => error.code, 'code', 'Disposed CameraController', ) .having( - (error) => error.description, + (CameraException error) => error.description, 'description', 'getMaxZoomLevel() was called on a disposed CameraController.', ), @@ -417,8 +419,8 @@ void main() { test( 'getMaxZoomLevel() throws $CameraException when a platform exception occured.', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -434,17 +436,18 @@ void main() { expect( cameraController.getMaxZoomLevel, throwsA(isA() - .having((error) => error.code, 'code', 'TEST_ERROR') .having( - (error) => error.description, + (CameraException error) => error.code, 'code', 'TEST_ERROR') + .having( + (CameraException error) => error.description, 'description', 'This is a test error messge', ))); }); test('getMaxZoomLevel() returns max zoom level.', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -452,16 +455,16 @@ void main() { await cameraController.initialize(); when(CameraPlatform.instance.getMaxZoomLevel(mockInitializeCamera)) - .thenAnswer((_) => Future.value(42.0)); + .thenAnswer((_) => Future.value(42.0)); - final maxZoomLevel = await cameraController.getMaxZoomLevel(); + final double maxZoomLevel = await cameraController.getMaxZoomLevel(); expect(maxZoomLevel, 42.0); }); test('getMinZoomLevel() throws $CameraException when uninitialized', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -472,12 +475,12 @@ void main() { throwsA( isA() .having( - (error) => error.code, + (CameraException error) => error.code, 'code', 'Uninitialized CameraController', ) .having( - (error) => error.description, + (CameraException error) => error.description, 'description', 'getMinZoomLevel() was called on an uninitialized CameraController.', ), @@ -486,8 +489,8 @@ void main() { }); test('getMinZoomLevel() throws $CameraException when disposed', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -501,12 +504,12 @@ void main() { throwsA( isA() .having( - (error) => error.code, + (CameraException error) => error.code, 'code', 'Disposed CameraController', ) .having( - (error) => error.description, + (CameraException error) => error.description, 'description', 'getMinZoomLevel() was called on a disposed CameraController.', ), @@ -517,8 +520,8 @@ void main() { test( 'getMinZoomLevel() throws $CameraException when a platform exception occured.', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -534,17 +537,18 @@ void main() { expect( cameraController.getMinZoomLevel, throwsA(isA() - .having((error) => error.code, 'code', 'TEST_ERROR') .having( - (error) => error.description, + (CameraException error) => error.code, 'code', 'TEST_ERROR') + .having( + (CameraException error) => error.description, 'description', 'This is a test error messge', ))); }); test('getMinZoomLevel() returns max zoom level.', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -552,15 +556,15 @@ void main() { await cameraController.initialize(); when(CameraPlatform.instance.getMinZoomLevel(mockInitializeCamera)) - .thenAnswer((_) => Future.value(42.0)); + .thenAnswer((_) => Future.value(42.0)); - final maxZoomLevel = await cameraController.getMinZoomLevel(); + final double maxZoomLevel = await cameraController.getMinZoomLevel(); expect(maxZoomLevel, 42.0); }); test('setZoomLevel() throws $CameraException when uninitialized', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -571,12 +575,12 @@ void main() { throwsA( isA() .having( - (error) => error.code, + (CameraException error) => error.code, 'code', 'Uninitialized CameraController', ) .having( - (error) => error.description, + (CameraException error) => error.description, 'description', 'setZoomLevel() was called on an uninitialized CameraController.', ), @@ -585,8 +589,8 @@ void main() { }); test('setZoomLevel() throws $CameraException when disposed', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -600,12 +604,12 @@ void main() { throwsA( isA() .having( - (error) => error.code, + (CameraException error) => error.code, 'code', 'Disposed CameraController', ) .having( - (error) => error.description, + (CameraException error) => error.description, 'description', 'setZoomLevel() was called on a disposed CameraController.', ), @@ -616,8 +620,8 @@ void main() { test( 'setZoomLevel() throws $CameraException when a platform exception occured.', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -633,9 +637,10 @@ void main() { expect( () => cameraController.setZoomLevel(42), throwsA(isA() - .having((error) => error.code, 'code', 'TEST_ERROR') .having( - (error) => error.description, + (CameraException error) => error.code, 'code', 'TEST_ERROR') + .having( + (CameraException error) => error.description, 'description', 'This is a test error messge', ))); @@ -646,8 +651,8 @@ void main() { test( 'setZoomLevel() completes and calls method channel with correct value.', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -661,8 +666,8 @@ void main() { }); test('setFlashMode() calls $CameraPlatform', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -678,8 +683,8 @@ void main() { test('setFlashMode() throws $CameraException on $PlatformException', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -699,15 +704,15 @@ void main() { expect( cameraController.setFlashMode(FlashMode.always), throwsA(isA().having( - (error) => error.description, + (CameraException error) => error.description, 'TEST_ERROR', 'This is a test error message', ))); }); test('setExposureMode() calls $CameraPlatform', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -723,8 +728,8 @@ void main() { test('setExposureMode() throws $CameraException on $PlatformException', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -744,32 +749,32 @@ void main() { expect( cameraController.setExposureMode(ExposureMode.auto), throwsA(isA().having( - (error) => error.description, + (CameraException error) => error.description, 'TEST_ERROR', 'This is a test error message', ))); }); test('setExposurePoint() calls $CameraPlatform', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), ResolutionPreset.max); await cameraController.initialize(); - await cameraController.setExposurePoint(Offset(0.5, 0.5)); + await cameraController.setExposurePoint(const Offset(0.5, 0.5)); verify(CameraPlatform.instance.setExposurePoint( - cameraController.cameraId, Point(0.5, 0.5))) + cameraController.cameraId, const Point(0.5, 0.5))) .called(1); }); test('setExposurePoint() throws $CameraException on $PlatformException', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -777,7 +782,7 @@ void main() { await cameraController.initialize(); when(CameraPlatform.instance.setExposurePoint( - cameraController.cameraId, Point(0.5, 0.5))) + cameraController.cameraId, const Point(0.5, 0.5))) .thenThrow( PlatformException( code: 'TEST_ERROR', @@ -787,17 +792,17 @@ void main() { ); expect( - cameraController.setExposurePoint(Offset(0.5, 0.5)), + cameraController.setExposurePoint(const Offset(0.5, 0.5)), throwsA(isA().having( - (error) => error.description, + (CameraException error) => error.description, 'TEST_ERROR', 'This is a test error message', ))); }); test('getMinExposureOffset() calls $CameraPlatform', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -806,7 +811,7 @@ void main() { when(CameraPlatform.instance .getMinExposureOffset(cameraController.cameraId)) - .thenAnswer((_) => Future.value(0.0)); + .thenAnswer((_) => Future.value(0.0)); await cameraController.getMinExposureOffset(); @@ -817,8 +822,8 @@ void main() { test('getMinExposureOffset() throws $CameraException on $PlatformException', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -837,15 +842,15 @@ void main() { expect( cameraController.getMinExposureOffset(), throwsA(isA().having( - (error) => error.description, + (CameraException error) => error.description, 'TEST_ERROR', 'This is a test error message', ))); }); test('getMaxExposureOffset() calls $CameraPlatform', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -854,7 +859,7 @@ void main() { when(CameraPlatform.instance .getMaxExposureOffset(cameraController.cameraId)) - .thenAnswer((_) => Future.value(1.0)); + .thenAnswer((_) => Future.value(1.0)); await cameraController.getMaxExposureOffset(); @@ -865,8 +870,8 @@ void main() { test('getMaxExposureOffset() throws $CameraException on $PlatformException', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -885,15 +890,15 @@ void main() { expect( cameraController.getMaxExposureOffset(), throwsA(isA().having( - (error) => error.description, + (CameraException error) => error.description, 'TEST_ERROR', 'This is a test error message', ))); }); test('getExposureOffsetStepSize() calls $CameraPlatform', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -902,7 +907,7 @@ void main() { when(CameraPlatform.instance .getExposureOffsetStepSize(cameraController.cameraId)) - .thenAnswer((_) => Future.value(0.0)); + .thenAnswer((_) => Future.value(0.0)); await cameraController.getExposureOffsetStepSize(); @@ -914,8 +919,8 @@ void main() { test( 'getExposureOffsetStepSize() throws $CameraException on $PlatformException', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -934,15 +939,15 @@ void main() { expect( cameraController.getExposureOffsetStepSize(), throwsA(isA().having( - (error) => error.description, + (CameraException error) => error.description, 'TEST_ERROR', 'This is a test error message', ))); }); test('setExposureOffset() calls $CameraPlatform', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -970,8 +975,8 @@ void main() { test('setExposureOffset() throws $CameraException on $PlatformException', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -998,7 +1003,7 @@ void main() { expect( cameraController.setExposureOffset(1.0), throwsA(isA().having( - (error) => error.description, + (CameraException error) => error.description, 'TEST_ERROR', 'This is a test error message', ))); @@ -1007,8 +1012,8 @@ void main() { test( 'setExposureOffset() throws $CameraException when offset is out of bounds', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -1036,14 +1041,14 @@ void main() { expect( cameraController.setExposureOffset(3.0), throwsA(isA().having( - (error) => error.description, + (CameraException error) => error.description, 'exposureOffsetOutOfBounds', 'The provided exposure offset was outside the supported range for this device.', ))); expect( cameraController.setExposureOffset(-2.0), throwsA(isA().having( - (error) => error.description, + (CameraException error) => error.description, 'exposureOffsetOutOfBounds', 'The provided exposure offset was outside the supported range for this device.', ))); @@ -1064,8 +1069,8 @@ void main() { }); test('setExposureOffset() rounds offset to nearest step', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -1138,8 +1143,8 @@ void main() { }); test('pausePreview() calls $CameraPlatform', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -1159,8 +1164,8 @@ void main() { test('pausePreview() does not call $CameraPlatform when already paused', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -1178,8 +1183,8 @@ void main() { test('pausePreview() throws $CameraException on $PlatformException', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -1197,15 +1202,15 @@ void main() { expect( cameraController.pausePreview(), throwsA(isA().having( - (error) => error.description, + (CameraException error) => error.description, 'TEST_ERROR', 'This is a test error message', ))); }); test('resumePreview() calls $CameraPlatform', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -1223,8 +1228,8 @@ void main() { test('resumePreview() does not call $CameraPlatform when not paused', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -1242,8 +1247,8 @@ void main() { test('resumePreview() throws $CameraException on $PlatformException', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -1263,15 +1268,15 @@ void main() { expect( cameraController.resumePreview(), throwsA(isA().having( - (error) => error.description, + (CameraException error) => error.description, 'TEST_ERROR', 'This is a test error message', ))); }); test('lockCaptureOrientation() calls $CameraPlatform', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -1297,8 +1302,8 @@ void main() { test( 'lockCaptureOrientation() throws $CameraException on $PlatformException', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -1317,15 +1322,15 @@ void main() { expect( cameraController.lockCaptureOrientation(DeviceOrientation.portraitUp), throwsA(isA().having( - (error) => error.description, + (CameraException error) => error.description, 'TEST_ERROR', 'This is a test error message', ))); }); test('unlockCaptureOrientation() calls $CameraPlatform', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -1343,8 +1348,8 @@ void main() { test( 'unlockCaptureOrientation() throws $CameraException on $PlatformException', () async { - CameraController cameraController = CameraController( - CameraDescription( + final CameraController cameraController = CameraController( + const CameraDescription( name: 'cam', lensDirection: CameraLensDirection.back, sensorOrientation: 90), @@ -1363,7 +1368,7 @@ void main() { expect( cameraController.unlockCaptureOrientation(), throwsA(isA().having( - (error) => error.description, + (CameraException error) => error.description, 'TEST_ERROR', 'This is a test error message', ))); @@ -1381,20 +1386,20 @@ class MockCameraPlatform extends Mock }) async => super.noSuchMethod(Invocation.method( #initializeCamera, - [cameraId], - { + [cameraId], + { #imageFormatGroup: imageFormatGroup, }, )); @override Future dispose(int? cameraId) async { - return super.noSuchMethod(Invocation.method(#dispose, [cameraId])); + return super.noSuchMethod(Invocation.method(#dispose, [cameraId])); } @override Future> availableCameras() => - Future.value(mockAvailableCameras); + Future>.value(mockAvailableCameras); @override Future createCamera( @@ -1404,28 +1409,29 @@ class MockCameraPlatform extends Mock }) => mockPlatformException ? throw PlatformException(code: 'foo', message: 'bar') - : Future.value(mockInitializeCamera); + : Future.value(mockInitializeCamera); @override Stream onCameraInitialized(int cameraId) => - Stream.value(mockOnCameraInitializedEvent); + Stream.value(mockOnCameraInitializedEvent); @override Stream onCameraClosing(int cameraId) => - Stream.value(mockOnCameraClosingEvent); + Stream.value(mockOnCameraClosingEvent); @override Stream onCameraError(int cameraId) => - Stream.value(mockOnCameraErrorEvent); + Stream.value(mockOnCameraErrorEvent); @override Stream onDeviceOrientationChanged() => - Stream.value(mockOnDeviceOrientationChangedEvent); + Stream.value( + mockOnDeviceOrientationChangedEvent); @override Future takePicture(int cameraId) => mockPlatformException ? throw PlatformException(code: 'foo', message: 'bar') - : Future.value(mockTakePicture); + : Future.value(mockTakePicture); @override Future prepareForVideoRecording() async => @@ -1434,87 +1440,91 @@ class MockCameraPlatform extends Mock @override Future startVideoRecording(int cameraId, {Duration? maxVideoDuration}) => - Future.value(mockVideoRecordingXFile); + Future.value(mockVideoRecordingXFile); @override Future lockCaptureOrientation( int? cameraId, DeviceOrientation? orientation) async => - super.noSuchMethod( - Invocation.method(#lockCaptureOrientation, [cameraId, orientation])); + super.noSuchMethod(Invocation.method( + #lockCaptureOrientation, [cameraId, orientation])); @override - Future unlockCaptureOrientation(int? cameraId) async => super - .noSuchMethod(Invocation.method(#unlockCaptureOrientation, [cameraId])); + Future unlockCaptureOrientation(int? cameraId) async => + super.noSuchMethod( + Invocation.method(#unlockCaptureOrientation, [cameraId])); @override Future pausePreview(int? cameraId) async => - super.noSuchMethod(Invocation.method(#pausePreview, [cameraId])); + super.noSuchMethod(Invocation.method(#pausePreview, [cameraId])); @override - Future resumePreview(int? cameraId) async => - super.noSuchMethod(Invocation.method(#resumePreview, [cameraId])); + Future resumePreview(int? cameraId) async => super + .noSuchMethod(Invocation.method(#resumePreview, [cameraId])); @override Future getMaxZoomLevel(int? cameraId) async => super.noSuchMethod( - Invocation.method(#getMaxZoomLevel, [cameraId]), - returnValue: 1.0, - ); + Invocation.method(#getMaxZoomLevel, [cameraId]), + returnValue: Future.value(1.0), + ) as Future; @override Future getMinZoomLevel(int? cameraId) async => super.noSuchMethod( - Invocation.method(#getMinZoomLevel, [cameraId]), - returnValue: 0.0, - ); + Invocation.method(#getMinZoomLevel, [cameraId]), + returnValue: Future.value(0.0), + ) as Future; @override Future setZoomLevel(int? cameraId, double? zoom) async => - super.noSuchMethod(Invocation.method(#setZoomLevel, [cameraId, zoom])); + super.noSuchMethod( + Invocation.method(#setZoomLevel, [cameraId, zoom])); @override Future setFlashMode(int? cameraId, FlashMode? mode) async => - super.noSuchMethod(Invocation.method(#setFlashMode, [cameraId, mode])); + super.noSuchMethod( + Invocation.method(#setFlashMode, [cameraId, mode])); @override Future setExposureMode(int? cameraId, ExposureMode? mode) async => - super.noSuchMethod(Invocation.method(#setExposureMode, [cameraId, mode])); + super.noSuchMethod( + Invocation.method(#setExposureMode, [cameraId, mode])); @override Future setExposurePoint(int? cameraId, Point? point) async => super.noSuchMethod( - Invocation.method(#setExposurePoint, [cameraId, point])); + Invocation.method(#setExposurePoint, [cameraId, point])); @override Future getMinExposureOffset(int? cameraId) async => super.noSuchMethod( - Invocation.method(#getMinExposureOffset, [cameraId]), - returnValue: 0.0, - ); + Invocation.method(#getMinExposureOffset, [cameraId]), + returnValue: Future.value(0.0), + ) as Future; @override Future getMaxExposureOffset(int? cameraId) async => super.noSuchMethod( - Invocation.method(#getMaxExposureOffset, [cameraId]), - returnValue: 1.0, - ); + Invocation.method(#getMaxExposureOffset, [cameraId]), + returnValue: Future.value(1.0), + ) as Future; @override Future getExposureOffsetStepSize(int? cameraId) async => super.noSuchMethod( - Invocation.method(#getExposureOffsetStepSize, [cameraId]), - returnValue: 1.0, - ); + Invocation.method(#getExposureOffsetStepSize, [cameraId]), + returnValue: Future.value(1.0), + ) as Future; @override Future setExposureOffset(int? cameraId, double? offset) async => super.noSuchMethod( - Invocation.method(#setExposureOffset, [cameraId, offset]), - returnValue: 1.0, - ); + Invocation.method(#setExposureOffset, [cameraId, offset]), + returnValue: Future.value(1.0), + ) as Future; } class MockCameraDescription extends CameraDescription { /// Creates a new camera description with the given properties. - MockCameraDescription() + const MockCameraDescription() : super( name: 'Test', lensDirection: CameraLensDirection.back, diff --git a/packages/camera/camera/test/camera_value_test.dart b/packages/camera/camera/test/camera_value_test.dart index 4718d8943c34..62df1fd1a2a1 100644 --- a/packages/camera/camera/test/camera_value_test.dart +++ b/packages/camera/camera/test/camera_value_test.dart @@ -13,7 +13,7 @@ import 'package:flutter_test/flutter_test.dart'; void main() { group('camera_value', () { test('Can be created', () { - var cameraValue = const CameraValue( + const CameraValue cameraValue = CameraValue( isInitialized: false, errorDescription: null, previewSize: Size(10, 10), @@ -36,7 +36,7 @@ void main() { expect(cameraValue, isA()); expect(cameraValue.isInitialized, isFalse); expect(cameraValue.errorDescription, null); - expect(cameraValue.previewSize, Size(10, 10)); + expect(cameraValue.previewSize, const Size(10, 10)); expect(cameraValue.isRecordingPaused, isFalse); expect(cameraValue.isRecordingVideo, isFalse); expect(cameraValue.isTakingPicture, isFalse); @@ -53,7 +53,7 @@ void main() { }); test('Can be created as uninitialized', () { - var cameraValue = const CameraValue.uninitialized(); + const CameraValue cameraValue = CameraValue.uninitialized(); expect(cameraValue, isA()); expect(cameraValue.isInitialized, isFalse); @@ -75,8 +75,8 @@ void main() { }); test('Can be copied with isInitialized', () { - var cv = const CameraValue.uninitialized(); - var cameraValue = cv.copyWith(isInitialized: true); + const CameraValue cv = CameraValue.uninitialized(); + final CameraValue cameraValue = cv.copyWith(isInitialized: true); expect(cameraValue, isA()); expect(cameraValue.isInitialized, isTrue); @@ -98,24 +98,24 @@ void main() { }); test('Has aspectRatio after setting size', () { - var cv = const CameraValue.uninitialized(); - var cameraValue = - cv.copyWith(isInitialized: true, previewSize: Size(20, 10)); + const CameraValue cv = CameraValue.uninitialized(); + final CameraValue cameraValue = + cv.copyWith(isInitialized: true, previewSize: const Size(20, 10)); expect(cameraValue.aspectRatio, 2.0); }); test('hasError is true after setting errorDescription', () { - var cv = const CameraValue.uninitialized(); - var cameraValue = cv.copyWith(errorDescription: 'error'); + const CameraValue cv = CameraValue.uninitialized(); + final CameraValue cameraValue = cv.copyWith(errorDescription: 'error'); expect(cameraValue.hasError, isTrue); expect(cameraValue.errorDescription, 'error'); }); test('Recording paused is false when not recording', () { - var cv = const CameraValue.uninitialized(); - var cameraValue = cv.copyWith( + const CameraValue cv = CameraValue.uninitialized(); + final CameraValue cameraValue = cv.copyWith( isInitialized: true, isRecordingVideo: false, isRecordingPaused: true); @@ -124,7 +124,7 @@ void main() { }); test('toString() works as expected', () { - var cameraValue = const CameraValue( + const CameraValue cameraValue = CameraValue( isInitialized: false, errorDescription: null, previewSize: Size(10, 10), diff --git a/packages/camera/camera/test/utils/method_channel_mock.dart b/packages/camera/camera/test/utils/method_channel_mock.dart index 60d8def6a2e3..7c8b4ca3d3f0 100644 --- a/packages/camera/camera/test/utils/method_channel_mock.dart +++ b/packages/camera/camera/test/utils/method_channel_mock.dart @@ -6,11 +6,6 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; class MethodChannelMock { - final Duration? delay; - final MethodChannel methodChannel; - final Map methods; - final log = []; - MethodChannelMock({ required String channelName, this.delay, @@ -19,7 +14,12 @@ class MethodChannelMock { methodChannel.setMockMethodCallHandler(_handler); } - Future _handler(MethodCall methodCall) async { + final Duration? delay; + final MethodChannel methodChannel; + final Map methods; + final List log = []; + + Future _handler(MethodCall methodCall) async { log.add(methodCall); if (!methods.containsKey(methodCall.method)) { @@ -27,13 +27,13 @@ class MethodChannelMock { '${methodCall.method} on channel ${methodChannel.name}'); } - return Future.delayed(delay ?? Duration.zero, () { - final result = methods[methodCall.method]; + return Future.delayed(delay ?? Duration.zero, () { + final Object? result = methods[methodCall.method]; if (result is Exception) { throw result; } - return Future.value(result); + return Future.value(result); }); } } diff --git a/script/configs/custom_analysis.yaml b/script/configs/custom_analysis.yaml index 493b6e69a54c..7e4d3832056e 100644 --- a/script/configs/custom_analysis.yaml +++ b/script/configs/custom_analysis.yaml @@ -11,7 +11,6 @@ # TODO(ecosystem): Remove everything from this list. See: # https://github.com/flutter/flutter/issues/76229 -- camera/camera - camera/camera_web - google_maps_flutter/google_maps_flutter - google_maps_flutter/google_maps_flutter_platform_interface From 8e1071f05fd72177c57c65de9282f9f21c7f81eb Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 14 Feb 2022 15:05:24 -0500 Subject: [PATCH 186/600] Roll Flutter from 19586569438a to 024d5f8aaa7a (3 revisions) (#4836) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 4765e192a2f0..3baaf1d27201 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -19586569438afdc52fcc0ab7d95edeeb7a2d961f +024d5f8aaa7a4618fd583563bdbfd42f524c0040 From 9bb870eb921d28007754c8ff152b96cbdb013da7 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 14 Feb 2022 15:50:12 -0500 Subject: [PATCH 187/600] [flutter_plugin_tool] Remove podspec --allow-warnings (#4839) --- script/tool/CHANGELOG.md | 3 +- .../tool/lib/src/lint_podspecs_command.dart | 11 +---- script/tool/pubspec.yaml | 3 +- .../tool/test/lint_podspecs_command_test.dart | 42 ------------------- 4 files changed, 4 insertions(+), 55 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index fbf7610b3ea3..8f2807f0dd03 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,4 +1,4 @@ -## NEXT +## 0.8.0 - Ensures that `firebase-test-lab` runs include an `integration_test` runner. - Adds a `make-deps-path-based` command to convert inter-repo package @@ -18,6 +18,7 @@ - Adds support for `CHROME_EXECUTABLE` in `drive-examples` to match similar `flutter` behavior. - Validates `default_package` entries in plugins. +- Removes `allow-warnings` from the `podspecs` command. ## 0.7.3 diff --git a/script/tool/lib/src/lint_podspecs_command.dart b/script/tool/lib/src/lint_podspecs_command.dart index ee44a82da5b9..198dd9472115 100644 --- a/script/tool/lib/src/lint_podspecs_command.dart +++ b/script/tool/lib/src/lint_podspecs_command.dart @@ -26,13 +26,7 @@ class LintPodspecsCommand extends PackageLoopingCommand { Directory packagesDir, { ProcessRunner processRunner = const ProcessRunner(), Platform platform = const LocalPlatform(), - }) : super(packagesDir, processRunner: processRunner, platform: platform) { - argParser.addMultiOption('ignore-warnings', - help: - 'Do not pass --allow-warnings flag to "pod lib lint" for podspecs ' - 'with this basename (example: plugins with known warnings)', - valueHelp: 'podspec_file_name'); - } + }) : super(packagesDir, processRunner: processRunner, platform: platform); @override final String name = 'podspecs'; @@ -118,8 +112,6 @@ class LintPodspecsCommand extends PackageLoopingCommand { Future _runPodLint(String podspecPath, {required bool libraryLint}) async { - final bool allowWarnings = (getStringListArg('ignore-warnings')) - .contains(p.basenameWithoutExtension(podspecPath)); final List arguments = [ 'lib', 'lint', @@ -127,7 +119,6 @@ class LintPodspecsCommand extends PackageLoopingCommand { '--configuration=Debug', // Release targets unsupported arm64 simulators. Use Debug to only build against targeted x86_64 simulator devices. '--skip-tests', '--use-modular-headers', // Flutter sets use_modular_headers! in its templates. - if (allowWarnings) '--allow-warnings', if (libraryLint) '--use-libraries' ]; diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index 4849feee7b85..9ca5e2b77580 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.7.2 +version: 0.8.0 dependencies: args: ^2.1.0 @@ -26,7 +26,6 @@ dev_dependencies: build_runner: ^2.0.3 matcher: ^0.12.10 mockito: ^5.0.7 - pedantic: ^1.11.0 environment: sdk: '>=2.12.0 <3.0.0' diff --git a/script/tool/test/lint_podspecs_command_test.dart b/script/tool/test/lint_podspecs_command_test.dart index 44247274028f..bccbec678666 100644 --- a/script/tool/test/lint_podspecs_command_test.dart +++ b/script/tool/test/lint_podspecs_command_test.dart @@ -123,48 +123,6 @@ void main() { expect(output, contains('Bar')); }); - test('allow warnings for podspecs with known warnings', () async { - final Directory plugin1Dir = createFakePlugin('plugin1', packagesDir, - extraFiles: ['plugin1.podspec']); - - final List output = await runCapturingPrint( - runner, ['podspecs', '--ignore-warnings=plugin1']); - - expect( - processRunner.recordedCalls, - orderedEquals([ - ProcessCall('which', const ['pod'], packagesDir.path), - ProcessCall( - 'pod', - [ - 'lib', - 'lint', - plugin1Dir.childFile('plugin1.podspec').path, - '--configuration=Debug', - '--skip-tests', - '--use-modular-headers', - '--allow-warnings', - '--use-libraries' - ], - packagesDir.path), - ProcessCall( - 'pod', - [ - 'lib', - 'lint', - plugin1Dir.childFile('plugin1.podspec').path, - '--configuration=Debug', - '--skip-tests', - '--use-modular-headers', - '--allow-warnings', - ], - packagesDir.path), - ]), - ); - - expect(output, contains('Linting plugin1.podspec')); - }); - test('fails if pod is missing', () async { createFakePlugin('plugin1', packagesDir, extraFiles: ['plugin1.podspec']); From 83bbd46ed4566a5bf5cb4ef8657ab13ecd2f2a41 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 14 Feb 2022 16:35:21 -0500 Subject: [PATCH 188/600] Roll Flutter from 024d5f8aaa7a to 79df99d18221 (1 revision) (#4840) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 3baaf1d27201..6fed625579c6 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -024d5f8aaa7a4618fd583563bdbfd42f524c0040 +79df99d18221c29e767e1b2da6acbe4deb5a439d From e603abe5ed9ffe43b1094350cfadec452ad5e630 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 14 Feb 2022 18:45:22 -0500 Subject: [PATCH 189/600] Roll Flutter from 79df99d18221 to 4d9e6d9b3ea2 (2 revisions) (#4842) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 6fed625579c6..004992f48c77 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -79df99d18221c29e767e1b2da6acbe4deb5a439d +4d9e6d9b3ea2c6d4b9c87a8ae3231961676a4b62 From ddcadacf30db6a78f5e7a8195c88e750a6a75ab8 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 14 Feb 2022 21:20:22 -0500 Subject: [PATCH 190/600] Roll Flutter from 4d9e6d9b3ea2 to 1ae5dd194c33 (3 revisions) (#4844) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 004992f48c77..e0b5a59565f3 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -4d9e6d9b3ea2c6d4b9c87a8ae3231961676a4b62 +1ae5dd194c3341cba1e0a24533f0434a9180f3a8 From 3752bc773f26736015a90b857c1ce6f52d8660f0 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 14 Feb 2022 22:25:23 -0500 Subject: [PATCH 191/600] Roll Flutter from 1ae5dd194c33 to 381cb280a35c (2 revisions) (#4845) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index e0b5a59565f3..25ccc3de6f4c 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -1ae5dd194c3341cba1e0a24533f0434a9180f3a8 +381cb280a35c9725c5928a08cf497548d6e83b02 From c48a5f87757ddfa08c863c1cbb899fe10e76f5ca Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 15 Feb 2022 04:55:23 -0500 Subject: [PATCH 192/600] Roll Flutter from 381cb280a35c to ca2a751661c8 (5 revisions) (#4850) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 25ccc3de6f4c..16adeff802c9 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -381cb280a35c9725c5928a08cf497548d6e83b02 +ca2a751661c8a1fe71025df3d32bd6ac6ecf221c From 10a59af20f143d52d5a2c471bb9e7e3e79367a1e Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Tue, 15 Feb 2022 09:16:34 -0800 Subject: [PATCH 193/600] [video_player] Updated Pigeon version (#4726) --- .../video_player_android/CHANGELOG.md | 4 + .../video_player_android/CONTRIBUTING.md | 10 +- .../flutter/plugins/videoplayer/Messages.java | 827 ++++++++++++------ .../plugins/videoplayer/VideoPlayer.java | 3 +- .../videoplayer/VideoPlayerPlugin.java | 10 +- .../lib/src/android_video_player.dart | 73 +- .../lib/src/messages.dart | 425 --------- .../lib/src/messages.g.dart | 539 ++++++++++++ .../pigeons/copyright.txt | 3 + .../pigeons/messages.dart | 41 +- .../video_player_android/pubspec.yaml | 4 +- .../test/android_video_player_test.dart | 6 +- .../video_player_android/test/test_api.dart | 254 ++++-- .../video_player_avfoundation/CHANGELOG.md | 4 + .../video_player_avfoundation/CONTRIBUTING.md | 10 +- .../ios/RunnerTests/VideoPlayerTests.m | 28 +- .../ios/Classes/FLTVideoPlayerPlugin.m | 17 +- .../ios/Classes/messages.g.h | 107 +++ .../ios/Classes/messages.g.m | 522 +++++++++++ .../ios/Classes/messages.h | 79 -- .../ios/Classes/messages.m | 379 -------- .../lib/src/avfoundation_video_player.dart | 73 +- .../lib/src/messages.dart | 425 --------- .../lib/src/messages.g.dart | 539 ++++++++++++ .../pigeons/copyright.txt | 3 + .../pigeons/messages.dart | 53 +- .../video_player_avfoundation/pubspec.yaml | 4 +- .../test/avfoundation_video_player_test.dart | 6 +- .../test/test_api.dart | 254 ++++-- 29 files changed, 2838 insertions(+), 1864 deletions(-) delete mode 100644 packages/video_player/video_player_android/lib/src/messages.dart create mode 100644 packages/video_player/video_player_android/lib/src/messages.g.dart create mode 100644 packages/video_player/video_player_android/pigeons/copyright.txt create mode 100644 packages/video_player/video_player_avfoundation/ios/Classes/messages.g.h create mode 100644 packages/video_player/video_player_avfoundation/ios/Classes/messages.g.m delete mode 100644 packages/video_player/video_player_avfoundation/ios/Classes/messages.h delete mode 100644 packages/video_player/video_player_avfoundation/ios/Classes/messages.m delete mode 100644 packages/video_player/video_player_avfoundation/lib/src/messages.dart create mode 100644 packages/video_player/video_player_avfoundation/lib/src/messages.g.dart create mode 100644 packages/video_player/video_player_avfoundation/pigeons/copyright.txt diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index 626b9ef887cf..ec526ee1c7c9 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.0 + +* Updates Pigeon to ^1.0.16. + ## 2.2.17 * Splits from `video_player` as a federated implementation. diff --git a/packages/video_player/video_player_android/CONTRIBUTING.md b/packages/video_player/video_player_android/CONTRIBUTING.md index 387551bda2f6..8dfec9faf809 100644 --- a/packages/video_player/video_player_android/CONTRIBUTING.md +++ b/packages/video_player/video_player_android/CONTRIBUTING.md @@ -1,12 +1,11 @@ ## Updating pigeon-generated files If you update files in the pigeons/ directory, run the following -command in this directory (ignore the errors you get about -dependencies in the examples directory): +command in this directory: ```bash flutter pub upgrade -flutter pub run pigeon --dart_null_safety --input pigeons/messages.dart +flutter pub run pigeon --input pigeons/messages.dart # git commit your changes so that your working environment is clean (cd ../../../; ./script/tool_runner.sh format --clang-format=clang-format-7) ``` @@ -28,6 +27,5 @@ publish pigeon before you can land the updates to this package, since the CI tests run the analysis using latest published version of pigeon, not your version or the version on master. -In either case, the configuration will be obtained automatically from -the `pigeons/messages.dart` file (see `configurePigeon` at the bottom -of that file). +In either case, the configuration will be obtained automatically from the +`pigeons/messages.dart` file (see `ConfigurePigeon` at the top of that file). diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java index e0a4a3b8dd08..0cdf3564d334 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java @@ -1,322 +1,622 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - -// Autogenerated from Pigeon (v0.1.21), do not edit directly. +// Autogenerated from Pigeon (v1.0.16), do not edit directly. // See also: https://pub.dev/packages/pigeon package io.flutter.plugins.videoplayer; +import android.util.Log; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import io.flutter.plugin.common.BasicMessageChannel; import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugin.common.MessageCodec; import io.flutter.plugin.common.StandardMessageCodec; +import java.io.ByteArrayOutputStream; +import java.nio.ByteBuffer; +import java.util.ArrayList; import java.util.HashMap; +import java.util.Map; /** Generated class from Pigeon. */ -@SuppressWarnings("unused") +@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression"}) public class Messages { /** Generated class from Pigeon that represents data sent in messages. */ public static class TextureMessage { - private Long textureId; + private @NonNull Long textureId; - public Long getTextureId() { + public @NonNull Long getTextureId() { return textureId; } - public void setTextureId(Long setterArg) { + public void setTextureId(@NonNull Long setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"textureId\" is null."); + } this.textureId = setterArg; } - HashMap toMap() { - HashMap toMapResult = new HashMap<>(); + /** Constructor is private to enforce null safety; use Builder. */ + private TextureMessage() {} + + public static class Builder { + private @Nullable Long textureId; + + public @NonNull Builder setTextureId(@NonNull Long setterArg) { + this.textureId = setterArg; + return this; + } + + public @NonNull TextureMessage build() { + TextureMessage pigeonReturn = new TextureMessage(); + pigeonReturn.setTextureId(textureId); + return pigeonReturn; + } + } + + @NonNull + Map toMap() { + Map toMapResult = new HashMap<>(); toMapResult.put("textureId", textureId); return toMapResult; } - static TextureMessage fromMap(HashMap map) { - TextureMessage fromMapResult = new TextureMessage(); + static @NonNull TextureMessage fromMap(@NonNull Map map) { + TextureMessage pigeonResult = new TextureMessage(); Object textureId = map.get("textureId"); - fromMapResult.textureId = + pigeonResult.setTextureId( (textureId == null) ? null - : ((textureId instanceof Integer) ? (Integer) textureId : (Long) textureId); - return fromMapResult; + : ((textureId instanceof Integer) ? (Integer) textureId : (Long) textureId)); + return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ - public static class CreateMessage { - private String asset; - - public String getAsset() { - return asset; - } - - public void setAsset(String setterArg) { - this.asset = setterArg; - } - - private String uri; - - public String getUri() { - return uri; - } - - public void setUri(String setterArg) { - this.uri = setterArg; - } - - private String packageName; - - public String getPackageName() { - return packageName; - } - - public void setPackageName(String setterArg) { - this.packageName = setterArg; - } - - private String formatHint; - - public String getFormatHint() { - return formatHint; - } + public static class LoopingMessage { + private @NonNull Long textureId; - public void setFormatHint(String setterArg) { - this.formatHint = setterArg; + public @NonNull Long getTextureId() { + return textureId; } - private HashMap httpHeaders; - - public HashMap getHttpHeaders() { - return httpHeaders; + public void setTextureId(@NonNull Long setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"textureId\" is null."); + } + this.textureId = setterArg; } - public void setHttpHeaders(HashMap setterArg) { - this.httpHeaders = setterArg; - } + private @NonNull Boolean isLooping; - HashMap toMap() { - HashMap toMapResult = new HashMap<>(); - toMapResult.put("asset", asset); - toMapResult.put("uri", uri); - toMapResult.put("packageName", packageName); - toMapResult.put("formatHint", formatHint); - toMapResult.put("httpHeaders", httpHeaders); - return toMapResult; + public @NonNull Boolean getIsLooping() { + return isLooping; } - static CreateMessage fromMap(HashMap map) { - CreateMessage fromMapResult = new CreateMessage(); - Object asset = map.get("asset"); - fromMapResult.asset = (String) asset; - Object uri = map.get("uri"); - fromMapResult.uri = (String) uri; - Object packageName = map.get("packageName"); - fromMapResult.packageName = (String) packageName; - Object formatHint = map.get("formatHint"); - fromMapResult.formatHint = (String) formatHint; - Object httpHeaders = map.get("httpHeaders"); - fromMapResult.httpHeaders = (HashMap) httpHeaders; - return fromMapResult; + public void setIsLooping(@NonNull Boolean setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"isLooping\" is null."); + } + this.isLooping = setterArg; } - } - /** Generated class from Pigeon that represents data sent in messages. */ - public static class LoopingMessage { - private Long textureId; + /** Constructor is private to enforce null safety; use Builder. */ + private LoopingMessage() {} - public Long getTextureId() { - return textureId; - } + public static class Builder { + private @Nullable Long textureId; - public void setTextureId(Long setterArg) { - this.textureId = setterArg; - } + public @NonNull Builder setTextureId(@NonNull Long setterArg) { + this.textureId = setterArg; + return this; + } - private Boolean isLooping; + private @Nullable Boolean isLooping; - public Boolean getIsLooping() { - return isLooping; - } + public @NonNull Builder setIsLooping(@NonNull Boolean setterArg) { + this.isLooping = setterArg; + return this; + } - public void setIsLooping(Boolean setterArg) { - this.isLooping = setterArg; + public @NonNull LoopingMessage build() { + LoopingMessage pigeonReturn = new LoopingMessage(); + pigeonReturn.setTextureId(textureId); + pigeonReturn.setIsLooping(isLooping); + return pigeonReturn; + } } - HashMap toMap() { - HashMap toMapResult = new HashMap<>(); + @NonNull + Map toMap() { + Map toMapResult = new HashMap<>(); toMapResult.put("textureId", textureId); toMapResult.put("isLooping", isLooping); return toMapResult; } - static LoopingMessage fromMap(HashMap map) { - LoopingMessage fromMapResult = new LoopingMessage(); + static @NonNull LoopingMessage fromMap(@NonNull Map map) { + LoopingMessage pigeonResult = new LoopingMessage(); Object textureId = map.get("textureId"); - fromMapResult.textureId = + pigeonResult.setTextureId( (textureId == null) ? null - : ((textureId instanceof Integer) ? (Integer) textureId : (Long) textureId); + : ((textureId instanceof Integer) ? (Integer) textureId : (Long) textureId)); Object isLooping = map.get("isLooping"); - fromMapResult.isLooping = (Boolean) isLooping; - return fromMapResult; + pigeonResult.setIsLooping((Boolean) isLooping); + return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ public static class VolumeMessage { - private Long textureId; + private @NonNull Long textureId; - public Long getTextureId() { + public @NonNull Long getTextureId() { return textureId; } - public void setTextureId(Long setterArg) { + public void setTextureId(@NonNull Long setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"textureId\" is null."); + } this.textureId = setterArg; } - private Double volume; + private @NonNull Double volume; - public Double getVolume() { + public @NonNull Double getVolume() { return volume; } - public void setVolume(Double setterArg) { + public void setVolume(@NonNull Double setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"volume\" is null."); + } this.volume = setterArg; } - HashMap toMap() { - HashMap toMapResult = new HashMap<>(); + /** Constructor is private to enforce null safety; use Builder. */ + private VolumeMessage() {} + + public static class Builder { + private @Nullable Long textureId; + + public @NonNull Builder setTextureId(@NonNull Long setterArg) { + this.textureId = setterArg; + return this; + } + + private @Nullable Double volume; + + public @NonNull Builder setVolume(@NonNull Double setterArg) { + this.volume = setterArg; + return this; + } + + public @NonNull VolumeMessage build() { + VolumeMessage pigeonReturn = new VolumeMessage(); + pigeonReturn.setTextureId(textureId); + pigeonReturn.setVolume(volume); + return pigeonReturn; + } + } + + @NonNull + Map toMap() { + Map toMapResult = new HashMap<>(); toMapResult.put("textureId", textureId); toMapResult.put("volume", volume); return toMapResult; } - static VolumeMessage fromMap(HashMap map) { - VolumeMessage fromMapResult = new VolumeMessage(); + static @NonNull VolumeMessage fromMap(@NonNull Map map) { + VolumeMessage pigeonResult = new VolumeMessage(); Object textureId = map.get("textureId"); - fromMapResult.textureId = + pigeonResult.setTextureId( (textureId == null) ? null - : ((textureId instanceof Integer) ? (Integer) textureId : (Long) textureId); + : ((textureId instanceof Integer) ? (Integer) textureId : (Long) textureId)); Object volume = map.get("volume"); - fromMapResult.volume = (Double) volume; - return fromMapResult; + pigeonResult.setVolume((Double) volume); + return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ public static class PlaybackSpeedMessage { - private Long textureId; + private @NonNull Long textureId; - public Long getTextureId() { + public @NonNull Long getTextureId() { return textureId; } - public void setTextureId(Long setterArg) { + public void setTextureId(@NonNull Long setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"textureId\" is null."); + } this.textureId = setterArg; } - private Double speed; + private @NonNull Double speed; - public Double getSpeed() { + public @NonNull Double getSpeed() { return speed; } - public void setSpeed(Double setterArg) { + public void setSpeed(@NonNull Double setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"speed\" is null."); + } this.speed = setterArg; } - HashMap toMap() { - HashMap toMapResult = new HashMap<>(); + /** Constructor is private to enforce null safety; use Builder. */ + private PlaybackSpeedMessage() {} + + public static class Builder { + private @Nullable Long textureId; + + public @NonNull Builder setTextureId(@NonNull Long setterArg) { + this.textureId = setterArg; + return this; + } + + private @Nullable Double speed; + + public @NonNull Builder setSpeed(@NonNull Double setterArg) { + this.speed = setterArg; + return this; + } + + public @NonNull PlaybackSpeedMessage build() { + PlaybackSpeedMessage pigeonReturn = new PlaybackSpeedMessage(); + pigeonReturn.setTextureId(textureId); + pigeonReturn.setSpeed(speed); + return pigeonReturn; + } + } + + @NonNull + Map toMap() { + Map toMapResult = new HashMap<>(); toMapResult.put("textureId", textureId); toMapResult.put("speed", speed); return toMapResult; } - static PlaybackSpeedMessage fromMap(HashMap map) { - PlaybackSpeedMessage fromMapResult = new PlaybackSpeedMessage(); + static @NonNull PlaybackSpeedMessage fromMap(@NonNull Map map) { + PlaybackSpeedMessage pigeonResult = new PlaybackSpeedMessage(); Object textureId = map.get("textureId"); - fromMapResult.textureId = + pigeonResult.setTextureId( (textureId == null) ? null - : ((textureId instanceof Integer) ? (Integer) textureId : (Long) textureId); + : ((textureId instanceof Integer) ? (Integer) textureId : (Long) textureId)); Object speed = map.get("speed"); - fromMapResult.speed = (Double) speed; - return fromMapResult; + pigeonResult.setSpeed((Double) speed); + return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ public static class PositionMessage { - private Long textureId; + private @NonNull Long textureId; - public Long getTextureId() { + public @NonNull Long getTextureId() { return textureId; } - public void setTextureId(Long setterArg) { + public void setTextureId(@NonNull Long setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"textureId\" is null."); + } this.textureId = setterArg; } - private Long position; + private @NonNull Long position; - public Long getPosition() { + public @NonNull Long getPosition() { return position; } - public void setPosition(Long setterArg) { + public void setPosition(@NonNull Long setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"position\" is null."); + } this.position = setterArg; } - HashMap toMap() { - HashMap toMapResult = new HashMap<>(); + /** Constructor is private to enforce null safety; use Builder. */ + private PositionMessage() {} + + public static class Builder { + private @Nullable Long textureId; + + public @NonNull Builder setTextureId(@NonNull Long setterArg) { + this.textureId = setterArg; + return this; + } + + private @Nullable Long position; + + public @NonNull Builder setPosition(@NonNull Long setterArg) { + this.position = setterArg; + return this; + } + + public @NonNull PositionMessage build() { + PositionMessage pigeonReturn = new PositionMessage(); + pigeonReturn.setTextureId(textureId); + pigeonReturn.setPosition(position); + return pigeonReturn; + } + } + + @NonNull + Map toMap() { + Map toMapResult = new HashMap<>(); toMapResult.put("textureId", textureId); toMapResult.put("position", position); return toMapResult; } - static PositionMessage fromMap(HashMap map) { - PositionMessage fromMapResult = new PositionMessage(); + static @NonNull PositionMessage fromMap(@NonNull Map map) { + PositionMessage pigeonResult = new PositionMessage(); Object textureId = map.get("textureId"); - fromMapResult.textureId = + pigeonResult.setTextureId( (textureId == null) ? null - : ((textureId instanceof Integer) ? (Integer) textureId : (Long) textureId); + : ((textureId instanceof Integer) ? (Integer) textureId : (Long) textureId)); Object position = map.get("position"); - fromMapResult.position = + pigeonResult.setPosition( (position == null) ? null - : ((position instanceof Integer) ? (Integer) position : (Long) position); - return fromMapResult; + : ((position instanceof Integer) ? (Integer) position : (Long) position)); + return pigeonResult; + } + } + + /** Generated class from Pigeon that represents data sent in messages. */ + public static class CreateMessage { + private @Nullable String asset; + + public @Nullable String getAsset() { + return asset; + } + + public void setAsset(@Nullable String setterArg) { + this.asset = setterArg; + } + + private @Nullable String uri; + + public @Nullable String getUri() { + return uri; + } + + public void setUri(@Nullable String setterArg) { + this.uri = setterArg; + } + + private @Nullable String packageName; + + public @Nullable String getPackageName() { + return packageName; + } + + public void setPackageName(@Nullable String setterArg) { + this.packageName = setterArg; + } + + private @Nullable String formatHint; + + public @Nullable String getFormatHint() { + return formatHint; + } + + public void setFormatHint(@Nullable String setterArg) { + this.formatHint = setterArg; + } + + private @NonNull Map httpHeaders; + + public @NonNull Map getHttpHeaders() { + return httpHeaders; + } + + public void setHttpHeaders(@NonNull Map setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"httpHeaders\" is null."); + } + this.httpHeaders = setterArg; + } + + /** Constructor is private to enforce null safety; use Builder. */ + private CreateMessage() {} + + public static class Builder { + private @Nullable String asset; + + public @NonNull Builder setAsset(@Nullable String setterArg) { + this.asset = setterArg; + return this; + } + + private @Nullable String uri; + + public @NonNull Builder setUri(@Nullable String setterArg) { + this.uri = setterArg; + return this; + } + + private @Nullable String packageName; + + public @NonNull Builder setPackageName(@Nullable String setterArg) { + this.packageName = setterArg; + return this; + } + + private @Nullable String formatHint; + + public @NonNull Builder setFormatHint(@Nullable String setterArg) { + this.formatHint = setterArg; + return this; + } + + private @Nullable Map httpHeaders; + + public @NonNull Builder setHttpHeaders(@NonNull Map setterArg) { + this.httpHeaders = setterArg; + return this; + } + + public @NonNull CreateMessage build() { + CreateMessage pigeonReturn = new CreateMessage(); + pigeonReturn.setAsset(asset); + pigeonReturn.setUri(uri); + pigeonReturn.setPackageName(packageName); + pigeonReturn.setFormatHint(formatHint); + pigeonReturn.setHttpHeaders(httpHeaders); + return pigeonReturn; + } + } + + @NonNull + Map toMap() { + Map toMapResult = new HashMap<>(); + toMapResult.put("asset", asset); + toMapResult.put("uri", uri); + toMapResult.put("packageName", packageName); + toMapResult.put("formatHint", formatHint); + toMapResult.put("httpHeaders", httpHeaders); + return toMapResult; + } + + static @NonNull CreateMessage fromMap(@NonNull Map map) { + CreateMessage pigeonResult = new CreateMessage(); + Object asset = map.get("asset"); + pigeonResult.setAsset((String) asset); + Object uri = map.get("uri"); + pigeonResult.setUri((String) uri); + Object packageName = map.get("packageName"); + pigeonResult.setPackageName((String) packageName); + Object formatHint = map.get("formatHint"); + pigeonResult.setFormatHint((String) formatHint); + Object httpHeaders = map.get("httpHeaders"); + pigeonResult.setHttpHeaders((Map) httpHeaders); + return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ public static class MixWithOthersMessage { - private Boolean mixWithOthers; + private @NonNull Boolean mixWithOthers; - public Boolean getMixWithOthers() { + public @NonNull Boolean getMixWithOthers() { return mixWithOthers; } - public void setMixWithOthers(Boolean setterArg) { + public void setMixWithOthers(@NonNull Boolean setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"mixWithOthers\" is null."); + } this.mixWithOthers = setterArg; } - HashMap toMap() { - HashMap toMapResult = new HashMap<>(); + /** Constructor is private to enforce null safety; use Builder. */ + private MixWithOthersMessage() {} + + public static class Builder { + private @Nullable Boolean mixWithOthers; + + public @NonNull Builder setMixWithOthers(@NonNull Boolean setterArg) { + this.mixWithOthers = setterArg; + return this; + } + + public @NonNull MixWithOthersMessage build() { + MixWithOthersMessage pigeonReturn = new MixWithOthersMessage(); + pigeonReturn.setMixWithOthers(mixWithOthers); + return pigeonReturn; + } + } + + @NonNull + Map toMap() { + Map toMapResult = new HashMap<>(); toMapResult.put("mixWithOthers", mixWithOthers); return toMapResult; } - static MixWithOthersMessage fromMap(HashMap map) { - MixWithOthersMessage fromMapResult = new MixWithOthersMessage(); + static @NonNull MixWithOthersMessage fromMap(@NonNull Map map) { + MixWithOthersMessage pigeonResult = new MixWithOthersMessage(); Object mixWithOthers = map.get("mixWithOthers"); - fromMapResult.mixWithOthers = (Boolean) mixWithOthers; - return fromMapResult; + pigeonResult.setMixWithOthers((Boolean) mixWithOthers); + return pigeonResult; + } + } + + private static class VideoPlayerApiCodec extends StandardMessageCodec { + public static final VideoPlayerApiCodec INSTANCE = new VideoPlayerApiCodec(); + + private VideoPlayerApiCodec() {} + + @Override + protected Object readValueOfType(byte type, ByteBuffer buffer) { + switch (type) { + case (byte) 128: + return CreateMessage.fromMap((Map) readValue(buffer)); + + case (byte) 129: + return LoopingMessage.fromMap((Map) readValue(buffer)); + + case (byte) 130: + return MixWithOthersMessage.fromMap((Map) readValue(buffer)); + + case (byte) 131: + return PlaybackSpeedMessage.fromMap((Map) readValue(buffer)); + + case (byte) 132: + return PositionMessage.fromMap((Map) readValue(buffer)); + + case (byte) 133: + return TextureMessage.fromMap((Map) readValue(buffer)); + + case (byte) 134: + return VolumeMessage.fromMap((Map) readValue(buffer)); + + default: + return super.readValueOfType(type, buffer); + } + } + + @Override + protected void writeValue(ByteArrayOutputStream stream, Object value) { + if (value instanceof CreateMessage) { + stream.write(128); + writeValue(stream, ((CreateMessage) value).toMap()); + } else if (value instanceof LoopingMessage) { + stream.write(129); + writeValue(stream, ((LoopingMessage) value).toMap()); + } else if (value instanceof MixWithOthersMessage) { + stream.write(130); + writeValue(stream, ((MixWithOthersMessage) value).toMap()); + } else if (value instanceof PlaybackSpeedMessage) { + stream.write(131); + writeValue(stream, ((PlaybackSpeedMessage) value).toMap()); + } else if (value instanceof PositionMessage) { + stream.write(132); + writeValue(stream, ((PositionMessage) value).toMap()); + } else if (value instanceof TextureMessage) { + stream.write(133); + writeValue(stream, ((TextureMessage) value).toMap()); + } else if (value instanceof VolumeMessage) { + stream.write(134); + writeValue(stream, ((VolumeMessage) value).toMap()); + } else { + super.writeValue(stream, value); + } } } @@ -324,42 +624,45 @@ static MixWithOthersMessage fromMap(HashMap map) { public interface VideoPlayerApi { void initialize(); - TextureMessage create(CreateMessage arg); + TextureMessage create(CreateMessage msg); + + void dispose(TextureMessage msg); - void dispose(TextureMessage arg); + void setLooping(LoopingMessage msg); - void setLooping(LoopingMessage arg); + void setVolume(VolumeMessage msg); - void setVolume(VolumeMessage arg); + void setPlaybackSpeed(PlaybackSpeedMessage msg); - void setPlaybackSpeed(PlaybackSpeedMessage arg); + void play(TextureMessage msg); - void play(TextureMessage arg); + PositionMessage position(TextureMessage msg); - PositionMessage position(TextureMessage arg); + void seekTo(PositionMessage msg); - void seekTo(PositionMessage arg); + void pause(TextureMessage msg); - void pause(TextureMessage arg); + void setMixWithOthers(MixWithOthersMessage msg); - void setMixWithOthers(MixWithOthersMessage arg); + /** The codec used by VideoPlayerApi. */ + static MessageCodec getCodec() { + return VideoPlayerApiCodec.INSTANCE; + } - /** Sets up an instance of `VideoPlayerApi` to handle messages through the `binaryMessenger` */ + /** Sets up an instance of `VideoPlayerApi` to handle messages through the `binaryMessenger`. */ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.VideoPlayerApi.initialize", - new StandardMessageCodec()); + binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.initialize", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - HashMap wrapped = new HashMap<>(); + Map wrapped = new HashMap<>(); try { api.initialize(); wrapped.put("result", null); - } catch (Exception exception) { + } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); } reply.reply(wrapped); @@ -371,19 +674,20 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.VideoPlayerApi.create", - new StandardMessageCodec()); + binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.create", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - HashMap wrapped = new HashMap<>(); + Map wrapped = new HashMap<>(); try { - @SuppressWarnings("ConstantConditions") - CreateMessage input = CreateMessage.fromMap((HashMap) message); - TextureMessage output = api.create(input); - wrapped.put("result", output.toMap()); - } catch (Exception exception) { + ArrayList args = (ArrayList) message; + CreateMessage msgArg = (CreateMessage) args.get(0); + if (msgArg == null) { + throw new NullPointerException("msgArg unexpectedly null."); + } + TextureMessage output = api.create(msgArg); + wrapped.put("result", output); + } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); } reply.reply(wrapped); @@ -395,19 +699,20 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.VideoPlayerApi.dispose", - new StandardMessageCodec()); + binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.dispose", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - HashMap wrapped = new HashMap<>(); + Map wrapped = new HashMap<>(); try { - @SuppressWarnings("ConstantConditions") - TextureMessage input = TextureMessage.fromMap((HashMap) message); - api.dispose(input); + ArrayList args = (ArrayList) message; + TextureMessage msgArg = (TextureMessage) args.get(0); + if (msgArg == null) { + throw new NullPointerException("msgArg unexpectedly null."); + } + api.dispose(msgArg); wrapped.put("result", null); - } catch (Exception exception) { + } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); } reply.reply(wrapped); @@ -419,19 +724,20 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.VideoPlayerApi.setLooping", - new StandardMessageCodec()); + binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.setLooping", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - HashMap wrapped = new HashMap<>(); + Map wrapped = new HashMap<>(); try { - @SuppressWarnings("ConstantConditions") - LoopingMessage input = LoopingMessage.fromMap((HashMap) message); - api.setLooping(input); + ArrayList args = (ArrayList) message; + LoopingMessage msgArg = (LoopingMessage) args.get(0); + if (msgArg == null) { + throw new NullPointerException("msgArg unexpectedly null."); + } + api.setLooping(msgArg); wrapped.put("result", null); - } catch (Exception exception) { + } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); } reply.reply(wrapped); @@ -443,19 +749,20 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.VideoPlayerApi.setVolume", - new StandardMessageCodec()); + binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.setVolume", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - HashMap wrapped = new HashMap<>(); + Map wrapped = new HashMap<>(); try { - @SuppressWarnings("ConstantConditions") - VolumeMessage input = VolumeMessage.fromMap((HashMap) message); - api.setVolume(input); + ArrayList args = (ArrayList) message; + VolumeMessage msgArg = (VolumeMessage) args.get(0); + if (msgArg == null) { + throw new NullPointerException("msgArg unexpectedly null."); + } + api.setVolume(msgArg); wrapped.put("result", null); - } catch (Exception exception) { + } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); } reply.reply(wrapped); @@ -467,19 +774,20 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed", - new StandardMessageCodec()); + binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - HashMap wrapped = new HashMap<>(); + Map wrapped = new HashMap<>(); try { - @SuppressWarnings("ConstantConditions") - PlaybackSpeedMessage input = PlaybackSpeedMessage.fromMap((HashMap) message); - api.setPlaybackSpeed(input); + ArrayList args = (ArrayList) message; + PlaybackSpeedMessage msgArg = (PlaybackSpeedMessage) args.get(0); + if (msgArg == null) { + throw new NullPointerException("msgArg unexpectedly null."); + } + api.setPlaybackSpeed(msgArg); wrapped.put("result", null); - } catch (Exception exception) { + } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); } reply.reply(wrapped); @@ -491,19 +799,20 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.VideoPlayerApi.play", - new StandardMessageCodec()); + binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.play", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - HashMap wrapped = new HashMap<>(); + Map wrapped = new HashMap<>(); try { - @SuppressWarnings("ConstantConditions") - TextureMessage input = TextureMessage.fromMap((HashMap) message); - api.play(input); + ArrayList args = (ArrayList) message; + TextureMessage msgArg = (TextureMessage) args.get(0); + if (msgArg == null) { + throw new NullPointerException("msgArg unexpectedly null."); + } + api.play(msgArg); wrapped.put("result", null); - } catch (Exception exception) { + } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); } reply.reply(wrapped); @@ -515,19 +824,20 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.VideoPlayerApi.position", - new StandardMessageCodec()); + binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.position", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - HashMap wrapped = new HashMap<>(); + Map wrapped = new HashMap<>(); try { - @SuppressWarnings("ConstantConditions") - TextureMessage input = TextureMessage.fromMap((HashMap) message); - PositionMessage output = api.position(input); - wrapped.put("result", output.toMap()); - } catch (Exception exception) { + ArrayList args = (ArrayList) message; + TextureMessage msgArg = (TextureMessage) args.get(0); + if (msgArg == null) { + throw new NullPointerException("msgArg unexpectedly null."); + } + PositionMessage output = api.position(msgArg); + wrapped.put("result", output); + } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); } reply.reply(wrapped); @@ -539,19 +849,20 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.VideoPlayerApi.seekTo", - new StandardMessageCodec()); + binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.seekTo", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - HashMap wrapped = new HashMap<>(); + Map wrapped = new HashMap<>(); try { - @SuppressWarnings("ConstantConditions") - PositionMessage input = PositionMessage.fromMap((HashMap) message); - api.seekTo(input); + ArrayList args = (ArrayList) message; + PositionMessage msgArg = (PositionMessage) args.get(0); + if (msgArg == null) { + throw new NullPointerException("msgArg unexpectedly null."); + } + api.seekTo(msgArg); wrapped.put("result", null); - } catch (Exception exception) { + } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); } reply.reply(wrapped); @@ -563,19 +874,20 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.VideoPlayerApi.pause", - new StandardMessageCodec()); + binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.pause", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - HashMap wrapped = new HashMap<>(); + Map wrapped = new HashMap<>(); try { - @SuppressWarnings("ConstantConditions") - TextureMessage input = TextureMessage.fromMap((HashMap) message); - api.pause(input); + ArrayList args = (ArrayList) message; + TextureMessage msgArg = (TextureMessage) args.get(0); + if (msgArg == null) { + throw new NullPointerException("msgArg unexpectedly null."); + } + api.pause(msgArg); wrapped.put("result", null); - } catch (Exception exception) { + } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); } reply.reply(wrapped); @@ -587,19 +899,20 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers", - new StandardMessageCodec()); + binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - HashMap wrapped = new HashMap<>(); + Map wrapped = new HashMap<>(); try { - @SuppressWarnings("ConstantConditions") - MixWithOthersMessage input = MixWithOthersMessage.fromMap((HashMap) message); - api.setMixWithOthers(input); + ArrayList args = (ArrayList) message; + MixWithOthersMessage msgArg = (MixWithOthersMessage) args.get(0); + if (msgArg == null) { + throw new NullPointerException("msgArg unexpectedly null."); + } + api.setMixWithOthers(msgArg); wrapped.put("result", null); - } catch (Exception exception) { + } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); } reply.reply(wrapped); @@ -611,11 +924,13 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { } } - private static HashMap wrapError(Exception exception) { - HashMap errorMap = new HashMap<>(); + private static Map wrapError(Throwable exception) { + Map errorMap = new HashMap<>(); errorMap.put("message", exception.toString()); errorMap.put("code", exception.getClass().getSimpleName()); - errorMap.put("details", null); + errorMap.put( + "details", + "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); return errorMap; } } diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java index 887d3d15f175..33593267338c 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java @@ -10,6 +10,7 @@ import android.content.Context; import android.net.Uri; import android.view.Surface; +import androidx.annotation.NonNull; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.Format; @@ -64,7 +65,7 @@ final class VideoPlayer { TextureRegistry.SurfaceTextureEntry textureEntry, String dataSource, String formatHint, - Map httpHeaders, + @NonNull Map httpHeaders, VideoPlayerOptions options) { this.eventChannel = eventChannel; this.textureEntry = textureEntry; diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java index d77b45e03d4b..168d90d8a57a 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java @@ -156,8 +156,7 @@ public TextureMessage create(CreateMessage arg) { } videoPlayers.put(handle.id(), player); - TextureMessage result = new TextureMessage(); - result.setTextureId(handle.id()); + TextureMessage result = new TextureMessage.Builder().setTextureId(handle.id()).build(); return result; } @@ -189,8 +188,11 @@ public void play(TextureMessage arg) { public PositionMessage position(TextureMessage arg) { VideoPlayer player = videoPlayers.get(arg.getTextureId()); - PositionMessage result = new PositionMessage(); - result.setPosition(player.getPosition()); + PositionMessage result = + new PositionMessage.Builder() + .setPosition(player.getPosition()) + .setTextureId(arg.getTextureId()) + .build(); player.sendBufferingUpdate(); return result; } diff --git a/packages/video_player/video_player_android/lib/src/android_video_player.dart b/packages/video_player/video_player_android/lib/src/android_video_player.dart index adb386afa2b8..d713b282d197 100644 --- a/packages/video_player/video_player_android/lib/src/android_video_player.dart +++ b/packages/video_player/video_player_android/lib/src/android_video_player.dart @@ -9,7 +9,7 @@ import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; -import 'messages.dart'; +import 'messages.g.dart'; /// An Android implementation of [VideoPlayerPlatform] that uses the /// Pigeon-generated [VideoPlayerApi]. @@ -28,30 +28,40 @@ class AndroidVideoPlayer extends VideoPlayerPlatform { @override Future dispose(int textureId) { - return _api.dispose(TextureMessage()..textureId = textureId); + return _api.dispose(TextureMessage(textureId: textureId)); } @override Future create(DataSource dataSource) async { - final CreateMessage message = CreateMessage(); - + String? asset; + String? packageName; + String? uri; + String? formatHint; + Map httpHeaders = {}; switch (dataSource.sourceType) { case DataSourceType.asset: - message.asset = dataSource.asset; - message.packageName = dataSource.package; + asset = dataSource.asset; + packageName = dataSource.package; break; case DataSourceType.network: - message.uri = dataSource.uri; - message.formatHint = _videoFormatStringMap[dataSource.formatHint]; - message.httpHeaders = dataSource.httpHeaders; + uri = dataSource.uri; + formatHint = _videoFormatStringMap[dataSource.formatHint]; + httpHeaders = dataSource.httpHeaders; break; case DataSourceType.file: - message.uri = dataSource.uri; + uri = dataSource.uri; break; case DataSourceType.contentUri: - message.uri = dataSource.uri; + uri = dataSource.uri; break; } + final CreateMessage message = CreateMessage( + asset: asset, + packageName: packageName, + uri: uri, + httpHeaders: httpHeaders, + formatHint: formatHint, + ); final TextureMessage response = await _api.create(message); return response.textureId; @@ -59,49 +69,53 @@ class AndroidVideoPlayer extends VideoPlayerPlatform { @override Future setLooping(int textureId, bool looping) { - return _api.setLooping(LoopingMessage() - ..textureId = textureId - ..isLooping = looping); + return _api.setLooping(LoopingMessage( + textureId: textureId, + isLooping: looping, + )); } @override Future play(int textureId) { - return _api.play(TextureMessage()..textureId = textureId); + return _api.play(TextureMessage(textureId: textureId)); } @override Future pause(int textureId) { - return _api.pause(TextureMessage()..textureId = textureId); + return _api.pause(TextureMessage(textureId: textureId)); } @override Future setVolume(int textureId, double volume) { - return _api.setVolume(VolumeMessage() - ..textureId = textureId - ..volume = volume); + return _api.setVolume(VolumeMessage( + textureId: textureId, + volume: volume, + )); } @override Future setPlaybackSpeed(int textureId, double speed) { assert(speed > 0); - return _api.setPlaybackSpeed(PlaybackSpeedMessage() - ..textureId = textureId - ..speed = speed); + return _api.setPlaybackSpeed(PlaybackSpeedMessage( + textureId: textureId, + speed: speed, + )); } @override Future seekTo(int textureId, Duration position) { - return _api.seekTo(PositionMessage() - ..textureId = textureId - ..position = position.inMilliseconds); + return _api.seekTo(PositionMessage( + textureId: textureId, + position: position.inMilliseconds, + )); } @override Future getPosition(int textureId) async { final PositionMessage response = - await _api.position(TextureMessage()..textureId = textureId); - return Duration(milliseconds: response.position!); + await _api.position(TextureMessage(textureId: textureId)); + return Duration(milliseconds: response.position); } @override @@ -146,9 +160,8 @@ class AndroidVideoPlayer extends VideoPlayerPlatform { @override Future setMixWithOthers(bool mixWithOthers) { - return _api.setMixWithOthers( - MixWithOthersMessage()..mixWithOthers = mixWithOthers, - ); + return _api + .setMixWithOthers(MixWithOthersMessage(mixWithOthers: mixWithOthers)); } EventChannel _eventChannelFor(int textureId) { diff --git a/packages/video_player/video_player_android/lib/src/messages.dart b/packages/video_player/video_player_android/lib/src/messages.dart deleted file mode 100644 index 831f4e3755d9..000000000000 --- a/packages/video_player/video_player_android/lib/src/messages.dart +++ /dev/null @@ -1,425 +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. - -// Autogenerated from Pigeon (v0.1.21), do not edit directly. -// See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, cast_nullable_to_non_nullable -// @dart = 2.12 -import 'dart:async'; -import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; - -import 'package:flutter/services.dart'; - -class TextureMessage { - int? textureId; - - Object encode() { - final Map pigeonMap = {}; - pigeonMap['textureId'] = textureId; - return pigeonMap; - } - - static TextureMessage decode(Object message) { - final Map pigeonMap = message as Map; - return TextureMessage()..textureId = pigeonMap['textureId'] as int?; - } -} - -class CreateMessage { - String? asset; - String? uri; - String? packageName; - String? formatHint; - Map? httpHeaders; - - Object encode() { - final Map pigeonMap = {}; - pigeonMap['asset'] = asset; - pigeonMap['uri'] = uri; - pigeonMap['packageName'] = packageName; - pigeonMap['formatHint'] = formatHint; - pigeonMap['httpHeaders'] = httpHeaders; - return pigeonMap; - } - - static CreateMessage decode(Object message) { - final Map pigeonMap = message as Map; - return CreateMessage() - ..asset = pigeonMap['asset'] as String? - ..uri = pigeonMap['uri'] as String? - ..packageName = pigeonMap['packageName'] as String? - ..formatHint = pigeonMap['formatHint'] as String? - ..httpHeaders = pigeonMap['httpHeaders'] as Map?; - } -} - -class LoopingMessage { - int? textureId; - bool? isLooping; - - Object encode() { - final Map pigeonMap = {}; - pigeonMap['textureId'] = textureId; - pigeonMap['isLooping'] = isLooping; - return pigeonMap; - } - - static LoopingMessage decode(Object message) { - final Map pigeonMap = message as Map; - return LoopingMessage() - ..textureId = pigeonMap['textureId'] as int? - ..isLooping = pigeonMap['isLooping'] as bool?; - } -} - -class VolumeMessage { - int? textureId; - double? volume; - - Object encode() { - final Map pigeonMap = {}; - pigeonMap['textureId'] = textureId; - pigeonMap['volume'] = volume; - return pigeonMap; - } - - static VolumeMessage decode(Object message) { - final Map pigeonMap = message as Map; - return VolumeMessage() - ..textureId = pigeonMap['textureId'] as int? - ..volume = pigeonMap['volume'] as double?; - } -} - -class PlaybackSpeedMessage { - int? textureId; - double? speed; - - Object encode() { - final Map pigeonMap = {}; - pigeonMap['textureId'] = textureId; - pigeonMap['speed'] = speed; - return pigeonMap; - } - - static PlaybackSpeedMessage decode(Object message) { - final Map pigeonMap = message as Map; - return PlaybackSpeedMessage() - ..textureId = pigeonMap['textureId'] as int? - ..speed = pigeonMap['speed'] as double?; - } -} - -class PositionMessage { - int? textureId; - int? position; - - Object encode() { - final Map pigeonMap = {}; - pigeonMap['textureId'] = textureId; - pigeonMap['position'] = position; - return pigeonMap; - } - - static PositionMessage decode(Object message) { - final Map pigeonMap = message as Map; - return PositionMessage() - ..textureId = pigeonMap['textureId'] as int? - ..position = pigeonMap['position'] as int?; - } -} - -class MixWithOthersMessage { - bool? mixWithOthers; - - Object encode() { - final Map pigeonMap = {}; - pigeonMap['mixWithOthers'] = mixWithOthers; - return pigeonMap; - } - - static MixWithOthersMessage decode(Object message) { - final Map pigeonMap = message as Map; - return MixWithOthersMessage() - ..mixWithOthers = pigeonMap['mixWithOthers'] as bool?; - } -} - -class VideoPlayerApi { - Future initialize() async { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.initialize', StandardMessageCodec()); - final Map? replyMap = - await channel.send(null) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - // noop - } - } - - Future create(CreateMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.create', StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - return TextureMessage.decode(replyMap['result']!); - } - } - - Future dispose(TextureMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.dispose', StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - // noop - } - } - - Future setLooping(LoopingMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setLooping', StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - // noop - } - } - - Future setVolume(VolumeMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setVolume', StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - // noop - } - } - - Future setPlaybackSpeed(PlaybackSpeedMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed', - StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - // noop - } - } - - Future play(TextureMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.play', StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - // noop - } - } - - Future position(TextureMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.position', StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - return PositionMessage.decode(replyMap['result']!); - } - } - - Future seekTo(PositionMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.seekTo', StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - // noop - } - } - - Future pause(TextureMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.pause', StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - // noop - } - } - - Future setMixWithOthers(MixWithOthersMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers', - StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - // noop - } - } -} diff --git a/packages/video_player/video_player_android/lib/src/messages.g.dart b/packages/video_player/video_player_android/lib/src/messages.g.dart new file mode 100644 index 000000000000..5fa09e33bc7e --- /dev/null +++ b/packages/video_player/video_player_android/lib/src/messages.g.dart @@ -0,0 +1,539 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v1.0.16), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name +// @dart = 2.12 +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; + +import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; +import 'package:flutter/services.dart'; + +class TextureMessage { + TextureMessage({ + required this.textureId, + }); + + int textureId; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['textureId'] = textureId; + return pigeonMap; + } + + static TextureMessage decode(Object message) { + final Map pigeonMap = message as Map; + return TextureMessage( + textureId: pigeonMap['textureId']! as int, + ); + } +} + +class LoopingMessage { + LoopingMessage({ + required this.textureId, + required this.isLooping, + }); + + int textureId; + bool isLooping; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['textureId'] = textureId; + pigeonMap['isLooping'] = isLooping; + return pigeonMap; + } + + static LoopingMessage decode(Object message) { + final Map pigeonMap = message as Map; + return LoopingMessage( + textureId: pigeonMap['textureId']! as int, + isLooping: pigeonMap['isLooping']! as bool, + ); + } +} + +class VolumeMessage { + VolumeMessage({ + required this.textureId, + required this.volume, + }); + + int textureId; + double volume; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['textureId'] = textureId; + pigeonMap['volume'] = volume; + return pigeonMap; + } + + static VolumeMessage decode(Object message) { + final Map pigeonMap = message as Map; + return VolumeMessage( + textureId: pigeonMap['textureId']! as int, + volume: pigeonMap['volume']! as double, + ); + } +} + +class PlaybackSpeedMessage { + PlaybackSpeedMessage({ + required this.textureId, + required this.speed, + }); + + int textureId; + double speed; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['textureId'] = textureId; + pigeonMap['speed'] = speed; + return pigeonMap; + } + + static PlaybackSpeedMessage decode(Object message) { + final Map pigeonMap = message as Map; + return PlaybackSpeedMessage( + textureId: pigeonMap['textureId']! as int, + speed: pigeonMap['speed']! as double, + ); + } +} + +class PositionMessage { + PositionMessage({ + required this.textureId, + required this.position, + }); + + int textureId; + int position; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['textureId'] = textureId; + pigeonMap['position'] = position; + return pigeonMap; + } + + static PositionMessage decode(Object message) { + final Map pigeonMap = message as Map; + return PositionMessage( + textureId: pigeonMap['textureId']! as int, + position: pigeonMap['position']! as int, + ); + } +} + +class CreateMessage { + CreateMessage({ + this.asset, + this.uri, + this.packageName, + this.formatHint, + required this.httpHeaders, + }); + + String? asset; + String? uri; + String? packageName; + String? formatHint; + Map httpHeaders; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['asset'] = asset; + pigeonMap['uri'] = uri; + pigeonMap['packageName'] = packageName; + pigeonMap['formatHint'] = formatHint; + pigeonMap['httpHeaders'] = httpHeaders; + return pigeonMap; + } + + static CreateMessage decode(Object message) { + final Map pigeonMap = message as Map; + return CreateMessage( + asset: pigeonMap['asset'] as String?, + uri: pigeonMap['uri'] as String?, + packageName: pigeonMap['packageName'] as String?, + formatHint: pigeonMap['formatHint'] as String?, + httpHeaders: (pigeonMap['httpHeaders'] as Map?)! + .cast(), + ); + } +} + +class MixWithOthersMessage { + MixWithOthersMessage({ + required this.mixWithOthers, + }); + + bool mixWithOthers; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['mixWithOthers'] = mixWithOthers; + return pigeonMap; + } + + static MixWithOthersMessage decode(Object message) { + final Map pigeonMap = message as Map; + return MixWithOthersMessage( + mixWithOthers: pigeonMap['mixWithOthers']! as bool, + ); + } +} + +class _VideoPlayerApiCodec extends StandardMessageCodec { + const _VideoPlayerApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is CreateMessage) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is LoopingMessage) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is MixWithOthersMessage) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else if (value is PlaybackSpeedMessage) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); + } else if (value is PositionMessage) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); + } else if (value is TextureMessage) { + buffer.putUint8(133); + writeValue(buffer, value.encode()); + } else if (value is VolumeMessage) { + buffer.putUint8(134); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return CreateMessage.decode(readValue(buffer)!); + + case 129: + return LoopingMessage.decode(readValue(buffer)!); + + case 130: + return MixWithOthersMessage.decode(readValue(buffer)!); + + case 131: + return PlaybackSpeedMessage.decode(readValue(buffer)!); + + case 132: + return PositionMessage.decode(readValue(buffer)!); + + case 133: + return TextureMessage.decode(readValue(buffer)!); + + case 134: + return VolumeMessage.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +class VideoPlayerApi { + /// Constructor for [VideoPlayerApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + VideoPlayerApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _VideoPlayerApiCodec(); + + Future initialize() async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.initialize', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send(null) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future create(CreateMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.create', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_msg]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return (replyMap['result'] as TextureMessage?)!; + } + } + + Future dispose(TextureMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.dispose', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_msg]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setLooping(LoopingMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setLooping', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_msg]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setVolume(VolumeMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setVolume', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_msg]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setPlaybackSpeed(PlaybackSpeedMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_msg]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future play(TextureMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.play', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_msg]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future position(TextureMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.position', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_msg]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return (replyMap['result'] as PositionMessage?)!; + } + } + + Future seekTo(PositionMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.seekTo', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_msg]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future pause(TextureMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.pause', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_msg]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setMixWithOthers(MixWithOthersMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_msg]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} diff --git a/packages/video_player/video_player_android/pigeons/copyright.txt b/packages/video_player/video_player_android/pigeons/copyright.txt new file mode 100644 index 000000000000..1236b63caf3a --- /dev/null +++ b/packages/video_player/video_player_android/pigeons/copyright.txt @@ -0,0 +1,3 @@ +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. diff --git a/packages/video_player/video_player_android/pigeons/messages.dart b/packages/video_player/video_player_android/pigeons/messages.dart index 42d96408da87..9efbfc947b5c 100644 --- a/packages/video_player/video_player_android/pigeons/messages.dart +++ b/packages/video_player/video_player_android/pigeons/messages.dart @@ -2,43 +2,57 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.9 - -import 'package:pigeon/pigeon_lib.dart'; +import 'package:pigeon/pigeon.dart'; +@ConfigurePigeon(PigeonOptions( + dartOut: 'lib/src/messages.g.dart', + dartTestOut: 'test/test_api.dart', + javaOut: 'android/src/main/java/io/flutter/plugins/videoplayer/Messages.java', + javaOptions: JavaOptions( + package: 'io.flutter.plugins.videoplayer', + ), + copyrightHeader: 'pigeons/copyright.txt', +)) class TextureMessage { + TextureMessage(this.textureId); int textureId; } class LoopingMessage { + LoopingMessage(this.textureId, this.isLooping); int textureId; bool isLooping; } class VolumeMessage { + VolumeMessage(this.textureId, this.volume); int textureId; double volume; } class PlaybackSpeedMessage { + PlaybackSpeedMessage(this.textureId, this.speed); int textureId; double speed; } class PositionMessage { + PositionMessage(this.textureId, this.position); int textureId; int position; } class CreateMessage { - String asset; - String uri; - String packageName; - String formatHint; - Map httpHeaders; + CreateMessage({required this.httpHeaders}); + String? asset; + String? uri; + String? packageName; + String? formatHint; + Map httpHeaders; } class MixWithOthersMessage { + MixWithOthersMessage(this.mixWithOthers); bool mixWithOthers; } @@ -56,14 +70,3 @@ abstract class VideoPlayerApi { void pause(TextureMessage msg); void setMixWithOthers(MixWithOthersMessage msg); } - -void configurePigeon(PigeonOptions opts) { - opts.dartOut = 'lib/src/messages.dart'; - opts.dartTestOut = 'test/test_api.dart'; - opts.objcHeaderOut = 'ios/Classes/messages.h'; - opts.objcSourceOut = 'ios/Classes/messages.m'; - opts.objcOptions.prefix = 'FLT'; - opts.javaOut = - 'android/src/main/java/io/flutter/plugins/videoplayer/Messages.java'; - opts.javaOptions.package = 'io.flutter.plugins.videoplayer'; -} diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index 423c5ade0ac8..c40d5185e854 100644 --- a/packages/video_player/video_player_android/pubspec.yaml +++ b/packages/video_player/video_player_android/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_android description: Android implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.2.17 +version: 2.3.0 environment: sdk: ">=2.14.0 <3.0.0" @@ -25,4 +25,4 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - pigeon: ^0.1.21 + pigeon: ^1.0.16 diff --git a/packages/video_player/video_player_android/test/android_video_player_test.dart b/packages/video_player/video_player_android/test/android_video_player_test.dart index 9eac540160a0..b7cf763e16a6 100644 --- a/packages/video_player/video_player_android/test/android_video_player_test.dart +++ b/packages/video_player/video_player_android/test/android_video_player_test.dart @@ -6,7 +6,7 @@ import 'dart:ui'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:video_player_android/src/messages.dart'; +import 'package:video_player_android/src/messages.g.dart'; import 'package:video_player_android/video_player_android.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; @@ -26,7 +26,7 @@ class _ApiLogger implements TestHostVideoPlayerApi { TextureMessage create(CreateMessage arg) { log.add('create'); createMessage = arg; - return TextureMessage()..textureId = 3; + return TextureMessage(textureId: 3); } @override @@ -62,7 +62,7 @@ class _ApiLogger implements TestHostVideoPlayerApi { PositionMessage position(TextureMessage arg) { log.add('position'); textureMessage = arg; - return PositionMessage()..position = 234; + return PositionMessage(textureId: arg.textureId, position: 234); } @override diff --git a/packages/video_player/video_player_android/test/test_api.dart b/packages/video_player/video_player_android/test/test_api.dart index 2fbcbcf9e898..e8bc9d85283f 100644 --- a/packages/video_player/video_player_android/test/test_api.dart +++ b/packages/video_player/video_player_android/test/test_api.dart @@ -1,35 +1,99 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - -// Autogenerated from Pigeon (v0.1.21), do not edit directly. +// Autogenerated from Pigeon (v1.0.16), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis // @dart = 2.12 import 'dart:async'; import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; - +import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:video_player_android/src/messages.dart'; + +// TODO(gaaclarke): This had to be hand tweaked from a relative path. +import 'package:video_player_android/src/messages.g.dart'; + +class _TestHostVideoPlayerApiCodec extends StandardMessageCodec { + const _TestHostVideoPlayerApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is CreateMessage) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is LoopingMessage) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is MixWithOthersMessage) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else if (value is PlaybackSpeedMessage) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); + } else if (value is PositionMessage) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); + } else if (value is TextureMessage) { + buffer.putUint8(133); + writeValue(buffer, value.encode()); + } else if (value is VolumeMessage) { + buffer.putUint8(134); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return CreateMessage.decode(readValue(buffer)!); + + case 129: + return LoopingMessage.decode(readValue(buffer)!); + + case 130: + return MixWithOthersMessage.decode(readValue(buffer)!); + + case 131: + return PlaybackSpeedMessage.decode(readValue(buffer)!); + + case 132: + return PositionMessage.decode(readValue(buffer)!); + + case 133: + return TextureMessage.decode(readValue(buffer)!); + + case 134: + return VolumeMessage.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} abstract class TestHostVideoPlayerApi { + static const MessageCodec codec = _TestHostVideoPlayerApiCodec(); + void initialize(); - TextureMessage create(CreateMessage arg); - void dispose(TextureMessage arg); - void setLooping(LoopingMessage arg); - void setVolume(VolumeMessage arg); - void setPlaybackSpeed(PlaybackSpeedMessage arg); - void play(TextureMessage arg); - PositionMessage position(TextureMessage arg); - void seekTo(PositionMessage arg); - void pause(TextureMessage arg); - void setMixWithOthers(MixWithOthersMessage arg); - static void setup(TestHostVideoPlayerApi? api) { + TextureMessage create(CreateMessage msg); + void dispose(TextureMessage msg); + void setLooping(LoopingMessage msg); + void setVolume(VolumeMessage msg); + void setPlaybackSpeed(PlaybackSpeedMessage msg); + void play(TextureMessage msg); + PositionMessage position(TextureMessage msg); + void seekTo(PositionMessage msg); + void pause(TextureMessage msg); + void setMixWithOthers(MixWithOthersMessage msg); + static void setup(TestHostVideoPlayerApi? api, + {BinaryMessenger? binaryMessenger}) { { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.initialize', - StandardMessageCodec()); + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.initialize', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { @@ -41,157 +105,193 @@ abstract class TestHostVideoPlayerApi { } } { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.create', StandardMessageCodec()); + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.create', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.create was null. Expected CreateMessage.'); - final CreateMessage input = CreateMessage.decode(message!); - final TextureMessage output = api.create(input); - return {'result': output.encode()}; + 'Argument for dev.flutter.pigeon.VideoPlayerApi.create was null.'); + final List args = (message as List?)!; + final CreateMessage? arg_msg = (args[0] as CreateMessage?); + assert(arg_msg != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.create was null, expected non-null CreateMessage.'); + final TextureMessage output = api.create(arg_msg!); + return {'result': output}; }); } } { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.dispose', StandardMessageCodec()); + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.dispose', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.dispose was null. Expected TextureMessage.'); - final TextureMessage input = TextureMessage.decode(message!); - api.dispose(input); + 'Argument for dev.flutter.pigeon.VideoPlayerApi.dispose was null.'); + final List args = (message as List?)!; + final TextureMessage? arg_msg = (args[0] as TextureMessage?); + assert(arg_msg != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.dispose was null, expected non-null TextureMessage.'); + api.dispose(arg_msg!); return {}; }); } } { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setLooping', - StandardMessageCodec()); + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setLooping', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setLooping was null. Expected LoopingMessage.'); - final LoopingMessage input = LoopingMessage.decode(message!); - api.setLooping(input); + 'Argument for dev.flutter.pigeon.VideoPlayerApi.setLooping was null.'); + final List args = (message as List?)!; + final LoopingMessage? arg_msg = (args[0] as LoopingMessage?); + assert(arg_msg != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.setLooping was null, expected non-null LoopingMessage.'); + api.setLooping(arg_msg!); return {}; }); } } { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setVolume', - StandardMessageCodec()); + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setVolume', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setVolume was null. Expected VolumeMessage.'); - final VolumeMessage input = VolumeMessage.decode(message!); - api.setVolume(input); + 'Argument for dev.flutter.pigeon.VideoPlayerApi.setVolume was null.'); + final List args = (message as List?)!; + final VolumeMessage? arg_msg = (args[0] as VolumeMessage?); + assert(arg_msg != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.setVolume was null, expected non-null VolumeMessage.'); + api.setVolume(arg_msg!); return {}; }); } } { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed', - StandardMessageCodec()); + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed was null. Expected PlaybackSpeedMessage.'); - final PlaybackSpeedMessage input = - PlaybackSpeedMessage.decode(message!); - api.setPlaybackSpeed(input); + 'Argument for dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed was null.'); + final List args = (message as List?)!; + final PlaybackSpeedMessage? arg_msg = + (args[0] as PlaybackSpeedMessage?); + assert(arg_msg != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed was null, expected non-null PlaybackSpeedMessage.'); + api.setPlaybackSpeed(arg_msg!); return {}; }); } } { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.play', StandardMessageCodec()); + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.play', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.play was null. Expected TextureMessage.'); - final TextureMessage input = TextureMessage.decode(message!); - api.play(input); + 'Argument for dev.flutter.pigeon.VideoPlayerApi.play was null.'); + final List args = (message as List?)!; + final TextureMessage? arg_msg = (args[0] as TextureMessage?); + assert(arg_msg != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.play was null, expected non-null TextureMessage.'); + api.play(arg_msg!); return {}; }); } } { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.position', StandardMessageCodec()); + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.position', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.position was null. Expected TextureMessage.'); - final TextureMessage input = TextureMessage.decode(message!); - final PositionMessage output = api.position(input); - return {'result': output.encode()}; + 'Argument for dev.flutter.pigeon.VideoPlayerApi.position was null.'); + final List args = (message as List?)!; + final TextureMessage? arg_msg = (args[0] as TextureMessage?); + assert(arg_msg != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.position was null, expected non-null TextureMessage.'); + final PositionMessage output = api.position(arg_msg!); + return {'result': output}; }); } } { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.seekTo', StandardMessageCodec()); + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.seekTo', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.seekTo was null. Expected PositionMessage.'); - final PositionMessage input = PositionMessage.decode(message!); - api.seekTo(input); + 'Argument for dev.flutter.pigeon.VideoPlayerApi.seekTo was null.'); + final List args = (message as List?)!; + final PositionMessage? arg_msg = (args[0] as PositionMessage?); + assert(arg_msg != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.seekTo was null, expected non-null PositionMessage.'); + api.seekTo(arg_msg!); return {}; }); } } { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.pause', StandardMessageCodec()); + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.pause', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.pause was null. Expected TextureMessage.'); - final TextureMessage input = TextureMessage.decode(message!); - api.pause(input); + 'Argument for dev.flutter.pigeon.VideoPlayerApi.pause was null.'); + final List args = (message as List?)!; + final TextureMessage? arg_msg = (args[0] as TextureMessage?); + assert(arg_msg != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.pause was null, expected non-null TextureMessage.'); + api.pause(arg_msg!); return {}; }); } } { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers', - StandardMessageCodec()); + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers was null. Expected MixWithOthersMessage.'); - final MixWithOthersMessage input = - MixWithOthersMessage.decode(message!); - api.setMixWithOthers(input); + 'Argument for dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers was null.'); + final List args = (message as List?)!; + final MixWithOthersMessage? arg_msg = + (args[0] as MixWithOthersMessage?); + assert(arg_msg != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers was null, expected non-null MixWithOthersMessage.'); + api.setMixWithOthers(arg_msg!); return {}; }); } diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index 161e9002902e..d6ecabd4b9ad 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.0 + +* Updates Pigeon to ^1.0.16. + ## 2.2.18 * Wait to initialize m3u8 videos until size is set, fixing aspect ratio. diff --git a/packages/video_player/video_player_avfoundation/CONTRIBUTING.md b/packages/video_player/video_player_avfoundation/CONTRIBUTING.md index 387551bda2f6..8dfec9faf809 100644 --- a/packages/video_player/video_player_avfoundation/CONTRIBUTING.md +++ b/packages/video_player/video_player_avfoundation/CONTRIBUTING.md @@ -1,12 +1,11 @@ ## Updating pigeon-generated files If you update files in the pigeons/ directory, run the following -command in this directory (ignore the errors you get about -dependencies in the examples directory): +command in this directory: ```bash flutter pub upgrade -flutter pub run pigeon --dart_null_safety --input pigeons/messages.dart +flutter pub run pigeon --input pigeons/messages.dart # git commit your changes so that your working environment is clean (cd ../../../; ./script/tool_runner.sh format --clang-format=clang-format-7) ``` @@ -28,6 +27,5 @@ publish pigeon before you can land the updates to this package, since the CI tests run the analysis using latest published version of pigeon, not your version or the version on master. -In either case, the configuration will be obtained automatically from -the `pigeons/messages.dart` file (see `configurePigeon` at the bottom -of that file). +In either case, the configuration will be obtained automatically from the +`pigeons/messages.dart` file (see `ConfigurePigeon` at the top of that file). diff --git a/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m b/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m index d7044faf2332..6d8b3965fd94 100644 --- a/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m +++ b/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m @@ -33,9 +33,7 @@ - (void)testSeekToInvokesTextureFrameAvailableOnTextureRegistry { OCMStub([partialRegistrar textures]).andReturn(mockTextureRegistry); FLTVideoPlayerPlugin *videoPlayerPlugin = (FLTVideoPlayerPlugin *)[[FLTVideoPlayerPlugin alloc] initWithRegistrar:partialRegistrar]; - FLTPositionMessage *message = [[FLTPositionMessage alloc] init]; - message.textureId = @101; - message.position = @0; + FLTPositionMessage *message = [FLTPositionMessage makeWithTextureId:@101 position:@0]; FlutterError *error; [videoPlayerPlugin seekTo:message error:&error]; OCMVerify([mockTextureRegistry textureFrameAvailable:message.textureId.intValue]); @@ -53,8 +51,12 @@ - (void)testDeregistersFromPlayer { [videoPlayerPlugin initialize:&error]; XCTAssertNil(error); - FLTCreateMessage *create = [[FLTCreateMessage alloc] init]; - create.uri = @"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4"; + FLTCreateMessage *create = [FLTCreateMessage + makeWithAsset:nil + uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" + packageName:nil + formatHint:nil + httpHeaders:@{}]; FLTTextureMessage *textureMessage = [videoPlayerPlugin create:create error:&error]; XCTAssertNil(error); XCTAssertNotNil(textureMessage); @@ -125,8 +127,11 @@ - (void)testHLSControls { [videoPlayerPlugin initialize:&error]; XCTAssertNil(error); - FLTCreateMessage *create = [[FLTCreateMessage alloc] init]; - create.uri = uri; + FLTCreateMessage *create = [FLTCreateMessage makeWithAsset:nil + uri:uri + packageName:nil + formatHint:nil + httpHeaders:@{}]; FLTTextureMessage *textureMessage = [videoPlayerPlugin create:create error:&error]; NSNumber *textureId = textureMessage.textureId; @@ -152,18 +157,15 @@ - (void)testHLSControls { XCTAssertEqual(avPlayer.timeControlStatus, AVPlayerTimeControlStatusPaused); // Change playback speed. - FLTPlaybackSpeedMessage *playback = [[FLTPlaybackSpeedMessage alloc] init]; - playback.textureId = textureId; - playback.speed = @2; + FLTPlaybackSpeedMessage *playback = [FLTPlaybackSpeedMessage makeWithTextureId:textureId + speed:@2]; [videoPlayerPlugin setPlaybackSpeed:playback error:&error]; XCTAssertNil(error); XCTAssertEqual(avPlayer.rate, 2); XCTAssertEqual(avPlayer.timeControlStatus, AVPlayerTimeControlStatusWaitingToPlayAtSpecifiedRate); // Volume - FLTVolumeMessage *volume = [[FLTVolumeMessage alloc] init]; - volume.textureId = textureId; - volume.volume = @(0.1); + FLTVolumeMessage *volume = [FLTVolumeMessage makeWithTextureId:textureId volume:@0.1]; [videoPlayerPlugin setVolume:volume error:&error]; XCTAssertNil(error); XCTAssertEqual(avPlayer.volume, 0.1f); diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m index 55021cce64f9..026b576cdb10 100644 --- a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m +++ b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m @@ -5,7 +5,7 @@ #import "FLTVideoPlayerPlugin.h" #import #import -#import "messages.h" +#import "messages.g.h" #if !__has_feature(objc_arc) #error Code Requires ARC. @@ -43,7 +43,7 @@ @interface FLTVideoPlayer : NSObject @property(nonatomic, readonly) BOOL isInitialized; - (instancetype)initWithURL:(NSURL *)url frameUpdater:(FLTFrameUpdater *)frameUpdater - httpHeaders:(NSDictionary *)headers; + httpHeaders:(nonnull NSDictionary *)headers; @end static void *timeRangeContext = &timeRangeContext; @@ -57,7 +57,7 @@ - (instancetype)initWithURL:(NSURL *)url @implementation FLTVideoPlayer - (instancetype)initWithAsset:(NSString *)asset frameUpdater:(FLTFrameUpdater *)frameUpdater { NSString *path = [[NSBundle mainBundle] pathForResource:asset ofType:nil]; - return [self initWithURL:[NSURL fileURLWithPath:path] frameUpdater:frameUpdater httpHeaders:nil]; + return [self initWithURL:[NSURL fileURLWithPath:path] frameUpdater:frameUpdater httpHeaders:@{}]; } - (void)addObservers:(AVPlayerItem *)item { @@ -177,9 +177,9 @@ - (void)createVideoOutputAndDisplayLink:(FLTFrameUpdater *)frameUpdater { - (instancetype)initWithURL:(NSURL *)url frameUpdater:(FLTFrameUpdater *)frameUpdater - httpHeaders:(NSDictionary *)headers { + httpHeaders:(nonnull NSDictionary *)headers { NSDictionary *options = nil; - if (headers != nil && [headers count] != 0) { + if ([headers count] != 0) { options = @{@"AVURLAssetHTTPHeaderFieldsKey" : headers}; } AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:url options:options]; @@ -544,8 +544,7 @@ - (FLTTextureMessage *)onPlayerSetup:(FLTVideoPlayer *)player [eventChannel setStreamHandler:player]; player.eventChannel = eventChannel; self.playersByTextureId[@(textureId)] = player; - FLTTextureMessage *result = [[FLTTextureMessage alloc] init]; - result.textureId = @(textureId); + FLTTextureMessage *result = [FLTTextureMessage makeWithTextureId:@(textureId)]; return result; } @@ -628,8 +627,8 @@ - (void)play:(FLTTextureMessage *)input error:(FlutterError **)error { - (FLTPositionMessage *)position:(FLTTextureMessage *)input error:(FlutterError **)error { FLTVideoPlayer *player = self.playersByTextureId[input.textureId]; - FLTPositionMessage *result = [[FLTPositionMessage alloc] init]; - result.position = @([player position]); + FLTPositionMessage *result = [FLTPositionMessage makeWithTextureId:input.textureId + position:@([player position])]; return result; } diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.h b/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.h new file mode 100644 index 000000000000..96d02d2f7360 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.h @@ -0,0 +1,107 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v1.0.17), do not edit directly. +// See also: https://pub.dev/packages/pigeon +#import +@protocol FlutterBinaryMessenger; +@protocol FlutterMessageCodec; +@class FlutterError; +@class FlutterStandardTypedData; + +NS_ASSUME_NONNULL_BEGIN + +@class FLTTextureMessage; +@class FLTLoopingMessage; +@class FLTVolumeMessage; +@class FLTPlaybackSpeedMessage; +@class FLTPositionMessage; +@class FLTCreateMessage; +@class FLTMixWithOthersMessage; + +@interface FLTTextureMessage : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithTextureId:(NSNumber *)textureId; +@property(nonatomic, strong) NSNumber *textureId; +@end + +@interface FLTLoopingMessage : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithTextureId:(NSNumber *)textureId isLooping:(NSNumber *)isLooping; +@property(nonatomic, strong) NSNumber *textureId; +@property(nonatomic, strong) NSNumber *isLooping; +@end + +@interface FLTVolumeMessage : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithTextureId:(NSNumber *)textureId volume:(NSNumber *)volume; +@property(nonatomic, strong) NSNumber *textureId; +@property(nonatomic, strong) NSNumber *volume; +@end + +@interface FLTPlaybackSpeedMessage : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithTextureId:(NSNumber *)textureId speed:(NSNumber *)speed; +@property(nonatomic, strong) NSNumber *textureId; +@property(nonatomic, strong) NSNumber *speed; +@end + +@interface FLTPositionMessage : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithTextureId:(NSNumber *)textureId position:(NSNumber *)position; +@property(nonatomic, strong) NSNumber *textureId; +@property(nonatomic, strong) NSNumber *position; +@end + +@interface FLTCreateMessage : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithAsset:(nullable NSString *)asset + uri:(nullable NSString *)uri + packageName:(nullable NSString *)packageName + formatHint:(nullable NSString *)formatHint + httpHeaders:(NSDictionary *)httpHeaders; +@property(nonatomic, copy, nullable) NSString *asset; +@property(nonatomic, copy, nullable) NSString *uri; +@property(nonatomic, copy, nullable) NSString *packageName; +@property(nonatomic, copy, nullable) NSString *formatHint; +@property(nonatomic, strong) NSDictionary *httpHeaders; +@end + +@interface FLTMixWithOthersMessage : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithMixWithOthers:(NSNumber *)mixWithOthers; +@property(nonatomic, strong) NSNumber *mixWithOthers; +@end + +/// The codec used by FLTVideoPlayerApi. +NSObject *FLTVideoPlayerApiGetCodec(void); + +@protocol FLTVideoPlayerApi +- (void)initialize:(FlutterError *_Nullable *_Nonnull)error; +- (nullable FLTTextureMessage *)create:(FLTCreateMessage *)msg + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)dispose:(FLTTextureMessage *)msg error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setLooping:(FLTLoopingMessage *)msg error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setVolume:(FLTVolumeMessage *)msg error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setPlaybackSpeed:(FLTPlaybackSpeedMessage *)msg + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)play:(FLTTextureMessage *)msg error:(FlutterError *_Nullable *_Nonnull)error; +- (nullable FLTPositionMessage *)position:(FLTTextureMessage *)msg + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)seekTo:(FLTPositionMessage *)msg error:(FlutterError *_Nullable *_Nonnull)error; +- (void)pause:(FLTTextureMessage *)msg error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setMixWithOthers:(FLTMixWithOthersMessage *)msg + error:(FlutterError *_Nullable *_Nonnull)error; +@end + +extern void FLTVideoPlayerApiSetup(id binaryMessenger, + NSObject *_Nullable api); + +NS_ASSUME_NONNULL_END diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.m b/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.m new file mode 100644 index 000000000000..c9acee44593c --- /dev/null +++ b/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.m @@ -0,0 +1,522 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v1.0.17), do not edit directly. +// See also: https://pub.dev/packages/pigeon +#import "messages.g.h" +#import + +#if !__has_feature(objc_arc) +#error File requires ARC to be enabled. +#endif + +static NSDictionary *wrapResult(id result, FlutterError *error) { + NSDictionary *errorDict = (NSDictionary *)[NSNull null]; + if (error) { + errorDict = @{ + @"code" : (error.code ? error.code : [NSNull null]), + @"message" : (error.message ? error.message : [NSNull null]), + @"details" : (error.details ? error.details : [NSNull null]), + }; + } + return @{ + @"result" : (result ? result : [NSNull null]), + @"error" : errorDict, + }; +} +static id GetNullableObject(NSDictionary *dict, id key) { + id result = dict[key]; + return (result == [NSNull null]) ? nil : result; +} + +@interface FLTTextureMessage () ++ (FLTTextureMessage *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end +@interface FLTLoopingMessage () ++ (FLTLoopingMessage *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end +@interface FLTVolumeMessage () ++ (FLTVolumeMessage *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end +@interface FLTPlaybackSpeedMessage () ++ (FLTPlaybackSpeedMessage *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end +@interface FLTPositionMessage () ++ (FLTPositionMessage *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end +@interface FLTCreateMessage () ++ (FLTCreateMessage *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end +@interface FLTMixWithOthersMessage () ++ (FLTMixWithOthersMessage *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end + +@implementation FLTTextureMessage ++ (instancetype)makeWithTextureId:(NSNumber *)textureId { + FLTTextureMessage *pigeonResult = [[FLTTextureMessage alloc] init]; + pigeonResult.textureId = textureId; + return pigeonResult; +} ++ (FLTTextureMessage *)fromMap:(NSDictionary *)dict { + FLTTextureMessage *pigeonResult = [[FLTTextureMessage alloc] init]; + pigeonResult.textureId = GetNullableObject(dict, @"textureId"); + NSAssert(pigeonResult.textureId != nil, @""); + return pigeonResult; +} +- (NSDictionary *)toMap { + return + [NSDictionary dictionaryWithObjectsAndKeys:(self.textureId ? self.textureId : [NSNull null]), + @"textureId", nil]; +} +@end + +@implementation FLTLoopingMessage ++ (instancetype)makeWithTextureId:(NSNumber *)textureId isLooping:(NSNumber *)isLooping { + FLTLoopingMessage *pigeonResult = [[FLTLoopingMessage alloc] init]; + pigeonResult.textureId = textureId; + pigeonResult.isLooping = isLooping; + return pigeonResult; +} ++ (FLTLoopingMessage *)fromMap:(NSDictionary *)dict { + FLTLoopingMessage *pigeonResult = [[FLTLoopingMessage alloc] init]; + pigeonResult.textureId = GetNullableObject(dict, @"textureId"); + NSAssert(pigeonResult.textureId != nil, @""); + pigeonResult.isLooping = GetNullableObject(dict, @"isLooping"); + NSAssert(pigeonResult.isLooping != nil, @""); + return pigeonResult; +} +- (NSDictionary *)toMap { + return [NSDictionary + dictionaryWithObjectsAndKeys:(self.textureId ? self.textureId : [NSNull null]), @"textureId", + (self.isLooping ? self.isLooping : [NSNull null]), @"isLooping", + nil]; +} +@end + +@implementation FLTVolumeMessage ++ (instancetype)makeWithTextureId:(NSNumber *)textureId volume:(NSNumber *)volume { + FLTVolumeMessage *pigeonResult = [[FLTVolumeMessage alloc] init]; + pigeonResult.textureId = textureId; + pigeonResult.volume = volume; + return pigeonResult; +} ++ (FLTVolumeMessage *)fromMap:(NSDictionary *)dict { + FLTVolumeMessage *pigeonResult = [[FLTVolumeMessage alloc] init]; + pigeonResult.textureId = GetNullableObject(dict, @"textureId"); + NSAssert(pigeonResult.textureId != nil, @""); + pigeonResult.volume = GetNullableObject(dict, @"volume"); + NSAssert(pigeonResult.volume != nil, @""); + return pigeonResult; +} +- (NSDictionary *)toMap { + return [NSDictionary + dictionaryWithObjectsAndKeys:(self.textureId ? self.textureId : [NSNull null]), @"textureId", + (self.volume ? self.volume : [NSNull null]), @"volume", nil]; +} +@end + +@implementation FLTPlaybackSpeedMessage ++ (instancetype)makeWithTextureId:(NSNumber *)textureId speed:(NSNumber *)speed { + FLTPlaybackSpeedMessage *pigeonResult = [[FLTPlaybackSpeedMessage alloc] init]; + pigeonResult.textureId = textureId; + pigeonResult.speed = speed; + return pigeonResult; +} ++ (FLTPlaybackSpeedMessage *)fromMap:(NSDictionary *)dict { + FLTPlaybackSpeedMessage *pigeonResult = [[FLTPlaybackSpeedMessage alloc] init]; + pigeonResult.textureId = GetNullableObject(dict, @"textureId"); + NSAssert(pigeonResult.textureId != nil, @""); + pigeonResult.speed = GetNullableObject(dict, @"speed"); + NSAssert(pigeonResult.speed != nil, @""); + return pigeonResult; +} +- (NSDictionary *)toMap { + return [NSDictionary + dictionaryWithObjectsAndKeys:(self.textureId ? self.textureId : [NSNull null]), @"textureId", + (self.speed ? self.speed : [NSNull null]), @"speed", nil]; +} +@end + +@implementation FLTPositionMessage ++ (instancetype)makeWithTextureId:(NSNumber *)textureId position:(NSNumber *)position { + FLTPositionMessage *pigeonResult = [[FLTPositionMessage alloc] init]; + pigeonResult.textureId = textureId; + pigeonResult.position = position; + return pigeonResult; +} ++ (FLTPositionMessage *)fromMap:(NSDictionary *)dict { + FLTPositionMessage *pigeonResult = [[FLTPositionMessage alloc] init]; + pigeonResult.textureId = GetNullableObject(dict, @"textureId"); + NSAssert(pigeonResult.textureId != nil, @""); + pigeonResult.position = GetNullableObject(dict, @"position"); + NSAssert(pigeonResult.position != nil, @""); + return pigeonResult; +} +- (NSDictionary *)toMap { + return [NSDictionary + dictionaryWithObjectsAndKeys:(self.textureId ? self.textureId : [NSNull null]), @"textureId", + (self.position ? self.position : [NSNull null]), @"position", + nil]; +} +@end + +@implementation FLTCreateMessage ++ (instancetype)makeWithAsset:(nullable NSString *)asset + uri:(nullable NSString *)uri + packageName:(nullable NSString *)packageName + formatHint:(nullable NSString *)formatHint + httpHeaders:(NSDictionary *)httpHeaders { + FLTCreateMessage *pigeonResult = [[FLTCreateMessage alloc] init]; + pigeonResult.asset = asset; + pigeonResult.uri = uri; + pigeonResult.packageName = packageName; + pigeonResult.formatHint = formatHint; + pigeonResult.httpHeaders = httpHeaders; + return pigeonResult; +} ++ (FLTCreateMessage *)fromMap:(NSDictionary *)dict { + FLTCreateMessage *pigeonResult = [[FLTCreateMessage alloc] init]; + pigeonResult.asset = GetNullableObject(dict, @"asset"); + pigeonResult.uri = GetNullableObject(dict, @"uri"); + pigeonResult.packageName = GetNullableObject(dict, @"packageName"); + pigeonResult.formatHint = GetNullableObject(dict, @"formatHint"); + pigeonResult.httpHeaders = GetNullableObject(dict, @"httpHeaders"); + NSAssert(pigeonResult.httpHeaders != nil, @""); + return pigeonResult; +} +- (NSDictionary *)toMap { + return [NSDictionary + dictionaryWithObjectsAndKeys:(self.asset ? self.asset : [NSNull null]), @"asset", + (self.uri ? self.uri : [NSNull null]), @"uri", + (self.packageName ? self.packageName : [NSNull null]), + @"packageName", + (self.formatHint ? self.formatHint : [NSNull null]), + @"formatHint", + (self.httpHeaders ? self.httpHeaders : [NSNull null]), + @"httpHeaders", nil]; +} +@end + +@implementation FLTMixWithOthersMessage ++ (instancetype)makeWithMixWithOthers:(NSNumber *)mixWithOthers { + FLTMixWithOthersMessage *pigeonResult = [[FLTMixWithOthersMessage alloc] init]; + pigeonResult.mixWithOthers = mixWithOthers; + return pigeonResult; +} ++ (FLTMixWithOthersMessage *)fromMap:(NSDictionary *)dict { + FLTMixWithOthersMessage *pigeonResult = [[FLTMixWithOthersMessage alloc] init]; + pigeonResult.mixWithOthers = GetNullableObject(dict, @"mixWithOthers"); + NSAssert(pigeonResult.mixWithOthers != nil, @""); + return pigeonResult; +} +- (NSDictionary *)toMap { + return [NSDictionary + dictionaryWithObjectsAndKeys:(self.mixWithOthers ? self.mixWithOthers : [NSNull null]), + @"mixWithOthers", nil]; +} +@end + +@interface FLTVideoPlayerApiCodecReader : FlutterStandardReader +@end +@implementation FLTVideoPlayerApiCodecReader +- (nullable id)readValueOfType:(UInt8)type { + switch (type) { + case 128: + return [FLTCreateMessage fromMap:[self readValue]]; + + case 129: + return [FLTLoopingMessage fromMap:[self readValue]]; + + case 130: + return [FLTMixWithOthersMessage fromMap:[self readValue]]; + + case 131: + return [FLTPlaybackSpeedMessage fromMap:[self readValue]]; + + case 132: + return [FLTPositionMessage fromMap:[self readValue]]; + + case 133: + return [FLTTextureMessage fromMap:[self readValue]]; + + case 134: + return [FLTVolumeMessage fromMap:[self readValue]]; + + default: + return [super readValueOfType:type]; + } +} +@end + +@interface FLTVideoPlayerApiCodecWriter : FlutterStandardWriter +@end +@implementation FLTVideoPlayerApiCodecWriter +- (void)writeValue:(id)value { + if ([value isKindOfClass:[FLTCreateMessage class]]) { + [self writeByte:128]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FLTLoopingMessage class]]) { + [self writeByte:129]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FLTMixWithOthersMessage class]]) { + [self writeByte:130]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FLTPlaybackSpeedMessage class]]) { + [self writeByte:131]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FLTPositionMessage class]]) { + [self writeByte:132]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FLTTextureMessage class]]) { + [self writeByte:133]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FLTVolumeMessage class]]) { + [self writeByte:134]; + [self writeValue:[value toMap]]; + } else { + [super writeValue:value]; + } +} +@end + +@interface FLTVideoPlayerApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FLTVideoPlayerApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FLTVideoPlayerApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FLTVideoPlayerApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FLTVideoPlayerApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FLTVideoPlayerApiCodecReaderWriter *readerWriter = + [[FLTVideoPlayerApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FLTVideoPlayerApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.initialize" + binaryMessenger:binaryMessenger + codec:FLTVideoPlayerApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(initialize:)], + @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(initialize:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + FlutterError *error; + [api initialize:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.create" + binaryMessenger:binaryMessenger + codec:FLTVideoPlayerApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(create:error:)], + @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(create:error:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FLTCreateMessage *arg_msg = args[0]; + FlutterError *error; + FLTTextureMessage *output = [api create:arg_msg error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.dispose" + binaryMessenger:binaryMessenger + codec:FLTVideoPlayerApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(dispose:error:)], + @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(dispose:error:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FLTTextureMessage *arg_msg = args[0]; + FlutterError *error; + [api dispose:arg_msg error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.setLooping" + binaryMessenger:binaryMessenger + codec:FLTVideoPlayerApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(setLooping:error:)], + @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(setLooping:error:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FLTLoopingMessage *arg_msg = args[0]; + FlutterError *error; + [api setLooping:arg_msg error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.setVolume" + binaryMessenger:binaryMessenger + codec:FLTVideoPlayerApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(setVolume:error:)], + @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(setVolume:error:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FLTVolumeMessage *arg_msg = args[0]; + FlutterError *error; + [api setVolume:arg_msg error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed" + binaryMessenger:binaryMessenger + codec:FLTVideoPlayerApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(setPlaybackSpeed:error:)], + @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(setPlaybackSpeed:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FLTPlaybackSpeedMessage *arg_msg = args[0]; + FlutterError *error; + [api setPlaybackSpeed:arg_msg error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = + [FlutterBasicMessageChannel messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.play" + binaryMessenger:binaryMessenger + codec:FLTVideoPlayerApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(play:error:)], + @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(play:error:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FLTTextureMessage *arg_msg = args[0]; + FlutterError *error; + [api play:arg_msg error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.position" + binaryMessenger:binaryMessenger + codec:FLTVideoPlayerApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(position:error:)], + @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(position:error:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FLTTextureMessage *arg_msg = args[0]; + FlutterError *error; + FLTPositionMessage *output = [api position:arg_msg error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.seekTo" + binaryMessenger:binaryMessenger + codec:FLTVideoPlayerApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(seekTo:error:)], + @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(seekTo:error:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FLTPositionMessage *arg_msg = args[0]; + FlutterError *error; + [api seekTo:arg_msg error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.pause" + binaryMessenger:binaryMessenger + codec:FLTVideoPlayerApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(pause:error:)], + @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(pause:error:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FLTTextureMessage *arg_msg = args[0]; + FlutterError *error; + [api pause:arg_msg error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers" + binaryMessenger:binaryMessenger + codec:FLTVideoPlayerApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(setMixWithOthers:error:)], + @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(setMixWithOthers:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FLTMixWithOthersMessage *arg_msg = args[0]; + FlutterError *error; + [api setMixWithOthers:arg_msg error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/messages.h b/packages/video_player/video_player_avfoundation/ios/Classes/messages.h deleted file mode 100644 index e21e7860ba09..000000000000 --- a/packages/video_player/video_player_avfoundation/ios/Classes/messages.h +++ /dev/null @@ -1,79 +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. - -// Autogenerated from Pigeon (v0.1.21), do not edit directly. -// See also: https://pub.dev/packages/pigeon -#import -@protocol FlutterBinaryMessenger; -@class FlutterError; -@class FlutterStandardTypedData; - -NS_ASSUME_NONNULL_BEGIN - -@class FLTTextureMessage; -@class FLTCreateMessage; -@class FLTLoopingMessage; -@class FLTVolumeMessage; -@class FLTPlaybackSpeedMessage; -@class FLTPositionMessage; -@class FLTMixWithOthersMessage; - -@interface FLTTextureMessage : NSObject -@property(nonatomic, strong, nullable) NSNumber *textureId; -@end - -@interface FLTCreateMessage : NSObject -@property(nonatomic, copy, nullable) NSString *asset; -@property(nonatomic, copy, nullable) NSString *uri; -@property(nonatomic, copy, nullable) NSString *packageName; -@property(nonatomic, copy, nullable) NSString *formatHint; -@property(nonatomic, strong, nullable) NSDictionary *httpHeaders; -@end - -@interface FLTLoopingMessage : NSObject -@property(nonatomic, strong, nullable) NSNumber *textureId; -@property(nonatomic, strong, nullable) NSNumber *isLooping; -@end - -@interface FLTVolumeMessage : NSObject -@property(nonatomic, strong, nullable) NSNumber *textureId; -@property(nonatomic, strong, nullable) NSNumber *volume; -@end - -@interface FLTPlaybackSpeedMessage : NSObject -@property(nonatomic, strong, nullable) NSNumber *textureId; -@property(nonatomic, strong, nullable) NSNumber *speed; -@end - -@interface FLTPositionMessage : NSObject -@property(nonatomic, strong, nullable) NSNumber *textureId; -@property(nonatomic, strong, nullable) NSNumber *position; -@end - -@interface FLTMixWithOthersMessage : NSObject -@property(nonatomic, strong, nullable) NSNumber *mixWithOthers; -@end - -@protocol FLTVideoPlayerApi -- (void)initialize:(FlutterError *_Nullable *_Nonnull)error; -- (nullable FLTTextureMessage *)create:(FLTCreateMessage *)input - error:(FlutterError *_Nullable *_Nonnull)error; -- (void)dispose:(FLTTextureMessage *)input error:(FlutterError *_Nullable *_Nonnull)error; -- (void)setLooping:(FLTLoopingMessage *)input error:(FlutterError *_Nullable *_Nonnull)error; -- (void)setVolume:(FLTVolumeMessage *)input error:(FlutterError *_Nullable *_Nonnull)error; -- (void)setPlaybackSpeed:(FLTPlaybackSpeedMessage *)input - error:(FlutterError *_Nullable *_Nonnull)error; -- (void)play:(FLTTextureMessage *)input error:(FlutterError *_Nullable *_Nonnull)error; -- (nullable FLTPositionMessage *)position:(FLTTextureMessage *)input - error:(FlutterError *_Nullable *_Nonnull)error; -- (void)seekTo:(FLTPositionMessage *)input error:(FlutterError *_Nullable *_Nonnull)error; -- (void)pause:(FLTTextureMessage *)input error:(FlutterError *_Nullable *_Nonnull)error; -- (void)setMixWithOthers:(FLTMixWithOthersMessage *)input - error:(FlutterError *_Nullable *_Nonnull)error; -@end - -extern void FLTVideoPlayerApiSetup(id binaryMessenger, - id _Nullable api); - -NS_ASSUME_NONNULL_END diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/messages.m b/packages/video_player/video_player_avfoundation/ios/Classes/messages.m deleted file mode 100644 index 0936bbc7d995..000000000000 --- a/packages/video_player/video_player_avfoundation/ios/Classes/messages.m +++ /dev/null @@ -1,379 +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. - -// Autogenerated from Pigeon (v0.1.21), do not edit directly. -// See also: https://pub.dev/packages/pigeon -#import "messages.h" -#import - -#if !__has_feature(objc_arc) -#error File requires ARC to be enabled. -#endif - -static NSDictionary *wrapResult(NSDictionary *result, FlutterError *error) { - NSDictionary *errorDict = (NSDictionary *)[NSNull null]; - if (error) { - errorDict = @{ - @"code" : (error.code ? error.code : [NSNull null]), - @"message" : (error.message ? error.message : [NSNull null]), - @"details" : (error.details ? error.details : [NSNull null]), - }; - } - return @{ - @"result" : (result ? result : [NSNull null]), - @"error" : errorDict, - }; -} - -@interface FLTTextureMessage () -+ (FLTTextureMessage *)fromMap:(NSDictionary *)dict; -- (NSDictionary *)toMap; -@end -@interface FLTCreateMessage () -+ (FLTCreateMessage *)fromMap:(NSDictionary *)dict; -- (NSDictionary *)toMap; -@end -@interface FLTLoopingMessage () -+ (FLTLoopingMessage *)fromMap:(NSDictionary *)dict; -- (NSDictionary *)toMap; -@end -@interface FLTVolumeMessage () -+ (FLTVolumeMessage *)fromMap:(NSDictionary *)dict; -- (NSDictionary *)toMap; -@end -@interface FLTPlaybackSpeedMessage () -+ (FLTPlaybackSpeedMessage *)fromMap:(NSDictionary *)dict; -- (NSDictionary *)toMap; -@end -@interface FLTPositionMessage () -+ (FLTPositionMessage *)fromMap:(NSDictionary *)dict; -- (NSDictionary *)toMap; -@end -@interface FLTMixWithOthersMessage () -+ (FLTMixWithOthersMessage *)fromMap:(NSDictionary *)dict; -- (NSDictionary *)toMap; -@end - -@implementation FLTTextureMessage -+ (FLTTextureMessage *)fromMap:(NSDictionary *)dict { - FLTTextureMessage *result = [[FLTTextureMessage alloc] init]; - result.textureId = dict[@"textureId"]; - if ((NSNull *)result.textureId == [NSNull null]) { - result.textureId = nil; - } - return result; -} -- (NSDictionary *)toMap { - return [NSDictionary - dictionaryWithObjectsAndKeys:(self.textureId != nil ? self.textureId : [NSNull null]), - @"textureId", nil]; -} -@end - -@implementation FLTCreateMessage -+ (FLTCreateMessage *)fromMap:(NSDictionary *)dict { - FLTCreateMessage *result = [[FLTCreateMessage alloc] init]; - result.asset = dict[@"asset"]; - if ((NSNull *)result.asset == [NSNull null]) { - result.asset = nil; - } - result.uri = dict[@"uri"]; - if ((NSNull *)result.uri == [NSNull null]) { - result.uri = nil; - } - result.packageName = dict[@"packageName"]; - if ((NSNull *)result.packageName == [NSNull null]) { - result.packageName = nil; - } - result.formatHint = dict[@"formatHint"]; - if ((NSNull *)result.formatHint == [NSNull null]) { - result.formatHint = nil; - } - result.httpHeaders = dict[@"httpHeaders"]; - if ((NSNull *)result.httpHeaders == [NSNull null]) { - result.httpHeaders = nil; - } - return result; -} -- (NSDictionary *)toMap { - return [NSDictionary - dictionaryWithObjectsAndKeys:(self.asset ? self.asset : [NSNull null]), @"asset", - (self.uri ? self.uri : [NSNull null]), @"uri", - (self.packageName ? self.packageName : [NSNull null]), - @"packageName", - (self.formatHint ? self.formatHint : [NSNull null]), - @"formatHint", - (self.httpHeaders ? self.httpHeaders : [NSNull null]), - @"httpHeaders", nil]; -} -@end - -@implementation FLTLoopingMessage -+ (FLTLoopingMessage *)fromMap:(NSDictionary *)dict { - FLTLoopingMessage *result = [[FLTLoopingMessage alloc] init]; - result.textureId = dict[@"textureId"]; - if ((NSNull *)result.textureId == [NSNull null]) { - result.textureId = nil; - } - result.isLooping = dict[@"isLooping"]; - if ((NSNull *)result.isLooping == [NSNull null]) { - result.isLooping = nil; - } - return result; -} -- (NSDictionary *)toMap { - return [NSDictionary - dictionaryWithObjectsAndKeys:(self.textureId != nil ? self.textureId : [NSNull null]), - @"textureId", - (self.isLooping != nil ? self.isLooping : [NSNull null]), - @"isLooping", nil]; -} -@end - -@implementation FLTVolumeMessage -+ (FLTVolumeMessage *)fromMap:(NSDictionary *)dict { - FLTVolumeMessage *result = [[FLTVolumeMessage alloc] init]; - result.textureId = dict[@"textureId"]; - if ((NSNull *)result.textureId == [NSNull null]) { - result.textureId = nil; - } - result.volume = dict[@"volume"]; - if ((NSNull *)result.volume == [NSNull null]) { - result.volume = nil; - } - return result; -} -- (NSDictionary *)toMap { - return [NSDictionary - dictionaryWithObjectsAndKeys:(self.textureId != nil ? self.textureId : [NSNull null]), - @"textureId", (self.volume != nil ? self.volume : [NSNull null]), - @"volume", nil]; -} -@end - -@implementation FLTPlaybackSpeedMessage -+ (FLTPlaybackSpeedMessage *)fromMap:(NSDictionary *)dict { - FLTPlaybackSpeedMessage *result = [[FLTPlaybackSpeedMessage alloc] init]; - result.textureId = dict[@"textureId"]; - if ((NSNull *)result.textureId == [NSNull null]) { - result.textureId = nil; - } - result.speed = dict[@"speed"]; - if ((NSNull *)result.speed == [NSNull null]) { - result.speed = nil; - } - return result; -} -- (NSDictionary *)toMap { - return [NSDictionary - dictionaryWithObjectsAndKeys:(self.textureId != nil ? self.textureId : [NSNull null]), - @"textureId", (self.speed != nil ? self.speed : [NSNull null]), - @"speed", nil]; -} -@end - -@implementation FLTPositionMessage -+ (FLTPositionMessage *)fromMap:(NSDictionary *)dict { - FLTPositionMessage *result = [[FLTPositionMessage alloc] init]; - result.textureId = dict[@"textureId"]; - if ((NSNull *)result.textureId == [NSNull null]) { - result.textureId = nil; - } - result.position = dict[@"position"]; - if ((NSNull *)result.position == [NSNull null]) { - result.position = nil; - } - return result; -} -- (NSDictionary *)toMap { - return [NSDictionary - dictionaryWithObjectsAndKeys:(self.textureId != nil ? self.textureId : [NSNull null]), - @"textureId", - (self.position != nil ? self.position : [NSNull null]), - @"position", nil]; -} -@end - -@implementation FLTMixWithOthersMessage -+ (FLTMixWithOthersMessage *)fromMap:(NSDictionary *)dict { - FLTMixWithOthersMessage *result = [[FLTMixWithOthersMessage alloc] init]; - result.mixWithOthers = dict[@"mixWithOthers"]; - if ((NSNull *)result.mixWithOthers == [NSNull null]) { - result.mixWithOthers = nil; - } - return result; -} -- (NSDictionary *)toMap { - return [NSDictionary - dictionaryWithObjectsAndKeys:(self.mixWithOthers != nil ? self.mixWithOthers : [NSNull null]), - @"mixWithOthers", nil]; -} -@end - -void FLTVideoPlayerApiSetup(id binaryMessenger, id api) { - { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.initialize" - binaryMessenger:binaryMessenger]; - if (api) { - [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - FlutterError *error; - [api initialize:&error]; - callback(wrapResult(nil, error)); - }]; - } else { - [channel setMessageHandler:nil]; - } - } - { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.create" - binaryMessenger:binaryMessenger]; - if (api) { - [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - FLTCreateMessage *input = [FLTCreateMessage fromMap:message]; - FlutterError *error; - FLTTextureMessage *output = [api create:input error:&error]; - callback(wrapResult([output toMap], error)); - }]; - } else { - [channel setMessageHandler:nil]; - } - } - { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.dispose" - binaryMessenger:binaryMessenger]; - if (api) { - [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - FLTTextureMessage *input = [FLTTextureMessage fromMap:message]; - FlutterError *error; - [api dispose:input error:&error]; - callback(wrapResult(nil, error)); - }]; - } else { - [channel setMessageHandler:nil]; - } - } - { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.setLooping" - binaryMessenger:binaryMessenger]; - if (api) { - [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - FLTLoopingMessage *input = [FLTLoopingMessage fromMap:message]; - FlutterError *error; - [api setLooping:input error:&error]; - callback(wrapResult(nil, error)); - }]; - } else { - [channel setMessageHandler:nil]; - } - } - { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.setVolume" - binaryMessenger:binaryMessenger]; - if (api) { - [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - FLTVolumeMessage *input = [FLTVolumeMessage fromMap:message]; - FlutterError *error; - [api setVolume:input error:&error]; - callback(wrapResult(nil, error)); - }]; - } else { - [channel setMessageHandler:nil]; - } - } - { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed" - binaryMessenger:binaryMessenger]; - if (api) { - [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - FLTPlaybackSpeedMessage *input = [FLTPlaybackSpeedMessage fromMap:message]; - FlutterError *error; - [api setPlaybackSpeed:input error:&error]; - callback(wrapResult(nil, error)); - }]; - } else { - [channel setMessageHandler:nil]; - } - } - { - FlutterBasicMessageChannel *channel = - [FlutterBasicMessageChannel messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.play" - binaryMessenger:binaryMessenger]; - if (api) { - [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - FLTTextureMessage *input = [FLTTextureMessage fromMap:message]; - FlutterError *error; - [api play:input error:&error]; - callback(wrapResult(nil, error)); - }]; - } else { - [channel setMessageHandler:nil]; - } - } - { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.position" - binaryMessenger:binaryMessenger]; - if (api) { - [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - FLTTextureMessage *input = [FLTTextureMessage fromMap:message]; - FlutterError *error; - FLTPositionMessage *output = [api position:input error:&error]; - callback(wrapResult([output toMap], error)); - }]; - } else { - [channel setMessageHandler:nil]; - } - } - { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.seekTo" - binaryMessenger:binaryMessenger]; - if (api) { - [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - FLTPositionMessage *input = [FLTPositionMessage fromMap:message]; - FlutterError *error; - [api seekTo:input error:&error]; - callback(wrapResult(nil, error)); - }]; - } else { - [channel setMessageHandler:nil]; - } - } - { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.pause" - binaryMessenger:binaryMessenger]; - if (api) { - [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - FLTTextureMessage *input = [FLTTextureMessage fromMap:message]; - FlutterError *error; - [api pause:input error:&error]; - callback(wrapResult(nil, error)); - }]; - } else { - [channel setMessageHandler:nil]; - } - } - { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers" - binaryMessenger:binaryMessenger]; - if (api) { - [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - FLTMixWithOthersMessage *input = [FLTMixWithOthersMessage fromMap:message]; - FlutterError *error; - [api setMixWithOthers:input error:&error]; - callback(wrapResult(nil, error)); - }]; - } else { - [channel setMessageHandler:nil]; - } - } -} diff --git a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart index db7e04bd682a..bf1518dfa32a 100644 --- a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart +++ b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart @@ -9,7 +9,7 @@ import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; -import 'messages.dart'; +import 'messages.g.dart'; /// An iOS implementation of [VideoPlayerPlatform] that uses the /// Pigeon-generated [VideoPlayerApi]. @@ -28,30 +28,40 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { @override Future dispose(int textureId) { - return _api.dispose(TextureMessage()..textureId = textureId); + return _api.dispose(TextureMessage(textureId: textureId)); } @override Future create(DataSource dataSource) async { - final CreateMessage message = CreateMessage(); - + String? asset; + String? packageName; + String? uri; + String? formatHint; + Map httpHeaders = {}; switch (dataSource.sourceType) { case DataSourceType.asset: - message.asset = dataSource.asset; - message.packageName = dataSource.package; + asset = dataSource.asset; + packageName = dataSource.package; break; case DataSourceType.network: - message.uri = dataSource.uri; - message.formatHint = _videoFormatStringMap[dataSource.formatHint]; - message.httpHeaders = dataSource.httpHeaders; + uri = dataSource.uri; + formatHint = _videoFormatStringMap[dataSource.formatHint]; + httpHeaders = dataSource.httpHeaders; break; case DataSourceType.file: - message.uri = dataSource.uri; + uri = dataSource.uri; break; case DataSourceType.contentUri: - message.uri = dataSource.uri; + uri = dataSource.uri; break; } + final CreateMessage message = CreateMessage( + asset: asset, + packageName: packageName, + uri: uri, + httpHeaders: httpHeaders, + formatHint: formatHint, + ); final TextureMessage response = await _api.create(message); return response.textureId; @@ -59,49 +69,53 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { @override Future setLooping(int textureId, bool looping) { - return _api.setLooping(LoopingMessage() - ..textureId = textureId - ..isLooping = looping); + return _api.setLooping(LoopingMessage( + textureId: textureId, + isLooping: looping, + )); } @override Future play(int textureId) { - return _api.play(TextureMessage()..textureId = textureId); + return _api.play(TextureMessage(textureId: textureId)); } @override Future pause(int textureId) { - return _api.pause(TextureMessage()..textureId = textureId); + return _api.pause(TextureMessage(textureId: textureId)); } @override Future setVolume(int textureId, double volume) { - return _api.setVolume(VolumeMessage() - ..textureId = textureId - ..volume = volume); + return _api.setVolume(VolumeMessage( + textureId: textureId, + volume: volume, + )); } @override Future setPlaybackSpeed(int textureId, double speed) { assert(speed > 0); - return _api.setPlaybackSpeed(PlaybackSpeedMessage() - ..textureId = textureId - ..speed = speed); + return _api.setPlaybackSpeed(PlaybackSpeedMessage( + textureId: textureId, + speed: speed, + )); } @override Future seekTo(int textureId, Duration position) { - return _api.seekTo(PositionMessage() - ..textureId = textureId - ..position = position.inMilliseconds); + return _api.seekTo(PositionMessage( + textureId: textureId, + position: position.inMilliseconds, + )); } @override Future getPosition(int textureId) async { final PositionMessage response = - await _api.position(TextureMessage()..textureId = textureId); - return Duration(milliseconds: response.position!); + await _api.position(TextureMessage(textureId: textureId)); + return Duration(milliseconds: response.position); } @override @@ -146,9 +160,8 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { @override Future setMixWithOthers(bool mixWithOthers) { - return _api.setMixWithOthers( - MixWithOthersMessage()..mixWithOthers = mixWithOthers, - ); + return _api + .setMixWithOthers(MixWithOthersMessage(mixWithOthers: mixWithOthers)); } EventChannel _eventChannelFor(int textureId) { diff --git a/packages/video_player/video_player_avfoundation/lib/src/messages.dart b/packages/video_player/video_player_avfoundation/lib/src/messages.dart deleted file mode 100644 index 831f4e3755d9..000000000000 --- a/packages/video_player/video_player_avfoundation/lib/src/messages.dart +++ /dev/null @@ -1,425 +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. - -// Autogenerated from Pigeon (v0.1.21), do not edit directly. -// See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, cast_nullable_to_non_nullable -// @dart = 2.12 -import 'dart:async'; -import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; - -import 'package:flutter/services.dart'; - -class TextureMessage { - int? textureId; - - Object encode() { - final Map pigeonMap = {}; - pigeonMap['textureId'] = textureId; - return pigeonMap; - } - - static TextureMessage decode(Object message) { - final Map pigeonMap = message as Map; - return TextureMessage()..textureId = pigeonMap['textureId'] as int?; - } -} - -class CreateMessage { - String? asset; - String? uri; - String? packageName; - String? formatHint; - Map? httpHeaders; - - Object encode() { - final Map pigeonMap = {}; - pigeonMap['asset'] = asset; - pigeonMap['uri'] = uri; - pigeonMap['packageName'] = packageName; - pigeonMap['formatHint'] = formatHint; - pigeonMap['httpHeaders'] = httpHeaders; - return pigeonMap; - } - - static CreateMessage decode(Object message) { - final Map pigeonMap = message as Map; - return CreateMessage() - ..asset = pigeonMap['asset'] as String? - ..uri = pigeonMap['uri'] as String? - ..packageName = pigeonMap['packageName'] as String? - ..formatHint = pigeonMap['formatHint'] as String? - ..httpHeaders = pigeonMap['httpHeaders'] as Map?; - } -} - -class LoopingMessage { - int? textureId; - bool? isLooping; - - Object encode() { - final Map pigeonMap = {}; - pigeonMap['textureId'] = textureId; - pigeonMap['isLooping'] = isLooping; - return pigeonMap; - } - - static LoopingMessage decode(Object message) { - final Map pigeonMap = message as Map; - return LoopingMessage() - ..textureId = pigeonMap['textureId'] as int? - ..isLooping = pigeonMap['isLooping'] as bool?; - } -} - -class VolumeMessage { - int? textureId; - double? volume; - - Object encode() { - final Map pigeonMap = {}; - pigeonMap['textureId'] = textureId; - pigeonMap['volume'] = volume; - return pigeonMap; - } - - static VolumeMessage decode(Object message) { - final Map pigeonMap = message as Map; - return VolumeMessage() - ..textureId = pigeonMap['textureId'] as int? - ..volume = pigeonMap['volume'] as double?; - } -} - -class PlaybackSpeedMessage { - int? textureId; - double? speed; - - Object encode() { - final Map pigeonMap = {}; - pigeonMap['textureId'] = textureId; - pigeonMap['speed'] = speed; - return pigeonMap; - } - - static PlaybackSpeedMessage decode(Object message) { - final Map pigeonMap = message as Map; - return PlaybackSpeedMessage() - ..textureId = pigeonMap['textureId'] as int? - ..speed = pigeonMap['speed'] as double?; - } -} - -class PositionMessage { - int? textureId; - int? position; - - Object encode() { - final Map pigeonMap = {}; - pigeonMap['textureId'] = textureId; - pigeonMap['position'] = position; - return pigeonMap; - } - - static PositionMessage decode(Object message) { - final Map pigeonMap = message as Map; - return PositionMessage() - ..textureId = pigeonMap['textureId'] as int? - ..position = pigeonMap['position'] as int?; - } -} - -class MixWithOthersMessage { - bool? mixWithOthers; - - Object encode() { - final Map pigeonMap = {}; - pigeonMap['mixWithOthers'] = mixWithOthers; - return pigeonMap; - } - - static MixWithOthersMessage decode(Object message) { - final Map pigeonMap = message as Map; - return MixWithOthersMessage() - ..mixWithOthers = pigeonMap['mixWithOthers'] as bool?; - } -} - -class VideoPlayerApi { - Future initialize() async { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.initialize', StandardMessageCodec()); - final Map? replyMap = - await channel.send(null) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - // noop - } - } - - Future create(CreateMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.create', StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - return TextureMessage.decode(replyMap['result']!); - } - } - - Future dispose(TextureMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.dispose', StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - // noop - } - } - - Future setLooping(LoopingMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setLooping', StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - // noop - } - } - - Future setVolume(VolumeMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setVolume', StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - // noop - } - } - - Future setPlaybackSpeed(PlaybackSpeedMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed', - StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - // noop - } - } - - Future play(TextureMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.play', StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - // noop - } - } - - Future position(TextureMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.position', StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - return PositionMessage.decode(replyMap['result']!); - } - } - - Future seekTo(PositionMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.seekTo', StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - // noop - } - } - - Future pause(TextureMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.pause', StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - // noop - } - } - - Future setMixWithOthers(MixWithOthersMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers', - StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - // noop - } - } -} diff --git a/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart b/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart new file mode 100644 index 000000000000..1a679d5915b4 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart @@ -0,0 +1,539 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v1.0.17), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name +// @dart = 2.12 +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; + +import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; +import 'package:flutter/services.dart'; + +class TextureMessage { + TextureMessage({ + required this.textureId, + }); + + int textureId; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['textureId'] = textureId; + return pigeonMap; + } + + static TextureMessage decode(Object message) { + final Map pigeonMap = message as Map; + return TextureMessage( + textureId: pigeonMap['textureId']! as int, + ); + } +} + +class LoopingMessage { + LoopingMessage({ + required this.textureId, + required this.isLooping, + }); + + int textureId; + bool isLooping; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['textureId'] = textureId; + pigeonMap['isLooping'] = isLooping; + return pigeonMap; + } + + static LoopingMessage decode(Object message) { + final Map pigeonMap = message as Map; + return LoopingMessage( + textureId: pigeonMap['textureId']! as int, + isLooping: pigeonMap['isLooping']! as bool, + ); + } +} + +class VolumeMessage { + VolumeMessage({ + required this.textureId, + required this.volume, + }); + + int textureId; + double volume; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['textureId'] = textureId; + pigeonMap['volume'] = volume; + return pigeonMap; + } + + static VolumeMessage decode(Object message) { + final Map pigeonMap = message as Map; + return VolumeMessage( + textureId: pigeonMap['textureId']! as int, + volume: pigeonMap['volume']! as double, + ); + } +} + +class PlaybackSpeedMessage { + PlaybackSpeedMessage({ + required this.textureId, + required this.speed, + }); + + int textureId; + double speed; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['textureId'] = textureId; + pigeonMap['speed'] = speed; + return pigeonMap; + } + + static PlaybackSpeedMessage decode(Object message) { + final Map pigeonMap = message as Map; + return PlaybackSpeedMessage( + textureId: pigeonMap['textureId']! as int, + speed: pigeonMap['speed']! as double, + ); + } +} + +class PositionMessage { + PositionMessage({ + required this.textureId, + required this.position, + }); + + int textureId; + int position; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['textureId'] = textureId; + pigeonMap['position'] = position; + return pigeonMap; + } + + static PositionMessage decode(Object message) { + final Map pigeonMap = message as Map; + return PositionMessage( + textureId: pigeonMap['textureId']! as int, + position: pigeonMap['position']! as int, + ); + } +} + +class CreateMessage { + CreateMessage({ + this.asset, + this.uri, + this.packageName, + this.formatHint, + required this.httpHeaders, + }); + + String? asset; + String? uri; + String? packageName; + String? formatHint; + Map httpHeaders; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['asset'] = asset; + pigeonMap['uri'] = uri; + pigeonMap['packageName'] = packageName; + pigeonMap['formatHint'] = formatHint; + pigeonMap['httpHeaders'] = httpHeaders; + return pigeonMap; + } + + static CreateMessage decode(Object message) { + final Map pigeonMap = message as Map; + return CreateMessage( + asset: pigeonMap['asset'] as String?, + uri: pigeonMap['uri'] as String?, + packageName: pigeonMap['packageName'] as String?, + formatHint: pigeonMap['formatHint'] as String?, + httpHeaders: (pigeonMap['httpHeaders'] as Map?)! + .cast(), + ); + } +} + +class MixWithOthersMessage { + MixWithOthersMessage({ + required this.mixWithOthers, + }); + + bool mixWithOthers; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['mixWithOthers'] = mixWithOthers; + return pigeonMap; + } + + static MixWithOthersMessage decode(Object message) { + final Map pigeonMap = message as Map; + return MixWithOthersMessage( + mixWithOthers: pigeonMap['mixWithOthers']! as bool, + ); + } +} + +class _VideoPlayerApiCodec extends StandardMessageCodec { + const _VideoPlayerApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is CreateMessage) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is LoopingMessage) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is MixWithOthersMessage) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else if (value is PlaybackSpeedMessage) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); + } else if (value is PositionMessage) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); + } else if (value is TextureMessage) { + buffer.putUint8(133); + writeValue(buffer, value.encode()); + } else if (value is VolumeMessage) { + buffer.putUint8(134); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return CreateMessage.decode(readValue(buffer)!); + + case 129: + return LoopingMessage.decode(readValue(buffer)!); + + case 130: + return MixWithOthersMessage.decode(readValue(buffer)!); + + case 131: + return PlaybackSpeedMessage.decode(readValue(buffer)!); + + case 132: + return PositionMessage.decode(readValue(buffer)!); + + case 133: + return TextureMessage.decode(readValue(buffer)!); + + case 134: + return VolumeMessage.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +class VideoPlayerApi { + /// Constructor for [VideoPlayerApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + VideoPlayerApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _VideoPlayerApiCodec(); + + Future initialize() async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.initialize', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send(null) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future create(CreateMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.create', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_msg]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return (replyMap['result'] as TextureMessage?)!; + } + } + + Future dispose(TextureMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.dispose', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_msg]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setLooping(LoopingMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setLooping', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_msg]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setVolume(VolumeMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setVolume', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_msg]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setPlaybackSpeed(PlaybackSpeedMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_msg]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future play(TextureMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.play', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_msg]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future position(TextureMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.position', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_msg]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return (replyMap['result'] as PositionMessage?)!; + } + } + + Future seekTo(PositionMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.seekTo', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_msg]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future pause(TextureMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.pause', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_msg]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setMixWithOthers(MixWithOthersMessage arg_msg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_msg]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} diff --git a/packages/video_player/video_player_avfoundation/pigeons/copyright.txt b/packages/video_player/video_player_avfoundation/pigeons/copyright.txt new file mode 100644 index 000000000000..1236b63caf3a --- /dev/null +++ b/packages/video_player/video_player_avfoundation/pigeons/copyright.txt @@ -0,0 +1,3 @@ +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. diff --git a/packages/video_player/video_player_avfoundation/pigeons/messages.dart b/packages/video_player/video_player_avfoundation/pigeons/messages.dart index 42d96408da87..d357caf81e3d 100644 --- a/packages/video_player/video_player_avfoundation/pigeons/messages.dart +++ b/packages/video_player/video_player_avfoundation/pigeons/messages.dart @@ -2,68 +2,83 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.9 - -import 'package:pigeon/pigeon_lib.dart'; +import 'package:pigeon/pigeon.dart'; +@ConfigurePigeon(PigeonOptions( + dartOut: 'lib/src/messages.g.dart', + dartTestOut: 'test/test_api.dart', + objcHeaderOut: 'ios/Classes/messages.g.h', + objcSourceOut: 'ios/Classes/messages.g.m', + objcOptions: ObjcOptions( + prefix: 'FLT', + ), + copyrightHeader: 'pigeons/copyright.txt', +)) class TextureMessage { + TextureMessage(this.textureId); int textureId; } class LoopingMessage { + LoopingMessage(this.textureId, this.isLooping); int textureId; bool isLooping; } class VolumeMessage { + VolumeMessage(this.textureId, this.volume); int textureId; double volume; } class PlaybackSpeedMessage { + PlaybackSpeedMessage(this.textureId, this.speed); int textureId; double speed; } class PositionMessage { + PositionMessage(this.textureId, this.position); int textureId; int position; } class CreateMessage { - String asset; - String uri; - String packageName; - String formatHint; - Map httpHeaders; + CreateMessage({required this.httpHeaders}); + String? asset; + String? uri; + String? packageName; + String? formatHint; + Map httpHeaders; } class MixWithOthersMessage { + MixWithOthersMessage(this.mixWithOthers); bool mixWithOthers; } @HostApi(dartHostTestHandler: 'TestHostVideoPlayerApi') abstract class VideoPlayerApi { + @ObjCSelector('initialize') void initialize(); + @ObjCSelector('create:') TextureMessage create(CreateMessage msg); + @ObjCSelector('dispose:') void dispose(TextureMessage msg); + @ObjCSelector('setLooping:') void setLooping(LoopingMessage msg); + @ObjCSelector('setVolume:') void setVolume(VolumeMessage msg); + @ObjCSelector('setPlaybackSpeed:') void setPlaybackSpeed(PlaybackSpeedMessage msg); + @ObjCSelector('play:') void play(TextureMessage msg); + @ObjCSelector('position:') PositionMessage position(TextureMessage msg); + @ObjCSelector('seekTo:') void seekTo(PositionMessage msg); + @ObjCSelector('pause:') void pause(TextureMessage msg); + @ObjCSelector('setMixWithOthers:') void setMixWithOthers(MixWithOthersMessage msg); } - -void configurePigeon(PigeonOptions opts) { - opts.dartOut = 'lib/src/messages.dart'; - opts.dartTestOut = 'test/test_api.dart'; - opts.objcHeaderOut = 'ios/Classes/messages.h'; - opts.objcSourceOut = 'ios/Classes/messages.m'; - opts.objcOptions.prefix = 'FLT'; - opts.javaOut = - 'android/src/main/java/io/flutter/plugins/videoplayer/Messages.java'; - opts.javaOptions.package = 'io.flutter.plugins.videoplayer'; -} diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index 8b00249ee453..044d15b890b2 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_avfoundation description: iOS implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.2.18 +version: 2.3.0 environment: sdk: ">=2.14.0 <3.0.0" @@ -24,4 +24,4 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - pigeon: ^0.1.21 + pigeon: ^1.0.17 diff --git a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart index adb29a53562f..9b6d1dfa195c 100644 --- a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart +++ b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart @@ -6,7 +6,7 @@ import 'dart:ui'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:video_player_avfoundation/src/messages.dart'; +import 'package:video_player_avfoundation/src/messages.g.dart'; import 'package:video_player_avfoundation/video_player_avfoundation.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; @@ -26,7 +26,7 @@ class _ApiLogger implements TestHostVideoPlayerApi { TextureMessage create(CreateMessage arg) { log.add('create'); createMessage = arg; - return TextureMessage()..textureId = 3; + return TextureMessage(textureId: 3); } @override @@ -62,7 +62,7 @@ class _ApiLogger implements TestHostVideoPlayerApi { PositionMessage position(TextureMessage arg) { log.add('position'); textureMessage = arg; - return PositionMessage()..position = 234; + return PositionMessage(textureId: arg.textureId, position: 234); } @override diff --git a/packages/video_player/video_player_avfoundation/test/test_api.dart b/packages/video_player/video_player_avfoundation/test/test_api.dart index b173705891d5..191358e23024 100644 --- a/packages/video_player/video_player_avfoundation/test/test_api.dart +++ b/packages/video_player/video_player_avfoundation/test/test_api.dart @@ -1,34 +1,100 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - -// Autogenerated from Pigeon (v0.1.21), do not edit directly. +// Autogenerated from Pigeon (v1.0.17), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis +// ignore_for_file: avoid_relative_lib_imports // @dart = 2.12 import 'dart:async'; import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; +import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:video_player_avfoundation/src/messages.dart'; + +// TODO(gaaclarke): The following output had to be tweaked from a relative path to a uri. +import 'package:video_player_avfoundation/src/messages.g.dart'; + +class _TestHostVideoPlayerApiCodec extends StandardMessageCodec { + const _TestHostVideoPlayerApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is CreateMessage) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is LoopingMessage) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is MixWithOthersMessage) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else if (value is PlaybackSpeedMessage) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); + } else if (value is PositionMessage) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); + } else if (value is TextureMessage) { + buffer.putUint8(133); + writeValue(buffer, value.encode()); + } else if (value is VolumeMessage) { + buffer.putUint8(134); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return CreateMessage.decode(readValue(buffer)!); + + case 129: + return LoopingMessage.decode(readValue(buffer)!); + + case 130: + return MixWithOthersMessage.decode(readValue(buffer)!); + + case 131: + return PlaybackSpeedMessage.decode(readValue(buffer)!); + + case 132: + return PositionMessage.decode(readValue(buffer)!); + + case 133: + return TextureMessage.decode(readValue(buffer)!); + + case 134: + return VolumeMessage.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} abstract class TestHostVideoPlayerApi { + static const MessageCodec codec = _TestHostVideoPlayerApiCodec(); + void initialize(); - TextureMessage create(CreateMessage arg); - void dispose(TextureMessage arg); - void setLooping(LoopingMessage arg); - void setVolume(VolumeMessage arg); - void setPlaybackSpeed(PlaybackSpeedMessage arg); - void play(TextureMessage arg); - PositionMessage position(TextureMessage arg); - void seekTo(PositionMessage arg); - void pause(TextureMessage arg); - void setMixWithOthers(MixWithOthersMessage arg); - static void setup(TestHostVideoPlayerApi? api) { + TextureMessage create(CreateMessage msg); + void dispose(TextureMessage msg); + void setLooping(LoopingMessage msg); + void setVolume(VolumeMessage msg); + void setPlaybackSpeed(PlaybackSpeedMessage msg); + void play(TextureMessage msg); + PositionMessage position(TextureMessage msg); + void seekTo(PositionMessage msg); + void pause(TextureMessage msg); + void setMixWithOthers(MixWithOthersMessage msg); + static void setup(TestHostVideoPlayerApi? api, + {BinaryMessenger? binaryMessenger}) { { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.initialize', - StandardMessageCodec()); + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.initialize', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { @@ -40,157 +106,193 @@ abstract class TestHostVideoPlayerApi { } } { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.create', StandardMessageCodec()); + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.create', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.create was null. Expected CreateMessage.'); - final CreateMessage input = CreateMessage.decode(message!); - final TextureMessage output = api.create(input); - return {'result': output.encode()}; + 'Argument for dev.flutter.pigeon.VideoPlayerApi.create was null.'); + final List args = (message as List?)!; + final CreateMessage? arg_msg = (args[0] as CreateMessage?); + assert(arg_msg != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.create was null, expected non-null CreateMessage.'); + final TextureMessage output = api.create(arg_msg!); + return {'result': output}; }); } } { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.dispose', StandardMessageCodec()); + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.dispose', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.dispose was null. Expected TextureMessage.'); - final TextureMessage input = TextureMessage.decode(message!); - api.dispose(input); + 'Argument for dev.flutter.pigeon.VideoPlayerApi.dispose was null.'); + final List args = (message as List?)!; + final TextureMessage? arg_msg = (args[0] as TextureMessage?); + assert(arg_msg != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.dispose was null, expected non-null TextureMessage.'); + api.dispose(arg_msg!); return {}; }); } } { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setLooping', - StandardMessageCodec()); + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setLooping', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setLooping was null. Expected LoopingMessage.'); - final LoopingMessage input = LoopingMessage.decode(message!); - api.setLooping(input); + 'Argument for dev.flutter.pigeon.VideoPlayerApi.setLooping was null.'); + final List args = (message as List?)!; + final LoopingMessage? arg_msg = (args[0] as LoopingMessage?); + assert(arg_msg != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.setLooping was null, expected non-null LoopingMessage.'); + api.setLooping(arg_msg!); return {}; }); } } { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setVolume', - StandardMessageCodec()); + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setVolume', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setVolume was null. Expected VolumeMessage.'); - final VolumeMessage input = VolumeMessage.decode(message!); - api.setVolume(input); + 'Argument for dev.flutter.pigeon.VideoPlayerApi.setVolume was null.'); + final List args = (message as List?)!; + final VolumeMessage? arg_msg = (args[0] as VolumeMessage?); + assert(arg_msg != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.setVolume was null, expected non-null VolumeMessage.'); + api.setVolume(arg_msg!); return {}; }); } } { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed', - StandardMessageCodec()); + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed was null. Expected PlaybackSpeedMessage.'); - final PlaybackSpeedMessage input = - PlaybackSpeedMessage.decode(message!); - api.setPlaybackSpeed(input); + 'Argument for dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed was null.'); + final List args = (message as List?)!; + final PlaybackSpeedMessage? arg_msg = + (args[0] as PlaybackSpeedMessage?); + assert(arg_msg != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed was null, expected non-null PlaybackSpeedMessage.'); + api.setPlaybackSpeed(arg_msg!); return {}; }); } } { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.play', StandardMessageCodec()); + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.play', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.play was null. Expected TextureMessage.'); - final TextureMessage input = TextureMessage.decode(message!); - api.play(input); + 'Argument for dev.flutter.pigeon.VideoPlayerApi.play was null.'); + final List args = (message as List?)!; + final TextureMessage? arg_msg = (args[0] as TextureMessage?); + assert(arg_msg != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.play was null, expected non-null TextureMessage.'); + api.play(arg_msg!); return {}; }); } } { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.position', StandardMessageCodec()); + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.position', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.position was null. Expected TextureMessage.'); - final TextureMessage input = TextureMessage.decode(message!); - final PositionMessage output = api.position(input); - return {'result': output.encode()}; + 'Argument for dev.flutter.pigeon.VideoPlayerApi.position was null.'); + final List args = (message as List?)!; + final TextureMessage? arg_msg = (args[0] as TextureMessage?); + assert(arg_msg != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.position was null, expected non-null TextureMessage.'); + final PositionMessage output = api.position(arg_msg!); + return {'result': output}; }); } } { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.seekTo', StandardMessageCodec()); + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.seekTo', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.seekTo was null. Expected PositionMessage.'); - final PositionMessage input = PositionMessage.decode(message!); - api.seekTo(input); + 'Argument for dev.flutter.pigeon.VideoPlayerApi.seekTo was null.'); + final List args = (message as List?)!; + final PositionMessage? arg_msg = (args[0] as PositionMessage?); + assert(arg_msg != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.seekTo was null, expected non-null PositionMessage.'); + api.seekTo(arg_msg!); return {}; }); } } { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.pause', StandardMessageCodec()); + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.pause', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.pause was null. Expected TextureMessage.'); - final TextureMessage input = TextureMessage.decode(message!); - api.pause(input); + 'Argument for dev.flutter.pigeon.VideoPlayerApi.pause was null.'); + final List args = (message as List?)!; + final TextureMessage? arg_msg = (args[0] as TextureMessage?); + assert(arg_msg != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.pause was null, expected non-null TextureMessage.'); + api.pause(arg_msg!); return {}; }); } } { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers', - StandardMessageCodec()); + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers was null. Expected MixWithOthersMessage.'); - final MixWithOthersMessage input = - MixWithOthersMessage.decode(message!); - api.setMixWithOthers(input); + 'Argument for dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers was null.'); + final List args = (message as List?)!; + final MixWithOthersMessage? arg_msg = + (args[0] as MixWithOthersMessage?); + assert(arg_msg != null, + 'Argument for dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers was null, expected non-null MixWithOthersMessage.'); + api.setMixWithOthers(arg_msg!); return {}; }); } From 16a7daee62dac9511a79e07872dc518eb13e5d4a Mon Sep 17 00:00:00 2001 From: IlyaMax <33570996+IlyaMax@users.noreply.github.com> Date: Tue, 15 Feb 2022 21:35:20 +0200 Subject: [PATCH 194/600] [video_player] add allowBackgroundPlayback option platform interface changes (#4807) --- .../CHANGELOG.md | 4 ++++ .../lib/video_player_platform_interface.dart | 9 ++++++++- .../pubspec.yaml | 2 +- .../test/video_player_options_test.dart | 19 +++++++++++++++++++ 4 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 packages/video_player/video_player_platform_interface/test/video_player_options_test.dart diff --git a/packages/video_player/video_player_platform_interface/CHANGELOG.md b/packages/video_player/video_player_platform_interface/CHANGELOG.md index 353f2734642c..52612207d8f3 100644 --- a/packages/video_player/video_player_platform_interface/CHANGELOG.md +++ b/packages/video_player/video_player_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 5.1.0 + +* Adds `allowBackgroundPlayback` to `VideoPlayerOptions`. + ## 5.0.2 * Adds the Pigeon definitions used to create the method channel implementation. diff --git a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart index f10fe667f0ea..8a61005c429e 100644 --- a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart +++ b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart @@ -346,7 +346,14 @@ class VideoPlayerOptions { // in all of the other video player packages, fix this, and then update // the other packages to use const. // ignore: prefer_const_constructors_in_immutables - VideoPlayerOptions({this.mixWithOthers = false}); + VideoPlayerOptions({ + this.mixWithOthers = false, + this.allowBackgroundPlayback = false, + }); + + /// Set this to true to keep playing video in background, when app goes in background. + /// The default value is false. + final bool allowBackgroundPlayback; /// Set this to true to mix the video players audio with other audio sources. /// The default value is false diff --git a/packages/video_player/video_player_platform_interface/pubspec.yaml b/packages/video_player/video_player_platform_interface/pubspec.yaml index 7d5bd83278c0..b66fa0b46d75 100644 --- a/packages/video_player/video_player_platform_interface/pubspec.yaml +++ b/packages/video_player/video_player_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/video_player/v issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 5.0.2 +version: 5.1.0 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/video_player/video_player_platform_interface/test/video_player_options_test.dart b/packages/video_player/video_player_platform_interface/test/video_player_options_test.dart new file mode 100644 index 000000000000..a65fc392c2f8 --- /dev/null +++ b/packages/video_player/video_player_platform_interface/test/video_player_options_test.dart @@ -0,0 +1,19 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:video_player_platform_interface/video_player_platform_interface.dart'; + +void main() { + test( + 'VideoPlayerOptions allowBackgroundPlayback defaults to false', + () { + final VideoPlayerOptions options = VideoPlayerOptions(); + expect(options.allowBackgroundPlayback, false); + }, + ); + test( + 'VideoPlayerOptions mixWithOthers defaults to false', + () { + final VideoPlayerOptions options = VideoPlayerOptions(); + expect(options.mixWithOthers, false); + }, + ); +} From ca550c8054fa3746b540af56bfa4a65b1438e0cd Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Tue, 15 Feb 2022 12:31:05 -0800 Subject: [PATCH 195/600] fix license (#4858) --- .../test/video_player_options_test.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/video_player/video_player_platform_interface/test/video_player_options_test.dart b/packages/video_player/video_player_platform_interface/test/video_player_options_test.dart index a65fc392c2f8..8091cd580514 100644 --- a/packages/video_player/video_player_platform_interface/test/video_player_options_test.dart +++ b/packages/video_player/video_player_platform_interface/test/video_player_options_test.dart @@ -1,3 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + import 'package:flutter_test/flutter_test.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; From 2b08dcb98e719c8c10c2aa94c11f23ec725c92e4 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 15 Feb 2022 16:10:22 -0500 Subject: [PATCH 196/600] Roll Flutter from ca2a751661c8 to e9f83cf4f9df (10 revisions) (#4857) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 16adeff802c9..7fcaa2f6e72f 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ca2a751661c8a1fe71025df3d32bd6ac6ecf221c +e9f83cf4f9dfe233cc58ae9814ad144e9a022c7c From ce0870ebd3e007183b506d18c276adb5aa6d21f7 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 15 Feb 2022 13:50:26 -0800 Subject: [PATCH 197/600] [webview_flutter] Fix debuggingEnabled on Android (#4859) --- .../webview_flutter_android/CHANGELOG.md | 3 +- .../lib/webview_android_widget.dart | 2 +- .../webview_flutter_android/pubspec.yaml | 2 +- .../test/android_webview_test.dart | 19 +++--- .../test/instance_manager_test.dart | 2 +- .../test/webview_android_widget_test.dart | 63 ++++++++++++++++--- 6 files changed, 72 insertions(+), 19 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 145bc8fcc068..26fb5ab624f8 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.8.3 +* Fixes a bug causing `debuggingEnabled` to always be set to true. * Fixes an integration test race condition. ## 2.8.2 diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart index bf85ac97687e..7200aaa4a322 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart @@ -708,6 +708,6 @@ class WebViewProxy { /// /// See [android_webview.WebView].setWebContentsDebuggingEnabled. Future setWebContentsDebuggingEnabled(bool enabled) { - return android_webview.WebView.setWebContentsDebuggingEnabled(true); + return android_webview.WebView.setWebContentsDebuggingEnabled(enabled); } } diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index b2329d3fb904..704dc6719a7e 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_android description: A Flutter plugin that provides a WebView widget on Android. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.8.2 +version: 2.8.3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart index 8688a1977d83..91385ff2d364 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart @@ -32,7 +32,7 @@ void main() { TestWidgetsFlutterBinding.ensureInitialized(); group('Android WebView', () { - group('$WebView', () { + group('WebView', () { late MockTestWebViewHostApi mockPlatformHostApi; late InstanceManager instanceManager; @@ -55,11 +55,16 @@ void main() { verify(mockPlatformHostApi.create(webViewInstanceId, false)); }); - test('setWebContentsDebuggingEnabled', () { + test('setWebContentsDebuggingEnabled true', () { WebView.setWebContentsDebuggingEnabled(true); verify(mockPlatformHostApi.setWebContentsDebuggingEnabled(true)); }); + test('setWebContentsDebuggingEnabled false', () { + WebView.setWebContentsDebuggingEnabled(false); + verify(mockPlatformHostApi.setWebContentsDebuggingEnabled(false)); + }); + test('loadData', () { webView.loadData( data: 'hello', @@ -314,7 +319,7 @@ void main() { }); }); - group('$WebSettings', () { + group('WebSettings', () { late MockTestWebSettingsHostApi mockPlatformHostApi; late InstanceManager instanceManager; @@ -440,7 +445,7 @@ void main() { }); }); - group('$JavaScriptChannel', () { + group('JavaScriptChannel', () { late JavaScriptChannelFlutterApiImpl flutterApi; late InstanceManager instanceManager; @@ -468,7 +473,7 @@ void main() { }); }); - group('$WebViewClient', () { + group('WebViewClient', () { late WebViewClientFlutterApiImpl flutterApi; late InstanceManager instanceManager; @@ -583,7 +588,7 @@ void main() { }); }); - group('$DownloadListener', () { + group('DownloadListener', () { late DownloadListenerFlutterApiImpl flutterApi; late InstanceManager instanceManager; @@ -621,7 +626,7 @@ void main() { }); }); - group('$WebChromeClient', () { + group('WebChromeClient', () { late WebChromeClientFlutterApiImpl flutterApi; late InstanceManager instanceManager; diff --git a/packages/webview_flutter/webview_flutter_android/test/instance_manager_test.dart b/packages/webview_flutter/webview_flutter_android/test/instance_manager_test.dart index fd020fc362c8..3aeb005efb86 100644 --- a/packages/webview_flutter/webview_flutter_android/test/instance_manager_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/instance_manager_test.dart @@ -6,7 +6,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:webview_flutter_android/src/instance_manager.dart'; void main() { - group('$InstanceManager', () { + group('InstanceManager', () { late InstanceManager testInstanceManager; setUp(() { diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart index fed1c1113e55..af1093914047 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart @@ -11,9 +11,13 @@ import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:webview_flutter_android/src/android_webview.dart' as android_webview; +import 'package:webview_flutter_android/src/android_webview_api_impls.dart'; +import 'package:webview_flutter_android/src/instance_manager.dart'; import 'package:webview_flutter_android/webview_android_widget.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; +import 'android_webview.pigeon.dart'; +import 'android_webview_test.mocks.dart' show MockTestWebViewHostApi; import 'webview_android_widget_test.mocks.dart'; @GenerateMocks([ @@ -31,7 +35,7 @@ import 'webview_android_widget_test.mocks.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - group('$WebViewAndroidWidget', () { + group('WebViewAndroidWidget', () { late MockFlutterAssetManager mockFlutterAssetManager; late MockWebView mockWebView; late MockWebSettings mockWebSettings; @@ -93,7 +97,7 @@ void main() { webChromeClient = testController.webChromeClient; } - testWidgets('$WebViewAndroidWidget', (WidgetTester tester) async { + testWidgets('WebViewAndroidWidget', (WidgetTester tester) async { await buildWidget(tester); verify(mockWebSettings.setDomStorageEnabled(true)); @@ -119,7 +123,7 @@ void main() { }, ); - group('$CreationParams', () { + group('CreationParams', () { testWidgets('initialUrl', (WidgetTester tester) async { await buildWidget( tester, @@ -201,7 +205,7 @@ void main() { expect(javaScriptChannels[1].channelName, 'b'); }); - group('$WebSettings', () { + group('WebSettings', () { testWidgets('javascriptMode', (WidgetTester tester) async { await buildWidget( tester, @@ -232,7 +236,7 @@ void main() { expect(testController.webViewClient.shouldOverrideUrlLoading, isTrue); }); - testWidgets('debuggingEnabled', (WidgetTester tester) async { + testWidgets('debuggingEnabled true', (WidgetTester tester) async { await buildWidget( tester, creationParams: CreationParams( @@ -247,6 +251,21 @@ void main() { verify(mockWebViewProxy.setWebContentsDebuggingEnabled(true)); }); + testWidgets('debuggingEnabled false', (WidgetTester tester) async { + await buildWidget( + tester, + creationParams: CreationParams( + webSettings: WebSettings( + userAgent: const WebSetting.absent(), + debuggingEnabled: false, + hasNavigationDelegate: false, + ), + ), + ); + + verify(mockWebViewProxy.setWebContentsDebuggingEnabled(false)); + }); + testWidgets('userAgent', (WidgetTester tester) async { await buildWidget( tester, @@ -278,7 +297,7 @@ void main() { }); }); - group('$WebViewPlatformController', () { + group('WebViewPlatformController', () { testWidgets('loadFile without "file://" prefix', (WidgetTester tester) async { await buildWidget(tester); @@ -667,7 +686,7 @@ void main() { }); }); - group('$WebViewPlatformCallbacksHandler', () { + group('WebViewPlatformCallbacksHandler', () { testWidgets('onPageStarted', (WidgetTester tester) async { await buildWidget(tester); webViewClient.onPageStarted(mockWebView, 'https://google.com'); @@ -773,7 +792,7 @@ void main() { verify(mockWebView.loadUrl('https://google.com', {})); }); - group('$JavascriptChannelRegistry', () { + group('JavascriptChannelRegistry', () { testWidgets('onJavascriptChannelMessage', (WidgetTester tester) async { await buildWidget(tester); @@ -792,4 +811,32 @@ void main() { }); }); }); + + group('WebViewProxy', () { + late MockTestWebViewHostApi mockPlatformHostApi; + late InstanceManager instanceManager; + + setUp(() { + // WebViewProxy calls static methods that can't be mocked, so the mocks + // have to be set up at the next layer down, by mocking the implementation + // of WebView itstelf. + mockPlatformHostApi = MockTestWebViewHostApi(); + TestWebViewHostApi.setup(mockPlatformHostApi); + instanceManager = InstanceManager(); + android_webview.WebView.api = + WebViewHostApiImpl(instanceManager: instanceManager); + }); + + test('setWebContentsDebuggingEnabled true', () { + const WebViewProxy webViewProxy = WebViewProxy(); + webViewProxy.setWebContentsDebuggingEnabled(true); + verify(mockPlatformHostApi.setWebContentsDebuggingEnabled(true)); + }); + + test('setWebContentsDebuggingEnabled false', () { + const WebViewProxy webViewProxy = WebViewProxy(); + webViewProxy.setWebContentsDebuggingEnabled(false); + verify(mockPlatformHostApi.setWebContentsDebuggingEnabled(false)); + }); + }); } From d1105387872040cad7e09476b95332ffe0c531bf Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 15 Feb 2022 17:20:24 -0800 Subject: [PATCH 198/600] [flutter_plugin_tool] Fix iOS/macOS naming (#4861) --- .../tool/lib/src/build_examples_command.dart | 44 ++-- script/tool/lib/src/common/core.dart | 16 +- script/tool/lib/src/common/plugin_utils.dart | 16 +- .../tool/lib/src/drive_examples_command.dart | 63 +++--- script/tool/lib/src/lint_android_command.dart | 2 +- script/tool/lib/src/native_test_command.dart | 42 ++-- script/tool/lib/src/test_command.dart | 2 +- .../tool/lib/src/xcode_analyze_command.dart | 22 +- .../test/build_examples_command_test.dart | 30 +-- .../tool/test/common/plugin_utils_test.dart | 190 +++++++++--------- .../test/drive_examples_command_test.dart | 64 +++--- .../tool/test/lint_android_command_test.dart | 8 +- .../tool/test/native_test_command_test.dart | 96 ++++----- script/tool/test/test_command_test.dart | 2 +- script/tool/test/util.dart | 14 +- .../tool/test/xcode_analyze_command_test.dart | 22 +- 16 files changed, 316 insertions(+), 317 deletions(-) diff --git a/script/tool/lib/src/build_examples_command.dart b/script/tool/lib/src/build_examples_command.dart index 82ed074c462a..b88cfe309258 100644 --- a/script/tool/lib/src/build_examples_command.dart +++ b/script/tool/lib/src/build_examples_command.dart @@ -33,7 +33,7 @@ const int _exitInvalidPluginToolsConfig = 4; // Flutter build types. These are the values passed to `flutter build `. const String _flutterBuildTypeAndroid = 'apk'; -const String _flutterBuildTypeIos = 'ios'; +const String _flutterBuildTypeIOS = 'ios'; const String _flutterBuildTypeLinux = 'linux'; const String _flutterBuildTypeMacOS = 'macos'; const String _flutterBuildTypeWeb = 'web'; @@ -48,12 +48,12 @@ class BuildExamplesCommand extends PackageLoopingCommand { ProcessRunner processRunner = const ProcessRunner(), Platform platform = const LocalPlatform(), }) : super(packagesDir, processRunner: processRunner, platform: platform) { - argParser.addFlag(kPlatformLinux); - argParser.addFlag(kPlatformMacos); - argParser.addFlag(kPlatformWeb); - argParser.addFlag(kPlatformWindows); - argParser.addFlag(kPlatformWinUwp); - argParser.addFlag(kPlatformIos); + argParser.addFlag(platformLinux); + argParser.addFlag(platformMacOS); + argParser.addFlag(platformWeb); + argParser.addFlag(platformWindows); + argParser.addFlag(platformWinUwp); + argParser.addFlag(platformIOS); argParser.addFlag(_platformFlagApk); argParser.addOption( kEnableExperiment, @@ -68,39 +68,39 @@ class BuildExamplesCommand extends PackageLoopingCommand { { _platformFlagApk: const _PlatformDetails( 'Android', - pluginPlatform: kPlatformAndroid, + pluginPlatform: platformAndroid, flutterBuildType: _flutterBuildTypeAndroid, ), - kPlatformIos: const _PlatformDetails( + platformIOS: const _PlatformDetails( 'iOS', - pluginPlatform: kPlatformIos, - flutterBuildType: _flutterBuildTypeIos, + pluginPlatform: platformIOS, + flutterBuildType: _flutterBuildTypeIOS, extraBuildFlags: ['--no-codesign'], ), - kPlatformLinux: const _PlatformDetails( + platformLinux: const _PlatformDetails( 'Linux', - pluginPlatform: kPlatformLinux, + pluginPlatform: platformLinux, flutterBuildType: _flutterBuildTypeLinux, ), - kPlatformMacos: const _PlatformDetails( + platformMacOS: const _PlatformDetails( 'macOS', - pluginPlatform: kPlatformMacos, + pluginPlatform: platformMacOS, flutterBuildType: _flutterBuildTypeMacOS, ), - kPlatformWeb: const _PlatformDetails( + platformWeb: const _PlatformDetails( 'web', - pluginPlatform: kPlatformWeb, + pluginPlatform: platformWeb, flutterBuildType: _flutterBuildTypeWeb, ), - kPlatformWindows: const _PlatformDetails( + platformWindows: const _PlatformDetails( 'Win32', - pluginPlatform: kPlatformWindows, + pluginPlatform: platformWindows, pluginPlatformVariant: platformVariantWin32, flutterBuildType: _flutterBuildTypeWin32, ), - kPlatformWinUwp: const _PlatformDetails( + platformWinUwp: const _PlatformDetails( 'UWP', - pluginPlatform: kPlatformWindows, + pluginPlatform: platformWindows, pluginPlatformVariant: platformVariantWinUwp, flutterBuildType: _flutterBuildTypeWinUwp, ), @@ -288,7 +288,7 @@ class BuildExamplesCommand extends PackageLoopingCommand { if (!uwpDirectory.existsSync()) { print('Creating temporary winuwp folder'); final int exitCode = await processRunner.runAndStream(flutterCommand, - ['create', '--platforms=$kPlatformWinUwp', '.'], + ['create', '--platforms=$platformWinUwp', '.'], workingDir: example.directory); if (exitCode == 0) { temporaryPlatformDirectory = uwpDirectory; diff --git a/script/tool/lib/src/common/core.dart b/script/tool/lib/src/common/core.dart index 53778eccb87f..15a0d6f1f3b2 100644 --- a/script/tool/lib/src/common/core.dart +++ b/script/tool/lib/src/common/core.dart @@ -11,40 +11,40 @@ import 'package:yaml/yaml.dart'; typedef Print = void Function(Object? object); /// Key for APK (Android) platform. -const String kPlatformAndroid = 'android'; +const String platformAndroid = 'android'; /// Key for IPA (iOS) platform. -const String kPlatformIos = 'ios'; +const String platformIOS = 'ios'; /// Key for linux platform. -const String kPlatformLinux = 'linux'; +const String platformLinux = 'linux'; /// Key for macos platform. -const String kPlatformMacos = 'macos'; +const String platformMacOS = 'macos'; /// Key for Web platform. -const String kPlatformWeb = 'web'; +const String platformWeb = 'web'; /// Key for windows platform. /// /// Note that this corresponds to the Win32 variant for flutter commands like /// `build` and `run`, but is a general platform containing all Windows /// variants for purposes of the `platform` section of a plugin pubspec). -const String kPlatformWindows = 'windows'; +const String platformWindows = 'windows'; /// Key for WinUWP platform. /// /// Note that UWP is a platform for the purposes of flutter commands like /// `build` and `run`, but a variant of the `windows` platform for the purposes /// of plugin pubspecs). -const String kPlatformWinUwp = 'winuwp'; +const String platformWinUwp = 'winuwp'; /// Key for Win32 variant of the Windows platform. const String platformVariantWin32 = 'win32'; /// Key for UWP variant of the Windows platform. /// -/// See the note on [kPlatformWinUwp]. +/// See the note on [platformWinUwp]. const String platformVariantWinUwp = 'uwp'; /// Key for enable experiment. diff --git a/script/tool/lib/src/common/plugin_utils.dart b/script/tool/lib/src/common/plugin_utils.dart index 6cfe9928d689..081ce7f1e815 100644 --- a/script/tool/lib/src/common/plugin_utils.dart +++ b/script/tool/lib/src/common/plugin_utils.dart @@ -39,12 +39,12 @@ bool pluginSupportsPlatform( PlatformSupport? requiredMode, String? variant, }) { - assert(platform == kPlatformIos || - platform == kPlatformAndroid || - platform == kPlatformWeb || - platform == kPlatformMacos || - platform == kPlatformWindows || - platform == kPlatformLinux); + assert(platform == platformIOS || + platform == platformAndroid || + platform == platformWeb || + platform == platformMacOS || + platform == platformWindows || + platform == platformLinux); final YamlMap? platformEntry = _readPlatformPubspecSectionForPlugin(platform, plugin); @@ -73,7 +73,7 @@ bool pluginSupportsPlatform( // Platforms with variants have a default variant when unspecified for // backward compatibility. Must match the flutter tool logic. const Map defaultVariants = { - kPlatformWindows: platformVariantWin32, + platformWindows: platformVariantWin32, }; if (variant != defaultVariants[platform]) { return false; @@ -87,7 +87,7 @@ bool pluginSupportsPlatform( /// Returns true if [plugin] includes native code for [platform], as opposed to /// being implemented entirely in Dart. bool pluginHasNativeCodeForPlatform(String platform, RepositoryPackage plugin) { - if (platform == kPlatformWeb) { + if (platform == platformWeb) { // Web plugins are always Dart-only. return false; } diff --git a/script/tool/lib/src/drive_examples_command.dart b/script/tool/lib/src/drive_examples_command.dart index 5bf0298e4e32..d81153a0fefa 100644 --- a/script/tool/lib/src/drive_examples_command.dart +++ b/script/tool/lib/src/drive_examples_command.dart @@ -25,19 +25,19 @@ class DriveExamplesCommand extends PackageLoopingCommand { ProcessRunner processRunner = const ProcessRunner(), Platform platform = const LocalPlatform(), }) : super(packagesDir, processRunner: processRunner, platform: platform) { - argParser.addFlag(kPlatformAndroid, + argParser.addFlag(platformAndroid, help: 'Runs the Android implementation of the examples'); - argParser.addFlag(kPlatformIos, + argParser.addFlag(platformIOS, help: 'Runs the iOS implementation of the examples'); - argParser.addFlag(kPlatformLinux, + argParser.addFlag(platformLinux, help: 'Runs the Linux implementation of the examples'); - argParser.addFlag(kPlatformMacos, + argParser.addFlag(platformMacOS, help: 'Runs the macOS implementation of the examples'); - argParser.addFlag(kPlatformWeb, + argParser.addFlag(platformWeb, help: 'Runs the web implementation of the examples'); - argParser.addFlag(kPlatformWindows, + argParser.addFlag(platformWindows, help: 'Runs the Windows (Win32) implementation of the examples'); - argParser.addFlag(kPlatformWinUwp, + argParser.addFlag(platformWinUwp, help: 'Runs the UWP implementation of the examples [currently a no-op]'); argParser.addOption( @@ -64,13 +64,13 @@ class DriveExamplesCommand extends PackageLoopingCommand { @override Future initializeRun() async { final List platformSwitches = [ - kPlatformAndroid, - kPlatformIos, - kPlatformLinux, - kPlatformMacos, - kPlatformWeb, - kPlatformWindows, - kPlatformWinUwp, + platformAndroid, + platformIOS, + platformLinux, + platformMacOS, + platformWeb, + platformWindows, + platformWinUwp, ]; final int platformCount = platformSwitches .where((String platform) => getBoolArg(platform)) @@ -85,12 +85,12 @@ class DriveExamplesCommand extends PackageLoopingCommand { throw ToolExit(_exitNoPlatformFlags); } - if (getBoolArg(kPlatformWinUwp)) { + if (getBoolArg(platformWinUwp)) { logWarning('Driving UWP applications is not yet supported'); } String? androidDevice; - if (getBoolArg(kPlatformAndroid)) { + if (getBoolArg(platformAndroid)) { final List devices = await _getDevicesForPlatform('android'); if (devices.isEmpty) { printError('No Android devices available'); @@ -99,24 +99,24 @@ class DriveExamplesCommand extends PackageLoopingCommand { androidDevice = devices.first; } - String? iosDevice; - if (getBoolArg(kPlatformIos)) { + String? iOSDevice; + if (getBoolArg(platformIOS)) { final List devices = await _getDevicesForPlatform('ios'); if (devices.isEmpty) { printError('No iOS devices available'); throw ToolExit(_exitNoAvailableDevice); } - iosDevice = devices.first; + iOSDevice = devices.first; } _targetDeviceFlags = >{ - if (getBoolArg(kPlatformAndroid)) - kPlatformAndroid: ['-d', androidDevice!], - if (getBoolArg(kPlatformIos)) kPlatformIos: ['-d', iosDevice!], - if (getBoolArg(kPlatformLinux)) kPlatformLinux: ['-d', 'linux'], - if (getBoolArg(kPlatformMacos)) kPlatformMacos: ['-d', 'macos'], - if (getBoolArg(kPlatformWeb)) - kPlatformWeb: [ + if (getBoolArg(platformAndroid)) + platformAndroid: ['-d', androidDevice!], + if (getBoolArg(platformIOS)) platformIOS: ['-d', iOSDevice!], + if (getBoolArg(platformLinux)) platformLinux: ['-d', 'linux'], + if (getBoolArg(platformMacOS)) platformMacOS: ['-d', 'macos'], + if (getBoolArg(platformWeb)) + platformWeb: [ '-d', 'web-server', '--web-port=7357', @@ -124,12 +124,11 @@ class DriveExamplesCommand extends PackageLoopingCommand { if (platform.environment.containsKey('CHROME_EXECUTABLE')) '--chrome-binary=${platform.environment['CHROME_EXECUTABLE']}', ], - if (getBoolArg(kPlatformWindows)) - kPlatformWindows: ['-d', 'windows'], + if (getBoolArg(platformWindows)) + platformWindows: ['-d', 'windows'], // TODO(stuartmorgan): Check these flags once drive supports UWP: // https://github.com/flutter/flutter/issues/82821 - if (getBoolArg(kPlatformWinUwp)) - kPlatformWinUwp: ['-d', 'winuwp'], + if (getBoolArg(platformWinUwp)) platformWinUwp: ['-d', 'winuwp'], }; } @@ -148,9 +147,9 @@ class DriveExamplesCommand extends PackageLoopingCommand { in _targetDeviceFlags.entries) { final String platform = entry.key; String? variant; - if (platform == kPlatformWindows) { + if (platform == platformWindows) { variant = platformVariantWin32; - } else if (platform == kPlatformWinUwp) { + } else if (platform == platformWinUwp) { variant = platformVariantWinUwp; // TODO(stuartmorgan): Remove this once drive supports UWP. // https://github.com/flutter/flutter/issues/82821 diff --git a/script/tool/lib/src/lint_android_command.dart b/script/tool/lib/src/lint_android_command.dart index 49b2181a4615..8368160c4c95 100644 --- a/script/tool/lib/src/lint_android_command.dart +++ b/script/tool/lib/src/lint_android_command.dart @@ -32,7 +32,7 @@ class LintAndroidCommand extends PackageLoopingCommand { @override Future runForPackage(RepositoryPackage package) async { - if (!pluginSupportsPlatform(kPlatformAndroid, package, + if (!pluginSupportsPlatform(platformAndroid, package, requiredMode: PlatformSupport.inline)) { return PackageResult.skip( 'Plugin does not have an Android implemenatation.'); diff --git a/script/tool/lib/src/native_test_command.dart b/script/tool/lib/src/native_test_command.dart index 0b0dd26ba227..a0d2ebd4e23c 100644 --- a/script/tool/lib/src/native_test_command.dart +++ b/script/tool/lib/src/native_test_command.dart @@ -17,9 +17,9 @@ import 'common/xcode.dart'; const String _unitTestFlag = 'unit'; const String _integrationTestFlag = 'integration'; -const String _iosDestinationFlag = 'ios-destination'; +const String _iOSDestinationFlag = 'ios-destination'; -const int _exitNoIosSimulators = 3; +const int _exitNoIOSSimulators = 3; /// The command to run native tests for plugins: /// - iOS and macOS: XCTests (XCUnitTest and XCUITest) @@ -34,17 +34,17 @@ class NativeTestCommand extends PackageLoopingCommand { }) : _xcode = Xcode(processRunner: processRunner, log: true), super(packagesDir, processRunner: processRunner, platform: platform) { argParser.addOption( - _iosDestinationFlag, + _iOSDestinationFlag, help: 'Specify the destination when running iOS tests.\n' 'This is passed to the `-destination` argument in the xcodebuild command.\n' 'See https://developer.apple.com/library/archive/technotes/tn2339/_index.html#//apple_ref/doc/uid/DTS40014588-CH1-UNIT ' 'for details on how to specify the destination.', ); - argParser.addFlag(kPlatformAndroid, help: 'Runs Android tests'); - argParser.addFlag(kPlatformIos, help: 'Runs iOS tests'); - argParser.addFlag(kPlatformLinux, help: 'Runs Linux tests'); - argParser.addFlag(kPlatformMacos, help: 'Runs macOS tests'); - argParser.addFlag(kPlatformWindows, help: 'Runs Windows tests'); + argParser.addFlag(platformAndroid, help: 'Runs Android tests'); + argParser.addFlag(platformIOS, help: 'Runs iOS tests'); + argParser.addFlag(platformLinux, help: 'Runs Linux tests'); + argParser.addFlag(platformMacOS, help: 'Runs macOS tests'); + argParser.addFlag(platformWindows, help: 'Runs Windows tests'); // By default, both unit tests and integration tests are run, but provide // flags to disable one or the other. @@ -55,7 +55,7 @@ class NativeTestCommand extends PackageLoopingCommand { } // The device destination flags for iOS tests. - List _iosDestinationFlags = []; + List _iOSDestinationFlags = []; final Xcode _xcode; @@ -84,11 +84,11 @@ this command. @override Future initializeRun() async { _platforms = { - kPlatformAndroid: _PlatformDetails('Android', _testAndroid), - kPlatformIos: _PlatformDetails('iOS', _testIos), - kPlatformLinux: _PlatformDetails('Linux', _testLinux), - kPlatformMacos: _PlatformDetails('macOS', _testMacOS), - kPlatformWindows: _PlatformDetails('Windows', _testWindows), + platformAndroid: _PlatformDetails('Android', _testAndroid), + platformIOS: _PlatformDetails('iOS', _testIOS), + platformLinux: _PlatformDetails('Linux', _testLinux), + platformMacOS: _PlatformDetails('macOS', _testMacOS), + platformWindows: _PlatformDetails('Windows', _testWindows), }; _requestedPlatforms = _platforms.keys .where((String platform) => getBoolArg(platform)) @@ -105,29 +105,29 @@ this command. throw ToolExit(exitInvalidArguments); } - if (getBoolArg(kPlatformWindows) && getBoolArg(_integrationTestFlag)) { + if (getBoolArg(platformWindows) && getBoolArg(_integrationTestFlag)) { logWarning('This command currently only supports unit tests for Windows. ' 'See https://github.com/flutter/flutter/issues/70233.'); } - if (getBoolArg(kPlatformLinux) && getBoolArg(_integrationTestFlag)) { + if (getBoolArg(platformLinux) && getBoolArg(_integrationTestFlag)) { logWarning('This command currently only supports unit tests for Linux. ' 'See https://github.com/flutter/flutter/issues/70235.'); } // iOS-specific run-level state. if (_requestedPlatforms.contains('ios')) { - String destination = getStringArg(_iosDestinationFlag); + String destination = getStringArg(_iOSDestinationFlag); if (destination.isEmpty) { final String? simulatorId = await _xcode.findBestAvailableIphoneSimulator(); if (simulatorId == null) { printError('Cannot find any available iOS simulators.'); - throw ToolExit(_exitNoIosSimulators); + throw ToolExit(_exitNoIOSSimulators); } destination = 'id=$simulatorId'; } - _iosDestinationFlags = [ + _iOSDestinationFlags = [ '-destination', destination, ]; @@ -333,9 +333,9 @@ this command. return _PlatformResult(RunState.succeeded); } - Future<_PlatformResult> _testIos(RepositoryPackage plugin, _TestMode mode) { + Future<_PlatformResult> _testIOS(RepositoryPackage plugin, _TestMode mode) { return _runXcodeTests(plugin, 'iOS', mode, - extraFlags: _iosDestinationFlags); + extraFlags: _iOSDestinationFlags); } Future<_PlatformResult> _testMacOS(RepositoryPackage plugin, _TestMode mode) { diff --git a/script/tool/lib/src/test_command.dart b/script/tool/lib/src/test_command.dart index ee3540d90d3c..2c5dd9934b45 100644 --- a/script/tool/lib/src/test_command.dart +++ b/script/tool/lib/src/test_command.dart @@ -62,7 +62,7 @@ class TestCommand extends PackageLoopingCommand { '--color', if (experiment.isNotEmpty) '--enable-experiment=$experiment', // TODO(ditman): Remove this once all plugins are migrated to 'drive'. - if (pluginSupportsPlatform(kPlatformWeb, package)) '--platform=chrome', + if (pluginSupportsPlatform(platformWeb, package)) '--platform=chrome', ], workingDir: package.directory, ); diff --git a/script/tool/lib/src/xcode_analyze_command.dart b/script/tool/lib/src/xcode_analyze_command.dart index 3d34dab9f087..4298acb1c7e5 100644 --- a/script/tool/lib/src/xcode_analyze_command.dart +++ b/script/tool/lib/src/xcode_analyze_command.dart @@ -21,8 +21,8 @@ class XcodeAnalyzeCommand extends PackageLoopingCommand { Platform platform = const LocalPlatform(), }) : _xcode = Xcode(processRunner: processRunner, log: true), super(packagesDir, processRunner: processRunner, platform: platform) { - argParser.addFlag(kPlatformIos, help: 'Analyze iOS'); - argParser.addFlag(kPlatformMacos, help: 'Analyze macOS'); + argParser.addFlag(platformIOS, help: 'Analyze iOS'); + argParser.addFlag(platformMacOS, help: 'Analyze macOS'); } final Xcode _xcode; @@ -36,7 +36,7 @@ class XcodeAnalyzeCommand extends PackageLoopingCommand { @override Future initializeRun() async { - if (!(getBoolArg(kPlatformIos) || getBoolArg(kPlatformMacos))) { + if (!(getBoolArg(platformIOS) || getBoolArg(platformMacOS))) { printError('At least one platform flag must be provided.'); throw ToolExit(exitInvalidArguments); } @@ -44,28 +44,28 @@ class XcodeAnalyzeCommand extends PackageLoopingCommand { @override Future runForPackage(RepositoryPackage package) async { - final bool testIos = getBoolArg(kPlatformIos) && - pluginSupportsPlatform(kPlatformIos, package, + final bool testIOS = getBoolArg(platformIOS) && + pluginSupportsPlatform(platformIOS, package, requiredMode: PlatformSupport.inline); - final bool testMacos = getBoolArg(kPlatformMacos) && - pluginSupportsPlatform(kPlatformMacos, package, + final bool testMacOS = getBoolArg(platformMacOS) && + pluginSupportsPlatform(platformMacOS, package, requiredMode: PlatformSupport.inline); final bool multiplePlatformsRequested = - getBoolArg(kPlatformIos) && getBoolArg(kPlatformMacos); - if (!(testIos || testMacos)) { + getBoolArg(platformIOS) && getBoolArg(platformMacOS); + if (!(testIOS || testMacOS)) { return PackageResult.skip('Not implemented for target platform(s).'); } final List failures = []; - if (testIos && + if (testIOS && !await _analyzePlugin(package, 'iOS', extraFlags: [ '-destination', 'generic/platform=iOS Simulator' ])) { failures.add('iOS'); } - if (testMacos && !await _analyzePlugin(package, 'macOS')) { + if (testMacOS && !await _analyzePlugin(package, 'macOS')) { failures.add('macOS'); } diff --git a/script/tool/test/build_examples_command_test.dart b/script/tool/test/build_examples_command_test.dart index c3b0cb9d5cd1..6d8f0b9d6486 100644 --- a/script/tool/test/build_examples_command_test.dart +++ b/script/tool/test/build_examples_command_test.dart @@ -57,7 +57,7 @@ void main() { test('fails if building fails', () async { createFakePlugin('plugin', packagesDir, platformSupport: { - kPlatformIos: const PlatformDetails(PlatformSupport.inline), + platformIOS: const PlatformDetails(PlatformSupport.inline), }); processRunner @@ -86,7 +86,7 @@ void main() { createFakePlugin('plugin', packagesDir, examples: [], platformSupport: { - kPlatformIos: const PlatformDetails(PlatformSupport.inline) + platformIOS: const PlatformDetails(PlatformSupport.inline) }); processRunner @@ -136,7 +136,7 @@ void main() { mockPlatform.isMacOS = true; final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, platformSupport: { - kPlatformIos: const PlatformDetails(PlatformSupport.inline), + platformIOS: const PlatformDetails(PlatformSupport.inline), }); final Directory pluginExampleDirectory = @@ -193,7 +193,7 @@ void main() { mockPlatform.isLinux = true; final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, platformSupport: { - kPlatformLinux: const PlatformDetails(PlatformSupport.inline), + platformLinux: const PlatformDetails(PlatformSupport.inline), }); final Directory pluginExampleDirectory = @@ -242,7 +242,7 @@ void main() { mockPlatform.isMacOS = true; final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, platformSupport: { - kPlatformMacos: const PlatformDetails(PlatformSupport.inline), + platformMacOS: const PlatformDetails(PlatformSupport.inline), }); final Directory pluginExampleDirectory = @@ -288,7 +288,7 @@ void main() { test('building for web', () async { final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, platformSupport: { - kPlatformWeb: const PlatformDetails(PlatformSupport.inline), + platformWeb: const PlatformDetails(PlatformSupport.inline), }); final Directory pluginExampleDirectory = @@ -338,7 +338,7 @@ void main() { mockPlatform.isWindows = true; final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, platformSupport: { - kPlatformWindows: const PlatformDetails(PlatformSupport.inline), + platformWindows: const PlatformDetails(PlatformSupport.inline), }); final Directory pluginExampleDirectory = @@ -389,7 +389,7 @@ void main() { createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/test', ], platformSupport: { - kPlatformWindows: const PlatformDetails(PlatformSupport.federated, + platformWindows: const PlatformDetails(PlatformSupport.federated, variants: [platformVariantWinUwp]), }); @@ -419,7 +419,7 @@ void main() { createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/test', ], platformSupport: { - kPlatformWindows: const PlatformDetails(PlatformSupport.federated, + platformWindows: const PlatformDetails(PlatformSupport.federated, variants: [platformVariantWinUwp]), }); @@ -470,7 +470,7 @@ void main() { test('building for Android', () async { final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline), + platformAndroid: const PlatformDetails(PlatformSupport.inline), }); final Directory pluginExampleDirectory = @@ -499,7 +499,7 @@ void main() { test('enable-experiment flag for Android', () async { final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline), + platformAndroid: const PlatformDetails(PlatformSupport.inline), }); final Directory pluginExampleDirectory = @@ -521,7 +521,7 @@ void main() { test('enable-experiment flag for ios', () async { final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, platformSupport: { - kPlatformIos: const PlatformDetails(PlatformSupport.inline), + platformIOS: const PlatformDetails(PlatformSupport.inline), }); final Directory pluginExampleDirectory = @@ -547,7 +547,7 @@ void main() { test('logs skipped platforms', () async { createFakePlugin('plugin', packagesDir, platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline), + platformAndroid: const PlatformDetails(PlatformSupport.inline), }); final List output = await runCapturingPrint( @@ -681,8 +681,8 @@ void main() { mockPlatform.isLinux = true; final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, platformSupport: { - kPlatformLinux: const PlatformDetails(PlatformSupport.inline), - kPlatformMacos: const PlatformDetails(PlatformSupport.inline), + platformLinux: const PlatformDetails(PlatformSupport.inline), + platformMacOS: const PlatformDetails(PlatformSupport.inline), }); final Directory pluginExampleDirectory = diff --git a/script/tool/test/common/plugin_utils_test.dart b/script/tool/test/common/plugin_utils_test.dart index ac619e2622e0..cedd40acb7d6 100644 --- a/script/tool/test/common/plugin_utils_test.dart +++ b/script/tool/test/common/plugin_utils_test.dart @@ -25,109 +25,109 @@ void main() { final RepositoryPackage plugin = RepositoryPackage(createFakePlugin('plugin', packagesDir)); - expect(pluginSupportsPlatform(kPlatformAndroid, plugin), isFalse); - expect(pluginSupportsPlatform(kPlatformIos, plugin), isFalse); - expect(pluginSupportsPlatform(kPlatformLinux, plugin), isFalse); - expect(pluginSupportsPlatform(kPlatformMacos, plugin), isFalse); - expect(pluginSupportsPlatform(kPlatformWeb, plugin), isFalse); - expect(pluginSupportsPlatform(kPlatformWindows, plugin), isFalse); + expect(pluginSupportsPlatform(platformAndroid, plugin), isFalse); + expect(pluginSupportsPlatform(platformIOS, plugin), isFalse); + expect(pluginSupportsPlatform(platformLinux, plugin), isFalse); + expect(pluginSupportsPlatform(platformMacOS, plugin), isFalse); + expect(pluginSupportsPlatform(platformWeb, plugin), isFalse); + expect(pluginSupportsPlatform(platformWindows, plugin), isFalse); }); test('all platforms', () async { final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( 'plugin', packagesDir, platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline), - kPlatformIos: const PlatformDetails(PlatformSupport.inline), - kPlatformLinux: const PlatformDetails(PlatformSupport.inline), - kPlatformMacos: const PlatformDetails(PlatformSupport.inline), - kPlatformWeb: const PlatformDetails(PlatformSupport.inline), - kPlatformWindows: const PlatformDetails(PlatformSupport.inline), + platformAndroid: const PlatformDetails(PlatformSupport.inline), + platformIOS: const PlatformDetails(PlatformSupport.inline), + platformLinux: const PlatformDetails(PlatformSupport.inline), + platformMacOS: const PlatformDetails(PlatformSupport.inline), + platformWeb: const PlatformDetails(PlatformSupport.inline), + platformWindows: const PlatformDetails(PlatformSupport.inline), })); - expect(pluginSupportsPlatform(kPlatformAndroid, plugin), isTrue); - expect(pluginSupportsPlatform(kPlatformIos, plugin), isTrue); - expect(pluginSupportsPlatform(kPlatformLinux, plugin), isTrue); - expect(pluginSupportsPlatform(kPlatformMacos, plugin), isTrue); - expect(pluginSupportsPlatform(kPlatformWeb, plugin), isTrue); - expect(pluginSupportsPlatform(kPlatformWindows, plugin), isTrue); + expect(pluginSupportsPlatform(platformAndroid, plugin), isTrue); + expect(pluginSupportsPlatform(platformIOS, plugin), isTrue); + expect(pluginSupportsPlatform(platformLinux, plugin), isTrue); + expect(pluginSupportsPlatform(platformMacOS, plugin), isTrue); + expect(pluginSupportsPlatform(platformWeb, plugin), isTrue); + expect(pluginSupportsPlatform(platformWindows, plugin), isTrue); }); test('some platforms', () async { final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( 'plugin', packagesDir, platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline), - kPlatformLinux: const PlatformDetails(PlatformSupport.inline), - kPlatformWeb: const PlatformDetails(PlatformSupport.inline), + platformAndroid: const PlatformDetails(PlatformSupport.inline), + platformLinux: const PlatformDetails(PlatformSupport.inline), + platformWeb: const PlatformDetails(PlatformSupport.inline), })); - expect(pluginSupportsPlatform(kPlatformAndroid, plugin), isTrue); - expect(pluginSupportsPlatform(kPlatformIos, plugin), isFalse); - expect(pluginSupportsPlatform(kPlatformLinux, plugin), isTrue); - expect(pluginSupportsPlatform(kPlatformMacos, plugin), isFalse); - expect(pluginSupportsPlatform(kPlatformWeb, plugin), isTrue); - expect(pluginSupportsPlatform(kPlatformWindows, plugin), isFalse); + expect(pluginSupportsPlatform(platformAndroid, plugin), isTrue); + expect(pluginSupportsPlatform(platformIOS, plugin), isFalse); + expect(pluginSupportsPlatform(platformLinux, plugin), isTrue); + expect(pluginSupportsPlatform(platformMacOS, plugin), isFalse); + expect(pluginSupportsPlatform(platformWeb, plugin), isTrue); + expect(pluginSupportsPlatform(platformWindows, plugin), isFalse); }); test('inline plugins are only detected as inline', () async { final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( 'plugin', packagesDir, platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline), - kPlatformIos: const PlatformDetails(PlatformSupport.inline), - kPlatformLinux: const PlatformDetails(PlatformSupport.inline), - kPlatformMacos: const PlatformDetails(PlatformSupport.inline), - kPlatformWeb: const PlatformDetails(PlatformSupport.inline), - kPlatformWindows: const PlatformDetails(PlatformSupport.inline), + platformAndroid: const PlatformDetails(PlatformSupport.inline), + platformIOS: const PlatformDetails(PlatformSupport.inline), + platformLinux: const PlatformDetails(PlatformSupport.inline), + platformMacOS: const PlatformDetails(PlatformSupport.inline), + platformWeb: const PlatformDetails(PlatformSupport.inline), + platformWindows: const PlatformDetails(PlatformSupport.inline), })); expect( - pluginSupportsPlatform(kPlatformAndroid, plugin, + pluginSupportsPlatform(platformAndroid, plugin, requiredMode: PlatformSupport.inline), isTrue); expect( - pluginSupportsPlatform(kPlatformAndroid, plugin, + pluginSupportsPlatform(platformAndroid, plugin, requiredMode: PlatformSupport.federated), isFalse); expect( - pluginSupportsPlatform(kPlatformIos, plugin, + pluginSupportsPlatform(platformIOS, plugin, requiredMode: PlatformSupport.inline), isTrue); expect( - pluginSupportsPlatform(kPlatformIos, plugin, + pluginSupportsPlatform(platformIOS, plugin, requiredMode: PlatformSupport.federated), isFalse); expect( - pluginSupportsPlatform(kPlatformLinux, plugin, + pluginSupportsPlatform(platformLinux, plugin, requiredMode: PlatformSupport.inline), isTrue); expect( - pluginSupportsPlatform(kPlatformLinux, plugin, + pluginSupportsPlatform(platformLinux, plugin, requiredMode: PlatformSupport.federated), isFalse); expect( - pluginSupportsPlatform(kPlatformMacos, plugin, + pluginSupportsPlatform(platformMacOS, plugin, requiredMode: PlatformSupport.inline), isTrue); expect( - pluginSupportsPlatform(kPlatformMacos, plugin, + pluginSupportsPlatform(platformMacOS, plugin, requiredMode: PlatformSupport.federated), isFalse); expect( - pluginSupportsPlatform(kPlatformWeb, plugin, + pluginSupportsPlatform(platformWeb, plugin, requiredMode: PlatformSupport.inline), isTrue); expect( - pluginSupportsPlatform(kPlatformWeb, plugin, + pluginSupportsPlatform(platformWeb, plugin, requiredMode: PlatformSupport.federated), isFalse); expect( - pluginSupportsPlatform(kPlatformWindows, plugin, + pluginSupportsPlatform(platformWindows, plugin, requiredMode: PlatformSupport.inline), isTrue); expect( - pluginSupportsPlatform(kPlatformWindows, plugin, + pluginSupportsPlatform(platformWindows, plugin, requiredMode: PlatformSupport.federated), isFalse); }); @@ -136,60 +136,60 @@ void main() { final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( 'plugin', packagesDir, platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.federated), - kPlatformIos: const PlatformDetails(PlatformSupport.federated), - kPlatformLinux: const PlatformDetails(PlatformSupport.federated), - kPlatformMacos: const PlatformDetails(PlatformSupport.federated), - kPlatformWeb: const PlatformDetails(PlatformSupport.federated), - kPlatformWindows: const PlatformDetails(PlatformSupport.federated), + platformAndroid: const PlatformDetails(PlatformSupport.federated), + platformIOS: const PlatformDetails(PlatformSupport.federated), + platformLinux: const PlatformDetails(PlatformSupport.federated), + platformMacOS: const PlatformDetails(PlatformSupport.federated), + platformWeb: const PlatformDetails(PlatformSupport.federated), + platformWindows: const PlatformDetails(PlatformSupport.federated), })); expect( - pluginSupportsPlatform(kPlatformAndroid, plugin, + pluginSupportsPlatform(platformAndroid, plugin, requiredMode: PlatformSupport.federated), isTrue); expect( - pluginSupportsPlatform(kPlatformAndroid, plugin, + pluginSupportsPlatform(platformAndroid, plugin, requiredMode: PlatformSupport.inline), isFalse); expect( - pluginSupportsPlatform(kPlatformIos, plugin, + pluginSupportsPlatform(platformIOS, plugin, requiredMode: PlatformSupport.federated), isTrue); expect( - pluginSupportsPlatform(kPlatformIos, plugin, + pluginSupportsPlatform(platformIOS, plugin, requiredMode: PlatformSupport.inline), isFalse); expect( - pluginSupportsPlatform(kPlatformLinux, plugin, + pluginSupportsPlatform(platformLinux, plugin, requiredMode: PlatformSupport.federated), isTrue); expect( - pluginSupportsPlatform(kPlatformLinux, plugin, + pluginSupportsPlatform(platformLinux, plugin, requiredMode: PlatformSupport.inline), isFalse); expect( - pluginSupportsPlatform(kPlatformMacos, plugin, + pluginSupportsPlatform(platformMacOS, plugin, requiredMode: PlatformSupport.federated), isTrue); expect( - pluginSupportsPlatform(kPlatformMacos, plugin, + pluginSupportsPlatform(platformMacOS, plugin, requiredMode: PlatformSupport.inline), isFalse); expect( - pluginSupportsPlatform(kPlatformWeb, plugin, + pluginSupportsPlatform(platformWeb, plugin, requiredMode: PlatformSupport.federated), isTrue); expect( - pluginSupportsPlatform(kPlatformWeb, plugin, + pluginSupportsPlatform(platformWeb, plugin, requiredMode: PlatformSupport.inline), isFalse); expect( - pluginSupportsPlatform(kPlatformWindows, plugin, + pluginSupportsPlatform(platformWindows, plugin, requiredMode: PlatformSupport.federated), isTrue); expect( - pluginSupportsPlatform(kPlatformWindows, plugin, + pluginSupportsPlatform(platformWindows, plugin, requiredMode: PlatformSupport.inline), isFalse); }); @@ -199,16 +199,16 @@ void main() { 'plugin', packagesDir, platformSupport: { - kPlatformWindows: const PlatformDetails(PlatformSupport.inline), + platformWindows: const PlatformDetails(PlatformSupport.inline), }, )); expect( - pluginSupportsPlatform(kPlatformWindows, plugin, + pluginSupportsPlatform(platformWindows, plugin, variant: platformVariantWin32), isTrue); expect( - pluginSupportsPlatform(kPlatformWindows, plugin, + pluginSupportsPlatform(platformWindows, plugin, variant: platformVariantWinUwp), isFalse); }); @@ -217,18 +217,18 @@ void main() { final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( 'plugin', packagesDir, platformSupport: { - kPlatformWindows: const PlatformDetails( + platformWindows: const PlatformDetails( PlatformSupport.federated, variants: [platformVariantWin32, platformVariantWinUwp], ), })); expect( - pluginSupportsPlatform(kPlatformWindows, plugin, + pluginSupportsPlatform(platformWindows, plugin, variant: platformVariantWin32), isTrue); expect( - pluginSupportsPlatform(kPlatformWindows, plugin, + pluginSupportsPlatform(platformWindows, plugin, variant: platformVariantWinUwp), isTrue); }); @@ -237,18 +237,18 @@ void main() { final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( 'plugin', packagesDir, platformSupport: { - kPlatformWindows: const PlatformDetails( + platformWindows: const PlatformDetails( PlatformSupport.federated, variants: [platformVariantWin32], ), })); expect( - pluginSupportsPlatform(kPlatformWindows, plugin, + pluginSupportsPlatform(platformWindows, plugin, variant: platformVariantWin32), isTrue); expect( - pluginSupportsPlatform(kPlatformWindows, plugin, + pluginSupportsPlatform(platformWindows, plugin, variant: platformVariantWinUwp), isFalse); }); @@ -258,17 +258,17 @@ void main() { 'plugin', packagesDir, platformSupport: { - kPlatformWindows: const PlatformDetails(PlatformSupport.federated, + platformWindows: const PlatformDetails(PlatformSupport.federated, variants: [platformVariantWinUwp]), }, )); expect( - pluginSupportsPlatform(kPlatformWindows, plugin, + pluginSupportsPlatform(platformWindows, plugin, variant: platformVariantWin32), isFalse); expect( - pluginSupportsPlatform(kPlatformWindows, plugin, + pluginSupportsPlatform(platformWindows, plugin, variant: platformVariantWinUwp), isTrue); }); @@ -280,11 +280,11 @@ void main() { 'plugin', packagesDir, platformSupport: { - kPlatformWeb: const PlatformDetails(PlatformSupport.inline), + platformWeb: const PlatformDetails(PlatformSupport.inline), }, )); - expect(pluginHasNativeCodeForPlatform(kPlatformWeb, plugin), isFalse); + expect(pluginHasNativeCodeForPlatform(platformWeb, plugin), isFalse); }); test('returns false for a native-only plugin', () async { @@ -292,15 +292,15 @@ void main() { 'plugin', packagesDir, platformSupport: { - kPlatformLinux: const PlatformDetails(PlatformSupport.inline), - kPlatformMacos: const PlatformDetails(PlatformSupport.inline), - kPlatformWindows: const PlatformDetails(PlatformSupport.inline), + platformLinux: const PlatformDetails(PlatformSupport.inline), + platformMacOS: const PlatformDetails(PlatformSupport.inline), + platformWindows: const PlatformDetails(PlatformSupport.inline), }, )); - expect(pluginHasNativeCodeForPlatform(kPlatformLinux, plugin), isTrue); - expect(pluginHasNativeCodeForPlatform(kPlatformMacos, plugin), isTrue); - expect(pluginHasNativeCodeForPlatform(kPlatformWindows, plugin), isTrue); + expect(pluginHasNativeCodeForPlatform(platformLinux, plugin), isTrue); + expect(pluginHasNativeCodeForPlatform(platformMacOS, plugin), isTrue); + expect(pluginHasNativeCodeForPlatform(platformWindows, plugin), isTrue); }); test('returns true for a native+Dart plugin', () async { @@ -308,18 +308,18 @@ void main() { 'plugin', packagesDir, platformSupport: { - kPlatformLinux: const PlatformDetails(PlatformSupport.inline, + platformLinux: const PlatformDetails(PlatformSupport.inline, hasNativeCode: true, hasDartCode: true), - kPlatformMacos: const PlatformDetails(PlatformSupport.inline, + platformMacOS: const PlatformDetails(PlatformSupport.inline, hasNativeCode: true, hasDartCode: true), - kPlatformWindows: const PlatformDetails(PlatformSupport.inline, + platformWindows: const PlatformDetails(PlatformSupport.inline, hasNativeCode: true, hasDartCode: true), }, )); - expect(pluginHasNativeCodeForPlatform(kPlatformLinux, plugin), isTrue); - expect(pluginHasNativeCodeForPlatform(kPlatformMacos, plugin), isTrue); - expect(pluginHasNativeCodeForPlatform(kPlatformWindows, plugin), isTrue); + expect(pluginHasNativeCodeForPlatform(platformLinux, plugin), isTrue); + expect(pluginHasNativeCodeForPlatform(platformMacOS, plugin), isTrue); + expect(pluginHasNativeCodeForPlatform(platformWindows, plugin), isTrue); }); test('returns false for a Dart-only plugin', () async { @@ -327,18 +327,18 @@ void main() { 'plugin', packagesDir, platformSupport: { - kPlatformLinux: const PlatformDetails(PlatformSupport.inline, + platformLinux: const PlatformDetails(PlatformSupport.inline, hasNativeCode: false, hasDartCode: true), - kPlatformMacos: const PlatformDetails(PlatformSupport.inline, + platformMacOS: const PlatformDetails(PlatformSupport.inline, hasNativeCode: false, hasDartCode: true), - kPlatformWindows: const PlatformDetails(PlatformSupport.inline, + platformWindows: const PlatformDetails(PlatformSupport.inline, hasNativeCode: false, hasDartCode: true), }, )); - expect(pluginHasNativeCodeForPlatform(kPlatformLinux, plugin), isFalse); - expect(pluginHasNativeCodeForPlatform(kPlatformMacos, plugin), isFalse); - expect(pluginHasNativeCodeForPlatform(kPlatformWindows, plugin), isFalse); + expect(pluginHasNativeCodeForPlatform(platformLinux, plugin), isFalse); + expect(pluginHasNativeCodeForPlatform(platformMacOS, plugin), isFalse); + expect(pluginHasNativeCodeForPlatform(platformWindows, plugin), isFalse); }); }); } diff --git a/script/tool/test/drive_examples_command_test.dart b/script/tool/test/drive_examples_command_test.dart index 3c93d8bfe10c..9372c571b6f7 100644 --- a/script/tool/test/drive_examples_command_test.dart +++ b/script/tool/test/drive_examples_command_test.dart @@ -17,7 +17,7 @@ import 'package:test/test.dart'; import 'mocks.dart'; import 'util.dart'; -const String _fakeIosDevice = '67d5c3d1-8bdf-46ad-8f6b-b00e2a972dda'; +const String _fakeIOSDevice = '67d5c3d1-8bdf-46ad-8f6b-b00e2a972dda'; const String _fakeAndroidDevice = 'emulator-1234'; void main() { @@ -42,7 +42,7 @@ void main() { }); void setMockFlutterDevicesOutput({ - bool hasIosDevice = true, + bool hasIOSDevice = true, bool hasAndroidDevice = true, bool includeBanner = false, }) { @@ -54,7 +54,7 @@ void main() { ╚════════════════════════════════════════════════════════════════════════════╝ '''; final List devices = [ - if (hasIosDevice) '{"id": "$_fakeIosDevice", "targetPlatform": "ios"}', + if (hasIOSDevice) '{"id": "$_fakeIOSDevice", "targetPlatform": "ios"}', if (hasAndroidDevice) '{"id": "$_fakeAndroidDevice", "targetPlatform": "android-x86"}', ]; @@ -104,7 +104,7 @@ void main() { }); test('fails for iOS if no iOS devices are present', () async { - setMockFlutterDevicesOutput(hasIosDevice: false); + setMockFlutterDevicesOutput(hasIOSDevice: false); Error? commandError; final List output = await runCapturingPrint( @@ -130,7 +130,7 @@ void main() { 'example/integration_test/foo_test.dart', ], platformSupport: { - kPlatformIos: const PlatformDetails(PlatformSupport.inline), + platformIOS: const PlatformDetails(PlatformSupport.inline), }, ); @@ -195,8 +195,8 @@ void main() { 'example/test_driver/plugin.dart', ], platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline), - kPlatformIos: const PlatformDetails(PlatformSupport.inline), + platformAndroid: const PlatformDetails(PlatformSupport.inline), + platformIOS: const PlatformDetails(PlatformSupport.inline), }, ); @@ -225,7 +225,7 @@ void main() { const [ 'drive', '-d', - _fakeIosDevice, + _fakeIOSDevice, '--driver', 'test_driver/plugin_test.dart', '--target', @@ -245,8 +245,8 @@ void main() { 'example/test_driver/plugin_test.dart', ], platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline), - kPlatformIos: const PlatformDetails(PlatformSupport.inline), + platformAndroid: const PlatformDetails(PlatformSupport.inline), + platformIOS: const PlatformDetails(PlatformSupport.inline), }, ); @@ -278,8 +278,8 @@ void main() { 'example/lib/main.dart', ], platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline), - kPlatformIos: const PlatformDetails(PlatformSupport.inline), + platformAndroid: const PlatformDetails(PlatformSupport.inline), + platformIOS: const PlatformDetails(PlatformSupport.inline), }, ); @@ -314,8 +314,8 @@ void main() { 'example/integration_test/ignore_me.dart', ], platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline), - kPlatformIos: const PlatformDetails(PlatformSupport.inline), + platformAndroid: const PlatformDetails(PlatformSupport.inline), + platformIOS: const PlatformDetails(PlatformSupport.inline), }, ); @@ -344,7 +344,7 @@ void main() { const [ 'drive', '-d', - _fakeIosDevice, + _fakeIOSDevice, '--driver', 'test_driver/integration_test.dart', '--target', @@ -356,7 +356,7 @@ void main() { const [ 'drive', '-d', - _fakeIosDevice, + _fakeIOSDevice, '--driver', 'test_driver/integration_test.dart', '--target', @@ -400,7 +400,7 @@ void main() { 'example/test_driver/plugin.dart', ], platformSupport: { - kPlatformLinux: const PlatformDetails(PlatformSupport.inline), + platformLinux: const PlatformDetails(PlatformSupport.inline), }, ); @@ -473,7 +473,7 @@ void main() { 'example/macos/macos.swift', ], platformSupport: { - kPlatformMacos: const PlatformDetails(PlatformSupport.inline), + platformMacOS: const PlatformDetails(PlatformSupport.inline), }, ); @@ -544,7 +544,7 @@ void main() { 'example/test_driver/plugin.dart', ], platformSupport: { - kPlatformWeb: const PlatformDetails(PlatformSupport.inline), + platformWeb: const PlatformDetails(PlatformSupport.inline), }, ); @@ -593,7 +593,7 @@ void main() { 'example/test_driver/plugin.dart', ], platformSupport: { - kPlatformWeb: const PlatformDetails(PlatformSupport.inline), + platformWeb: const PlatformDetails(PlatformSupport.inline), }, ); @@ -670,7 +670,7 @@ void main() { 'example/test_driver/plugin.dart', ], platformSupport: { - kPlatformWindows: const PlatformDetails(PlatformSupport.inline), + platformWindows: const PlatformDetails(PlatformSupport.inline), }, ); @@ -717,7 +717,7 @@ void main() { 'example/test_driver/plugin.dart', ], platformSupport: { - kPlatformWindows: const PlatformDetails(PlatformSupport.inline, + platformWindows: const PlatformDetails(PlatformSupport.inline, variants: [platformVariantWinUwp]), }, ); @@ -751,7 +751,7 @@ void main() { 'example/test_driver/plugin.dart', ], platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline), + platformAndroid: const PlatformDetails(PlatformSupport.inline), }, ); @@ -801,7 +801,7 @@ void main() { 'example/test_driver/plugin.dart', ], platformSupport: { - kPlatformMacos: const PlatformDetails(PlatformSupport.inline), + platformMacOS: const PlatformDetails(PlatformSupport.inline), }, ); @@ -834,7 +834,7 @@ void main() { 'example/test_driver/plugin.dart', ], platformSupport: { - kPlatformMacos: const PlatformDetails(PlatformSupport.inline), + platformMacOS: const PlatformDetails(PlatformSupport.inline), }, ); @@ -889,8 +889,8 @@ void main() { 'example/test_driver/plugin.dart', ], platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline), - kPlatformIos: const PlatformDetails(PlatformSupport.inline), + platformAndroid: const PlatformDetails(PlatformSupport.inline), + platformIOS: const PlatformDetails(PlatformSupport.inline), }, ); @@ -914,7 +914,7 @@ void main() { const [ 'drive', '-d', - _fakeIosDevice, + _fakeIOSDevice, '--enable-experiment=exp1', '--driver', 'test_driver/plugin_test.dart', @@ -931,7 +931,7 @@ void main() { packagesDir, examples: [], platformSupport: { - kPlatformWeb: const PlatformDetails(PlatformSupport.inline), + platformWeb: const PlatformDetails(PlatformSupport.inline), }, ); @@ -963,7 +963,7 @@ void main() { 'example/integration_test/foo_test.dart', ], platformSupport: { - kPlatformWeb: const PlatformDetails(PlatformSupport.inline), + platformWeb: const PlatformDetails(PlatformSupport.inline), }, ); @@ -995,7 +995,7 @@ void main() { 'example/test_driver/integration_test.dart', ], platformSupport: { - kPlatformWeb: const PlatformDetails(PlatformSupport.inline), + platformWeb: const PlatformDetails(PlatformSupport.inline), }, ); @@ -1031,7 +1031,7 @@ void main() { 'example/integration_test/foo_test.dart', ], platformSupport: { - kPlatformMacos: const PlatformDetails(PlatformSupport.inline), + platformMacOS: const PlatformDetails(PlatformSupport.inline), }, ); diff --git a/script/tool/test/lint_android_command_test.dart b/script/tool/test/lint_android_command_test.dart index 5670a64f30d8..a9ad510f7ee9 100644 --- a/script/tool/test/lint_android_command_test.dart +++ b/script/tool/test/lint_android_command_test.dart @@ -44,7 +44,7 @@ void main() { createFakePlugin('plugin1', packagesDir, extraFiles: [ 'example/android/gradlew', ], platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline) + platformAndroid: const PlatformDetails(PlatformSupport.inline) }); final Directory androidDir = @@ -75,7 +75,7 @@ void main() { test('fails if gradlew is missing', () async { createFakePlugin('plugin1', packagesDir, platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline) + platformAndroid: const PlatformDetails(PlatformSupport.inline) }); Error? commandError; @@ -97,7 +97,7 @@ void main() { test('fails if linting finds issues', () async { createFakePlugin('plugin1', packagesDir, platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline) + platformAndroid: const PlatformDetails(PlatformSupport.inline) }); processRunner.mockProcessesForExecutable['gradlew'] = [ @@ -139,7 +139,7 @@ void main() { test('skips non-inline plugins', () async { createFakePlugin('plugin1', packagesDir, platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.federated) + platformAndroid: const PlatformDetails(PlatformSupport.federated) }); final List output = diff --git a/script/tool/test/native_test_command_test.dart b/script/tool/test/native_test_command_test.dart index 697cbd4b84d3..1069a68107c5 100644 --- a/script/tool/test/native_test_command_test.dart +++ b/script/tool/test/native_test_command_test.dart @@ -207,7 +207,7 @@ void main() { test('reports skips with no tests', () async { final Directory pluginDirectory1 = createFakePlugin('plugin', packagesDir, platformSupport: { - kPlatformMacos: const PlatformDetails(PlatformSupport.inline), + platformMacOS: const PlatformDetails(PlatformSupport.inline), }); final Directory pluginExampleDirectory = @@ -241,7 +241,7 @@ void main() { test('skip if iOS is not supported', () async { createFakePlugin('plugin', packagesDir, platformSupport: { - kPlatformMacos: const PlatformDetails(PlatformSupport.inline), + platformMacOS: const PlatformDetails(PlatformSupport.inline), }); final List output = await runCapturingPrint(runner, @@ -258,7 +258,7 @@ void main() { test('skip if iOS is implemented in a federated package', () async { createFakePlugin('plugin', packagesDir, platformSupport: { - kPlatformIos: const PlatformDetails(PlatformSupport.federated) + platformIOS: const PlatformDetails(PlatformSupport.federated) }); final List output = await runCapturingPrint(runner, @@ -275,7 +275,7 @@ void main() { test('running with correct destination', () async { final Directory pluginDirectory = createFakePlugin( 'plugin', packagesDir, platformSupport: { - kPlatformIos: const PlatformDetails(PlatformSupport.inline) + platformIOS: const PlatformDetails(PlatformSupport.inline) }); final Directory pluginExampleDirectory = @@ -313,7 +313,7 @@ void main() { () async { final Directory pluginDirectory = createFakePlugin( 'plugin', packagesDir, platformSupport: { - kPlatformIos: const PlatformDetails(PlatformSupport.inline) + platformIOS: const PlatformDetails(PlatformSupport.inline) }); final Directory pluginExampleDirectory = pluginDirectory.childDirectory('example'); @@ -366,7 +366,7 @@ void main() { test('skip if macOS is implemented in a federated package', () async { createFakePlugin('plugin', packagesDir, platformSupport: { - kPlatformMacos: const PlatformDetails(PlatformSupport.federated), + platformMacOS: const PlatformDetails(PlatformSupport.federated), }); final List output = @@ -385,7 +385,7 @@ void main() { final Directory pluginDirectory1 = createFakePlugin( 'plugin', packagesDir, platformSupport: { - kPlatformMacos: const PlatformDetails(PlatformSupport.inline), + platformMacOS: const PlatformDetails(PlatformSupport.inline), }); final Directory pluginExampleDirectory = @@ -421,7 +421,7 @@ void main() { 'plugin', packagesDir, platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline) + platformAndroid: const PlatformDetails(PlatformSupport.inline) }, extraFiles: [ 'example/android/gradlew', @@ -451,7 +451,7 @@ void main() { 'plugin', packagesDir, platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline) + platformAndroid: const PlatformDetails(PlatformSupport.inline) }, extraFiles: [ 'example/android/gradlew', @@ -481,7 +481,7 @@ void main() { 'plugin', packagesDir, platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline) + platformAndroid: const PlatformDetails(PlatformSupport.inline) }, extraFiles: [ 'example/android/gradlew', @@ -517,7 +517,7 @@ void main() { 'plugin', packagesDir, platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline) + platformAndroid: const PlatformDetails(PlatformSupport.inline) }, extraFiles: [ 'example/android/gradlew', @@ -543,7 +543,7 @@ void main() { 'plugin', packagesDir, platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline) + platformAndroid: const PlatformDetails(PlatformSupport.inline) }, extraFiles: [ 'android/src/test/example_test.java', @@ -582,7 +582,7 @@ void main() { 'plugin', packagesDir, platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline) + platformAndroid: const PlatformDetails(PlatformSupport.inline) }, extraFiles: [ 'android/src/test/example_test.java', @@ -617,7 +617,7 @@ void main() { 'plugin', packagesDir, platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline) + platformAndroid: const PlatformDetails(PlatformSupport.inline) }, extraFiles: [ 'android/src/test/example_test.java', @@ -649,7 +649,7 @@ void main() { 'plugin', packagesDir, platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline) + platformAndroid: const PlatformDetails(PlatformSupport.inline) }, extraFiles: [ 'example/android/app/src/test/example_test.java', @@ -681,7 +681,7 @@ void main() { 'plugin1', packagesDir, platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline) + platformAndroid: const PlatformDetails(PlatformSupport.inline) }, extraFiles: [ 'example/android/gradlew', @@ -693,7 +693,7 @@ void main() { 'plugin2', packagesDir, platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline) + platformAndroid: const PlatformDetails(PlatformSupport.inline) }, extraFiles: [ 'android/src/test/example_test.java', @@ -724,7 +724,7 @@ void main() { 'plugin', packagesDir, platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline) + platformAndroid: const PlatformDetails(PlatformSupport.inline) }, extraFiles: [ 'example/android/gradlew', @@ -765,7 +765,7 @@ void main() { 'plugin', packagesDir, platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline) + platformAndroid: const PlatformDetails(PlatformSupport.inline) }, extraFiles: [ 'example/android/gradlew', @@ -808,7 +808,7 @@ void main() { 'plugin', packagesDir, platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline) + platformAndroid: const PlatformDetails(PlatformSupport.inline) }, extraFiles: [ 'example/android/gradlew', @@ -861,7 +861,7 @@ void main() { 'plugin', packagesDir, platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline) + platformAndroid: const PlatformDetails(PlatformSupport.inline) }, ); @@ -886,7 +886,7 @@ void main() { createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/$testBinaryRelativePath' ], platformSupport: { - kPlatformLinux: const PlatformDetails(PlatformSupport.inline), + platformLinux: const PlatformDetails(PlatformSupport.inline), }); _createFakeCMakeCache(pluginDirectory, mockPlatform); @@ -925,7 +925,7 @@ void main() { 'example/$debugTestBinaryRelativePath', 'example/$releaseTestBinaryRelativePath' ], platformSupport: { - kPlatformLinux: const PlatformDetails(PlatformSupport.inline), + platformLinux: const PlatformDetails(PlatformSupport.inline), }); _createFakeCMakeCache(pluginDirectory, mockPlatform); @@ -958,7 +958,7 @@ void main() { test('fails if CMake has not been configured', () async { createFakePlugin('plugin', packagesDir, platformSupport: { - kPlatformLinux: const PlatformDetails(PlatformSupport.inline), + platformLinux: const PlatformDetails(PlatformSupport.inline), }); Error? commandError; @@ -986,7 +986,7 @@ void main() { final Directory pluginDirectory = createFakePlugin( 'plugin', packagesDir, platformSupport: { - kPlatformLinux: const PlatformDetails(PlatformSupport.inline), + platformLinux: const PlatformDetails(PlatformSupport.inline), }); _createFakeCMakeCache(pluginDirectory, mockPlatform); @@ -1021,7 +1021,7 @@ void main() { createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/$testBinaryRelativePath' ], platformSupport: { - kPlatformLinux: const PlatformDetails(PlatformSupport.inline), + platformLinux: const PlatformDetails(PlatformSupport.inline), }); _createFakeCMakeCache(pluginDirectory, mockPlatform); @@ -1062,7 +1062,7 @@ void main() { test('fails if xcrun fails', () async { createFakePlugin('plugin', packagesDir, platformSupport: { - kPlatformMacos: const PlatformDetails(PlatformSupport.inline), + platformMacOS: const PlatformDetails(PlatformSupport.inline), }); processRunner.mockProcessesForExecutable['xcrun'] = [ @@ -1090,7 +1090,7 @@ void main() { final Directory pluginDirectory1 = createFakePlugin( 'plugin', packagesDir, platformSupport: { - kPlatformMacos: const PlatformDetails(PlatformSupport.inline), + platformMacOS: const PlatformDetails(PlatformSupport.inline), }); final Directory pluginExampleDirectory = @@ -1126,7 +1126,7 @@ void main() { final Directory pluginDirectory1 = createFakePlugin( 'plugin', packagesDir, platformSupport: { - kPlatformMacos: const PlatformDetails(PlatformSupport.inline), + platformMacOS: const PlatformDetails(PlatformSupport.inline), }); final Directory pluginExampleDirectory = @@ -1162,7 +1162,7 @@ void main() { final Directory pluginDirectory1 = createFakePlugin( 'plugin', packagesDir, platformSupport: { - kPlatformMacos: const PlatformDetails(PlatformSupport.inline), + platformMacOS: const PlatformDetails(PlatformSupport.inline), }); final Directory pluginExampleDirectory = @@ -1198,7 +1198,7 @@ void main() { final Directory pluginDirectory1 = createFakePlugin( 'plugin', packagesDir, platformSupport: { - kPlatformMacos: const PlatformDetails(PlatformSupport.inline), + platformMacOS: const PlatformDetails(PlatformSupport.inline), }); final Directory pluginExampleDirectory = @@ -1238,7 +1238,7 @@ void main() { final Directory pluginDirectory1 = createFakePlugin( 'plugin', packagesDir, platformSupport: { - kPlatformMacos: const PlatformDetails(PlatformSupport.inline), + platformMacOS: const PlatformDetails(PlatformSupport.inline), }); final Directory pluginExampleDirectory = @@ -1283,9 +1283,9 @@ void main() { 'android/src/test/example_test.java', ], platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline), - kPlatformIos: const PlatformDetails(PlatformSupport.inline), - kPlatformMacos: const PlatformDetails(PlatformSupport.inline), + platformAndroid: const PlatformDetails(PlatformSupport.inline), + platformIOS: const PlatformDetails(PlatformSupport.inline), + platformMacOS: const PlatformDetails(PlatformSupport.inline), }, ); @@ -1337,7 +1337,7 @@ void main() { final Directory pluginDirectory1 = createFakePlugin( 'plugin', packagesDir, platformSupport: { - kPlatformMacos: const PlatformDetails(PlatformSupport.inline), + platformMacOS: const PlatformDetails(PlatformSupport.inline), }); final Directory pluginExampleDirectory = @@ -1374,7 +1374,7 @@ void main() { test('runs only iOS for a iOS plugin', () async { final Directory pluginDirectory = createFakePlugin( 'plugin', packagesDir, platformSupport: { - kPlatformIos: const PlatformDetails(PlatformSupport.inline) + platformIOS: const PlatformDetails(PlatformSupport.inline) }); final Directory pluginExampleDirectory = @@ -1439,9 +1439,9 @@ void main() { 'plugin', packagesDir, platformSupport: { - kPlatformMacos: const PlatformDetails(PlatformSupport.inline, + platformMacOS: const PlatformDetails(PlatformSupport.inline, hasDartCode: true, hasNativeCode: false), - kPlatformWindows: const PlatformDetails(PlatformSupport.inline, + platformWindows: const PlatformDetails(PlatformSupport.inline, hasDartCode: true, hasNativeCode: false), }, ); @@ -1470,8 +1470,8 @@ void main() { 'plugin', packagesDir, platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline), - kPlatformIos: const PlatformDetails(PlatformSupport.inline), + platformAndroid: const PlatformDetails(PlatformSupport.inline), + platformIOS: const PlatformDetails(PlatformSupport.inline), }, extraFiles: [ 'example/android/gradlew', @@ -1526,8 +1526,8 @@ void main() { 'plugin', packagesDir, platformSupport: { - kPlatformAndroid: const PlatformDetails(PlatformSupport.inline), - kPlatformIos: const PlatformDetails(PlatformSupport.inline), + platformAndroid: const PlatformDetails(PlatformSupport.inline), + platformIOS: const PlatformDetails(PlatformSupport.inline), }, extraFiles: [ 'example/android/gradlew', @@ -1625,7 +1625,7 @@ void main() { createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/$testBinaryRelativePath' ], platformSupport: { - kPlatformWindows: const PlatformDetails(PlatformSupport.inline), + platformWindows: const PlatformDetails(PlatformSupport.inline), }); _createFakeCMakeCache(pluginDirectory, mockPlatform); @@ -1664,7 +1664,7 @@ void main() { 'example/$debugTestBinaryRelativePath', 'example/$releaseTestBinaryRelativePath' ], platformSupport: { - kPlatformWindows: const PlatformDetails(PlatformSupport.inline), + platformWindows: const PlatformDetails(PlatformSupport.inline), }); _createFakeCMakeCache(pluginDirectory, mockPlatform); @@ -1696,7 +1696,7 @@ void main() { test('fails if CMake has not been configured', () async { createFakePlugin('plugin', packagesDir, platformSupport: { - kPlatformWindows: const PlatformDetails(PlatformSupport.inline), + platformWindows: const PlatformDetails(PlatformSupport.inline), }); Error? commandError; @@ -1724,7 +1724,7 @@ void main() { final Directory pluginDirectory = createFakePlugin( 'plugin', packagesDir, platformSupport: { - kPlatformWindows: const PlatformDetails(PlatformSupport.inline), + platformWindows: const PlatformDetails(PlatformSupport.inline), }); _createFakeCMakeCache(pluginDirectory, mockPlatform); @@ -1759,7 +1759,7 @@ void main() { createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/$testBinaryRelativePath' ], platformSupport: { - kPlatformWindows: const PlatformDetails(PlatformSupport.inline), + platformWindows: const PlatformDetails(PlatformSupport.inline), }); _createFakeCMakeCache(pluginDirectory, mockPlatform); diff --git a/script/tool/test/test_command_test.dart b/script/tool/test/test_command_test.dart index f8aca38d3478..9bcd8d1ae67a 100644 --- a/script/tool/test/test_command_test.dart +++ b/script/tool/test/test_command_test.dart @@ -181,7 +181,7 @@ void main() { packagesDir, extraFiles: ['test/empty_test.dart'], platformSupport: { - kPlatformWeb: const PlatformDetails(PlatformSupport.inline), + platformWeb: const PlatformDetails(PlatformSupport.inline), }, ); diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart index 9abb34bef35a..91d21c1d9bb3 100644 --- a/script/tool/test/util.dart +++ b/script/tool/test/util.dart @@ -229,24 +229,24 @@ String _pluginPlatformSection( ' $platform:', ]; switch (platform) { - case kPlatformAndroid: + case platformAndroid: lines.add(' package: io.flutter.plugins.fake'); continue nativeByDefault; nativeByDefault: - case kPlatformIos: - case kPlatformLinux: - case kPlatformMacos: - case kPlatformWindows: + case platformIOS: + case platformLinux: + case platformMacOS: + case platformWindows: if (support.hasNativeCode) { final String className = - platform == kPlatformIos ? 'FLTFakePlugin' : 'FakePlugin'; + platform == platformIOS ? 'FLTFakePlugin' : 'FakePlugin'; lines.add(' pluginClass: $className'); } if (support.hasDartCode) { lines.add(' dartPluginClass: FakeDartPlugin'); } break; - case kPlatformWeb: + case platformWeb: lines.addAll([ ' pluginClass: FakePlugin', ' fileName: ${packageName}_web.dart', diff --git a/script/tool/test/xcode_analyze_command_test.dart b/script/tool/test/xcode_analyze_command_test.dart index 10008ae33a11..097d4d21cb19 100644 --- a/script/tool/test/xcode_analyze_command_test.dart +++ b/script/tool/test/xcode_analyze_command_test.dart @@ -58,7 +58,7 @@ void main() { test('skip if iOS is not supported', () async { createFakePlugin('plugin', packagesDir, platformSupport: { - kPlatformMacos: const PlatformDetails(PlatformSupport.inline), + platformMacOS: const PlatformDetails(PlatformSupport.inline), }); final List output = @@ -71,7 +71,7 @@ void main() { test('skip if iOS is implemented in a federated package', () async { createFakePlugin('plugin', packagesDir, platformSupport: { - kPlatformIos: const PlatformDetails(PlatformSupport.federated) + platformIOS: const PlatformDetails(PlatformSupport.federated) }); final List output = @@ -84,7 +84,7 @@ void main() { test('runs for iOS plugin', () async { final Directory pluginDirectory = createFakePlugin( 'plugin', packagesDir, platformSupport: { - kPlatformIos: const PlatformDetails(PlatformSupport.inline) + platformIOS: const PlatformDetails(PlatformSupport.inline) }); final Directory pluginExampleDirectory = @@ -127,7 +127,7 @@ void main() { test('fails if xcrun fails', () async { createFakePlugin('plugin', packagesDir, platformSupport: { - kPlatformIos: const PlatformDetails(PlatformSupport.inline) + platformIOS: const PlatformDetails(PlatformSupport.inline) }); processRunner.mockProcessesForExecutable['xcrun'] = [ @@ -173,7 +173,7 @@ void main() { test('skip if macOS is implemented in a federated package', () async { createFakePlugin('plugin', packagesDir, platformSupport: { - kPlatformMacos: const PlatformDetails(PlatformSupport.federated), + platformMacOS: const PlatformDetails(PlatformSupport.federated), }); final List output = await runCapturingPrint( @@ -187,7 +187,7 @@ void main() { final Directory pluginDirectory1 = createFakePlugin( 'plugin', packagesDir, platformSupport: { - kPlatformMacos: const PlatformDetails(PlatformSupport.inline), + platformMacOS: const PlatformDetails(PlatformSupport.inline), }); final Directory pluginExampleDirectory = @@ -224,7 +224,7 @@ void main() { test('fails if xcrun fails', () async { createFakePlugin('plugin', packagesDir, platformSupport: { - kPlatformMacos: const PlatformDetails(PlatformSupport.inline), + platformMacOS: const PlatformDetails(PlatformSupport.inline), }); processRunner.mockProcessesForExecutable['xcrun'] = [ @@ -254,8 +254,8 @@ void main() { final Directory pluginDirectory1 = createFakePlugin( 'plugin', packagesDir, platformSupport: { - kPlatformIos: const PlatformDetails(PlatformSupport.inline), - kPlatformMacos: const PlatformDetails(PlatformSupport.inline), + platformIOS: const PlatformDetails(PlatformSupport.inline), + platformMacOS: const PlatformDetails(PlatformSupport.inline), }); final Directory pluginExampleDirectory = @@ -314,7 +314,7 @@ void main() { final Directory pluginDirectory1 = createFakePlugin( 'plugin', packagesDir, platformSupport: { - kPlatformMacos: const PlatformDetails(PlatformSupport.inline), + platformMacOS: const PlatformDetails(PlatformSupport.inline), }); final Directory pluginExampleDirectory = @@ -355,7 +355,7 @@ void main() { test('runs only iOS for a iOS plugin', () async { final Directory pluginDirectory = createFakePlugin( 'plugin', packagesDir, platformSupport: { - kPlatformIos: const PlatformDetails(PlatformSupport.inline) + platformIOS: const PlatformDetails(PlatformSupport.inline) }); final Directory pluginExampleDirectory = From 72ebbf33612bd8e75fa4eaef6ab7fe51de03b2f7 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 15 Feb 2022 18:00:16 -0800 Subject: [PATCH 199/600] [camera] Switch web package to new analysis options (#4834) --- packages/camera/camera_web/CHANGELOG.md | 4 + .../camera/camera_web/analysis_options.yaml | 1 - .../camera_error_code_test.dart | 43 +- .../camera_metadata_test.dart | 6 +- .../integration_test/camera_options_test.dart | 94 +- .../integration_test/camera_service_test.dart | 339 ++++--- .../example/integration_test/camera_test.dart | 588 ++++++----- .../camera_web_exception_test.dart | 23 +- .../integration_test/camera_web_test.dart | 922 ++++++++++-------- .../integration_test/helpers/mocks.dart | 4 +- .../zoom_level_capability_test.dart | 14 +- .../camera/camera_web/example/lib/main.dart | 2 +- .../camera/camera_web/example/pubspec.yaml | 2 +- .../camera/camera_web/lib/src/camera.dart | 111 ++- .../camera_web/lib/src/camera_service.dart | 67 +- .../camera/camera_web/lib/src/camera_web.dart | 134 +-- .../lib/src/shims/dart_js_util.dart | 3 +- .../camera_web/lib/src/shims/dart_ui.dart | 4 +- .../lib/src/shims/dart_ui_fake.dart | 11 +- .../lib/src/types/camera_error_code.dart | 10 +- .../lib/src/types/camera_metadata.dart | 7 +- .../lib/src/types/camera_options.dart | 77 +- .../lib/src/types/media_device_kind.dart | 6 +- .../lib/src/types/zoom_level_capability.dart | 11 +- packages/camera/camera_web/pubspec.yaml | 3 +- script/configs/custom_analysis.yaml | 1 - 26 files changed, 1389 insertions(+), 1098 deletions(-) delete mode 100644 packages/camera/camera_web/analysis_options.yaml diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index cfb980a602c1..c72e31fc0f18 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.1+3 + +* Internal code cleanup for stricter analysis options. + ## 0.2.1+2 * Fixes cameraNotReadable error that prevented access to the camera on some Android devices when initializing a camera. diff --git a/packages/camera/camera_web/analysis_options.yaml b/packages/camera/camera_web/analysis_options.yaml deleted file mode 100644 index 5aeb4e7c5e21..000000000000 --- a/packages/camera/camera_web/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../../analysis_options_legacy.yaml diff --git a/packages/camera/camera_web/example/integration_test/camera_error_code_test.dart b/packages/camera/camera_web/example/integration_test/camera_error_code_test.dart index a298b57dfd7f..112683bdad98 100644 --- a/packages/camera/camera_web/example/integration_test/camera_error_code_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_error_code_test.dart @@ -15,112 +15,112 @@ void main() { group('CameraErrorCode', () { group('toString returns a correct type for', () { - testWidgets('notSupported', (tester) async { + testWidgets('notSupported', (WidgetTester tester) async { expect( CameraErrorCode.notSupported.toString(), equals('cameraNotSupported'), ); }); - testWidgets('notFound', (tester) async { + testWidgets('notFound', (WidgetTester tester) async { expect( CameraErrorCode.notFound.toString(), equals('cameraNotFound'), ); }); - testWidgets('notReadable', (tester) async { + testWidgets('notReadable', (WidgetTester tester) async { expect( CameraErrorCode.notReadable.toString(), equals('cameraNotReadable'), ); }); - testWidgets('overconstrained', (tester) async { + testWidgets('overconstrained', (WidgetTester tester) async { expect( CameraErrorCode.overconstrained.toString(), equals('cameraOverconstrained'), ); }); - testWidgets('permissionDenied', (tester) async { + testWidgets('permissionDenied', (WidgetTester tester) async { expect( CameraErrorCode.permissionDenied.toString(), equals('cameraPermission'), ); }); - testWidgets('type', (tester) async { + testWidgets('type', (WidgetTester tester) async { expect( CameraErrorCode.type.toString(), equals('cameraType'), ); }); - testWidgets('abort', (tester) async { + testWidgets('abort', (WidgetTester tester) async { expect( CameraErrorCode.abort.toString(), equals('cameraAbort'), ); }); - testWidgets('security', (tester) async { + testWidgets('security', (WidgetTester tester) async { expect( CameraErrorCode.security.toString(), equals('cameraSecurity'), ); }); - testWidgets('missingMetadata', (tester) async { + testWidgets('missingMetadata', (WidgetTester tester) async { expect( CameraErrorCode.missingMetadata.toString(), equals('cameraMissingMetadata'), ); }); - testWidgets('orientationNotSupported', (tester) async { + testWidgets('orientationNotSupported', (WidgetTester tester) async { expect( CameraErrorCode.orientationNotSupported.toString(), equals('orientationNotSupported'), ); }); - testWidgets('torchModeNotSupported', (tester) async { + testWidgets('torchModeNotSupported', (WidgetTester tester) async { expect( CameraErrorCode.torchModeNotSupported.toString(), equals('torchModeNotSupported'), ); }); - testWidgets('zoomLevelNotSupported', (tester) async { + testWidgets('zoomLevelNotSupported', (WidgetTester tester) async { expect( CameraErrorCode.zoomLevelNotSupported.toString(), equals('zoomLevelNotSupported'), ); }); - testWidgets('zoomLevelInvalid', (tester) async { + testWidgets('zoomLevelInvalid', (WidgetTester tester) async { expect( CameraErrorCode.zoomLevelInvalid.toString(), equals('zoomLevelInvalid'), ); }); - testWidgets('notStarted', (tester) async { + testWidgets('notStarted', (WidgetTester tester) async { expect( CameraErrorCode.notStarted.toString(), equals('cameraNotStarted'), ); }); - testWidgets('videoRecordingNotStarted', (tester) async { + testWidgets('videoRecordingNotStarted', (WidgetTester tester) async { expect( CameraErrorCode.videoRecordingNotStarted.toString(), equals('videoRecordingNotStarted'), ); }); - testWidgets('unknown', (tester) async { + testWidgets('unknown', (WidgetTester tester) async { expect( CameraErrorCode.unknown.toString(), equals('cameraUnknown'), @@ -128,7 +128,7 @@ void main() { }); group('fromMediaError', () { - testWidgets('with aborted error code', (tester) async { + testWidgets('with aborted error code', (WidgetTester tester) async { expect( CameraErrorCode.fromMediaError( FakeMediaError(MediaError.MEDIA_ERR_ABORTED), @@ -137,7 +137,7 @@ void main() { ); }); - testWidgets('with network error code', (tester) async { + testWidgets('with network error code', (WidgetTester tester) async { expect( CameraErrorCode.fromMediaError( FakeMediaError(MediaError.MEDIA_ERR_NETWORK), @@ -146,7 +146,7 @@ void main() { ); }); - testWidgets('with decode error code', (tester) async { + testWidgets('with decode error code', (WidgetTester tester) async { expect( CameraErrorCode.fromMediaError( FakeMediaError(MediaError.MEDIA_ERR_DECODE), @@ -155,7 +155,8 @@ void main() { ); }); - testWidgets('with source not supported error code', (tester) async { + testWidgets('with source not supported error code', + (WidgetTester tester) async { expect( CameraErrorCode.fromMediaError( FakeMediaError(MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED), @@ -164,7 +165,7 @@ void main() { ); }); - testWidgets('with unknown error code', (tester) async { + testWidgets('with unknown error code', (WidgetTester tester) async { expect( CameraErrorCode.fromMediaError( FakeMediaError(5), diff --git a/packages/camera/camera_web/example/integration_test/camera_metadata_test.dart b/packages/camera/camera_web/example/integration_test/camera_metadata_test.dart index 36ecb3e47f31..07252be5c7a2 100644 --- a/packages/camera/camera_web/example/integration_test/camera_metadata_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_metadata_test.dart @@ -10,14 +10,14 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); group('CameraMetadata', () { - testWidgets('supports value equality', (tester) async { + testWidgets('supports value equality', (WidgetTester tester) async { expect( - CameraMetadata( + const CameraMetadata( deviceId: 'deviceId', facingMode: 'environment', ), equals( - CameraMetadata( + const CameraMetadata( deviceId: 'deviceId', facingMode: 'environment', ), diff --git a/packages/camera/camera_web/example/integration_test/camera_options_test.dart b/packages/camera/camera_web/example/integration_test/camera_options_test.dart index a74ba3088394..ee63d87e9f07 100644 --- a/packages/camera/camera_web/example/integration_test/camera_options_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_options_test.dart @@ -10,9 +10,9 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); group('CameraOptions', () { - testWidgets('serializes correctly', (tester) async { - final cameraOptions = CameraOptions( - audio: AudioConstraints(enabled: true), + testWidgets('serializes correctly', (WidgetTester tester) async { + final CameraOptions cameraOptions = CameraOptions( + audio: const AudioConstraints(enabled: true), video: VideoConstraints( facingMode: FacingModeConstraint.exact(CameraType.user), ), @@ -20,31 +20,35 @@ void main() { expect( cameraOptions.toJson(), - equals({ + equals({ 'audio': cameraOptions.audio.toJson(), 'video': cameraOptions.video.toJson(), }), ); }); - testWidgets('supports value equality', (tester) async { + testWidgets('supports value equality', (WidgetTester tester) async { expect( CameraOptions( - audio: AudioConstraints(enabled: false), + audio: const AudioConstraints(enabled: false), video: VideoConstraints( facingMode: FacingModeConstraint(CameraType.environment), - width: VideoSizeConstraint(minimum: 10, ideal: 15, maximum: 20), - height: VideoSizeConstraint(minimum: 15, ideal: 20, maximum: 25), + width: + const VideoSizeConstraint(minimum: 10, ideal: 15, maximum: 20), + height: + const VideoSizeConstraint(minimum: 15, ideal: 20, maximum: 25), deviceId: 'deviceId', ), ), equals( CameraOptions( - audio: AudioConstraints(enabled: false), + audio: const AudioConstraints(enabled: false), video: VideoConstraints( facingMode: FacingModeConstraint(CameraType.environment), - width: VideoSizeConstraint(minimum: 10, ideal: 15, maximum: 20), - height: VideoSizeConstraint(minimum: 15, ideal: 20, maximum: 25), + width: const VideoSizeConstraint( + minimum: 10, ideal: 15, maximum: 20), + height: const VideoSizeConstraint( + minimum: 15, ideal: 20, maximum: 25), deviceId: 'deviceId', ), ), @@ -54,56 +58,60 @@ void main() { }); group('AudioConstraints', () { - testWidgets('serializes correctly', (tester) async { + testWidgets('serializes correctly', (WidgetTester tester) async { expect( - AudioConstraints(enabled: true).toJson(), + const AudioConstraints(enabled: true).toJson(), equals(true), ); }); - testWidgets('supports value equality', (tester) async { + testWidgets('supports value equality', (WidgetTester tester) async { expect( - AudioConstraints(enabled: true), - equals(AudioConstraints(enabled: true)), + const AudioConstraints(enabled: true), + equals(const AudioConstraints(enabled: true)), ); }); }); group('VideoConstraints', () { - testWidgets('serializes correctly', (tester) async { - final videoConstraints = VideoConstraints( + testWidgets('serializes correctly', (WidgetTester tester) async { + final VideoConstraints videoConstraints = VideoConstraints( facingMode: FacingModeConstraint.exact(CameraType.user), - width: VideoSizeConstraint(ideal: 100, maximum: 100), - height: VideoSizeConstraint(ideal: 50, maximum: 50), + width: const VideoSizeConstraint(ideal: 100, maximum: 100), + height: const VideoSizeConstraint(ideal: 50, maximum: 50), deviceId: 'deviceId', ); expect( videoConstraints.toJson(), - equals({ + equals({ 'facingMode': videoConstraints.facingMode!.toJson(), 'width': videoConstraints.width!.toJson(), 'height': videoConstraints.height!.toJson(), - 'deviceId': { + 'deviceId': { 'exact': 'deviceId', } }), ); }); - testWidgets('supports value equality', (tester) async { + testWidgets('supports value equality', (WidgetTester tester) async { expect( VideoConstraints( facingMode: FacingModeConstraint.exact(CameraType.environment), - width: VideoSizeConstraint(minimum: 90, ideal: 100, maximum: 100), - height: VideoSizeConstraint(minimum: 40, ideal: 50, maximum: 50), + width: + const VideoSizeConstraint(minimum: 90, ideal: 100, maximum: 100), + height: + const VideoSizeConstraint(minimum: 40, ideal: 50, maximum: 50), deviceId: 'deviceId', ), equals( VideoConstraints( facingMode: FacingModeConstraint.exact(CameraType.environment), - width: VideoSizeConstraint(minimum: 90, ideal: 100, maximum: 100), - height: VideoSizeConstraint(minimum: 40, ideal: 50, maximum: 50), + width: const VideoSizeConstraint( + minimum: 90, ideal: 100, maximum: 100), + height: + const VideoSizeConstraint(minimum: 40, ideal: 50, maximum: 50), deviceId: 'deviceId', ), ), @@ -115,23 +123,23 @@ void main() { group('ideal', () { testWidgets( 'serializes correctly ' - 'for environment camera type', (tester) async { + 'for environment camera type', (WidgetTester tester) async { expect( FacingModeConstraint(CameraType.environment).toJson(), - equals({'ideal': 'environment'}), + equals({'ideal': 'environment'}), ); }); testWidgets( 'serializes correctly ' - 'for user camera type', (tester) async { + 'for user camera type', (WidgetTester tester) async { expect( FacingModeConstraint(CameraType.user).toJson(), - equals({'ideal': 'user'}), + equals({'ideal': 'user'}), ); }); - testWidgets('supports value equality', (tester) async { + testWidgets('supports value equality', (WidgetTester tester) async { expect( FacingModeConstraint(CameraType.user), equals(FacingModeConstraint(CameraType.user)), @@ -142,23 +150,23 @@ void main() { group('exact', () { testWidgets( 'serializes correctly ' - 'for environment camera type', (tester) async { + 'for environment camera type', (WidgetTester tester) async { expect( FacingModeConstraint.exact(CameraType.environment).toJson(), - equals({'exact': 'environment'}), + equals({'exact': 'environment'}), ); }); testWidgets( 'serializes correctly ' - 'for user camera type', (tester) async { + 'for user camera type', (WidgetTester tester) async { expect( FacingModeConstraint.exact(CameraType.user).toJson(), - equals({'exact': 'user'}), + equals({'exact': 'user'}), ); }); - testWidgets('supports value equality', (tester) async { + testWidgets('supports value equality', (WidgetTester tester) async { expect( FacingModeConstraint.exact(CameraType.environment), equals(FacingModeConstraint.exact(CameraType.environment)), @@ -168,14 +176,14 @@ void main() { }); group('VideoSizeConstraint ', () { - testWidgets('serializes correctly', (tester) async { + testWidgets('serializes correctly', (WidgetTester tester) async { expect( - VideoSizeConstraint( + const VideoSizeConstraint( minimum: 200, ideal: 400, maximum: 400, ).toJson(), - equals({ + equals({ 'min': 200, 'ideal': 400, 'max': 400, @@ -183,15 +191,15 @@ void main() { ); }); - testWidgets('supports value equality', (tester) async { + testWidgets('supports value equality', (WidgetTester tester) async { expect( - VideoSizeConstraint( + const VideoSizeConstraint( minimum: 100, ideal: 200, maximum: 300, ), equals( - VideoSizeConstraint( + const VideoSizeConstraint( minimum: 100, ideal: 200, maximum: 300, diff --git a/packages/camera/camera_web/example/integration_test/camera_service_test.dart b/packages/camera/camera_web/example/integration_test/camera_service_test.dart index 346ab26237ea..6bc03429940c 100644 --- a/packages/camera/camera_web/example/integration_test/camera_service_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_service_test.dart @@ -3,8 +3,8 @@ // found in the LICENSE file. import 'dart:html'; -import 'dart:ui'; import 'dart:js_util' as js_util; +import 'dart:ui'; import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:camera_web/src/camera.dart'; @@ -22,7 +22,7 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); group('CameraService', () { - const cameraId = 0; + const int cameraId = 0; late Window window; late Navigator navigator; @@ -40,10 +40,10 @@ void main() { when(() => navigator.mediaDevices).thenReturn(mediaDevices); // Mock JsUtil to return the real getProperty from dart:js_util. - when(() => jsUtil.getProperty(any(), any())).thenAnswer( - (invocation) => js_util.getProperty( - invocation.positionalArguments[0], - invocation.positionalArguments[1], + when(() => jsUtil.getProperty(any(), any())).thenAnswer( + (Invocation invocation) => js_util.getProperty( + invocation.positionalArguments[0] as Object, + invocation.positionalArguments[1] as Object, ), ); @@ -53,14 +53,14 @@ void main() { group('getMediaStreamForOptions', () { testWidgets( 'calls MediaDevices.getUserMedia ' - 'with provided options', (tester) async { + 'with provided options', (WidgetTester tester) async { when(() => mediaDevices.getUserMedia(any())) - .thenAnswer((_) async => FakeMediaStream([])); + .thenAnswer((_) async => FakeMediaStream([])); - final options = CameraOptions( + final CameraOptions options = CameraOptions( video: VideoConstraints( facingMode: FacingModeConstraint.exact(CameraType.user), - width: VideoSizeConstraint(ideal: 200), + width: const VideoSizeConstraint(ideal: 200), ), ); @@ -74,14 +74,14 @@ void main() { testWidgets( 'throws PlatformException ' 'with notSupported error ' - 'when there are no media devices', (tester) async { + 'when there are no media devices', (WidgetTester tester) async { when(() => navigator.mediaDevices).thenReturn(null); expect( - () => cameraService.getMediaStreamForOptions(CameraOptions()), + () => cameraService.getMediaStreamForOptions(const CameraOptions()), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', CameraErrorCode.notSupported.toString(), ), @@ -93,19 +93,21 @@ void main() { testWidgets( 'with notFound error ' 'when MediaDevices.getUserMedia throws DomException ' - 'with NotFoundError', (tester) async { + 'with NotFoundError', (WidgetTester tester) async { when(() => mediaDevices.getUserMedia(any())) .thenThrow(FakeDomException('NotFoundError')); expect( () => cameraService.getMediaStreamForOptions( - CameraOptions(), + const CameraOptions(), cameraId: cameraId, ), throwsA( isA() - .having((e) => e.cameraId, 'cameraId', cameraId) - .having((e) => e.code, 'code', CameraErrorCode.notFound), + .having((CameraWebException e) => e.cameraId, 'cameraId', + cameraId) + .having((CameraWebException e) => e.code, 'code', + CameraErrorCode.notFound), ), ); }); @@ -113,19 +115,21 @@ void main() { testWidgets( 'with notFound error ' 'when MediaDevices.getUserMedia throws DomException ' - 'with DevicesNotFoundError', (tester) async { + 'with DevicesNotFoundError', (WidgetTester tester) async { when(() => mediaDevices.getUserMedia(any())) .thenThrow(FakeDomException('DevicesNotFoundError')); expect( () => cameraService.getMediaStreamForOptions( - CameraOptions(), + const CameraOptions(), cameraId: cameraId, ), throwsA( isA() - .having((e) => e.cameraId, 'cameraId', cameraId) - .having((e) => e.code, 'code', CameraErrorCode.notFound), + .having((CameraWebException e) => e.cameraId, 'cameraId', + cameraId) + .having((CameraWebException e) => e.code, 'code', + CameraErrorCode.notFound), ), ); }); @@ -133,19 +137,21 @@ void main() { testWidgets( 'with notReadable error ' 'when MediaDevices.getUserMedia throws DomException ' - 'with NotReadableError', (tester) async { + 'with NotReadableError', (WidgetTester tester) async { when(() => mediaDevices.getUserMedia(any())) .thenThrow(FakeDomException('NotReadableError')); expect( () => cameraService.getMediaStreamForOptions( - CameraOptions(), + const CameraOptions(), cameraId: cameraId, ), throwsA( isA() - .having((e) => e.cameraId, 'cameraId', cameraId) - .having((e) => e.code, 'code', CameraErrorCode.notReadable), + .having((CameraWebException e) => e.cameraId, 'cameraId', + cameraId) + .having((CameraWebException e) => e.code, 'code', + CameraErrorCode.notReadable), ), ); }); @@ -153,19 +159,21 @@ void main() { testWidgets( 'with notReadable error ' 'when MediaDevices.getUserMedia throws DomException ' - 'with TrackStartError', (tester) async { + 'with TrackStartError', (WidgetTester tester) async { when(() => mediaDevices.getUserMedia(any())) .thenThrow(FakeDomException('TrackStartError')); expect( () => cameraService.getMediaStreamForOptions( - CameraOptions(), + const CameraOptions(), cameraId: cameraId, ), throwsA( isA() - .having((e) => e.cameraId, 'cameraId', cameraId) - .having((e) => e.code, 'code', CameraErrorCode.notReadable), + .having((CameraWebException e) => e.cameraId, 'cameraId', + cameraId) + .having((CameraWebException e) => e.code, 'code', + CameraErrorCode.notReadable), ), ); }); @@ -173,20 +181,21 @@ void main() { testWidgets( 'with overconstrained error ' 'when MediaDevices.getUserMedia throws DomException ' - 'with OverconstrainedError', (tester) async { + 'with OverconstrainedError', (WidgetTester tester) async { when(() => mediaDevices.getUserMedia(any())) .thenThrow(FakeDomException('OverconstrainedError')); expect( () => cameraService.getMediaStreamForOptions( - CameraOptions(), + const CameraOptions(), cameraId: cameraId, ), throwsA( isA() - .having((e) => e.cameraId, 'cameraId', cameraId) - .having( - (e) => e.code, 'code', CameraErrorCode.overconstrained), + .having((CameraWebException e) => e.cameraId, 'cameraId', + cameraId) + .having((CameraWebException e) => e.code, 'code', + CameraErrorCode.overconstrained), ), ); }); @@ -194,20 +203,21 @@ void main() { testWidgets( 'with overconstrained error ' 'when MediaDevices.getUserMedia throws DomException ' - 'with ConstraintNotSatisfiedError', (tester) async { + 'with ConstraintNotSatisfiedError', (WidgetTester tester) async { when(() => mediaDevices.getUserMedia(any())) .thenThrow(FakeDomException('ConstraintNotSatisfiedError')); expect( () => cameraService.getMediaStreamForOptions( - CameraOptions(), + const CameraOptions(), cameraId: cameraId, ), throwsA( isA() - .having((e) => e.cameraId, 'cameraId', cameraId) - .having( - (e) => e.code, 'code', CameraErrorCode.overconstrained), + .having((CameraWebException e) => e.cameraId, 'cameraId', + cameraId) + .having((CameraWebException e) => e.code, 'code', + CameraErrorCode.overconstrained), ), ); }); @@ -215,20 +225,21 @@ void main() { testWidgets( 'with permissionDenied error ' 'when MediaDevices.getUserMedia throws DomException ' - 'with NotAllowedError', (tester) async { + 'with NotAllowedError', (WidgetTester tester) async { when(() => mediaDevices.getUserMedia(any())) .thenThrow(FakeDomException('NotAllowedError')); expect( () => cameraService.getMediaStreamForOptions( - CameraOptions(), + const CameraOptions(), cameraId: cameraId, ), throwsA( isA() - .having((e) => e.cameraId, 'cameraId', cameraId) - .having( - (e) => e.code, 'code', CameraErrorCode.permissionDenied), + .having((CameraWebException e) => e.cameraId, 'cameraId', + cameraId) + .having((CameraWebException e) => e.code, 'code', + CameraErrorCode.permissionDenied), ), ); }); @@ -236,20 +247,21 @@ void main() { testWidgets( 'with permissionDenied error ' 'when MediaDevices.getUserMedia throws DomException ' - 'with PermissionDeniedError', (tester) async { + 'with PermissionDeniedError', (WidgetTester tester) async { when(() => mediaDevices.getUserMedia(any())) .thenThrow(FakeDomException('PermissionDeniedError')); expect( () => cameraService.getMediaStreamForOptions( - CameraOptions(), + const CameraOptions(), cameraId: cameraId, ), throwsA( isA() - .having((e) => e.cameraId, 'cameraId', cameraId) - .having( - (e) => e.code, 'code', CameraErrorCode.permissionDenied), + .having((CameraWebException e) => e.cameraId, 'cameraId', + cameraId) + .having((CameraWebException e) => e.code, 'code', + CameraErrorCode.permissionDenied), ), ); }); @@ -257,19 +269,21 @@ void main() { testWidgets( 'with type error ' 'when MediaDevices.getUserMedia throws DomException ' - 'with TypeError', (tester) async { + 'with TypeError', (WidgetTester tester) async { when(() => mediaDevices.getUserMedia(any())) .thenThrow(FakeDomException('TypeError')); expect( () => cameraService.getMediaStreamForOptions( - CameraOptions(), + const CameraOptions(), cameraId: cameraId, ), throwsA( isA() - .having((e) => e.cameraId, 'cameraId', cameraId) - .having((e) => e.code, 'code', CameraErrorCode.type), + .having((CameraWebException e) => e.cameraId, 'cameraId', + cameraId) + .having((CameraWebException e) => e.code, 'code', + CameraErrorCode.type), ), ); }); @@ -277,19 +291,21 @@ void main() { testWidgets( 'with abort error ' 'when MediaDevices.getUserMedia throws DomException ' - 'with AbortError', (tester) async { + 'with AbortError', (WidgetTester tester) async { when(() => mediaDevices.getUserMedia(any())) .thenThrow(FakeDomException('AbortError')); expect( () => cameraService.getMediaStreamForOptions( - CameraOptions(), + const CameraOptions(), cameraId: cameraId, ), throwsA( isA() - .having((e) => e.cameraId, 'cameraId', cameraId) - .having((e) => e.code, 'code', CameraErrorCode.abort), + .having((CameraWebException e) => e.cameraId, 'cameraId', + cameraId) + .having((CameraWebException e) => e.code, 'code', + CameraErrorCode.abort), ), ); }); @@ -297,19 +313,21 @@ void main() { testWidgets( 'with security error ' 'when MediaDevices.getUserMedia throws DomException ' - 'with SecurityError', (tester) async { + 'with SecurityError', (WidgetTester tester) async { when(() => mediaDevices.getUserMedia(any())) .thenThrow(FakeDomException('SecurityError')); expect( () => cameraService.getMediaStreamForOptions( - CameraOptions(), + const CameraOptions(), cameraId: cameraId, ), throwsA( isA() - .having((e) => e.cameraId, 'cameraId', cameraId) - .having((e) => e.code, 'code', CameraErrorCode.security), + .having((CameraWebException e) => e.cameraId, 'cameraId', + cameraId) + .having((CameraWebException e) => e.code, 'code', + CameraErrorCode.security), ), ); }); @@ -317,19 +335,21 @@ void main() { testWidgets( 'with unknown error ' 'when MediaDevices.getUserMedia throws DomException ' - 'with an unknown error', (tester) async { + 'with an unknown error', (WidgetTester tester) async { when(() => mediaDevices.getUserMedia(any())) .thenThrow(FakeDomException('Unknown')); expect( () => cameraService.getMediaStreamForOptions( - CameraOptions(), + const CameraOptions(), cameraId: cameraId, ), throwsA( isA() - .having((e) => e.cameraId, 'cameraId', cameraId) - .having((e) => e.code, 'code', CameraErrorCode.unknown), + .having((CameraWebException e) => e.cameraId, 'cameraId', + cameraId) + .having((CameraWebException e) => e.code, 'code', + CameraErrorCode.unknown), ), ); }); @@ -337,18 +357,20 @@ void main() { testWidgets( 'with unknown error ' 'when MediaDevices.getUserMedia throws an unknown exception', - (tester) async { + (WidgetTester tester) async { when(() => mediaDevices.getUserMedia(any())).thenThrow(Exception()); expect( () => cameraService.getMediaStreamForOptions( - CameraOptions(), + const CameraOptions(), cameraId: cameraId, ), throwsA( isA() - .having((e) => e.cameraId, 'cameraId', cameraId) - .having((e) => e.code, 'code', CameraErrorCode.unknown), + .having((CameraWebException e) => e.cameraId, 'cameraId', + cameraId) + .having((CameraWebException e) => e.code, 'code', + CameraErrorCode.unknown), ), ); }); @@ -361,7 +383,10 @@ void main() { setUp(() { camera = MockCamera(); - videoTracks = [MockMediaStreamTrack(), MockMediaStreamTrack()]; + videoTracks = [ + MockMediaStreamTrack(), + MockMediaStreamTrack() + ]; when(() => camera.textureId).thenReturn(0); when(() => camera.stream).thenReturn(FakeMediaStream(videoTracks)); @@ -371,20 +396,21 @@ void main() { testWidgets( 'returns the zoom level capability ' - 'based on the first video track', (tester) async { - when(mediaDevices.getSupportedConstraints).thenReturn({ + 'based on the first video track', (WidgetTester tester) async { + when(mediaDevices.getSupportedConstraints) + .thenReturn({ 'zoom': true, }); - when(videoTracks.first.getCapabilities).thenReturn({ - 'zoom': js_util.jsify({ + when(videoTracks.first.getCapabilities).thenReturn({ + 'zoom': js_util.jsify({ 'min': 100, 'max': 400, 'step': 2, }), }); - final zoomLevelCapability = + final ZoomLevelCapability zoomLevelCapability = cameraService.getZoomLevelCapabilityForCamera(camera); expect(zoomLevelCapability.minimum, equals(100.0)); @@ -395,7 +421,7 @@ void main() { group('throws CameraWebException', () { testWidgets( 'with zoomLevelNotSupported error ' - 'when there are no media devices', (tester) async { + 'when there are no media devices', (WidgetTester tester) async { when(() => navigator.mediaDevices).thenReturn(null); expect( @@ -403,12 +429,12 @@ void main() { throwsA( isA() .having( - (e) => e.cameraId, + (CameraWebException e) => e.cameraId, 'cameraId', camera.textureId, ) .having( - (e) => e.code, + (CameraWebException e) => e.code, 'code', CameraErrorCode.zoomLevelNotSupported, ), @@ -419,13 +445,14 @@ void main() { testWidgets( 'with zoomLevelNotSupported error ' 'when the zoom level is not supported ' - 'in the browser', (tester) async { - when(mediaDevices.getSupportedConstraints).thenReturn({ + 'in the browser', (WidgetTester tester) async { + when(mediaDevices.getSupportedConstraints) + .thenReturn({ 'zoom': false, }); - when(videoTracks.first.getCapabilities).thenReturn({ - 'zoom': { + when(videoTracks.first.getCapabilities).thenReturn({ + 'zoom': { 'min': 100, 'max': 400, 'step': 2, @@ -437,12 +464,12 @@ void main() { throwsA( isA() .having( - (e) => e.cameraId, + (CameraWebException e) => e.cameraId, 'cameraId', camera.textureId, ) .having( - (e) => e.code, + (CameraWebException e) => e.code, 'code', CameraErrorCode.zoomLevelNotSupported, ), @@ -453,24 +480,26 @@ void main() { testWidgets( 'with zoomLevelNotSupported error ' 'when the zoom level is not supported ' - 'by the camera', (tester) async { - when(mediaDevices.getSupportedConstraints).thenReturn({ + 'by the camera', (WidgetTester tester) async { + when(mediaDevices.getSupportedConstraints) + .thenReturn({ 'zoom': true, }); - when(videoTracks.first.getCapabilities).thenReturn({}); + when(videoTracks.first.getCapabilities) + .thenReturn({}); expect( () => cameraService.getZoomLevelCapabilityForCamera(camera), throwsA( isA() .having( - (e) => e.cameraId, + (CameraWebException e) => e.cameraId, 'cameraId', camera.textureId, ) .having( - (e) => e.code, + (CameraWebException e) => e.code, 'code', CameraErrorCode.zoomLevelNotSupported, ), @@ -480,25 +509,28 @@ void main() { testWidgets( 'with notStarted error ' - 'when the camera stream has not been initialized', (tester) async { - when(mediaDevices.getSupportedConstraints).thenReturn({ + 'when the camera stream has not been initialized', + (WidgetTester tester) async { + when(mediaDevices.getSupportedConstraints) + .thenReturn({ 'zoom': true, }); // Create a camera stream with no video tracks. - when(() => camera.stream).thenReturn(FakeMediaStream([])); + when(() => camera.stream) + .thenReturn(FakeMediaStream([])); expect( () => cameraService.getZoomLevelCapabilityForCamera(camera), throwsA( isA() .having( - (e) => e.cameraId, + (CameraWebException e) => e.cameraId, 'cameraId', camera.textureId, ) .having( - (e) => e.code, + (CameraWebException e) => e.code, 'code', CameraErrorCode.notStarted, ), @@ -516,7 +548,7 @@ void main() { testWidgets( 'throws PlatformException ' 'with notSupported error ' - 'when there are no media devices', (tester) async { + 'when there are no media devices', (WidgetTester tester) async { when(() => navigator.mediaDevices).thenReturn(null); expect( @@ -524,7 +556,7 @@ void main() { cameraService.getFacingModeForVideoTrack(MockMediaStreamTrack()), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', CameraErrorCode.notSupported.toString(), ), @@ -534,12 +566,13 @@ void main() { testWidgets( 'returns null ' - 'when the facing mode is not supported', (tester) async { - when(mediaDevices.getSupportedConstraints).thenReturn({ + 'when the facing mode is not supported', (WidgetTester tester) async { + when(mediaDevices.getSupportedConstraints) + .thenReturn({ 'facingMode': false, }); - final facingMode = + final String? facingMode = cameraService.getFacingModeForVideoTrack(MockMediaStreamTrack()); expect(facingMode, isNull); @@ -554,17 +587,19 @@ void main() { when(() => jsUtil.hasProperty(videoTrack, 'getCapabilities')) .thenReturn(true); - when(mediaDevices.getSupportedConstraints).thenReturn({ + when(mediaDevices.getSupportedConstraints) + .thenReturn({ 'facingMode': true, }); }); testWidgets( 'returns an appropriate facing mode ' - 'based on the video track settings', (tester) async { - when(videoTrack.getSettings).thenReturn({'facingMode': 'user'}); + 'based on the video track settings', (WidgetTester tester) async { + when(videoTrack.getSettings) + .thenReturn({'facingMode': 'user'}); - final facingMode = + final String? facingMode = cameraService.getFacingModeForVideoTrack(videoTrack); expect(facingMode, equals('user')); @@ -573,16 +608,17 @@ void main() { testWidgets( 'returns an appropriate facing mode ' 'based on the video track capabilities ' - 'when the facing mode setting is empty', (tester) async { - when(videoTrack.getSettings).thenReturn({}); - when(videoTrack.getCapabilities).thenReturn({ - 'facingMode': ['environment', 'left'] + 'when the facing mode setting is empty', + (WidgetTester tester) async { + when(videoTrack.getSettings).thenReturn({}); + when(videoTrack.getCapabilities).thenReturn({ + 'facingMode': ['environment', 'left'] }); when(() => jsUtil.hasProperty(videoTrack, 'getCapabilities')) .thenReturn(true); - final facingMode = + final String? facingMode = cameraService.getFacingModeForVideoTrack(videoTrack); expect(facingMode, equals('environment')); @@ -591,11 +627,12 @@ void main() { testWidgets( 'returns null ' 'when the facing mode setting ' - 'and capabilities are empty', (tester) async { - when(videoTrack.getSettings).thenReturn({}); - when(videoTrack.getCapabilities).thenReturn({'facingMode': []}); + 'and capabilities are empty', (WidgetTester tester) async { + when(videoTrack.getSettings).thenReturn({}); + when(videoTrack.getCapabilities) + .thenReturn({'facingMode': []}); - final facingMode = + final String? facingMode = cameraService.getFacingModeForVideoTrack(videoTrack); expect(facingMode, isNull); @@ -604,13 +641,14 @@ void main() { testWidgets( 'returns null ' 'when the facing mode setting is empty and ' - 'the video track capabilities are not supported', (tester) async { - when(videoTrack.getSettings).thenReturn({}); + 'the video track capabilities are not supported', + (WidgetTester tester) async { + when(videoTrack.getSettings).thenReturn({}); when(() => jsUtil.hasProperty(videoTrack, 'getCapabilities')) .thenReturn(false); - final facingMode = + final String? facingMode = cameraService.getFacingModeForVideoTrack(videoTrack); expect(facingMode, isNull); @@ -621,7 +659,7 @@ void main() { group('mapFacingModeToLensDirection', () { testWidgets( 'returns front ' - 'when the facing mode is user', (tester) async { + 'when the facing mode is user', (WidgetTester tester) async { expect( cameraService.mapFacingModeToLensDirection('user'), equals(CameraLensDirection.front), @@ -630,7 +668,7 @@ void main() { testWidgets( 'returns back ' - 'when the facing mode is environment', (tester) async { + 'when the facing mode is environment', (WidgetTester tester) async { expect( cameraService.mapFacingModeToLensDirection('environment'), equals(CameraLensDirection.back), @@ -639,7 +677,7 @@ void main() { testWidgets( 'returns external ' - 'when the facing mode is left', (tester) async { + 'when the facing mode is left', (WidgetTester tester) async { expect( cameraService.mapFacingModeToLensDirection('left'), equals(CameraLensDirection.external), @@ -648,7 +686,7 @@ void main() { testWidgets( 'returns external ' - 'when the facing mode is right', (tester) async { + 'when the facing mode is right', (WidgetTester tester) async { expect( cameraService.mapFacingModeToLensDirection('right'), equals(CameraLensDirection.external), @@ -659,7 +697,7 @@ void main() { group('mapFacingModeToCameraType', () { testWidgets( 'returns user ' - 'when the facing mode is user', (tester) async { + 'when the facing mode is user', (WidgetTester tester) async { expect( cameraService.mapFacingModeToCameraType('user'), equals(CameraType.user), @@ -668,7 +706,7 @@ void main() { testWidgets( 'returns environment ' - 'when the facing mode is environment', (tester) async { + 'when the facing mode is environment', (WidgetTester tester) async { expect( cameraService.mapFacingModeToCameraType('environment'), equals(CameraType.environment), @@ -677,7 +715,7 @@ void main() { testWidgets( 'returns user ' - 'when the facing mode is left', (tester) async { + 'when the facing mode is left', (WidgetTester tester) async { expect( cameraService.mapFacingModeToCameraType('left'), equals(CameraType.user), @@ -686,7 +724,7 @@ void main() { testWidgets( 'returns user ' - 'when the facing mode is right', (tester) async { + 'when the facing mode is right', (WidgetTester tester) async { expect( cameraService.mapFacingModeToCameraType('right'), equals(CameraType.user), @@ -697,55 +735,57 @@ void main() { group('mapResolutionPresetToSize', () { testWidgets( 'returns 4096x2160 ' - 'when the resolution preset is max', (tester) async { + 'when the resolution preset is max', (WidgetTester tester) async { expect( cameraService.mapResolutionPresetToSize(ResolutionPreset.max), - equals(Size(4096, 2160)), + equals(const Size(4096, 2160)), ); }); testWidgets( 'returns 4096x2160 ' - 'when the resolution preset is ultraHigh', (tester) async { + 'when the resolution preset is ultraHigh', + (WidgetTester tester) async { expect( cameraService.mapResolutionPresetToSize(ResolutionPreset.ultraHigh), - equals(Size(4096, 2160)), + equals(const Size(4096, 2160)), ); }); testWidgets( 'returns 1920x1080 ' - 'when the resolution preset is veryHigh', (tester) async { + 'when the resolution preset is veryHigh', + (WidgetTester tester) async { expect( cameraService.mapResolutionPresetToSize(ResolutionPreset.veryHigh), - equals(Size(1920, 1080)), + equals(const Size(1920, 1080)), ); }); testWidgets( 'returns 1280x720 ' - 'when the resolution preset is high', (tester) async { + 'when the resolution preset is high', (WidgetTester tester) async { expect( cameraService.mapResolutionPresetToSize(ResolutionPreset.high), - equals(Size(1280, 720)), + equals(const Size(1280, 720)), ); }); testWidgets( 'returns 720x480 ' - 'when the resolution preset is medium', (tester) async { + 'when the resolution preset is medium', (WidgetTester tester) async { expect( cameraService.mapResolutionPresetToSize(ResolutionPreset.medium), - equals(Size(720, 480)), + equals(const Size(720, 480)), ); }); testWidgets( 'returns 320x240 ' - 'when the resolution preset is low', (tester) async { + 'when the resolution preset is low', (WidgetTester tester) async { expect( cameraService.mapResolutionPresetToSize(ResolutionPreset.low), - equals(Size(320, 240)), + equals(const Size(320, 240)), ); }); }); @@ -753,7 +793,8 @@ void main() { group('mapDeviceOrientationToOrientationType', () { testWidgets( 'returns portraitPrimary ' - 'when the device orientation is portraitUp', (tester) async { + 'when the device orientation is portraitUp', + (WidgetTester tester) async { expect( cameraService.mapDeviceOrientationToOrientationType( DeviceOrientation.portraitUp, @@ -764,7 +805,8 @@ void main() { testWidgets( 'returns landscapePrimary ' - 'when the device orientation is landscapeLeft', (tester) async { + 'when the device orientation is landscapeLeft', + (WidgetTester tester) async { expect( cameraService.mapDeviceOrientationToOrientationType( DeviceOrientation.landscapeLeft, @@ -775,7 +817,8 @@ void main() { testWidgets( 'returns portraitSecondary ' - 'when the device orientation is portraitDown', (tester) async { + 'when the device orientation is portraitDown', + (WidgetTester tester) async { expect( cameraService.mapDeviceOrientationToOrientationType( DeviceOrientation.portraitDown, @@ -786,7 +829,8 @@ void main() { testWidgets( 'returns landscapeSecondary ' - 'when the device orientation is landscapeRight', (tester) async { + 'when the device orientation is landscapeRight', + (WidgetTester tester) async { expect( cameraService.mapDeviceOrientationToOrientationType( DeviceOrientation.landscapeRight, @@ -799,7 +843,8 @@ void main() { group('mapOrientationTypeToDeviceOrientation', () { testWidgets( 'returns portraitUp ' - 'when the orientation type is portraitPrimary', (tester) async { + 'when the orientation type is portraitPrimary', + (WidgetTester tester) async { expect( cameraService.mapOrientationTypeToDeviceOrientation( OrientationType.portraitPrimary, @@ -810,7 +855,8 @@ void main() { testWidgets( 'returns landscapeLeft ' - 'when the orientation type is landscapePrimary', (tester) async { + 'when the orientation type is landscapePrimary', + (WidgetTester tester) async { expect( cameraService.mapOrientationTypeToDeviceOrientation( OrientationType.landscapePrimary, @@ -821,7 +867,8 @@ void main() { testWidgets( 'returns portraitDown ' - 'when the orientation type is portraitSecondary', (tester) async { + 'when the orientation type is portraitSecondary', + (WidgetTester tester) async { expect( cameraService.mapOrientationTypeToDeviceOrientation( OrientationType.portraitSecondary, @@ -832,7 +879,8 @@ void main() { testWidgets( 'returns portraitDown ' - 'when the orientation type is portraitSecondary', (tester) async { + 'when the orientation type is portraitSecondary', + (WidgetTester tester) async { expect( cameraService.mapOrientationTypeToDeviceOrientation( OrientationType.portraitSecondary, @@ -843,7 +891,8 @@ void main() { testWidgets( 'returns landscapeRight ' - 'when the orientation type is landscapeSecondary', (tester) async { + 'when the orientation type is landscapeSecondary', + (WidgetTester tester) async { expect( cameraService.mapOrientationTypeToDeviceOrientation( OrientationType.landscapeSecondary, @@ -854,7 +903,7 @@ void main() { testWidgets( 'returns portraitUp ' - 'for an unknown orientation type', (tester) async { + 'for an unknown orientation type', (WidgetTester tester) async { expect( cameraService.mapOrientationTypeToDeviceOrientation( 'unknown', diff --git a/packages/camera/camera_web/example/integration_test/camera_test.dart b/packages/camera/camera_web/example/integration_test/camera_test.dart index 3a25e33c5398..4e8050eef6a8 100644 --- a/packages/camera/camera_web/example/integration_test/camera_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_test.dart @@ -21,7 +21,7 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); group('Camera', () { - const textureId = 1; + const int textureId = 1; late Window window; late Navigator navigator; @@ -40,7 +40,8 @@ void main() { cameraService = MockCameraService(); - final videoElement = getVideoElementWithBlankStream(Size(10, 10)); + final VideoElement videoElement = + getVideoElementWithBlankStream(const Size(10, 10)); mediaStream = videoElement.captureStream(); when( @@ -48,7 +49,7 @@ void main() { any(), cameraId: any(named: 'cameraId'), ), - ).thenAnswer((_) => Future.value(mediaStream)); + ).thenAnswer((_) => Future.value(mediaStream)); }); setUpAll(() { @@ -58,15 +59,15 @@ void main() { group('initialize', () { testWidgets( 'calls CameraService.getMediaStreamForOptions ' - 'with provided options', (tester) async { - final options = CameraOptions( + 'with provided options', (WidgetTester tester) async { + final CameraOptions options = CameraOptions( video: VideoConstraints( facingMode: FacingModeConstraint.exact(CameraType.user), - width: VideoSizeConstraint(ideal: 200), + width: const VideoSizeConstraint(ideal: 200), ), ); - final camera = Camera( + final Camera camera = Camera( textureId: textureId, options: options, cameraService: cameraService, @@ -84,15 +85,16 @@ void main() { testWidgets( 'creates a video element ' - 'with correct properties', (tester) async { - const audioConstraints = AudioConstraints(enabled: true); - final videoConstraints = VideoConstraints( + 'with correct properties', (WidgetTester tester) async { + const AudioConstraints audioConstraints = + AudioConstraints(enabled: true); + final VideoConstraints videoConstraints = VideoConstraints( facingMode: FacingModeConstraint( CameraType.user, ), ); - final camera = Camera( + final Camera camera = Camera( textureId: textureId, options: CameraOptions( audio: audioConstraints, @@ -119,14 +121,14 @@ void main() { testWidgets( 'flips the video element horizontally ' - 'for a back camera', (tester) async { - final videoConstraints = VideoConstraints( + 'for a back camera', (WidgetTester tester) async { + final VideoConstraints videoConstraints = VideoConstraints( facingMode: FacingModeConstraint( CameraType.environment, ), ); - final camera = Camera( + final Camera camera = Camera( textureId: textureId, options: CameraOptions( video: videoConstraints, @@ -141,8 +143,8 @@ void main() { testWidgets( 'creates a wrapping div element ' - 'with correct properties', (tester) async { - final camera = Camera( + 'with correct properties', (WidgetTester tester) async { + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ); @@ -154,8 +156,8 @@ void main() { expect(camera.divElement.children, contains(camera.videoElement)); }); - testWidgets('initializes the camera stream', (tester) async { - final camera = Camera( + testWidgets('initializes the camera stream', (WidgetTester tester) async { + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ); @@ -167,13 +169,15 @@ void main() { testWidgets( 'throws an exception ' - 'when CameraService.getMediaStreamForOptions throws', (tester) async { - final exception = Exception('A media stream exception occured.'); + 'when CameraService.getMediaStreamForOptions throws', + (WidgetTester tester) async { + final Exception exception = + Exception('A media stream exception occured.'); when(() => cameraService.getMediaStreamForOptions(any(), cameraId: any(named: 'cameraId'))).thenThrow(exception); - final camera = Camera( + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ); @@ -186,18 +190,20 @@ void main() { }); group('play', () { - testWidgets('starts playing the video element', (tester) async { - var startedPlaying = false; + testWidgets('starts playing the video element', + (WidgetTester tester) async { + bool startedPlaying = false; - final camera = Camera( + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ); await camera.initialize(); - final cameraPlaySubscription = - camera.videoElement.onPlay.listen((event) => startedPlaying = true); + final StreamSubscription cameraPlaySubscription = camera + .videoElement.onPlay + .listen((Event event) => startedPlaying = true); await camera.play(); @@ -209,14 +215,14 @@ void main() { testWidgets( 'initializes the camera stream ' 'from CameraService.getMediaStreamForOptions ' - 'if it does not exist', (tester) async { - final options = CameraOptions( + 'if it does not exist', (WidgetTester tester) async { + const CameraOptions options = CameraOptions( video: VideoConstraints( width: VideoSizeConstraint(ideal: 100), ), ); - final camera = Camera( + final Camera camera = Camera( textureId: textureId, options: options, cameraService: cameraService, @@ -244,8 +250,8 @@ void main() { }); group('pause', () { - testWidgets('pauses the camera stream', (tester) async { - final camera = Camera( + testWidgets('pauses the camera stream', (WidgetTester tester) async { + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ); @@ -262,8 +268,8 @@ void main() { }); group('stop', () { - testWidgets('resets the camera stream', (tester) async { - final camera = Camera( + testWidgets('resets the camera stream', (WidgetTester tester) async { + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ); @@ -279,8 +285,8 @@ void main() { }); group('takePicture', () { - testWidgets('returns a captured picture', (tester) async { - final camera = Camera( + testWidgets('returns a captured picture', (WidgetTester tester) async { + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ); @@ -288,7 +294,7 @@ void main() { await camera.initialize(); await camera.play(); - final pictureFile = await camera.takePicture(); + final XFile pictureFile = await camera.takePicture(); expect(pictureFile, isNotNull); }); @@ -301,22 +307,25 @@ void main() { late VideoElement videoElement; setUp(() { - videoTracks = [MockMediaStreamTrack(), MockMediaStreamTrack()]; + videoTracks = [ + MockMediaStreamTrack(), + MockMediaStreamTrack() + ]; videoStream = FakeMediaStream(videoTracks); - videoElement = getVideoElementWithBlankStream(Size(100, 100)) + videoElement = getVideoElementWithBlankStream(const Size(100, 100)) ..muted = true; when(() => videoTracks.first.applyConstraints(any())) - .thenAnswer((_) async => {}); + .thenAnswer((_) async => {}); - when(videoTracks.first.getCapabilities).thenReturn({ + when(videoTracks.first.getCapabilities).thenReturn({ 'torch': true, }); }); - testWidgets('if the flash mode is auto', (tester) async { - final camera = Camera( + testWidgets('if the flash mode is auto', (WidgetTester tester) async { + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ) @@ -327,31 +336,31 @@ void main() { await camera.play(); - final _ = await camera.takePicture(); + final XFile _ = await camera.takePicture(); verify( - () => videoTracks.first.applyConstraints({ - "advanced": [ - { - "torch": true, + () => videoTracks.first.applyConstraints({ + 'advanced': [ + { + 'torch': true, } ] }), ).called(1); verify( - () => videoTracks.first.applyConstraints({ - "advanced": [ - { - "torch": false, + () => videoTracks.first.applyConstraints({ + 'advanced': [ + { + 'torch': false, } ] }), ).called(1); }); - testWidgets('if the flash mode is always', (tester) async { - final camera = Camera( + testWidgets('if the flash mode is always', (WidgetTester tester) async { + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ) @@ -362,23 +371,23 @@ void main() { await camera.play(); - final _ = await camera.takePicture(); + final XFile _ = await camera.takePicture(); verify( - () => videoTracks.first.applyConstraints({ - "advanced": [ - { - "torch": true, + () => videoTracks.first.applyConstraints({ + 'advanced': [ + { + 'torch': true, } ] }), ).called(1); verify( - () => videoTracks.first.applyConstraints({ - "advanced": [ - { - "torch": false, + () => videoTracks.first.applyConstraints({ + 'advanced': [ + { + 'torch': false, } ] }), @@ -390,13 +399,15 @@ void main() { group('getVideoSize', () { testWidgets( 'returns a size ' - 'based on the first video track settings', (tester) async { - const videoSize = Size(1280, 720); + 'based on the first video track settings', + (WidgetTester tester) async { + const Size videoSize = Size(1280, 720); - final videoElement = getVideoElementWithBlankStream(videoSize); + final VideoElement videoElement = + getVideoElementWithBlankStream(videoSize); mediaStream = videoElement.captureStream(); - final camera = Camera( + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ); @@ -411,12 +422,12 @@ void main() { testWidgets( 'returns Size.zero ' - 'if the camera is missing video tracks', (tester) async { + 'if the camera is missing video tracks', (WidgetTester tester) async { // Create a video stream with no video tracks. - final videoElement = VideoElement(); + final VideoElement videoElement = VideoElement(); mediaStream = videoElement.captureStream(); - final camera = Camera( + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ); @@ -435,32 +446,37 @@ void main() { late MediaStream videoStream; setUp(() { - videoTracks = [MockMediaStreamTrack(), MockMediaStreamTrack()]; + videoTracks = [ + MockMediaStreamTrack(), + MockMediaStreamTrack() + ]; videoStream = FakeMediaStream(videoTracks); when(() => videoTracks.first.applyConstraints(any())) - .thenAnswer((_) async => {}); + .thenAnswer((_) async => {}); - when(videoTracks.first.getCapabilities).thenReturn({}); + when(videoTracks.first.getCapabilities) + .thenReturn({}); }); - testWidgets('sets the camera flash mode', (tester) async { - when(mediaDevices.getSupportedConstraints).thenReturn({ + testWidgets('sets the camera flash mode', (WidgetTester tester) async { + when(mediaDevices.getSupportedConstraints) + .thenReturn({ 'torch': true, }); - when(videoTracks.first.getCapabilities).thenReturn({ + when(videoTracks.first.getCapabilities).thenReturn({ 'torch': true, }); - final camera = Camera( + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ) ..window = window ..stream = videoStream; - const flashMode = FlashMode.always; + const FlashMode flashMode = FlashMode.always; camera.setFlashMode(flashMode); @@ -472,16 +488,17 @@ void main() { testWidgets( 'enables the torch mode ' - 'if the flash mode is torch', (tester) async { - when(mediaDevices.getSupportedConstraints).thenReturn({ + 'if the flash mode is torch', (WidgetTester tester) async { + when(mediaDevices.getSupportedConstraints) + .thenReturn({ 'torch': true, }); - when(videoTracks.first.getCapabilities).thenReturn({ + when(videoTracks.first.getCapabilities).thenReturn({ 'torch': true, }); - final camera = Camera( + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ) @@ -491,10 +508,10 @@ void main() { camera.setFlashMode(FlashMode.torch); verify( - () => videoTracks.first.applyConstraints({ - "advanced": [ - { - "torch": true, + () => videoTracks.first.applyConstraints({ + 'advanced': [ + { + 'torch': true, } ] }), @@ -503,16 +520,17 @@ void main() { testWidgets( 'disables the torch mode ' - 'if the flash mode is not torch', (tester) async { - when(mediaDevices.getSupportedConstraints).thenReturn({ + 'if the flash mode is not torch', (WidgetTester tester) async { + when(mediaDevices.getSupportedConstraints) + .thenReturn({ 'torch': true, }); - when(videoTracks.first.getCapabilities).thenReturn({ + when(videoTracks.first.getCapabilities).thenReturn({ 'torch': true, }); - final camera = Camera( + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ) @@ -522,10 +540,10 @@ void main() { camera.setFlashMode(FlashMode.auto); verify( - () => videoTracks.first.applyConstraints({ - "advanced": [ - { - "torch": false, + () => videoTracks.first.applyConstraints({ + 'advanced': [ + { + 'torch': false, } ] }), @@ -535,10 +553,10 @@ void main() { group('throws a CameraWebException', () { testWidgets( 'with torchModeNotSupported error ' - 'when there are no media devices', (tester) async { + 'when there are no media devices', (WidgetTester tester) async { when(() => navigator.mediaDevices).thenReturn(null); - final camera = Camera( + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ) @@ -550,12 +568,12 @@ void main() { throwsA( isA() .having( - (e) => e.cameraId, + (CameraWebException e) => e.cameraId, 'cameraId', textureId, ) .having( - (e) => e.code, + (CameraWebException e) => e.code, 'code', CameraErrorCode.torchModeNotSupported, ), @@ -566,16 +584,17 @@ void main() { testWidgets( 'with torchModeNotSupported error ' 'when the torch mode is not supported ' - 'in the browser', (tester) async { - when(mediaDevices.getSupportedConstraints).thenReturn({ + 'in the browser', (WidgetTester tester) async { + when(mediaDevices.getSupportedConstraints) + .thenReturn({ 'torch': false, }); - when(videoTracks.first.getCapabilities).thenReturn({ + when(videoTracks.first.getCapabilities).thenReturn({ 'torch': true, }); - final camera = Camera( + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ) @@ -587,12 +606,12 @@ void main() { throwsA( isA() .having( - (e) => e.cameraId, + (CameraWebException e) => e.cameraId, 'cameraId', textureId, ) .having( - (e) => e.code, + (CameraWebException e) => e.code, 'code', CameraErrorCode.torchModeNotSupported, ), @@ -603,16 +622,17 @@ void main() { testWidgets( 'with torchModeNotSupported error ' 'when the torch mode is not supported ' - 'by the camera', (tester) async { - when(mediaDevices.getSupportedConstraints).thenReturn({ + 'by the camera', (WidgetTester tester) async { + when(mediaDevices.getSupportedConstraints) + .thenReturn({ 'torch': true, }); - when(videoTracks.first.getCapabilities).thenReturn({ + when(videoTracks.first.getCapabilities).thenReturn({ 'torch': false, }); - final camera = Camera( + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ) @@ -624,12 +644,12 @@ void main() { throwsA( isA() .having( - (e) => e.cameraId, + (CameraWebException e) => e.cameraId, 'cameraId', textureId, ) .having( - (e) => e.code, + (CameraWebException e) => e.code, 'code', CameraErrorCode.torchModeNotSupported, ), @@ -639,16 +659,18 @@ void main() { testWidgets( 'with notStarted error ' - 'when the camera stream has not been initialized', (tester) async { - when(mediaDevices.getSupportedConstraints).thenReturn({ + 'when the camera stream has not been initialized', + (WidgetTester tester) async { + when(mediaDevices.getSupportedConstraints) + .thenReturn({ 'torch': true, }); - when(videoTracks.first.getCapabilities).thenReturn({ + when(videoTracks.first.getCapabilities).thenReturn({ 'torch': true, }); - final camera = Camera( + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, )..window = window; @@ -658,12 +680,12 @@ void main() { throwsA( isA() .having( - (e) => e.cameraId, + (CameraWebException e) => e.cameraId, 'cameraId', textureId, ) .having( - (e) => e.code, + (CameraWebException e) => e.code, 'code', CameraErrorCode.notStarted, ), @@ -678,13 +700,13 @@ void main() { testWidgets( 'returns maximum ' 'from CameraService.getZoomLevelCapabilityForCamera', - (tester) async { - final camera = Camera( + (WidgetTester tester) async { + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ); - final zoomLevelCapability = ZoomLevelCapability( + final ZoomLevelCapability zoomLevelCapability = ZoomLevelCapability( minimum: 50.0, maximum: 100.0, videoTrack: MockMediaStreamTrack(), @@ -693,7 +715,7 @@ void main() { when(() => cameraService.getZoomLevelCapabilityForCamera(camera)) .thenReturn(zoomLevelCapability); - final maximumZoomLevel = camera.getMaxZoomLevel(); + final double maximumZoomLevel = camera.getMaxZoomLevel(); verify(() => cameraService.getZoomLevelCapabilityForCamera(camera)) .called(1); @@ -709,13 +731,13 @@ void main() { testWidgets( 'returns minimum ' 'from CameraService.getZoomLevelCapabilityForCamera', - (tester) async { - final camera = Camera( + (WidgetTester tester) async { + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ); - final zoomLevelCapability = ZoomLevelCapability( + final ZoomLevelCapability zoomLevelCapability = ZoomLevelCapability( minimum: 50.0, maximum: 100.0, videoTrack: MockMediaStreamTrack(), @@ -724,7 +746,7 @@ void main() { when(() => cameraService.getZoomLevelCapabilityForCamera(camera)) .thenReturn(zoomLevelCapability); - final minimumZoomLevel = camera.getMinZoomLevel(); + final double minimumZoomLevel = camera.getMinZoomLevel(); verify(() => cameraService.getZoomLevelCapabilityForCamera(camera)) .called(1); @@ -740,15 +762,15 @@ void main() { testWidgets( 'applies zoom on the video track ' 'from CameraService.getZoomLevelCapabilityForCamera', - (tester) async { - final camera = Camera( + (WidgetTester tester) async { + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ); - final videoTrack = MockMediaStreamTrack(); + final MockMediaStreamTrack videoTrack = MockMediaStreamTrack(); - final zoomLevelCapability = ZoomLevelCapability( + final ZoomLevelCapability zoomLevelCapability = ZoomLevelCapability( minimum: 50.0, maximum: 100.0, videoTrack: videoTrack, @@ -760,14 +782,14 @@ void main() { when(() => cameraService.getZoomLevelCapabilityForCamera(camera)) .thenReturn(zoomLevelCapability); - const zoom = 75.0; + const double zoom = 75.0; camera.setZoomLevel(zoom); verify( - () => videoTrack.applyConstraints({ - "advanced": [ - { + () => videoTrack.applyConstraints({ + 'advanced': [ + { ZoomLevelCapability.constraintName: zoom, } ] @@ -778,13 +800,14 @@ void main() { group('throws a CameraWebException', () { testWidgets( 'with zoomLevelInvalid error ' - 'when the provided zoom level is below minimum', (tester) async { - final camera = Camera( + 'when the provided zoom level is below minimum', + (WidgetTester tester) async { + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ); - final zoomLevelCapability = ZoomLevelCapability( + final ZoomLevelCapability zoomLevelCapability = ZoomLevelCapability( minimum: 50.0, maximum: 100.0, videoTrack: MockMediaStreamTrack(), @@ -798,12 +821,12 @@ void main() { throwsA( isA() .having( - (e) => e.cameraId, + (CameraWebException e) => e.cameraId, 'cameraId', textureId, ) .having( - (e) => e.code, + (CameraWebException e) => e.code, 'code', CameraErrorCode.zoomLevelInvalid, ), @@ -812,13 +835,14 @@ void main() { testWidgets( 'with zoomLevelInvalid error ' - 'when the provided zoom level is below minimum', (tester) async { - final camera = Camera( + 'when the provided zoom level is below minimum', + (WidgetTester tester) async { + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ); - final zoomLevelCapability = ZoomLevelCapability( + final ZoomLevelCapability zoomLevelCapability = ZoomLevelCapability( minimum: 50.0, maximum: 100.0, videoTrack: MockMediaStreamTrack(), @@ -832,12 +856,12 @@ void main() { throwsA( isA() .having( - (e) => e.cameraId, + (CameraWebException e) => e.cameraId, 'cameraId', textureId, ) .having( - (e) => e.code, + (CameraWebException e) => e.code, 'code', CameraErrorCode.zoomLevelInvalid, ), @@ -851,25 +875,26 @@ void main() { group('getLensDirection', () { testWidgets( 'returns a lens direction ' - 'based on the first video track settings', (tester) async { - final videoElement = MockVideoElement(); + 'based on the first video track settings', + (WidgetTester tester) async { + final MockVideoElement videoElement = MockVideoElement(); - final camera = Camera( + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, )..videoElement = videoElement; - final firstVideoTrack = MockMediaStreamTrack(); + final MockMediaStreamTrack firstVideoTrack = MockMediaStreamTrack(); when(() => videoElement.srcObject).thenReturn( - FakeMediaStream([ + FakeMediaStream([ firstVideoTrack, MockMediaStreamTrack(), ]), ); when(firstVideoTrack.getSettings) - .thenReturn({'facingMode': 'environment'}); + .thenReturn({'facingMode': 'environment'}); when(() => cameraService.mapFacingModeToLensDirection('environment')) .thenReturn(CameraLensDirection.external); @@ -883,24 +908,24 @@ void main() { testWidgets( 'returns null ' 'if the first video track is missing the facing mode', - (tester) async { - final videoElement = MockVideoElement(); + (WidgetTester tester) async { + final MockVideoElement videoElement = MockVideoElement(); - final camera = Camera( + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, )..videoElement = videoElement; - final firstVideoTrack = MockMediaStreamTrack(); + final MockMediaStreamTrack firstVideoTrack = MockMediaStreamTrack(); when(() => videoElement.srcObject).thenReturn( - FakeMediaStream([ + FakeMediaStream([ firstVideoTrack, MockMediaStreamTrack(), ]), ); - when(firstVideoTrack.getSettings).thenReturn({}); + when(firstVideoTrack.getSettings).thenReturn({}); expect( camera.getLensDirection(), @@ -910,12 +935,12 @@ void main() { testWidgets( 'returns null ' - 'if the camera is missing video tracks', (tester) async { + 'if the camera is missing video tracks', (WidgetTester tester) async { // Create a video stream with no video tracks. - final videoElement = VideoElement(); + final VideoElement videoElement = VideoElement(); mediaStream = videoElement.captureStream(); - final camera = Camera( + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ); @@ -930,8 +955,8 @@ void main() { }); group('getViewType', () { - testWidgets('returns a correct view type', (tester) async { - final camera = Camera( + testWidgets('returns a correct view type', (WidgetTester tester) async { + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ); @@ -946,7 +971,7 @@ void main() { }); group('video recording', () { - const supportedVideoType = 'video/webm'; + const String supportedVideoType = 'video/webm'; late MediaRecorder mediaRecorder; @@ -956,14 +981,14 @@ void main() { mediaRecorder = MockMediaRecorder(); when(() => mediaRecorder.onError) - .thenAnswer((_) => const Stream.empty()); + .thenAnswer((_) => const Stream.empty()); }); group('startVideoRecording', () { testWidgets( 'creates a media recorder ' - 'with appropriate options', (tester) async { - final camera = Camera( + 'with appropriate options', (WidgetTester tester) async { + final Camera camera = Camera( textureId: 1, cameraService: cameraService, )..isVideoTypeSupported = isVideoTypeSupported; @@ -990,8 +1015,8 @@ void main() { }); testWidgets('listens to the media recorder data events', - (tester) async { - final camera = Camera( + (WidgetTester tester) async { + final Camera camera = Camera( textureId: 1, cameraService: cameraService, ) @@ -1009,8 +1034,8 @@ void main() { }); testWidgets('listens to the media recorder stop events', - (tester) async { - final camera = Camera( + (WidgetTester tester) async { + final Camera camera = Camera( textureId: 1, cameraService: cameraService, ) @@ -1027,8 +1052,8 @@ void main() { ).called(1); }); - testWidgets('starts a video recording', (tester) async { - final camera = Camera( + testWidgets('starts a video recording', (WidgetTester tester) async { + final Camera camera = Camera( textureId: 1, cameraService: cameraService, ) @@ -1045,10 +1070,10 @@ void main() { testWidgets( 'starts a video recording ' - 'with maxVideoDuration', (tester) async { - const maxVideoDuration = Duration(hours: 1); + 'with maxVideoDuration', (WidgetTester tester) async { + const Duration maxVideoDuration = Duration(hours: 1); - final camera = Camera( + final Camera camera = Camera( textureId: 1, cameraService: cameraService, ) @@ -1068,8 +1093,8 @@ void main() { testWidgets( 'with notSupported error ' 'when maxVideoDuration is 0 milliseconds or less', - (tester) async { - final camera = Camera( + (WidgetTester tester) async { + final Camera camera = Camera( textureId: 1, cameraService: cameraService, ) @@ -1084,12 +1109,12 @@ void main() { throwsA( isA() .having( - (e) => e.cameraId, + (CameraWebException e) => e.cameraId, 'cameraId', textureId, ) .having( - (e) => e.code, + (CameraWebException e) => e.code, 'code', CameraErrorCode.notSupported, ), @@ -1099,11 +1124,11 @@ void main() { testWidgets( 'with notSupported error ' - 'when no video types are supported', (tester) async { - final camera = Camera( + 'when no video types are supported', (WidgetTester tester) async { + final Camera camera = Camera( textureId: 1, cameraService: cameraService, - )..isVideoTypeSupported = (type) => false; + )..isVideoTypeSupported = (String type) => false; await camera.initialize(); await camera.play(); @@ -1113,12 +1138,12 @@ void main() { throwsA( isA() .having( - (e) => e.cameraId, + (CameraWebException e) => e.cameraId, 'cameraId', textureId, ) .having( - (e) => e.code, + (CameraWebException e) => e.code, 'code', CameraErrorCode.notSupported, ), @@ -1129,8 +1154,8 @@ void main() { }); group('pauseVideoRecording', () { - testWidgets('pauses a video recording', (tester) async { - final camera = Camera( + testWidgets('pauses a video recording', (WidgetTester tester) async { + final Camera camera = Camera( textureId: 1, cameraService: cameraService, )..mediaRecorder = mediaRecorder; @@ -1143,8 +1168,9 @@ void main() { testWidgets( 'throws a CameraWebException ' 'with videoRecordingNotStarted error ' - 'if the video recording was not started', (tester) async { - final camera = Camera( + 'if the video recording was not started', + (WidgetTester tester) async { + final Camera camera = Camera( textureId: 1, cameraService: cameraService, ); @@ -1154,12 +1180,12 @@ void main() { throwsA( isA() .having( - (e) => e.cameraId, + (CameraWebException e) => e.cameraId, 'cameraId', textureId, ) .having( - (e) => e.code, + (CameraWebException e) => e.code, 'code', CameraErrorCode.videoRecordingNotStarted, ), @@ -1169,8 +1195,8 @@ void main() { }); group('resumeVideoRecording', () { - testWidgets('resumes a video recording', (tester) async { - final camera = Camera( + testWidgets('resumes a video recording', (WidgetTester tester) async { + final Camera camera = Camera( textureId: 1, cameraService: cameraService, )..mediaRecorder = mediaRecorder; @@ -1183,8 +1209,9 @@ void main() { testWidgets( 'throws a CameraWebException ' 'with videoRecordingNotStarted error ' - 'if the video recording was not started', (tester) async { - final camera = Camera( + 'if the video recording was not started', + (WidgetTester tester) async { + final Camera camera = Camera( textureId: 1, cameraService: cameraService, ); @@ -1194,12 +1221,12 @@ void main() { throwsA( isA() .having( - (e) => e.cameraId, + (CameraWebException e) => e.cameraId, 'cameraId', textureId, ) .having( - (e) => e.code, + (CameraWebException e) => e.code, 'code', CameraErrorCode.videoRecordingNotStarted, ), @@ -1212,8 +1239,8 @@ void main() { testWidgets( 'stops a video recording and ' 'returns the captured file ' - 'based on all video data parts', (tester) async { - final camera = Camera( + 'based on all video data parts', (WidgetTester tester) async { + final Camera camera = Camera( textureId: 1, cameraService: cameraService, ) @@ -1228,31 +1255,33 @@ void main() { when( () => mediaRecorder.addEventListener('dataavailable', any()), - ).thenAnswer((invocation) { - videoDataAvailableListener = invocation.positionalArguments[1]; + ).thenAnswer((Invocation invocation) { + videoDataAvailableListener = + invocation.positionalArguments[1] as void Function(Event); }); when( () => mediaRecorder.addEventListener('stop', any()), - ).thenAnswer((invocation) { - videoRecordingStoppedListener = invocation.positionalArguments[1]; + ).thenAnswer((Invocation invocation) { + videoRecordingStoppedListener = + invocation.positionalArguments[1] as void Function(Event); }); Blob? finalVideo; List? videoParts; - camera.blobBuilder = (blobs, videoType) { - videoParts = [...blobs]; + camera.blobBuilder = (List blobs, String videoType) { + videoParts = [...blobs]; finalVideo = Blob(blobs, videoType); return finalVideo!; }; await camera.startVideoRecording(); - final videoFileFuture = camera.stopVideoRecording(); + final Future videoFileFuture = camera.stopVideoRecording(); - final capturedVideoPartOne = Blob([]); - final capturedVideoPartTwo = Blob([]); + final Blob capturedVideoPartOne = Blob([]); + final Blob capturedVideoPartTwo = Blob([]); - final capturedVideoParts = [ + final List capturedVideoParts = [ capturedVideoPartOne, capturedVideoPartTwo, ]; @@ -1263,7 +1292,7 @@ void main() { videoRecordingStoppedListener.call(Event('stop')); - final videoFile = await videoFileFuture; + final XFile videoFile = await videoFileFuture; verify(mediaRecorder.stop).called(1); @@ -1291,8 +1320,9 @@ void main() { testWidgets( 'throws a CameraWebException ' 'with videoRecordingNotStarted error ' - 'if the video recording was not started', (tester) async { - final camera = Camera( + 'if the video recording was not started', + (WidgetTester tester) async { + final Camera camera = Camera( textureId: 1, cameraService: cameraService, ); @@ -1302,12 +1332,12 @@ void main() { throwsA( isA() .having( - (e) => e.cameraId, + (CameraWebException e) => e.cameraId, 'cameraId', textureId, ) .having( - (e) => e.code, + (CameraWebException e) => e.code, 'code', CameraErrorCode.videoRecordingNotStarted, ), @@ -1322,18 +1352,20 @@ void main() { setUp(() { when( () => mediaRecorder.addEventListener('dataavailable', any()), - ).thenAnswer((invocation) { - videoDataAvailableListener = invocation.positionalArguments[1]; + ).thenAnswer((Invocation invocation) { + videoDataAvailableListener = + invocation.positionalArguments[1] as void Function(Event); }); }); testWidgets( 'stops a video recording ' 'if maxVideoDuration is given and ' - 'the recording was not stopped manually', (tester) async { - const maxVideoDuration = Duration(hours: 1); + 'the recording was not stopped manually', + (WidgetTester tester) async { + const Duration maxVideoDuration = Duration(hours: 1); - final camera = Camera( + final Camera camera = Camera( textureId: 1, cameraService: cameraService, ) @@ -1346,9 +1378,9 @@ void main() { when(() => mediaRecorder.state).thenReturn('recording'); - videoDataAvailableListener.call(FakeBlobEvent(Blob([]))); + videoDataAvailableListener.call(FakeBlobEvent(Blob([]))); - await Future.microtask(() {}); + await Future.microtask(() {}); verify(mediaRecorder.stop).called(1); }); @@ -1360,14 +1392,15 @@ void main() { setUp(() { when( () => mediaRecorder.addEventListener('stop', any()), - ).thenAnswer((invocation) { - videoRecordingStoppedListener = invocation.positionalArguments[1]; + ).thenAnswer((Invocation invocation) { + videoRecordingStoppedListener = + invocation.positionalArguments[1] as void Function(Event); }); }); testWidgets('stops listening to the media recorder data events', - (tester) async { - final camera = Camera( + (WidgetTester tester) async { + final Camera camera = Camera( textureId: 1, cameraService: cameraService, ) @@ -1381,7 +1414,7 @@ void main() { videoRecordingStoppedListener.call(Event('stop')); - await Future.microtask(() {}); + await Future.microtask(() {}); verify( () => mediaRecorder.removeEventListener('dataavailable', any()), @@ -1389,8 +1422,8 @@ void main() { }); testWidgets('stops listening to the media recorder stop events', - (tester) async { - final camera = Camera( + (WidgetTester tester) async { + final Camera camera = Camera( textureId: 1, cameraService: cameraService, ) @@ -1404,7 +1437,7 @@ void main() { videoRecordingStoppedListener.call(Event('stop')); - await Future.microtask(() {}); + await Future.microtask(() {}); verify( () => mediaRecorder.removeEventListener('stop', any()), @@ -1412,10 +1445,11 @@ void main() { }); testWidgets('stops listening to the media recorder errors', - (tester) async { - final onErrorStreamController = StreamController(); + (WidgetTester tester) async { + final StreamController onErrorStreamController = + StreamController(); - final camera = Camera( + final Camera camera = Camera( textureId: 1, cameraService: cameraService, ) @@ -1432,7 +1466,7 @@ void main() { videoRecordingStoppedListener.call(Event('stop')); - await Future.microtask(() {}); + await Future.microtask(() {}); expect( onErrorStreamController.hasListener, @@ -1443,8 +1477,9 @@ void main() { }); group('dispose', () { - testWidgets('resets the video element\'s source', (tester) async { - final camera = Camera( + testWidgets('resets the video element\'s source', + (WidgetTester tester) async { + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ); @@ -1455,8 +1490,8 @@ void main() { expect(camera.videoElement.srcObject, isNull); }); - testWidgets('closes the onEnded stream', (tester) async { - final camera = Camera( + testWidgets('closes the onEnded stream', (WidgetTester tester) async { + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ); @@ -1470,8 +1505,9 @@ void main() { ); }); - testWidgets('closes the onVideoRecordedEvent stream', (tester) async { - final camera = Camera( + testWidgets('closes the onVideoRecordedEvent stream', + (WidgetTester tester) async { + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ); @@ -1485,8 +1521,9 @@ void main() { ); }); - testWidgets('closes the onVideoRecordingError stream', (tester) async { - final camera = Camera( + testWidgets('closes the onVideoRecordingError stream', + (WidgetTester tester) async { + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ); @@ -1505,20 +1542,20 @@ void main() { group('onVideoRecordedEvent', () { testWidgets( 'emits a VideoRecordedEvent ' - 'when a video recording is created', (tester) async { - const maxVideoDuration = Duration(hours: 1); - const supportedVideoType = 'video/webm'; + 'when a video recording is created', (WidgetTester tester) async { + const Duration maxVideoDuration = Duration(hours: 1); + const String supportedVideoType = 'video/webm'; - final mediaRecorder = MockMediaRecorder(); + final MockMediaRecorder mediaRecorder = MockMediaRecorder(); when(() => mediaRecorder.onError) - .thenAnswer((_) => const Stream.empty()); + .thenAnswer((_) => const Stream.empty()); - final camera = Camera( + final Camera camera = Camera( textureId: 1, cameraService: cameraService, ) ..mediaRecorder = mediaRecorder - ..isVideoTypeSupported = (type) => type == 'video/webm'; + ..isVideoTypeSupported = (String type) => type == 'video/webm'; await camera.initialize(); await camera.play(); @@ -1528,27 +1565,30 @@ void main() { when( () => mediaRecorder.addEventListener('dataavailable', any()), - ).thenAnswer((invocation) { - videoDataAvailableListener = invocation.positionalArguments[1]; + ).thenAnswer((Invocation invocation) { + videoDataAvailableListener = + invocation.positionalArguments[1] as void Function(Event); }); when( () => mediaRecorder.addEventListener('stop', any()), - ).thenAnswer((invocation) { - videoRecordingStoppedListener = invocation.positionalArguments[1]; + ).thenAnswer((Invocation invocation) { + videoRecordingStoppedListener = + invocation.positionalArguments[1] as void Function(Event); }); - final streamQueue = StreamQueue(camera.onVideoRecordedEvent); + final StreamQueue streamQueue = + StreamQueue(camera.onVideoRecordedEvent); await camera.startVideoRecording(maxVideoDuration: maxVideoDuration); Blob? finalVideo; - camera.blobBuilder = (blobs, videoType) { + camera.blobBuilder = (List blobs, String videoType) { finalVideo = Blob(blobs, videoType); return finalVideo!; }; - videoDataAvailableListener.call(FakeBlobEvent(Blob([]))); + videoDataAvailableListener.call(FakeBlobEvent(Blob([]))); videoRecordingStoppedListener.call(Event('stop')); expect( @@ -1556,27 +1596,27 @@ void main() { equals( isA() .having( - (e) => e.cameraId, + (VideoRecordedEvent e) => e.cameraId, 'cameraId', textureId, ) .having( - (e) => e.file, + (VideoRecordedEvent e) => e.file, 'file', isA() .having( - (f) => f.mimeType, + (XFile f) => f.mimeType, 'mimeType', supportedVideoType, ) .having( - (f) => f.name, + (XFile f) => f.name, 'name', finalVideo.hashCode.toString(), ), ) .having( - (e) => e.maxVideoDuration, + (VideoRecordedEvent e) => e.maxVideoDuration, 'maxVideoDuration', maxVideoDuration, ), @@ -1590,18 +1630,20 @@ void main() { group('onEnded', () { testWidgets( 'emits the default video track ' - 'when it emits an ended event', (tester) async { - final camera = Camera( + 'when it emits an ended event', (WidgetTester tester) async { + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ); - final streamQueue = StreamQueue(camera.onEnded); + final StreamQueue streamQueue = + StreamQueue(camera.onEnded); await camera.initialize(); - final videoTracks = camera.stream!.getVideoTracks(); - final defaultVideoTrack = videoTracks.first; + final List videoTracks = + camera.stream!.getVideoTracks(); + final MediaStreamTrack defaultVideoTrack = videoTracks.first; defaultVideoTrack.dispatchEvent(Event('ended')); @@ -1615,18 +1657,20 @@ void main() { testWidgets( 'emits the default video track ' - 'when the camera is stopped', (tester) async { - final camera = Camera( + 'when the camera is stopped', (WidgetTester tester) async { + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, ); - final streamQueue = StreamQueue(camera.onEnded); + final StreamQueue streamQueue = + StreamQueue(camera.onEnded); await camera.initialize(); - final videoTracks = camera.stream!.getVideoTracks(); - final defaultVideoTrack = videoTracks.first; + final List videoTracks = + camera.stream!.getVideoTracks(); + final MediaStreamTrack defaultVideoTrack = videoTracks.first; camera.stop(); @@ -1643,11 +1687,12 @@ void main() { testWidgets( 'emits an ErrorEvent ' 'when the media recorder fails ' - 'when recording a video', (tester) async { - final mediaRecorder = MockMediaRecorder(); - final errorController = StreamController(); + 'when recording a video', (WidgetTester tester) async { + final MockMediaRecorder mediaRecorder = MockMediaRecorder(); + final StreamController errorController = + StreamController(); - final camera = Camera( + final Camera camera = Camera( textureId: textureId, cameraService: cameraService, )..mediaRecorder = mediaRecorder; @@ -1655,14 +1700,15 @@ void main() { when(() => mediaRecorder.onError) .thenAnswer((_) => errorController.stream); - final streamQueue = StreamQueue(camera.onVideoRecordingError); + final StreamQueue streamQueue = + StreamQueue(camera.onVideoRecordingError); await camera.initialize(); await camera.play(); await camera.startVideoRecording(); - final errorEvent = ErrorEvent('type'); + final ErrorEvent errorEvent = ErrorEvent('type'); errorController.add(errorEvent); expect( diff --git a/packages/camera/camera_web/example/integration_test/camera_web_exception_test.dart b/packages/camera/camera_web/example/integration_test/camera_web_exception_test.dart index 6f8531b6f4af..fcb54da1aed5 100644 --- a/packages/camera/camera_web/example/integration_test/camera_web_exception_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_web_exception_test.dart @@ -10,24 +10,27 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); group('CameraWebException', () { - testWidgets('sets all properties', (tester) async { - final cameraId = 1; - final code = CameraErrorCode.notFound; - final description = 'The camera is not found.'; + testWidgets('sets all properties', (WidgetTester tester) async { + const int cameraId = 1; + const CameraErrorCode code = CameraErrorCode.notFound; + const String description = 'The camera is not found.'; - final exception = CameraWebException(cameraId, code, description); + final CameraWebException exception = + CameraWebException(cameraId, code, description); expect(exception.cameraId, equals(cameraId)); expect(exception.code, equals(code)); expect(exception.description, equals(description)); }); - testWidgets('toString includes all properties', (tester) async { - final cameraId = 2; - final code = CameraErrorCode.notReadable; - final description = 'The camera is not readable.'; + testWidgets('toString includes all properties', + (WidgetTester tester) async { + const int cameraId = 2; + const CameraErrorCode code = CameraErrorCode.notReadable; + const String description = 'The camera is not readable.'; - final exception = CameraWebException(cameraId, code, description); + final CameraWebException exception = + CameraWebException(cameraId, code, description); expect( exception.toString(), diff --git a/packages/camera/camera_web/example/integration_test/camera_web_test.dart b/packages/camera/camera_web/example/integration_test/camera_web_test.dart index 0943d7a94ea9..9f21eb43fb34 100644 --- a/packages/camera/camera_web/example/integration_test/camera_web_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_web_test.dart @@ -24,7 +24,7 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); group('CameraPlugin', () { - const cameraId = 0; + const int cameraId = 0; late Window window; late Navigator navigator; @@ -42,7 +42,7 @@ void main() { navigator = MockNavigator(); mediaDevices = MockMediaDevices(); - videoElement = getVideoElementWithBlankStream(Size(10, 10)); + videoElement = getVideoElementWithBlankStream(const Size(10, 10)); when(() => window.navigator).thenReturn(navigator); when(() => navigator.mediaDevices).thenReturn(mediaDevices); @@ -81,7 +81,8 @@ void main() { registerFallbackValue(FlashMode.off); }); - testWidgets('CameraPlugin is the live instance', (tester) async { + testWidgets('CameraPlugin is the live instance', + (WidgetTester tester) async { expect(CameraPlatform.instance, isA()); }); @@ -94,16 +95,18 @@ void main() { ).thenReturn(null); when(mediaDevices.enumerateDevices).thenAnswer( - (_) async => [], + (_) async => [], ); }); - testWidgets('requests video and audio permissions', (tester) async { - final _ = await CameraPlatform.instance.availableCameras(); + testWidgets('requests video and audio permissions', + (WidgetTester tester) async { + final List _ = + await CameraPlatform.instance.availableCameras(); verify( () => cameraService.getMediaStreamForOptions( - CameraOptions( + const CameraOptions( audio: AudioConstraints(enabled: true), ), ), @@ -112,45 +115,48 @@ void main() { testWidgets( 'releases the camera stream ' - 'used to request video and audio permissions', (tester) async { - final videoTrack = MockMediaStreamTrack(); + 'used to request video and audio permissions', + (WidgetTester tester) async { + final MockMediaStreamTrack videoTrack = MockMediaStreamTrack(); - var videoTrackStopped = false; - when(videoTrack.stop).thenAnswer((_) { + bool videoTrackStopped = false; + when(videoTrack.stop).thenAnswer((Invocation _) { videoTrackStopped = true; }); when( () => cameraService.getMediaStreamForOptions( - CameraOptions( + const CameraOptions( audio: AudioConstraints(enabled: true), ), ), ).thenAnswer( - (_) => Future.value( - FakeMediaStream([videoTrack]), + (_) => Future.value( + FakeMediaStream([videoTrack]), ), ); - final _ = await CameraPlatform.instance.availableCameras(); + final List _ = + await CameraPlatform.instance.availableCameras(); expect(videoTrackStopped, isTrue); }); testWidgets( 'gets a video stream ' - 'for a video input device', (tester) async { - final videoDevice = FakeMediaDeviceInfo( + 'for a video input device', (WidgetTester tester) async { + final FakeMediaDeviceInfo videoDevice = FakeMediaDeviceInfo( '1', 'Camera 1', MediaDeviceKind.videoInput, ); when(mediaDevices.enumerateDevices).thenAnswer( - (_) => Future.value([videoDevice]), + (_) => Future>.value([videoDevice]), ); - final _ = await CameraPlatform.instance.availableCameras(); + final List _ = + await CameraPlatform.instance.availableCameras(); verify( () => cameraService.getMediaStreamForOptions( @@ -166,18 +172,19 @@ void main() { testWidgets( 'does not get a video stream ' 'for the video input device ' - 'with an empty device id', (tester) async { - final videoDevice = FakeMediaDeviceInfo( + 'with an empty device id', (WidgetTester tester) async { + final FakeMediaDeviceInfo videoDevice = FakeMediaDeviceInfo( '', 'Camera 1', MediaDeviceKind.videoInput, ); when(mediaDevices.enumerateDevices).thenAnswer( - (_) => Future.value([videoDevice]), + (_) => Future>.value([videoDevice]), ); - final _ = await CameraPlatform.instance.availableCameras(); + final List _ = + await CameraPlatform.instance.availableCameras(); verifyNever( () => cameraService.getMediaStreamForOptions( @@ -193,15 +200,15 @@ void main() { testWidgets( 'gets the facing mode ' 'from the first available video track ' - 'of the video input device', (tester) async { - final videoDevice = FakeMediaDeviceInfo( + 'of the video input device', (WidgetTester tester) async { + final FakeMediaDeviceInfo videoDevice = FakeMediaDeviceInfo( '1', 'Camera 1', MediaDeviceKind.videoInput, ); - final videoStream = - FakeMediaStream([MockMediaStreamTrack(), MockMediaStreamTrack()]); + final FakeMediaStream videoStream = FakeMediaStream( + [MockMediaStreamTrack(), MockMediaStreamTrack()]); when( () => cameraService.getMediaStreamForOptions( @@ -209,13 +216,14 @@ void main() { video: VideoConstraints(deviceId: videoDevice.deviceId), ), ), - ).thenAnswer((_) => Future.value(videoStream)); + ).thenAnswer((Invocation _) => Future.value(videoStream)); when(mediaDevices.enumerateDevices).thenAnswer( - (_) => Future.value([videoDevice]), + (_) => Future>.value([videoDevice]), ); - final _ = await CameraPlatform.instance.availableCameras(); + final List _ = + await CameraPlatform.instance.availableCameras(); verify( () => cameraService.getFacingModeForVideoTrack( @@ -227,30 +235,31 @@ void main() { testWidgets( 'returns appropriate camera descriptions ' 'for multiple video devices ' - 'based on video streams', (tester) async { - final firstVideoDevice = FakeMediaDeviceInfo( + 'based on video streams', (WidgetTester tester) async { + final FakeMediaDeviceInfo firstVideoDevice = FakeMediaDeviceInfo( '1', 'Camera 1', MediaDeviceKind.videoInput, ); - final secondVideoDevice = FakeMediaDeviceInfo( + final FakeMediaDeviceInfo secondVideoDevice = FakeMediaDeviceInfo( '4', 'Camera 4', MediaDeviceKind.videoInput, ); // Create a video stream for the first video device. - final firstVideoStream = - FakeMediaStream([MockMediaStreamTrack(), MockMediaStreamTrack()]); + final FakeMediaStream firstVideoStream = FakeMediaStream( + [MockMediaStreamTrack(), MockMediaStreamTrack()]); // Create a video stream for the second video device. - final secondVideoStream = FakeMediaStream([MockMediaStreamTrack()]); + final FakeMediaStream secondVideoStream = + FakeMediaStream([MockMediaStreamTrack()]); // Mock media devices to return two video input devices // and two audio devices. when(mediaDevices.enumerateDevices).thenAnswer( - (_) => Future.value([ + (_) => Future>.value([ firstVideoDevice, FakeMediaDeviceInfo( '2', @@ -274,7 +283,8 @@ void main() { video: VideoConstraints(deviceId: firstVideoDevice.deviceId), ), ), - ).thenAnswer((_) => Future.value(firstVideoStream)); + ).thenAnswer( + (Invocation _) => Future.value(firstVideoStream)); // Mock camera service to return the second video stream // for the second video device. @@ -284,7 +294,8 @@ void main() { video: VideoConstraints(deviceId: secondVideoDevice.deviceId), ), ), - ).thenAnswer((_) => Future.value(secondVideoStream)); + ).thenAnswer( + (Invocation _) => Future.value(secondVideoStream)); // Mock camera service to return a user facing mode // for the first video stream. @@ -308,12 +319,13 @@ void main() { when(() => cameraService.mapFacingModeToLensDirection('environment')) .thenReturn(CameraLensDirection.back); - final cameras = await CameraPlatform.instance.availableCameras(); + final List cameras = + await CameraPlatform.instance.availableCameras(); // Expect two cameras and ignore two audio devices. expect( cameras, - equals([ + equals([ CameraDescription( name: firstVideoDevice.label!, lensDirection: CameraLensDirection.front, @@ -330,18 +342,18 @@ void main() { testWidgets( 'sets camera metadata ' - 'for the camera description', (tester) async { - final videoDevice = FakeMediaDeviceInfo( + 'for the camera description', (WidgetTester tester) async { + final FakeMediaDeviceInfo videoDevice = FakeMediaDeviceInfo( '1', 'Camera 1', MediaDeviceKind.videoInput, ); - final videoStream = - FakeMediaStream([MockMediaStreamTrack(), MockMediaStreamTrack()]); + final FakeMediaStream videoStream = FakeMediaStream( + [MockMediaStreamTrack(), MockMediaStreamTrack()]); when(mediaDevices.enumerateDevices).thenAnswer( - (_) => Future.value([videoDevice]), + (_) => Future>.value([videoDevice]), ); when( @@ -350,7 +362,7 @@ void main() { video: VideoConstraints(deviceId: videoDevice.deviceId), ), ), - ).thenAnswer((_) => Future.value(videoStream)); + ).thenAnswer((Invocation _) => Future.value(videoStream)); when( () => cameraService.getFacingModeForVideoTrack( @@ -361,11 +373,12 @@ void main() { when(() => cameraService.mapFacingModeToLensDirection('left')) .thenReturn(CameraLensDirection.external); - final camera = (await CameraPlatform.instance.availableCameras()).first; + final CameraDescription camera = + (await CameraPlatform.instance.availableCameras()).first; expect( (CameraPlatform.instance as CameraPlugin).camerasMetadata, - equals({ + equals({ camera: CameraMetadata( deviceId: videoDevice.deviceId!, facingMode: 'left', @@ -376,18 +389,18 @@ void main() { testWidgets( 'releases the video stream ' - 'of a video input device', (tester) async { - final videoDevice = FakeMediaDeviceInfo( + 'of a video input device', (WidgetTester tester) async { + final FakeMediaDeviceInfo videoDevice = FakeMediaDeviceInfo( '1', 'Camera 1', MediaDeviceKind.videoInput, ); - final videoStream = - FakeMediaStream([MockMediaStreamTrack(), MockMediaStreamTrack()]); + final FakeMediaStream videoStream = FakeMediaStream( + [MockMediaStreamTrack(), MockMediaStreamTrack()]); when(mediaDevices.enumerateDevices).thenAnswer( - (_) => Future.value([videoDevice]), + (_) => Future>.value([videoDevice]), ); when( @@ -396,11 +409,13 @@ void main() { video: VideoConstraints(deviceId: videoDevice.deviceId), ), ), - ).thenAnswer((_) => Future.value(videoStream)); + ).thenAnswer((Invocation _) => Future.value(videoStream)); - final _ = await CameraPlatform.instance.availableCameras(); + final List _ = + await CameraPlatform.instance.availableCameras(); - for (var videoTrack in videoStream.getVideoTracks()) { + for (final MediaStreamTrack videoTrack + in videoStream.getVideoTracks()) { verify(videoTrack.stop).called(1); } }); @@ -408,14 +423,14 @@ void main() { group('throws CameraException', () { testWidgets( 'with notSupported error ' - 'when there are no media devices', (tester) async { + 'when there are no media devices', (WidgetTester tester) async { when(() => navigator.mediaDevices).thenReturn(null); expect( () => CameraPlatform.instance.availableCameras(), throwsA( isA().having( - (e) => e.code, + (CameraException e) => e.code, 'code', CameraErrorCode.notSupported.toString(), ), @@ -424,8 +439,9 @@ void main() { }); testWidgets('when MediaDevices.enumerateDevices throws DomException', - (tester) async { - final exception = FakeDomException(DomException.UNKNOWN); + (WidgetTester tester) async { + final FakeDomException exception = + FakeDomException(DomException.UNKNOWN); when(mediaDevices.enumerateDevices).thenThrow(exception); @@ -433,7 +449,7 @@ void main() { () => CameraPlatform.instance.availableCameras(), throwsA( isA().having( - (e) => e.code, + (CameraException e) => e.code, 'code', exception.name, ), @@ -443,8 +459,8 @@ void main() { testWidgets( 'when CameraService.getMediaStreamForOptions ' - 'throws CameraWebException', (tester) async { - final exception = CameraWebException( + 'throws CameraWebException', (WidgetTester tester) async { + final CameraWebException exception = CameraWebException( cameraId, CameraErrorCode.security, 'description', @@ -457,7 +473,7 @@ void main() { () => CameraPlatform.instance.availableCameras(), throwsA( isA().having( - (e) => e.code, + (CameraException e) => e.code, 'code', exception.code.toString(), ), @@ -467,8 +483,8 @@ void main() { testWidgets( 'when CameraService.getMediaStreamForOptions ' - 'throws PlatformException', (tester) async { - final exception = PlatformException( + 'throws PlatformException', (WidgetTester tester) async { + final PlatformException exception = PlatformException( code: CameraErrorCode.notSupported.toString(), message: 'message', ); @@ -480,7 +496,7 @@ void main() { () => CameraPlatform.instance.availableCameras(), throwsA( isA().having( - (e) => e.code, + (CameraException e) => e.code, 'code', exception.code.toString(), ), @@ -492,16 +508,16 @@ void main() { group('createCamera', () { group('creates a camera', () { - const ultraHighResolutionSize = Size(3840, 2160); - const maxResolutionSize = Size(3840, 2160); + const Size ultraHighResolutionSize = Size(3840, 2160); + const Size maxResolutionSize = Size(3840, 2160); - final cameraDescription = CameraDescription( + const CameraDescription cameraDescription = CameraDescription( name: 'name', lensDirection: CameraLensDirection.front, sensorOrientation: 0, ); - final cameraMetadata = CameraMetadata( + const CameraMetadata cameraMetadata = CameraMetadata( deviceId: 'deviceId', facingMode: 'user', ); @@ -516,13 +532,13 @@ void main() { ).thenReturn(CameraType.user); }); - testWidgets('with appropriate options', (tester) async { + testWidgets('with appropriate options', (WidgetTester tester) async { when( () => cameraService .mapResolutionPresetToSize(ResolutionPreset.ultraHigh), ).thenReturn(ultraHighResolutionSize); - final cameraId = await CameraPlatform.instance.createCamera( + final int cameraId = await CameraPlatform.instance.createCamera( cameraDescription, ResolutionPreset.ultraHigh, enableAudio: true, @@ -532,15 +548,15 @@ void main() { (CameraPlatform.instance as CameraPlugin).cameras[cameraId], isA() .having( - (camera) => camera.textureId, + (Camera camera) => camera.textureId, 'textureId', cameraId, ) .having( - (camera) => camera.options, + (Camera camera) => camera.options, 'options', CameraOptions( - audio: AudioConstraints(enabled: true), + audio: const AudioConstraints(enabled: true), video: VideoConstraints( facingMode: FacingModeConstraint(CameraType.user), width: VideoSizeConstraint( @@ -559,12 +575,12 @@ void main() { testWidgets( 'with a max resolution preset ' 'and enabled audio set to false ' - 'when no options are specified', (tester) async { + 'when no options are specified', (WidgetTester tester) async { when( () => cameraService.mapResolutionPresetToSize(ResolutionPreset.max), ).thenReturn(maxResolutionSize); - final cameraId = await CameraPlatform.instance.createCamera( + final int cameraId = await CameraPlatform.instance.createCamera( cameraDescription, null, ); @@ -572,10 +588,10 @@ void main() { expect( (CameraPlatform.instance as CameraPlugin).cameras[cameraId], isA().having( - (camera) => camera.options, + (Camera camera) => camera.options, 'options', CameraOptions( - audio: AudioConstraints(enabled: false), + audio: const AudioConstraints(enabled: false), video: VideoConstraints( facingMode: FacingModeConstraint(CameraType.user), width: VideoSizeConstraint( @@ -596,10 +612,10 @@ void main() { 'throws CameraException ' 'with missingMetadata error ' 'if there is no metadata ' - 'for the given camera description', (tester) async { + 'for the given camera description', (WidgetTester tester) async { expect( () => CameraPlatform.instance.createCamera( - CameraDescription( + const CameraDescription( name: 'name', lensDirection: CameraLensDirection.back, sensorOrientation: 0, @@ -608,7 +624,7 @@ void main() { ), throwsA( isA().having( - (e) => e.code, + (CameraException e) => e.code, 'code', CameraErrorCode.missingMetadata.toString(), ), @@ -632,21 +648,23 @@ void main() { abortStreamController = StreamController(); endedStreamController = StreamController(); - when(camera.getVideoSize).thenReturn(Size(10, 10)); - when(camera.initialize).thenAnswer((_) => Future.value()); - when(camera.play).thenAnswer((_) => Future.value()); + when(camera.getVideoSize).thenReturn(const Size(10, 10)); + when(camera.initialize) + .thenAnswer((Invocation _) => Future.value()); + when(camera.play).thenAnswer((Invocation _) => Future.value()); when(() => camera.videoElement).thenReturn(videoElement); - when(() => videoElement.onError) - .thenAnswer((_) => FakeElementStream(errorStreamController.stream)); - when(() => videoElement.onAbort) - .thenAnswer((_) => FakeElementStream(abortStreamController.stream)); + when(() => videoElement.onError).thenAnswer((Invocation _) => + FakeElementStream(errorStreamController.stream)); + when(() => videoElement.onAbort).thenAnswer((Invocation _) => + FakeElementStream(abortStreamController.stream)); when(() => camera.onEnded) - .thenAnswer((_) => endedStreamController.stream); + .thenAnswer((Invocation _) => endedStreamController.stream); }); - testWidgets('initializes and plays the camera', (tester) async { + testWidgets('initializes and plays the camera', + (WidgetTester tester) async { // Save the camera in the camera plugin. (CameraPlatform.instance as CameraPlugin).cameras[cameraId] = camera; @@ -657,7 +675,7 @@ void main() { }); testWidgets('starts listening to the camera video error and abort events', - (tester) async { + (WidgetTester tester) async { // Save the camera in the camera plugin. (CameraPlatform.instance as CameraPlugin).cameras[cameraId] = camera; @@ -671,7 +689,7 @@ void main() { }); testWidgets('starts listening to the camera ended events', - (tester) async { + (WidgetTester tester) async { // Save the camera in the camera plugin. (CameraPlatform.instance as CameraPlugin).cameras[cameraId] = camera; @@ -685,12 +703,12 @@ void main() { group('throws PlatformException', () { testWidgets( 'with notFound error ' - 'if the camera does not exist', (tester) async { + 'if the camera does not exist', (WidgetTester tester) async { expect( () => CameraPlatform.instance.initializeCamera(cameraId), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', CameraErrorCode.notFound.toString(), ), @@ -698,8 +716,9 @@ void main() { ); }); - testWidgets('when camera throws CameraWebException', (tester) async { - final exception = CameraWebException( + testWidgets('when camera throws CameraWebException', + (WidgetTester tester) async { + final CameraWebException exception = CameraWebException( cameraId, CameraErrorCode.permissionDenied, 'description', @@ -714,7 +733,7 @@ void main() { () => CameraPlatform.instance.initializeCamera(cameraId), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', exception.code.toString(), ), @@ -722,10 +741,13 @@ void main() { ); }); - testWidgets('when camera throws DomException', (tester) async { - final exception = FakeDomException(DomException.NOT_ALLOWED); + testWidgets('when camera throws DomException', + (WidgetTester tester) async { + final FakeDomException exception = + FakeDomException(DomException.NOT_ALLOWED); - when(camera.initialize).thenAnswer((_) => Future.value()); + when(camera.initialize) + .thenAnswer((Invocation _) => Future.value()); when(camera.play).thenThrow(exception); // Save the camera in the camera plugin. @@ -735,7 +757,7 @@ void main() { () => CameraPlatform.instance.initializeCamera(cameraId), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', exception.name.toString(), ), @@ -754,7 +776,7 @@ void main() { testWidgets( 'requests full-screen mode ' - 'on documentElement', (tester) async { + 'on documentElement', (WidgetTester tester) async { await CameraPlatform.instance.lockCaptureOrientation( cameraId, DeviceOrientation.portraitUp, @@ -765,7 +787,7 @@ void main() { testWidgets( 'locks the capture orientation ' - 'based on the given device orientation', (tester) async { + 'based on the given device orientation', (WidgetTester tester) async { when( () => cameraService.mapDeviceOrientationToOrientationType( DeviceOrientation.landscapeRight, @@ -793,7 +815,7 @@ void main() { group('throws PlatformException', () { testWidgets( 'with orientationNotSupported error ' - 'when screen is not supported', (tester) async { + 'when screen is not supported', (WidgetTester tester) async { when(() => window.screen).thenReturn(null); expect( @@ -803,7 +825,7 @@ void main() { ), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', CameraErrorCode.orientationNotSupported.toString(), ), @@ -813,7 +835,8 @@ void main() { testWidgets( 'with orientationNotSupported error ' - 'when screen orientation is not supported', (tester) async { + 'when screen orientation is not supported', + (WidgetTester tester) async { when(() => screen.orientation).thenReturn(null); expect( @@ -823,7 +846,7 @@ void main() { ), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', CameraErrorCode.orientationNotSupported.toString(), ), @@ -833,7 +856,8 @@ void main() { testWidgets( 'with orientationNotSupported error ' - 'when documentElement is not available', (tester) async { + 'when documentElement is not available', + (WidgetTester tester) async { when(() => document.documentElement).thenReturn(null); expect( @@ -843,7 +867,7 @@ void main() { ), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', CameraErrorCode.orientationNotSupported.toString(), ), @@ -851,8 +875,10 @@ void main() { ); }); - testWidgets('when lock throws DomException', (tester) async { - final exception = FakeDomException(DomException.NOT_ALLOWED); + testWidgets('when lock throws DomException', + (WidgetTester tester) async { + final FakeDomException exception = + FakeDomException(DomException.NOT_ALLOWED); when(() => screenOrientation.lock(any())).thenThrow(exception); @@ -863,7 +889,7 @@ void main() { ), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', exception.name, ), @@ -880,7 +906,8 @@ void main() { ).thenReturn(OrientationType.portraitPrimary); }); - testWidgets('unlocks the capture orientation', (tester) async { + testWidgets('unlocks the capture orientation', + (WidgetTester tester) async { await CameraPlatform.instance.unlockCaptureOrientation( cameraId, ); @@ -891,7 +918,7 @@ void main() { group('throws PlatformException', () { testWidgets( 'with orientationNotSupported error ' - 'when screen is not supported', (tester) async { + 'when screen is not supported', (WidgetTester tester) async { when(() => window.screen).thenReturn(null); expect( @@ -900,7 +927,7 @@ void main() { ), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', CameraErrorCode.orientationNotSupported.toString(), ), @@ -910,7 +937,8 @@ void main() { testWidgets( 'with orientationNotSupported error ' - 'when screen orientation is not supported', (tester) async { + 'when screen orientation is not supported', + (WidgetTester tester) async { when(() => screen.orientation).thenReturn(null); expect( @@ -919,7 +947,7 @@ void main() { ), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', CameraErrorCode.orientationNotSupported.toString(), ), @@ -929,7 +957,8 @@ void main() { testWidgets( 'with orientationNotSupported error ' - 'when documentElement is not available', (tester) async { + 'when documentElement is not available', + (WidgetTester tester) async { when(() => document.documentElement).thenReturn(null); expect( @@ -938,7 +967,7 @@ void main() { ), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', CameraErrorCode.orientationNotSupported.toString(), ), @@ -946,8 +975,10 @@ void main() { ); }); - testWidgets('when unlock throws DomException', (tester) async { - final exception = FakeDomException(DomException.NOT_ALLOWED); + testWidgets('when unlock throws DomException', + (WidgetTester tester) async { + final FakeDomException exception = + FakeDomException(DomException.NOT_ALLOWED); when(screenOrientation.unlock).thenThrow(exception); @@ -957,7 +988,7 @@ void main() { ), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', exception.name, ), @@ -968,17 +999,18 @@ void main() { }); group('takePicture', () { - testWidgets('captures a picture', (tester) async { - final camera = MockCamera(); - final capturedPicture = MockXFile(); + testWidgets('captures a picture', (WidgetTester tester) async { + final MockCamera camera = MockCamera(); + final MockXFile capturedPicture = MockXFile(); when(camera.takePicture) - .thenAnswer((_) => Future.value(capturedPicture)); + .thenAnswer((Invocation _) => Future.value(capturedPicture)); // Save the camera in the camera plugin. (CameraPlatform.instance as CameraPlugin).cameras[cameraId] = camera; - final picture = await CameraPlatform.instance.takePicture(cameraId); + final XFile picture = + await CameraPlatform.instance.takePicture(cameraId); verify(camera.takePicture).called(1); @@ -988,12 +1020,12 @@ void main() { group('throws PlatformException', () { testWidgets( 'with notFound error ' - 'if the camera does not exist', (tester) async { + 'if the camera does not exist', (WidgetTester tester) async { expect( () => CameraPlatform.instance.takePicture(cameraId), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', CameraErrorCode.notFound.toString(), ), @@ -1001,9 +1033,11 @@ void main() { ); }); - testWidgets('when takePicture throws DomException', (tester) async { - final camera = MockCamera(); - final exception = FakeDomException(DomException.NOT_SUPPORTED); + testWidgets('when takePicture throws DomException', + (WidgetTester tester) async { + final MockCamera camera = MockCamera(); + final FakeDomException exception = + FakeDomException(DomException.NOT_SUPPORTED); when(camera.takePicture).thenThrow(exception); @@ -1014,7 +1048,7 @@ void main() { () => CameraPlatform.instance.takePicture(cameraId), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', exception.name, ), @@ -1023,9 +1057,9 @@ void main() { }); testWidgets('when takePicture throws CameraWebException', - (tester) async { - final camera = MockCamera(); - final exception = CameraWebException( + (WidgetTester tester) async { + final MockCamera camera = MockCamera(); + final CameraWebException exception = CameraWebException( cameraId, CameraErrorCode.notStarted, 'description', @@ -1040,7 +1074,7 @@ void main() { () => CameraPlatform.instance.takePicture(cameraId), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', exception.code.toString(), ), @@ -1056,13 +1090,13 @@ void main() { setUp(() { camera = MockCamera(); - when(camera.startVideoRecording).thenAnswer((_) async {}); + when(camera.startVideoRecording).thenAnswer((Invocation _) async {}); when(() => camera.onVideoRecordingError) - .thenAnswer((_) => const Stream.empty()); + .thenAnswer((Invocation _) => const Stream.empty()); }); - testWidgets('starts a video recording', (tester) async { + testWidgets('starts a video recording', (WidgetTester tester) async { // Save the camera in the camera plugin. (CameraPlatform.instance as CameraPlugin).cameras[cameraId] = camera; @@ -1072,11 +1106,12 @@ void main() { }); testWidgets('listens to the onVideoRecordingError stream', - (tester) async { - final videoRecordingErrorController = StreamController(); + (WidgetTester tester) async { + final StreamController videoRecordingErrorController = + StreamController(); when(() => camera.onVideoRecordingError) - .thenAnswer((_) => videoRecordingErrorController.stream); + .thenAnswer((Invocation _) => videoRecordingErrorController.stream); // Save the camera in the camera plugin. (CameraPlatform.instance as CameraPlugin).cameras[cameraId] = camera; @@ -1092,12 +1127,12 @@ void main() { group('throws PlatformException', () { testWidgets( 'with notFound error ' - 'if the camera does not exist', (tester) async { + 'if the camera does not exist', (WidgetTester tester) async { expect( () => CameraPlatform.instance.startVideoRecording(cameraId), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', CameraErrorCode.notFound.toString(), ), @@ -1106,8 +1141,9 @@ void main() { }); testWidgets('when startVideoRecording throws DomException', - (tester) async { - final exception = FakeDomException(DomException.INVALID_STATE); + (WidgetTester tester) async { + final FakeDomException exception = + FakeDomException(DomException.INVALID_STATE); when(camera.startVideoRecording).thenThrow(exception); @@ -1118,7 +1154,7 @@ void main() { () => CameraPlatform.instance.startVideoRecording(cameraId), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', exception.name, ), @@ -1127,8 +1163,8 @@ void main() { }); testWidgets('when startVideoRecording throws CameraWebException', - (tester) async { - final exception = CameraWebException( + (WidgetTester tester) async { + final CameraWebException exception = CameraWebException( cameraId, CameraErrorCode.notStarted, 'description', @@ -1143,7 +1179,7 @@ void main() { () => CameraPlatform.instance.startVideoRecording(cameraId), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', exception.code.toString(), ), @@ -1154,17 +1190,17 @@ void main() { }); group('stopVideoRecording', () { - testWidgets('stops a video recording', (tester) async { - final camera = MockCamera(); - final capturedVideo = MockXFile(); + testWidgets('stops a video recording', (WidgetTester tester) async { + final MockCamera camera = MockCamera(); + final MockXFile capturedVideo = MockXFile(); when(camera.stopVideoRecording) - .thenAnswer((_) => Future.value(capturedVideo)); + .thenAnswer((Invocation _) => Future.value(capturedVideo)); // Save the camera in the camera plugin. (CameraPlatform.instance as CameraPlugin).cameras[cameraId] = camera; - final video = + final XFile video = await CameraPlatform.instance.stopVideoRecording(cameraId); verify(camera.stopVideoRecording).called(1); @@ -1173,23 +1209,25 @@ void main() { }); testWidgets('stops listening to the onVideoRecordingError stream', - (tester) async { - final camera = MockCamera(); - final videoRecordingErrorController = StreamController(); + (WidgetTester tester) async { + final MockCamera camera = MockCamera(); + final StreamController videoRecordingErrorController = + StreamController(); - when(camera.startVideoRecording).thenAnswer((_) async => {}); + when(camera.startVideoRecording).thenAnswer((Invocation _) async {}); when(camera.stopVideoRecording) - .thenAnswer((_) => Future.value(MockXFile())); + .thenAnswer((Invocation _) => Future.value(MockXFile())); when(() => camera.onVideoRecordingError) - .thenAnswer((_) => videoRecordingErrorController.stream); + .thenAnswer((Invocation _) => videoRecordingErrorController.stream); // Save the camera in the camera plugin. (CameraPlatform.instance as CameraPlugin).cameras[cameraId] = camera; await CameraPlatform.instance.startVideoRecording(cameraId); - final _ = await CameraPlatform.instance.stopVideoRecording(cameraId); + final XFile _ = + await CameraPlatform.instance.stopVideoRecording(cameraId); expect( videoRecordingErrorController.hasListener, @@ -1200,12 +1238,12 @@ void main() { group('throws PlatformException', () { testWidgets( 'with notFound error ' - 'if the camera does not exist', (tester) async { + 'if the camera does not exist', (WidgetTester tester) async { expect( () => CameraPlatform.instance.stopVideoRecording(cameraId), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', CameraErrorCode.notFound.toString(), ), @@ -1214,9 +1252,10 @@ void main() { }); testWidgets('when stopVideoRecording throws DomException', - (tester) async { - final camera = MockCamera(); - final exception = FakeDomException(DomException.INVALID_STATE); + (WidgetTester tester) async { + final MockCamera camera = MockCamera(); + final FakeDomException exception = + FakeDomException(DomException.INVALID_STATE); when(camera.stopVideoRecording).thenThrow(exception); @@ -1227,7 +1266,7 @@ void main() { () => CameraPlatform.instance.stopVideoRecording(cameraId), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', exception.name, ), @@ -1236,9 +1275,9 @@ void main() { }); testWidgets('when stopVideoRecording throws CameraWebException', - (tester) async { - final camera = MockCamera(); - final exception = CameraWebException( + (WidgetTester tester) async { + final MockCamera camera = MockCamera(); + final CameraWebException exception = CameraWebException( cameraId, CameraErrorCode.notStarted, 'description', @@ -1253,7 +1292,7 @@ void main() { () => CameraPlatform.instance.stopVideoRecording(cameraId), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', exception.code.toString(), ), @@ -1264,10 +1303,10 @@ void main() { }); group('pauseVideoRecording', () { - testWidgets('pauses a video recording', (tester) async { - final camera = MockCamera(); + testWidgets('pauses a video recording', (WidgetTester tester) async { + final MockCamera camera = MockCamera(); - when(camera.pauseVideoRecording).thenAnswer((_) async {}); + when(camera.pauseVideoRecording).thenAnswer((Invocation _) async {}); // Save the camera in the camera plugin. (CameraPlatform.instance as CameraPlugin).cameras[cameraId] = camera; @@ -1280,12 +1319,12 @@ void main() { group('throws PlatformException', () { testWidgets( 'with notFound error ' - 'if the camera does not exist', (tester) async { + 'if the camera does not exist', (WidgetTester tester) async { expect( () => CameraPlatform.instance.pauseVideoRecording(cameraId), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', CameraErrorCode.notFound.toString(), ), @@ -1294,9 +1333,10 @@ void main() { }); testWidgets('when pauseVideoRecording throws DomException', - (tester) async { - final camera = MockCamera(); - final exception = FakeDomException(DomException.INVALID_STATE); + (WidgetTester tester) async { + final MockCamera camera = MockCamera(); + final FakeDomException exception = + FakeDomException(DomException.INVALID_STATE); when(camera.pauseVideoRecording).thenThrow(exception); @@ -1307,7 +1347,7 @@ void main() { () => CameraPlatform.instance.pauseVideoRecording(cameraId), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', exception.name, ), @@ -1316,9 +1356,9 @@ void main() { }); testWidgets('when pauseVideoRecording throws CameraWebException', - (tester) async { - final camera = MockCamera(); - final exception = CameraWebException( + (WidgetTester tester) async { + final MockCamera camera = MockCamera(); + final CameraWebException exception = CameraWebException( cameraId, CameraErrorCode.notStarted, 'description', @@ -1333,7 +1373,7 @@ void main() { () => CameraPlatform.instance.pauseVideoRecording(cameraId), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', exception.code.toString(), ), @@ -1344,10 +1384,10 @@ void main() { }); group('resumeVideoRecording', () { - testWidgets('resumes a video recording', (tester) async { - final camera = MockCamera(); + testWidgets('resumes a video recording', (WidgetTester tester) async { + final MockCamera camera = MockCamera(); - when(camera.resumeVideoRecording).thenAnswer((_) async {}); + when(camera.resumeVideoRecording).thenAnswer((Invocation _) async {}); // Save the camera in the camera plugin. (CameraPlatform.instance as CameraPlugin).cameras[cameraId] = camera; @@ -1360,12 +1400,12 @@ void main() { group('throws PlatformException', () { testWidgets( 'with notFound error ' - 'if the camera does not exist', (tester) async { + 'if the camera does not exist', (WidgetTester tester) async { expect( () => CameraPlatform.instance.resumeVideoRecording(cameraId), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', CameraErrorCode.notFound.toString(), ), @@ -1374,9 +1414,10 @@ void main() { }); testWidgets('when resumeVideoRecording throws DomException', - (tester) async { - final camera = MockCamera(); - final exception = FakeDomException(DomException.INVALID_STATE); + (WidgetTester tester) async { + final MockCamera camera = MockCamera(); + final FakeDomException exception = + FakeDomException(DomException.INVALID_STATE); when(camera.resumeVideoRecording).thenThrow(exception); @@ -1387,7 +1428,7 @@ void main() { () => CameraPlatform.instance.resumeVideoRecording(cameraId), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', exception.name, ), @@ -1396,9 +1437,9 @@ void main() { }); testWidgets('when resumeVideoRecording throws CameraWebException', - (tester) async { - final camera = MockCamera(); - final exception = CameraWebException( + (WidgetTester tester) async { + final MockCamera camera = MockCamera(); + final CameraWebException exception = CameraWebException( cameraId, CameraErrorCode.notStarted, 'description', @@ -1413,7 +1454,7 @@ void main() { () => CameraPlatform.instance.resumeVideoRecording(cameraId), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', exception.code.toString(), ), @@ -1424,9 +1465,10 @@ void main() { }); group('setFlashMode', () { - testWidgets('calls setFlashMode on the camera', (tester) async { - final camera = MockCamera(); - const flashMode = FlashMode.always; + testWidgets('calls setFlashMode on the camera', + (WidgetTester tester) async { + final MockCamera camera = MockCamera(); + const FlashMode flashMode = FlashMode.always; // Save the camera in the camera plugin. (CameraPlatform.instance as CameraPlugin).cameras[cameraId] = camera; @@ -1442,7 +1484,7 @@ void main() { group('throws PlatformException', () { testWidgets( 'with notFound error ' - 'if the camera does not exist', (tester) async { + 'if the camera does not exist', (WidgetTester tester) async { expect( () => CameraPlatform.instance.setFlashMode( cameraId, @@ -1450,7 +1492,7 @@ void main() { ), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', CameraErrorCode.notFound.toString(), ), @@ -1458,9 +1500,11 @@ void main() { ); }); - testWidgets('when setFlashMode throws DomException', (tester) async { - final camera = MockCamera(); - final exception = FakeDomException(DomException.NOT_SUPPORTED); + testWidgets('when setFlashMode throws DomException', + (WidgetTester tester) async { + final MockCamera camera = MockCamera(); + final FakeDomException exception = + FakeDomException(DomException.NOT_SUPPORTED); when(() => camera.setFlashMode(any())).thenThrow(exception); @@ -1474,7 +1518,7 @@ void main() { ), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', exception.name, ), @@ -1483,9 +1527,9 @@ void main() { }); testWidgets('when setFlashMode throws CameraWebException', - (tester) async { - final camera = MockCamera(); - final exception = CameraWebException( + (WidgetTester tester) async { + final MockCamera camera = MockCamera(); + final CameraWebException exception = CameraWebException( cameraId, CameraErrorCode.notStarted, 'description', @@ -1503,7 +1547,7 @@ void main() { ), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', exception.code.toString(), ), @@ -1513,7 +1557,8 @@ void main() { }); }); - testWidgets('setExposureMode throws UnimplementedError', (tester) async { + testWidgets('setExposureMode throws UnimplementedError', + (WidgetTester tester) async { expect( () => CameraPlatform.instance.setExposureMode( cameraId, @@ -1523,18 +1568,19 @@ void main() { ); }); - testWidgets('setExposurePoint throws UnimplementedError', (tester) async { + testWidgets('setExposurePoint throws UnimplementedError', + (WidgetTester tester) async { expect( () => CameraPlatform.instance.setExposurePoint( cameraId, - const Point(0, 0), + const Point(0, 0), ), throwsUnimplementedError, ); }); testWidgets('getMinExposureOffset throws UnimplementedError', - (tester) async { + (WidgetTester tester) async { expect( () => CameraPlatform.instance.getMinExposureOffset(cameraId), throwsUnimplementedError, @@ -1542,7 +1588,7 @@ void main() { }); testWidgets('getMaxExposureOffset throws UnimplementedError', - (tester) async { + (WidgetTester tester) async { expect( () => CameraPlatform.instance.getMaxExposureOffset(cameraId), throwsUnimplementedError, @@ -1550,14 +1596,15 @@ void main() { }); testWidgets('getExposureOffsetStepSize throws UnimplementedError', - (tester) async { + (WidgetTester tester) async { expect( () => CameraPlatform.instance.getExposureOffsetStepSize(cameraId), throwsUnimplementedError, ); }); - testWidgets('setExposureOffset throws UnimplementedError', (tester) async { + testWidgets('setExposureOffset throws UnimplementedError', + (WidgetTester tester) async { expect( () => CameraPlatform.instance.setExposureOffset( cameraId, @@ -1567,7 +1614,8 @@ void main() { ); }); - testWidgets('setFocusMode throws UnimplementedError', (tester) async { + testWidgets('setFocusMode throws UnimplementedError', + (WidgetTester tester) async { expect( () => CameraPlatform.instance.setFocusMode( cameraId, @@ -1577,20 +1625,22 @@ void main() { ); }); - testWidgets('setFocusPoint throws UnimplementedError', (tester) async { + testWidgets('setFocusPoint throws UnimplementedError', + (WidgetTester tester) async { expect( () => CameraPlatform.instance.setFocusPoint( cameraId, - const Point(0, 0), + const Point(0, 0), ), throwsUnimplementedError, ); }); group('getMaxZoomLevel', () { - testWidgets('calls getMaxZoomLevel on the camera', (tester) async { - final camera = MockCamera(); - const maximumZoomLevel = 100.0; + testWidgets('calls getMaxZoomLevel on the camera', + (WidgetTester tester) async { + final MockCamera camera = MockCamera(); + const double maximumZoomLevel = 100.0; when(camera.getMaxZoomLevel).thenReturn(maximumZoomLevel); @@ -1610,14 +1660,14 @@ void main() { group('throws PlatformException', () { testWidgets( 'with notFound error ' - 'if the camera does not exist', (tester) async { + 'if the camera does not exist', (WidgetTester tester) async { expect( () async => await CameraPlatform.instance.getMaxZoomLevel( cameraId, ), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', CameraErrorCode.notFound.toString(), ), @@ -1625,9 +1675,11 @@ void main() { ); }); - testWidgets('when getMaxZoomLevel throws DomException', (tester) async { - final camera = MockCamera(); - final exception = FakeDomException(DomException.NOT_SUPPORTED); + testWidgets('when getMaxZoomLevel throws DomException', + (WidgetTester tester) async { + final MockCamera camera = MockCamera(); + final FakeDomException exception = + FakeDomException(DomException.NOT_SUPPORTED); when(camera.getMaxZoomLevel).thenThrow(exception); @@ -1640,7 +1692,7 @@ void main() { ), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', exception.name, ), @@ -1649,9 +1701,9 @@ void main() { }); testWidgets('when getMaxZoomLevel throws CameraWebException', - (tester) async { - final camera = MockCamera(); - final exception = CameraWebException( + (WidgetTester tester) async { + final MockCamera camera = MockCamera(); + final CameraWebException exception = CameraWebException( cameraId, CameraErrorCode.notStarted, 'description', @@ -1668,7 +1720,7 @@ void main() { ), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', exception.code.toString(), ), @@ -1679,9 +1731,10 @@ void main() { }); group('getMinZoomLevel', () { - testWidgets('calls getMinZoomLevel on the camera', (tester) async { - final camera = MockCamera(); - const minimumZoomLevel = 100.0; + testWidgets('calls getMinZoomLevel on the camera', + (WidgetTester tester) async { + final MockCamera camera = MockCamera(); + const double minimumZoomLevel = 100.0; when(camera.getMinZoomLevel).thenReturn(minimumZoomLevel); @@ -1701,14 +1754,14 @@ void main() { group('throws PlatformException', () { testWidgets( 'with notFound error ' - 'if the camera does not exist', (tester) async { + 'if the camera does not exist', (WidgetTester tester) async { expect( () async => await CameraPlatform.instance.getMinZoomLevel( cameraId, ), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', CameraErrorCode.notFound.toString(), ), @@ -1716,9 +1769,11 @@ void main() { ); }); - testWidgets('when getMinZoomLevel throws DomException', (tester) async { - final camera = MockCamera(); - final exception = FakeDomException(DomException.NOT_SUPPORTED); + testWidgets('when getMinZoomLevel throws DomException', + (WidgetTester tester) async { + final MockCamera camera = MockCamera(); + final FakeDomException exception = + FakeDomException(DomException.NOT_SUPPORTED); when(camera.getMinZoomLevel).thenThrow(exception); @@ -1731,7 +1786,7 @@ void main() { ), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', exception.name, ), @@ -1740,9 +1795,9 @@ void main() { }); testWidgets('when getMinZoomLevel throws CameraWebException', - (tester) async { - final camera = MockCamera(); - final exception = CameraWebException( + (WidgetTester tester) async { + final MockCamera camera = MockCamera(); + final CameraWebException exception = CameraWebException( cameraId, CameraErrorCode.notStarted, 'description', @@ -1759,7 +1814,7 @@ void main() { ), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', exception.code.toString(), ), @@ -1770,13 +1825,14 @@ void main() { }); group('setZoomLevel', () { - testWidgets('calls setZoomLevel on the camera', (tester) async { - final camera = MockCamera(); + testWidgets('calls setZoomLevel on the camera', + (WidgetTester tester) async { + final MockCamera camera = MockCamera(); // Save the camera in the camera plugin. (CameraPlatform.instance as CameraPlugin).cameras[cameraId] = camera; - const zoom = 100.0; + const double zoom = 100.0; await CameraPlatform.instance.setZoomLevel(cameraId, zoom); @@ -1786,7 +1842,7 @@ void main() { group('throws CameraException', () { testWidgets( 'with notFound error ' - 'if the camera does not exist', (tester) async { + 'if the camera does not exist', (WidgetTester tester) async { expect( () async => await CameraPlatform.instance.setZoomLevel( cameraId, @@ -1794,7 +1850,7 @@ void main() { ), throwsA( isA().having( - (e) => e.code, + (CameraException e) => e.code, 'code', CameraErrorCode.notFound.toString(), ), @@ -1802,9 +1858,11 @@ void main() { ); }); - testWidgets('when setZoomLevel throws DomException', (tester) async { - final camera = MockCamera(); - final exception = FakeDomException(DomException.NOT_SUPPORTED); + testWidgets('when setZoomLevel throws DomException', + (WidgetTester tester) async { + final MockCamera camera = MockCamera(); + final FakeDomException exception = + FakeDomException(DomException.NOT_SUPPORTED); when(() => camera.setZoomLevel(any())).thenThrow(exception); @@ -1818,7 +1876,7 @@ void main() { ), throwsA( isA().having( - (e) => e.code, + (CameraException e) => e.code, 'code', exception.name, ), @@ -1827,9 +1885,9 @@ void main() { }); testWidgets('when setZoomLevel throws PlatformException', - (tester) async { - final camera = MockCamera(); - final exception = PlatformException( + (WidgetTester tester) async { + final MockCamera camera = MockCamera(); + final PlatformException exception = PlatformException( code: CameraErrorCode.notSupported.toString(), message: 'message', ); @@ -1846,7 +1904,7 @@ void main() { ), throwsA( isA().having( - (e) => e.code, + (CameraException e) => e.code, 'code', exception.code, ), @@ -1855,9 +1913,9 @@ void main() { }); testWidgets('when setZoomLevel throws CameraWebException', - (tester) async { - final camera = MockCamera(); - final exception = CameraWebException( + (WidgetTester tester) async { + final MockCamera camera = MockCamera(); + final CameraWebException exception = CameraWebException( cameraId, CameraErrorCode.notStarted, 'description', @@ -1875,7 +1933,7 @@ void main() { ), throwsA( isA().having( - (e) => e.code, + (CameraException e) => e.code, 'code', exception.code.toString(), ), @@ -1886,8 +1944,8 @@ void main() { }); group('pausePreview', () { - testWidgets('calls pause on the camera', (tester) async { - final camera = MockCamera(); + testWidgets('calls pause on the camera', (WidgetTester tester) async { + final MockCamera camera = MockCamera(); // Save the camera in the camera plugin. (CameraPlatform.instance as CameraPlugin).cameras[cameraId] = camera; @@ -1900,12 +1958,12 @@ void main() { group('throws PlatformException', () { testWidgets( 'with notFound error ' - 'if the camera does not exist', (tester) async { + 'if the camera does not exist', (WidgetTester tester) async { expect( () async => await CameraPlatform.instance.pausePreview(cameraId), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', CameraErrorCode.notFound.toString(), ), @@ -1913,9 +1971,11 @@ void main() { ); }); - testWidgets('when pause throws DomException', (tester) async { - final camera = MockCamera(); - final exception = FakeDomException(DomException.NOT_SUPPORTED); + testWidgets('when pause throws DomException', + (WidgetTester tester) async { + final MockCamera camera = MockCamera(); + final FakeDomException exception = + FakeDomException(DomException.NOT_SUPPORTED); when(camera.pause).thenThrow(exception); @@ -1926,7 +1986,7 @@ void main() { () async => await CameraPlatform.instance.pausePreview(cameraId), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', exception.name, ), @@ -1937,10 +1997,10 @@ void main() { }); group('resumePreview', () { - testWidgets('calls play on the camera', (tester) async { - final camera = MockCamera(); + testWidgets('calls play on the camera', (WidgetTester tester) async { + final MockCamera camera = MockCamera(); - when(camera.play).thenAnswer((_) async => {}); + when(camera.play).thenAnswer((Invocation _) async {}); // Save the camera in the camera plugin. (CameraPlatform.instance as CameraPlugin).cameras[cameraId] = camera; @@ -1953,12 +2013,12 @@ void main() { group('throws PlatformException', () { testWidgets( 'with notFound error ' - 'if the camera does not exist', (tester) async { + 'if the camera does not exist', (WidgetTester tester) async { expect( () async => await CameraPlatform.instance.resumePreview(cameraId), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', CameraErrorCode.notFound.toString(), ), @@ -1966,9 +2026,11 @@ void main() { ); }); - testWidgets('when play throws DomException', (tester) async { - final camera = MockCamera(); - final exception = FakeDomException(DomException.NOT_SUPPORTED); + testWidgets('when play throws DomException', + (WidgetTester tester) async { + final MockCamera camera = MockCamera(); + final FakeDomException exception = + FakeDomException(DomException.NOT_SUPPORTED); when(camera.play).thenThrow(exception); @@ -1979,7 +2041,7 @@ void main() { () async => await CameraPlatform.instance.resumePreview(cameraId), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', exception.name, ), @@ -1987,9 +2049,10 @@ void main() { ); }); - testWidgets('when play throws CameraWebException', (tester) async { - final camera = MockCamera(); - final exception = CameraWebException( + testWidgets('when play throws CameraWebException', + (WidgetTester tester) async { + final MockCamera camera = MockCamera(); + final CameraWebException exception = CameraWebException( cameraId, CameraErrorCode.unknown, 'description', @@ -2004,7 +2067,7 @@ void main() { () async => await CameraPlatform.instance.resumePreview(cameraId), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', exception.code.toString(), ), @@ -2016,8 +2079,8 @@ void main() { testWidgets( 'buildPreview returns an HtmlElementView ' - 'with an appropriate view type', (tester) async { - final camera = Camera( + 'with an appropriate view type', (WidgetTester tester) async { + final Camera camera = Camera( textureId: cameraId, cameraService: cameraService, ); @@ -2028,7 +2091,7 @@ void main() { expect( CameraPlatform.instance.buildPreview(cameraId), isA().having( - (view) => view.viewType, + (widgets.HtmlElementView view) => view.viewType, 'viewType', camera.getViewType(), ), @@ -2052,38 +2115,41 @@ void main() { endedStreamController = StreamController(); videoRecordingErrorController = StreamController(); - when(camera.getVideoSize).thenReturn(Size(10, 10)); - when(camera.initialize).thenAnswer((_) => Future.value()); - when(camera.play).thenAnswer((_) => Future.value()); - when(camera.dispose).thenAnswer((_) => Future.value()); + when(camera.getVideoSize).thenReturn(const Size(10, 10)); + when(camera.initialize) + .thenAnswer((Invocation _) => Future.value()); + when(camera.play).thenAnswer((Invocation _) => Future.value()); + when(camera.dispose).thenAnswer((Invocation _) => Future.value()); when(() => camera.videoElement).thenReturn(videoElement); - when(() => videoElement.onError) - .thenAnswer((_) => FakeElementStream(errorStreamController.stream)); - when(() => videoElement.onAbort) - .thenAnswer((_) => FakeElementStream(abortStreamController.stream)); + when(() => videoElement.onError).thenAnswer((Invocation _) => + FakeElementStream(errorStreamController.stream)); + when(() => videoElement.onAbort).thenAnswer((Invocation _) => + FakeElementStream(abortStreamController.stream)); when(() => camera.onEnded) - .thenAnswer((_) => endedStreamController.stream); + .thenAnswer((Invocation _) => endedStreamController.stream); when(() => camera.onVideoRecordingError) - .thenAnswer((_) => videoRecordingErrorController.stream); + .thenAnswer((Invocation _) => videoRecordingErrorController.stream); - when(camera.startVideoRecording).thenAnswer((_) async {}); + when(camera.startVideoRecording).thenAnswer((Invocation _) async {}); }); - testWidgets('disposes the correct camera', (tester) async { - const firstCameraId = 0; - const secondCameraId = 1; + testWidgets('disposes the correct camera', (WidgetTester tester) async { + const int firstCameraId = 0; + const int secondCameraId = 1; - final firstCamera = MockCamera(); - final secondCamera = MockCamera(); + final MockCamera firstCamera = MockCamera(); + final MockCamera secondCamera = MockCamera(); - when(firstCamera.dispose).thenAnswer((_) => Future.value()); - when(secondCamera.dispose).thenAnswer((_) => Future.value()); + when(firstCamera.dispose) + .thenAnswer((Invocation _) => Future.value()); + when(secondCamera.dispose) + .thenAnswer((Invocation _) => Future.value()); // Save cameras in the camera plugin. - (CameraPlatform.instance as CameraPlugin).cameras.addAll({ + (CameraPlatform.instance as CameraPlugin).cameras.addAll({ firstCameraId: firstCamera, secondCameraId: secondCamera, }); @@ -2098,14 +2164,14 @@ void main() { // The first camera should be removed from the camera plugin. expect( (CameraPlatform.instance as CameraPlugin).cameras, - equals({ + equals({ secondCameraId: secondCamera, }), ); }); testWidgets('cancels the camera video error and abort subscriptions', - (tester) async { + (WidgetTester tester) async { // Save the camera in the camera plugin. (CameraPlatform.instance as CameraPlugin).cameras[cameraId] = camera; @@ -2116,7 +2182,8 @@ void main() { expect(abortStreamController.hasListener, isFalse); }); - testWidgets('cancels the camera ended subscriptions', (tester) async { + testWidgets('cancels the camera ended subscriptions', + (WidgetTester tester) async { // Save the camera in the camera plugin. (CameraPlatform.instance as CameraPlugin).cameras[cameraId] = camera; @@ -2127,7 +2194,7 @@ void main() { }); testWidgets('cancels the camera video recording error subscriptions', - (tester) async { + (WidgetTester tester) async { // Save the camera in the camera plugin. (CameraPlatform.instance as CameraPlugin).cameras[cameraId] = camera; @@ -2141,12 +2208,12 @@ void main() { group('throws PlatformException', () { testWidgets( 'with notFound error ' - 'if the camera does not exist', (tester) async { + 'if the camera does not exist', (WidgetTester tester) async { expect( () => CameraPlatform.instance.dispose(cameraId), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', CameraErrorCode.notFound.toString(), ), @@ -2154,9 +2221,11 @@ void main() { ); }); - testWidgets('when dispose throws DomException', (tester) async { - final camera = MockCamera(); - final exception = FakeDomException(DomException.INVALID_ACCESS); + testWidgets('when dispose throws DomException', + (WidgetTester tester) async { + final MockCamera camera = MockCamera(); + final FakeDomException exception = + FakeDomException(DomException.INVALID_ACCESS); when(camera.dispose).thenThrow(exception); @@ -2167,7 +2236,7 @@ void main() { () => CameraPlatform.instance.dispose(cameraId), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', exception.name, ), @@ -2178,8 +2247,8 @@ void main() { }); group('getCamera', () { - testWidgets('returns the correct camera', (tester) async { - final camera = Camera( + testWidgets('returns the correct camera', (WidgetTester tester) async { + final Camera camera = Camera( textureId: cameraId, cameraService: cameraService, ); @@ -2196,12 +2265,12 @@ void main() { testWidgets( 'throws PlatformException ' 'with notFound error ' - 'if the camera does not exist', (tester) async { + 'if the camera does not exist', (WidgetTester tester) async { expect( () => (CameraPlatform.instance as CameraPlugin).getCamera(cameraId), throwsA( isA().having( - (e) => e.code, + (PlatformException e) => e.code, 'code', CameraErrorCode.notFound.toString(), ), @@ -2227,30 +2296,32 @@ void main() { endedStreamController = StreamController(); videoRecordingErrorController = StreamController(); - when(camera.getVideoSize).thenReturn(Size(10, 10)); - when(camera.initialize).thenAnswer((_) => Future.value()); - when(camera.play).thenAnswer((_) => Future.value()); + when(camera.getVideoSize).thenReturn(const Size(10, 10)); + when(camera.initialize) + .thenAnswer((Invocation _) => Future.value()); + when(camera.play).thenAnswer((Invocation _) => Future.value()); when(() => camera.videoElement).thenReturn(videoElement); - when(() => videoElement.onError) - .thenAnswer((_) => FakeElementStream(errorStreamController.stream)); - when(() => videoElement.onAbort) - .thenAnswer((_) => FakeElementStream(abortStreamController.stream)); + when(() => videoElement.onError).thenAnswer((Invocation _) => + FakeElementStream(errorStreamController.stream)); + when(() => videoElement.onAbort).thenAnswer((Invocation _) => + FakeElementStream(abortStreamController.stream)); when(() => camera.onEnded) - .thenAnswer((_) => endedStreamController.stream); + .thenAnswer((Invocation _) => endedStreamController.stream); when(() => camera.onVideoRecordingError) - .thenAnswer((_) => videoRecordingErrorController.stream); + .thenAnswer((Invocation _) => videoRecordingErrorController.stream); - when(() => camera.startVideoRecording()).thenAnswer((_) async => {}); + when(() => camera.startVideoRecording()) + .thenAnswer((Invocation _) async {}); }); testWidgets( 'onCameraInitialized emits a CameraInitializedEvent ' - 'on initializeCamera', (tester) async { + 'on initializeCamera', (WidgetTester tester) async { // Mock the camera to use a blank video stream of size 1280x720. - const videoSize = Size(1280, 720); + const Size videoSize = Size(1280, 720); videoElement = getVideoElementWithBlankStream(videoSize); @@ -2259,9 +2330,9 @@ void main() { any(), cameraId: cameraId, ), - ).thenAnswer((_) async => videoElement.captureStream()); + ).thenAnswer((Invocation _) async => videoElement.captureStream()); - final camera = Camera( + final Camera camera = Camera( textureId: cameraId, cameraService: cameraService, ); @@ -2272,7 +2343,8 @@ void main() { final Stream eventStream = CameraPlatform.instance.onCameraInitialized(cameraId); - final streamQueue = StreamQueue(eventStream); + final StreamQueue streamQueue = + StreamQueue(eventStream); await CameraPlatform.instance.initializeCamera(cameraId); @@ -2295,7 +2367,7 @@ void main() { }); testWidgets('onCameraResolutionChanged emits an empty stream', - (tester) async { + (WidgetTester tester) async { expect( CameraPlatform.instance.onCameraResolutionChanged(cameraId), emits(isEmpty), @@ -2304,14 +2376,15 @@ void main() { testWidgets( 'onCameraClosing emits a CameraClosingEvent ' - 'on the camera ended event', (tester) async { + 'on the camera ended event', (WidgetTester tester) async { // Save the camera in the camera plugin. (CameraPlatform.instance as CameraPlugin).cameras[cameraId] = camera; final Stream eventStream = CameraPlatform.instance.onCameraClosing(cameraId); - final streamQueue = StreamQueue(eventStream); + final StreamQueue streamQueue = + StreamQueue(eventStream); await CameraPlatform.instance.initializeCamera(cameraId); @@ -2320,7 +2393,7 @@ void main() { expect( await streamQueue.next, equals( - CameraClosingEvent(cameraId), + const CameraClosingEvent(cameraId), ), ); @@ -2336,20 +2409,22 @@ void main() { testWidgets( 'emits a CameraErrorEvent ' 'on the camera video error event ' - 'with a message', (tester) async { + 'with a message', (WidgetTester tester) async { final Stream eventStream = CameraPlatform.instance.onCameraError(cameraId); - final streamQueue = StreamQueue(eventStream); + final StreamQueue streamQueue = + StreamQueue(eventStream); await CameraPlatform.instance.initializeCamera(cameraId); - final error = FakeMediaError( + final FakeMediaError error = FakeMediaError( MediaError.MEDIA_ERR_NETWORK, 'A network error occured.', ); - final errorCode = CameraErrorCode.fromMediaError(error); + final CameraErrorCode errorCode = + CameraErrorCode.fromMediaError(error); when(() => videoElement.error).thenReturn(error); @@ -2360,7 +2435,7 @@ void main() { equals( CameraErrorEvent( cameraId, - 'Error code: ${errorCode}, error message: ${error.message}', + 'Error code: $errorCode, error message: ${error.message}', ), ), ); @@ -2371,16 +2446,19 @@ void main() { testWidgets( 'emits a CameraErrorEvent ' 'on the camera video error event ' - 'with no message', (tester) async { + 'with no message', (WidgetTester tester) async { final Stream eventStream = CameraPlatform.instance.onCameraError(cameraId); - final streamQueue = StreamQueue(eventStream); + final StreamQueue streamQueue = + StreamQueue(eventStream); await CameraPlatform.instance.initializeCamera(cameraId); - final error = FakeMediaError(MediaError.MEDIA_ERR_NETWORK); - final errorCode = CameraErrorCode.fromMediaError(error); + final FakeMediaError error = + FakeMediaError(MediaError.MEDIA_ERR_NETWORK); + final CameraErrorCode errorCode = + CameraErrorCode.fromMediaError(error); when(() => videoElement.error).thenReturn(error); @@ -2391,7 +2469,7 @@ void main() { equals( CameraErrorEvent( cameraId, - 'Error code: ${errorCode}, error message: No further diagnostic information can be determined or provided.', + 'Error code: $errorCode, error message: No further diagnostic information can be determined or provided.', ), ), ); @@ -2401,11 +2479,12 @@ void main() { testWidgets( 'emits a CameraErrorEvent ' - 'on the camera video abort event', (tester) async { + 'on the camera video abort event', (WidgetTester tester) async { final Stream eventStream = CameraPlatform.instance.onCameraError(cameraId); - final streamQueue = StreamQueue(eventStream); + final StreamQueue streamQueue = + StreamQueue(eventStream); await CameraPlatform.instance.initializeCamera(cameraId); @@ -2426,8 +2505,8 @@ void main() { testWidgets( 'emits a CameraErrorEvent ' - 'on takePicture error', (tester) async { - final exception = CameraWebException( + 'on takePicture error', (WidgetTester tester) async { + final CameraWebException exception = CameraWebException( cameraId, CameraErrorCode.notStarted, 'description', @@ -2438,7 +2517,8 @@ void main() { final Stream eventStream = CameraPlatform.instance.onCameraError(cameraId); - final streamQueue = StreamQueue(eventStream); + final StreamQueue streamQueue = + StreamQueue(eventStream); expect( () async => await CameraPlatform.instance.takePicture(cameraId), @@ -2462,8 +2542,8 @@ void main() { testWidgets( 'emits a CameraErrorEvent ' - 'on setFlashMode error', (tester) async { - final exception = CameraWebException( + 'on setFlashMode error', (WidgetTester tester) async { + final CameraWebException exception = CameraWebException( cameraId, CameraErrorCode.notStarted, 'description', @@ -2474,7 +2554,8 @@ void main() { final Stream eventStream = CameraPlatform.instance.onCameraError(cameraId); - final streamQueue = StreamQueue(eventStream); + final StreamQueue streamQueue = + StreamQueue(eventStream); expect( () async => await CameraPlatform.instance.setFlashMode( @@ -2501,8 +2582,8 @@ void main() { testWidgets( 'emits a CameraErrorEvent ' - 'on getMaxZoomLevel error', (tester) async { - final exception = CameraWebException( + 'on getMaxZoomLevel error', (WidgetTester tester) async { + final CameraWebException exception = CameraWebException( cameraId, CameraErrorCode.zoomLevelNotSupported, 'description', @@ -2513,7 +2594,8 @@ void main() { final Stream eventStream = CameraPlatform.instance.onCameraError(cameraId); - final streamQueue = StreamQueue(eventStream); + final StreamQueue streamQueue = + StreamQueue(eventStream); expect( () async => await CameraPlatform.instance.getMaxZoomLevel( @@ -2539,8 +2621,8 @@ void main() { testWidgets( 'emits a CameraErrorEvent ' - 'on getMinZoomLevel error', (tester) async { - final exception = CameraWebException( + 'on getMinZoomLevel error', (WidgetTester tester) async { + final CameraWebException exception = CameraWebException( cameraId, CameraErrorCode.zoomLevelNotSupported, 'description', @@ -2551,7 +2633,8 @@ void main() { final Stream eventStream = CameraPlatform.instance.onCameraError(cameraId); - final streamQueue = StreamQueue(eventStream); + final StreamQueue streamQueue = + StreamQueue(eventStream); expect( () async => await CameraPlatform.instance.getMinZoomLevel( @@ -2577,8 +2660,8 @@ void main() { testWidgets( 'emits a CameraErrorEvent ' - 'on setZoomLevel error', (tester) async { - final exception = CameraWebException( + 'on setZoomLevel error', (WidgetTester tester) async { + final CameraWebException exception = CameraWebException( cameraId, CameraErrorCode.zoomLevelNotSupported, 'description', @@ -2589,7 +2672,8 @@ void main() { final Stream eventStream = CameraPlatform.instance.onCameraError(cameraId); - final streamQueue = StreamQueue(eventStream); + final StreamQueue streamQueue = + StreamQueue(eventStream); expect( () async => await CameraPlatform.instance.setZoomLevel( @@ -2616,8 +2700,8 @@ void main() { testWidgets( 'emits a CameraErrorEvent ' - 'on resumePreview error', (tester) async { - final exception = CameraWebException( + 'on resumePreview error', (WidgetTester tester) async { + final CameraWebException exception = CameraWebException( cameraId, CameraErrorCode.unknown, 'description', @@ -2628,7 +2712,8 @@ void main() { final Stream eventStream = CameraPlatform.instance.onCameraError(cameraId); - final streamQueue = StreamQueue(eventStream); + final StreamQueue streamQueue = + StreamQueue(eventStream); expect( () async => await CameraPlatform.instance.resumePreview(cameraId), @@ -2652,15 +2737,15 @@ void main() { testWidgets( 'emits a CameraErrorEvent ' - 'on startVideoRecording error', (tester) async { - final exception = CameraWebException( + 'on startVideoRecording error', (WidgetTester tester) async { + final CameraWebException exception = CameraWebException( cameraId, CameraErrorCode.notStarted, 'description', ); when(() => camera.onVideoRecordingError) - .thenAnswer((_) => const Stream.empty()); + .thenAnswer((Invocation _) => const Stream.empty()); when( () => camera.startVideoRecording( @@ -2671,7 +2756,8 @@ void main() { final Stream eventStream = CameraPlatform.instance.onCameraError(cameraId); - final streamQueue = StreamQueue(eventStream); + final StreamQueue streamQueue = + StreamQueue(eventStream); expect( () async => @@ -2696,16 +2782,18 @@ void main() { testWidgets( 'emits a CameraErrorEvent ' - 'on the camera video recording error event', (tester) async { + 'on the camera video recording error event', + (WidgetTester tester) async { final Stream eventStream = CameraPlatform.instance.onCameraError(cameraId); - final streamQueue = StreamQueue(eventStream); + final StreamQueue streamQueue = + StreamQueue(eventStream); await CameraPlatform.instance.initializeCamera(cameraId); await CameraPlatform.instance.startVideoRecording(cameraId); - final errorEvent = FakeErrorEvent('type', 'message'); + final FakeErrorEvent errorEvent = FakeErrorEvent('type', 'message'); videoRecordingErrorController.add(errorEvent); @@ -2724,8 +2812,8 @@ void main() { testWidgets( 'emits a CameraErrorEvent ' - 'on stopVideoRecording error', (tester) async { - final exception = CameraWebException( + 'on stopVideoRecording error', (WidgetTester tester) async { + final CameraWebException exception = CameraWebException( cameraId, CameraErrorCode.notStarted, 'description', @@ -2736,7 +2824,8 @@ void main() { final Stream eventStream = CameraPlatform.instance.onCameraError(cameraId); - final streamQueue = StreamQueue(eventStream); + final StreamQueue streamQueue = + StreamQueue(eventStream); expect( () async => @@ -2761,8 +2850,8 @@ void main() { testWidgets( 'emits a CameraErrorEvent ' - 'on pauseVideoRecording error', (tester) async { - final exception = CameraWebException( + 'on pauseVideoRecording error', (WidgetTester tester) async { + final CameraWebException exception = CameraWebException( cameraId, CameraErrorCode.notStarted, 'description', @@ -2773,7 +2862,8 @@ void main() { final Stream eventStream = CameraPlatform.instance.onCameraError(cameraId); - final streamQueue = StreamQueue(eventStream); + final StreamQueue streamQueue = + StreamQueue(eventStream); expect( () async => @@ -2798,8 +2888,8 @@ void main() { testWidgets( 'emits a CameraErrorEvent ' - 'on resumeVideoRecording error', (tester) async { - final exception = CameraWebException( + 'on resumeVideoRecording error', (WidgetTester tester) async { + final CameraWebException exception = CameraWebException( cameraId, CameraErrorCode.notStarted, 'description', @@ -2810,7 +2900,8 @@ void main() { final Stream eventStream = CameraPlatform.instance.onCameraError(cameraId); - final streamQueue = StreamQueue(eventStream); + final StreamQueue streamQueue = + StreamQueue(eventStream); expect( () async => @@ -2835,18 +2926,21 @@ void main() { }); testWidgets('onVideoRecordedEvent emits a VideoRecordedEvent', - (tester) async { - final camera = MockCamera(); - final capturedVideo = MockXFile(); - final stream = Stream.value( - VideoRecordedEvent(cameraId, capturedVideo, Duration.zero)); - when(() => camera.onVideoRecordedEvent).thenAnswer((_) => stream); + (WidgetTester tester) async { + final MockCamera camera = MockCamera(); + final MockXFile capturedVideo = MockXFile(); + final Stream stream = + Stream.value( + VideoRecordedEvent(cameraId, capturedVideo, Duration.zero)); + when(() => camera.onVideoRecordedEvent) + .thenAnswer((Invocation _) => stream); // Save the camera in the camera plugin. (CameraPlatform.instance as CameraPlugin).cameras[cameraId] = camera; - final streamQueue = - StreamQueue(CameraPlatform.instance.onVideoRecordedEvent(cameraId)); + final StreamQueue streamQueue = + StreamQueue( + CameraPlatform.instance.onVideoRecordedEvent(cameraId)); expect( await streamQueue.next, @@ -2858,7 +2952,8 @@ void main() { group('onDeviceOrientationChanged', () { group('emits an empty stream', () { - testWidgets('when screen is not supported', (tester) async { + testWidgets('when screen is not supported', + (WidgetTester tester) async { when(() => window.screen).thenReturn(null); expect( @@ -2868,7 +2963,7 @@ void main() { }); testWidgets('when screen orientation is not supported', - (tester) async { + (WidgetTester tester) async { when(() => screen.orientation).thenReturn(null); expect( @@ -2879,7 +2974,7 @@ void main() { }); testWidgets('emits the initial DeviceOrientationChangedEvent', - (tester) async { + (WidgetTester tester) async { when( () => cameraService.mapOrientationTypeToDeviceOrientation( OrientationType.portraitPrimary, @@ -2890,20 +2985,22 @@ void main() { when(() => screenOrientation.type) .thenReturn(OrientationType.portraitPrimary); - final eventStreamController = StreamController(); + final StreamController eventStreamController = + StreamController(); when(() => screenOrientation.onChange) - .thenAnswer((_) => eventStreamController.stream); + .thenAnswer((Invocation _) => eventStreamController.stream); final Stream eventStream = CameraPlatform.instance.onDeviceOrientationChanged(); - final streamQueue = StreamQueue(eventStream); + final StreamQueue streamQueue = + StreamQueue(eventStream); expect( await streamQueue.next, equals( - DeviceOrientationChangedEvent( + const DeviceOrientationChangedEvent( DeviceOrientation.portraitUp, ), ), @@ -2914,7 +3011,8 @@ void main() { testWidgets( 'emits a DeviceOrientationChangedEvent ' - 'when the screen orientation is changed', (tester) async { + 'when the screen orientation is changed', + (WidgetTester tester) async { when( () => cameraService.mapOrientationTypeToDeviceOrientation( OrientationType.landscapePrimary, @@ -2927,15 +3025,17 @@ void main() { ), ).thenReturn(DeviceOrientation.portraitDown); - final eventStreamController = StreamController(); + final StreamController eventStreamController = + StreamController(); when(() => screenOrientation.onChange) - .thenAnswer((_) => eventStreamController.stream); + .thenAnswer((Invocation _) => eventStreamController.stream); final Stream eventStream = CameraPlatform.instance.onDeviceOrientationChanged(); - final streamQueue = StreamQueue(eventStream); + final StreamQueue streamQueue = + StreamQueue(eventStream); // Change the screen orientation to landscapePrimary and // emit an event on the screenOrientation.onChange stream. @@ -2947,7 +3047,7 @@ void main() { expect( await streamQueue.next, equals( - DeviceOrientationChangedEvent( + const DeviceOrientationChangedEvent( DeviceOrientation.landscapeLeft, ), ), @@ -2963,7 +3063,7 @@ void main() { expect( await streamQueue.next, equals( - DeviceOrientationChangedEvent( + const DeviceOrientationChangedEvent( DeviceOrientation.portraitDown, ), ), diff --git a/packages/camera/camera_web/example/integration_test/helpers/mocks.dart b/packages/camera/camera_web/example/integration_test/helpers/mocks.dart index 77e9077356f7..3d9550fb7ab8 100644 --- a/packages/camera/camera_web/example/integration_test/helpers/mocks.dart +++ b/packages/camera/camera_web/example/integration_test/helpers/mocks.dart @@ -160,12 +160,12 @@ class FakeErrorEvent extends Fake implements ErrorEvent { /// final videoStream = videoElement.captureStream(); /// ``` VideoElement getVideoElementWithBlankStream(Size videoSize) { - final canvasElement = CanvasElement( + final CanvasElement canvasElement = CanvasElement( width: videoSize.width.toInt(), height: videoSize.height.toInt(), )..context2D.fillRect(0, 0, videoSize.width, videoSize.height); - final videoElement = VideoElement() + final VideoElement videoElement = VideoElement() ..srcObject = canvasElement.captureStream(); return videoElement; diff --git a/packages/camera/camera_web/example/integration_test/zoom_level_capability_test.dart b/packages/camera/camera_web/example/integration_test/zoom_level_capability_test.dart index 09de03100871..8614cd95880f 100644 --- a/packages/camera/camera_web/example/integration_test/zoom_level_capability_test.dart +++ b/packages/camera/camera_web/example/integration_test/zoom_level_capability_test.dart @@ -12,12 +12,12 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); group('ZoomLevelCapability', () { - testWidgets('sets all properties', (tester) async { - const minimum = 100.0; - const maximum = 400.0; - final videoTrack = MockMediaStreamTrack(); + testWidgets('sets all properties', (WidgetTester tester) async { + const double minimum = 100.0; + const double maximum = 400.0; + final MockMediaStreamTrack videoTrack = MockMediaStreamTrack(); - final capability = ZoomLevelCapability( + final ZoomLevelCapability capability = ZoomLevelCapability( minimum: minimum, maximum: maximum, videoTrack: videoTrack, @@ -28,8 +28,8 @@ void main() { expect(capability.videoTrack, equals(videoTrack)); }); - testWidgets('supports value equality', (tester) async { - final videoTrack = MockMediaStreamTrack(); + testWidgets('supports value equality', (WidgetTester tester) async { + final MockMediaStreamTrack videoTrack = MockMediaStreamTrack(); expect( ZoomLevelCapability( diff --git a/packages/camera/camera_web/example/lib/main.dart b/packages/camera/camera_web/example/lib/main.dart index 6e8f85e74f40..ab04ce2ca2c7 100644 --- a/packages/camera/camera_web/example/lib/main.dart +++ b/packages/camera/camera_web/example/lib/main.dart @@ -10,7 +10,7 @@ void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - return Directionality( + return const Directionality( textDirection: TextDirection.ltr, child: Text('Testing... Look at the console output for results!'), ); diff --git a/packages/camera/camera_web/example/pubspec.yaml b/packages/camera/camera_web/example/pubspec.yaml index 1e075712325e..0457e8fdfcf2 100644 --- a/packages/camera/camera_web/example/pubspec.yaml +++ b/packages/camera/camera_web/example/pubspec.yaml @@ -10,7 +10,6 @@ dependencies: sdk: flutter dev_dependencies: - mocktail: ^0.1.4 camera_web: path: ../ flutter_driver: @@ -19,3 +18,4 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter + mocktail: ^0.1.4 diff --git a/packages/camera/camera_web/lib/src/camera.dart b/packages/camera/camera_web/lib/src/camera.dart index cf0187057188..71368c65e99d 100644 --- a/packages/camera/camera_web/lib/src/camera.dart +++ b/packages/camera/camera_web/lib/src/camera.dart @@ -49,7 +49,7 @@ class Camera { // A torch mode constraint name. // See: https://w3c.github.io/mediacapture-image/#dom-mediatracksupportedconstraints-torch - static const _torchModeKey = "torch"; + static const String _torchModeKey = 'torch'; /// The texture id used to register the camera view. final int textureId; @@ -82,7 +82,8 @@ class Camera { /// The stream controller for the [onEnded] stream. @visibleForTesting - final onEndedController = StreamController.broadcast(); + final StreamController onEndedController = + StreamController.broadcast(); StreamSubscription? _onEndedSubscription; @@ -98,7 +99,7 @@ class Camera { /// The stream controller for the [onVideoRecordingError] stream. @visibleForTesting - final videoRecordingErrorController = + final StreamController videoRecordingErrorController = StreamController.broadcast(); StreamSubscription? _onVideoRecordingErrorSubscription; @@ -124,7 +125,7 @@ class Camera { html.MediaRecorder.isTypeSupported; /// The list of consecutive video data files recorded with [mediaRecorder]. - List _videoData = []; + final List _videoData = []; /// Completes when the video recording is stopped/finished. Completer? _videoAvailableCompleter; @@ -138,7 +139,7 @@ class Camera { /// A builder to merge a list of blobs into a single blob. @visibleForTesting html.Blob Function(List blobs, String type) blobBuilder = - (blobs, type) => html.Blob(blobs, type); + (List blobs, String type) => html.Blob(blobs, type); /// The stream that emits a [VideoRecordedEvent] when a video recording is created. Stream get onVideoRecordedEvent => @@ -177,10 +178,10 @@ class Camera { _applyDefaultVideoStyles(videoElement); - final videoTracks = stream!.getVideoTracks(); + final List videoTracks = stream!.getVideoTracks(); if (videoTracks.isNotEmpty) { - final defaultVideoTrack = videoTracks.first; + final html.MediaStreamTrack defaultVideoTrack = videoTracks.first; _onEndedSubscription = defaultVideoTrack.onEnded.listen((html.Event _) { onEndedController.add(defaultVideoTrack); @@ -209,14 +210,14 @@ class Camera { /// Stops the camera stream and resets the camera source. void stop() { - final videoTracks = stream!.getVideoTracks(); + final List videoTracks = stream!.getVideoTracks(); if (videoTracks.isNotEmpty) { onEndedController.add(videoTracks.first); } - final tracks = stream?.getTracks(); + final List? tracks = stream?.getTracks(); if (tracks != null) { - for (final track in tracks) { + for (final html.MediaStreamTrack track in tracks) { track.stop(); } } @@ -229,17 +230,18 @@ class Camera { /// Enables the camera flash (torch mode) for a period of taking a picture /// if the flash mode is either [FlashMode.auto] or [FlashMode.always]. Future takePicture() async { - final shouldEnableTorchMode = + final bool shouldEnableTorchMode = flashMode == FlashMode.auto || flashMode == FlashMode.always; if (shouldEnableTorchMode) { _setTorchMode(enabled: true); } - final videoWidth = videoElement.videoWidth; - final videoHeight = videoElement.videoHeight; - final canvas = html.CanvasElement(width: videoWidth, height: videoHeight); - final isBackCamera = getLensDirection() == CameraLensDirection.back; + final int videoWidth = videoElement.videoWidth; + final int videoHeight = videoElement.videoHeight; + final html.CanvasElement canvas = + html.CanvasElement(width: videoWidth, height: videoHeight); + final bool isBackCamera = getLensDirection() == CameraLensDirection.back; // Flip the picture horizontally if it is not taken from a back camera. if (!isBackCamera) { @@ -251,7 +253,7 @@ class Camera { canvas.context2D .drawImageScaled(videoElement, 0, 0, videoWidth, videoHeight); - final blob = await canvas.toBlob('image/jpeg'); + final html.Blob blob = await canvas.toBlob('image/jpeg'); if (shouldEnableTorchMode) { _setTorchMode(enabled: false); @@ -265,17 +267,19 @@ class Camera { /// Returns [Size.zero] if the camera is missing a video track or /// the video track does not include the width or height setting. Size getVideoSize() { - final videoTracks = videoElement.srcObject?.getVideoTracks() ?? []; + final List videoTracks = + videoElement.srcObject?.getVideoTracks() ?? []; if (videoTracks.isEmpty) { return Size.zero; } - final defaultVideoTrack = videoTracks.first; - final defaultVideoTrackSettings = defaultVideoTrack.getSettings(); + final html.MediaStreamTrack defaultVideoTrack = videoTracks.first; + final Map defaultVideoTrackSettings = + defaultVideoTrack.getSettings(); - final width = defaultVideoTrackSettings['width']; - final height = defaultVideoTrackSettings['height']; + final double? width = defaultVideoTrackSettings['width'] as double?; + final double? height = defaultVideoTrackSettings['height'] as double?; if (width != null && height != null) { return Size(width, height); @@ -296,9 +300,11 @@ class Camera { /// Throws a [CameraWebException] if the torch mode is not supported /// or the camera has not been initialized or started. void setFlashMode(FlashMode mode) { - final mediaDevices = window?.navigator.mediaDevices; - final supportedConstraints = mediaDevices?.getSupportedConstraints(); - final torchModeSupported = supportedConstraints?[_torchModeKey] ?? false; + final html.MediaDevices? mediaDevices = window?.navigator.mediaDevices; + final Map? supportedConstraints = + mediaDevices?.getSupportedConstraints(); + final bool torchModeSupported = + supportedConstraints?[_torchModeKey] as bool? ?? false; if (!torchModeSupported) { throw CameraWebException( @@ -320,18 +326,19 @@ class Camera { /// Throws a [CameraWebException] if the torch mode is not supported /// or the camera has not been initialized or started. void _setTorchMode({required bool enabled}) { - final videoTracks = stream?.getVideoTracks() ?? []; + final List videoTracks = + stream?.getVideoTracks() ?? []; if (videoTracks.isNotEmpty) { - final defaultVideoTrack = videoTracks.first; + final html.MediaStreamTrack defaultVideoTrack = videoTracks.first; final bool canEnableTorchMode = - defaultVideoTrack.getCapabilities()[_torchModeKey] ?? false; + defaultVideoTrack.getCapabilities()[_torchModeKey] as bool? ?? false; if (canEnableTorchMode) { - defaultVideoTrack.applyConstraints({ - "advanced": [ - { + defaultVideoTrack.applyConstraints({ + 'advanced': [ + { _torchModeKey: enabled, } ] @@ -371,7 +378,7 @@ class Camera { /// Throws a [CameraWebException] if the zoom level is invalid, /// not supported or the camera has not been initialized or started. void setZoomLevel(double zoom) { - final zoomLevelCapability = + final ZoomLevelCapability zoomLevelCapability = _cameraService.getZoomLevelCapabilityForCamera(this); if (zoom < zoomLevelCapability.minimum || @@ -383,9 +390,9 @@ class Camera { ); } - zoomLevelCapability.videoTrack.applyConstraints({ - "advanced": [ - { + zoomLevelCapability.videoTrack.applyConstraints({ + 'advanced': [ + { ZoomLevelCapability.constraintName: zoom, } ] @@ -397,16 +404,19 @@ class Camera { /// Returns null if the camera is missing a video track or /// the video track does not include the facing mode setting. CameraLensDirection? getLensDirection() { - final videoTracks = videoElement.srcObject?.getVideoTracks() ?? []; + final List videoTracks = + videoElement.srcObject?.getVideoTracks() ?? []; if (videoTracks.isEmpty) { return null; } - final defaultVideoTrack = videoTracks.first; - final defaultVideoTrackSettings = defaultVideoTrack.getSettings(); + final html.MediaStreamTrack defaultVideoTrack = videoTracks.first; + final Map defaultVideoTrackSettings = + defaultVideoTrack.getSettings(); - final facingMode = defaultVideoTrackSettings['facingMode']; + final String? facingMode = + defaultVideoTrackSettings['facingMode'] as String?; if (facingMode != null) { return _cameraService.mapFacingModeToLensDirection(facingMode); @@ -432,17 +442,18 @@ class Camera { ); } - mediaRecorder ??= html.MediaRecorder(videoElement.srcObject!, { + mediaRecorder ??= + html.MediaRecorder(videoElement.srcObject!, { 'mimeType': _videoMimeType, }); _videoAvailableCompleter = Completer(); _videoDataAvailableListener = - (event) => _onVideoDataAvailable(event, maxVideoDuration); + (html.Event event) => _onVideoDataAvailable(event, maxVideoDuration); _videoRecordingStoppedListener = - (event) => _onVideoRecordingStopped(event, maxVideoDuration); + (html.Event event) => _onVideoRecordingStopped(event, maxVideoDuration); mediaRecorder!.addEventListener( 'dataavailable', @@ -456,7 +467,7 @@ class Camera { _onVideoRecordingErrorSubscription = mediaRecorder!.onError.listen((html.Event event) { - final error = event as html.ErrorEvent; + final html.ErrorEvent error = event as html.ErrorEvent; if (error != null) { videoRecordingErrorController.add(error); } @@ -474,7 +485,7 @@ class Camera { html.Event event, [ Duration? maxVideoDuration, ]) { - final blob = (event as html.BlobEvent).data; + final html.Blob? blob = (event as html.BlobEvent).data; // Append the recorded part of the video to the list of all video data files. if (blob != null) { @@ -494,11 +505,11 @@ class Camera { ]) async { if (_videoData.isNotEmpty) { // Concatenate all video data files into a single blob. - final videoType = _videoData.first.type; - final videoBlob = blobBuilder(_videoData, videoType); + final String videoType = _videoData.first.type; + final html.Blob videoBlob = blobBuilder(_videoData, videoType); // Create a file containing the video blob. - final file = XFile( + final XFile file = XFile( html.Url.createObjectUrl(videoBlob), mimeType: _videoMimeType, name: videoBlob.hashCode.toString(), @@ -506,7 +517,7 @@ class Camera { // Emit an event containing the recorded video file. videoRecorderController.add( - VideoRecordedEvent(this.textureId, file, maxVideoDuration), + VideoRecordedEvent(textureId, file, maxVideoDuration), ); _videoAvailableCompleter?.complete(file); @@ -594,13 +605,13 @@ class Camera { /// Throws a [CameraWebException] if the browser does not support /// any of the available video mime types. String get _videoMimeType { - const types = [ + const List types = [ 'video/mp4', 'video/webm', ]; return types.firstWhere( - (type) => isVideoTypeSupported(type), + (String type) => isVideoTypeSupported(type), orElse: () => throw CameraWebException( textureId, CameraErrorCode.notSupported, @@ -618,7 +629,7 @@ class Camera { /// Applies default styles to the video [element]. void _applyDefaultVideoStyles(html.VideoElement element) { - final isBackCamera = getLensDirection() == CameraLensDirection.back; + final bool isBackCamera = getLensDirection() == CameraLensDirection.back; // Flip the video horizontally if it is not taken from a back camera. if (!isBackCamera) { diff --git a/packages/camera/camera_web/lib/src/camera_service.dart b/packages/camera/camera_web/lib/src/camera_service.dart index 5ba5c80395cc..f15845cf823b 100644 --- a/packages/camera/camera_web/lib/src/camera_service.dart +++ b/packages/camera/camera_web/lib/src/camera_service.dart @@ -16,7 +16,7 @@ import 'package:flutter/services.dart'; /// obtain the camera stream. class CameraService { // A facing mode constraint name. - static const _facingModeKey = "facingMode"; + static const String _facingModeKey = 'facingMode'; /// The current browser window used to access media devices. @visibleForTesting @@ -32,7 +32,7 @@ class CameraService { CameraOptions options, { int cameraId = 0, }) async { - final mediaDevices = window?.navigator.mediaDevices; + final html.MediaDevices? mediaDevices = window?.navigator.mediaDevices; // Throw a not supported exception if the current browser window // does not support any media devices. @@ -44,7 +44,7 @@ class CameraService { } try { - final constraints = await options.toJson(); + final Map constraints = options.toJson(); return await mediaDevices.getUserMedia(constraints); } on html.DomException catch (e) { switch (e.name) { @@ -120,10 +120,12 @@ class CameraService { ZoomLevelCapability getZoomLevelCapabilityForCamera( Camera camera, ) { - final mediaDevices = window?.navigator.mediaDevices; - final supportedConstraints = mediaDevices?.getSupportedConstraints(); - final zoomLevelSupported = - supportedConstraints?[ZoomLevelCapability.constraintName] ?? false; + final html.MediaDevices? mediaDevices = window?.navigator.mediaDevices; + final Map? supportedConstraints = + mediaDevices?.getSupportedConstraints(); + final bool zoomLevelSupported = + supportedConstraints?[ZoomLevelCapability.constraintName] as bool? ?? + false; if (!zoomLevelSupported) { throw CameraWebException( @@ -133,22 +135,26 @@ class CameraService { ); } - final videoTracks = camera.stream?.getVideoTracks() ?? []; + final List videoTracks = + camera.stream?.getVideoTracks() ?? []; if (videoTracks.isNotEmpty) { - final defaultVideoTrack = videoTracks.first; + final html.MediaStreamTrack defaultVideoTrack = videoTracks.first; /// The zoom level capability is represented by MediaSettingsRange. /// See: https://developer.mozilla.org/en-US/docs/Web/API/MediaSettingsRange - final zoomLevelCapability = defaultVideoTrack - .getCapabilities()[ZoomLevelCapability.constraintName] ?? - {}; + final Object zoomLevelCapability = defaultVideoTrack + .getCapabilities()[ZoomLevelCapability.constraintName] + as Object? ?? + {}; // The zoom level capability is a nested JS object, therefore // we need to access its properties with the js_util library. // See: https://api.dart.dev/stable/2.13.4/dart-js_util/getProperty.html - final minimumZoomLevel = jsUtil.getProperty(zoomLevelCapability, 'min'); - final maximumZoomLevel = jsUtil.getProperty(zoomLevelCapability, 'max'); + final num? minimumZoomLevel = + jsUtil.getProperty(zoomLevelCapability, 'min') as num?; + final num? maximumZoomLevel = + jsUtil.getProperty(zoomLevelCapability, 'max') as num?; if (minimumZoomLevel != null && maximumZoomLevel != null) { return ZoomLevelCapability( @@ -175,7 +181,7 @@ class CameraService { /// Returns a facing mode of the [videoTrack] /// (null if the facing mode is not available). String? getFacingModeForVideoTrack(html.MediaStreamTrack videoTrack) { - final mediaDevices = window?.navigator.mediaDevices; + final html.MediaDevices? mediaDevices = window?.navigator.mediaDevices; // Throw a not supported exception if the current browser window // does not support any media devices. @@ -187,8 +193,10 @@ class CameraService { } // Check if the camera facing mode is supported by the current browser. - final supportedConstraints = mediaDevices.getSupportedConstraints(); - final facingModeSupported = supportedConstraints[_facingModeKey] ?? false; + final Map supportedConstraints = + mediaDevices.getSupportedConstraints(); + final bool facingModeSupported = + supportedConstraints[_facingModeKey] as bool? ?? false; // Return null if the facing mode is not supported. if (!facingModeSupported) { @@ -201,8 +209,8 @@ class CameraService { // // MediaTrackSettings: // https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackSettings - final videoTrackSettings = videoTrack.getSettings(); - final facingMode = videoTrackSettings[_facingModeKey]; + final Map videoTrackSettings = videoTrack.getSettings(); + final String? facingMode = videoTrackSettings[_facingModeKey] as String?; if (facingMode == null) { // If the facing mode does not exist in the video track settings, @@ -220,15 +228,18 @@ class CameraService { return null; } - final videoTrackCapabilities = videoTrack.getCapabilities(); + final Map videoTrackCapabilities = + videoTrack.getCapabilities(); // A list of facing mode capabilities as // the camera may support multiple facing modes. - final facingModeCapabilities = - List.from(videoTrackCapabilities[_facingModeKey] ?? []); + final List facingModeCapabilities = List.from( + (videoTrackCapabilities[_facingModeKey] as List?) + ?.cast() ?? + []); if (facingModeCapabilities.isNotEmpty) { - final facingModeCapability = facingModeCapabilities.first; + final String facingModeCapability = facingModeCapabilities.first; return facingModeCapability; } else { // Return null if there are no facing mode capabilities. @@ -277,16 +288,16 @@ class CameraService { switch (resolutionPreset) { case ResolutionPreset.max: case ResolutionPreset.ultraHigh: - return Size(4096, 2160); + return const Size(4096, 2160); case ResolutionPreset.veryHigh: - return Size(1920, 1080); + return const Size(1920, 1080); case ResolutionPreset.high: - return Size(1280, 720); + return const Size(1280, 720); case ResolutionPreset.medium: - return Size(720, 480); + return const Size(720, 480); case ResolutionPreset.low: default: - return Size(320, 240); + return const Size(320, 240); } } diff --git a/packages/camera/camera_web/lib/src/camera_web.dart b/packages/camera/camera_web/lib/src/camera_web.dart index f183b787fb6c..6f9f10d68f84 100644 --- a/packages/camera/camera_web/lib/src/camera_web.dart +++ b/packages/camera/camera_web/lib/src/camera_web.dart @@ -40,37 +40,41 @@ class CameraPlugin extends CameraPlatform { /// The cameras managed by the [CameraPlugin]. @visibleForTesting - final cameras = {}; - var _textureCounter = 1; + final Map cameras = {}; + int _textureCounter = 1; /// Metadata associated with each camera description. /// Populated in [availableCameras]. @visibleForTesting - final camerasMetadata = {}; + final Map camerasMetadata = + {}; /// The controller used to broadcast different camera events. /// /// It is `broadcast` as multiple controllers may subscribe /// to different stream views of this controller. @visibleForTesting - final cameraEventStreamController = StreamController.broadcast(); + final StreamController cameraEventStreamController = + StreamController.broadcast(); - final _cameraVideoErrorSubscriptions = - >{}; + final Map> + _cameraVideoErrorSubscriptions = >{}; - final _cameraVideoAbortSubscriptions = - >{}; + final Map> + _cameraVideoAbortSubscriptions = >{}; - final _cameraEndedSubscriptions = + final Map> + _cameraEndedSubscriptions = >{}; - final _cameraVideoRecordingErrorSubscriptions = + final Map> + _cameraVideoRecordingErrorSubscriptions = >{}; /// Returns a stream of camera events for the given [cameraId]. Stream _cameraEvents(int cameraId) => cameraEventStreamController.stream - .where((event) => event.cameraId == cameraId); + .where((CameraEvent event) => event.cameraId == cameraId); /// The current browser window used to access media devices. @visibleForTesting @@ -79,8 +83,8 @@ class CameraPlugin extends CameraPlatform { @override Future> availableCameras() async { try { - final mediaDevices = window?.navigator.mediaDevices; - final cameras = []; + final html.MediaDevices? mediaDevices = window?.navigator.mediaDevices; + final List cameras = []; // Throw a not supported exception if the current browser window // does not support any media devices. @@ -92,50 +96,56 @@ class CameraPlugin extends CameraPlatform { } // Request video and audio permissions. - final cameraStream = await _cameraService.getMediaStreamForOptions( - CameraOptions( + final html.MediaStream cameraStream = + await _cameraService.getMediaStreamForOptions( + const CameraOptions( audio: AudioConstraints(enabled: true), ), ); // Release the camera stream used to request video and audio permissions. - cameraStream.getVideoTracks().forEach((videoTrack) => videoTrack.stop()); + cameraStream + .getVideoTracks() + .forEach((html.MediaStreamTrack videoTrack) => videoTrack.stop()); // Request available media devices. - final devices = await mediaDevices.enumerateDevices(); + final List devices = await mediaDevices.enumerateDevices(); // Filter video input devices. - final videoInputDevices = devices + final Iterable videoInputDevices = devices .whereType() - .where((device) => device.kind == MediaDeviceKind.videoInput) + .where((html.MediaDeviceInfo device) => + device.kind == MediaDeviceKind.videoInput) /// The device id property is currently not supported on Internet Explorer: /// https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo/deviceId#browser_compatibility .where( - (device) => device.deviceId != null && device.deviceId!.isNotEmpty, + (html.MediaDeviceInfo device) => + device.deviceId != null && device.deviceId!.isNotEmpty, ); // Map video input devices to camera descriptions. - for (final videoInputDevice in videoInputDevices) { + for (final html.MediaDeviceInfo videoInputDevice in videoInputDevices) { // Get the video stream for the current video input device // to later use for the available video tracks. - final videoStream = await _getVideoStreamForDevice( + final html.MediaStream videoStream = await _getVideoStreamForDevice( videoInputDevice.deviceId!, ); // Get all video tracks in the video stream // to later extract the lens direction from the first track. - final videoTracks = videoStream.getVideoTracks(); + final List videoTracks = + videoStream.getVideoTracks(); if (videoTracks.isNotEmpty) { // Get the facing mode from the first available video track. - final facingMode = + final String? facingMode = _cameraService.getFacingModeForVideoTrack(videoTracks.first); // Get the lens direction based on the facing mode. // Fallback to the external lens direction // if the facing mode is not available. - final lensDirection = facingMode != null + final CameraLensDirection lensDirection = facingMode != null ? _cameraService.mapFacingModeToLensDirection(facingMode) : CameraLensDirection.external; @@ -148,14 +158,14 @@ class CameraPlugin extends CameraPlatform { // https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo/label // // Sensor orientation is currently not supported. - final cameraLabel = videoInputDevice.label ?? ''; - final camera = CameraDescription( + final String cameraLabel = videoInputDevice.label ?? ''; + final CameraDescription camera = CameraDescription( name: cameraLabel, lensDirection: lensDirection, sensorOrientation: 0, ); - final cameraMetadata = CameraMetadata( + final CameraMetadata cameraMetadata = CameraMetadata( deviceId: videoInputDevice.deviceId!, facingMode: facingMode, ); @@ -165,7 +175,9 @@ class CameraPlugin extends CameraPlatform { camerasMetadata[camera] = cameraMetadata; // Release the camera stream of the current video input device. - videoTracks.forEach((videoTrack) => videoTrack.stop()); + for (final html.MediaStreamTrack videoTrack in videoTracks) { + videoTrack.stop(); + } } else { // Ignore as no video tracks exist in the current video input device. continue; @@ -198,22 +210,22 @@ class CameraPlugin extends CameraPlatform { ); } - final textureId = _textureCounter++; + final int textureId = _textureCounter++; - final cameraMetadata = camerasMetadata[cameraDescription]!; + final CameraMetadata cameraMetadata = camerasMetadata[cameraDescription]!; - final cameraType = cameraMetadata.facingMode != null + final CameraType? cameraType = cameraMetadata.facingMode != null ? _cameraService.mapFacingModeToCameraType(cameraMetadata.facingMode!) : null; // Use the highest resolution possible // if the resolution preset is not specified. - final videoSize = _cameraService + final Size videoSize = _cameraService .mapResolutionPresetToSize(resolutionPreset ?? ResolutionPreset.max); // Create a camera with the given audio and video constraints. // Sensor orientation is currently not supported. - final camera = Camera( + final Camera camera = Camera( textureId: textureId, cameraService: _cameraService, options: CameraOptions( @@ -247,7 +259,7 @@ class CameraPlugin extends CameraPlatform { ImageFormatGroup imageFormatGroup = ImageFormatGroup.unknown, }) async { try { - final camera = getCamera(cameraId); + final Camera camera = getCamera(cameraId); await camera.initialize(); @@ -258,15 +270,15 @@ class CameraPlugin extends CameraPlatform { // The Event itself (_) doesn't contain information about the actual error. // We need to look at the HTMLMediaElement.error. // See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/error - final error = camera.videoElement.error!; - final errorCode = CameraErrorCode.fromMediaError(error); - final errorMessage = + final html.MediaError error = camera.videoElement.error!; + final CameraErrorCode errorCode = CameraErrorCode.fromMediaError(error); + final String? errorMessage = error.message != '' ? error.message : _kDefaultErrorMessage; cameraEventStreamController.add( CameraErrorEvent( cameraId, - 'Error code: ${errorCode}, error message: ${errorMessage}', + 'Error code: $errorCode, error message: $errorMessage', ), ); }); @@ -294,17 +306,17 @@ class CameraPlugin extends CameraPlatform { ); }); - final cameraSize = camera.getVideoSize(); + final Size cameraSize = camera.getVideoSize(); cameraEventStreamController.add( CameraInitializedEvent( cameraId, cameraSize.width, cameraSize.height, - // TODO(camera_web): Add support for exposure mode and point (https://github.com/flutter/flutter/issues/86857). + // TODO(bselwe): Add support for exposure mode and point (https://github.com/flutter/flutter/issues/86857). ExposureMode.auto, false, - // TODO(camera_web): Add support for focus mode and point (https://github.com/flutter/flutter/issues/86858). + // TODO(bselwe): Add support for focus mode and point (https://github.com/flutter/flutter/issues/86858). FocusMode.auto, false, ), @@ -329,7 +341,7 @@ class CameraPlugin extends CameraPlatform { /// [CameraOptions.video] constraints has to be created and initialized. @override Stream onCameraResolutionChanged(int cameraId) { - return const Stream.empty(); + return const Stream.empty(); } @override @@ -349,37 +361,38 @@ class CameraPlugin extends CameraPlatform { @override Stream onDeviceOrientationChanged() { - final orientation = window?.screen?.orientation; + final html.ScreenOrientation? orientation = window?.screen?.orientation; if (orientation != null) { // Create an initial orientation event that emits the device orientation // as soon as subscribed to this stream. - final initialOrientationEvent = html.Event("change"); + final html.Event initialOrientationEvent = html.Event('change'); return orientation.onChange.startWith(initialOrientationEvent).map( (html.Event _) { - final deviceOrientation = _cameraService + final DeviceOrientation deviceOrientation = _cameraService .mapOrientationTypeToDeviceOrientation(orientation.type!); return DeviceOrientationChangedEvent(deviceOrientation); }, ); } else { - return const Stream.empty(); + return const Stream.empty(); } } @override Future lockCaptureOrientation( int cameraId, - DeviceOrientation deviceOrientation, + DeviceOrientation orientation, ) async { try { - final orientation = window?.screen?.orientation; - final documentElement = window?.document.documentElement; + final html.ScreenOrientation? screenOrientation = + window?.screen?.orientation; + final html.Element? documentElement = window?.document.documentElement; - if (orientation != null && documentElement != null) { - final orientationType = _cameraService - .mapDeviceOrientationToOrientationType(deviceOrientation); + if (screenOrientation != null && documentElement != null) { + final String orientationType = + _cameraService.mapDeviceOrientationToOrientationType(orientation); // Full-screen mode may be required to modify the device orientation. // See: https://w3c.github.io/screen-orientation/#interaction-with-fullscreen-api @@ -387,7 +400,7 @@ class CameraPlugin extends CameraPlatform { // This wrapper allows use of both the old and new APIs. dynamic fullScreen() => documentElement.requestFullscreen(); await fullScreen(); - await orientation.lock(orientationType.toString()); + await screenOrientation.lock(orientationType.toString()); } else { throw PlatformException( code: CameraErrorCode.orientationNotSupported.toString(), @@ -402,8 +415,8 @@ class CameraPlugin extends CameraPlatform { @override Future unlockCaptureOrientation(int cameraId) async { try { - final orientation = window?.screen?.orientation; - final documentElement = window?.document.documentElement; + final html.ScreenOrientation? orientation = window?.screen?.orientation; + final html.Element? documentElement = window?.document.documentElement; if (orientation != null && documentElement != null) { orientation.unlock(); @@ -438,7 +451,7 @@ class CameraPlugin extends CameraPlatform { @override Future startVideoRecording(int cameraId, {Duration? maxVideoDuration}) { try { - final camera = getCamera(cameraId); + final Camera camera = getCamera(cameraId); // Add camera's video recording errors to the camera events stream. // The error event fires when the video recording is not allowed or an unsupported @@ -465,7 +478,8 @@ class CameraPlugin extends CameraPlatform { @override Future stopVideoRecording(int cameraId) async { try { - final videoRecording = await getCamera(cameraId).stopVideoRecording(); + final XFile videoRecording = + await getCamera(cameraId).stopVideoRecording(); await _cameraVideoRecordingErrorSubscriptions[cameraId]?.cancel(); return videoRecording; } on html.DomException catch (e) { @@ -641,7 +655,7 @@ class CameraPlugin extends CameraPlatform { String deviceId, ) { // Create camera options with the desired device id. - final cameraOptions = CameraOptions( + final CameraOptions cameraOptions = CameraOptions( video: VideoConstraints(deviceId: deviceId), ); @@ -653,7 +667,7 @@ class CameraPlugin extends CameraPlatform { /// Throws a [CameraException] if the camera does not exist. @visibleForTesting Camera getCamera(int cameraId) { - final camera = cameras[cameraId]; + final Camera? camera = cameras[cameraId]; if (camera == null) { throw PlatformException( diff --git a/packages/camera/camera_web/lib/src/shims/dart_js_util.dart b/packages/camera/camera_web/lib/src/shims/dart_js_util.dart index 6601bec6f529..7d766e8c269e 100644 --- a/packages/camera/camera_web/lib/src/shims/dart_js_util.dart +++ b/packages/camera/camera_web/lib/src/shims/dart_js_util.dart @@ -10,5 +10,6 @@ class JsUtil { bool hasProperty(Object o, Object name) => js_util.hasProperty(o, name); /// Returns the value of the property [name] in the object [o]. - dynamic getProperty(Object o, Object name) => js_util.getProperty(o, name); + dynamic getProperty(Object o, Object name) => + js_util.getProperty(o, name); } diff --git a/packages/camera/camera_web/lib/src/shims/dart_ui.dart b/packages/camera/camera_web/lib/src/shims/dart_ui.dart index 5eacec5fe867..3a32721cb9c8 100644 --- a/packages/camera/camera_web/lib/src/shims/dart_ui.dart +++ b/packages/camera/camera_web/lib/src/shims/dart_ui.dart @@ -5,6 +5,6 @@ /// This file shims dart:ui in web-only scenarios, getting rid of the need to /// suppress analyzer warnings. -// TODO(flutter/flutter#55000) Remove this file once web-only dart:ui APIs -// are exposed from a dedicated place. +// TODO(ditman): Remove this file once web-only dart:ui APIs are exposed from +// a dedicated place. https://github.com/flutter/flutter/issues/55000 export 'dart_ui_fake.dart' if (dart.library.html) 'dart_ui_real.dart'; diff --git a/packages/camera/camera_web/lib/src/shims/dart_ui_fake.dart b/packages/camera/camera_web/lib/src/shims/dart_ui_fake.dart index f2862af8b704..8757ca22be17 100644 --- a/packages/camera/camera_web/lib/src/shims/dart_ui_fake.dart +++ b/packages/camera/camera_web/lib/src/shims/dart_ui_fake.dart @@ -7,13 +7,18 @@ import 'dart:html' as html; // Fake interface for the logic that this package needs from (web-only) dart:ui. // This is conditionally exported so the analyzer sees these methods as available. +// ignore_for_file: avoid_classes_with_only_static_members +// ignore_for_file: camel_case_types + /// Shim for web_ui engine.PlatformViewRegistry /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L62 class platformViewRegistry { /// Shim for registerViewFactory /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L72 - static registerViewFactory( - String viewTypeId, html.Element Function(int viewId) viewFactory) {} + static bool registerViewFactory( + String viewTypeId, html.Element Function(int viewId) viewFactory) { + return false; + } } /// Shim for web_ui engine.AssetManager. @@ -21,7 +26,7 @@ class platformViewRegistry { class webOnlyAssetManager { /// Shim for getAssetUrl. /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/src/engine/assets.dart#L45 - static getAssetUrl(String asset) {} + static String getAssetUrl(String asset) => ''; } /// Signature of callbacks that have no arguments and return no data. diff --git a/packages/camera/camera_web/lib/src/types/camera_error_code.dart b/packages/camera/camera_web/lib/src/types/camera_error_code.dart index f70925b4bede..2e8a49b63873 100644 --- a/packages/camera/camera_web/lib/src/types/camera_error_code.dart +++ b/packages/camera/camera_web/lib/src/types/camera_error_code.dart @@ -81,15 +81,15 @@ class CameraErrorCode { static CameraErrorCode fromMediaError(html.MediaError error) { switch (error.code) { case html.MediaError.MEDIA_ERR_ABORTED: - return CameraErrorCode._('mediaErrorAborted'); + return const CameraErrorCode._('mediaErrorAborted'); case html.MediaError.MEDIA_ERR_NETWORK: - return CameraErrorCode._('mediaErrorNetwork'); + return const CameraErrorCode._('mediaErrorNetwork'); case html.MediaError.MEDIA_ERR_DECODE: - return CameraErrorCode._('mediaErrorDecode'); + return const CameraErrorCode._('mediaErrorDecode'); case html.MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED: - return CameraErrorCode._('mediaErrorSourceNotSupported'); + return const CameraErrorCode._('mediaErrorSourceNotSupported'); default: - return CameraErrorCode._('mediaErrorUnknown'); + return const CameraErrorCode._('mediaErrorUnknown'); } } } diff --git a/packages/camera/camera_web/lib/src/types/camera_metadata.dart b/packages/camera/camera_web/lib/src/types/camera_metadata.dart index c9998e58a52c..c42dd3ad8b75 100644 --- a/packages/camera/camera_web/lib/src/types/camera_metadata.dart +++ b/packages/camera/camera_web/lib/src/types/camera_metadata.dart @@ -4,8 +4,11 @@ import 'dart:ui' show hashValues; +import 'package:flutter/foundation.dart'; + /// Metadata used along the camera description /// to store additional web-specific camera details. +@immutable class CameraMetadata { /// Creates a new instance of [CameraMetadata] /// with the given [deviceId] and [facingMode]. @@ -25,7 +28,9 @@ class CameraMetadata { @override bool operator ==(Object other) { - if (identical(this, other)) return true; + if (identical(this, other)) { + return true; + } return other is CameraMetadata && other.deviceId == deviceId && diff --git a/packages/camera/camera_web/lib/src/types/camera_options.dart b/packages/camera/camera_web/lib/src/types/camera_options.dart index 2a4cdbf15348..8fa40bdc1bb8 100644 --- a/packages/camera/camera_web/lib/src/types/camera_options.dart +++ b/packages/camera/camera_web/lib/src/types/camera_options.dart @@ -4,6 +4,8 @@ import 'dart:ui' show hashValues; +import 'package:flutter/foundation.dart'; + /// Options used to create a camera with the given /// [audio] and [video] media constraints. /// @@ -12,6 +14,7 @@ import 'dart:ui' show hashValues; /// with audio and video tracks containing the requested types of media. /// /// https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamConstraints +@immutable class CameraOptions { /// Creates a new instance of [CameraOptions] /// with the given [audio] and [video] constraints. @@ -29,7 +32,7 @@ class CameraOptions { /// Converts the current instance to a Map. Map toJson() { - return { + return { 'audio': audio.toJson(), 'video': video.toJson(), }; @@ -37,7 +40,9 @@ class CameraOptions { @override bool operator ==(Object other) { - if (identical(this, other)) return true; + if (identical(this, other)) { + return true; + } return other is CameraOptions && other.audio == audio && @@ -51,6 +56,7 @@ class CameraOptions { /// Indicates whether the audio track is requested. /// /// By default, the audio track is not requested. +@immutable class AudioConstraints { /// Creates a new instance of [AudioConstraints] /// with the given [enabled] constraint. @@ -64,7 +70,9 @@ class AudioConstraints { @override bool operator ==(Object other) { - if (identical(this, other)) return true; + if (identical(this, other)) { + return true; + } return other is AudioConstraints && other.enabled == enabled; } @@ -75,6 +83,7 @@ class AudioConstraints { /// Defines constraints that the video track must have /// to be considered acceptable. +@immutable class VideoConstraints { /// Creates a new instance of [VideoConstraints] /// with the given constraints. @@ -99,19 +108,29 @@ class VideoConstraints { /// Converts the current instance to a Map. Object toJson() { - final json = {}; - - if (width != null) json['width'] = width!.toJson(); - if (height != null) json['height'] = height!.toJson(); - if (facingMode != null) json['facingMode'] = facingMode!.toJson(); - if (deviceId != null) json['deviceId'] = {'exact': deviceId!}; + final Map json = {}; + + if (width != null) { + json['width'] = width!.toJson(); + } + if (height != null) { + json['height'] = height!.toJson(); + } + if (facingMode != null) { + json['facingMode'] = facingMode!.toJson(); + } + if (deviceId != null) { + json['deviceId'] = {'exact': deviceId!}; + } return json; } @override bool operator ==(Object other) { - if (identical(this, other)) return true; + if (identical(this, other)) { + return true; + } return other is VideoConstraints && other.facingMode == facingMode && @@ -146,16 +165,17 @@ class CameraType { } /// Indicates the direction in which the desired camera should be pointing. +@immutable class FacingModeConstraint { - /// Creates a new instance of [FacingModeConstraint] - /// with the given [ideal] and [exact] constraints. - const FacingModeConstraint._({this.ideal, this.exact}); - /// Creates a new instance of [FacingModeConstraint] /// with [ideal] constraint set to [type]. factory FacingModeConstraint(CameraType type) => FacingModeConstraint._(ideal: type); + /// Creates a new instance of [FacingModeConstraint] + /// with the given [ideal] and [exact] constraints. + const FacingModeConstraint._({this.ideal, this.exact}); + /// Creates a new instance of [FacingModeConstraint] /// with [exact] constraint set to [type]. factory FacingModeConstraint.exact(CameraType type) => @@ -174,8 +194,8 @@ class FacingModeConstraint { final CameraType? exact; /// Converts the current instance to a Map. - Object? toJson() { - return { + Object toJson() { + return { if (ideal != null) 'ideal': ideal.toString(), if (exact != null) 'exact': exact.toString(), }; @@ -183,7 +203,9 @@ class FacingModeConstraint { @override bool operator ==(Object other) { - if (identical(this, other)) return true; + if (identical(this, other)) { + return true; + } return other is FacingModeConstraint && other.ideal == ideal && @@ -200,6 +222,7 @@ class FacingModeConstraint { /// The obtained video track will have a size between [minimum] and [maximum] /// with ideally a size of [ideal]. The size is determined by /// the capabilities of the hardware and the other specified constraints. +@immutable class VideoSizeConstraint { /// Creates a new instance of [VideoSizeConstraint] with the given /// [minimum], [ideal] and [maximum] constraints. @@ -221,18 +244,26 @@ class VideoSizeConstraint { /// Converts the current instance to a Map. Object toJson() { - final json = {}; - - if (ideal != null) json['ideal'] = ideal; - if (minimum != null) json['min'] = minimum; - if (maximum != null) json['max'] = maximum; + final Map json = {}; + + if (ideal != null) { + json['ideal'] = ideal; + } + if (minimum != null) { + json['min'] = minimum; + } + if (maximum != null) { + json['max'] = maximum; + } return json; } @override bool operator ==(Object other) { - if (identical(this, other)) return true; + if (identical(this, other)) { + return true; + } return other is VideoSizeConstraint && other.minimum == minimum && diff --git a/packages/camera/camera_web/lib/src/types/media_device_kind.dart b/packages/camera/camera_web/lib/src/types/media_device_kind.dart index 1f746808df9e..3607bb260f1e 100644 --- a/packages/camera/camera_web/lib/src/types/media_device_kind.dart +++ b/packages/camera/camera_web/lib/src/types/media_device_kind.dart @@ -7,11 +7,11 @@ /// See: https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo/kind abstract class MediaDeviceKind { /// A video input media device kind. - static const videoInput = 'videoinput'; + static const String videoInput = 'videoinput'; /// An audio input media device kind. - static const audioInput = 'audioinput'; + static const String audioInput = 'audioinput'; /// An audio output media device kind. - static const audioOutput = 'audiooutput'; + static const String audioOutput = 'audiooutput'; } diff --git a/packages/camera/camera_web/lib/src/types/zoom_level_capability.dart b/packages/camera/camera_web/lib/src/types/zoom_level_capability.dart index ace57140d956..9a868d2bc0dc 100644 --- a/packages/camera/camera_web/lib/src/types/zoom_level_capability.dart +++ b/packages/camera/camera_web/lib/src/types/zoom_level_capability.dart @@ -5,13 +5,16 @@ import 'dart:html' as html; import 'dart:ui' show hashValues; +import 'package:flutter/foundation.dart'; + /// The possible range of values for the zoom level configurable /// on the camera video track. +@immutable class ZoomLevelCapability { /// Creates a new instance of [ZoomLevelCapability] with the given /// zoom level range of [minimum] to [maximum] configurable /// on the [videoTrack]. - ZoomLevelCapability({ + const ZoomLevelCapability({ required this.minimum, required this.maximum, required this.videoTrack, @@ -19,7 +22,7 @@ class ZoomLevelCapability { /// The zoom level constraint name. /// See: https://w3c.github.io/mediacapture-image/#dom-mediatracksupportedconstraints-zoom - static const constraintName = "zoom"; + static const String constraintName = 'zoom'; /// The minimum zoom level. final double minimum; @@ -32,7 +35,9 @@ class ZoomLevelCapability { @override bool operator ==(Object other) { - if (identical(this, other)) return true; + if (identical(this, other)) { + return true; + } return other is ZoomLevelCapability && other.minimum == minimum && diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index 2839710af2f5..6d6f110157bc 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_web description: A Flutter plugin for getting information about and controlling the camera on Web. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.2.1+2 +version: 0.2.1+3 environment: sdk: ">=2.12.0 <3.0.0" @@ -22,7 +22,6 @@ dependencies: sdk: flutter flutter_web_plugins: sdk: flutter - pedantic: ^1.11.1 stream_transform: ^2.0.0 dev_dependencies: diff --git a/script/configs/custom_analysis.yaml b/script/configs/custom_analysis.yaml index 7e4d3832056e..d434b5d56662 100644 --- a/script/configs/custom_analysis.yaml +++ b/script/configs/custom_analysis.yaml @@ -11,7 +11,6 @@ # TODO(ecosystem): Remove everything from this list. See: # https://github.com/flutter/flutter/issues/76229 -- camera/camera_web - google_maps_flutter/google_maps_flutter - google_maps_flutter/google_maps_flutter_platform_interface - google_maps_flutter/google_maps_flutter_web From adafd7b1a362d94e81e04e1472baecab22c35cbf Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 15 Feb 2022 21:35:15 -0500 Subject: [PATCH 200/600] Roll Flutter from e9f83cf4f9df to 838634496214 (6 revisions) (#4862) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 7fcaa2f6e72f..7a5763b3ff84 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -e9f83cf4f9dfe233cc58ae9814ad144e9a022c7c +8386344962140577d4a1acfcbde7d24146a25466 From 136f49b3e8ec8f96e222a71f17b32a16e9e3e649 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 15 Feb 2022 22:40:21 -0500 Subject: [PATCH 201/600] Roll Flutter from 838634496214 to 286c9751d372 (4 revisions) (#4864) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 7a5763b3ff84..bc5d568fa433 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -8386344962140577d4a1acfcbde7d24146a25466 +286c9751d372e5f8e0857052378fa5c817321357 From eb330b09828f4fed9d6f96e1454eb2b3864368ea Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 16 Feb 2022 07:00:22 -0800 Subject: [PATCH 202/600] [image_picker] Update app-facing and web analysis options (#4838) --- .../image_picker/image_picker/CHANGELOG.md | 4 + .../image_picker/analysis_options.yaml | 1 - .../image_picker/example/lib/main.dart | 48 ++++----- .../image_picker/example/pubspec.yaml | 3 +- .../image_picker/image_picker/pubspec.yaml | 3 +- .../test/image_picker_deprecated_test.dart | 4 +- .../image_picker/test/image_picker_test.dart | 6 +- .../image_picker_for_web/CHANGELOG.md | 4 + .../analysis_options.yaml | 1 - .../image_picker_for_web_test.dart | 59 ++++++----- .../integration_test/image_resizer_test.dart | 99 ++++++++++--------- .../example/lib/main.dart | 2 +- .../image_picker_for_web/example/pubspec.yaml | 10 +- .../lib/image_picker_for_web.dart | 68 +++++++------ .../lib/src/image_resizer.dart | 37 ++++--- .../lib/src/image_resizer_utils.dart | 11 ++- .../image_picker_for_web/pubspec.yaml | 3 +- .../test/image_resizer_utils_test.dart | 54 +++++----- script/configs/custom_analysis.yaml | 2 - 19 files changed, 223 insertions(+), 196 deletions(-) delete mode 100644 packages/image_picker/image_picker/analysis_options.yaml delete mode 100644 packages/image_picker/image_picker_for_web/analysis_options.yaml diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index 992c8b299345..382798f53f90 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.4+9 + +* Internal code cleanup for stricter analysis options. + ## 0.8.4+8 * Configures the `UIImagePicker` to default to gallery instead of camera when diff --git a/packages/image_picker/image_picker/analysis_options.yaml b/packages/image_picker/image_picker/analysis_options.yaml deleted file mode 100644 index 5aeb4e7c5e21..000000000000 --- a/packages/image_picker/image_picker/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../../analysis_options_legacy.yaml diff --git a/packages/image_picker/image_picker/example/lib/main.dart b/packages/image_picker/image_picker/example/lib/main.dart index 0f5ba76db6df..f3ad2375b8f2 100755 --- a/packages/image_picker/image_picker/example/lib/main.dart +++ b/packages/image_picker/image_picker/example/lib/main.dart @@ -19,7 +19,7 @@ void main() { class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - return MaterialApp( + return const MaterialApp( title: 'Image Picker Demo', home: MyHomePage(title: 'Image Picker Example'), ); @@ -27,7 +27,7 @@ class MyApp extends StatelessWidget { } class MyHomePage extends StatefulWidget { - MyHomePage({Key? key, this.title}) : super(key: key); + const MyHomePage({Key? key, this.title}) : super(key: key); final String? title; @@ -39,7 +39,7 @@ class _MyHomePageState extends State { List? _imageFileList; set _imageFile(XFile? value) { - _imageFileList = value == null ? null : [value]; + _imageFileList = value == null ? null : [value]; } dynamic _pickImageError; @@ -69,7 +69,7 @@ class _MyHomePageState extends State { // Mute the video so it auto-plays in web! // This is not needed if the call to .play is the result of user // interaction (clicking on a "play" button, for example). - final double volume = kIsWeb ? 0.0 : 1.0; + const double volume = kIsWeb ? 0.0 : 1.0; await controller.setVolume(volume); await controller.initialize(); await controller.setLooping(true); @@ -78,7 +78,7 @@ class _MyHomePageState extends State { } } - void _onImageButtonPressed(ImageSource source, + Future _onImageButtonPressed(ImageSource source, {BuildContext? context, bool isMultiImage = false}) async { if (_controller != null) { await _controller!.setVolume(0.0); @@ -91,7 +91,7 @@ class _MyHomePageState extends State { await _displayPickImageDialog(context!, (double? maxWidth, double? maxHeight, int? quality) async { try { - final pickedFileList = await _picker.pickMultiImage( + final List? pickedFileList = await _picker.pickMultiImage( maxWidth: maxWidth, maxHeight: maxHeight, imageQuality: quality, @@ -109,7 +109,7 @@ class _MyHomePageState extends State { await _displayPickImageDialog(context!, (double? maxWidth, double? maxHeight, int? quality) async { try { - final pickedFile = await _picker.pickImage( + final XFile? pickedFile = await _picker.pickImage( source: source, maxWidth: maxWidth, maxHeight: maxHeight, @@ -179,7 +179,7 @@ class _MyHomePageState extends State { return Semantics( child: ListView.builder( key: UniqueKey(), - itemBuilder: (context, index) { + itemBuilder: (BuildContext context, int index) { // Why network for web? // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform return Semantics( @@ -358,28 +358,30 @@ class _MyHomePageState extends State { BuildContext context, OnPickImageCallback onPick) async { return showDialog( context: context, - builder: (context) { + builder: (BuildContext context) { return AlertDialog( - title: Text('Add optional parameters'), + title: const Text('Add optional parameters'), content: Column( children: [ TextField( controller: maxWidthController, - keyboardType: TextInputType.numberWithOptions(decimal: true), - decoration: - InputDecoration(hintText: "Enter maxWidth if desired"), + keyboardType: + const TextInputType.numberWithOptions(decimal: true), + decoration: const InputDecoration( + hintText: 'Enter maxWidth if desired'), ), TextField( controller: maxHeightController, - keyboardType: TextInputType.numberWithOptions(decimal: true), - decoration: - InputDecoration(hintText: "Enter maxHeight if desired"), + keyboardType: + const TextInputType.numberWithOptions(decimal: true), + decoration: const InputDecoration( + hintText: 'Enter maxHeight if desired'), ), TextField( controller: qualityController, keyboardType: TextInputType.number, - decoration: - InputDecoration(hintText: "Enter quality if desired"), + decoration: const InputDecoration( + hintText: 'Enter quality if desired'), ), ], ), @@ -393,13 +395,13 @@ class _MyHomePageState extends State { TextButton( child: const Text('PICK'), onPressed: () { - double? width = maxWidthController.text.isNotEmpty + final double? width = maxWidthController.text.isNotEmpty ? double.parse(maxWidthController.text) : null; - double? height = maxHeightController.text.isNotEmpty + final double? height = maxHeightController.text.isNotEmpty ? double.parse(maxHeightController.text) : null; - int? quality = qualityController.text.isNotEmpty + final int? quality = qualityController.text.isNotEmpty ? int.parse(qualityController.text) : null; onPick(width, height, quality); @@ -411,11 +413,11 @@ class _MyHomePageState extends State { } } -typedef void OnPickImageCallback( +typedef OnPickImageCallback = void Function( double? maxWidth, double? maxHeight, int? quality); class AspectRatioVideo extends StatefulWidget { - AspectRatioVideo(this.controller); + const AspectRatioVideo(this.controller); final VideoPlayerController? controller; diff --git a/packages/image_picker/image_picker/example/pubspec.yaml b/packages/image_picker/image_picker/example/pubspec.yaml index e11da82d5da8..28b37197d8ff 100755 --- a/packages/image_picker/image_picker/example/pubspec.yaml +++ b/packages/image_picker/image_picker/example/pubspec.yaml @@ -7,7 +7,6 @@ environment: flutter: ">=2.5.0" dependencies: - video_player: ^2.1.4 flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.1 @@ -18,6 +17,7 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ + video_player: ^2.1.4 dev_dependencies: espresso: ^0.1.0+2 @@ -25,7 +25,6 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.10.0 flutter: uses-material-design: true diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 43143f1dbd57..1d280f16560b 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.4+8 +version: 0.8.4+9 environment: sdk: ">=2.14.0 <3.0.0" @@ -31,5 +31,4 @@ dev_dependencies: flutter_test: sdk: flutter mockito: ^5.0.0 - pedantic: ^1.10.0 plugin_platform_interface: ^2.0.0 diff --git a/packages/image_picker/image_picker/test/image_picker_deprecated_test.dart b/packages/image_picker/image_picker/test/image_picker_deprecated_test.dart index f295e3d02f66..00049e14f808 100644 --- a/packages/image_picker/image_picker/test/image_picker_deprecated_test.dart +++ b/packages/image_picker/image_picker/test/image_picker_deprecated_test.dart @@ -23,7 +23,7 @@ void main() { final List log = []; - final picker = ImagePicker(); + final ImagePicker picker = ImagePicker(); test('ImagePicker platform instance overrides the actual platform used', () { @@ -359,7 +359,7 @@ void main() { setUp(() { channel.setMockMethodCallHandler((MethodCall methodCall) async { log.add(methodCall); - return []; + return []; }); log.clear(); }); diff --git a/packages/image_picker/image_picker/test/image_picker_test.dart b/packages/image_picker/image_picker/test/image_picker_test.dart index 10bc64082aca..b41fbe3381df 100644 --- a/packages/image_picker/image_picker/test/image_picker_test.dart +++ b/packages/image_picker/image_picker/test/image_picker_test.dart @@ -18,7 +18,7 @@ void main() { final List log = []; - final picker = ImagePicker(); + final ImagePicker picker = ImagePicker(); test('ImagePicker platform instance overrides the actual platform used', () { @@ -321,7 +321,7 @@ void main() { return { 'type': 'image', 'path': '/example/path1', - 'pathList': ['/example/path0', '/example/path1'], + 'pathList': ['/example/path0', '/example/path1'], }; }); @@ -372,7 +372,7 @@ void main() { setUp(() { channel.setMockMethodCallHandler((MethodCall methodCall) async { log.add(methodCall); - return []; + return []; }); log.clear(); }); diff --git a/packages/image_picker/image_picker_for_web/CHANGELOG.md b/packages/image_picker/image_picker_for_web/CHANGELOG.md index 78629a4d252d..dcf353fe19b1 100644 --- a/packages/image_picker/image_picker_for_web/CHANGELOG.md +++ b/packages/image_picker/image_picker_for_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.6 + +* Internal code cleanup for stricter analysis options. + ## 2.1.5 * Removes dependency on `meta`. diff --git a/packages/image_picker/image_picker_for_web/analysis_options.yaml b/packages/image_picker/image_picker_for_web/analysis_options.yaml deleted file mode 100644 index 5aeb4e7c5e21..000000000000 --- a/packages/image_picker/image_picker_for_web/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../../analysis_options_legacy.yaml diff --git a/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart b/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart index c1025a9f07d3..9fe40da2557c 100644 --- a/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart +++ b/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart @@ -11,16 +11,17 @@ import 'package:image_picker_for_web/image_picker_for_web.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; import 'package:integration_test/integration_test.dart'; -final String expectedStringContents = 'Hello, world!'; -final String otherStringContents = 'Hello again, world!'; +const String expectedStringContents = 'Hello, world!'; +const String otherStringContents = 'Hello again, world!'; final Uint8List bytes = utf8.encode(expectedStringContents) as Uint8List; final Uint8List otherBytes = utf8.encode(otherStringContents) as Uint8List; -final Map options = { +final Map options = { 'type': 'text/plain', 'lastModified': DateTime.utc(2017, 12, 13).millisecondsSinceEpoch, }; -final html.File textFile = html.File([bytes], 'hello.txt', options); -final html.File secondTextFile = html.File([otherBytes], 'secondFile.txt'); +final html.File textFile = html.File([bytes], 'hello.txt', options); +final html.File secondTextFile = + html.File([otherBytes], 'secondFile.txt'); void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); @@ -33,16 +34,17 @@ void main() { }); testWidgets('Can select a file (Deprecated)', (WidgetTester tester) async { - final mockInput = html.FileUploadInputElement(); + final html.FileUploadInputElement mockInput = html.FileUploadInputElement(); - final overrides = ImagePickerPluginTestOverrides() - ..createInputElement = ((_, __) => mockInput) - ..getMultipleFilesFromInput = ((_) => [textFile]); + final ImagePickerPluginTestOverrides overrides = + ImagePickerPluginTestOverrides() + ..createInputElement = ((_, __) => mockInput) + ..getMultipleFilesFromInput = ((_) => [textFile]); - final plugin = ImagePickerPlugin(overrides: overrides); + final ImagePickerPlugin plugin = ImagePickerPlugin(overrides: overrides); // Init the pick file dialog... - final file = plugin.pickFile(); + final Future file = plugin.pickFile(); // Mock the browser behavior of selecting a file... mockInput.dispatchEvent(html.Event('change')); @@ -54,16 +56,17 @@ void main() { }); testWidgets('Can select a file', (WidgetTester tester) async { - final mockInput = html.FileUploadInputElement(); + final html.FileUploadInputElement mockInput = html.FileUploadInputElement(); - final overrides = ImagePickerPluginTestOverrides() - ..createInputElement = ((_, __) => mockInput) - ..getMultipleFilesFromInput = ((_) => [textFile]); + final ImagePickerPluginTestOverrides overrides = + ImagePickerPluginTestOverrides() + ..createInputElement = ((_, __) => mockInput) + ..getMultipleFilesFromInput = ((_) => [textFile]); - final plugin = ImagePickerPlugin(overrides: overrides); + final ImagePickerPlugin plugin = ImagePickerPlugin(overrides: overrides); // Init the pick file dialog... - final image = plugin.getImage(source: ImageSource.camera); + final Future image = plugin.getImage(source: ImageSource.camera); // Mock the browser behavior of selecting a file... mockInput.dispatchEvent(html.Event('change')); @@ -85,16 +88,18 @@ void main() { }); testWidgets('Can select multiple files', (WidgetTester tester) async { - final mockInput = html.FileUploadInputElement(); + final html.FileUploadInputElement mockInput = html.FileUploadInputElement(); - final overrides = ImagePickerPluginTestOverrides() - ..createInputElement = ((_, __) => mockInput) - ..getMultipleFilesFromInput = ((_) => [textFile, secondTextFile]); + final ImagePickerPluginTestOverrides overrides = + ImagePickerPluginTestOverrides() + ..createInputElement = ((_, __) => mockInput) + ..getMultipleFilesFromInput = + ((_) => [textFile, secondTextFile]); - final plugin = ImagePickerPlugin(overrides: overrides); + final ImagePickerPlugin plugin = ImagePickerPlugin(overrides: overrides); // Init the pick file dialog... - final files = plugin.getMultiImage(); + final Future> files = plugin.getMultiImage(); // Mock the browser behavior of selecting a file... mockInput.dispatchEvent(html.Event('change')); @@ -135,7 +140,7 @@ void main() { group('createInputElement', () { testWidgets('accept: any, capture: null', (WidgetTester tester) async { - html.Element input = plugin.createInputElement('any', null); + final html.Element input = plugin.createInputElement('any', null); expect(input.attributes, containsPair('accept', 'any')); expect(input.attributes, isNot(contains('capture'))); @@ -143,7 +148,7 @@ void main() { }); testWidgets('accept: any, capture: something', (WidgetTester tester) async { - html.Element input = plugin.createInputElement('any', 'something'); + final html.Element input = plugin.createInputElement('any', 'something'); expect(input.attributes, containsPair('accept', 'any')); expect(input.attributes, containsPair('capture', 'something')); @@ -152,7 +157,7 @@ void main() { testWidgets('accept: any, capture: null, multi: true', (WidgetTester tester) async { - html.Element input = + final html.Element input = plugin.createInputElement('any', null, multiple: true); expect(input.attributes, containsPair('accept', 'any')); @@ -162,7 +167,7 @@ void main() { testWidgets('accept: any, capture: something, multi: true', (WidgetTester tester) async { - html.Element input = + final html.Element input = plugin.createInputElement('any', 'something', multiple: true); expect(input.attributes, containsPair('accept', 'any')); diff --git a/packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart b/packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart index 067c7750eb11..91794a7d5e78 100644 --- a/packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart +++ b/packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart @@ -13,8 +13,8 @@ import 'package:image_picker_platform_interface/image_picker_platform_interface. import 'package:integration_test/integration_test.dart'; //This is a sample 10x10 png image -final String pngFileBase64Contents = - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKAQMAAAC3/F3+AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEXqQzX+/v6lfubTAAAAAWJLR0QB/wIt3gAAAAlwSFlzAAAHEwAABxMBziAPCAAAAAd0SU1FB+UJHgsdDM0ErZoAAAALSURBVAjXY2DABwAAHgABboVHMgAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMS0wOS0zMFQxMToyOToxMi0wNDowMHCDC24AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjEtMDktMzBUMTE6Mjk6MTItMDQ6MDAB3rPSAAAAAElFTkSuQmCC"; +const String pngFileBase64Contents = + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKAQMAAAC3/F3+AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEXqQzX+/v6lfubTAAAAAWJLR0QB/wIt3gAAAAlwSFlzAAAHEwAABxMBziAPCAAAAAd0SU1FB+UJHgsdDM0ErZoAAAALSURBVAjXY2DABwAAHgABboVHMgAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMS0wOS0zMFQxMToyOToxMi0wNDowMHCDC24AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjEtMDktMzBUMTE6Mjk6MTItMDQ6MDAB3rPSAAAAAElFTkSuQmCC'; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); @@ -24,105 +24,114 @@ void main() { late XFile pngFile; setUp(() { imageResizer = ImageResizer(); - final pngHtmlFile = _base64ToFile(pngFileBase64Contents, "pngImage.png"); + final html.File pngHtmlFile = + _base64ToFile(pngFileBase64Contents, 'pngImage.png'); pngFile = XFile(html.Url.createObjectUrl(pngHtmlFile), name: pngHtmlFile.name, mimeType: pngHtmlFile.type); }); - testWidgets("image is loaded correctly ", (WidgetTester tester) async { - final imageElement = await imageResizer.loadImage(pngFile.path); + testWidgets('image is loaded correctly ', (WidgetTester tester) async { + final html.ImageElement imageElement = + await imageResizer.loadImage(pngFile.path); expect(imageElement.width!, 10); expect(imageElement.height!, 10); }); testWidgets( "canvas is loaded with image's width and height when max width and max height are null", - (widgetTester) async { - final imageElement = await imageResizer.loadImage(pngFile.path); - final canvas = imageResizer.resizeImageElement(imageElement, null, null); + (WidgetTester widgetTester) async { + final html.ImageElement imageElement = + await imageResizer.loadImage(pngFile.path); + final html.CanvasElement canvas = + imageResizer.resizeImageElement(imageElement, null, null); expect(canvas.width, imageElement.width); expect(canvas.height, imageElement.height); }); testWidgets( - "canvas size is scaled when max width and max height are not null", - (widgetTester) async { - final imageElement = await imageResizer.loadImage(pngFile.path); - final canvas = imageResizer.resizeImageElement(imageElement, 8, 8); + 'canvas size is scaled when max width and max height are not null', + (WidgetTester widgetTester) async { + final html.ImageElement imageElement = + await imageResizer.loadImage(pngFile.path); + final html.CanvasElement canvas = + imageResizer.resizeImageElement(imageElement, 8, 8); expect(canvas.width, 8); expect(canvas.height, 8); }); - testWidgets("resized image is returned after converting canvas to file", - (widgetTester) async { - final imageElement = await imageResizer.loadImage(pngFile.path); - final canvas = imageResizer.resizeImageElement(imageElement, null, null); - final resizedImage = + testWidgets('resized image is returned after converting canvas to file', + (WidgetTester widgetTester) async { + final html.ImageElement imageElement = + await imageResizer.loadImage(pngFile.path); + final html.CanvasElement canvas = + imageResizer.resizeImageElement(imageElement, null, null); + final XFile resizedImage = await imageResizer.writeCanvasToFile(pngFile, canvas, null); - expect(resizedImage.name, "scaled_${pngFile.name}"); + expect(resizedImage.name, 'scaled_${pngFile.name}'); }); - testWidgets("image is scaled when maxWidth is set", + testWidgets('image is scaled when maxWidth is set', (WidgetTester tester) async { - final scaledImage = + final XFile scaledImage = await imageResizer.resizeImageIfNeeded(pngFile, 5, null, null); - expect(scaledImage.name, "scaled_${pngFile.name}"); - final scaledImageSize = await _getImageSize(scaledImage); - expect(scaledImageSize, Size(5, 5)); + expect(scaledImage.name, 'scaled_${pngFile.name}'); + final Size scaledImageSize = await _getImageSize(scaledImage); + expect(scaledImageSize, const Size(5, 5)); }); - testWidgets("image is scaled when maxHeight is set", + testWidgets('image is scaled when maxHeight is set', (WidgetTester tester) async { - final scaledImage = + final XFile scaledImage = await imageResizer.resizeImageIfNeeded(pngFile, null, 6, null); - expect(scaledImage.name, "scaled_${pngFile.name}"); - final scaledImageSize = await _getImageSize(scaledImage); - expect(scaledImageSize, Size(6, 6)); + expect(scaledImage.name, 'scaled_${pngFile.name}'); + final Size scaledImageSize = await _getImageSize(scaledImage); + expect(scaledImageSize, const Size(6, 6)); }); - testWidgets("image is scaled when imageQuality is set", + testWidgets('image is scaled when imageQuality is set', (WidgetTester tester) async { - final scaledImage = + final XFile scaledImage = await imageResizer.resizeImageIfNeeded(pngFile, null, null, 89); - expect(scaledImage.name, "scaled_${pngFile.name}"); + expect(scaledImage.name, 'scaled_${pngFile.name}'); }); - testWidgets("image is scaled when maxWidth,maxHeight,imageQuality are set", + testWidgets('image is scaled when maxWidth,maxHeight,imageQuality are set', (WidgetTester tester) async { - final scaledImage = + final XFile scaledImage = await imageResizer.resizeImageIfNeeded(pngFile, 3, 4, 89); - expect(scaledImage.name, "scaled_${pngFile.name}"); + expect(scaledImage.name, 'scaled_${pngFile.name}'); }); - testWidgets("image is not scaled when maxWidth,maxHeight, is set", + testWidgets('image is not scaled when maxWidth,maxHeight, is set', (WidgetTester tester) async { - final scaledImage = + final XFile scaledImage = await imageResizer.resizeImageIfNeeded(pngFile, null, null, null); expect(scaledImage.name, pngFile.name); }); } Future _getImageSize(XFile file) async { - final completer = Completer(); - final image = html.ImageElement(src: file.path); - image.onLoad.listen((event) { + final Completer completer = Completer(); + final html.ImageElement image = html.ImageElement(src: file.path); + image.onLoad.listen((html.Event event) { completer.complete(Size(image.width!.toDouble(), image.height!.toDouble())); }); - image.onError.listen((event) { - completer.complete(Size(0, 0)); + image.onError.listen((html.Event event) { + completer.complete(const Size(0, 0)); }); return completer.future; } html.File _base64ToFile(String data, String fileName) { - var arr = data.split(','); - var bstr = html.window.atob(arr[1]); - var n = bstr.length, u8arr = Uint8List(n); + final List arr = data.split(','); + final String bstr = html.window.atob(arr[1]); + int n = bstr.length; + final Uint8List u8arr = Uint8List(n); while (n >= 1) { u8arr[n - 1] = bstr.codeUnitAt(n - 1); n--; } - return html.File([u8arr], fileName); + return html.File([u8arr], fileName); } diff --git a/packages/image_picker/image_picker_for_web/example/lib/main.dart b/packages/image_picker/image_picker_for_web/example/lib/main.dart index e1a38dcdcd46..341913a18490 100644 --- a/packages/image_picker/image_picker_for_web/example/lib/main.dart +++ b/packages/image_picker/image_picker_for_web/example/lib/main.dart @@ -17,7 +17,7 @@ class MyApp extends StatefulWidget { class _MyAppState extends State { @override Widget build(BuildContext context) { - return Directionality( + return const Directionality( textDirection: TextDirection.ltr, child: Text('Testing... Look at the console output for results!'), ); diff --git a/packages/image_picker/image_picker_for_web/example/pubspec.yaml b/packages/image_picker/image_picker_for_web/example/pubspec.yaml index 306a857731e8..a9d6c7b9b5bd 100644 --- a/packages/image_picker/image_picker_for_web/example/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/example/pubspec.yaml @@ -6,16 +6,16 @@ environment: flutter: ">=2.2.0" dependencies: - image_picker_for_web: - path: ../ flutter: sdk: flutter + image_picker_for_web: + path: ../ dev_dependencies: - js: ^0.6.3 - flutter_test: - sdk: flutter flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter + js: ^0.6.3 diff --git a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart index 132087576c1d..88d439c5487f 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart @@ -10,22 +10,14 @@ import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:image_picker_for_web/src/image_resizer.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; -final String _kImagePickerInputsDomId = '__image_picker_web-file-input'; -final String _kAcceptImageMimeType = 'image/*'; -final String _kAcceptVideoMimeType = 'video/3gpp,video/x-m4v,video/mp4,video/*'; +const String _kImagePickerInputsDomId = '__image_picker_web-file-input'; +const String _kAcceptImageMimeType = 'image/*'; +const String _kAcceptVideoMimeType = 'video/3gpp,video/x-m4v,video/mp4,video/*'; /// The web implementation of [ImagePickerPlatform]. /// /// This class implements the `package:image_picker` functionality for the web. class ImagePickerPlugin extends ImagePickerPlatform { - final ImagePickerPluginTestOverrides? _overrides; - - bool get _hasOverrides => _overrides != null; - - late html.Element _target; - - late ImageResizer _imageResizer; - /// A constructor that allows tests to override the function that creates file inputs. ImagePickerPlugin({ @visibleForTesting ImagePickerPluginTestOverrides? overrides, @@ -35,6 +27,14 @@ class ImagePickerPlugin extends ImagePickerPlatform { _target = _ensureInitialized(_kImagePickerInputsDomId); } + final ImagePickerPluginTestOverrides? _overrides; + + bool get _hasOverrides => _overrides != null; + + late html.Element _target; + + late ImageResizer _imageResizer; + /// Registers this class as the default instance of [ImagePickerPlatform]. static void registerWith(Registrar registrar) { ImagePickerPlatform.instance = ImagePickerPlugin(); @@ -60,7 +60,8 @@ class ImagePickerPlugin extends ImagePickerPlatform { int? imageQuality, CameraDevice preferredCameraDevice = CameraDevice.rear, }) { - String? capture = computeCaptureAttribute(source, preferredCameraDevice); + final String? capture = + computeCaptureAttribute(source, preferredCameraDevice); return pickFile(accept: _kAcceptImageMimeType, capture: capture); } @@ -82,7 +83,8 @@ class ImagePickerPlugin extends ImagePickerPlatform { CameraDevice preferredCameraDevice = CameraDevice.rear, Duration? maxDuration, }) { - String? capture = computeCaptureAttribute(source, preferredCameraDevice); + final String? capture = + computeCaptureAttribute(source, preferredCameraDevice); return pickFile(accept: _kAcceptVideoMimeType, capture: capture); } @@ -96,7 +98,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { String? accept, String? capture, }) { - html.FileUploadInputElement input = + final html.FileUploadInputElement input = createInputElement(accept, capture) as html.FileUploadInputElement; _injectAndActivate(input); return _getSelectedFile(input); @@ -122,8 +124,9 @@ class ImagePickerPlugin extends ImagePickerPlatform { int? imageQuality, CameraDevice preferredCameraDevice = CameraDevice.rear, }) async { - String? capture = computeCaptureAttribute(source, preferredCameraDevice); - List files = await getFiles( + final String? capture = + computeCaptureAttribute(source, preferredCameraDevice); + final List files = await getFiles( accept: _kAcceptImageMimeType, capture: capture, ); @@ -153,8 +156,9 @@ class ImagePickerPlugin extends ImagePickerPlatform { CameraDevice preferredCameraDevice = CameraDevice.rear, Duration? maxDuration, }) async { - String? capture = computeCaptureAttribute(source, preferredCameraDevice); - List files = await getFiles( + final String? capture = + computeCaptureAttribute(source, preferredCameraDevice); + final List files = await getFiles( accept: _kAcceptVideoMimeType, capture: capture, ); @@ -173,7 +177,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { multiple: true, ); final Iterable> resized = images.map( - (image) => _imageResizer.resizeImageIfNeeded( + (XFile image) => _imageResizer.resizeImageIfNeeded( image, maxWidth, maxHeight, @@ -199,7 +203,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { String? capture, bool multiple = false, }) { - html.FileUploadInputElement input = createInputElement( + final html.FileUploadInputElement input = createInputElement( accept, capture, multiple: multiple, @@ -232,24 +236,24 @@ class ImagePickerPlugin extends ImagePickerPlatform { /// Handles the OnChange event from a FileUploadInputElement object /// Returns a list of selected files. List? _handleOnChangeEvent(html.Event event) { - final html.FileUploadInputElement input = - event.target as html.FileUploadInputElement; - return _getFilesFromInput(input); + final html.FileUploadInputElement? input = + event.target as html.FileUploadInputElement?; + return input == null ? null : _getFilesFromInput(input); } /// Monitors an and returns the selected file. Future _getSelectedFile(html.FileUploadInputElement input) { final Completer _completer = Completer(); // Observe the input until we can return something - input.onChange.first.then((event) { - final files = _handleOnChangeEvent(event); + input.onChange.first.then((html.Event event) { + final List? files = _handleOnChangeEvent(event); if (!_completer.isCompleted && files != null) { _completer.complete(PickedFile( html.Url.createObjectUrl(files.first), )); } }); - input.onError.first.then((event) { + input.onError.first.then((html.Event event) { if (!_completer.isCompleted) { _completer.completeError(event); } @@ -264,10 +268,10 @@ class ImagePickerPlugin extends ImagePickerPlatform { Future> _getSelectedXFiles(html.FileUploadInputElement input) { final Completer> _completer = Completer>(); // Observe the input until we can return something - input.onChange.first.then((event) { - final files = _handleOnChangeEvent(event); + input.onChange.first.then((html.Event event) { + final List? files = _handleOnChangeEvent(event); if (!_completer.isCompleted && files != null) { - _completer.complete(files.map((file) { + _completer.complete(files.map((html.File file) { return XFile( html.Url.createObjectUrl(file), name: file.name, @@ -280,7 +284,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { }).toList()); } }); - input.onError.first.then((event) { + input.onError.first.then((html.Event event) { if (!_completer.isCompleted) { _completer.completeError(event); } @@ -293,7 +297,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { /// Initializes a DOM container where we can host input elements. html.Element _ensureInitialized(String id) { - var target = html.querySelector('#${id}'); + html.Element? target = html.querySelector('#$id'); if (target == null) { final html.Element targetElement = html.Element.tag('flt-image-picker-inputs')..id = id; @@ -316,7 +320,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { return _overrides!.createInputElement(accept, capture); } - html.Element element = html.FileUploadInputElement() + final html.Element element = html.FileUploadInputElement() ..accept = accept ..multiple = multiple; diff --git a/packages/image_picker/image_picker_for_web/lib/src/image_resizer.dart b/packages/image_picker/image_picker_for_web/lib/src/image_resizer.dart index 6ee7c5f015e2..e063099e3319 100644 --- a/packages/image_picker/image_picker_for_web/lib/src/image_resizer.dart +++ b/packages/image_picker/image_picker_for_web/lib/src/image_resizer.dart @@ -3,11 +3,12 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:html' as html; import 'dart:math'; import 'dart:ui'; + import 'package:image_picker_for_web/src/image_resizer_utils.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; -import 'dart:html' as html; /// Helper class that resizes images. class ImageResizer { @@ -16,14 +17,16 @@ class ImageResizer { Future resizeImageIfNeeded(XFile file, double? maxWidth, double? maxHeight, int? imageQuality) async { if (!imageResizeNeeded(maxWidth, maxHeight, imageQuality) || - file.mimeType == "image/gif") { + file.mimeType == 'image/gif') { // Implement maxWidth and maxHeight for image/gif return file; } try { - final imageElement = await loadImage(file.path); - final canvas = resizeImageElement(imageElement, maxWidth, maxHeight); - final resizedImage = await writeCanvasToFile(file, canvas, imageQuality); + final html.ImageElement imageElement = await loadImage(file.path); + final html.CanvasElement canvas = + resizeImageElement(imageElement, maxWidth, maxHeight); + final XFile resizedImage = + await writeCanvasToFile(file, canvas, imageQuality); html.Url.revokeObjectUrl(file.path); return resizedImage; } catch (e) { @@ -33,15 +36,16 @@ class ImageResizer { /// function that loads the blobUrl into an imageElement Future loadImage(String blobUrl) { - final imageLoadCompleter = Completer(); - final imageElement = html.ImageElement(); + final Completer imageLoadCompleter = + Completer(); + final html.ImageElement imageElement = html.ImageElement(); imageElement.src = blobUrl; - imageElement.onLoad.listen((event) { + imageElement.onLoad.listen((html.Event event) { imageLoadCompleter.complete(imageElement); }); - imageElement.onError.listen((event) { - final exception = ("Error while loading image."); + imageElement.onError.listen((html.Event event) { + const String exception = 'Error while loading image.'; imageElement.remove(); imageLoadCompleter.completeError(exception); }); @@ -51,14 +55,14 @@ class ImageResizer { /// Draws image to a canvas while resizing the image to fit the [maxWidth],[maxHeight] constraints html.CanvasElement resizeImageElement( html.ImageElement source, double? maxWidth, double? maxHeight) { - final newImageSize = calculateSizeOfDownScaledImage( + final Size newImageSize = calculateSizeOfDownScaledImage( Size(source.width!.toDouble(), source.height!.toDouble()), maxWidth, maxHeight); - final canvas = html.CanvasElement(); + final html.CanvasElement canvas = html.CanvasElement(); canvas.width = newImageSize.width.toInt(); canvas.height = newImageSize.height.toInt(); - final context = canvas.context2D; + final html.CanvasRenderingContext2D context = canvas.context2D; if (maxHeight == null && maxWidth == null) { context.drawImage(source, 0, 0); } else { @@ -71,12 +75,13 @@ class ImageResizer { /// [imageQuality] is only supported for jpeg and webp images. Future writeCanvasToFile( XFile originalFile, html.CanvasElement canvas, int? imageQuality) async { - final calculatedImageQuality = ((min(imageQuality ?? 100, 100)) / 100.0); - final blob = + final double calculatedImageQuality = + (min(imageQuality ?? 100, 100)) / 100.0; + final html.Blob blob = await canvas.toBlob(originalFile.mimeType, calculatedImageQuality); return XFile(html.Url.createObjectUrlFromBlob(blob), mimeType: originalFile.mimeType, - name: "scaled_" + originalFile.name, + name: 'scaled_' + originalFile.name, lastModified: DateTime.now(), length: blob.size); } diff --git a/packages/image_picker/image_picker_for_web/lib/src/image_resizer_utils.dart b/packages/image_picker/image_picker_for_web/lib/src/image_resizer_utils.dart index 6ef789254b3f..e906a88f00fe 100644 --- a/packages/image_picker/image_picker_for_web/lib/src/image_resizer_utils.dart +++ b/packages/image_picker/image_picker_for_web/lib/src/image_resizer_utils.dart @@ -16,7 +16,7 @@ bool imageResizeNeeded(double? maxWidth, double? maxHeight, int? imageQuality) { /// a function that checks if image quality is between 0 to 100 bool isImageQualityValid(int imageQuality) { - return (imageQuality >= 0 && imageQuality <= 100); + return imageQuality >= 0 && imageQuality <= 100; } /// a function that calculates the size of the downScaled image. @@ -26,8 +26,9 @@ bool isImageQualityValid(int imageQuality) { /// maxHeight is the maximum height of the scaled image Size calculateSizeOfDownScaledImage( Size imageSize, double? maxWidth, double? maxHeight) { - double widthFactor = maxWidth != null ? imageSize.width / maxWidth : 1; - double heightFactor = maxHeight != null ? imageSize.height / maxHeight : 1; - double resizeFactor = max(widthFactor, heightFactor); - return (resizeFactor > 1 ? imageSize ~/ resizeFactor : imageSize); + final double widthFactor = maxWidth != null ? imageSize.width / maxWidth : 1; + final double heightFactor = + maxHeight != null ? imageSize.height / maxHeight : 1; + final double resizeFactor = max(widthFactor, heightFactor); + return resizeFactor > 1 ? imageSize ~/ resizeFactor : imageSize; } diff --git a/packages/image_picker/image_picker_for_web/pubspec.yaml b/packages/image_picker/image_picker_for_web/pubspec.yaml index 285656ac9e4f..deccd2b50a1f 100644 --- a/packages/image_picker/image_picker_for_web/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_for_web description: Web platform implementation of image_picker repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_for_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 2.1.5 +version: 2.1.6 environment: sdk: ">=2.12.0 <3.0.0" @@ -22,7 +22,6 @@ dependencies: flutter_web_plugins: sdk: flutter image_picker_platform_interface: ^2.2.0 - pedantic: ^1.10.0 dev_dependencies: flutter_test: diff --git a/packages/image_picker/image_picker_for_web/test/image_resizer_utils_test.dart b/packages/image_picker/image_picker_for_web/test/image_resizer_utils_test.dart index 352d2bea48a5..0bfa81729bf0 100644 --- a/packages/image_picker/image_picker_for_web/test/image_resizer_utils_test.dart +++ b/packages/image_picker/image_picker_for_web/test/image_resizer_utils_test.dart @@ -8,34 +8,34 @@ import 'package:image_picker_for_web/src/image_resizer_utils.dart'; void main() { group('Image Resizer Utils', () { - group("calculateSizeOfScaledImage", () { + group('calculateSizeOfScaledImage', () { test( "scaled image height and width are same if max width and max height are same as image's width and height", () { - expect(calculateSizeOfDownScaledImage(Size(500, 300), 500, 300), - Size(500, 300)); + expect(calculateSizeOfDownScaledImage(const Size(500, 300), 500, 300), + const Size(500, 300)); }); test( - "scaled image height and width are same if max width and max height are null", + 'scaled image height and width are same if max width and max height are null', () { - expect(calculateSizeOfDownScaledImage(Size(500, 300), null, null), - Size(500, 300)); + expect(calculateSizeOfDownScaledImage(const Size(500, 300), null, null), + const Size(500, 300)); }); - test("image size is scaled when maxWidth is set", () { - final imageSize = Size(500, 300); - final maxWidth = 400; - final scaledSize = calculateSizeOfDownScaledImage( + test('image size is scaled when maxWidth is set', () { + const Size imageSize = Size(500, 300); + const int maxWidth = 400; + final Size scaledSize = calculateSizeOfDownScaledImage( Size(imageSize.width, imageSize.height), maxWidth.toDouble(), null); expect(scaledSize.height <= imageSize.height, true); expect(scaledSize.width <= maxWidth, true); }); - test("image size is scaled when maxHeight is set", () { - final imageSize = Size(500, 300); - final maxHeight = 400; - final scaledSize = calculateSizeOfDownScaledImage( + test('image size is scaled when maxHeight is set', () { + const Size imageSize = Size(500, 300); + const int maxHeight = 400; + final Size scaledSize = calculateSizeOfDownScaledImage( Size(imageSize.width, imageSize.height), null, maxHeight.toDouble()); @@ -43,11 +43,11 @@ void main() { expect(scaledSize.width <= imageSize.width, true); }); - test("image size is scaled when both maxWidth and maxHeight is set", () { - final imageSize = Size(1120, 2000); - final maxHeight = 1200; - final maxWidth = 99; - final scaledSize = calculateSizeOfDownScaledImage( + test('image size is scaled when both maxWidth and maxHeight is set', () { + const Size imageSize = Size(1120, 2000); + const int maxHeight = 1200; + const int maxWidth = 99; + final Size scaledSize = calculateSizeOfDownScaledImage( Size(imageSize.width, imageSize.height), maxWidth.toDouble(), maxHeight.toDouble()); @@ -55,34 +55,34 @@ void main() { expect(scaledSize.width <= maxWidth, true); }); }); - group("imageResizeNeeded", () { - test("image needs to be resized when maxWidth is set", () { + group('imageResizeNeeded', () { + test('image needs to be resized when maxWidth is set', () { expect(imageResizeNeeded(50, null, null), true); }); - test("image needs to be resized when maxHeight is set", () { + test('image needs to be resized when maxHeight is set', () { expect(imageResizeNeeded(null, 50, null), true); }); - test("image needs to be resized when imageQuality is set", () { + test('image needs to be resized when imageQuality is set', () { expect(imageResizeNeeded(null, null, 100), true); }); - test("image will not be resized when imageQuality is not valid", () { + test('image will not be resized when imageQuality is not valid', () { expect(imageResizeNeeded(null, null, 101), false); expect(imageResizeNeeded(null, null, -1), false); }); }); - group("isImageQualityValid", () { - test("image quality is valid in 0 to 100", () { + group('isImageQualityValid', () { + test('image quality is valid in 0 to 100', () { expect(isImageQualityValid(50), true); expect(isImageQualityValid(0), true); expect(isImageQualityValid(100), true); }); test( - "image quality is not valid when imageQuality is less than 0 or greater than 100", + 'image quality is not valid when imageQuality is less than 0 or greater than 100', () { expect(isImageQualityValid(-1), false); expect(isImageQualityValid(101), false); diff --git a/script/configs/custom_analysis.yaml b/script/configs/custom_analysis.yaml index d434b5d56662..13a6819acd79 100644 --- a/script/configs/custom_analysis.yaml +++ b/script/configs/custom_analysis.yaml @@ -17,8 +17,6 @@ - google_sign_in/google_sign_in - google_sign_in/google_sign_in_platform_interface - google_sign_in/google_sign_in_web -- image_picker/image_picker -- image_picker/image_picker_for_web - image_picker/image_picker_platform_interface - in_app_purchase/in_app_purchase - in_app_purchase/in_app_purchase_android From 9318efc766bb26d7b71e2704ea18be9a8584c53f Mon Sep 17 00:00:00 2001 From: Alex Furaiev <33546696+furaiev@users.noreply.github.com> Date: Wed, 16 Feb 2022 17:02:43 +0200 Subject: [PATCH 203/600] [local_auth] support localizedFallbackTitle in IOSAuthMessages (#3806) Add support localizedFallbackTitle in IOSAuthMessages. --- packages/local_auth/local_auth/CHANGELOG.md | 4 + .../ios/RunnerTests/FLTLocalAuthPluginTests.m | 83 ++++++++ .../ios/Classes/FLTLocalAuthPlugin.m | 9 +- .../local_auth/lib/auth_strings.dart | 4 + packages/local_auth/local_auth/pubspec.yaml | 2 +- .../local_auth/test/local_auth_test.dart | 177 +++++++++++++----- 6 files changed, 228 insertions(+), 51 deletions(-) diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index d17eb4a97a21..9387540d6795 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.1.11 + +* Adds support `localizedFallbackTitle` in authenticateWithBiometrics on iOS. + ## 1.1.10 * Removes dependency on `meta`. diff --git a/packages/local_auth/local_auth/example/ios/RunnerTests/FLTLocalAuthPluginTests.m b/packages/local_auth/local_auth/example/ios/RunnerTests/FLTLocalAuthPluginTests.m index dc409da9f57c..3572524d8991 100644 --- a/packages/local_auth/local_auth/example/ios/RunnerTests/FLTLocalAuthPluginTests.m +++ b/packages/local_auth/local_auth/example/ios/RunnerTests/FLTLocalAuthPluginTests.m @@ -186,4 +186,87 @@ - (void)testFailedAuthWithoutBiometrics { [self waitForExpectationsWithTimeout:kTimeout handler:nil]; } +- (void)testLocalizedFallbackTitle { + FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc] init]; + id mockAuthContext = OCMClassMock([LAContext class]); + plugin.authContextOverrides = @[ mockAuthContext ]; + + const LAPolicy policy = LAPolicyDeviceOwnerAuthentication; + NSString *reason = @"a reason"; + NSString *localizedFallbackTitle = @"a title"; + OCMStub([mockAuthContext canEvaluatePolicy:policy error:[OCMArg setTo:nil]]).andReturn(YES); + + // evaluatePolicy:localizedReason:reply: calls back on an internal queue, which is not + // guaranteed to be on the main thread. Ensure that's handled correctly by calling back on + // a background thread. + void (^backgroundThreadReplyCaller)(NSInvocation *) = ^(NSInvocation *invocation) { + void (^reply)(BOOL, NSError *); + [invocation getArgument:&reply atIndex:4]; + dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{ + reply(NO, [NSError errorWithDomain:@"error" code:99 userInfo:nil]); + }); + }; + OCMStub([mockAuthContext evaluatePolicy:policy localizedReason:reason reply:[OCMArg any]]) + .andDo(backgroundThreadReplyCaller); + + FlutterMethodCall *call = + [FlutterMethodCall methodCallWithMethodName:@"authenticate" + arguments:@{ + @"biometricOnly" : @(NO), + @"localizedReason" : reason, + @"localizedFallbackTitle" : localizedFallbackTitle, + }]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"]; + [plugin handleMethodCall:call + result:^(id _Nullable result) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertTrue([result isKindOfClass:[NSNumber class]]); + OCMVerify([mockAuthContext setLocalizedFallbackTitle:localizedFallbackTitle]); + XCTAssertFalse([result boolValue]); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kTimeout handler:nil]; +} + +- (void)testSkippedLocalizedFallbackTitle { + FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc] init]; + id mockAuthContext = OCMClassMock([LAContext class]); + plugin.authContextOverrides = @[ mockAuthContext ]; + + const LAPolicy policy = LAPolicyDeviceOwnerAuthentication; + NSString *reason = @"a reason"; + OCMStub([mockAuthContext canEvaluatePolicy:policy error:[OCMArg setTo:nil]]).andReturn(YES); + + // evaluatePolicy:localizedReason:reply: calls back on an internal queue, which is not + // guaranteed to be on the main thread. Ensure that's handled correctly by calling back on + // a background thread. + void (^backgroundThreadReplyCaller)(NSInvocation *) = ^(NSInvocation *invocation) { + void (^reply)(BOOL, NSError *); + [invocation getArgument:&reply atIndex:4]; + dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{ + reply(NO, [NSError errorWithDomain:@"error" code:99 userInfo:nil]); + }); + }; + OCMStub([mockAuthContext evaluatePolicy:policy localizedReason:reason reply:[OCMArg any]]) + .andDo(backgroundThreadReplyCaller); + + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"authenticate" + arguments:@{ + @"biometricOnly" : @(NO), + @"localizedReason" : reason, + }]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"]; + [plugin handleMethodCall:call + result:^(id _Nullable result) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertTrue([result isKindOfClass:[NSNumber class]]); + OCMVerify([mockAuthContext setLocalizedFallbackTitle:nil]); + XCTAssertFalse([result boolValue]); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kTimeout handler:nil]; +} + @end diff --git a/packages/local_auth/local_auth/ios/Classes/FLTLocalAuthPlugin.m b/packages/local_auth/local_auth/ios/Classes/FLTLocalAuthPlugin.m index c2dc9db25fc8..70113efa00a0 100644 --- a/packages/local_auth/local_auth/ios/Classes/FLTLocalAuthPlugin.m +++ b/packages/local_auth/local_auth/ios/Classes/FLTLocalAuthPlugin.m @@ -122,7 +122,9 @@ - (void)authenticateWithBiometrics:(NSDictionary *)arguments NSError *authError = nil; self.lastCallArgs = nil; self.lastResult = nil; - context.localizedFallbackTitle = @""; + context.localizedFallbackTitle = arguments[@"localizedFallbackTitle"] == [NSNull null] + ? nil + : arguments[@"localizedFallbackTitle"]; if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) { @@ -146,7 +148,9 @@ - (void)authenticate:(NSDictionary *)arguments withFlutterResult:(FlutterResult) NSError *authError = nil; _lastCallArgs = nil; _lastResult = nil; - context.localizedFallbackTitle = @""; + context.localizedFallbackTitle = arguments[@"localizedFallbackTitle"] == [NSNull null] + ? nil + : arguments[@"localizedFallbackTitle"]; if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:&authError]) { [context evaluatePolicy:kLAPolicyDeviceOwnerAuthentication @@ -176,6 +180,7 @@ - (void)handleAuthReplyWithSuccess:(BOOL)success case LAErrorPasscodeNotSet: case LAErrorTouchIDNotAvailable: case LAErrorTouchIDNotEnrolled: + case LAErrorUserFallback: case LAErrorTouchIDLockout: [self handleErrors:error flutterArguments:arguments withFlutterResult:result]; return; diff --git a/packages/local_auth/local_auth/lib/auth_strings.dart b/packages/local_auth/local_auth/lib/auth_strings.dart index 537340b79d4e..3e34659b8dad 100644 --- a/packages/local_auth/local_auth/lib/auth_strings.dart +++ b/packages/local_auth/local_auth/lib/auth_strings.dart @@ -68,12 +68,14 @@ class IOSAuthMessages { this.goToSettingsButton, this.goToSettingsDescription, this.cancelButton, + this.localizedFallbackTitle, }); final String? lockOut; final String? goToSettingsButton; final String? goToSettingsDescription; final String? cancelButton; + final String? localizedFallbackTitle; Map get args { return { @@ -82,6 +84,8 @@ class IOSAuthMessages { 'goToSettingDescriptionIOS': goToSettingsDescription ?? iOSGoToSettingsDescription, 'okButton': cancelButton ?? iOSOkButton, + if (localizedFallbackTitle != null) + 'localizedFallbackTitle': localizedFallbackTitle!, }; } } diff --git a/packages/local_auth/local_auth/pubspec.yaml b/packages/local_auth/local_auth/pubspec.yaml index cd9d0d9760f4..78c79f4abce4 100644 --- a/packages/local_auth/local_auth/pubspec.yaml +++ b/packages/local_auth/local_auth/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Android and iOS devices to allow local authentication via fingerprint, touch ID, face ID, passcode, pin, or pattern. repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.1.10 +version: 1.1.11 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth/test/local_auth_test.dart b/packages/local_auth/local_auth/test/local_auth_test.dart index 758b9cec0e97..3de9758f9d0c 100644 --- a/packages/local_auth/local_auth/test/local_auth_test.dart +++ b/packages/local_auth/local_auth/test/local_auth_test.dart @@ -40,14 +40,28 @@ void main() { expect( log, [ - isMethodCall('authenticate', - arguments: { - 'localizedReason': 'Needs secure', - 'useErrorDialogs': true, - 'stickyAuth': false, - 'sensitiveTransaction': true, - 'biometricOnly': true, - }..addAll(const AndroidAuthMessages().args)), + isMethodCall( + 'authenticate', + arguments: { + 'localizedReason': 'Needs secure', + 'useErrorDialogs': true, + 'stickyAuth': false, + 'sensitiveTransaction': true, + 'biometricOnly': true, + 'biometricHint': androidBiometricHint, + 'biometricNotRecognized': androidBiometricNotRecognized, + 'biometricSuccess': androidBiometricSuccess, + 'biometricRequired': androidBiometricRequiredTitle, + 'cancelButton': androidCancelButton, + 'deviceCredentialsRequired': + androidDeviceCredentialsRequiredTitle, + 'deviceCredentialsSetupDescription': + androidDeviceCredentialsSetupDescription, + 'goToSetting': goToSettings, + 'goToSettingDescription': androidGoToSettingsDescription, + 'signInTitle': androidSignInTitle, + }, + ), ], ); }); @@ -61,14 +75,45 @@ void main() { expect( log, [ - isMethodCall('authenticate', - arguments: { - 'localizedReason': 'Needs secure', - 'useErrorDialogs': true, - 'stickyAuth': false, - 'sensitiveTransaction': true, - 'biometricOnly': true, - }..addAll(const IOSAuthMessages().args)), + isMethodCall('authenticate', arguments: { + 'localizedReason': 'Needs secure', + 'useErrorDialogs': true, + 'stickyAuth': false, + 'sensitiveTransaction': true, + 'biometricOnly': true, + 'lockOut': iOSLockOut, + 'goToSetting': goToSettings, + 'goToSettingDescriptionIOS': iOSGoToSettingsDescription, + 'okButton': iOSOkButton, + }), + ], + ); + }); + + test('authenticate with `localizedFallbackTitle` on iOS.', () async { + const IOSAuthMessages iosAuthMessages = + IOSAuthMessages(localizedFallbackTitle: 'Enter PIN'); + setMockPathProviderPlatform(FakePlatform(operatingSystem: 'ios')); + await localAuthentication.authenticate( + localizedReason: 'Needs secure', + biometricOnly: true, + iOSAuthStrings: iosAuthMessages, + ); + expect( + log, + [ + isMethodCall('authenticate', arguments: { + 'localizedReason': 'Needs secure', + 'useErrorDialogs': true, + 'stickyAuth': false, + 'sensitiveTransaction': true, + 'biometricOnly': true, + 'lockOut': iOSLockOut, + 'goToSetting': goToSettings, + 'goToSettingDescriptionIOS': iOSGoToSettingsDescription, + 'okButton': iOSOkButton, + 'localizedFallbackTitle': 'Enter PIN', + }), ], ); }); @@ -95,14 +140,25 @@ void main() { expect( log, [ - isMethodCall('authenticate', - arguments: { - 'localizedReason': 'Insecure', - 'useErrorDialogs': false, - 'stickyAuth': false, - 'sensitiveTransaction': false, - 'biometricOnly': true, - }..addAll(const AndroidAuthMessages().args)), + isMethodCall('authenticate', arguments: { + 'localizedReason': 'Insecure', + 'useErrorDialogs': false, + 'stickyAuth': false, + 'sensitiveTransaction': false, + 'biometricOnly': true, + 'biometricHint': androidBiometricHint, + 'biometricNotRecognized': androidBiometricNotRecognized, + 'biometricSuccess': androidBiometricSuccess, + 'biometricRequired': androidBiometricRequiredTitle, + 'cancelButton': androidCancelButton, + 'deviceCredentialsRequired': + androidDeviceCredentialsRequiredTitle, + 'deviceCredentialsSetupDescription': + androidDeviceCredentialsSetupDescription, + 'goToSetting': goToSettings, + 'goToSettingDescription': androidGoToSettingsDescription, + 'signInTitle': androidSignInTitle, + }), ], ); }); @@ -117,14 +173,25 @@ void main() { expect( log, [ - isMethodCall('authenticate', - arguments: { - 'localizedReason': 'Needs secure', - 'useErrorDialogs': true, - 'stickyAuth': false, - 'sensitiveTransaction': true, - 'biometricOnly': false, - }..addAll(const AndroidAuthMessages().args)), + isMethodCall('authenticate', arguments: { + 'localizedReason': 'Needs secure', + 'useErrorDialogs': true, + 'stickyAuth': false, + 'sensitiveTransaction': true, + 'biometricOnly': false, + 'biometricHint': androidBiometricHint, + 'biometricNotRecognized': androidBiometricNotRecognized, + 'biometricSuccess': androidBiometricSuccess, + 'biometricRequired': androidBiometricRequiredTitle, + 'cancelButton': androidCancelButton, + 'deviceCredentialsRequired': + androidDeviceCredentialsRequiredTitle, + 'deviceCredentialsSetupDescription': + androidDeviceCredentialsSetupDescription, + 'goToSetting': goToSettings, + 'goToSettingDescription': androidGoToSettingsDescription, + 'signInTitle': androidSignInTitle, + }), ], ); }); @@ -137,14 +204,17 @@ void main() { expect( log, [ - isMethodCall('authenticate', - arguments: { - 'localizedReason': 'Needs secure', - 'useErrorDialogs': true, - 'stickyAuth': false, - 'sensitiveTransaction': true, - 'biometricOnly': false, - }..addAll(const IOSAuthMessages().args)), + isMethodCall('authenticate', arguments: { + 'localizedReason': 'Needs secure', + 'useErrorDialogs': true, + 'stickyAuth': false, + 'sensitiveTransaction': true, + 'biometricOnly': false, + 'lockOut': iOSLockOut, + 'goToSetting': goToSettings, + 'goToSettingDescriptionIOS': iOSGoToSettingsDescription, + 'okButton': iOSOkButton, + }), ], ); }); @@ -159,14 +229,25 @@ void main() { expect( log, [ - isMethodCall('authenticate', - arguments: { - 'localizedReason': 'Insecure', - 'useErrorDialogs': false, - 'stickyAuth': false, - 'sensitiveTransaction': false, - 'biometricOnly': false, - }..addAll(const AndroidAuthMessages().args)), + isMethodCall('authenticate', arguments: { + 'localizedReason': 'Insecure', + 'useErrorDialogs': false, + 'stickyAuth': false, + 'sensitiveTransaction': false, + 'biometricOnly': false, + 'biometricHint': androidBiometricHint, + 'biometricNotRecognized': androidBiometricNotRecognized, + 'biometricSuccess': androidBiometricSuccess, + 'biometricRequired': androidBiometricRequiredTitle, + 'cancelButton': androidCancelButton, + 'deviceCredentialsRequired': + androidDeviceCredentialsRequiredTitle, + 'deviceCredentialsSetupDescription': + androidDeviceCredentialsSetupDescription, + 'goToSetting': goToSettings, + 'goToSettingDescription': androidGoToSettingsDescription, + 'signInTitle': androidSignInTitle, + }), ], ); }); From 2c21e7dbcba5be5764a97dbc2d2ad58cccea5e68 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 16 Feb 2022 10:40:22 -0500 Subject: [PATCH 204/600] Roll Flutter from 286c9751d372 to 14a2b1323bf9 (1 revision) (#4865) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index bc5d568fa433..126098df94b1 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -286c9751d372e5f8e0857052378fa5c817321357 +14a2b1323bf96e6bd7333d5d8df3fac624e52448 From 304b83d75177c0d2b615527e6220f91b4d45086c Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 16 Feb 2022 09:15:23 -0800 Subject: [PATCH 205/600] [ci] Re-enable stable webview Android tests (#4867) --- .cirrus.yml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index c4951a6a7750..f2372a91f2f5 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -230,15 +230,7 @@ task: - export CIRRUS_COMMIT_MESSAGE="" - if [[ -n "$GCLOUD_FIREBASE_TESTLAB_KEY" ]]; then - echo $GCLOUD_FIREBASE_TESTLAB_KEY > ${HOME}/gcloud-service-key.json - # TODO(stuartmorgan): Remove this condition once - # https://github.com/flutter/flutter/issues/96661 is fixed and - # cherry picked. Currently webview_flutter tests crash on stable on - # Android so must be skipped. - - if [[ "$CHANNEL" == "stable" ]]; then - - ./script/tool_runner.sh firebase-test-lab --device model=redfin,version=30 --device model=starqlteue,version=26 --exclude=script/configs/exclude_integration_android.yaml,webview_flutter - - else - - ./script/tool_runner.sh firebase-test-lab --device model=redfin,version=30 --device model=starqlteue,version=26 --exclude=script/configs/exclude_integration_android.yaml - - fi + - ./script/tool_runner.sh firebase-test-lab --device model=redfin,version=30 --device model=starqlteue,version=26 --exclude=script/configs/exclude_integration_android.yaml - else - echo "This user does not have permission to run Firebase Test Lab tests." - fi From 0a579fe86cb1061655d694db7f1948424941e3ad Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 16 Feb 2022 09:50:25 -0800 Subject: [PATCH 206/600] [image_picker] Update platform interface analysis options (#4837) --- .../CHANGELOG.md | 4 ++ .../analysis_options.yaml | 1 - .../lib/image_picker_platform_interface.dart | 2 +- .../method_channel_image_picker.dart | 41 +++++++++++-------- .../image_picker_platform.dart | 2 +- .../lib/src/types/picked_file/base.dart | 3 +- .../lib/src/types/picked_file/html.dart | 15 +++---- .../lib/src/types/picked_file/io.dart | 6 +-- .../lib/src/types/types.dart | 4 +- .../pubspec.yaml | 5 +-- .../new_method_channel_image_picker_test.dart | 20 ++++----- .../test/picked_file_html_test.dart | 6 +-- .../test/picked_file_io_test.dart | 6 +-- script/configs/custom_analysis.yaml | 1 - 14 files changed, 63 insertions(+), 53 deletions(-) delete mode 100644 packages/image_picker/image_picker_platform_interface/analysis_options.yaml diff --git a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md index 8f80610f307a..9f6d1749c671 100644 --- a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md +++ b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.4.4 + +* Internal code cleanup for stricter analysis options. + ## 2.4.3 * Removes dependency on `meta`. diff --git a/packages/image_picker/image_picker_platform_interface/analysis_options.yaml b/packages/image_picker/image_picker_platform_interface/analysis_options.yaml deleted file mode 100644 index 5aeb4e7c5e21..000000000000 --- a/packages/image_picker/image_picker_platform_interface/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../../analysis_options_legacy.yaml diff --git a/packages/image_picker/image_picker_platform_interface/lib/image_picker_platform_interface.dart b/packages/image_picker/image_picker_platform_interface/lib/image_picker_platform_interface.dart index 133c05ecfebf..bdc168617567 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/image_picker_platform_interface.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/image_picker_platform_interface.dart @@ -2,6 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +export 'package:cross_file/cross_file.dart'; export 'package:image_picker_platform_interface/src/platform_interface/image_picker_platform.dart'; export 'package:image_picker_platform_interface/src/types/types.dart'; -export 'package:cross_file/cross_file.dart'; diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart index 3731c5626ea2..e1e6082c8047 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart @@ -9,7 +9,7 @@ import 'package:flutter/services.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; -final MethodChannel _channel = MethodChannel('plugins.flutter.io/image_picker'); +const MethodChannel _channel = MethodChannel('plugins.flutter.io/image_picker'); /// An implementation of [ImagePickerPlatform] that uses method channels. class MethodChannelImagePicker extends ImagePickerPlatform { @@ -25,7 +25,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform { int? imageQuality, CameraDevice preferredCameraDevice = CameraDevice.rear, }) async { - String? path = await _getImagePath( + final String? path = await _getImagePath( source: source, maxWidth: maxWidth, maxHeight: maxHeight, @@ -46,9 +46,11 @@ class MethodChannelImagePicker extends ImagePickerPlatform { maxHeight: maxHeight, imageQuality: imageQuality, ); - if (paths == null) return null; + if (paths == null) { + return null; + } - return paths.map((path) => PickedFile(path)).toList(); + return paths.map((dynamic path) => PickedFile(path as String)).toList(); } Future?> _getMultiImagePath({ @@ -151,7 +153,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform { assert(result.containsKey('path') != result.containsKey('errorCode')); - final String? type = result['type']; + final String? type = result['type'] as String?; assert(type == kTypeImage || type == kTypeVideo); RetrieveType? retrieveType; @@ -164,10 +166,11 @@ class MethodChannelImagePicker extends ImagePickerPlatform { PlatformException? exception; if (result.containsKey('errorCode')) { exception = PlatformException( - code: result['errorCode'], message: result['errorMessage']); + code: result['errorCode']! as String, + message: result['errorMessage'] as String?); } - final String? path = result['path']; + final String? path = result['path'] as String?; return LostData( file: path != null ? PickedFile(path) : null, @@ -184,7 +187,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform { int? imageQuality, CameraDevice preferredCameraDevice = CameraDevice.rear, }) async { - String? path = await _getImagePath( + final String? path = await _getImagePath( source: source, maxWidth: maxWidth, maxHeight: maxHeight, @@ -205,9 +208,11 @@ class MethodChannelImagePicker extends ImagePickerPlatform { maxHeight: maxHeight, imageQuality: imageQuality, ); - if (paths == null) return null; + if (paths == null) { + return null; + } - return paths.map((path) => XFile(path)).toList(); + return paths.map((dynamic path) => XFile(path as String)).toList(); } @override @@ -228,7 +233,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform { Future getLostData() async { List? pickedFileList; - Map? result = + final Map? result = await _channel.invokeMapMethod('retrieve'); if (result == null) { @@ -237,7 +242,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform { assert(result.containsKey('path') != result.containsKey('errorCode')); - final String? type = result['type']; + final String? type = result['type'] as String?; assert(type == kTypeImage || type == kTypeVideo); RetrieveType? retrieveType; @@ -250,15 +255,17 @@ class MethodChannelImagePicker extends ImagePickerPlatform { PlatformException? exception; if (result.containsKey('errorCode')) { exception = PlatformException( - code: result['errorCode'], message: result['errorMessage']); + code: result['errorCode']! as String, + message: result['errorMessage'] as String?); } - final String? path = result['path']; + final String? path = result['path'] as String?; - final pathList = result['pathList']; + final List? pathList = + (result['pathList'] as List?)?.cast(); if (pathList != null) { - pickedFileList = []; - for (String path in pathList) { + pickedFileList = []; + for (final String path in pathList) { pickedFileList.add(XFile(path)); } } diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart b/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart index dbc8d89f8ac0..8f02e1683267 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart @@ -5,9 +5,9 @@ import 'dart:async'; import 'package:cross_file/cross_file.dart'; -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'package:image_picker_platform_interface/src/method_channel/method_channel_image_picker.dart'; import 'package:image_picker_platform_interface/src/types/types.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; /// The interface that implementations of image_picker must implement. /// diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart index d63bc6aad993..77bf87ca045d 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart @@ -18,7 +18,8 @@ import 'package:flutter/foundation.dart' show immutable; @immutable abstract class PickedFileBase { /// Construct a PickedFile - PickedFileBase(String path); + // ignore: avoid_unused_constructor_parameters + const PickedFileBase(String path); /// Get the path of the picked file. /// diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart index 24e1931008b6..7d9761a57602 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart @@ -13,20 +13,21 @@ import './base.dart'; /// /// It wraps the bytes of a selected file. class PickedFile extends PickedFileBase { - final String path; - final Uint8List? _initBytes; - /// Construct a PickedFile object from its ObjectUrl. /// /// Optionally, this can be initialized with `bytes` /// so no http requests are performed to retrieve files later. - PickedFile(this.path, {Uint8List? bytes}) + const PickedFile(this.path, {Uint8List? bytes}) : _initBytes = bytes, super(path); + @override + final String path; + final Uint8List? _initBytes; + Future get _bytes async { if (_initBytes != null) { - return Future.value(UnmodifiableUint8ListView(_initBytes!)); + return Future.value(UnmodifiableUint8ListView(_initBytes!)); } return http.readBytes(Uri.parse(path)); } @@ -38,12 +39,12 @@ class PickedFile extends PickedFileBase { @override Future readAsBytes() async { - return Future.value(await _bytes); + return Future.value(await _bytes); } @override Stream openRead([int? start, int? end]) async* { - final bytes = await _bytes; + final Uint8List bytes = await _bytes; yield bytes.sublist(start ?? 0, end ?? bytes.length); } } diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart index 7037b6b7121a..500cc65a0870 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart @@ -10,13 +10,13 @@ import './base.dart'; /// A PickedFile backed by a dart:io File. class PickedFile extends PickedFileBase { - final File _file; - /// Construct a PickedFile object backed by a dart:io File. PickedFile(String path) : _file = File(path), super(path); + final File _file; + @override String get path { return _file.path; @@ -36,6 +36,6 @@ class PickedFile extends PickedFileBase { Stream openRead([int? start, int? end]) { return _file .openRead(start ?? 0, end) - .map((chunk) => Uint8List.fromList(chunk)); + .map((List chunk) => Uint8List.fromList(chunk)); } } diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart index ad7cd3fbcaab..7f2844230287 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart @@ -4,9 +4,9 @@ export 'camera_device.dart'; export 'image_source.dart'; -export 'retrieve_type.dart'; -export 'picked_file/picked_file.dart'; export 'lost_data_response.dart'; +export 'picked_file/picked_file.dart'; +export 'retrieve_type.dart'; /// Denotes that an image is being picked. const String kTypeImage = 'image'; diff --git a/packages/image_picker/image_picker_platform_interface/pubspec.yaml b/packages/image_picker/image_picker_platform_interface/pubspec.yaml index 0dfe2c009d59..54fd17e47260 100644 --- a/packages/image_picker/image_picker_platform_interface/pubspec.yaml +++ b/packages/image_picker/image_picker_platform_interface/pubspec.yaml @@ -4,20 +4,19 @@ repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/i issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.4.3 +version: 2.4.4 environment: sdk: ">=2.12.0 <3.0.0" flutter: ">=2.0.0" dependencies: + cross_file: ^0.3.1+1 flutter: sdk: flutter http: ^0.13.0 plugin_platform_interface: ^2.1.0 - cross_file: ^0.3.1+1 dev_dependencies: flutter_test: sdk: flutter - pedantic: ^1.10.0 diff --git a/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart b/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart index 17caa8456621..79d971f217f0 100644 --- a/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart +++ b/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart @@ -12,7 +12,7 @@ void main() { TestWidgetsFlutterBinding.ensureInitialized(); group('$MethodChannelImagePicker', () { - MethodChannelImagePicker picker = MethodChannelImagePicker(); + final MethodChannelImagePicker picker = MethodChannelImagePicker(); final List log = []; dynamic returnValue = ''; @@ -223,7 +223,7 @@ void main() { group('#pickMultiImage', () { test('calls the method correctly', () async { - returnValue = ['0', '1']; + returnValue = ['0', '1']; await picker.pickMultiImage(); expect( @@ -239,7 +239,7 @@ void main() { }); test('passes the width and height arguments correctly', () async { - returnValue = ['0', '1']; + returnValue = ['0', '1']; await picker.pickMultiImage(); await picker.pickMultiImage( maxWidth: 10.0, @@ -308,7 +308,7 @@ void main() { }); test('does not accept a negative width or height argument', () { - returnValue = ['0', '1']; + returnValue = ['0', '1']; expect( () => picker.pickMultiImage(maxWidth: -1.0), throwsArgumentError, @@ -321,7 +321,7 @@ void main() { }); test('does not accept a invalid imageQuality argument', () { - returnValue = ['0', '1']; + returnValue = ['0', '1']; expect( () => picker.pickMultiImage(imageQuality: -1), throwsArgumentError, @@ -691,7 +691,7 @@ void main() { group('#getMultiImage', () { test('calls the method correctly', () async { - returnValue = ['0', '1']; + returnValue = ['0', '1']; await picker.getMultiImage(); expect( @@ -707,7 +707,7 @@ void main() { }); test('passes the width and height arguments correctly', () async { - returnValue = ['0', '1']; + returnValue = ['0', '1']; await picker.getMultiImage(); await picker.getMultiImage( maxWidth: 10.0, @@ -776,7 +776,7 @@ void main() { }); test('does not accept a negative width or height argument', () { - returnValue = ['0', '1']; + returnValue = ['0', '1']; expect( () => picker.getMultiImage(maxWidth: -1.0), throwsArgumentError, @@ -789,7 +789,7 @@ void main() { }); test('does not accept a invalid imageQuality argument', () { - returnValue = ['0', '1']; + returnValue = ['0', '1']; expect( () => picker.getMultiImage(imageQuality: -1), throwsArgumentError, @@ -934,7 +934,7 @@ void main() { return { 'type': 'image', 'path': '/example/path1', - 'pathList': ['/example/path0', '/example/path1'], + 'pathList': ['/example/path0', '/example/path1'], }; }); final LostDataResponse response = await picker.getLostData(); diff --git a/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart b/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart index 7721f66148e0..17233376114a 100644 --- a/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart +++ b/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart @@ -10,14 +10,14 @@ import 'dart:html' as html; import 'package:flutter_test/flutter_test.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; -final String expectedStringContents = 'Hello, world!'; +const String expectedStringContents = 'Hello, world!'; final List bytes = utf8.encode(expectedStringContents); -final html.File textFile = html.File([bytes], 'hello.txt'); +final html.File textFile = html.File(>[bytes], 'hello.txt'); final String textFileUrl = html.Url.createObjectUrl(textFile); void main() { group('Create with an objectUrl', () { - final pickedFile = PickedFile(textFileUrl); + final PickedFile pickedFile = PickedFile(textFileUrl); test('Can be read as a string', () async { expect(await pickedFile.readAsString(), equals(expectedStringContents)); diff --git a/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart b/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart index d366204c36bf..3201d3adea41 100644 --- a/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart +++ b/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart @@ -11,10 +11,10 @@ import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; -final pathPrefix = +final String pathPrefix = Directory.current.path.endsWith('test') ? './assets/' : './test/assets/'; -final path = pathPrefix + 'hello.txt'; -final String expectedStringContents = 'Hello, world!'; +final String path = pathPrefix + 'hello.txt'; +const String expectedStringContents = 'Hello, world!'; final Uint8List bytes = Uint8List.fromList(utf8.encode(expectedStringContents)); final File textFile = File(path); final String textFilePath = textFile.path; diff --git a/script/configs/custom_analysis.yaml b/script/configs/custom_analysis.yaml index 13a6819acd79..b802db11011c 100644 --- a/script/configs/custom_analysis.yaml +++ b/script/configs/custom_analysis.yaml @@ -17,7 +17,6 @@ - google_sign_in/google_sign_in - google_sign_in/google_sign_in_platform_interface - google_sign_in/google_sign_in_web -- image_picker/image_picker_platform_interface - in_app_purchase/in_app_purchase - in_app_purchase/in_app_purchase_android - in_app_purchase/in_app_purchase_storekit From 956cdcdcb313f4e9a630d7dc9452b29c42bdd3ad Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 16 Feb 2022 13:55:15 -0500 Subject: [PATCH 207/600] Roll Flutter from 14a2b1323bf9 to b62327903d31 (5 revisions) (#4870) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 126098df94b1..8169840cef6f 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -14a2b1323bf96e6bd7333d5d8df3fac624e52448 +b62327903d3190a50abd4b6c3693583f560285f3 From a4081546aebbc1ee3feb8f65eb1ed20c1d373f1a Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 16 Feb 2022 16:50:23 -0500 Subject: [PATCH 208/600] Roll Flutter from b62327903d31 to 919d20511cf6 (2 revisions) (#4871) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 8169840cef6f..e152d93498ce 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b62327903d3190a50abd4b6c3693583f560285f3 +919d20511cf667d1dc0b8b4bac06b202b60af641 From 6f1f8d8017eba88158cabb5c53317603c5f24332 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 16 Feb 2022 14:30:23 -0800 Subject: [PATCH 209/600] [google_sign_in] Update platform interface analysis options (#4872) --- .../CHANGELOG.md | 4 ++++ .../analysis_options.yaml | 1 - .../src/method_channel_google_sign_in.dart | 2 +- .../lib/src/types.dart | 24 +++++++++++++++---- .../lib/src/utils.dart | 18 +++++++------- .../pubspec.yaml | 3 +-- ...oogle_sign_in_platform_interface_test.dart | 2 +- .../method_channel_google_sign_in_test.dart | 18 +++++++------- script/configs/custom_analysis.yaml | 1 - 9 files changed, 46 insertions(+), 27 deletions(-) delete mode 100644 packages/google_sign_in/google_sign_in_platform_interface/analysis_options.yaml diff --git a/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md b/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md index 928186029a48..66fdb3e72a56 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.2 + +* Internal code cleanup for stricter analysis options. + ## 2.1.1 * Removes dependency on `meta`. diff --git a/packages/google_sign_in/google_sign_in_platform_interface/analysis_options.yaml b/packages/google_sign_in/google_sign_in_platform_interface/analysis_options.yaml deleted file mode 100644 index 5aeb4e7c5e21..000000000000 --- a/packages/google_sign_in/google_sign_in_platform_interface/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../../analysis_options_legacy.yaml diff --git a/packages/google_sign_in/google_sign_in_platform_interface/lib/src/method_channel_google_sign_in.dart b/packages/google_sign_in/google_sign_in_platform_interface/lib/src/method_channel_google_sign_in.dart index c569133a1b34..1abda09fa99f 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/lib/src/method_channel_google_sign_in.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/lib/src/method_channel_google_sign_in.dart @@ -55,7 +55,7 @@ class MethodChannelGoogleSignIn extends GoogleSignInPlatform { .invokeMapMethod('getTokens', { 'email': email, 'shouldRecoverAuth': shouldRecoverAuth, - }).then((result) => getTokenDataFromMap(result!)); + }).then((Map? result) => getTokenDataFromMap(result!)); } @override diff --git a/packages/google_sign_in/google_sign_in_platform_interface/lib/src/types.dart b/packages/google_sign_in/google_sign_in_platform_interface/lib/src/types.dart index e30966f87598..bc50a1d2516d 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/lib/src/types.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/lib/src/types.dart @@ -71,13 +71,21 @@ class GoogleSignInUserData { String? serverAuthCode; @override + // TODO(stuartmorgan): Make this class immutable in the next breaking change. + // ignore: avoid_equals_and_hash_code_on_mutable_classes int get hashCode => hashObjects( [displayName, email, id, photoUrl, idToken, serverAuthCode]); @override + // TODO(stuartmorgan): Make this class immutable in the next breaking change. + // ignore: avoid_equals_and_hash_code_on_mutable_classes bool operator ==(dynamic other) { - if (identical(this, other)) return true; - if (other is! GoogleSignInUserData) return false; + if (identical(this, other)) { + return true; + } + if (other is! GoogleSignInUserData) { + return false; + } final GoogleSignInUserData otherUserData = other; return otherUserData.displayName == displayName && otherUserData.email == email && @@ -107,12 +115,20 @@ class GoogleSignInTokenData { String? serverAuthCode; @override + // TODO(stuartmorgan): Make this class immutable in the next breaking change. + // ignore: avoid_equals_and_hash_code_on_mutable_classes int get hashCode => hash3(idToken, accessToken, serverAuthCode); @override + // TODO(stuartmorgan): Make this class immutable in the next breaking change. + // ignore: avoid_equals_and_hash_code_on_mutable_classes bool operator ==(dynamic other) { - if (identical(this, other)) return true; - if (other is! GoogleSignInTokenData) return false; + if (identical(this, other)) { + return true; + } + if (other is! GoogleSignInTokenData) { + return false; + } final GoogleSignInTokenData otherTokenData = other; return otherTokenData.idToken == idToken && otherTokenData.accessToken == accessToken && diff --git a/packages/google_sign_in/google_sign_in_platform_interface/lib/src/utils.dart b/packages/google_sign_in/google_sign_in_platform_interface/lib/src/utils.dart index 0d89835fb498..6f03a6c357fe 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/lib/src/utils.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/lib/src/utils.dart @@ -10,19 +10,19 @@ GoogleSignInUserData? getUserDataFromMap(Map? data) { return null; } return GoogleSignInUserData( - email: data['email']!, - id: data['id']!, - displayName: data['displayName'], - photoUrl: data['photoUrl'], - idToken: data['idToken'], - serverAuthCode: data['serverAuthCode']); + email: data['email']! as String, + id: data['id']! as String, + displayName: data['displayName'] as String?, + photoUrl: data['photoUrl'] as String?, + idToken: data['idToken'] as String?, + serverAuthCode: data['serverAuthCode'] as String?); } /// Converts token data coming from native code into the proper platform interface type. GoogleSignInTokenData getTokenDataFromMap(Map data) { return GoogleSignInTokenData( - idToken: data['idToken'], - accessToken: data['accessToken'], - serverAuthCode: data['serverAuthCode'], + idToken: data['idToken'] as String?, + accessToken: data['accessToken'] as String?, + serverAuthCode: data['serverAuthCode'] as String?, ); } diff --git a/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml b/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml index f5a7eb866ef9..a5bbaedd51e7 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.1.1 +version: 2.1.2 environment: sdk: ">=2.12.0 <3.0.0" @@ -19,4 +19,3 @@ dev_dependencies: flutter_test: sdk: flutter mockito: ^5.0.0 - pedantic: ^1.10.0 diff --git a/packages/google_sign_in/google_sign_in_platform_interface/test/google_sign_in_platform_interface_test.dart b/packages/google_sign_in/google_sign_in_platform_interface/test/google_sign_in_platform_interface_test.dart index a3450b6f3fb9..78e57a3eb2ac 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/test/google_sign_in_platform_interface_test.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/test/google_sign_in_platform_interface_test.dart @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; import 'package:mockito/mockito.dart'; void main() { diff --git a/packages/google_sign_in/google_sign_in_platform_interface/test/method_channel_google_sign_in_test.dart b/packages/google_sign_in/google_sign_in_platform_interface/test/method_channel_google_sign_in_test.dart index fcb443f84293..a1d83c3f05e6 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/test/method_channel_google_sign_in_test.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/test/method_channel_google_sign_in_test.dart @@ -9,10 +9,10 @@ import 'package:google_sign_in_platform_interface/src/types.dart'; import 'package:google_sign_in_platform_interface/src/utils.dart'; const Map kUserData = { - "email": "john.doe@gmail.com", - "id": "8162538176523816253123", - "photoUrl": "https://lh5.googleusercontent.com/photo.jpg", - "displayName": "John Doe", + 'email': 'john.doe@gmail.com', + 'id': '8162538176523816253123', + 'photoUrl': 'https://lh5.googleusercontent.com/photo.jpg', + 'displayName': 'John Doe', 'idToken': '123', 'serverAuthCode': '789', }; @@ -35,7 +35,7 @@ const Map kDefaultResponses = { }; final GoogleSignInUserData? kUser = getUserDataFromMap(kUserData); -final GoogleSignInTokenData? kToken = +final GoogleSignInTokenData kToken = getTokenDataFromMap(kTokenData as Map); void main() { @@ -122,16 +122,18 @@ void main() { 'token': 'abc', }), () { - googleSignIn.requestScopes(['newScope', 'anotherScope']); + googleSignIn.requestScopes(['newScope', 'anotherScope']); }: isMethodCall('requestScopes', arguments: { - 'scopes': ['newScope', 'anotherScope'], + 'scopes': ['newScope', 'anotherScope'], }), googleSignIn.signOut: isMethodCall('signOut', arguments: null), googleSignIn.disconnect: isMethodCall('disconnect', arguments: null), googleSignIn.isSignedIn: isMethodCall('isSignedIn', arguments: null), }; - tests.keys.forEach((Function f) => f()); + for (final Function f in tests.keys) { + f(); + } expect(log, tests.values); }); diff --git a/script/configs/custom_analysis.yaml b/script/configs/custom_analysis.yaml index b802db11011c..0022beee149a 100644 --- a/script/configs/custom_analysis.yaml +++ b/script/configs/custom_analysis.yaml @@ -15,7 +15,6 @@ - google_maps_flutter/google_maps_flutter_platform_interface - google_maps_flutter/google_maps_flutter_web - google_sign_in/google_sign_in -- google_sign_in/google_sign_in_platform_interface - google_sign_in/google_sign_in_web - in_app_purchase/in_app_purchase - in_app_purchase/in_app_purchase_android From 6407c3e16d298eaf34e203c77d9674ab3bc21dc0 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 16 Feb 2022 19:00:21 -0500 Subject: [PATCH 210/600] Roll Flutter from 919d20511cf6 to adafd66d9cc7 (5 revisions) (#4876) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index e152d93498ce..c2fc9dc220d4 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -919d20511cf667d1dc0b8b4bac06b202b60af641 +adafd66d9cc7796214404c8117395e207bf23d01 From 0b969a4282ddc185f972075873764f70bda5a69c Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Wed, 16 Feb 2022 17:10:18 -0800 Subject: [PATCH 211/600] [camera]remove "selfRef" for SavePhotoDelegate and ensure thread safety (#4780) --- packages/camera/camera/CHANGELOG.md | 4 + .../ios/Runner.xcodeproj/project.pbxproj | 14 ++- .../ios/RunnerTests/FLTCamPhotoCaptureTests.m | 114 ++++++++++++++++++ ...QueueTests.m => FLTCamSampleBufferTests.m} | 4 +- .../RunnerTests/FLTSavePhotoDelegateTests.m | 93 +++++++------- .../camera/camera/ios/Classes/CameraPlugin.m | 4 + packages/camera/camera/ios/Classes/FLTCam.m | 36 ++++-- .../camera/camera/ios/Classes/FLTCam_Test.h | 13 ++ .../camera/ios/Classes/FLTSavePhotoDelegate.h | 27 +++-- .../camera/ios/Classes/FLTSavePhotoDelegate.m | 24 ++-- .../ios/Classes/FLTSavePhotoDelegate_Test.h | 5 + .../camera/camera/ios/Classes/QueueHelper.h | 10 ++ .../camera/camera/ios/Classes/QueueHelper.m | 4 + packages/camera/camera/pubspec.yaml | 2 +- 14 files changed, 272 insertions(+), 82 deletions(-) create mode 100644 packages/camera/camera/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m rename packages/camera/camera/example/ios/RunnerTests/{SampleBufferQueueTests.m => FLTCamSampleBufferTests.m} (93%) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index d2b2f907a6fb..0089130b5e90 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.4+13 + +* Updates iOS camera's photo capture delegate reference on a background queue to prevent potential race conditions, and some related internal code cleanup. + ## 0.9.4+12 * Skips unnecessary AppDelegate setup for unit tests on iOS. diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj index ac39de2e3f84..5f788fc9b9f9 100644 --- a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 46; objects = { /* Begin PBXBuildFile section */ @@ -23,11 +23,12 @@ E01EE4A82799F3A5008C1950 /* QueueHelperTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E01EE4A72799F3A5008C1950 /* QueueHelperTests.m */; }; E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */; }; E04F108627A87CA600573D0C /* FLTSavePhotoDelegateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */; }; + E071CF7227B3061B006EF3BA /* FLTCamPhotoCaptureTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */; }; + E071CF7427B31DE4006EF3BA /* FLTCamSampleBufferTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */; }; E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */; }; E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */; }; E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */; }; E0F95E3D27A32AB900699390 /* CameraPropertiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */; }; - E0F95E4427A36B9200699390 /* SampleBufferQueueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0F95E4327A36B9200699390 /* SampleBufferQueueTests.m */; }; E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */; }; F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */ = {isa = PBXBuildFile; fileRef = F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */; }; /* End PBXBuildFile section */ @@ -85,11 +86,12 @@ E01EE4A72799F3A5008C1950 /* QueueHelperTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QueueHelperTests.m; sourceTree = ""; }; E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CameraCaptureSessionQueueRaceConditionTests.m; sourceTree = ""; }; E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTSavePhotoDelegateTests.m; sourceTree = ""; }; + E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTCamPhotoCaptureTests.m; sourceTree = ""; }; + E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTCamSampleBufferTests.m; sourceTree = ""; }; E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeMethodChannelTests.m; sourceTree = ""; }; E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeTextureRegistryTests.m; sourceTree = ""; }; E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeEventChannelTests.m; sourceTree = ""; }; E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPropertiesTests.m; sourceTree = ""; }; - E0F95E4327A36B9200699390 /* SampleBufferQueueTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SampleBufferQueueTests.m; sourceTree = ""; }; E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPreviewPauseTests.m; sourceTree = ""; }; F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockFLTThreadSafeFlutterResult.h; sourceTree = ""; }; F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockFLTThreadSafeFlutterResult.m; sourceTree = ""; }; @@ -126,8 +128,9 @@ E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */, E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */, E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */, - E0F95E4327A36B9200699390 /* SampleBufferQueueTests.m */, E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */, + E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */, + E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */, E01EE4A72799F3A5008C1950 /* QueueHelperTests.m */, E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */, F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */, @@ -396,12 +399,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - E0F95E4427A36B9200699390 /* SampleBufferQueueTests.m in Sources */, 03F6F8B226CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m in Sources */, 033B94BE269C40A200B4DF97 /* CameraMethodChannelTests.m in Sources */, + E071CF7227B3061B006EF3BA /* FLTCamPhotoCaptureTests.m in Sources */, E0F95E3D27A32AB900699390 /* CameraPropertiesTests.m in Sources */, 03BB766B2665316900CE5A93 /* CameraFocusTests.m in Sources */, E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */, + E071CF7427B31DE4006EF3BA /* FLTCamSampleBufferTests.m in Sources */, E04F108627A87CA600573D0C /* FLTSavePhotoDelegateTests.m in Sources */, F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */, 334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */, diff --git a/packages/camera/camera/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m b/packages/camera/camera/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m new file mode 100644 index 000000000000..fdb2abd4933e --- /dev/null +++ b/packages/camera/camera/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m @@ -0,0 +1,114 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import camera; +@import camera.Test; +@import AVFoundation; +@import XCTest; +#import + +@interface FLTCamPhotoCaptureTests : XCTestCase + +@end + +@implementation FLTCamPhotoCaptureTests + +- (void)testCaptureToFile_mustReportErrorToResultIfSavePhotoDelegateCompletionsWithError { + XCTestExpectation *errorExpectation = + [self expectationWithDescription: + @"Must send error to result if save photo delegate completes with error."]; + + dispatch_queue_t captureSessionQueue = dispatch_queue_create("capture_session_queue", NULL); + dispatch_queue_set_specific(captureSessionQueue, FLTCaptureSessionQueueSpecific, + (void *)FLTCaptureSessionQueueSpecific, NULL); + FLTCam *cam = [self createFLTCamWithCaptureSessionQueue:captureSessionQueue]; + AVCapturePhotoSettings *settings = [AVCapturePhotoSettings photoSettings]; + id mockSettings = OCMClassMock([AVCapturePhotoSettings class]); + OCMStub([mockSettings photoSettings]).andReturn(settings); + + NSError *error = [NSError errorWithDomain:@"test" code:0 userInfo:nil]; + id mockResult = OCMClassMock([FLTThreadSafeFlutterResult class]); + OCMStub([mockResult sendError:error]).andDo(^(NSInvocation *invocation) { + [errorExpectation fulfill]; + }); + + id mockOutput = OCMClassMock([AVCapturePhotoOutput class]); + OCMStub([mockOutput capturePhotoWithSettings:OCMOCK_ANY delegate:OCMOCK_ANY]) + .andDo(^(NSInvocation *invocation) { + FLTSavePhotoDelegate *delegate = cam.inProgressSavePhotoDelegates[@(settings.uniqueID)]; + // Completion runs on IO queue. + dispatch_queue_t ioQueue = dispatch_queue_create("io_queue", NULL); + dispatch_async(ioQueue, ^{ + delegate.completionHandler(nil, error); + }); + }); + cam.capturePhotoOutput = mockOutput; + + // `FLTCam::captureToFile` runs on capture session queue. + dispatch_async(captureSessionQueue, ^{ + [cam captureToFile:mockResult]; + }); + + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testCaptureToFile_mustReportPathToResultIfSavePhotoDelegateCompletionsWithPath { + XCTestExpectation *pathExpectation = + [self expectationWithDescription: + @"Must send file path to result if save photo delegate completes with file path."]; + + dispatch_queue_t captureSessionQueue = dispatch_queue_create("capture_session_queue", NULL); + dispatch_queue_set_specific(captureSessionQueue, FLTCaptureSessionQueueSpecific, + (void *)FLTCaptureSessionQueueSpecific, NULL); + FLTCam *cam = [self createFLTCamWithCaptureSessionQueue:captureSessionQueue]; + + AVCapturePhotoSettings *settings = [AVCapturePhotoSettings photoSettings]; + id mockSettings = OCMClassMock([AVCapturePhotoSettings class]); + OCMStub([mockSettings photoSettings]).andReturn(settings); + + NSString *filePath = @"test"; + id mockResult = OCMClassMock([FLTThreadSafeFlutterResult class]); + OCMStub([mockResult sendSuccessWithData:filePath]).andDo(^(NSInvocation *invocation) { + [pathExpectation fulfill]; + }); + + id mockOutput = OCMClassMock([AVCapturePhotoOutput class]); + OCMStub([mockOutput capturePhotoWithSettings:OCMOCK_ANY delegate:OCMOCK_ANY]) + .andDo(^(NSInvocation *invocation) { + FLTSavePhotoDelegate *delegate = cam.inProgressSavePhotoDelegates[@(settings.uniqueID)]; + // Completion runs on IO queue. + dispatch_queue_t ioQueue = dispatch_queue_create("io_queue", NULL); + dispatch_async(ioQueue, ^{ + delegate.completionHandler(filePath, nil); + }); + }); + cam.capturePhotoOutput = mockOutput; + + // `FLTCam::captureToFile` runs on capture session queue. + dispatch_async(captureSessionQueue, ^{ + [cam captureToFile:mockResult]; + }); + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +/// Creates an `FLTCam` that runs its operations on a given capture session queue. +- (FLTCam *)createFLTCamWithCaptureSessionQueue:(dispatch_queue_t)captureSessionQueue { + id inputMock = OCMClassMock([AVCaptureDeviceInput class]); + OCMStub([inputMock deviceInputWithDevice:[OCMArg any] error:[OCMArg setTo:nil]]) + .andReturn(inputMock); + + id sessionMock = OCMClassMock([AVCaptureSession class]); + OCMStub([sessionMock alloc]).andReturn(sessionMock); + OCMStub([sessionMock addInputWithNoConnections:[OCMArg any]]); // no-op + OCMStub([sessionMock canSetSessionPreset:[OCMArg any]]).andReturn(YES); + + return [[FLTCam alloc] initWithCameraName:@"camera" + resolutionPreset:@"medium" + enableAudio:true + orientation:UIDeviceOrientationPortrait + captureSessionQueue:captureSessionQueue + error:nil]; +} + +@end diff --git a/packages/camera/camera/example/ios/RunnerTests/SampleBufferQueueTests.m b/packages/camera/camera/example/ios/RunnerTests/FLTCamSampleBufferTests.m similarity index 93% rename from packages/camera/camera/example/ios/RunnerTests/SampleBufferQueueTests.m rename to packages/camera/camera/example/ios/RunnerTests/FLTCamSampleBufferTests.m index 19cead905f61..ccc8de5b23bd 100644 --- a/packages/camera/camera/example/ios/RunnerTests/SampleBufferQueueTests.m +++ b/packages/camera/camera/example/ios/RunnerTests/FLTCamSampleBufferTests.m @@ -8,11 +8,11 @@ @import XCTest; #import -@interface SampleBufferQueueTests : XCTestCase +@interface FLTCamSampleBufferTests : XCTestCase @end -@implementation SampleBufferQueueTests +@implementation FLTCamSampleBufferTests - (void)testSampleBufferCallbackQueueMustBeCaptureSessionQueue { id inputMock = OCMClassMock([AVCaptureDeviceInput class]); diff --git a/packages/camera/camera/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m b/packages/camera/camera/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m index b6ea84da449c..9e8e2441f0b9 100644 --- a/packages/camera/camera/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m +++ b/packages/camera/camera/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m @@ -14,40 +14,44 @@ @interface FLTSavePhotoDelegateTests : XCTestCase @implementation FLTSavePhotoDelegateTests -- (void)testHandlePhotoCaptureResult_mustSendErrorIfFailedToCapture { - NSError *error = [NSError errorWithDomain:@"test" code:0 userInfo:nil]; - dispatch_queue_t ioQueue = dispatch_queue_create("test", NULL); - id mockResult = OCMClassMock([FLTThreadSafeFlutterResult class]); - FLTSavePhotoDelegate *delegate = [[FLTSavePhotoDelegate alloc] initWithPath:@"test" - result:mockResult - ioQueue:ioQueue]; +- (void)testHandlePhotoCaptureResult_mustCompleteWithErrorIfFailedToCapture { + XCTestExpectation *completionExpectation = + [self expectationWithDescription:@"Must complete with error if failed to capture photo."]; - [delegate handlePhotoCaptureResultWithError:error + NSError *captureError = [NSError errorWithDomain:@"test" code:0 userInfo:nil]; + dispatch_queue_t ioQueue = dispatch_queue_create("test", NULL); + FLTSavePhotoDelegate *delegate = [[FLTSavePhotoDelegate alloc] + initWithPath:@"test" + ioQueue:ioQueue + completionHandler:^(NSString *_Nullable path, NSError *_Nullable error) { + XCTAssertEqualObjects(captureError, error); + XCTAssertNil(path); + [completionExpectation fulfill]; + }]; + + [delegate handlePhotoCaptureResultWithError:captureError photoDataProvider:^NSData * { return nil; }]; - OCMVerify([mockResult sendError:error]); + [self waitForExpectationsWithTimeout:1 handler:nil]; } -- (void)testHandlePhotoCaptureResult_mustSendErrorIfFailedToWrite { - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Must send IOError to the result if failed to write file."]; +- (void)testHandlePhotoCaptureResult_mustCompleteWithErrorIfFailedToWrite { + XCTestExpectation *completionExpectation = + [self expectationWithDescription:@"Must complete with error if failed to write file."]; dispatch_queue_t ioQueue = dispatch_queue_create("test", NULL); - id mockResult = OCMClassMock([FLTThreadSafeFlutterResult class]); NSError *ioError = [NSError errorWithDomain:@"IOError" code:0 userInfo:@{NSLocalizedDescriptionKey : @"Localized IO Error"}]; - - OCMStub([mockResult sendErrorWithCode:@"IOError" - message:@"Unable to write file" - details:ioError.localizedDescription]) - .andDo(^(NSInvocation *invocation) { - [resultExpectation fulfill]; - }); - FLTSavePhotoDelegate *delegate = [[FLTSavePhotoDelegate alloc] initWithPath:@"test" - result:mockResult - ioQueue:ioQueue]; + FLTSavePhotoDelegate *delegate = [[FLTSavePhotoDelegate alloc] + initWithPath:@"test" + ioQueue:ioQueue + completionHandler:^(NSString *_Nullable path, NSError *_Nullable error) { + XCTAssertEqualObjects(ioError, error); + XCTAssertNil(path); + [completionExpectation fulfill]; + }]; // We can't use OCMClassMock for NSData because some XCTest APIs uses NSData (e.g. // `XCTRunnerIDESession::logDebugMessage:`) on a private queue. @@ -63,23 +67,25 @@ - (void)testHandlePhotoCaptureResult_mustSendErrorIfFailedToWrite { [self waitForExpectationsWithTimeout:1 handler:nil]; } -- (void)testHandlePhotoCaptureResult_mustSendSuccessIfSuccessToWrite { - XCTestExpectation *resultExpectation = [self - expectationWithDescription:@"Must send file path to the result if success to write file."]; +- (void)testHandlePhotoCaptureResult_mustCompleteWithFilePathIfSuccessToWrite { + XCTestExpectation *completionExpectation = + [self expectationWithDescription:@"Must complete with file path if success to write file."]; dispatch_queue_t ioQueue = dispatch_queue_create("test", NULL); - id mockResult = OCMClassMock([FLTThreadSafeFlutterResult class]); - FLTSavePhotoDelegate *delegate = [[FLTSavePhotoDelegate alloc] initWithPath:@"test" - result:mockResult - ioQueue:ioQueue]; - OCMStub([mockResult sendSuccessWithData:delegate.path]).andDo(^(NSInvocation *invocation) { - [resultExpectation fulfill]; - }); + NSString *filePath = @"test"; + FLTSavePhotoDelegate *delegate = [[FLTSavePhotoDelegate alloc] + initWithPath:filePath + ioQueue:ioQueue + completionHandler:^(NSString *_Nullable path, NSError *_Nullable error) { + XCTAssertNil(error); + XCTAssertEqualObjects(filePath, path); + [completionExpectation fulfill]; + }]; // We can't use OCMClassMock for NSData because some XCTest APIs uses NSData (e.g. // `XCTRunnerIDESession::logDebugMessage:`) on a private queue. id mockData = OCMPartialMock([NSData data]); - OCMStub([mockData writeToFile:OCMOCK_ANY options:NSDataWritingAtomic error:[OCMArg setTo:nil]]) + OCMStub([mockData writeToFile:filePath options:NSDataWritingAtomic error:[OCMArg setTo:nil]]) .andReturn(YES); [delegate handlePhotoCaptureResultWithError:nil @@ -94,16 +100,12 @@ - (void)testHandlePhotoCaptureResult_bothProvideDataAndSaveFileMustRunOnIOQueue [self expectationWithDescription:@"Data provider must run on io queue."]; XCTestExpectation *writeFileQueueExpectation = [self expectationWithDescription:@"File writing must run on io queue"]; - XCTestExpectation *resultExpectation = [self - expectationWithDescription:@"Must send file path to the result if success to write file."]; + XCTestExpectation *completionExpectation = + [self expectationWithDescription:@"Must complete with file path if success to write file."]; dispatch_queue_t ioQueue = dispatch_queue_create("test", NULL); const char *ioQueueSpecific = "io_queue_specific"; dispatch_queue_set_specific(ioQueue, ioQueueSpecific, (void *)ioQueueSpecific, NULL); - id mockResult = OCMClassMock([FLTThreadSafeFlutterResult class]); - OCMStub([mockResult sendSuccessWithData:OCMOCK_ANY]).andDo(^(NSInvocation *invocation) { - [resultExpectation fulfill]; - }); // We can't use OCMClassMock for NSData because some XCTest APIs uses NSData (e.g. // `XCTRunnerIDESession::logDebugMessage:`) on a private queue. @@ -116,9 +118,14 @@ - (void)testHandlePhotoCaptureResult_bothProvideDataAndSaveFileMustRunOnIOQueue }) .andReturn(YES); - FLTSavePhotoDelegate *delegate = [[FLTSavePhotoDelegate alloc] initWithPath:@"test" - result:mockResult - ioQueue:ioQueue]; + NSString *filePath = @"test"; + FLTSavePhotoDelegate *delegate = [[FLTSavePhotoDelegate alloc] + initWithPath:filePath + ioQueue:ioQueue + completionHandler:^(NSString *_Nullable path, NSError *_Nullable error) { + [completionExpectation fulfill]; + }]; + [delegate handlePhotoCaptureResultWithError:nil photoDataProvider:^NSData * { if (dispatch_get_specific(ioQueueSpecific)) { diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.m b/packages/camera/camera/ios/Classes/CameraPlugin.m index 97e995429e14..fcea190de705 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin.m +++ b/packages/camera/camera/ios/Classes/CameraPlugin.m @@ -13,6 +13,7 @@ #import "FLTThreadSafeFlutterResult.h" #import "FLTThreadSafeMethodChannel.h" #import "FLTThreadSafeTextureRegistry.h" +#import "QueueHelper.h" @interface CameraPlugin () @property(readonly, nonatomic) FLTThreadSafeTextureRegistry *registry; @@ -38,6 +39,9 @@ - (instancetype)initWithRegistry:(NSObject *)registry _registry = [[FLTThreadSafeTextureRegistry alloc] initWithTextureRegistry:registry]; _messenger = messenger; _captureSessionQueue = dispatch_queue_create("io.flutter.camera.captureSessionQueue", NULL); + dispatch_queue_set_specific(_captureSessionQueue, FLTCaptureSessionQueueSpecific, + (void *)FLTCaptureSessionQueueSpecific, NULL); + [self initDeviceEventMethodChannel]; [self startOrientationListener]; return self; diff --git a/packages/camera/camera/ios/Classes/FLTCam.m b/packages/camera/camera/ios/Classes/FLTCam.m index 94f985066675..31a9decd59b9 100644 --- a/packages/camera/camera/ios/Classes/FLTCam.m +++ b/packages/camera/camera/ios/Classes/FLTCam.m @@ -5,6 +5,7 @@ #import "FLTCam.h" #import "FLTCam_Test.h" #import "FLTSavePhotoDelegate.h" +#import "QueueHelper.h" @import CoreMotion; #import @@ -50,7 +51,6 @@ @interface FLTCam () *inProgressSavePhotoDelegates; + @end diff --git a/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.h b/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.h index a773b4613931..40e4562e4483 100644 --- a/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.h +++ b/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.h @@ -4,34 +4,35 @@ @import AVFoundation; @import Foundation; -@import Flutter; #import "FLTThreadSafeFlutterResult.h" NS_ASSUME_NONNULL_BEGIN +/// The completion handler block for save photo operations. +/// Can be called from either main queue or IO queue. +/// If success, `error` will be present and `path` will be nil. Otherewise, `error` will be nil and +/// `path` will be present. +/// @param path the path for successfully saved photo file. +/// @param error photo capture error or IO error. +typedef void (^FLTSavePhotoDelegateCompletionHandler)(NSString *_Nullable path, + NSError *_Nullable error); + /** Delegate object that handles photo capture results. */ @interface FLTSavePhotoDelegate : NSObject -/// The file path for the captured photo. -@property(readonly, nonatomic) NSString *path; -/// The thread safe flutter result wrapper to report the result. -@property(readonly, nonatomic) FLTThreadSafeFlutterResult *result; -/// The queue on which captured photos are wrote to disk. -@property(strong, nonatomic) dispatch_queue_t ioQueue; -/// Used to keep the delegate alive until didFinishProcessingPhotoSampleBuffer. -@property(strong, nonatomic, nullable) FLTSavePhotoDelegate *selfReference; /** * Initialize a photo capture delegate. * @param path the path for captured photo file. - * @param result the thread safe flutter result wrapper to report the result. - * @param ioQueue the queue on which captured photos are wrote to disk. + * @param ioQueue the queue on which captured photos are written to disk. + * @param completionHandler The completion handler block for save photo operations. Can + * be called from either main queue or IO queue. */ - (instancetype)initWithPath:(NSString *)path - result:(FLTThreadSafeFlutterResult *)result - ioQueue:(dispatch_queue_t)ioQueue; + ioQueue:(dispatch_queue_t)ioQueue + completionHandler:(FLTSavePhotoDelegateCompletionHandler)completionHandler; @end NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.m b/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.m index 8dadfec7fecd..ced3cb5e407f 100644 --- a/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.m +++ b/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.m @@ -3,37 +3,41 @@ // found in the LICENSE file. #import "FLTSavePhotoDelegate.h" +#import "FLTSavePhotoDelegate_Test.h" + +@interface FLTSavePhotoDelegate () +/// The file path for the captured photo. +@property(readonly, nonatomic) NSString *path; +/// The queue on which captured photos are written to disk. +@property(readonly, nonatomic) dispatch_queue_t ioQueue; +@end @implementation FLTSavePhotoDelegate - (instancetype)initWithPath:(NSString *)path - result:(FLTThreadSafeFlutterResult *)result - ioQueue:(dispatch_queue_t)ioQueue { + ioQueue:(dispatch_queue_t)ioQueue + completionHandler:(FLTSavePhotoDelegateCompletionHandler)completionHandler { self = [super init]; NSAssert(self, @"super init cannot be nil"); _path = path; - _selfReference = self; - _result = result; _ioQueue = ioQueue; + _completionHandler = completionHandler; return self; } - (void)handlePhotoCaptureResultWithError:(NSError *)error photoDataProvider:(NSData * (^)(void))photoDataProvider { - self.selfReference = nil; if (error) { - [self.result sendError:error]; + self.completionHandler(nil, error); return; } dispatch_async(self.ioQueue, ^{ NSData *data = photoDataProvider(); NSError *ioError; if ([data writeToFile:self.path options:NSDataWritingAtomic error:&ioError]) { - [self.result sendSuccessWithData:self.path]; + self.completionHandler(self.path, nil); } else { - [self.result sendErrorWithCode:@"IOError" - message:@"Unable to write file" - details:ioError.localizedDescription]; + self.completionHandler(nil, ioError); } }); } diff --git a/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate_Test.h b/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate_Test.h index c0b77c7bac83..2d0d4f96be9d 100644 --- a/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate_Test.h +++ b/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate_Test.h @@ -9,6 +9,11 @@ */ @interface FLTSavePhotoDelegate () +/// The completion handler block for capture and save photo operations. +/// Can be called from either main queue or IO queue. +/// Exposed for unit tests to manually trigger the completion. +@property(readonly, nonatomic) FLTSavePhotoDelegateCompletionHandler completionHandler; + /// Handler to write captured photo data into a file. /// @param error the capture error. /// @param photoDataProvider a closure that provides photo data. diff --git a/packages/camera/camera/ios/Classes/QueueHelper.h b/packages/camera/camera/ios/Classes/QueueHelper.h index c2548148c499..dc7373220521 100644 --- a/packages/camera/camera/ios/Classes/QueueHelper.h +++ b/packages/camera/camera/ios/Classes/QueueHelper.h @@ -6,8 +6,18 @@ NS_ASSUME_NONNULL_BEGIN +/// Queue-specific context data to be associated with the capture session queue. +extern const char *FLTCaptureSessionQueueSpecific; + +/// A class that contains dispatch queue related helper functions. @interface QueueHelper : NSObject + +/// Ensures the given block to be run on the main queue. +/// If caller site is already on the main queue, the block will be run synchronously. Otherwise, the +/// block will be dispatched asynchronously to the main queue. +/// @param block the block to be run on the main queue. + (void)ensureToRunOnMainQueue:(void (^)(void))block; + @end NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera/ios/Classes/QueueHelper.m b/packages/camera/camera/ios/Classes/QueueHelper.m index 194dfa9cfb95..2cef7b677bfa 100644 --- a/packages/camera/camera/ios/Classes/QueueHelper.m +++ b/packages/camera/camera/ios/Classes/QueueHelper.m @@ -4,7 +4,10 @@ #import "QueueHelper.h" +const char *FLTCaptureSessionQueueSpecific = "capture_session_queue"; + @implementation QueueHelper + + (void)ensureToRunOnMainQueue:(void (^)(void))block { if (!NSThread.isMainThread) { dispatch_async(dispatch_get_main_queue(), block); @@ -12,4 +15,5 @@ + (void)ensureToRunOnMainQueue:(void (^)(void))block { block(); } } + @end diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 7421a7738ddd..80d0393087d8 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+12 +version: 0.9.4+13 environment: sdk: ">=2.14.0 <3.0.0" From 3fd2e5d0d54218a8d395ed00dd1135c3407d9070 Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Thu, 17 Feb 2022 07:04:14 -0800 Subject: [PATCH 212/600] [shared_preferences] upgraded ios to using pigeon (#4732) --- .../shared_preferences_ios/CHANGELOG.md | 4 + .../ios/Classes/FLTSharedPreferencesPlugin.m | 109 +++++----- .../ios/Classes/messages.g.h | 33 +++ .../ios/Classes/messages.g.m | 178 ++++++++++++++++ .../lib/messages.g.dart | 179 ++++++++++++++++ .../lib/shared_preferences_ios.dart | 67 +++--- .../pigeons/copyright_header.txt | 3 + .../pigeons/messages.dart | 22 ++ .../shared_preferences_ios/pubspec.yaml | 5 +- .../test/messages.g.dart | 145 +++++++++++++ .../test/shared_preferences_ios_test.dart | 194 ++++++++---------- 11 files changed, 745 insertions(+), 194 deletions(-) create mode 100644 packages/shared_preferences/shared_preferences_ios/ios/Classes/messages.g.h create mode 100644 packages/shared_preferences/shared_preferences_ios/ios/Classes/messages.g.m create mode 100644 packages/shared_preferences/shared_preferences_ios/lib/messages.g.dart create mode 100644 packages/shared_preferences/shared_preferences_ios/pigeons/copyright_header.txt create mode 100644 packages/shared_preferences/shared_preferences_ios/pigeons/messages.dart create mode 100644 packages/shared_preferences/shared_preferences_ios/test/messages.g.dart diff --git a/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md b/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md index b2a9f141ae68..a5cc1d34e034 100644 --- a/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.0 + +* Upgrades to using Pigeon. + ## 2.0.10 * Switches to an in-package method channel implementation. diff --git a/packages/shared_preferences/shared_preferences_ios/ios/Classes/FLTSharedPreferencesPlugin.m b/packages/shared_preferences/shared_preferences_ios/ios/Classes/FLTSharedPreferencesPlugin.m index 4d49e3b14619..bb11da2b5406 100644 --- a/packages/shared_preferences/shared_preferences_ios/ios/Classes/FLTSharedPreferencesPlugin.m +++ b/packages/shared_preferences/shared_preferences_ios/ios/Classes/FLTSharedPreferencesPlugin.m @@ -3,68 +3,7 @@ // found in the LICENSE file. #import "FLTSharedPreferencesPlugin.h" - -static NSString *const CHANNEL_NAME = @"plugins.flutter.io/shared_preferences_ios"; - -@implementation FLTSharedPreferencesPlugin - -+ (void)registerWithRegistrar:(NSObject *)registrar { - FlutterMethodChannel *channel = [FlutterMethodChannel methodChannelWithName:CHANNEL_NAME - binaryMessenger:registrar.messenger]; - [channel setMethodCallHandler:^(FlutterMethodCall *call, FlutterResult result) { - NSString *method = [call method]; - NSDictionary *arguments = [call arguments]; - - if ([method isEqualToString:@"getAll"]) { - result(getAllPrefs()); - } else if ([method isEqualToString:@"setBool"]) { - NSString *key = arguments[@"key"]; - NSNumber *value = arguments[@"value"]; - [[NSUserDefaults standardUserDefaults] setBool:value.boolValue forKey:key]; - result(@YES); - } else if ([method isEqualToString:@"setInt"]) { - NSString *key = arguments[@"key"]; - NSNumber *value = arguments[@"value"]; - // int type in Dart can come to native side in a variety of forms - // It is best to store it as is and send it back when needed. - // Platform channel will handle the conversion. - [[NSUserDefaults standardUserDefaults] setValue:value forKey:key]; - result(@YES); - } else if ([method isEqualToString:@"setDouble"]) { - NSString *key = arguments[@"key"]; - NSNumber *value = arguments[@"value"]; - [[NSUserDefaults standardUserDefaults] setDouble:value.doubleValue forKey:key]; - result(@YES); - } else if ([method isEqualToString:@"setString"]) { - NSString *key = arguments[@"key"]; - NSString *value = arguments[@"value"]; - [[NSUserDefaults standardUserDefaults] setValue:value forKey:key]; - result(@YES); - } else if ([method isEqualToString:@"setStringList"]) { - NSString *key = arguments[@"key"]; - NSArray *value = arguments[@"value"]; - [[NSUserDefaults standardUserDefaults] setValue:value forKey:key]; - result(@YES); - } else if ([method isEqualToString:@"commit"]) { - // synchronize is deprecated. - // "this method is unnecessary and shouldn't be used." - result(@YES); - } else if ([method isEqualToString:@"remove"]) { - [[NSUserDefaults standardUserDefaults] removeObjectForKey:arguments[@"key"]]; - result(@YES); - } else if ([method isEqualToString:@"clear"]) { - NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; - for (NSString *key in getAllPrefs()) { - [defaults removeObjectForKey:key]; - } - result(@YES); - } else { - result(FlutterMethodNotImplemented); - } - }]; -} - -#pragma mark - Private +#import "messages.g.h" static NSMutableDictionary *getAllPrefs() { NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier]; @@ -80,4 +19,50 @@ + (void)registerWithRegistrar:(NSObject *)registrar { return filteredPrefs; } +@interface FLTSharedPreferencesPlugin () +@end + +@implementation FLTSharedPreferencesPlugin + ++ (void)registerWithRegistrar:(NSObject *)registrar { + FLTSharedPreferencesPlugin *plugin = [[FLTSharedPreferencesPlugin alloc] init]; + UserDefaultsApiSetup(registrar.messenger, plugin); +} + +// Must not return nil unless "error" is set. +- (nullable NSDictionary *)getAllWithError: + (FlutterError *_Nullable __autoreleasing *_Nonnull)error { + return getAllPrefs(); +} + +- (void)clearWithError:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + for (NSString *key in getAllPrefs()) { + [defaults removeObjectForKey:key]; + } +} + +- (void)removeKey:(nonnull NSString *)key + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + [[NSUserDefaults standardUserDefaults] removeObjectForKey:key]; +} + +- (void)setBoolKey:(nonnull NSString *)key + value:(nonnull NSNumber *)value + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + [[NSUserDefaults standardUserDefaults] setBool:value.boolValue forKey:key]; +} + +- (void)setDoubleKey:(nonnull NSString *)key + value:(nonnull NSNumber *)value + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + [[NSUserDefaults standardUserDefaults] setDouble:value.doubleValue forKey:key]; +} + +- (void)setValueKey:(nonnull NSString *)key + value:(nonnull NSString *)value + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + [[NSUserDefaults standardUserDefaults] setValue:value forKey:key]; +} + @end diff --git a/packages/shared_preferences/shared_preferences_ios/ios/Classes/messages.g.h b/packages/shared_preferences/shared_preferences_ios/ios/Classes/messages.g.h new file mode 100644 index 000000000000..592402344a04 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_ios/ios/Classes/messages.g.h @@ -0,0 +1,33 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v1.0.16), do not edit directly. +// See also: https://pub.dev/packages/pigeon +#import +@protocol FlutterBinaryMessenger; +@protocol FlutterMessageCodec; +@class FlutterError; +@class FlutterStandardTypedData; + +NS_ASSUME_NONNULL_BEGIN + +/// The codec used by UserDefaultsApi. +NSObject *UserDefaultsApiGetCodec(void); + +@protocol UserDefaultsApi +- (void)removeKey:(NSString *)key error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setBoolKey:(NSString *)key + value:(NSNumber *)value + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setDoubleKey:(NSString *)key + value:(NSNumber *)value + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setValueKey:(NSString *)key value:(id)value error:(FlutterError *_Nullable *_Nonnull)error; +- (nullable NSDictionary *)getAllWithError:(FlutterError *_Nullable *_Nonnull)error; +- (void)clearWithError:(FlutterError *_Nullable *_Nonnull)error; +@end + +extern void UserDefaultsApiSetup(id binaryMessenger, + NSObject *_Nullable api); + +NS_ASSUME_NONNULL_END diff --git a/packages/shared_preferences/shared_preferences_ios/ios/Classes/messages.g.m b/packages/shared_preferences/shared_preferences_ios/ios/Classes/messages.g.m new file mode 100644 index 000000000000..ea8c45c7a66c --- /dev/null +++ b/packages/shared_preferences/shared_preferences_ios/ios/Classes/messages.g.m @@ -0,0 +1,178 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v1.0.16), do not edit directly. +// See also: https://pub.dev/packages/pigeon +#import "messages.g.h" +#import + +#if !__has_feature(objc_arc) +#error File requires ARC to be enabled. +#endif + +static NSDictionary *wrapResult(id result, FlutterError *error) { + NSDictionary *errorDict = (NSDictionary *)[NSNull null]; + if (error) { + errorDict = @{ + @"code" : (error.code ? error.code : [NSNull null]), + @"message" : (error.message ? error.message : [NSNull null]), + @"details" : (error.details ? error.details : [NSNull null]), + }; + } + return @{ + @"result" : (result ? result : [NSNull null]), + @"error" : errorDict, + }; +} + +@interface UserDefaultsApiCodecReader : FlutterStandardReader +@end +@implementation UserDefaultsApiCodecReader +@end + +@interface UserDefaultsApiCodecWriter : FlutterStandardWriter +@end +@implementation UserDefaultsApiCodecWriter +@end + +@interface UserDefaultsApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation UserDefaultsApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[UserDefaultsApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[UserDefaultsApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *UserDefaultsApiGetCodec() { + static dispatch_once_t s_pred = 0; + static FlutterStandardMessageCodec *s_sharedObject = nil; + dispatch_once(&s_pred, ^{ + UserDefaultsApiCodecReaderWriter *readerWriter = + [[UserDefaultsApiCodecReaderWriter alloc] init]; + s_sharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return s_sharedObject; +} + +void UserDefaultsApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.UserDefaultsApi.remove" + binaryMessenger:binaryMessenger + codec:UserDefaultsApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(removeKey:error:)], + @"UserDefaultsApi api (%@) doesn't respond to @selector(removeKey:error:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSString *arg_key = args[0]; + FlutterError *error; + [api removeKey:arg_key error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.UserDefaultsApi.setBool" + binaryMessenger:binaryMessenger + codec:UserDefaultsApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(setBoolKey:value:error:)], + @"UserDefaultsApi api (%@) doesn't respond to @selector(setBoolKey:value:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSString *arg_key = args[0]; + NSNumber *arg_value = args[1]; + FlutterError *error; + [api setBoolKey:arg_key value:arg_value error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.UserDefaultsApi.setDouble" + binaryMessenger:binaryMessenger + codec:UserDefaultsApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(setDoubleKey:value:error:)], + @"UserDefaultsApi api (%@) doesn't respond to @selector(setDoubleKey:value:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSString *arg_key = args[0]; + NSNumber *arg_value = args[1]; + FlutterError *error; + [api setDoubleKey:arg_key value:arg_value error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.UserDefaultsApi.setValue" + binaryMessenger:binaryMessenger + codec:UserDefaultsApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(setValueKey:value:error:)], + @"UserDefaultsApi api (%@) doesn't respond to @selector(setValueKey:value:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSString *arg_key = args[0]; + id arg_value = args[1]; + FlutterError *error; + [api setValueKey:arg_key value:arg_value error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.UserDefaultsApi.getAll" + binaryMessenger:binaryMessenger + codec:UserDefaultsApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(getAllWithError:)], + @"UserDefaultsApi api (%@) doesn't respond to @selector(getAllWithError:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + FlutterError *error; + NSDictionary *output = [api getAllWithError:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.UserDefaultsApi.clear" + binaryMessenger:binaryMessenger + codec:UserDefaultsApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(clearWithError:)], + @"UserDefaultsApi api (%@) doesn't respond to @selector(clearWithError:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + FlutterError *error; + [api clearWithError:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} diff --git a/packages/shared_preferences/shared_preferences_ios/lib/messages.g.dart b/packages/shared_preferences/shared_preferences_ios/lib/messages.g.dart new file mode 100644 index 000000000000..0e76291fb655 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_ios/lib/messages.g.dart @@ -0,0 +1,179 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v1.0.16), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name +// @dart = 2.12 +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; + +import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; +import 'package:flutter/services.dart'; + +class _UserDefaultsApiCodec extends StandardMessageCodec { + const _UserDefaultsApiCodec(); +} + +class UserDefaultsApi { + /// Constructor for [UserDefaultsApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + UserDefaultsApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _UserDefaultsApiCodec(); + + Future remove(String arg_key) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UserDefaultsApi.remove', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_key]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setBool(String arg_key, bool arg_value) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UserDefaultsApi.setBool', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_key, arg_value]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setDouble(String arg_key, double arg_value) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UserDefaultsApi.setDouble', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_key, arg_value]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setValue(String arg_key, Object arg_value) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UserDefaultsApi.setValue', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_key, arg_value]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future> getAll() async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UserDefaultsApi.getAll', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send(null) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return (replyMap['result'] as Map?)! + .cast(); + } + } + + Future clear() async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UserDefaultsApi.clear', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send(null) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} diff --git a/packages/shared_preferences/shared_preferences_ios/lib/shared_preferences_ios.dart b/packages/shared_preferences/shared_preferences_ios/lib/shared_preferences_ios.dart index 15f1e2f9d94d..10638840804e 100644 --- a/packages/shared_preferences/shared_preferences_ios/lib/shared_preferences_ios.dart +++ b/packages/shared_preferences/shared_preferences_ios/lib/shared_preferences_ios.dart @@ -2,52 +2,65 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; - import 'package:flutter/services.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'messages.g.dart'; -const MethodChannel _kChannel = - MethodChannel('plugins.flutter.io/shared_preferences_ios'); +typedef _Setter = Future Function(String key, Object value); -/// The macOS implementation of [SharedPreferencesStorePlatform]. -/// -/// This class implements the `package:shared_preferences` functionality for iOS. +/// iOS implementation of shared_preferences. class SharedPreferencesIOS extends SharedPreferencesStorePlatform { - /// Registers this class as the default instance of [SharedPreferencesStorePlatform]. + final UserDefaultsApi _api = UserDefaultsApi(); + late final Map _setters = { + 'Bool': (String key, Object value) { + return _api.setBool(key, value as bool); + }, + 'Double': (String key, Object value) { + return _api.setDouble(key, value as double); + }, + 'Int': (String key, Object value) { + return _api.setValue(key, value as int); + }, + 'String': (String key, Object value) { + return _api.setValue(key, value as String); + }, + 'StringList': (String key, Object value) { + return _api.setValue(key, value as List); + }, + }; + + /// Registers this class as the default instance of [PathProviderPlatform]. static void registerWith() { SharedPreferencesStorePlatform.instance = SharedPreferencesIOS(); } @override - Future remove(String key) async { - return (await _kChannel.invokeMethod( - 'remove', - {'key': key}, - ))!; + Future clear() async { + await _api.clear(); + return true; } @override - Future setValue(String valueType, String key, Object value) async { - return (await _kChannel.invokeMethod( - 'set$valueType', - {'key': key, 'value': value}, - ))!; + Future> getAll() async { + final Map result = await _api.getAll(); + return result.cast(); } @override - Future clear() async { - return (await _kChannel.invokeMethod('clear'))!; + Future remove(String key) async { + await _api.remove(key); + return true; } @override - Future> getAll() async { - final Map? preferences = - await _kChannel.invokeMapMethod('getAll'); - - if (preferences == null) { - return {}; + Future setValue(String valueType, String key, Object value) async { + final _Setter? setter = _setters[valueType]; + if (setter == null) { + throw PlatformException( + code: 'InvalidOperation', + message: '"$valueType" is not a supported type.'); } - return preferences; + await setter(key, value); + return true; } } diff --git a/packages/shared_preferences/shared_preferences_ios/pigeons/copyright_header.txt b/packages/shared_preferences/shared_preferences_ios/pigeons/copyright_header.txt new file mode 100644 index 000000000000..fb682b1ab965 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_ios/pigeons/copyright_header.txt @@ -0,0 +1,3 @@ +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. \ No newline at end of file diff --git a/packages/shared_preferences/shared_preferences_ios/pigeons/messages.dart b/packages/shared_preferences/shared_preferences_ios/pigeons/messages.dart new file mode 100644 index 000000000000..6b5648f9e2f0 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_ios/pigeons/messages.dart @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:pigeon/pigeon.dart'; + +@ConfigurePigeon(PigeonOptions( + dartOut: 'lib/messages.g.dart', + dartTestOut: 'test/messages.g.dart', + objcHeaderOut: 'ios/Classes/messages.g.h', + objcSourceOut: 'ios/Classes/messages.g.m', + copyrightHeader: 'pigeons/copyright_header.txt', +)) +@HostApi(dartHostTestHandler: 'TestUserDefaultsApi') +abstract class UserDefaultsApi { + void remove(String key); + void setBool(String key, bool value); + void setDouble(String key, double value); + void setValue(String key, Object value); + Map getAll(); + void clear(); +} diff --git a/packages/shared_preferences/shared_preferences_ios/pubspec.yaml b/packages/shared_preferences/shared_preferences_ios/pubspec.yaml index 68ab03523841..33bf5baffd18 100644 --- a/packages/shared_preferences/shared_preferences_ios/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_ios description: iOS implementation of the shared_preferences plugin repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.10 +version: 2.1.0 environment: sdk: ">=2.14.0 <3.0.0" @@ -13,8 +13,8 @@ flutter: implements: shared_preferences platforms: ios: - pluginClass: FLTSharedPreferencesPlugin dartPluginClass: SharedPreferencesIOS + pluginClass: FLTSharedPreferencesPlugin dependencies: flutter: @@ -24,3 +24,4 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + pigeon: ^1.0.16 diff --git a/packages/shared_preferences/shared_preferences_ios/test/messages.g.dart b/packages/shared_preferences/shared_preferences_ios/test/messages.g.dart new file mode 100644 index 000000000000..12fbc0635784 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_ios/test/messages.g.dart @@ -0,0 +1,145 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v1.0.16), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import +// @dart = 2.12 +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; +import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:shared_preferences_ios/messages.g.dart'; + +class _TestUserDefaultsApiCodec extends StandardMessageCodec { + const _TestUserDefaultsApiCodec(); +} + +abstract class TestUserDefaultsApi { + static const MessageCodec codec = _TestUserDefaultsApiCodec(); + + void remove(String key); + void setBool(String key, bool value); + void setDouble(String key, double value); + void setValue(String key, Object value); + Map getAll(); + void clear(); + static void setup(TestUserDefaultsApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UserDefaultsApi.remove', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.UserDefaultsApi.remove was null.'); + final List args = (message as List?)!; + final String? arg_key = (args[0] as String?); + assert(arg_key != null, + 'Argument for dev.flutter.pigeon.UserDefaultsApi.remove was null, expected non-null String.'); + api.remove(arg_key!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UserDefaultsApi.setBool', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.UserDefaultsApi.setBool was null.'); + final List args = (message as List?)!; + final String? arg_key = (args[0] as String?); + assert(arg_key != null, + 'Argument for dev.flutter.pigeon.UserDefaultsApi.setBool was null, expected non-null String.'); + final bool? arg_value = (args[1] as bool?); + assert(arg_value != null, + 'Argument for dev.flutter.pigeon.UserDefaultsApi.setBool was null, expected non-null bool.'); + api.setBool(arg_key!, arg_value!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UserDefaultsApi.setDouble', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.UserDefaultsApi.setDouble was null.'); + final List args = (message as List?)!; + final String? arg_key = (args[0] as String?); + assert(arg_key != null, + 'Argument for dev.flutter.pigeon.UserDefaultsApi.setDouble was null, expected non-null String.'); + final double? arg_value = (args[1] as double?); + assert(arg_value != null, + 'Argument for dev.flutter.pigeon.UserDefaultsApi.setDouble was null, expected non-null double.'); + api.setDouble(arg_key!, arg_value!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UserDefaultsApi.setValue', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.UserDefaultsApi.setValue was null.'); + final List args = (message as List?)!; + final String? arg_key = (args[0] as String?); + assert(arg_key != null, + 'Argument for dev.flutter.pigeon.UserDefaultsApi.setValue was null, expected non-null String.'); + final Object? arg_value = (args[1] as Object?); + assert(arg_value != null, + 'Argument for dev.flutter.pigeon.UserDefaultsApi.setValue was null, expected non-null Object.'); + api.setValue(arg_key!, arg_value!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UserDefaultsApi.getAll', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + // ignore message + final Map output = api.getAll(); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UserDefaultsApi.clear', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + // ignore message + api.clear(); + return {}; + }); + } + } + } +} diff --git a/packages/shared_preferences/shared_preferences_ios/test/shared_preferences_ios_test.dart b/packages/shared_preferences/shared_preferences_ios/test/shared_preferences_ios_test.dart index 8eb23f28c424..efafb230d9de 100644 --- a/packages/shared_preferences/shared_preferences_ios/test/shared_preferences_ios_test.dart +++ b/packages/shared_preferences/shared_preferences_ios/test/shared_preferences_ios_test.dart @@ -5,113 +5,101 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:shared_preferences_ios/shared_preferences_ios.dart'; -import 'package:shared_preferences_platform_interface/method_channel_shared_preferences.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'messages.g.dart'; + +class _MockSharedPreferencesApi implements TestUserDefaultsApi { + final Map items = {}; + + @override + Map getAll() { + return items; + } + + @override + void remove(String key) { + items.remove(key); + } + + @override + void setBool(String key, bool value) { + items[key] = value; + } + + @override + void setDouble(String key, double value) { + items[key] = value; + } + + @override + void setValue(String key, Object value) { + items[key] = value; + } + + @override + void clear() { + items.clear(); + } +} + void main() { TestWidgetsFlutterBinding.ensureInitialized(); + _MockSharedPreferencesApi api = _MockSharedPreferencesApi(); + SharedPreferencesIOS plugin = SharedPreferencesIOS(); + + setUp(() { + api = _MockSharedPreferencesApi(); + TestUserDefaultsApi.setup(api); + plugin = SharedPreferencesIOS(); + }); + + test('registerWith', () { + SharedPreferencesIOS.registerWith(); + expect( + SharedPreferencesStorePlatform.instance, isA()); + }); + + test('remove', () async { + api.items['flutter.hi'] = 'world'; + expect(await plugin.remove('flutter.hi'), isTrue); + expect(api.items.containsKey('flutter.hi'), isFalse); + }); + + test('clear', () async { + api.items['flutter.hi'] = 'world'; + expect(await plugin.clear(), isTrue); + expect(api.items.containsKey('flutter.hi'), isFalse); + }); + + test('getAll', () async { + api.items['flutter.hi'] = 'world'; + api.items['flutter.bye'] = 'dust'; + final Map all = await plugin.getAll(); + expect(all.length, 2); + expect(all['flutter.hi'], api.items['flutter.hi']); + expect(all['flutter.bye'], api.items['flutter.bye']); + }); + + test('setValue', () async { + expect(await plugin.setValue('Bool', 'flutter.Bool', true), isTrue); + expect(api.items['flutter.Bool'], true); + expect(await plugin.setValue('Double', 'flutter.Double', 1.5), isTrue); + expect(api.items['flutter.Double'], 1.5); + expect(await plugin.setValue('Int', 'flutter.Int', 12), isTrue); + expect(api.items['flutter.Int'], 12); + expect(await plugin.setValue('String', 'flutter.String', 'hi'), isTrue); + expect(api.items['flutter.String'], 'hi'); + expect( + await plugin + .setValue('StringList', 'flutter.StringList', ['hi']), + isTrue); + expect(api.items['flutter.StringList'], ['hi']); + }); - group(MethodChannelSharedPreferencesStore, () { - const MethodChannel channel = MethodChannel( - 'plugins.flutter.io/shared_preferences_ios', - ); - - const Map kTestValues = { - 'flutter.String': 'hello world', - 'flutter.Bool': true, - 'flutter.Int': 42, - 'flutter.Double': 3.14159, - 'flutter.StringList': ['foo', 'bar'], - }; - // Create a dummy in-memory implementation to back the mocked method channel - // API to simplify validation of the expected calls. - late InMemorySharedPreferencesStore testData; - - final List log = []; - late SharedPreferencesStorePlatform store; - - setUp(() async { - testData = InMemorySharedPreferencesStore.empty(); - - channel.setMockMethodCallHandler((MethodCall methodCall) async { - log.add(methodCall); - if (methodCall.method == 'getAll') { - return await testData.getAll(); - } - if (methodCall.method == 'remove') { - final String key = methodCall.arguments['key'] as String; - return await testData.remove(key); - } - if (methodCall.method == 'clear') { - return await testData.clear(); - } - final RegExp setterRegExp = RegExp(r'set(.*)'); - final Match? match = setterRegExp.matchAsPrefix(methodCall.method); - if (match?.groupCount == 1) { - final String valueType = match!.group(1)!; - final String key = methodCall.arguments['key'] as String; - final Object value = methodCall.arguments['value'] as Object; - return await testData.setValue(valueType, key, value); - } - fail('Unexpected method call: ${methodCall.method}'); - }); - log.clear(); - }); - - test('registered instance', () { - SharedPreferencesIOS.registerWith(); - expect( - SharedPreferencesStorePlatform.instance, isA()); - }); - - test('getAll', () async { - store = SharedPreferencesIOS(); - testData = InMemorySharedPreferencesStore.withData(kTestValues); - expect(await store.getAll(), kTestValues); - expect(log.single.method, 'getAll'); - }); - - test('remove', () async { - store = SharedPreferencesIOS(); - testData = InMemorySharedPreferencesStore.withData(kTestValues); - expect(await store.remove('flutter.String'), true); - expect(await store.remove('flutter.Bool'), true); - expect(await store.remove('flutter.Int'), true); - expect(await store.remove('flutter.Double'), true); - expect(await testData.getAll(), { - 'flutter.StringList': ['foo', 'bar'], - }); - - expect(log, hasLength(4)); - for (final MethodCall call in log) { - expect(call.method, 'remove'); - } - }); - - test('setValue', () async { - store = SharedPreferencesIOS(); - expect(await testData.getAll(), isEmpty); - for (final String key in kTestValues.keys) { - final Object value = kTestValues[key]!; - expect(await store.setValue(key.split('.').last, key, value), true); - } - expect(await testData.getAll(), kTestValues); - - expect(log, hasLength(5)); - expect(log[0].method, 'setString'); - expect(log[1].method, 'setBool'); - expect(log[2].method, 'setInt'); - expect(log[3].method, 'setDouble'); - expect(log[4].method, 'setStringList'); - }); - - test('clear', () async { - store = SharedPreferencesIOS(); - testData = InMemorySharedPreferencesStore.withData(kTestValues); - expect(await testData.getAll(), isNotEmpty); - expect(await store.clear(), true); - expect(await testData.getAll(), isEmpty); - expect(log.single.method, 'clear'); - }); + test('setValue with unsupported type', () { + expect(() async { + await plugin.setValue('Map', 'flutter.key', {}); + }, throwsA(isA())); }); } From ed29396208cd915ab7dec5113e81216422224cc1 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 17 Feb 2022 10:44:23 -0500 Subject: [PATCH 213/600] Roll Flutter from adafd66d9cc7 to 93c0c043cf2c (6 revisions) (#4880) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index c2fc9dc220d4..143dde748c88 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -adafd66d9cc7796214404c8117395e207bf23d01 +93c0c043cf2c38947e0e95c81e936aa46541015c From 4d5db3529481e627c5a391b9285deb4495c4acca Mon Sep 17 00:00:00 2001 From: Casey Hillers Date: Thu, 17 Feb 2022 10:44:24 -0800 Subject: [PATCH 214/600] [ci.yaml] Migrate to Cocoon scheduler (#4884) --- .ci.yaml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.ci.yaml b/.ci.yaml index 061e42092ad8..c00fcf0b7865 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -47,7 +47,6 @@ targets: [ {"dependency": "vs_build"} ] - scheduler: luci - name: Windows win32-platform_tests stable recipe: plugins/plugins @@ -60,7 +59,6 @@ targets: [ {"dependency": "vs_build"} ] - scheduler: luci - name: Windows windows-build_all_plugins master recipe: plugins/plugins @@ -74,7 +72,6 @@ targets: [ {"dependency": "vs_build"} ] - scheduler: luci - name: Windows windows-build_all_plugins stable recipe: plugins/plugins @@ -87,7 +84,6 @@ targets: [ {"dependency": "vs_build"} ] - scheduler: luci - name: Windows uwp-platform_tests master recipe: plugins/plugins @@ -101,7 +97,6 @@ targets: [ {"dependency": "vs_build"} ] - scheduler: luci - name: Windows plugin_tools_tests recipe: plugins/plugins @@ -111,11 +106,9 @@ targets: target_file: plugin_tools_tests.yaml channel: master version_file: flutter_master.version - scheduler: luci - name: Linux ci_yaml plugins roller recipe: infra/ci_yaml timeout: 30 - scheduler: luci runIf: - .ci.yaml From aa7a47415052cba618f221c80caee3934138a753 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 17 Feb 2022 13:24:18 -0800 Subject: [PATCH 215/600] [camera] Restore compatibility with older Flutter (#4885) --- packages/camera/camera/CHANGELOG.md | 4 ++++ packages/camera/camera/lib/src/camera_controller.dart | 8 ++++++-- packages/camera/camera/pubspec.yaml | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 0089130b5e90..9ef626a8ad2b 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.4+14 + +* Restores compatibility with Flutter 2.5 and 2.8. + ## 0.9.4+13 * Updates iOS camera's photo capture delegate reference on a background queue to prevent potential race conditions, and some related internal code cleanup. diff --git a/packages/camera/camera/lib/src/camera_controller.dart b/packages/camera/camera/lib/src/camera_controller.dart index 835ac1680c6e..30e6221697c9 100644 --- a/packages/camera/camera/lib/src/camera_controller.dart +++ b/packages/camera/camera/lib/src/camera_controller.dart @@ -29,6 +29,10 @@ Future> availableCameras() async { return CameraPlatform.instance.availableCameras(); } +// TODO(stuartmorgan): Remove this once the package requires 2.10, where the +// dart:async `unawaited` accepts a nullable future. +void _unawaited(Future? future) {} + /// The state of a [CameraController]. class CameraValue { /// Creates a new camera controller state. @@ -296,7 +300,7 @@ class CameraController extends ValueNotifier { enableAudio: enableAudio, ); - unawaited(CameraPlatform.instance + _unawaited(CameraPlatform.instance .onCameraInitialized(_cameraId) .first .then((CameraInitializedEvent event) { @@ -810,7 +814,7 @@ class CameraController extends ValueNotifier { if (_isDisposed) { return; } - unawaited(_deviceOrientationSubscription?.cancel()); + _unawaited(_deviceOrientationSubscription?.cancel()); _isDisposed = true; super.dispose(); if (_initCalled != null) { diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 80d0393087d8..7bc0e452bb29 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+13 +version: 0.9.4+14 environment: sdk: ">=2.14.0 <3.0.0" From e3ebc88d683225e0b29938aad85ef6ccfd425f4f Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 17 Feb 2022 14:49:05 -0800 Subject: [PATCH 216/600] [webview_flutter_wkwebview] Add support for `WKUIDelegate` (#4809) --- .../lib/src/foundation/foundation.dart | 35 +++++++++ .../lib/src/web_kit/web_kit.dart | 73 ++++++++++++++++++- .../lib/src/web_kit_webview_widget.dart | 39 +++++++--- .../test/src/web_kit_webview_widget_test.dart | 29 +++++++- .../web_kit_webview_widget_test.mocks.dart | 65 ++++++++++++----- 5 files changed, 207 insertions(+), 34 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart new file mode 100644 index 000000000000..9615fc5a9a51 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart @@ -0,0 +1,35 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:typed_data'; + +import 'package:flutter/foundation.dart'; + +/// A URL load request that is independent of protocol or URL scheme. +/// +/// Wraps [NSUrlRequest](https://developer.apple.com/documentation/foundation/nsurlrequest?language=objc). +@immutable +class NSUrlRequest { + /// Constructs an [NSUrlRequest]. + const NSUrlRequest({ + required this.url, + this.httpMethod, + this.httpBody, + this.allHttpHeaderFields = const {}, + }); + + /// The URL being requested. + final String url; + + /// The HTTP request method. + /// + /// The default HTTP method is “GET”. + final String? httpMethod; + + /// Data sent as the message body of a request, as in an HTTP POST request. + final Uint8List? httpBody; + + /// All of the HTTP header fields for a request. + final Map allHttpHeaderFields; +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart index 9f4f8edfc1f2..d71509597f3f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart @@ -2,6 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter/foundation.dart'; + +import '../foundation/foundation.dart'; + /// Times at which to inject script content into a webpage. /// /// Wraps [WKUserScriptInjectionTime](https://developer.apple.com/documentation/webkit/wkuserscriptinjectiontime?language=objc). @@ -42,12 +46,43 @@ enum WKAudiovisualMediaType { all, } +/// An object that contains information about an action that causes navigation to occur. +/// +/// Wraps [WKNavigationAction](https://developer.apple.com/documentation/webkit/wknavigationaction?language=objc). +@immutable +class WKNavigationAction { + /// Constructs a [WKNavigationAction]. + const WKNavigationAction({required this.request, required this.targetFrame}); + + /// The URL request object associated with the navigation action. + final NSUrlRequest request; + + /// The frame in which to display the new content. + final WKFrameInfo targetFrame; +} + +/// An object that contains information about a frame on a webpage. +/// +/// An instance of this class is a transient, data-only object; it does not +/// uniquely identify a frame across multiple delegate method calls. +/// +/// Wraps [WKFrameInfo](https://developer.apple.com/documentation/webkit/wkframeinfo?language=objc). +@immutable +class WKFrameInfo { + /// Construct a [WKFrameInfo]. + const WKFrameInfo({required this.isMainFrame}); + + /// Indicates whether the frame is the web site's main frame or a subframe. + final bool isMainFrame; +} + /// A script that the web view injects into a webpage. /// /// Wraps [WKUserScript](https://developer.apple.com/documentation/webkit/wkuserscript?language=objc). +@immutable class WKUserScript { /// Constructs a [UserScript]. - WKUserScript( + const WKUserScript( this.source, this.injectionTime, { required this.isMainFrameOnly, @@ -66,9 +101,10 @@ class WKUserScript { /// An object that encapsulates a message sent by JavaScript code from a webpage. /// /// Wraps [WKScriptMessage](https://developer.apple.com/documentation/webkit/wkscriptmessage?language=objc). +@immutable class WKScriptMessage { /// Constructs a [WKScriptMessage]. - WKScriptMessage({required this.name, this.body}); + const WKScriptMessage({required this.name, this.body}); /// The name of the message handler to which the message is sent. final String name; @@ -88,7 +124,7 @@ class WKScriptMessageHandler { /// Use this method to respond to a message sent from the webpage’s /// JavaScript code. Use the [message] parameter to get the message contents and /// to determine the originating web view. - Future setDidReceiveScriptMessage( + set didReceiveScriptMessage( void Function( WKUserContentController userContentController, WKScriptMessage message, @@ -169,7 +205,7 @@ class WKUserContentController { /// A collection of properties that you use to initialize a web view. /// -/// Wraps [WKWebViewConfiguration](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration?language=objc) +/// Wraps [WKWebViewConfiguration](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration?language=objc). class WKWebViewConfiguration { /// Constructs a [WKWebViewConfiguration]. WKWebViewConfiguration({required this.userContentController}); @@ -202,6 +238,22 @@ class WKWebViewConfiguration { } } +/// The methods for presenting native user interface elements on behalf of a webpage. +/// +/// Wraps [WKUIDelegate](https://developer.apple.com/documentation/webkit/wkuidelegate?language=objc). +class WKUIDelegate { + /// Indicates a new [WebView] was requested to be created with [configuration]. + set onCreateWebView( + void Function( + WKWebViewConfiguration configuration, + WKNavigationAction navigationAction, + ) + onCreateeWebView, + ) { + throw UnimplementedError(); + } +} + /// Object that displays interactive web content, such as for an in-app browser. /// /// Wraps [WKWebView](https://developer.apple.com/documentation/webkit/wkwebview?language=objc). @@ -232,4 +284,17 @@ class WKWebView { /// property contains a default configuration object. late final WKWebViewConfiguration configuration = WKWebViewConfiguration._fromWebView(this); + + /// Used to integrate custom user interface elements into web view interactions. + set uiDelegate(WKUIDelegate delegate) { + throw UnimplementedError(); + } + + /// Loads the web content referenced by the specified URL request object and navigates to it. + /// + /// Use this method to load a page from a local or network-based URL. For + /// example, you might use it to navigate to a network-based webpage. + Future loadRequest(NSUrlRequest request) { + throw UnimplementedError(); + } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 25137c9c0af6..d44dd7bcc148 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -87,6 +87,16 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { userContentController: WKUserContentController(), ), ); + + webView.uiDelegate = uiDelegate; + uiDelegate.onCreateWebView = ( + WKWebViewConfiguration configuration, + WKNavigationAction navigationAction, + ) { + if (!navigationAction.targetFrame.isMainFrame) { + webView.loadRequest(navigationAction.request); + } + }; } final Map _scriptMessageHandlers = @@ -106,6 +116,10 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { /// Represents the WebView maintained by platform code. late final WKWebView webView; + /// Used to integrate custom user interface elements into web view interactions. + @visibleForTesting + late final WKUIDelegate uiDelegate = webViewProxy.createUIDelgate(); + Future _setCreationParams( CreationParams params, { required WKWebViewConfiguration configuration, @@ -158,17 +172,15 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { (String channelName) { final WKScriptMessageHandler handler = webViewProxy.createScriptMessageHandler() - ..setDidReceiveScriptMessage( - ( - WKUserContentController userContentController, - WKScriptMessage message, - ) { - javascriptChannelRegistry.onJavascriptChannelMessage( - message.name, - message.body!.toString(), - ); - }, - ); + ..didReceiveScriptMessage = ( + WKUserContentController userContentController, + WKScriptMessage message, + ) { + javascriptChannelRegistry.onJavascriptChannelMessage( + message.name, + message.body!.toString(), + ); + }; _scriptMessageHandlers[channelName] = handler; final String wrapperSource = @@ -231,4 +243,9 @@ class WebViewWidgetProxy { WKScriptMessageHandler createScriptMessageHandler() { return WKScriptMessageHandler(); } + + /// Constructs a [WKUIDelegate]. + WKUIDelegate createUIDelgate() { + return WKUIDelegate(); + } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index 95d5e2cc5fa6..30ba3ee9cb8c 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -9,6 +9,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; +import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart'; import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart'; @@ -18,6 +19,7 @@ import 'web_kit_webview_widget_test.mocks.dart'; WKScriptMessageHandler, WKWebView, WKWebViewConfiguration, + WKUIDelegate, WKUserContentController, JavascriptChannelRegistry, WebViewPlatformCallbacksHandler, @@ -31,6 +33,7 @@ void main() { late MockWebViewWidgetProxy mockWebViewWidgetProxy; late MockWKUserContentController mockUserContentController; late MockWKWebViewConfiguration mockWebViewConfiguration; + late MockWKUIDelegate mockUIDelegate; late MockWebViewPlatformCallbacksHandler mockCallbacksHandler; late MockJavascriptChannelRegistry mockJavascriptChannelRegistry; @@ -41,9 +44,11 @@ void main() { mockWebView = MockWKWebView(); mockWebViewConfiguration = MockWKWebViewConfiguration(); mockUserContentController = MockWKUserContentController(); + mockUIDelegate = MockWKUIDelegate(); mockWebViewWidgetProxy = MockWebViewWidgetProxy(); when(mockWebViewWidgetProxy.createWebView(any)).thenReturn(mockWebView); + when(mockWebViewWidgetProxy.createUIDelgate()).thenReturn(mockUIDelegate); when(mockWebView.configuration).thenReturn(mockWebViewConfiguration); when(mockWebViewConfiguration.userContentController).thenReturn( mockUserContentController, @@ -84,6 +89,26 @@ void main() { await buildWidget(tester); }); + testWidgets('Requests to open a new window loads request in same window', + (WidgetTester tester) async { + await buildWidget(tester); + + final dynamic onCreateWebView = + verify(mockUIDelegate.onCreateWebView = captureAny).captured.single + as void Function(WKWebViewConfiguration, WKNavigationAction); + + const NSUrlRequest request = NSUrlRequest(url: 'https://google.com'); + onCreateWebView( + mockWebViewConfiguration, + const WKNavigationAction( + request: request, + targetFrame: WKFrameInfo(isMainFrame: false), + ), + ); + + verify(mockWebView.loadRequest(request)); + }); + group('$CreationParams', () { testWidgets('autoMediaPlaybackPolicy true', (WidgetTester tester) async { await buildWidget( @@ -271,7 +296,7 @@ void main() { .single as MockWKScriptMessageHandler; final dynamic didReceiveScriptMessage = - verify(messageHandler.setDidReceiveScriptMessage(captureAny)) + verify(messageHandler.didReceiveScriptMessage = captureAny) .captured .single as void Function( WKUserContentController userContentController, @@ -280,7 +305,7 @@ void main() { didReceiveScriptMessage( mockUserContentController, - WKScriptMessage(name: 'hello', body: 'A message.'), + const WKScriptMessage(name: 'hello', body: 'A message.'), ); verify(mockJavascriptChannelRegistry.onJavascriptChannelMessage( 'hello', diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index 6c177b87e953..0138995fd148 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -6,13 +6,15 @@ import 'dart:async' as _i3; import 'package:mockito/mockito.dart' as _i1; import 'package:webview_flutter_platform_interface/src/types/javascript_channel.dart' - as _i5; -import 'package:webview_flutter_platform_interface/src/types/types.dart' as _i6; + as _i6; +import 'package:webview_flutter_platform_interface/src/types/types.dart' as _i7; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart' + as _i5; +import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart' as _i4; import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart' as _i2; import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart' - as _i7; + as _i8; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters @@ -34,6 +36,8 @@ class _FakeWKWebView_2 extends _i1.Fake implements _i2.WKWebView {} class _FakeWKScriptMessageHandler_3 extends _i1.Fake implements _i2.WKScriptMessageHandler {} +class _FakeWKUIDelegate_4 extends _i1.Fake implements _i2.WKUIDelegate {} + /// A class which mocks [WKScriptMessageHandler]. /// /// See the documentation for Mockito's code generation for more information. @@ -44,14 +48,12 @@ class MockWKScriptMessageHandler extends _i1.Mock } @override - _i3.Future setDidReceiveScriptMessage( + set didReceiveScriptMessage( void Function(_i2.WKUserContentController, _i2.WKScriptMessage)? didReceiveScriptMessage) => - (super.noSuchMethod( - Invocation.method( - #setDidReceiveScriptMessage, [didReceiveScriptMessage]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i3.Future); + super.noSuchMethod( + Invocation.setter(#didReceiveScriptMessage, didReceiveScriptMessage), + returnValueForMissingStub: null); } /// A class which mocks [WKWebView]. @@ -67,6 +69,15 @@ class MockWKWebView extends _i1.Mock implements _i2.WKWebView { (super.noSuchMethod(Invocation.getter(#configuration), returnValue: _FakeWKWebViewConfiguration_0()) as _i2.WKWebViewConfiguration); + @override + set uiDelegate(_i2.WKUIDelegate? delegate) => + super.noSuchMethod(Invocation.setter(#uiDelegate, delegate), + returnValueForMissingStub: null); + @override + _i3.Future loadRequest(_i4.NSUrlRequest? request) => + (super.noSuchMethod(Invocation.method(#loadRequest, [request]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i3.Future); } /// A class which mocks [WKWebViewConfiguration]. @@ -101,6 +112,22 @@ class MockWKWebViewConfiguration extends _i1.Mock returnValueForMissingStub: null); } +/// A class which mocks [WKUIDelegate]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockWKUIDelegate extends _i1.Mock implements _i2.WKUIDelegate { + MockWKUIDelegate() { + _i1.throwOnMissingStub(this); + } + + @override + set onCreateWebView( + void Function(_i2.WKWebViewConfiguration, _i2.WKNavigationAction)? + onCreateeWebView) => + super.noSuchMethod(Invocation.setter(#onCreateWebView, onCreateeWebView), + returnValueForMissingStub: null); +} + /// A class which mocks [WKUserContentController]. /// /// See the documentation for Mockito's code generation for more information. @@ -143,23 +170,23 @@ class MockWKUserContentController extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockJavascriptChannelRegistry extends _i1.Mock - implements _i4.JavascriptChannelRegistry { + implements _i5.JavascriptChannelRegistry { MockJavascriptChannelRegistry() { _i1.throwOnMissingStub(this); } @override - Map get channels => + Map get channels => (super.noSuchMethod(Invocation.getter(#channels), - returnValue: {}) - as Map); + returnValue: {}) + as Map); @override void onJavascriptChannelMessage(String? channel, String? message) => super.noSuchMethod( Invocation.method(#onJavascriptChannelMessage, [channel, message]), returnValueForMissingStub: null); @override - void updateJavascriptChannelsFromSet(Set<_i5.JavascriptChannel>? channels) => + void updateJavascriptChannelsFromSet(Set<_i6.JavascriptChannel>? channels) => super.noSuchMethod( Invocation.method(#updateJavascriptChannelsFromSet, [channels]), returnValueForMissingStub: null); @@ -169,7 +196,7 @@ class MockJavascriptChannelRegistry extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockWebViewPlatformCallbacksHandler extends _i1.Mock - implements _i4.WebViewPlatformCallbacksHandler { + implements _i5.WebViewPlatformCallbacksHandler { MockWebViewPlatformCallbacksHandler() { _i1.throwOnMissingStub(this); } @@ -193,7 +220,7 @@ class MockWebViewPlatformCallbacksHandler extends _i1.Mock super.noSuchMethod(Invocation.method(#onProgress, [progress]), returnValueForMissingStub: null); @override - void onWebResourceError(_i6.WebResourceError? error) => + void onWebResourceError(_i7.WebResourceError? error) => super.noSuchMethod(Invocation.method(#onWebResourceError, [error]), returnValueForMissingStub: null); } @@ -202,7 +229,7 @@ class MockWebViewPlatformCallbacksHandler extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockWebViewWidgetProxy extends _i1.Mock - implements _i7.WebViewWidgetProxy { + implements _i8.WebViewWidgetProxy { MockWebViewWidgetProxy() { _i1.throwOnMissingStub(this); } @@ -216,4 +243,8 @@ class MockWebViewWidgetProxy extends _i1.Mock (super.noSuchMethod(Invocation.method(#createScriptMessageHandler, []), returnValue: _FakeWKScriptMessageHandler_3()) as _i2.WKScriptMessageHandler); + @override + _i2.WKUIDelegate createUIDelgate() => + (super.noSuchMethod(Invocation.method(#createUIDelgate, []), + returnValue: _FakeWKUIDelegate_4()) as _i2.WKUIDelegate); } From 9946ef0c6df77f68ba493ea49d0c5fbd41e0b528 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 17 Feb 2022 20:24:17 -0500 Subject: [PATCH 217/600] Roll Flutter from 93c0c043cf2c to 38dae1bfc045 (10 revisions) (#4891) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 143dde748c88..ad14349bdeb6 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -93c0c043cf2c38947e0e95c81e936aa46541015c +38dae1bfc045806f4d2041b2d98a5a98a7140fbf From 04a13e5cdaa47f3c67cdd54ffb27bce368f42262 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 17 Feb 2022 21:29:18 -0500 Subject: [PATCH 218/600] Roll Flutter from 38dae1bfc045 to 1bdcf04ddd7b (3 revisions) (#4892) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index ad14349bdeb6..7a2a2160573a 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -38dae1bfc045806f4d2041b2d98a5a98a7140fbf +1bdcf04ddd7bf42edf1be291f9e3a444284d1a53 From 1e7af5e1d66b568968fe975cb60e543ebd364a8e Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 18 Feb 2022 05:39:19 -0800 Subject: [PATCH 219/600] [google_sign_in_web] Update web package analysis options (#4875) --- .../google_sign_in_web/CHANGELOG.md | 4 +++ .../google_sign_in_web/analysis_options.yaml | 1 - .../example/integration_test/auth2_test.dart | 20 ++++++----- .../gapi_mocks/src/auth2_init.dart | 2 +- .../google_sign_in_web/example/lib/main.dart | 2 +- .../google_sign_in_web/example/pubspec.yaml | 4 +-- .../lib/google_sign_in_web.dart | 35 ++++++++++++------- .../lib/src/generated/gapi.dart | 2 +- .../lib/src/generated/gapiauth2.dart | 2 +- .../google_sign_in_web/lib/src/load_gapi.dart | 8 +++-- .../google_sign_in_web/lib/src/utils.dart | 6 ++-- .../google_sign_in_web/pubspec.yaml | 3 +- script/configs/custom_analysis.yaml | 1 - 13 files changed, 52 insertions(+), 38 deletions(-) delete mode 100644 packages/google_sign_in/google_sign_in_web/analysis_options.yaml diff --git a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md index e65ce0fb23ba..c1a1606d3a2f 100644 --- a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.10.0+5 + +* Internal code cleanup for stricter analysis options. + ## 0.10.0+4 * Removes dependency on `meta`. diff --git a/packages/google_sign_in/google_sign_in_web/analysis_options.yaml b/packages/google_sign_in/google_sign_in_web/analysis_options.yaml deleted file mode 100644 index 5aeb4e7c5e21..000000000000 --- a/packages/google_sign_in/google_sign_in_web/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../../analysis_options_legacy.yaml diff --git a/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_test.dart b/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_test.dart index e1a97cee6cf7..2af9476dbb33 100644 --- a/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_test.dart +++ b/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_test.dart @@ -15,10 +15,10 @@ import 'src/test_utils.dart'; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - GoogleSignInTokenData expectedTokenData = + final GoogleSignInTokenData expectedTokenData = GoogleSignInTokenData(idToken: '70k3n', accessToken: 'access_70k3n'); - GoogleSignInUserData expectedUserData = GoogleSignInUserData( + final GoogleSignInUserData expectedUserData = GoogleSignInUserData( displayName: 'Foo Bar', email: 'foo@example.com', id: '123', @@ -55,7 +55,7 @@ void main() { ); fail('plugin.init should have thrown an exception!'); } catch (e) { - final String code = js_util.getProperty(e, 'code') as String; + final String code = js_util.getProperty(e, 'code'); expect(code, 'idpiframe_initialization_failed'); } }); @@ -99,7 +99,7 @@ void main() { }); testWidgets('requestScopes', (WidgetTester tester) async { await _discardInit(); - await expectLater(plugin.requestScopes(['newScope']), + await expectLater(plugin.requestScopes(['newScope']), throwsA(isA())); }); }); @@ -137,26 +137,28 @@ void main() { }); testWidgets('signInSilently', (WidgetTester tester) async { - GoogleSignInUserData actualUser = (await plugin.signInSilently())!; + final GoogleSignInUserData actualUser = + (await plugin.signInSilently())!; expect(actualUser, expectedUserData); }); testWidgets('signIn', (WidgetTester tester) async { - GoogleSignInUserData actualUser = (await plugin.signIn())!; + final GoogleSignInUserData actualUser = (await plugin.signIn())!; expect(actualUser, expectedUserData); }); testWidgets('getTokens', (WidgetTester tester) async { - GoogleSignInTokenData actualToken = + final GoogleSignInTokenData actualToken = await plugin.getTokens(email: expectedUserData.email); expect(actualToken, expectedTokenData); }); testWidgets('requestScopes', (WidgetTester tester) async { - bool scopeGranted = await plugin.requestScopes(['newScope']); + final bool scopeGranted = + await plugin.requestScopes(['newScope']); expect(scopeGranted, isTrue); }); @@ -187,7 +189,7 @@ void main() { await plugin.signIn(); fail('plugin.signIn() should have thrown an exception!'); } catch (e) { - final String code = js_util.getProperty(e, 'code') as String; + final String code = js_util.getProperty(e, 'code'); expect(code, 'popup_closed_by_user'); } }); diff --git a/packages/google_sign_in/google_sign_in_web/example/integration_test/gapi_mocks/src/auth2_init.dart b/packages/google_sign_in/google_sign_in_web/example/integration_test/gapi_mocks/src/auth2_init.dart index 2a085ccf3588..cc49b2759e7f 100644 --- a/packages/google_sign_in/google_sign_in_web/example/integration_test/gapi_mocks/src/auth2_init.dart +++ b/packages/google_sign_in/google_sign_in_web/example/integration_test/gapi_mocks/src/auth2_init.dart @@ -95,7 +95,7 @@ GapiAuth2.prototype.getAuthInstance = function () { return new Promise((resolve, reject) => { window.setTimeout(() => { reject({ - error: '${error}' + error: '$error' }); }, 30); }); diff --git a/packages/google_sign_in/google_sign_in_web/example/lib/main.dart b/packages/google_sign_in/google_sign_in_web/example/lib/main.dart index 10415204570c..d381fb4af3ab 100644 --- a/packages/google_sign_in/google_sign_in_web/example/lib/main.dart +++ b/packages/google_sign_in/google_sign_in_web/example/lib/main.dart @@ -17,6 +17,6 @@ class MyApp extends StatefulWidget { class _MyAppState extends State { @override Widget build(BuildContext context) { - return Text('Testing... Look at the console output for results!'); + return const Text('Testing... Look at the console output for results!'); } } diff --git a/packages/google_sign_in/google_sign_in_web/example/pubspec.yaml b/packages/google_sign_in/google_sign_in_web/example/pubspec.yaml index e370ecc561d2..5a51cd567275 100644 --- a/packages/google_sign_in/google_sign_in_web/example/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_web/example/pubspec.yaml @@ -12,11 +12,11 @@ dependencies: path: ../ dev_dependencies: - http: ^0.13.0 - js: ^0.6.3 flutter_driver: sdk: flutter flutter_test: sdk: flutter + http: ^0.13.0 integration_test: sdk: flutter + js: ^0.6.3 diff --git a/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart b/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart index ff0d8e4cea89..731ced5ddbbe 100644 --- a/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart +++ b/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart @@ -55,7 +55,7 @@ class GoogleSignInPlugin extends GoogleSignInPlatform { @visibleForTesting Future get initialized { _assertIsInitCalled(); - return Future.wait([_isGapiInitialized, _isAuthInitialized]); + return Future.wait(>[_isGapiInitialized, _isAuthInitialized]); } String? _autoDetectedClientId; @@ -94,14 +94,14 @@ class GoogleSignInPlugin extends GoogleSignInPlatform { client_id: appClientId!, )); - Completer isAuthInitialized = Completer(); + final Completer isAuthInitialized = Completer(); _isAuthInitialized = isAuthInitialized.future; _isInitCalled = true; auth.then(allowInterop((auth2.GoogleAuth initializedAuth) { // onSuccess - // TODO: https://github.com/flutter/flutter/issues/48528 + // TODO(ditman): https://github.com/flutter/flutter/issues/48528 // This plugin doesn't notify the app of external changes to the // state of the authentication, i.e: if you logout elsewhere... @@ -124,7 +124,7 @@ class GoogleSignInPlugin extends GoogleSignInPlatform { await initialized; return gapiUserToPluginUserData( - await auth2.getAuthInstance()?.currentUser?.get()); + auth2.getAuthInstance()?.currentUser?.get()); } @override @@ -169,7 +169,9 @@ class GoogleSignInPlugin extends GoogleSignInPlatform { final auth2.GoogleUser? currentUser = auth2.getAuthInstance()?.currentUser?.get(); - if (currentUser == null) return; + if (currentUser == null) { + return; + } return currentUser.disconnect(); } @@ -181,7 +183,9 @@ class GoogleSignInPlugin extends GoogleSignInPlatform { final auth2.GoogleUser? currentUser = auth2.getAuthInstance()?.currentUser?.get(); - if (currentUser == null) return false; + if (currentUser == null) { + return false; + } return currentUser.isSignedIn(); } @@ -197,17 +201,22 @@ class GoogleSignInPlugin extends GoogleSignInPlatform { Future requestScopes(List scopes) async { await initialized; - final currentUser = auth2.getAuthInstance()?.currentUser?.get(); + final auth2.GoogleUser? currentUser = + auth2.getAuthInstance()?.currentUser?.get(); - if (currentUser == null) return false; + if (currentUser == null) { + return false; + } - final grantedScopes = currentUser.getGrantedScopes() ?? ''; - final missingScopes = - scopes.where((scope) => !grantedScopes.contains(scope)); + final String grantedScopes = currentUser.getGrantedScopes() ?? ''; + final Iterable missingScopes = + scopes.where((String scope) => !grantedScopes.contains(scope)); - if (missingScopes.isEmpty) return true; + if (missingScopes.isEmpty) { + return true; + } - final response = await currentUser + final Object? response = await currentUser .grant(auth2.SigninOptions(scope: missingScopes.join(' '))); return response != null; diff --git a/packages/google_sign_in/google_sign_in_web/lib/src/generated/gapi.dart b/packages/google_sign_in/google_sign_in_web/lib/src/generated/gapi.dart index 1e2db0fe4609..a6d5b9d8dbbb 100644 --- a/packages/google_sign_in/google_sign_in_web/lib/src/generated/gapi.dart +++ b/packages/google_sign_in/google_sign_in_web/lib/src/generated/gapi.dart @@ -10,7 +10,7 @@ // https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/gapi -// ignore_for_file: public_member_api_docs, unused_element +// ignore_for_file: public_member_api_docs, unused_element, sort_constructors_first, prefer_generic_function_type_aliases @JS() library gapi; diff --git a/packages/google_sign_in/google_sign_in_web/lib/src/generated/gapiauth2.dart b/packages/google_sign_in/google_sign_in_web/lib/src/generated/gapiauth2.dart index d5efc71d469a..88d196bad007 100644 --- a/packages/google_sign_in/google_sign_in_web/lib/src/generated/gapiauth2.dart +++ b/packages/google_sign_in/google_sign_in_web/lib/src/generated/gapiauth2.dart @@ -12,7 +12,7 @@ // https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/gapi.auth2 -// ignore_for_file: public_member_api_docs, unused_element +// ignore_for_file: public_member_api_docs, unused_element, non_constant_identifier_names, sort_constructors_first, always_specify_types @JS() library gapiauth2; diff --git a/packages/google_sign_in/google_sign_in_web/lib/src/load_gapi.dart b/packages/google_sign_in/google_sign_in_web/lib/src/load_gapi.dart index 621e1673d636..f60d6cd57e56 100644 --- a/packages/google_sign_in/google_sign_in_web/lib/src/load_gapi.dart +++ b/packages/google_sign_in/google_sign_in_web/lib/src/load_gapi.dart @@ -7,8 +7,8 @@ library gapi_onload; import 'dart:async'; -import 'package:js/js.dart'; import 'package:flutter/foundation.dart' show visibleForTesting; +import 'package:js/js.dart'; import 'generated/gapi.dart' as gapi; import 'utils.dart' show injectJSLibraries; @@ -37,8 +37,10 @@ Future inject(String url, {List libraries = const []}) { }); // Attach the onload callback to the main url - final List allLibraries = [_addOnloadToScript(url)] - ..addAll(libraries); + final List allLibraries = [ + _addOnloadToScript(url), + ...libraries + ]; return Future.wait( >[injectJSLibraries(allLibraries), gapiOnLoad.future]); diff --git a/packages/google_sign_in/google_sign_in_web/lib/src/utils.dart b/packages/google_sign_in/google_sign_in_web/lib/src/utils.dart index bcfefc2054b4..cae20d28db44 100644 --- a/packages/google_sign_in/google_sign_in_web/lib/src/utils.dart +++ b/packages/google_sign_in/google_sign_in_web/lib/src/utils.dart @@ -25,15 +25,15 @@ Future injectJSLibraries( final html.Element targetElement = target ?? html.querySelector('head')!; - libraries.forEach((String library) { + for (final String library in libraries) { final html.ScriptElement script = html.ScriptElement() ..async = true ..defer = true ..src = library; - // TODO add a timeout race to fail this future + // TODO(ditman): add a timeout race to fail this future loading.add(script.onLoad.first); tags.add(script); - }); + } targetElement.children.addAll(tags); return Future.wait(loading); diff --git a/packages/google_sign_in/google_sign_in_web/pubspec.yaml b/packages/google_sign_in/google_sign_in_web/pubspec.yaml index 2f033d9a008a..3bc05d14e34e 100644 --- a/packages/google_sign_in/google_sign_in_web/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_web/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Google Sign-In, a secure authentication system for signing in with a Google account on Android, iOS and Web. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 0.10.0+4 +version: 0.10.0+5 environment: sdk: ">=2.12.0 <3.0.0" @@ -24,7 +24,6 @@ dependencies: sdk: flutter google_sign_in_platform_interface: ^2.0.0 js: ^0.6.3 - pedantic: ^1.10.0 dev_dependencies: flutter_test: diff --git a/script/configs/custom_analysis.yaml b/script/configs/custom_analysis.yaml index 0022beee149a..0e8e1cb05dbf 100644 --- a/script/configs/custom_analysis.yaml +++ b/script/configs/custom_analysis.yaml @@ -15,7 +15,6 @@ - google_maps_flutter/google_maps_flutter_platform_interface - google_maps_flutter/google_maps_flutter_web - google_sign_in/google_sign_in -- google_sign_in/google_sign_in_web - in_app_purchase/in_app_purchase - in_app_purchase/in_app_purchase_android - in_app_purchase/in_app_purchase_storekit From 7363b7a2df3449e8d574777374cde3434e71d0a1 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Sat, 19 Feb 2022 11:29:20 -0500 Subject: [PATCH 220/600] [google_sign_in] Update app-facing package analysis options (#4881) --- .../google_sign_in/CHANGELOG.md | 4 +++ .../google_sign_in/analysis_options.yaml | 1 - .../integration_test/google_sign_in_test.dart | 4 +-- .../google_sign_in/example/lib/main.dart | 29 ++++++++-------- .../google_sign_in/example/pubspec.yaml | 5 ++- .../google_sign_in/lib/google_sign_in.dart | 22 ++++++++----- .../google_sign_in/lib/widgets.dart | 2 +- .../google_sign_in/pubspec.yaml | 3 +- .../google_sign_in/test/fife_test.dart | 18 +++++----- .../test/google_sign_in_test.dart | 33 ++++++++++--------- .../google_sign_in/test/widgets_test.dart | 7 ++-- script/configs/custom_analysis.yaml | 1 - 12 files changed, 69 insertions(+), 60 deletions(-) delete mode 100644 packages/google_sign_in/google_sign_in/analysis_options.yaml diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index 183edaddf625..a46023bfd788 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,3 +1,7 @@ +## 5.2.4 + +* Internal code cleanup for stricter analysis options. + ## 5.2.3 * Bumps the Android dependency on `com.google.android.gms:play-services-auth` and therefore removes the need for `jetifier`. diff --git a/packages/google_sign_in/google_sign_in/analysis_options.yaml b/packages/google_sign_in/google_sign_in/analysis_options.yaml deleted file mode 100644 index 5aeb4e7c5e21..000000000000 --- a/packages/google_sign_in/google_sign_in/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../../analysis_options_legacy.yaml diff --git a/packages/google_sign_in/google_sign_in/example/integration_test/google_sign_in_test.dart b/packages/google_sign_in/google_sign_in/example/integration_test/google_sign_in_test.dart index 7a1522346e37..be3cc89674a3 100644 --- a/packages/google_sign_in/google_sign_in/example/integration_test/google_sign_in_test.dart +++ b/packages/google_sign_in/google_sign_in/example/integration_test/google_sign_in_test.dart @@ -4,15 +4,15 @@ // @dart = 2.9 -import 'package:integration_test/integration_test.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_sign_in/google_sign_in.dart'; +import 'package:integration_test/integration_test.dart'; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('Can initialize the plugin', (WidgetTester tester) async { - GoogleSignIn signIn = GoogleSignIn(); + final GoogleSignIn signIn = GoogleSignIn(); expect(signIn, isNotNull); }); } diff --git a/packages/google_sign_in/google_sign_in/example/lib/main.dart b/packages/google_sign_in/google_sign_in/example/lib/main.dart index c677d4e75bc3..9840a1e0a9f6 100644 --- a/packages/google_sign_in/google_sign_in/example/lib/main.dart +++ b/packages/google_sign_in/google_sign_in/example/lib/main.dart @@ -7,9 +7,9 @@ import 'dart:async'; import 'dart:convert' show json; -import "package:http/http.dart" as http; import 'package:flutter/material.dart'; import 'package:google_sign_in/google_sign_in.dart'; +import 'package:http/http.dart' as http; GoogleSignIn _googleSignIn = GoogleSignIn( // Optional clientId @@ -54,7 +54,7 @@ class SignInDemoState extends State { Future _handleGetContact(GoogleSignInAccount user) async { setState(() { - _contactText = "Loading contact info..."; + _contactText = 'Loading contact info...'; }); final http.Response response = await http.get( Uri.parse('https://people.googleapis.com/v1/people/me/connections' @@ -63,36 +63,37 @@ class SignInDemoState extends State { ); if (response.statusCode != 200) { setState(() { - _contactText = "People API gave a ${response.statusCode} " - "response. Check logs for details."; + _contactText = 'People API gave a ${response.statusCode} ' + 'response. Check logs for details.'; }); print('People API ${response.statusCode} response: ${response.body}'); return; } - final Map data = json.decode(response.body); + final Map data = + json.decode(response.body) as Map; final String? namedContact = _pickFirstNamedContact(data); setState(() { if (namedContact != null) { - _contactText = "I see you know $namedContact!"; + _contactText = 'I see you know $namedContact!'; } else { - _contactText = "No contacts to display."; + _contactText = 'No contacts to display.'; } }); } String? _pickFirstNamedContact(Map data) { - final List? connections = data['connections']; + final List? connections = data['connections'] as List?; final Map? contact = connections?.firstWhere( (dynamic contact) => contact['names'] != null, orElse: () => null, - ); + ) as Map?; if (contact != null) { final Map? name = contact['names'].firstWhere( (dynamic name) => name['displayName'] != null, orElse: () => null, - ); + ) as Map?; if (name != null) { - return name['displayName']; + return name['displayName'] as String?; } } return null; @@ -109,7 +110,7 @@ class SignInDemoState extends State { Future _handleSignOut() => _googleSignIn.disconnect(); Widget _buildBody() { - GoogleSignInAccount? user = _currentUser; + final GoogleSignInAccount? user = _currentUser; if (user != null) { return Column( mainAxisAlignment: MainAxisAlignment.spaceAround, @@ -121,7 +122,7 @@ class SignInDemoState extends State { title: Text(user.displayName ?? ''), subtitle: Text(user.email), ), - const Text("Signed in successfully."), + const Text('Signed in successfully.'), Text(_contactText), ElevatedButton( child: const Text('SIGN OUT'), @@ -137,7 +138,7 @@ class SignInDemoState extends State { return Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ - const Text("You are not currently signed in."), + const Text('You are not currently signed in.'), ElevatedButton( child: const Text('SIGN IN'), onPressed: _handleSignIn, diff --git a/packages/google_sign_in/google_sign_in/example/pubspec.yaml b/packages/google_sign_in/google_sign_in/example/pubspec.yaml index dfd942d3d438..af9ed877e523 100644 --- a/packages/google_sign_in/google_sign_in/example/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/example/pubspec.yaml @@ -20,11 +20,10 @@ dependencies: dev_dependencies: espresso: ^0.1.0+2 - pedantic: ^1.10.0 - integration_test: - sdk: flutter flutter_driver: sdk: flutter + integration_test: + sdk: flutter flutter: uses-material-design: true diff --git a/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart b/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart index e104093c69bc..6afd409807fa 100644 --- a/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart +++ b/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart @@ -5,6 +5,7 @@ import 'dart:async'; import 'dart:ui' show hashValues; +import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart' show PlatformException; import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; @@ -39,6 +40,7 @@ class GoogleSignInAuthentication { /// [GoogleSignInUserData]. /// /// [id] is guaranteed to be non-null. +@immutable class GoogleSignInAccount implements GoogleIdentity { GoogleSignInAccount._(this._googleSignIn, GoogleSignInUserData data) : displayName = data.displayName, @@ -99,9 +101,7 @@ class GoogleSignInAccount implements GoogleIdentity { // On Android, there isn't an API for refreshing the idToken, so re-use // the one we obtained on login. - if (response.idToken == null) { - response.idToken = _idToken; - } + response.idToken ??= _idToken; return GoogleSignInAuthentication._(response); } @@ -113,10 +113,10 @@ class GoogleSignInAccount implements GoogleIdentity { Future> get authHeaders async { final String? token = (await authentication).accessToken; return { - "Authorization": "Bearer $token", + 'Authorization': 'Bearer $token', // TODO(kevmoo): Use the correct value once it's available from authentication // See https://github.com/flutter/flutter/issues/80905 - "X-Goog-AuthUser": "0", + 'X-Goog-AuthUser': '0', }; } @@ -131,8 +131,12 @@ class GoogleSignInAccount implements GoogleIdentity { @override bool operator ==(dynamic other) { - if (identical(this, other)) return true; - if (other is! GoogleSignInAccount) return false; + if (identical(this, other)) { + return true; + } + if (other is! GoogleSignInAccount) { + return false; + } final GoogleSignInAccount otherAccount = other; return displayName == otherAccount.displayName && email == otherAccount.email && @@ -228,7 +232,7 @@ class GoogleSignIn { /// Client ID being used to connect to google sign-in. Only supported on web. final String? clientId; - StreamController _currentUserController = + final StreamController _currentUserController = StreamController.broadcast(); /// Subscribe to this stream to be notified when the current user changes. @@ -277,7 +281,7 @@ class GoogleSignIn { final Completer completer = Completer(); future.whenComplete(completer.complete).catchError((dynamic _) { // Ignore if previous call completed with an error. - // TODO: Should we log errors here, if debug or similar? + // TODO(ditman): Should we log errors here, if debug or similar? }); return completer.future; } diff --git a/packages/google_sign_in/google_sign_in/lib/widgets.dart b/packages/google_sign_in/google_sign_in/lib/widgets.dart index c031cb2b6eca..61f89133fba4 100644 --- a/packages/google_sign_in/google_sign_in/lib/widgets.dart +++ b/packages/google_sign_in/google_sign_in/lib/widgets.dart @@ -118,7 +118,7 @@ class GoogleUserCircleAvatar extends StatelessWidget { /// /// Those bytes come from `resources/transparentImage.gif`. final Uint8List _transparentImage = Uint8List.fromList( - [ + [ 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0x80, 0x00, // 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x21, 0xf9, 0x04, 0x01, 0x00, // 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, // diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index 40f8d0f5f02c..de15d0bb0740 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Google Sign-In, a secure authentication system for signing in with a Google account on Android and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.2.3 +version: 5.2.4 environment: sdk: ">=2.14.0 <3.0.0" @@ -34,7 +34,6 @@ dev_dependencies: http: ^0.13.0 integration_test: sdk: flutter - pedantic: ^1.10.0 # The example deliberately includes limited-use secrets. false_secrets: diff --git a/packages/google_sign_in/google_sign_in/test/fife_test.dart b/packages/google_sign_in/google_sign_in/test/fife_test.dart index c81454ef0a8c..5b0524771eb8 100644 --- a/packages/google_sign_in/google_sign_in/test/fife_test.dart +++ b/packages/google_sign_in/google_sign_in/test/fife_test.dart @@ -15,17 +15,17 @@ void main() { const String expected = '$base/s20-c/photo.jpg'; test('with directives, sets size', () { - final String url = '$base/s64-c/photo.jpg'; + const String url = '$base/s64-c/photo.jpg'; expect(addSizeDirectiveToUrl(url, size), expected); }); test('no directives, sets size and crop', () { - final String url = '$base/photo.jpg'; + const String url = '$base/photo.jpg'; expect(addSizeDirectiveToUrl(url, size), expected); }); test('no crop, sets size and crop', () { - final String url = '$base/s64/photo.jpg'; + const String url = '$base/s64/photo.jpg'; expect(addSizeDirectiveToUrl(url, size), expected); }); }); @@ -36,29 +36,29 @@ void main() { const String expected = '$base=c-s20'; test('with directives, sets size', () { - final String url = '$base=s120-c'; + const String url = '$base=s120-c'; expect(addSizeDirectiveToUrl(url, size), expected); }); test('no directives, sets size and crop', () { - final String url = base; + const String url = base; expect(addSizeDirectiveToUrl(url, size), expected); }); test('no directives, but with an equals sign, sets size and crop', () { - final String url = '$base='; + const String url = '$base='; expect(addSizeDirectiveToUrl(url, size), expected); }); test('no crop, adds crop', () { - final String url = '$base=s120'; + const String url = '$base=s120'; expect(addSizeDirectiveToUrl(url, size), expected); }); test('many directives, sets size and crop, preserves other directives', () { - final String url = '$base=s120-c-fSoften=1,50,0'; - final String expected = '$base=c-fSoften=1,50,0-s20'; + const String url = '$base=s120-c-fSoften=1,50,0'; + const String expected = '$base=c-fSoften=1,50,0-s20'; expect(addSizeDirectiveToUrl(url, size), expected); }); }); diff --git a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart index 0a019a2ffae5..119ee50a383b 100644 --- a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart +++ b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart @@ -6,9 +6,9 @@ import 'dart:async'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; import 'package:google_sign_in/google_sign_in.dart'; import 'package:google_sign_in/testing.dart'; +import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -19,11 +19,11 @@ void main() { ); const Map kUserData = { - "email": "john.doe@gmail.com", - "id": "8162538176523816253123", - "photoUrl": "https://lh5.googleusercontent.com/photo.jpg", - "displayName": "John Doe", - "serverAuthCode": "789" + 'email': 'john.doe@gmail.com', + 'id': '8162538176523816253123', + 'photoUrl': 'https://lh5.googleusercontent.com/photo.jpg', + 'displayName': 'John Doe', + 'serverAuthCode': '789' }; const Map kDefaultResponses = { @@ -84,7 +84,7 @@ void main() { }); test('signIn prioritize clientId parameter when available', () async { - final fakeClientId = 'fakeClientId'; + const String fakeClientId = 'fakeClientId'; googleSignIn = GoogleSignIn(clientId: fakeClientId); await googleSignIn.signIn(); expect(googleSignIn.currentUser, isNotNull); @@ -269,14 +269,14 @@ void main() { test('signInSilently suppresses errors by default', () async { channel.setMockMethodCallHandler((MethodCall methodCall) { - throw "I am an error"; + throw 'I am an error'; }); expect(await googleSignIn.signInSilently(), isNull); // should not throw }); test('signInSilently forwards errors', () async { channel.setMockMethodCallHandler((MethodCall methodCall) { - throw "I am an error"; + throw 'I am an error'; }); expect(googleSignIn.signInSilently(suppressErrors: false), throwsA(isInstanceOf())); @@ -304,7 +304,7 @@ void main() { if (methodCall.method == 'init') { initCount++; if (initCount == 1) { - throw "First init fails"; + throw 'First init fails'; } } return Future.value(responses[methodCall.method]); @@ -364,7 +364,8 @@ void main() { test('requestScopes returns true once new scope is granted', () async { await googleSignIn.signIn(); - final result = await googleSignIn.requestScopes(['testScope']); + final bool result = + await googleSignIn.requestScopes(['testScope']); expect(result, isTrue); expect( @@ -373,7 +374,7 @@ void main() { _isSignInMethodCall(), isMethodCall('signIn', arguments: null), isMethodCall('requestScopes', arguments: { - 'scopes': ['testScope'], + 'scopes': ['testScope'], }), ], ); @@ -382,10 +383,10 @@ void main() { group('GoogleSignIn with fake backend', () { const FakeUser kUserData = FakeUser( - id: "8162538176523816253123", - displayName: "John Doe", - email: "john.doe@gmail.com", - photoUrl: "https://lh5.googleusercontent.com/photo.jpg", + id: '8162538176523816253123', + displayName: 'John Doe', + email: 'john.doe@gmail.com', + photoUrl: 'https://lh5.googleusercontent.com/photo.jpg', serverAuthCode: '789'); late GoogleSignIn googleSignIn; diff --git a/packages/google_sign_in/google_sign_in/test/widgets_test.dart b/packages/google_sign_in/google_sign_in/test/widgets_test.dart index f7bd6f803e7c..b847bc6de36e 100644 --- a/packages/google_sign_in/google_sign_in/test/widgets_test.dart +++ b/packages/google_sign_in/google_sign_in/test/widgets_test.dart @@ -18,9 +18,12 @@ class _TestGoogleIdentity extends GoogleIdentity { this.photoUrl, }); + @override final String id; + @override final String email; + @override final String? photoUrl; @override @@ -56,7 +59,7 @@ class _MockHttpRequest extends Fake implements HttpClientRequest { /// /// Those bytes come from `resources/transparentImage.gif`. final Uint8List _transparentImage = Uint8List.fromList( - [ + [ 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0x80, 0x00, // 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x21, 0xf9, 0x04, 0x01, 0x00, // 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, // @@ -97,7 +100,7 @@ void main() { id: 'userId', photoUrl: 'photoUrl', ); - tester.binding.window.physicalSizeTestValue = Size(100, 100); + tester.binding.window.physicalSizeTestValue = const Size(100, 100); await HttpOverrides.runZoned( () async { diff --git a/script/configs/custom_analysis.yaml b/script/configs/custom_analysis.yaml index 0e8e1cb05dbf..d10424e0b944 100644 --- a/script/configs/custom_analysis.yaml +++ b/script/configs/custom_analysis.yaml @@ -14,7 +14,6 @@ - google_maps_flutter/google_maps_flutter - google_maps_flutter/google_maps_flutter_platform_interface - google_maps_flutter/google_maps_flutter_web -- google_sign_in/google_sign_in - in_app_purchase/in_app_purchase - in_app_purchase/in_app_purchase_android - in_app_purchase/in_app_purchase_storekit From 0d40e93e73832b85c39f7cf721d17c14b684fd16 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 19 Feb 2022 12:14:20 -0500 Subject: [PATCH 221/600] Roll Flutter from 1bdcf04ddd7b to c50b02f2c3e6 (2 revisions) (#4896) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 7a2a2160573a..4d5ae1f40ac4 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -1bdcf04ddd7bf42edf1be291f9e3a444284d1a53 +c50b02f2c3e69e23b9c044c571d1a34f816d7ab4 From 013f90307951d21d614aa88891307a158d84daa4 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 19 Feb 2022 13:19:23 -0500 Subject: [PATCH 222/600] Roll Flutter from c50b02f2c3e6 to c849844feb3d (20 revisions) (#4898) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 4d5ae1f40ac4..2eebe0e311a7 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c50b02f2c3e69e23b9c044c571d1a34f816d7ab4 +c849844feb3de477b27dfcb3bd6879ca621bccc7 From d9fc677ab06fa4cc0913e21d0c09e309f5dad18e Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 19 Feb 2022 14:24:23 -0500 Subject: [PATCH 223/600] Roll Flutter from c849844feb3d to 54a9d6528289 (1 revision) (#4900) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 2eebe0e311a7..10b0279d7dca 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c849844feb3de477b27dfcb3bd6879ca621bccc7 +54a9d652828941dbebbe8de32cf667e89578b13d From 6d88e3a2a1848ed93670890bc79532426b1344a2 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 21 Feb 2022 15:24:16 -0500 Subject: [PATCH 224/600] Roll Flutter from 54a9d6528289 to 617de911982a (1 revision) (#4902) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 10b0279d7dca..2db38147e55e 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -54a9d652828941dbebbe8de32cf667e89578b13d +617de911982a07d0edd484dd017fa7470e23cb1c From 0da533e927c7bd7c095f0f73731c37bcc81703a4 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 23 Feb 2022 14:59:16 -0500 Subject: [PATCH 225/600] Roll Flutter from 617de911982a to 3b05d806222a (2 revisions) (#4905) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 2db38147e55e..fb28fa78643f 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -617de911982a07d0edd484dd017fa7470e23cb1c +3b05d806222a75a5076ffaeec4d93c95e3bb72ea From 5fc1fa7efd8fe8281500ab669f2d4b308eda55e0 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Wed, 23 Feb 2022 21:39:23 +0100 Subject: [PATCH 226/600] [in_app_purchase] Updates the dependency on in_app_purchase_storekit. (#4910) --- packages/in_app_purchase/in_app_purchase/CHANGELOG.md | 7 ++++++- packages/in_app_purchase/in_app_purchase/pubspec.yaml | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md index 232dacf0b71e..588993a01fd0 100644 --- a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md @@ -1,3 +1,8 @@ +## 3.0.0 + +* **BREAKING CHANGE** Updates `restorePurchases` to emit an empty list of purchases on StoreKit when there are no purchases to restore (same as Android). + * This change was listed in the CHANGELOG for 2.0.0, but the change was accidentally not included in 2.0.0. + ## 2.0.1 * Removes the instructions on initializing the plugin since this functionality is deprecated. @@ -6,7 +11,7 @@ * **BREAKING CHANGES**: * Adds a new `PurchaseStatus` named `canceled`. This means developers can distinguish between an error and user cancellation. - * Updates `restorePurchases` to emit an empty list of purchases on StoreKit when there are no purchases to restore (same as Android). + * ~~Updates `restorePurchases` to emit an empty list of purchases on StoreKit when there are no purchases to restore (same as Android).~~ * Renames `in_app_purchase_ios` to `in_app_purchase_storekit`. * Renames `InAppPurchaseIosPlatform` to `InAppPurchaseStoreKitPlatform`. * Renames `InAppPurchaseIosPlatformAddition` to diff --git a/packages/in_app_purchase/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/pubspec.yaml index b18853be2372..80c150d3f9e2 100644 --- a/packages/in_app_purchase/in_app_purchase/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase description: A Flutter plugin for in-app purchases. Exposes APIs for making in-app purchases through the App Store and Google Play. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 2.0.1 +version: 3.0.0 environment: sdk: ">=2.12.0 <3.0.0" @@ -21,7 +21,7 @@ dependencies: sdk: flutter in_app_purchase_platform_interface: ^1.0.0 in_app_purchase_android: ^0.2.1 - in_app_purchase_storekit: ^0.2.0 + in_app_purchase_storekit: ^0.3.0+1 dev_dependencies: flutter_driver: From 3daf0b7f14c5db845d4f88dd3d07293bba6cc94b Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 23 Feb 2022 16:24:22 -0500 Subject: [PATCH 227/600] Roll Flutter from 3b05d806222a to c74a646b7bba (21 revisions) (#4913) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index fb28fa78643f..c09c5a5d6212 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -3b05d806222a75a5076ffaeec4d93c95e3bb72ea +c74a646b7bba1641d0b2b29f6024dc867844e94c From cc868bc868a0fdbf1cb785f1c96d616a2c332e62 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 24 Feb 2022 12:26:23 -0500 Subject: [PATCH 228/600] [video_player] Update app-facing package analysis options (#4786) --- .../video_player/video_player/CHANGELOG.md | 4 + .../video_player/analysis_options.yaml | 1 - .../controller_swap_test.dart | 31 +++--- .../integration_test/video_player_test.dart | 23 ++--- .../video_player/example/lib/main.dart | 64 ++++++------- .../video_player/example/pubspec.yaml | 5 +- .../test_driver/video_player_test.dart | 4 +- .../lib/src/closed_caption_file.dart | 8 +- .../video_player/lib/src/sub_rip.dart | 10 +- .../video_player/lib/src/web_vtt.dart | 26 ++--- .../video_player/lib/video_player.dart | 57 +++++------ .../video_player/video_player/pubspec.yaml | 5 +- .../test/closed_caption_file_test.dart | 2 +- .../video_player/test/sub_rip_file_test.dart | 20 ++-- .../video_player_initialization_test.dart | 3 +- .../video_player/test/video_player_test.dart | 96 ++++++++++--------- .../video_player/test/web_vtt_test.dart | 42 ++++---- script/configs/custom_analysis.yaml | 1 - 18 files changed, 205 insertions(+), 197 deletions(-) delete mode 100644 packages/video_player/video_player/analysis_options.yaml diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index adf696ad0120..ee0accb22c79 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.2.19 + +* Internal code cleanup for stricter analysis options. + ## 2.2.18 * Moves Android and iOS implementations to federated packages. diff --git a/packages/video_player/video_player/analysis_options.yaml b/packages/video_player/video_player/analysis_options.yaml deleted file mode 100644 index 5aeb4e7c5e21..000000000000 --- a/packages/video_player/video_player/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../../analysis_options_legacy.yaml diff --git a/packages/video_player/video_player/example/integration_test/controller_swap_test.dart b/packages/video_player/video_player/example/integration_test/controller_swap_test.dart index 847807635f14..f80e600bcff5 100644 --- a/packages/video_player/video_player/example/integration_test/controller_swap_test.dart +++ b/packages/video_player/video_player/example/integration_test/controller_swap_test.dart @@ -6,8 +6,8 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:integration_test/integration_test.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; import 'package:video_player/video_player.dart'; const Duration _playDuration = Duration(seconds: 1); @@ -18,13 +18,13 @@ void main() { 'can substitute one controller by another without crashing', (WidgetTester tester) async { // Use WebM for web to allow CI to use Chromium. - final String videoAssetKey = + const String videoAssetKey = kIsWeb ? 'assets/Butterfly-209.webm' : 'assets/Butterfly-209.mp4'; - VideoPlayerController controller = VideoPlayerController.asset( + final VideoPlayerController controller = VideoPlayerController.asset( videoAssetKey, ); - VideoPlayerController another = VideoPlayerController.asset( + final VideoPlayerController another = VideoPlayerController.asset( videoAssetKey, ); await controller.initialize(); @@ -32,18 +32,16 @@ void main() { await controller.setVolume(0); await another.setVolume(0); - final Completer started = Completer(); - final Completer ended = Completer(); - bool startedBuffering = false; - bool endedBuffering = false; + final Completer started = Completer(); + final Completer ended = Completer(); another.addListener(() { - if (another.value.isBuffering && !startedBuffering) { - startedBuffering = true; + if (another.value.isBuffering && !started.isCompleted) { started.complete(); } - if (startedBuffering && !another.value.isBuffering && !endedBuffering) { - endedBuffering = true; + if (started.isCompleted && + !another.value.isBuffering && + !ended.isCompleted) { ended.complete(); } }); @@ -69,11 +67,8 @@ void main() { expect(another.value.position, (Duration position) => position > const Duration(seconds: 0)); - await started; - expect(startedBuffering, true); - - await ended; - expect(endedBuffering, true); + await expectLater(started.future, completes); + await expectLater(ended.future, completes); }, skip: !(kIsWeb || defaultTargetPlatform == TargetPlatform.android), ); @@ -86,7 +81,7 @@ Widget renderVideoWidget(VideoPlayerController controller) { textDirection: TextDirection.ltr, child: Center( child: AspectRatio( - key: Key('same'), + key: const Key('same'), aspectRatio: controller.value.aspectRatio, child: VideoPlayer(controller), ), 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 0c2252bf7447..746c63fcbfd6 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 @@ -17,7 +17,7 @@ import 'package:video_player/video_player.dart'; const Duration _playDuration = Duration(seconds: 1); // Use WebM for web to allow CI to use Chromium. -final String _videoAssetKey = +const String _videoAssetKey = kIsWeb ? 'assets/Butterfly-209.webm' : 'assets/Butterfly-209.mp4'; // Returns the URL to load an asset from this example app as a network source. @@ -48,13 +48,14 @@ void main() { expect(_controller.value.isPlaying, false); // The WebM version has a slightly different duration than the MP4. expect(_controller.value.duration, - Duration(seconds: 7, milliseconds: kIsWeb ? 544 : 540)); + const Duration(seconds: 7, milliseconds: kIsWeb ? 544 : 540)); }); testWidgets( 'live stream duration != 0', (WidgetTester tester) async { - VideoPlayerController networkController = VideoPlayerController.network( + final VideoPlayerController networkController = + VideoPlayerController.network( 'https://cph-p2p-msl.akamaized.net/hls/live/2000341/test/master.m3u8', ); await networkController.initialize(); @@ -65,7 +66,7 @@ void main() { expect(networkController.value.duration, (Duration duration) => duration != Duration.zero); }, - skip: (kIsWeb), + skip: kIsWeb, ); testWidgets( @@ -124,7 +125,7 @@ void main() { // Mute to allow playing without DOM interaction on Web. // See https://developers.google.com/web/updates/2017/09/autoplay-policy-changes await _controller.setVolume(0); - Duration tenMillisBeforeEnd = + final Duration tenMillisBeforeEnd = _controller.value.duration - const Duration(milliseconds: 10); await _controller.seekTo(tenMillisBeforeEnd); await _controller.play(); @@ -203,12 +204,12 @@ void main() { group('file-based videos', () { setUp(() async { // Load the data from the asset. - String tempDir = (await getTemporaryDirectory()).path; - ByteData bytes = await rootBundle.load(_videoAssetKey); + final String tempDir = (await getTemporaryDirectory()).path; + final ByteData bytes = await rootBundle.load(_videoAssetKey); // Write it to a file to use as a source. final String filename = _videoAssetKey.split('/').last; - File file = File('$tempDir/$filename'); + final File file = File('$tempDir/$filename'); await file.writeAsBytes(bytes.buffer.asInt8List()); _controller = VideoPlayerController.file(file); @@ -244,8 +245,8 @@ void main() { // Mute to allow playing without DOM interaction on Web. // See https://developers.google.com/web/updates/2017/09/autoplay-policy-changes await _controller.setVolume(0); - final Completer started = Completer(); - final Completer ended = Completer(); + final Completer started = Completer(); + final Completer ended = Completer(); _controller.addListener(() { if (!started.isCompleted && _controller.value.isBuffering) { started.complete(); @@ -291,7 +292,7 @@ void main() { // The audio was made with 44100 Hz, 192 Kbps CBR, and 32 bits. expect( _controller.value.duration, - Duration(seconds: 5, milliseconds: kIsWeb ? 42 : 41), + const Duration(seconds: 5, milliseconds: kIsWeb ? 42 : 41), ); }); diff --git a/packages/video_player/video_player/example/lib/main.dart b/packages/video_player/video_player/example/lib/main.dart index 9297cc69470c..5d496a9f5e7d 100644 --- a/packages/video_player/video_player/example/lib/main.dart +++ b/packages/video_player/video_player/example/lib/main.dart @@ -47,10 +47,10 @@ class _App extends StatelessWidget { tabs: [ Tab( icon: Icon(Icons.cloud), - text: "Remote", + text: 'Remote', ), - Tab(icon: Icon(Icons.insert_drive_file), text: "Asset"), - Tab(icon: Icon(Icons.list), text: "List example"), + Tab(icon: Icon(Icons.insert_drive_file), text: 'Asset'), + Tab(icon: Icon(Icons.list), text: 'List example'), ], ), ), @@ -71,20 +71,20 @@ class _ButterFlyAssetVideoInList extends StatelessWidget { Widget build(BuildContext context) { return ListView( children: [ - _ExampleCard(title: "Item a"), - _ExampleCard(title: "Item b"), - _ExampleCard(title: "Item c"), - _ExampleCard(title: "Item d"), - _ExampleCard(title: "Item e"), - _ExampleCard(title: "Item f"), - _ExampleCard(title: "Item g"), + const _ExampleCard(title: 'Item a'), + const _ExampleCard(title: 'Item b'), + const _ExampleCard(title: 'Item c'), + const _ExampleCard(title: 'Item d'), + const _ExampleCard(title: 'Item e'), + const _ExampleCard(title: 'Item f'), + const _ExampleCard(title: 'Item g'), Card( child: Column(children: [ Column( children: [ const ListTile( leading: Icon(Icons.cake), - title: Text("Video video"), + title: Text('Video video'), ), Stack( alignment: FractionalOffset.bottomRight + @@ -96,11 +96,11 @@ class _ButterFlyAssetVideoInList extends StatelessWidget { ], ), ])), - _ExampleCard(title: "Item h"), - _ExampleCard(title: "Item i"), - _ExampleCard(title: "Item j"), - _ExampleCard(title: "Item k"), - _ExampleCard(title: "Item l"), + const _ExampleCard(title: 'Item h'), + const _ExampleCard(title: 'Item i'), + const _ExampleCard(title: 'Item j'), + const _ExampleCard(title: 'Item k'), + const _ExampleCard(title: 'Item l'), ], ); } @@ -269,7 +269,7 @@ class _ControlsOverlay extends StatelessWidget { const _ControlsOverlay({Key? key, required this.controller}) : super(key: key); - static const _exampleCaptionOffsets = [ + static const List _exampleCaptionOffsets = [ Duration(seconds: -10), Duration(seconds: -3), Duration(seconds: -1, milliseconds: -500), @@ -280,7 +280,7 @@ class _ControlsOverlay extends StatelessWidget { Duration(seconds: 3), Duration(seconds: 10), ]; - static const _examplePlaybackRates = [ + static const List _examplePlaybackRates = [ 0.25, 0.5, 1.0, @@ -298,13 +298,13 @@ class _ControlsOverlay extends StatelessWidget { return Stack( children: [ AnimatedSwitcher( - duration: Duration(milliseconds: 50), - reverseDuration: Duration(milliseconds: 200), + duration: const Duration(milliseconds: 50), + reverseDuration: const Duration(milliseconds: 200), child: controller.value.isPlaying - ? SizedBox.shrink() + ? const SizedBox.shrink() : Container( color: Colors.black26, - child: Center( + child: const Center( child: Icon( Icons.play_arrow, color: Colors.white, @@ -324,13 +324,13 @@ class _ControlsOverlay extends StatelessWidget { child: PopupMenuButton( initialValue: controller.value.captionOffset, tooltip: 'Caption Offset', - onSelected: (delay) { + onSelected: (Duration delay) { controller.setCaptionOffset(delay); }, - itemBuilder: (context) { - return [ - for (final offsetDuration in _exampleCaptionOffsets) - PopupMenuItem( + itemBuilder: (BuildContext context) { + return >[ + for (final Duration offsetDuration in _exampleCaptionOffsets) + PopupMenuItem( value: offsetDuration, child: Text('${offsetDuration.inMilliseconds}ms'), ) @@ -353,13 +353,13 @@ class _ControlsOverlay extends StatelessWidget { child: PopupMenuButton( initialValue: controller.value.playbackSpeed, tooltip: 'Playback speed', - onSelected: (speed) { + onSelected: (double speed) { controller.setPlaybackSpeed(speed); }, - itemBuilder: (context) { - return [ - for (final speed in _examplePlaybackRates) - PopupMenuItem( + itemBuilder: (BuildContext context) { + return >[ + for (final double speed in _examplePlaybackRates) + PopupMenuItem( value: speed, child: Text('${speed}x'), ) diff --git a/packages/video_player/video_player/example/pubspec.yaml b/packages/video_player/video_player/example/pubspec.yaml index eef6eb7ab63b..6032f3ceed65 100644 --- a/packages/video_player/video_player/example/pubspec.yaml +++ b/packages/video_player/video_player/example/pubspec.yaml @@ -18,14 +18,13 @@ dependencies: path: ../ dev_dependencies: - flutter_test: - sdk: flutter flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter path_provider: ^2.0.6 - pedantic: ^1.10.0 test: any flutter: diff --git a/packages/video_player/video_player/example/test_driver/video_player_test.dart b/packages/video_player/video_player/example/test_driver/video_player_test.dart index 1d5ac79c77bf..5fbed804d8d8 100644 --- a/packages/video_player/video_player/example/test_driver/video_player_test.dart +++ b/packages/video_player/video_player/example/test_driver/video_player_test.dart @@ -12,8 +12,8 @@ Future main() async { await driver.close(); }); - //TODO(cyanglaz): Use TabBar tabs to navigate between pages after https://github.com/flutter/flutter/issues/16991 is fixed. - //TODO(cyanglaz): Un-skip the test after https://github.com/flutter/flutter/issues/43012 is fixed + // TODO(cyanglaz): Use TabBar tabs to navigate between pages after https://github.com/flutter/flutter/issues/16991 is fixed. + // TODO(cyanglaz): Un-skip the test after https://github.com/flutter/flutter/issues/43012 is fixed test('Push a page contains video and pop back, do not crash.', () async { final SerializableFinder pushTab = find.byValueKey('push_tab'); await driver.waitFor(pushTab); diff --git a/packages/video_player/video_player/lib/src/closed_caption_file.dart b/packages/video_player/video_player/lib/src/closed_caption_file.dart index e410e2652ad3..324ffc471ffe 100644 --- a/packages/video_player/video_player/lib/src/closed_caption_file.dart +++ b/packages/video_player/video_player/lib/src/closed_caption_file.dart @@ -2,10 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'sub_rip.dart'; -export 'sub_rip.dart' show SubRipCaptionFile; +import 'package:flutter/foundation.dart' show objectRuntimeType; +import 'sub_rip.dart'; import 'web_vtt.dart'; + +export 'sub_rip.dart' show SubRipCaptionFile; export 'web_vtt.dart' show WebVTTCaptionFile; /// A structured representation of a parsed closed caption file. @@ -66,7 +68,7 @@ class Caption { @override String toString() { - return '$runtimeType(' + return '${objectRuntimeType(this, 'Caption')}(' 'number: $number, ' 'start: $start, ' 'end: $end, ' diff --git a/packages/video_player/video_player/lib/src/sub_rip.dart b/packages/video_player/video_player/lib/src/sub_rip.dart index 5d6863f72bb8..7b807cd4d5d9 100644 --- a/packages/video_player/video_player/lib/src/sub_rip.dart +++ b/packages/video_player/video_player/lib/src/sub_rip.dart @@ -28,8 +28,10 @@ class SubRipCaptionFile extends ClosedCaptionFile { List _parseCaptionsFromSubRipString(String file) { final List captions = []; - for (List captionLines in _readSubRipFile(file)) { - if (captionLines.length < 3) break; + for (final List captionLines in _readSubRipFile(file)) { + if (captionLines.length < 3) { + break; + } final int captionNumber = int.parse(captionLines[0]); final _CaptionRange captionRange = @@ -52,11 +54,11 @@ List _parseCaptionsFromSubRipString(String file) { } class _CaptionRange { + _CaptionRange(this.start, this.end); + final Duration start; final Duration end; - _CaptionRange(this.start, this.end); - // Assumes format from an SubRip file. // For example: // 00:01:54,724 --> 00:01:56,760 diff --git a/packages/video_player/video_player/lib/src/web_vtt.dart b/packages/video_player/video_player/lib/src/web_vtt.dart index 6c4527d34d67..5527e62b69f1 100644 --- a/packages/video_player/video_player/lib/src/web_vtt.dart +++ b/packages/video_player/video_player/lib/src/web_vtt.dart @@ -5,9 +5,9 @@ import 'dart:convert'; import 'package:html/dom.dart'; +import 'package:html/parser.dart' as html_parser; import 'closed_caption_file.dart'; -import 'package:html/parser.dart' as html_parser; /// Represents a [ClosedCaptionFile], parsed from the WebVTT file format. /// See: https://en.wikipedia.org/wiki/WebVTT @@ -28,10 +28,10 @@ List _parseCaptionsFromWebVTTString(String file) { final List captions = []; // Ignore metadata - Set metadata = {'HEADER', 'NOTE', 'REGION', 'WEBVTT'}; + final Set metadata = {'HEADER', 'NOTE', 'REGION', 'WEBVTT'}; int captionNumber = 1; - for (List captionLines in _readWebVTTFile(file)) { + for (final List captionLines in _readWebVTTFile(file)) { // CaptionLines represent a complete caption. // E.g // [ @@ -39,14 +39,18 @@ List _parseCaptionsFromWebVTTString(String file) { // ['Introduction'] // ] // If caption has just header or time, but no text, `captionLines.length` will be 1. - if (captionLines.length < 2) continue; + if (captionLines.length < 2) { + continue; + } // If caption has header equal metadata, ignore. - String metadaType = captionLines[0].split(' ')[0]; - if (metadata.contains(metadaType)) continue; + final String metadaType = captionLines[0].split(' ')[0]; + if (metadata.contains(metadaType)) { + continue; + } // Caption has header - bool hasHeader = captionLines.length > 2; + final bool hasHeader = captionLines.length > 2; if (hasHeader) { final int? tryParseCaptionNumber = int.tryParse(captionLines[0]); if (tryParseCaptionNumber != null) { @@ -82,11 +86,11 @@ List _parseCaptionsFromWebVTTString(String file) { } class _CaptionRange { + _CaptionRange(this.start, this.end); + final Duration start; final Duration end; - _CaptionRange(this.start, this.end); - // Assumes format from an VTT file. // For example: // 00:09.000 --> 00:11.000 @@ -161,7 +165,7 @@ Duration? _parseWebVTTTimestamp(String timestampString) { return null; } - List milisecondsStyles = dotSections[1].split(" "); + final List milisecondsStyles = dotSections[1].split(' '); // TODO(cyanglaz): Handle caption styles. // https://github.com/flutter/flutter/issues/90009. @@ -172,7 +176,7 @@ Duration? _parseWebVTTTimestamp(String timestampString) { // ``` // For a better readable code style, style parsing should happen before // calling this method. See: https://github.com/flutter/plugins/pull/2878/files#r713381134. - int milliseconds = int.parse(milisecondsStyles[0]); + final int milliseconds = int.parse(milisecondsStyles[0]); return Duration( hours: hours, diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index 8fd216286fca..672ba2efcde3 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -10,16 +10,17 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; +import 'src/closed_caption_file.dart'; + export 'package:video_player_platform_interface/video_player_platform_interface.dart' show DurationRange, DataSourceType, VideoFormat, VideoPlayerOptions; -import 'src/closed_caption_file.dart'; export 'src/closed_caption_file.dart'; VideoPlayerPlatform? _lastVideoPlayerPlatform; VideoPlayerPlatform get _videoPlayerPlatform { - VideoPlayerPlatform currentInstance = VideoPlayerPlatform.instance; + final VideoPlayerPlatform currentInstance = VideoPlayerPlatform.instance; if (_lastVideoPlayerPlatform != currentInstance) { // This will clear all open videos on the platform when a full restart is // performed. @@ -32,10 +33,6 @@ VideoPlayerPlatform get _videoPlayerPlatform { /// The duration, current position, buffering state, error state and settings /// of a [VideoPlayerController]. class VideoPlayerValue { - /// This constant is just to indicate that parameter is not passed to [copyWith] - /// workaround for this issue https://github.com/dart-lang/language/issues/2009 - static const _defaultErrorDescription = 'defaultErrorDescription'; - /// Constructs a video with the given values. Only [duration] is required. The /// rest will initialize with default values when unset. VideoPlayerValue({ @@ -65,6 +62,10 @@ class VideoPlayerValue { isInitialized: false, errorDescription: errorDescription); + /// This constant is just to indicate that parameter is not passed to [copyWith] + /// workaround for this issue https://github.com/dart-lang/language/issues/2009 + static const String _defaultErrorDescription = 'defaultErrorDescription'; + /// The total duration of the video. /// /// The duration is [Duration.zero] if the video hasn't been initialized. @@ -172,7 +173,7 @@ class VideoPlayerValue { @override String toString() { - return '$runtimeType(' + return '${objectRuntimeType(this, 'VideoPlayerValue')}(' 'duration: $duration, ' 'size: $size, ' 'position: $position, ' @@ -209,7 +210,7 @@ class VideoPlayerController extends ValueNotifier { {this.package, this.closedCaptionFile, this.videoPlayerOptions}) : dataSourceType = DataSourceType.asset, formatHint = null, - httpHeaders = const {}, + httpHeaders = const {}, super(VideoPlayerValue(duration: Duration.zero)); /// Constructs a [VideoPlayerController] playing a video from obtained from @@ -226,7 +227,7 @@ class VideoPlayerController extends ValueNotifier { this.formatHint, this.closedCaptionFile, this.videoPlayerOptions, - this.httpHeaders = const {}, + this.httpHeaders = const {}, }) : dataSourceType = DataSourceType.network, package = null, super(VideoPlayerValue(duration: Duration.zero)); @@ -241,7 +242,7 @@ class VideoPlayerController extends ValueNotifier { dataSourceType = DataSourceType.file, package = null, formatHint = null, - httpHeaders = const {}, + httpHeaders = const {}, super(VideoPlayerValue(duration: Duration.zero)); /// Constructs a [VideoPlayerController] playing a video from a contentUri. @@ -256,7 +257,7 @@ class VideoPlayerController extends ValueNotifier { dataSourceType = DataSourceType.contentUri, package = null, formatHint = null, - httpHeaders = const {}, + httpHeaders = const {}, super(VideoPlayerValue(duration: Duration.zero)); /// The URI to the video file. This will be in different formats depending on @@ -393,9 +394,7 @@ class VideoPlayerController extends ValueNotifier { } if (closedCaptionFile != null) { - if (_closedCaptionFile == null) { - _closedCaptionFile = await closedCaptionFile; - } + _closedCaptionFile ??= await closedCaptionFile; value = value.copyWith(caption: _getCaptionAt(value.position)); } @@ -513,7 +512,9 @@ class VideoPlayerController extends ValueNotifier { // Setting the playback speed on iOS will trigger the video to play. We // prevent this from happening by not applying the playback speed until // the video is manually played from Flutter. - if (!value.isPlaying) return; + if (!value.isPlaying) { + return; + } await _videoPlayerPlatform.setPlaybackSpeed( _textureId, @@ -618,9 +619,9 @@ class VideoPlayerController extends ValueNotifier { return Caption.none; } - final delayedPosition = position + value.captionOffset; - // TODO: This would be more efficient as a binary search. - for (final caption in _closedCaptionFile!.captions) { + final Duration delayedPosition = position + value.captionOffset; + // TODO(johnsonmh): This would be more efficient as a binary search. + for (final Caption caption in _closedCaptionFile!.captions) { if (caption.start <= delayedPosition && caption.end >= delayedPosition) { return caption; } @@ -682,7 +683,7 @@ class _VideoAppLifeCycleObserver extends Object with WidgetsBindingObserver { /// Widget that displays the video controlled by [controller]. class VideoPlayer extends StatefulWidget { /// Uses the given [controller] for all video rendered in this widget. - VideoPlayer(this.controller); + const VideoPlayer(this.controller); /// The [VideoPlayerController] responsible for the video being rendered in /// this widget. @@ -780,7 +781,7 @@ class VideoProgressColors { } class _VideoScrubber extends StatefulWidget { - _VideoScrubber({ + const _VideoScrubber({ required this.child, required this.controller, }); @@ -800,7 +801,7 @@ class _VideoScrubberState extends State<_VideoScrubber> { @override Widget build(BuildContext context) { void seekToRelativePosition(Offset globalPosition) { - final RenderBox box = context.findRenderObject() as RenderBox; + final RenderBox box = context.findRenderObject()! as RenderBox; final Offset tapPos = box.globalToLocal(globalPosition); final double relative = tapPos.dx / box.size.width; final Duration position = controller.value.duration * relative; @@ -855,7 +856,7 @@ class VideoProgressIndicator extends StatefulWidget { /// Defaults will be used for everything except [controller] if they're not /// provided. [allowScrubbing] defaults to false, and [padding] will default /// to `top: 5.0`. - VideoProgressIndicator( + const VideoProgressIndicator( this.controller, { this.colors = const VideoProgressColors(), required this.allowScrubbing, @@ -923,7 +924,7 @@ class _VideoProgressIndicatorState extends State { final int position = controller.value.position.inMilliseconds; int maxBuffering = 0; - for (DurationRange range in controller.value.buffered) { + for (final DurationRange range in controller.value.buffered) { final int end = range.end.inMilliseconds; if (end > maxBuffering) { maxBuffering = end; @@ -1005,9 +1006,9 @@ class ClosedCaption extends StatelessWidget { @override Widget build(BuildContext context) { - final text = this.text; + final String? text = this.text; if (text == null || text.isEmpty) { - return SizedBox.shrink(); + return const SizedBox.shrink(); } final TextStyle effectiveTextStyle = textStyle ?? @@ -1019,14 +1020,14 @@ class ClosedCaption extends StatelessWidget { return Align( alignment: Alignment.bottomCenter, child: Padding( - padding: EdgeInsets.only(bottom: 24.0), + padding: const EdgeInsets.only(bottom: 24.0), child: DecoratedBox( decoration: BoxDecoration( - color: Color(0xB8000000), + color: const Color(0xB8000000), borderRadius: BorderRadius.circular(2.0), ), child: Padding( - padding: EdgeInsets.symmetric(horizontal: 2.0), + padding: const EdgeInsets.symmetric(horizontal: 2.0), child: Text(text, style: effectiveTextStyle), ), ), diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index a444fc69c912..2f6e28919d00 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for displaying inline video with other Flutter widgets on Android, iOS, and web. repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.2.18 +version: 2.2.19 environment: sdk: ">=2.14.0 <3.0.0" @@ -22,13 +22,12 @@ flutter: dependencies: flutter: sdk: flutter + html: ^0.15.0 video_player_android: ^2.2.17 video_player_avfoundation: ^2.2.17 video_player_platform_interface: ">=4.2.0 <6.0.0" video_player_web: ^2.0.0 - html: ^0.15.0 dev_dependencies: flutter_test: sdk: flutter - pedantic: ^1.10.0 diff --git a/packages/video_player/video_player/test/closed_caption_file_test.dart b/packages/video_player/video_player/test/closed_caption_file_test.dart index 369a3b362557..b5c0a8e1db12 100644 --- a/packages/video_player/video_player/test/closed_caption_file_test.dart +++ b/packages/video_player/video_player/test/closed_caption_file_test.dart @@ -8,7 +8,7 @@ import 'package:video_player/src/closed_caption_file.dart'; void main() { group('ClosedCaptionFile', () { test('toString()', () { - final Caption caption = const Caption( + const Caption caption = Caption( number: 1, start: Duration(seconds: 1), end: Duration(seconds: 2), diff --git a/packages/video_player/video_player/test/sub_rip_file_test.dart b/packages/video_player/video_player/test/sub_rip_file_test.dart index 5808e0b9d2e3..ea3bfda036ec 100644 --- a/packages/video_player/video_player/test/sub_rip_file_test.dart +++ b/packages/video_player/video_player/test/sub_rip_file_test.dart @@ -14,19 +14,19 @@ void main() { final Caption firstCaption = parsedFile.captions.first; expect(firstCaption.number, 1); - expect(firstCaption.start, Duration(seconds: 6)); - expect(firstCaption.end, Duration(seconds: 12, milliseconds: 74)); + expect(firstCaption.start, const Duration(seconds: 6)); + expect(firstCaption.end, const Duration(seconds: 12, milliseconds: 74)); expect(firstCaption.text, 'This is a test file'); final Caption secondCaption = parsedFile.captions[1]; expect(secondCaption.number, 2); expect( secondCaption.start, - Duration(minutes: 1, seconds: 54, milliseconds: 724), + const Duration(minutes: 1, seconds: 54, milliseconds: 724), ); expect( secondCaption.end, - Duration(minutes: 1, seconds: 56, milliseconds: 760), + const Duration(minutes: 1, seconds: 56, milliseconds: 760), ); expect(secondCaption.text, '- Hello.\n- Yes?'); @@ -34,11 +34,11 @@ void main() { expect(thirdCaption.number, 3); expect( thirdCaption.start, - Duration(minutes: 1, seconds: 56, milliseconds: 884), + const Duration(minutes: 1, seconds: 56, milliseconds: 884), ); expect( thirdCaption.end, - Duration(minutes: 1, seconds: 58, milliseconds: 954), + const Duration(minutes: 1, seconds: 58, milliseconds: 954), ); expect( thirdCaption.text, @@ -49,11 +49,11 @@ void main() { expect(fourthCaption.number, 4); expect( fourthCaption.start, - Duration(hours: 1, minutes: 1, seconds: 59, milliseconds: 84), + const Duration(hours: 1, minutes: 1, seconds: 59, milliseconds: 84), ); expect( fourthCaption.end, - Duration(hours: 1, minutes: 2, seconds: 1, milliseconds: 552), + const Duration(hours: 1, minutes: 2, seconds: 1, milliseconds: 552), ); expect( fourthCaption.text, @@ -68,8 +68,8 @@ void main() { final Caption firstCaption = parsedFile.captions.single; expect(firstCaption.number, 2); - expect(firstCaption.start, Duration(seconds: 15)); - expect(firstCaption.end, Duration(seconds: 17, milliseconds: 74)); + expect(firstCaption.start, const Duration(seconds: 15)); + expect(firstCaption.end, const Duration(seconds: 17, milliseconds: 74)); expect(firstCaption.text, 'This one is valid'); }); } diff --git a/packages/video_player/video_player/test/video_player_initialization_test.dart b/packages/video_player/video_player/test/video_player_initialization_test.dart index 1870934a931e..af0886fdec18 100644 --- a/packages/video_player/video_player/test/video_player_initialization_test.dart +++ b/packages/video_player/video_player/test/video_player_initialization_test.dart @@ -13,7 +13,8 @@ void main() { // in this file. test('plugin initialized', () async { TestWidgetsFlutterBinding.ensureInitialized(); - FakeVideoPlayerPlatform fakeVideoPlayerPlatform = FakeVideoPlayerPlatform(); + final FakeVideoPlayerPlatform fakeVideoPlayerPlatform = + FakeVideoPlayerPlatform(); VideoPlayerPlatform.instance = fakeVideoPlayerPlatform; final VideoPlayerController controller = VideoPlayerController.network( diff --git a/packages/video_player/video_player/test/video_player_test.dart b/packages/video_player/video_player/test/video_player_test.dart index 3bce821e8fe3..07afd0f65402 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -29,7 +29,7 @@ class FakeController extends ValueNotifier String get dataSource => ''; @override - Map get httpHeaders => {}; + Map get httpHeaders => {}; @override DataSourceType get dataSourceType => DataSourceType.file; @@ -81,13 +81,13 @@ class _FakeClosedCaptionFile extends ClosedCaptionFile { @override List get captions { return [ - Caption( + const Caption( text: 'one', number: 0, start: Duration(milliseconds: 100), end: Duration(milliseconds: 200), ), - Caption( + const Caption( text: 'two', number: 1, start: Duration(milliseconds: 300), @@ -135,8 +135,9 @@ void main() { group('ClosedCaption widget', () { testWidgets('uses a default text style', (WidgetTester tester) async { - final String text = 'foo'; - await tester.pumpWidget(MaterialApp(home: ClosedCaption(text: text))); + const String text = 'foo'; + await tester + .pumpWidget(const MaterialApp(home: ClosedCaption(text: text))); final Text textWidget = tester.widget(find.text(text)); expect(textWidget.style!.fontSize, 36.0); @@ -144,9 +145,9 @@ void main() { }); testWidgets('uses given text and style', (WidgetTester tester) async { - final String text = 'foo'; - final TextStyle textStyle = TextStyle(fontSize: 14.725); - await tester.pumpWidget(MaterialApp( + const String text = 'foo'; + const TextStyle textStyle = TextStyle(fontSize: 14.725); + await tester.pumpWidget(const MaterialApp( home: ClosedCaption( text: text, textStyle: textStyle, @@ -159,19 +160,20 @@ void main() { }); testWidgets('handles null text', (WidgetTester tester) async { - await tester.pumpWidget(MaterialApp(home: ClosedCaption(text: null))); + await tester + .pumpWidget(const MaterialApp(home: ClosedCaption(text: null))); expect(find.byType(Text), findsNothing); }); testWidgets('handles empty text', (WidgetTester tester) async { - await tester.pumpWidget(MaterialApp(home: ClosedCaption(text: ''))); + await tester.pumpWidget(const MaterialApp(home: ClosedCaption(text: ''))); expect(find.byType(Text), findsNothing); }); testWidgets('Passes text contrast ratio guidelines', (WidgetTester tester) async { - final String text = 'foo'; - await tester.pumpWidget(MaterialApp( + const String text = 'foo'; + await tester.pumpWidget(const MaterialApp( home: Scaffold( backgroundColor: Colors.white, body: ClosedCaption(text: text), @@ -218,7 +220,7 @@ void main() { ); expect( fakeVideoPlayerPlatform.dataSources[0].httpHeaders, - {}, + {}, ); }); @@ -246,7 +248,7 @@ void main() { test('network with some headers', () async { final VideoPlayerController controller = VideoPlayerController.network( 'https://127.0.0.1', - httpHeaders: {'Authorization': 'Bearer token'}, + httpHeaders: {'Authorization': 'Bearer token'}, ); await controller.initialize(); @@ -260,7 +262,7 @@ void main() { ); expect( fakeVideoPlayerPlatform.dataSources[0].httpHeaders, - {'Authorization': 'Bearer token'}, + {'Authorization': 'Bearer token'}, ); }); @@ -269,10 +271,10 @@ void main() { 'http://testing.com/invalid_url', ); - late dynamic error; + late Object error; fakeVideoPlayerPlatform.forceInitError = true; - await controller.initialize().catchError((dynamic e) => error = e); - final PlatformException platformEx = error; + await controller.initialize().catchError((Object e) => error = e); + final PlatformException platformEx = error as PlatformException; expect(platformEx.code, equals('VideoError')); }); @@ -493,7 +495,7 @@ void main() { 'https://127.0.0.1', ); await controller.initialize(); - final progressWidget = + final VideoProgressIndicator progressWidget = VideoProgressIndicator(controller, allowScrubbing: true); await tester.pumpWidget(Directionality( @@ -505,7 +507,7 @@ void main() { expect(controller.value.isPlaying, isTrue); final Rect progressRect = tester.getRect(find.byWidget(progressWidget)); - await tester.dragFrom(progressRect.center, Offset(1.0, 0.0)); + await tester.dragFrom(progressRect.center, const Offset(1.0, 0.0)); await tester.pumpAndSettle(); expect(controller.value.position, lessThan(controller.value.duration)); @@ -520,7 +522,7 @@ void main() { 'https://127.0.0.1', ); await controller.initialize(); - final progressWidget = + final VideoProgressIndicator progressWidget = VideoProgressIndicator(controller, allowScrubbing: true); await tester.pumpWidget(Directionality( @@ -580,7 +582,7 @@ void main() { ); await controller.initialize(); - controller.setCaptionOffset(Duration(milliseconds: 100)); + controller.setCaptionOffset(const Duration(milliseconds: 100)); expect(controller.value.position, const Duration()); expect(controller.value.caption.text, ''); @@ -616,7 +618,7 @@ void main() { ); await controller.initialize(); - controller.setCaptionOffset(Duration(milliseconds: -100)); + controller.setCaptionOffset(const Duration(milliseconds: -100)); expect(controller.value.position, const Duration()); expect(controller.value.caption.text, ''); @@ -688,12 +690,11 @@ void main() { const Duration bufferStart = Duration(seconds: 0); const Duration bufferEnd = Duration(milliseconds: 500); - fakeVideoEventStream - ..add(VideoEvent( - eventType: VideoEventType.bufferingUpdate, - buffered: [ - DurationRange(bufferStart, bufferEnd), - ])); + fakeVideoEventStream.add(VideoEvent( + eventType: VideoEventType.bufferingUpdate, + buffered: [ + DurationRange(bufferStart, bufferEnd), + ])); await tester.pumpAndSettle(); expect(controller.value.isBuffering, isTrue); expect(controller.value.buffered.length, 1); @@ -854,45 +855,45 @@ void main() { group('aspectRatio', () { test('640x480 -> 4:3', () { - final value = VideoPlayerValue( + final VideoPlayerValue value = VideoPlayerValue( isInitialized: true, - size: Size(640, 480), - duration: Duration(seconds: 1), + size: const Size(640, 480), + duration: const Duration(seconds: 1), ); expect(value.aspectRatio, 4 / 3); }); test('no size -> 1.0', () { - final value = VideoPlayerValue( + final VideoPlayerValue value = VideoPlayerValue( isInitialized: true, - duration: Duration(seconds: 1), + duration: const Duration(seconds: 1), ); expect(value.aspectRatio, 1.0); }); test('height = 0 -> 1.0', () { - final value = VideoPlayerValue( + final VideoPlayerValue value = VideoPlayerValue( isInitialized: true, - size: Size(640, 0), - duration: Duration(seconds: 1), + size: const Size(640, 0), + duration: const Duration(seconds: 1), ); expect(value.aspectRatio, 1.0); }); test('width = 0 -> 1.0', () { - final value = VideoPlayerValue( + final VideoPlayerValue value = VideoPlayerValue( isInitialized: true, - size: Size(0, 480), - duration: Duration(seconds: 1), + size: const Size(0, 480), + duration: const Duration(seconds: 1), ); expect(value.aspectRatio, 1.0); }); test('negative aspect ratio -> 1.0', () { - final value = VideoPlayerValue( + final VideoPlayerValue value = VideoPlayerValue( isInitialized: true, - size: Size(640, -480), - duration: Duration(seconds: 1), + size: const Size(640, -480), + duration: const Duration(seconds: 1), ); expect(value.aspectRatio, 1.0); }); @@ -904,7 +905,7 @@ void main() { const Color bufferedColor = Color.fromRGBO(0, 255, 0, 0.5); const Color backgroundColor = Color.fromRGBO(255, 255, 0, 0.25); - final VideoProgressColors colors = VideoProgressColors( + const VideoProgressColors colors = VideoProgressColors( playedColor: playedColor, bufferedColor: bufferedColor, backgroundColor: backgroundColor); @@ -936,7 +937,7 @@ class FakeVideoPlayerPlatform extends VideoPlayerPlatform { @override Future create(DataSource dataSource) async { calls.add('create'); - StreamController stream = StreamController(); + final StreamController stream = StreamController(); streams[nextTextureId] = stream; if (forceInitError) { stream.addError(PlatformException( @@ -944,8 +945,8 @@ class FakeVideoPlayerPlatform extends VideoPlayerPlatform { } else { stream.add(VideoEvent( eventType: VideoEventType.initialized, - size: Size(100, 100), - duration: Duration(seconds: 1))); + size: const Size(100, 100), + duration: const Duration(seconds: 1))); } dataSources.add(dataSource); return nextTextureId++; @@ -962,6 +963,7 @@ class FakeVideoPlayerPlatform extends VideoPlayerPlatform { initialized.complete(true); } + @override Stream videoEventsFor(int textureId) { return streams[textureId]!.stream; } diff --git a/packages/video_player/video_player/test/web_vtt_test.dart b/packages/video_player/video_player/test/web_vtt_test.dart index 59fce98c5b71..bde629219484 100644 --- a/packages/video_player/video_player/test/web_vtt_test.dart +++ b/packages/video_player/video_player/test/web_vtt_test.dart @@ -14,9 +14,9 @@ void main() { parsedFile = WebVTTCaptionFile(_valid_vtt_with_metadata); expect(parsedFile.captions.length, 1); - expect(parsedFile.captions[0].start, Duration(seconds: 1)); - expect( - parsedFile.captions[0].end, Duration(seconds: 2, milliseconds: 500)); + expect(parsedFile.captions[0].start, const Duration(seconds: 1)); + expect(parsedFile.captions[0].end, + const Duration(seconds: 2, milliseconds: 500)); expect(parsedFile.captions[0].text, 'We are in New York City'); }); @@ -25,11 +25,11 @@ void main() { expect(parsedFile.captions.length, 1); expect(parsedFile.captions[0].start, - Duration(seconds: 2, milliseconds: 800)); - expect( - parsedFile.captions[0].end, Duration(seconds: 3, milliseconds: 283)); + const Duration(seconds: 2, milliseconds: 800)); + expect(parsedFile.captions[0].end, + const Duration(seconds: 3, milliseconds: 283)); expect(parsedFile.captions[0].text, - "— It will perforate your stomach.\n— You could die."); + '— It will perforate your stomach.\n— You could die.'); }); test('with styles tags', () { @@ -37,9 +37,9 @@ void main() { expect(parsedFile.captions.length, 3); expect(parsedFile.captions[0].start, - Duration(seconds: 5, milliseconds: 200)); - expect( - parsedFile.captions[0].end, Duration(seconds: 6, milliseconds: 000)); + const Duration(seconds: 5, milliseconds: 200)); + expect(parsedFile.captions[0].end, + const Duration(seconds: 6, milliseconds: 000)); expect(parsedFile.captions[0].text, "You know I'm so excited my glasses are falling off here."); }); @@ -49,9 +49,9 @@ void main() { expect(parsedFile.captions.length, 3); expect(parsedFile.captions[0].number, 1); - expect(parsedFile.captions.last.start, Duration(seconds: 4)); - expect(parsedFile.captions.last.end, Duration(seconds: 5)); - expect(parsedFile.captions.last.text, "Transcrit par Célestes™"); + expect(parsedFile.captions.last.start, const Duration(seconds: 4)); + expect(parsedFile.captions.last.end, const Duration(seconds: 5)); + expect(parsedFile.captions.last.text, 'Transcrit par Célestes™'); }); test('with [hours]:[minutes]:[seconds].[milliseconds].', () { @@ -59,9 +59,9 @@ void main() { expect(parsedFile.captions.length, 1); expect(parsedFile.captions[0].number, 1); - expect(parsedFile.captions.last.start, Duration(seconds: 1)); - expect(parsedFile.captions.last.end, Duration(seconds: 2)); - expect(parsedFile.captions.last.text, "This is a test."); + expect(parsedFile.captions.last.start, const Duration(seconds: 1)); + expect(parsedFile.captions.last.end, const Duration(seconds: 2)); + expect(parsedFile.captions.last.text, 'This is a test.'); }); test('with [minutes]:[seconds].[milliseconds].', () { @@ -69,9 +69,9 @@ void main() { expect(parsedFile.captions.length, 1); expect(parsedFile.captions[0].number, 1); - expect(parsedFile.captions.last.start, Duration(seconds: 3)); - expect(parsedFile.captions.last.end, Duration(seconds: 4)); - expect(parsedFile.captions.last.text, "This is a test."); + expect(parsedFile.captions.last.start, const Duration(seconds: 3)); + expect(parsedFile.captions.last.end, const Duration(seconds: 4)); + expect(parsedFile.captions.last.text, 'This is a test.'); }); test('with invalid seconds format returns empty captions.', () { @@ -105,8 +105,8 @@ void main() { final Caption firstCaption = parsedFile.captions.single; expect(firstCaption.number, 1); - expect(firstCaption.start, Duration(seconds: 13)); - expect(firstCaption.end, Duration(seconds: 16, milliseconds: 0)); + expect(firstCaption.start, const Duration(seconds: 13)); + expect(firstCaption.end, const Duration(seconds: 16, milliseconds: 0)); expect(firstCaption.text, 'Valid'); }); } diff --git a/script/configs/custom_analysis.yaml b/script/configs/custom_analysis.yaml index d10424e0b944..71f6f03de917 100644 --- a/script/configs/custom_analysis.yaml +++ b/script/configs/custom_analysis.yaml @@ -17,4 +17,3 @@ - in_app_purchase/in_app_purchase - in_app_purchase/in_app_purchase_android - in_app_purchase/in_app_purchase_storekit -- video_player/video_player From f3f8e72ed927b64d84a14f76be990475dda2c26a Mon Sep 17 00:00:00 2001 From: fzyzcjy <5236035+fzyzcjy@users.noreply.github.com> Date: Fri, 25 Feb 2022 06:26:23 +0800 Subject: [PATCH 229/600] [webview] Fix comments (accidentially mixed `//` with `///`) (#4907) --- packages/webview_flutter/webview_flutter/CHANGELOG.md | 3 ++- packages/webview_flutter/webview_flutter/lib/src/webview.dart | 4 ++-- packages/webview_flutter/webview_flutter/pubspec.yaml | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index 662070437cf7..34b56d61bea8 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,7 +1,8 @@ -## NEXT +## 3.0.1 * Removes a duplicate Android-specific integration test. * Fixes an integration test race condition. +* Fixes comments (accidentially mixed // with ///). ## 3.0.0 diff --git a/packages/webview_flutter/webview_flutter/lib/src/webview.dart b/packages/webview_flutter/webview_flutter/lib/src/webview.dart index a17702750d62..6a24d3d4cb2d 100644 --- a/packages/webview_flutter/webview_flutter/lib/src/webview.dart +++ b/packages/webview_flutter/webview_flutter/lib/src/webview.dart @@ -715,8 +715,8 @@ class WebViewController { /// The Future completes with an error if a JavaScript error occurred. /// /// When running JavaScript in a [WebView], it is best practice to wait for - // the [WebView.onPageFinished] callback. This guarantees all the JavaScript - // embedded in the main frame HTML has been loaded. + /// the [WebView.onPageFinished] callback. This guarantees all the JavaScript + /// embedded in the main frame HTML has been loaded. Future runJavascript(String javaScriptString) { if (_settings.javascriptMode == JavascriptMode.disabled) { return Future.error(FlutterError( diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index afa615cd8b2a..322f4ada3b56 100644 --- a/packages/webview_flutter/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter description: A Flutter plugin that provides a WebView widget on Android and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 3.0.0 +version: 3.0.1 environment: sdk: ">=2.14.0 <3.0.0" From 4cfcb4c971f971f580ecf0c105292cf7881cb75e Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 24 Feb 2022 18:06:23 -0500 Subject: [PATCH 230/600] [path_provider] Use a different Android channel name (#4912) --- packages/path_provider/path_provider_android/CHANGELOG.md | 6 ++++++ .../flutter/plugins/pathprovider/PathProviderPlugin.java | 2 +- .../path_provider_android/lib/path_provider_android.dart | 2 +- packages/path_provider/path_provider_android/pubspec.yaml | 5 ++--- .../test/path_provider_android_test.dart | 8 -------- 5 files changed, 10 insertions(+), 13 deletions(-) diff --git a/packages/path_provider/path_provider_android/CHANGELOG.md b/packages/path_provider/path_provider_android/CHANGELOG.md index 1282ab6e20a9..7b04e90d5e0f 100644 --- a/packages/path_provider/path_provider_android/CHANGELOG.md +++ b/packages/path_provider/path_provider_android/CHANGELOG.md @@ -1,3 +1,9 @@ +## 2.0.12 + +* Returns to using a different platform channel name, undoing the revert in + 2.0.11, but updates the minimum Flutter version to 2.8 to avoid the issue + that caused the revert. + ## 2.0.11 * Temporarily reverts the platform channel name change from 2.0.10 in order to diff --git a/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java index 3ff2416527d0..278ff58b59dc 100644 --- a/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java +++ b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java @@ -153,7 +153,7 @@ public void getApplicationSupportDirectory(@NonNull Result result) { public PathProviderPlugin() {} private void setup(BinaryMessenger messenger, Context context) { - String channelName = "plugins.flutter.io/path_provider"; + String channelName = "plugins.flutter.io/path_provider_android"; // TODO(gaaclarke): Remove reflection guard when https://github.com/flutter/engine/pull/29147 // becomes available on the stable branch. try { diff --git a/packages/path_provider/path_provider_android/lib/path_provider_android.dart b/packages/path_provider/path_provider_android/lib/path_provider_android.dart index 281cc5141eda..b0f3808d2859 100644 --- a/packages/path_provider/path_provider_android/lib/path_provider_android.dart +++ b/packages/path_provider/path_provider_android/lib/path_provider_android.dart @@ -11,7 +11,7 @@ class PathProviderAndroid extends PathProviderPlatform { /// The method channel used to interact with the native platform. @visibleForTesting MethodChannel methodChannel = - const MethodChannel('plugins.flutter.io/path_provider'); + const MethodChannel('plugins.flutter.io/path_provider_android'); /// Registers this class as the default instance of [PathProviderPlatform]. static void registerWith() { diff --git a/packages/path_provider/path_provider_android/pubspec.yaml b/packages/path_provider/path_provider_android/pubspec.yaml index e259fdc958cc..63b9330a89f9 100644 --- a/packages/path_provider/path_provider_android/pubspec.yaml +++ b/packages/path_provider/path_provider_android/pubspec.yaml @@ -2,11 +2,11 @@ name: path_provider_android description: Android implementation of the path_provider plugin. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.11 +version: 2.0.12 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: @@ -29,5 +29,4 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - plugin_platform_interface: ^2.0.0 test: ^1.16.0 diff --git a/packages/path_provider/path_provider_android/test/path_provider_android_test.dart b/packages/path_provider/path_provider_android/test/path_provider_android_test.dart index 2388059f0b34..d2f9682bf6d7 100644 --- a/packages/path_provider/path_provider_android/test/path_provider_android_test.dart +++ b/packages/path_provider/path_provider_android/test/path_provider_android_test.dart @@ -52,14 +52,6 @@ void main() { log.clear(); }); - // TODO(stuartmorgan): Change this to a test that it uses a different name, - // to avoid potential confusion, once the SDK is changed to 2.8+. See - // https://github.com/flutter/plugins/pull/4617#discussion_r774673962 - test('channel name is compatible with shared method channel', () async { - expect( - pathProvider.methodChannel.name, 'plugins.flutter.io/path_provider'); - }); - test('getTemporaryPath', () async { final String? path = await pathProvider.getTemporaryPath(); expect( From 470b3027c9d8ab6fcd96fb94a055bbc47184af82 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 24 Feb 2022 18:41:23 -0500 Subject: [PATCH 231/600] [path_provider] Switch iOS to an internal method channel (#4921) --- .../path_provider_ios/CHANGELOG.md | 4 + .../ios/Classes/FLTPathProviderPlugin.m | 24 +--- .../lib/path_provider_ios.dart | 72 ++++++++++ .../path_provider_ios/pubspec.yaml | 6 +- .../test/path_provider_ios_test.dart | 128 ++++++++++++++++++ 5 files changed, 210 insertions(+), 24 deletions(-) create mode 100644 packages/path_provider/path_provider_ios/lib/path_provider_ios.dart create mode 100644 packages/path_provider/path_provider_ios/test/path_provider_ios_test.dart diff --git a/packages/path_provider/path_provider_ios/CHANGELOG.md b/packages/path_provider/path_provider_ios/CHANGELOG.md index eb155d138218..543af778d2e2 100644 --- a/packages/path_provider/path_provider_ios/CHANGELOG.md +++ b/packages/path_provider/path_provider_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.8 + +* Switches to a package-internal implementation of the platform interface. + ## 2.0.7 * Fixes link in README. diff --git a/packages/path_provider/path_provider_ios/ios/Classes/FLTPathProviderPlugin.m b/packages/path_provider/path_provider_ios/ios/Classes/FLTPathProviderPlugin.m index 063e028f4d3c..ac6a1be9414f 100644 --- a/packages/path_provider/path_provider_ios/ios/Classes/FLTPathProviderPlugin.m +++ b/packages/path_provider/path_provider_ios/ios/Classes/FLTPathProviderPlugin.m @@ -9,18 +9,11 @@ return paths.firstObject; } -static FlutterError *getFlutterError(NSError *error) { - if (error == nil) return nil; - return [FlutterError errorWithCode:[NSString stringWithFormat:@"Error %ld", (long)error.code] - message:error.domain - details:error.localizedDescription]; -} - @implementation FLTPathProviderPlugin + (void)registerWithRegistrar:(NSObject *)registrar { FlutterMethodChannel *channel = - [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/path_provider" + [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/path_provider_ios" binaryMessenger:registrar.messenger]; [channel setMethodCallHandler:^(FlutterMethodCall *call, FlutterResult result) { if ([@"getTemporaryDirectory" isEqualToString:call.method]) { @@ -28,20 +21,7 @@ + (void)registerWithRegistrar:(NSObject *)registrar { } else if ([@"getApplicationDocumentsDirectory" isEqualToString:call.method]) { result([self getApplicationDocumentsDirectory]); } else if ([@"getApplicationSupportDirectory" isEqualToString:call.method]) { - NSString *path = [self getApplicationSupportDirectory]; - - // Create the path if it doesn't exist - NSError *error; - NSFileManager *fileManager = [NSFileManager defaultManager]; - BOOL success = [fileManager createDirectoryAtPath:path - withIntermediateDirectories:YES - attributes:nil - error:&error]; - if (!success) { - result(getFlutterError(error)); - } else { - result(path); - } + result([self getApplicationSupportDirectory]); } else if ([@"getLibraryDirectory" isEqualToString:call.method]) { result([self getLibraryDirectory]); } else { diff --git a/packages/path_provider/path_provider_ios/lib/path_provider_ios.dart b/packages/path_provider/path_provider_ios/lib/path_provider_ios.dart new file mode 100644 index 000000000000..88becf20850e --- /dev/null +++ b/packages/path_provider/path_provider_ios/lib/path_provider_ios.dart @@ -0,0 +1,72 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io'; + +import 'package:flutter/foundation.dart' show visibleForTesting; +import 'package:flutter/services.dart'; +import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; + +/// The iOS implementation of [PathProviderPlatform]. +class PathProviderIOS extends PathProviderPlatform { + /// The method channel used to interact with the native platform. + @visibleForTesting + MethodChannel methodChannel = + const MethodChannel('plugins.flutter.io/path_provider_ios'); + + /// Registers this class as the default instance of [PathProviderPlatform] + static void registerWith() { + PathProviderPlatform.instance = PathProviderIOS(); + } + + @override + Future getTemporaryPath() async { + return methodChannel.invokeMethod('getTemporaryDirectory'); + } + + @override + Future getApplicationSupportPath() async { + final String? path = await methodChannel + .invokeMethod('getApplicationSupportDirectory'); + if (path != null) { + // Ensure the directory exists before returning it, for consistency with + // other platforms. + await Directory(path).create(recursive: true); + } + return path; + } + + @override + Future getLibraryPath() async { + return methodChannel.invokeMethod('getLibraryDirectory'); + } + + @override + Future getApplicationDocumentsPath() async { + return methodChannel + .invokeMethod('getApplicationDocumentsDirectory'); + } + + @override + Future getExternalStoragePath() async { + throw UnsupportedError('getExternalStoragePath is not supported on iOS'); + } + + @override + Future?> getExternalCachePaths() async { + throw UnsupportedError('getExternalCachePaths is not supported on iOS'); + } + + @override + Future?> getExternalStoragePaths({ + StorageDirectory? type, + }) async { + throw UnsupportedError('getExternalStoragePaths is not supported on iOS'); + } + + @override + Future getDownloadsPath() async { + throw UnsupportedError('getDownloadsPath is not supported on iOS'); + } +} diff --git a/packages/path_provider/path_provider_ios/pubspec.yaml b/packages/path_provider/path_provider_ios/pubspec.yaml index 0e05121f3052..282f8e4f0ebd 100644 --- a/packages/path_provider/path_provider_ios/pubspec.yaml +++ b/packages/path_provider/path_provider_ios/pubspec.yaml @@ -2,11 +2,11 @@ name: path_provider_ios description: iOS implementation of the path_provider plugin. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.7 +version: 2.0.8 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: @@ -14,6 +14,7 @@ flutter: platforms: ios: pluginClass: FLTPathProviderPlugin + dartPluginClass: PathProviderIOS dependencies: flutter: @@ -27,5 +28,6 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter + path: ^1.8.0 plugin_platform_interface: ^2.0.0 test: ^1.16.0 diff --git a/packages/path_provider/path_provider_ios/test/path_provider_ios_test.dart b/packages/path_provider/path_provider_ios/test/path_provider_ios_test.dart new file mode 100644 index 000000000000..40f81c5f445a --- /dev/null +++ b/packages/path_provider/path_provider_ios/test/path_provider_ios_test.dart @@ -0,0 +1,128 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io'; + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:path/path.dart' as p; +import 'package:path_provider_ios/path_provider_ios.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('PathProviderIOS', () { + late PathProviderIOS pathProvider; + late List log; + // These unit tests use the actual filesystem, since an injectable + // filesystem would add a runtime dependency to the package, so everything + // is contained to a temporary directory. + late Directory testRoot; + + late String temporaryPath; + late String applicationSupportPath; + late String libraryPath; + late String applicationDocumentsPath; + + setUp(() async { + pathProvider = PathProviderIOS(); + + testRoot = Directory.systemTemp.createTempSync(); + final String basePath = testRoot.path; + temporaryPath = p.join(basePath, 'temporary', 'path'); + applicationSupportPath = + p.join(basePath, 'application', 'support', 'path'); + libraryPath = p.join(basePath, 'library', 'path'); + applicationDocumentsPath = + p.join(basePath, 'application', 'documents', 'path'); + + log = []; + TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger + .setMockMethodCallHandler(pathProvider.methodChannel, + (MethodCall methodCall) async { + log.add(methodCall); + switch (methodCall.method) { + case 'getTemporaryDirectory': + return temporaryPath; + case 'getApplicationSupportDirectory': + return applicationSupportPath; + case 'getLibraryDirectory': + return libraryPath; + case 'getApplicationDocumentsDirectory': + return applicationDocumentsPath; + default: + return null; + } + }); + }); + + tearDown(() { + testRoot.deleteSync(recursive: true); + }); + + test('getTemporaryPath', () async { + final String? path = await pathProvider.getTemporaryPath(); + expect( + log, + [isMethodCall('getTemporaryDirectory', arguments: null)], + ); + expect(path, temporaryPath); + }); + + test('getApplicationSupportPath', () async { + final String? path = await pathProvider.getApplicationSupportPath(); + expect( + log, + [ + isMethodCall('getApplicationSupportDirectory', arguments: null) + ], + ); + expect(path, applicationSupportPath); + }); + + test('getApplicationSupportPath creates the directory if necessary', + () async { + final String? path = await pathProvider.getApplicationSupportPath(); + expect(Directory(path!).existsSync(), isTrue); + }); + + test('getLibraryPath', () async { + final String? path = await pathProvider.getLibraryPath(); + expect( + log, + [isMethodCall('getLibraryDirectory', arguments: null)], + ); + expect(path, libraryPath); + }); + + test('getApplicationDocumentsPath', () async { + final String? path = await pathProvider.getApplicationDocumentsPath(); + expect( + log, + [ + isMethodCall('getApplicationDocumentsDirectory', arguments: null) + ], + ); + expect(path, applicationDocumentsPath); + }); + + test('getDownloadsPath throws', () async { + expect(pathProvider.getDownloadsPath(), throwsA(isUnsupportedError)); + }); + + test('getExternalCachePaths throws', () async { + expect(pathProvider.getExternalCachePaths(), throwsA(isUnsupportedError)); + }); + + test('getExternalStoragePath throws', () async { + expect( + pathProvider.getExternalStoragePath(), throwsA(isUnsupportedError)); + }); + + test('getExternalStoragePaths throws', () async { + expect( + pathProvider.getExternalStoragePaths(), throwsA(isUnsupportedError)); + }); + }); +} From 351415caa735e220963ee51435261de00dd38bda Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 24 Feb 2022 20:26:13 -0500 Subject: [PATCH 232/600] Roll Flutter from c74a646b7bba to 47b5ed3948e8 (2 revisions) (#4914) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index c09c5a5d6212..438032a7e219 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c74a646b7bba1641d0b2b29f6024dc867844e94c +47b5ed3948e8ca3e79a120e220ad548d22f479bf From 045e65199c774a42027c53642210fe2e77fb56c5 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 25 Feb 2022 11:46:25 -0500 Subject: [PATCH 233/600] [in_app_purchase] Update iOS to new analysis options (#4920) --- .../in_app_purchase_storekit/CHANGELOG.md | 4 + .../analysis_options.yaml | 1 - .../in_app_purchase_test.dart | 2 +- .../example/lib/consumable_store.dart | 15 +- .../example/lib/main.dart | 147 +++++------ .../example/pubspec.yaml | 6 +- .../in_app_purchase_storekit_platform.dart | 76 +++--- ...p_purchase_storekit_platform_addition.dart | 10 +- .../sk_payment_queue_wrapper.dart | 73 +++--- .../sk_payment_transaction_wrappers.dart | 33 ++- .../sk_product_wrapper.dart | 103 ++++---- .../sk_receipt_manager.dart | 3 +- .../sk_storefront_wrapper.dart | 15 +- .../src/types/app_store_product_details.dart | 8 +- .../src/types/app_store_purchase_details.dart | 37 +-- .../in_app_purchase_storekit/pubspec.yaml | 3 +- .../test/fakes/fake_storekit_platform.dart | 63 ++--- ...rchase_storekit_platform_addtion_test.dart | 9 +- ...n_app_purchase_storekit_platform_test.dart | 230 ++++++++++-------- .../sk_methodchannel_apis_test.dart | 62 ++--- .../sk_payment_queue_delegate_api_test.dart | 24 +- .../store_kit_wrappers/sk_product_test.dart | 19 +- .../sk_test_stub_objects.dart | 34 +-- script/configs/custom_analysis.yaml | 1 - 24 files changed, 531 insertions(+), 447 deletions(-) delete mode 100644 packages/in_app_purchase/in_app_purchase_storekit/analysis_options.yaml diff --git a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md index fdc16d27945a..c5df52151411 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.0+2 + +* Internal code cleanup for stricter analysis options. + ## 0.3.0+1 * Removes dependency on `meta`. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/analysis_options.yaml b/packages/in_app_purchase/in_app_purchase_storekit/analysis_options.yaml deleted file mode 100644 index 5aeb4e7c5e21..000000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../../analysis_options_legacy.yaml diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/integration_test/in_app_purchase_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/example/integration_test/in_app_purchase_test.dart index b10f1d8c62ae..32ea11314ee8 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/integration_test/in_app_purchase_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/integration_test/in_app_purchase_test.dart @@ -3,8 +3,8 @@ // found in the LICENSE file. import 'package:flutter_test/flutter_test.dart'; -import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart'; import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; +import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart'; import 'package:integration_test/integration_test.dart'; void main() { diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/consumable_store.dart b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/consumable_store.dart index 4d10a50e1ee8..f8791d3b18c0 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/consumable_store.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/consumable_store.dart @@ -5,13 +5,14 @@ import 'dart:async'; import 'package:shared_preferences/shared_preferences.dart'; +// ignore: avoid_classes_with_only_static_members /// A store of consumable items. /// -/// This is a development prototype tha stores consumables in the shared +/// This is a development prototype that stores consumables in the shared /// preferences. Do not use this in real world apps. class ConsumableStore { static const String _kPrefKey = 'consumables'; - static Future _writes = Future.value(); + static Future _writes = Future.value(); /// Adds a consumable with ID `id` to the store. /// @@ -32,19 +33,19 @@ class ConsumableStore { /// Returns the list of consumables from the store. static Future> load() async { return (await SharedPreferences.getInstance()).getStringList(_kPrefKey) ?? - []; + []; } static Future _doSave(String id) async { - List cached = await load(); - SharedPreferences prefs = await SharedPreferences.getInstance(); + final List cached = await load(); + final SharedPreferences prefs = await SharedPreferences.getInstance(); cached.add(id); await prefs.setStringList(_kPrefKey, cached); } static Future _doConsume(String id) async { - List cached = await load(); - SharedPreferences prefs = await SharedPreferences.getInstance(); + final List cached = await load(); + final SharedPreferences prefs = await SharedPreferences.getInstance(); cached.remove(id); await prefs.setStringList(_kPrefKey, cached); } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart index 094289cfbd16..2ee2deb7fe35 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart @@ -4,10 +4,12 @@ import 'dart:async'; import 'dart:io'; + import 'package:flutter/material.dart'; +import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart'; import 'package:in_app_purchase_storekit_example/example_payment_queue_delegate.dart'; -import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; + import 'consumable_store.dart'; void main() { @@ -42,13 +44,13 @@ class _MyAppState extends State<_MyApp> { final InAppPurchaseStoreKitPlatform _iapStoreKitPlatform = InAppPurchasePlatform.instance as InAppPurchaseStoreKitPlatform; final InAppPurchaseStoreKitPlatformAddition _iapStoreKitPlatformAddition = - InAppPurchasePlatformAddition.instance + InAppPurchasePlatformAddition.instance! as InAppPurchaseStoreKitPlatformAddition; late StreamSubscription> _subscription; - List _notFoundIds = []; - List _products = []; - List _purchases = []; - List _consumables = []; + List _notFoundIds = []; + List _products = []; + List _purchases = []; + List _consumables = []; bool _isAvailable = false; bool _purchasePending = false; bool _loading = true; @@ -58,11 +60,12 @@ class _MyAppState extends State<_MyApp> { void initState() { final Stream> purchaseUpdated = _iapStoreKitPlatform.purchaseStream; - _subscription = purchaseUpdated.listen((purchaseDetailsList) { + _subscription = + purchaseUpdated.listen((List purchaseDetailsList) { _listenToPurchaseUpdated(purchaseDetailsList); }, onDone: () { _subscription.cancel(); - }, onError: (error) { + }, onError: (Object error) { // handle error here. }); @@ -78,26 +81,26 @@ class _MyAppState extends State<_MyApp> { if (!isAvailable) { setState(() { _isAvailable = isAvailable; - _products = []; - _purchases = []; - _notFoundIds = []; - _consumables = []; + _products = []; + _purchases = []; + _notFoundIds = []; + _consumables = []; _purchasePending = false; _loading = false; }); return; } - ProductDetailsResponse productDetailResponse = + final ProductDetailsResponse productDetailResponse = await _iapStoreKitPlatform.queryProductDetails(_kProductIds.toSet()); if (productDetailResponse.error != null) { setState(() { _queryProductError = productDetailResponse.error!.message; _isAvailable = isAvailable; _products = productDetailResponse.productDetails; - _purchases = []; + _purchases = []; _notFoundIds = productDetailResponse.notFoundIDs; - _consumables = []; + _consumables = []; _purchasePending = false; _loading = false; }); @@ -109,16 +112,16 @@ class _MyAppState extends State<_MyApp> { _queryProductError = null; _isAvailable = isAvailable; _products = productDetailResponse.productDetails; - _purchases = []; + _purchases = []; _notFoundIds = productDetailResponse.notFoundIDs; - _consumables = []; + _consumables = []; _purchasePending = false; _loading = false; }); return; } - List consumables = await ConsumableStore.load(); + final List consumables = await ConsumableStore.load(); setState(() { _isAvailable = isAvailable; _products = productDetailResponse.productDetails; @@ -137,11 +140,11 @@ class _MyAppState extends State<_MyApp> { @override Widget build(BuildContext context) { - List stack = []; + final List stack = []; if (_queryProductError == null) { stack.add( ListView( - children: [ + children: [ _buildConnectionCheckTile(), _buildProductList(), _buildConsumableBox(), @@ -157,10 +160,10 @@ class _MyAppState extends State<_MyApp> { if (_purchasePending) { stack.add( Stack( - children: [ + children: const [ Opacity( opacity: 0.3, - child: const ModalBarrier(dismissible: false, color: Colors.grey), + child: ModalBarrier(dismissible: false, color: Colors.grey), ), Center( child: CircularProgressIndicator(), @@ -184,7 +187,7 @@ class _MyAppState extends State<_MyApp> { Card _buildConnectionCheckTile() { if (_loading) { - return Card(child: ListTile(title: const Text('Trying to connect...'))); + return const Card(child: ListTile(title: Text('Trying to connect...'))); } final Widget storeHeader = ListTile( leading: Icon(_isAvailable ? Icons.check : Icons.block, @@ -195,8 +198,8 @@ class _MyAppState extends State<_MyApp> { final List children = [storeHeader]; if (!_isAvailable) { - children.addAll([ - Divider(), + children.addAll([ + const Divider(), ListTile( title: Text('Not connected', style: TextStyle(color: ThemeData.light().errorColor)), @@ -210,29 +213,30 @@ class _MyAppState extends State<_MyApp> { Card _buildProductList() { if (_loading) { - return Card( - child: (ListTile( + return const Card( + child: ListTile( leading: CircularProgressIndicator(), - title: Text('Fetching products...')))); + title: Text('Fetching products...'))); } if (!_isAvailable) { - return Card(); + return const Card(); } - final ListTile productHeader = ListTile(title: Text('Products for Sale')); - List productList = []; + const ListTile productHeader = ListTile(title: Text('Products for Sale')); + final List productList = []; if (_notFoundIds.isNotEmpty) { productList.add(ListTile( title: Text('[${_notFoundIds.join(", ")}] not found', style: TextStyle(color: ThemeData.light().errorColor)), - subtitle: Text( + subtitle: const Text( 'This app needs special configuration to run. Please see example/README.md for instructions.'))); } // This loading previous purchases code is just a demo. Please do not use this as it is. // In your app you should always verify the purchase data using the `verificationData` inside the [PurchaseDetails] object before trusting it. // We recommend that you use your own server to verify the purchase data. - Map purchases = - Map.fromEntries(_purchases.map((PurchaseDetails purchase) { + final Map purchases = + Map.fromEntries( + _purchases.map((PurchaseDetails purchase) { if (purchase.pendingCompletePurchase) { _iapStoreKitPlatform.completePurchase(purchase); } @@ -240,7 +244,7 @@ class _MyAppState extends State<_MyApp> { })); productList.addAll(_products.map( (ProductDetails productDetails) { - PurchaseDetails? previousPurchase = purchases[productDetails.id]; + final PurchaseDetails? previousPurchase = purchases[productDetails.id]; return ListTile( title: Text( productDetails.title, @@ -253,7 +257,7 @@ class _MyAppState extends State<_MyApp> { onPressed: () { _iapStoreKitPlatformAddition.showPriceConsentIfNeeded(); }, - icon: Icon(Icons.upgrade)) + icon: const Icon(Icons.upgrade)) : TextButton( child: Text(productDetails.price), style: TextButton.styleFrom( @@ -261,7 +265,7 @@ class _MyAppState extends State<_MyApp> { primary: Colors.white, ), onPressed: () { - PurchaseParam purchaseParam = PurchaseParam( + final PurchaseParam purchaseParam = PurchaseParam( productDetails: productDetails, applicationUserName: null, ); @@ -279,26 +283,26 @@ class _MyAppState extends State<_MyApp> { )); return Card( - child: - Column(children: [productHeader, Divider()] + productList)); + child: Column( + children: [productHeader, const Divider()] + productList)); } Card _buildConsumableBox() { if (_loading) { - return Card( - child: (ListTile( + return const Card( + child: ListTile( leading: CircularProgressIndicator(), - title: Text('Fetching consumables...')))); + title: Text('Fetching consumables...'))); } if (!_isAvailable || _notFoundIds.contains(_kConsumableId)) { - return Card(); + return const Card(); } - final ListTile consumableHeader = + const ListTile consumableHeader = ListTile(title: Text('Purchased consumables')); final List tokens = _consumables.map((String id) { return GridTile( child: IconButton( - icon: Icon( + icon: const Icon( Icons.stars, size: 42.0, color: Colors.orange, @@ -311,12 +315,12 @@ class _MyAppState extends State<_MyApp> { return Card( child: Column(children: [ consumableHeader, - Divider(), + const Divider(), GridView.count( crossAxisCount: 5, children: tokens, shrinkWrap: true, - padding: EdgeInsets.all(16.0), + padding: const EdgeInsets.all(16.0), ) ])); } @@ -331,9 +335,9 @@ class _MyAppState extends State<_MyApp> { child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.end, - children: [ + children: [ TextButton( - child: Text('Restore purchases'), + child: const Text('Restore purchases'), style: TextButton.styleFrom( backgroundColor: Theme.of(context).primaryColor, primary: Colors.white, @@ -359,11 +363,11 @@ class _MyAppState extends State<_MyApp> { }); } - void deliverProduct(PurchaseDetails purchaseDetails) async { + Future deliverProduct(PurchaseDetails purchaseDetails) async { // IMPORTANT!! Always verify purchase details before delivering the product. if (purchaseDetails.productID == _kConsumableId) { await ConsumableStore.save(purchaseDetails.purchaseID!); - List consumables = await ConsumableStore.load(); + final List consumables = await ConsumableStore.load(); setState(() { _purchasePending = false; _consumables = consumables; @@ -393,27 +397,30 @@ class _MyAppState extends State<_MyApp> { } void _listenToPurchaseUpdated(List purchaseDetailsList) { - purchaseDetailsList.forEach((PurchaseDetails purchaseDetails) async { - if (purchaseDetails.status == PurchaseStatus.pending) { - showPendingUI(); - } else { - if (purchaseDetails.status == PurchaseStatus.error) { - handleError(purchaseDetails.error!); - } else if (purchaseDetails.status == PurchaseStatus.purchased || - purchaseDetails.status == PurchaseStatus.restored) { - bool valid = await _verifyPurchase(purchaseDetails); - if (valid) { - deliverProduct(purchaseDetails); - } else { - _handleInvalidPurchase(purchaseDetails); - return; - } - } + purchaseDetailsList.forEach(_handleReportedPurchaseState); + } - if (purchaseDetails.pendingCompletePurchase) { - await _iapStoreKitPlatform.completePurchase(purchaseDetails); + Future _handleReportedPurchaseState( + PurchaseDetails purchaseDetails) async { + if (purchaseDetails.status == PurchaseStatus.pending) { + showPendingUI(); + } else { + if (purchaseDetails.status == PurchaseStatus.error) { + handleError(purchaseDetails.error!); + } else if (purchaseDetails.status == PurchaseStatus.purchased || + purchaseDetails.status == PurchaseStatus.restored) { + final bool valid = await _verifyPurchase(purchaseDetails); + if (valid) { + await deliverProduct(purchaseDetails); + } else { + _handleInvalidPurchase(purchaseDetails); + return; } } - }); + + if (purchaseDetails.pendingCompletePurchase) { + await _iapStoreKitPlatform.completePurchase(purchaseDetails); + } + } } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/example/pubspec.yaml index 493f111881c5..597dfb0703bb 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/pubspec.yaml @@ -9,7 +9,7 @@ environment: dependencies: flutter: sdk: flutter - shared_preferences: ^2.0.0 + in_app_purchase_platform_interface: ^1.0.0 in_app_purchase_storekit: # When depending on this package from a real application you should use: # in_app_purchase_storekit: ^x.y.z @@ -17,15 +17,13 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - - in_app_purchase_platform_interface: ^1.0.0 + shared_preferences: ^2.0.0 dev_dependencies: flutter_driver: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.10.0 flutter: uses-material-design: true diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart index e912dd606673..6db2e59e1485 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart @@ -6,8 +6,8 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; -import 'package:in_app_purchase_storekit/src/in_app_purchase_storekit_platform_addition.dart'; import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; +import 'package:in_app_purchase_storekit/src/in_app_purchase_storekit_platform_addition.dart'; import '../in_app_purchase_storekit.dart'; import '../store_kit_wrappers.dart'; @@ -23,9 +23,6 @@ const String kIAPSource = 'app_store'; /// This translates various `StoreKit` calls and responses into the /// generic plugin API. class InAppPurchaseStoreKitPlatform extends InAppPurchasePlatform { - static late SKPaymentQueueWrapper _skPaymentQueueWrapper; - static late _TransactionObserver _observer; - /// Creates an [InAppPurchaseStoreKitPlatform] object. /// /// This constructor should only be used for testing, for any other purpose @@ -33,6 +30,10 @@ class InAppPurchaseStoreKitPlatform extends InAppPurchasePlatform { @visibleForTesting InAppPurchaseStoreKitPlatform(); + static late SKPaymentQueueWrapper _skPaymentQueueWrapper; + static late _TransactionObserver _observer; + + @override Stream> get purchaseStream => _observer.purchaseUpdatedController.stream; @@ -55,8 +56,8 @@ class InAppPurchaseStoreKitPlatform extends InAppPurchasePlatform { // Create a purchaseUpdatedController and notify the native side when to // start of stop sending updates. - StreamController> updateController = - StreamController.broadcast( + final StreamController> updateController = + StreamController>.broadcast( onListen: () => _skPaymentQueueWrapper.startObservingTransactionQueue(), onCancel: () => _skPaymentQueueWrapper.stopObservingTransactionQueue(), ); @@ -73,9 +74,8 @@ class InAppPurchaseStoreKitPlatform extends InAppPurchasePlatform { productIdentifier: purchaseParam.productDetails.id, quantity: 1, applicationUsername: purchaseParam.applicationUserName, - simulatesAskToBuyInSandbox: (purchaseParam is AppStorePurchaseParam) - ? purchaseParam.simulatesAskToBuyInSandbox - : false, + simulatesAskToBuyInSandbox: purchaseParam is AppStorePurchaseParam && + purchaseParam.simulatesAskToBuyInSandbox, requestData: null)); return true; // There's no error feedback from iOS here to return. @@ -125,9 +125,10 @@ class InAppPurchaseStoreKitPlatform extends InAppPurchasePlatform { } on PlatformException catch (e) { exception = e; response = SkProductResponseWrapper( - products: [], invalidProductIdentifiers: identifiers.toList()); + products: const [], + invalidProductIdentifiers: identifiers.toList()); } - List productDetails = []; + List productDetails = []; if (response.products != null) { productDetails = response.products .map((SKProductWrapper productWrapper) => @@ -138,7 +139,8 @@ class InAppPurchaseStoreKitPlatform extends InAppPurchasePlatform { if (productDetails.isEmpty) { invalidIdentifiers = identifiers.toList(); } - ProductDetailsResponse productDetailsResponse = ProductDetailsResponse( + final ProductDetailsResponse productDetailsResponse = + ProductDetailsResponse( productDetails: productDetails, notFoundIDs: invalidIdentifiers, error: exception == null @@ -160,21 +162,21 @@ enum _TransactionRestoreState { } class _TransactionObserver implements SKTransactionObserverWrapper { + _TransactionObserver(this.purchaseUpdatedController); + final StreamController> purchaseUpdatedController; - Completer? _restoreCompleter; + Completer? _restoreCompleter; late String _receiptData; _TransactionRestoreState _transactionRestoreState = _TransactionRestoreState.notRunning; - _TransactionObserver(this.purchaseUpdatedController); - Future restoreTransactions({ required SKPaymentQueueWrapper queue, String? applicationUserName, }) { _transactionRestoreState = _TransactionRestoreState.waitingForTransactions; - _restoreCompleter = Completer(); + _restoreCompleter = Completer(); queue.restoreTransactions(applicationUserName: applicationUserName); return _restoreCompleter!.future; } @@ -183,34 +185,24 @@ class _TransactionObserver implements SKTransactionObserverWrapper { _restoreCompleter = null; } + @override void updatedTransactions( - {required List transactions}) async { - if (_transactionRestoreState == - _TransactionRestoreState.waitingForTransactions && - transactions.any((transaction) => - transaction.transactionState == - SKPaymentTransactionStateWrapper.restored)) { - _transactionRestoreState = _TransactionRestoreState.receivedTransaction; - } - - String receiptData = await getReceiptData(); - List purchases = transactions - .map((SKPaymentTransactionWrapper transaction) => - AppStorePurchaseDetails.fromSKTransaction(transaction, receiptData)) - .toList(); - - purchaseUpdatedController.add(purchases); + {required List transactions}) { + _handleTransationUpdates(transactions); } + @override void removedTransactions( {required List transactions}) {} /// Triggered when there is an error while restoring transactions. + @override void restoreCompletedTransactionsFailed({required SKError error}) { _restoreCompleter!.completeError(error); _transactionRestoreState = _TransactionRestoreState.notRunning; } + @override void paymentQueueRestoreCompletedTransactionsFinished() { _restoreCompleter!.complete(); @@ -225,6 +217,7 @@ class _TransactionObserver implements SKTransactionObserverWrapper { _transactionRestoreState = _TransactionRestoreState.notRunning; } + @override bool shouldAddStorePayment( {required SKPaymentWrapper payment, required SKProductWrapper product}) { // In this unified API, we always return true to keep it consistent with the behavior on Google Play. @@ -239,4 +232,23 @@ class _TransactionObserver implements SKTransactionObserverWrapper { } return _receiptData; } + + Future _handleTransationUpdates( + List transactions) async { + if (_transactionRestoreState == + _TransactionRestoreState.waitingForTransactions && + transactions.any((SKPaymentTransactionWrapper transaction) => + transaction.transactionState == + SKPaymentTransactionStateWrapper.restored)) { + _transactionRestoreState = _TransactionRestoreState.receivedTransaction; + } + + final String receiptData = await getReceiptData(); + final List purchases = transactions + .map((SKPaymentTransactionWrapper transaction) => + AppStorePurchaseDetails.fromSKTransaction(transaction, receiptData)) + .toList(); + + purchaseUpdatedController.add(purchases); + } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform_addition.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform_addition.dart index adead56d7be9..87655df53d34 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform_addition.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform_addition.dart @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart'; import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; +import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart'; import '../store_kit_wrappers.dart'; @@ -13,7 +13,7 @@ class InAppPurchaseStoreKitPlatformAddition /// Present Code Redemption Sheet. /// /// Available on devices running iOS 14 and iPadOS 14 and later. - Future presentCodeRedemptionSheet() { + Future presentCodeRedemptionSheet() { return SKPaymentQueueWrapper().presentCodeRedemptionSheet(); } @@ -23,7 +23,7 @@ class InAppPurchaseStoreKitPlatformAddition Future refreshPurchaseVerificationData() async { await SKRequestMaker().startRefreshReceiptRequest(); try { - String receipt = await SKReceiptManager.retrieveReceiptData(); + final String receipt = await SKReceiptManager.retrieveReceiptData(); return PurchaseVerificationData( localVerificationData: receipt, serverVerificationData: receipt, @@ -46,7 +46,7 @@ class InAppPurchaseStoreKitPlatformAddition /// /// When set to `null` the payment queue delegate will be removed and the /// default behaviour will apply (see [documentation](https://developer.apple.com/documentation/storekit/skpaymentqueue/3182429-delegate?language=objc)). - Future setDelegate(SKPaymentQueueDelegateWrapper? delegate) => + Future setDelegate(SKPaymentQueueDelegateWrapper? delegate) => SKPaymentQueueWrapper().setDelegate(delegate); /// Shows the price consent sheet if the user has not yet responded to a @@ -57,6 +57,6 @@ class InAppPurchaseStoreKitPlatformAddition /// `SKPaymentQueueDelegateWrapper.shouldShowPriceConsent()` method was called. /// /// See documentation of StoreKit's [`-[SKPaymentQueue showPriceConsentIfNeeded]`](https://developer.apple.com/documentation/storekit/skpaymentqueue/3521327-showpriceconsentifneeded?language=objc). - Future showPriceConsentIfNeeded() => + Future showPriceConsentIfNeeded() => SKPaymentQueueWrapper().showPriceConsentIfNeeded(); } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart index d55b07ab4f10..022848281327 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart @@ -73,7 +73,7 @@ class SKPaymentQueueWrapper { /// /// Call this method when the first listener is subscribed to the /// [InAppPurchaseStoreKitPlatform.purchaseStream]. - Future startObservingTransactionQueue() => channel + Future startObservingTransactionQueue() => channel .invokeMethod('-[SKPaymentQueue startObservingTransactionQueue]'); /// Instructs the iOS implementation to remove the transaction observer and @@ -81,7 +81,7 @@ class SKPaymentQueueWrapper { /// /// Call this when there are no longer any listeners subscribed to the /// [InAppPurchaseStoreKitPlatform.purchaseStream]. - Future stopObservingTransactionQueue() => channel + Future stopObservingTransactionQueue() => channel .invokeMethod('-[SKPaymentQueue stopObservingTransactionQueue]'); /// Sets an implementation of the [SKPaymentQueueDelegateWrapper]. @@ -94,7 +94,7 @@ class SKPaymentQueueWrapper { /// /// When set to `null` the payment queue delegate will be removed and the /// default behaviour will apply (see [documentation](https://developer.apple.com/documentation/storekit/skpaymentqueue/3182429-delegate?language=objc)). - Future setDelegate(SKPaymentQueueDelegateWrapper? delegate) async { + Future setDelegate(SKPaymentQueueDelegateWrapper? delegate) async { if (delegate == null) { await channel.invokeMethod('-[SKPaymentQueue removeDelegate]'); paymentQueueDelegateChannel.setMethodCallHandler(null); @@ -150,7 +150,7 @@ class SKPaymentQueueWrapper { /// finishTransaction:]`](https://developer.apple.com/documentation/storekit/skpaymentqueue/1506003-finishtransaction?language=objc). Future finishTransaction( SKPaymentTransactionWrapper transaction) async { - Map requestMap = transaction.toFinishMap(); + final Map requestMap = transaction.toFinishMap(); await channel.invokeMethod( '-[InAppPurchasePlugin finishTransaction:result:]', requestMap, @@ -220,7 +220,7 @@ class SKPaymentQueueWrapper { case 'updatedTransactions': { final List transactions = - _getTransactionList(call.arguments); + _getTransactionList(call.arguments as List); return Future(() { observer.updatedTransactions(transactions: transactions); }); @@ -228,15 +228,15 @@ class SKPaymentQueueWrapper { case 'removedTransactions': { final List transactions = - _getTransactionList(call.arguments); + _getTransactionList(call.arguments as List); return Future(() { observer.removedTransactions(transactions: transactions); }); } case 'restoreCompletedTransactionsFailed': { - SKError error = - SKError.fromJson(Map.from(call.arguments)); + final SKError error = SKError.fromJson(Map.from( + call.arguments as Map)); return Future(() { observer.restoreCompletedTransactionsFailed(error: error); }); @@ -249,10 +249,12 @@ class SKPaymentQueueWrapper { } case 'shouldAddStorePayment': { - SKPaymentWrapper payment = - SKPaymentWrapper.fromJson(call.arguments['payment']); - SKProductWrapper product = - SKProductWrapper.fromJson(call.arguments['product']); + final SKPaymentWrapper payment = SKPaymentWrapper.fromJson( + (call.arguments['payment'] as Map) + .cast()); + final SKProductWrapper product = SKProductWrapper.fromJson( + (call.arguments['product'] as Map) + .cast()); return Future(() { if (observer.shouldAddStorePayment( payment: payment, product: product) == @@ -274,7 +276,8 @@ class SKPaymentQueueWrapper { List transactionsData) { return transactionsData.map((dynamic map) { return SKPaymentTransactionWrapper.fromJson( - Map.castFrom(map)); + Map.castFrom( + map as Map)); }).toList(); } @@ -292,9 +295,12 @@ class SKPaymentQueueWrapper { switch (call.method) { case 'shouldContinueTransaction': final SKPaymentTransactionWrapper transaction = - SKPaymentTransactionWrapper.fromJson(call.arguments['transaction']); - final SKStorefrontWrapper storefront = - SKStorefrontWrapper.fromJson(call.arguments['storefront']); + SKPaymentTransactionWrapper.fromJson( + (call.arguments['transaction'] as Map) + .cast()); + final SKStorefrontWrapper storefront = SKStorefrontWrapper.fromJson( + (call.arguments['storefront'] as Map) + .cast()); return delegate.shouldContinueTransaction(transaction, storefront); case 'shouldShowPriceConsent': return delegate.shouldShowPriceConsent(); @@ -351,11 +357,11 @@ class SKError { if (other.runtimeType != runtimeType) { return false; } - final SKError typedOther = other as SKError; - return typedOther.code == code && - typedOther.domain == domain && - DeepCollectionEquality.unordered() - .equals(typedOther.userInfo, userInfo); + return other is SKError && + other.code == code && + other.domain == domain && + const DeepCollectionEquality.unordered() + .equals(other.userInfo, userInfo); } @override @@ -467,12 +473,12 @@ class SKPaymentWrapper { if (other.runtimeType != runtimeType) { return false; } - final SKPaymentWrapper typedOther = other as SKPaymentWrapper; - return typedOther.productIdentifier == productIdentifier && - typedOther.applicationUsername == applicationUsername && - typedOther.quantity == quantity && - typedOther.simulatesAskToBuyInSandbox == simulatesAskToBuyInSandbox && - typedOther.requestData == requestData; + return other is SKPaymentWrapper && + other.productIdentifier == productIdentifier && + other.applicationUsername == applicationUsername && + other.quantity == quantity && + other.simulatesAskToBuyInSandbox == simulatesAskToBuyInSandbox && + other.requestData == requestData; } @override @@ -569,13 +575,12 @@ class SKPaymentDiscountWrapper { if (other.runtimeType != runtimeType) { return false; } - final SKPaymentDiscountWrapper typedOther = - other as SKPaymentDiscountWrapper; - return typedOther.identifier == identifier && - typedOther.keyIdentifier == keyIdentifier && - typedOther.nonce == nonce && - typedOther.signature == signature && - typedOther.timestamp == timestamp; + return other is SKPaymentDiscountWrapper && + other.identifier == identifier && + other.keyIdentifier == keyIdentifier && + other.nonce == nonce && + other.signature == signature && + other.timestamp == timestamp; } @override diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_transaction_wrappers.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_transaction_wrappers.dart index 885e9c075b48..4c4c91257d9d 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_transaction_wrappers.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_transaction_wrappers.dart @@ -4,6 +4,7 @@ import 'dart:ui' show hashValues; +import 'package:flutter/foundation.dart'; import 'package:json_annotation/json_annotation.dart'; import 'enum_converters.dart'; @@ -104,8 +105,12 @@ enum SKPaymentTransactionStateWrapper { /// Dart wrapper around StoreKit's /// [SKPaymentTransaction](https://developer.apple.com/documentation/storekit/skpaymenttransaction?language=objc). @JsonSerializable(createToJson: true) +@immutable class SKPaymentTransactionWrapper { /// Creates a new [SKPaymentTransactionWrapper] with the provided information. + // TODO(stuartmorgan): Temporarily ignore const warning in other parts of the + // federated package, and remove this. + // ignore: prefer_const_constructors_in_immutables SKPaymentTransactionWrapper({ required this.payment, required this.transactionState, @@ -175,31 +180,25 @@ class SKPaymentTransactionWrapper { if (other.runtimeType != runtimeType) { return false; } - final SKPaymentTransactionWrapper typedOther = - other as SKPaymentTransactionWrapper; - return typedOther.payment == payment && - typedOther.transactionState == transactionState && - typedOther.originalTransaction == originalTransaction && - typedOther.transactionTimeStamp == transactionTimeStamp && - typedOther.transactionIdentifier == transactionIdentifier && - typedOther.error == error; + return other is SKPaymentTransactionWrapper && + other.payment == payment && + other.transactionState == transactionState && + other.originalTransaction == originalTransaction && + other.transactionTimeStamp == transactionTimeStamp && + other.transactionIdentifier == transactionIdentifier && + other.error == error; } @override - int get hashCode => hashValues( - this.payment, - this.transactionState, - this.originalTransaction, - this.transactionTimeStamp, - this.transactionIdentifier, - this.error); + int get hashCode => hashValues(payment, transactionState, originalTransaction, + transactionTimeStamp, transactionIdentifier, error); @override String toString() => _$SKPaymentTransactionWrapperToJson(this).toString(); /// The payload that is used to finish this transaction. Map toFinishMap() => { - "transactionIdentifier": this.transactionIdentifier, - "productIdentifier": this.payment.productIdentifier, + 'transactionIdentifier': transactionIdentifier, + 'productIdentifier': payment.productIdentifier, }; } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart index 4110754da57b..105d999d8a69 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart @@ -4,6 +4,7 @@ import 'dart:ui' show hashValues; import 'package:collection/collection.dart'; +import 'package:flutter/foundation.dart'; import 'package:json_annotation/json_annotation.dart'; import 'enum_converters.dart'; @@ -17,8 +18,12 @@ part 'sk_product_wrapper.g.dart'; /// Represents the response object returned by [SKRequestMaker.startProductRequest]. /// Contains information about a list of products and a list of invalid product identifiers. @JsonSerializable() +@immutable class SkProductResponseWrapper { /// Creates an [SkProductResponseWrapper] with the given product details. + // TODO(stuartmorgan): Temporarily ignore const warning in other parts of the + // federated package, and remove this. + // ignore: prefer_const_constructors_in_immutables SkProductResponseWrapper( {required this.products, required this.invalidProductIdentifiers}); @@ -52,15 +57,14 @@ class SkProductResponseWrapper { if (other.runtimeType != runtimeType) { return false; } - final SkProductResponseWrapper typedOther = - other as SkProductResponseWrapper; - return DeepCollectionEquality().equals(typedOther.products, products) && - DeepCollectionEquality().equals( - typedOther.invalidProductIdentifiers, invalidProductIdentifiers); + return other is SkProductResponseWrapper && + const DeepCollectionEquality().equals(other.products, products) && + const DeepCollectionEquality() + .equals(other.invalidProductIdentifiers, invalidProductIdentifiers); } @override - int get hashCode => hashValues(this.products, this.invalidProductIdentifiers); + int get hashCode => hashValues(products, invalidProductIdentifiers); } /// Dart wrapper around StoreKit's [SKProductPeriodUnit](https://developer.apple.com/documentation/storekit/skproductperiodunit?language=objc). @@ -93,8 +97,12 @@ enum SKSubscriptionPeriodUnit { /// A period is defined by a [numberOfUnits] and a [unit], e.g for a 3 months period [numberOfUnits] is 3 and [unit] is a month. /// It is used as a property in [SKProductDiscountWrapper] and [SKProductWrapper]. @JsonSerializable() +@immutable class SKProductSubscriptionPeriodWrapper { /// Creates an [SKProductSubscriptionPeriodWrapper] for a `numberOfUnits`x`unit` period. + // TODO(stuartmorgan): Temporarily ignore const warning in other parts of the + // federated package, and remove this. + // ignore: prefer_const_constructors_in_immutables SKProductSubscriptionPeriodWrapper( {required this.numberOfUnits, required this.unit}); @@ -128,13 +136,13 @@ class SKProductSubscriptionPeriodWrapper { if (other.runtimeType != runtimeType) { return false; } - final SKProductSubscriptionPeriodWrapper typedOther = - other as SKProductSubscriptionPeriodWrapper; - return typedOther.numberOfUnits == numberOfUnits && typedOther.unit == unit; + return other is SKProductSubscriptionPeriodWrapper && + other.numberOfUnits == numberOfUnits && + other.unit == unit; } @override - int get hashCode => hashValues(this.numberOfUnits, this.unit); + int get hashCode => hashValues(numberOfUnits, unit); } /// Dart wrapper around StoreKit's [SKProductDiscountPaymentMode](https://developer.apple.com/documentation/storekit/skproductdiscountpaymentmode?language=objc). @@ -164,8 +172,12 @@ enum SKProductDiscountPaymentMode { /// /// It is used as a property in [SKProductWrapper]. @JsonSerializable() +@immutable class SKProductDiscountWrapper { /// Creates an [SKProductDiscountWrapper] with the given discount details. + // TODO(stuartmorgan): Temporarily ignore const warning in other parts of the + // federated package, and remove this. + // ignore: prefer_const_constructors_in_immutables SKProductDiscountWrapper( {required this.price, required this.priceLocale, @@ -211,18 +223,17 @@ class SKProductDiscountWrapper { if (other.runtimeType != runtimeType) { return false; } - final SKProductDiscountWrapper typedOther = - other as SKProductDiscountWrapper; - return typedOther.price == price && - typedOther.priceLocale == priceLocale && - typedOther.numberOfPeriods == numberOfPeriods && - typedOther.paymentMode == paymentMode && - typedOther.subscriptionPeriod == subscriptionPeriod; + return other is SKProductDiscountWrapper && + other.price == price && + other.priceLocale == priceLocale && + other.numberOfPeriods == numberOfPeriods && + other.paymentMode == paymentMode && + other.subscriptionPeriod == subscriptionPeriod; } @override - int get hashCode => hashValues(this.price, this.priceLocale, - this.numberOfPeriods, this.paymentMode, this.subscriptionPeriod); + int get hashCode => hashValues( + price, priceLocale, numberOfPeriods, paymentMode, subscriptionPeriod); } /// Dart wrapper around StoreKit's [SKProduct](https://developer.apple.com/documentation/storekit/skproduct?language=objc). @@ -230,8 +241,12 @@ class SKProductDiscountWrapper { /// A list of [SKProductWrapper] is returned in the [SKRequestMaker.startProductRequest] method, and /// should be stored for use when making a payment. @JsonSerializable() +@immutable class SKProductWrapper { /// Creates an [SKProductWrapper] with the given product details. + // TODO(stuartmorgan): Temporarily ignore const warning in other parts of the + // federated package, and remove this. + // ignore: prefer_const_constructors_in_immutables SKProductWrapper({ required this.productIdentifier, required this.localizedTitle, @@ -314,29 +329,29 @@ class SKProductWrapper { if (other.runtimeType != runtimeType) { return false; } - final SKProductWrapper typedOther = other as SKProductWrapper; - return typedOther.productIdentifier == productIdentifier && - typedOther.localizedTitle == localizedTitle && - typedOther.localizedDescription == localizedDescription && - typedOther.priceLocale == priceLocale && - typedOther.subscriptionGroupIdentifier == subscriptionGroupIdentifier && - typedOther.price == price && - typedOther.subscriptionPeriod == subscriptionPeriod && - typedOther.introductoryPrice == introductoryPrice && - DeepCollectionEquality().equals(typedOther.discounts, discounts); + return other is SKProductWrapper && + other.productIdentifier == productIdentifier && + other.localizedTitle == localizedTitle && + other.localizedDescription == localizedDescription && + other.priceLocale == priceLocale && + other.subscriptionGroupIdentifier == subscriptionGroupIdentifier && + other.price == price && + other.subscriptionPeriod == subscriptionPeriod && + other.introductoryPrice == introductoryPrice && + const DeepCollectionEquality().equals(other.discounts, discounts); } @override int get hashCode => hashValues( - this.productIdentifier, - this.localizedTitle, - this.localizedDescription, - this.priceLocale, - this.subscriptionGroupIdentifier, - this.price, - this.subscriptionPeriod, - this.introductoryPrice, - this.discounts); + productIdentifier, + localizedTitle, + localizedDescription, + priceLocale, + subscriptionGroupIdentifier, + price, + subscriptionPeriod, + introductoryPrice, + discounts); } /// Object that indicates the locale of the price @@ -346,8 +361,12 @@ class SKProductWrapper { // Matching android to only get the currencySymbol for now. // https://github.com/flutter/flutter/issues/26610 @JsonSerializable() +@immutable class SKPriceLocaleWrapper { /// Creates a new price locale for `currencySymbol` and `currencyCode`. + // TODO(stuartmorgan): Temporarily ignore const warning in other parts of the + // federated package, and remove this. + // ignore: prefer_const_constructors_in_immutables SKPriceLocaleWrapper({ required this.currencySymbol, required this.currencyCode, @@ -385,11 +404,11 @@ class SKPriceLocaleWrapper { if (other.runtimeType != runtimeType) { return false; } - final SKPriceLocaleWrapper typedOther = other as SKPriceLocaleWrapper; - return typedOther.currencySymbol == currencySymbol && - typedOther.currencyCode == currencyCode; + return other is SKPriceLocaleWrapper && + other.currencySymbol == currencySymbol && + other.currencyCode == currencyCode; } @override - int get hashCode => hashValues(this.currencySymbol, this.currencyCode); + int get hashCode => hashValues(currencySymbol, currencyCode); } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_receipt_manager.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_receipt_manager.dart index 3eb41cb66a14..b31a3d59c172 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_receipt_manager.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_receipt_manager.dart @@ -6,7 +6,8 @@ import 'dart:async'; import '../channel.dart'; -///This class contains static methods to manage StoreKit receipts. +// ignore: avoid_classes_with_only_static_members +/// This class contains static methods to manage StoreKit receipts. class SKReceiptManager { /// Retrieve the receipt data from your application's main bundle. /// diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_storefront_wrapper.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_storefront_wrapper.dart index bcdae3206c00..0bf1103a5abd 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_storefront_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_storefront_wrapper.dart @@ -4,6 +4,7 @@ import 'dart:ui' show hashValues; +import 'package:flutter/foundation.dart'; import 'package:json_annotation/json_annotation.dart'; part 'sk_storefront_wrapper.g.dart'; @@ -13,8 +14,12 @@ part 'sk_storefront_wrapper.g.dart'; /// Dart wrapper around StoreKit's /// [SKStorefront](https://developer.apple.com/documentation/storekit/skstorefront?language=objc). @JsonSerializable(createToJson: true) +@immutable class SKStorefrontWrapper { /// Creates a new [SKStorefrontWrapper] with the provided information. + // TODO(stuartmorgan): Temporarily ignore const warning in other parts of the + // federated package, and remove this. + // ignore: prefer_const_constructors_in_immutables SKStorefrontWrapper({ required this.countryCode, required this.identifier, @@ -45,15 +50,15 @@ class SKStorefrontWrapper { if (other.runtimeType != runtimeType) { return false; } - final SKStorefrontWrapper typedOther = other as SKStorefrontWrapper; - return typedOther.countryCode == countryCode && - typedOther.identifier == identifier; + return other is SKStorefrontWrapper && + other.countryCode == countryCode && + other.identifier == identifier; } @override int get hashCode => hashValues( - this.countryCode, - this.identifier, + countryCode, + identifier, ); @override diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/types/app_store_product_details.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/types/app_store_product_details.dart index ff1153e27e47..a5d8c7287e3c 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/types/app_store_product_details.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/types/app_store_product_details.dart @@ -30,10 +30,6 @@ class AppStoreProductDetails extends ProductDetails { currencySymbol: currencySymbol, ); - /// Points back to the [SKProductWrapper] object that was used to generate - /// this [AppStoreProductDetails] object. - final SKProductWrapper skProduct; - /// Generate a [AppStoreProductDetails] object based on an iOS [SKProductWrapper] object. factory AppStoreProductDetails.fromSKProduct(SKProductWrapper product) { return AppStoreProductDetails( @@ -49,4 +45,8 @@ class AppStoreProductDetails extends ProductDetails { skProduct: product, ); } + + /// Points back to the [SKProductWrapper] object that was used to generate + /// this [AppStoreProductDetails] object. + final SKProductWrapper skProduct; } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/types/app_store_purchase_details.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/types/app_store_purchase_details.dart index ac547be94f10..42cb225ede0a 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/types/app_store_purchase_details.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/types/app_store_purchase_details.dart @@ -29,22 +29,6 @@ class AppStorePurchaseDetails extends PurchaseDetails { this.status = status; } - /// Points back to the [SKPaymentTransactionWrapper] which was used to - /// generate this [AppStorePurchaseDetails] object. - final SKPaymentTransactionWrapper skPaymentTransaction; - - late PurchaseStatus _status; - - /// The status that this [PurchaseDetails] is currently on. - PurchaseStatus get status => _status; - set status(PurchaseStatus status) { - _pendingCompletePurchase = status != PurchaseStatus.pending; - _status = status; - } - - bool _pendingCompletePurchase = false; - bool get pendingCompletePurchase => _pendingCompletePurchase; - /// Generate a [AppStorePurchaseDetails] object based on an iOS /// [SKPaymentTransactionWrapper] object. factory AppStorePurchaseDetails.fromSKTransaction( @@ -55,7 +39,7 @@ class AppStorePurchaseDetails extends PurchaseDetails { productID: transaction.payment.productIdentifier, purchaseID: transaction.transactionIdentifier, skPaymentTransaction: transaction, - status: SKTransactionStatusConverter() + status: const SKTransactionStatusConverter() .toPurchaseStatus(transaction.transactionState, transaction.error), transactionDate: transaction.transactionTimeStamp != null ? (transaction.transactionTimeStamp! * 1000).toInt().toString() @@ -78,4 +62,23 @@ class AppStorePurchaseDetails extends PurchaseDetails { return purchaseDetails; } + + /// Points back to the [SKPaymentTransactionWrapper] which was used to + /// generate this [AppStorePurchaseDetails] object. + final SKPaymentTransactionWrapper skPaymentTransaction; + + late PurchaseStatus _status; + + /// The status that this [PurchaseDetails] is currently on. + @override + PurchaseStatus get status => _status; + @override + set status(PurchaseStatus status) { + _pendingCompletePurchase = status != PurchaseStatus.pending; + _status = status; + } + + bool _pendingCompletePurchase = false; + @override + bool get pendingCompletePurchase => _pendingCompletePurchase; } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml index 3cb04cd815df..7e701686e00d 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_storekit description: An implementation for the iOS platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase_storekit issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.3.0+1 +version: 0.3.0+2 environment: sdk: ">=2.14.0 <3.0.0" @@ -27,5 +27,4 @@ dev_dependencies: flutter_test: sdk: flutter json_serializable: ^6.0.0 - pedantic: ^1.10.0 test: ^1.16.0 diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart index 352ba3206145..9667d789f1f7 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart @@ -33,12 +33,12 @@ class FakeStoreKitPlatform { bool queueIsActive = false; void reset() { - transactions = []; + transactions = []; receiptData = 'dummy base64data'; - validProductIDs = ['123', '456'].toSet(); - validProducts = Map(); - for (String validID in validProductIDs) { - Map productWrapperMap = + validProductIDs = {'123', '456'}; + validProducts = {}; + for (final String validID in validProductIDs) { + final Map productWrapperMap = buildProductMap(dummyProductWrapper); productWrapperMap['productIdentifier'] = validID; if (validID == '456') { @@ -47,7 +47,7 @@ class FakeStoreKitPlatform { validProducts[validID] = SKProductWrapper.fromJson(productWrapperMap); } - finishedTransactions = []; + finishedTransactions = []; testRestoredTransactionsNull = false; testTransactionFail = false; testTransactionCancel = -1; @@ -84,10 +84,10 @@ class FakeStoreKitPlatform { payment: SKPaymentWrapper(productIdentifier: productId), transactionState: SKPaymentTransactionStateWrapper.failed, transactionTimeStamp: 123123.121, - error: SKError( + error: const SKError( code: 0, domain: 'ios_domain', - userInfo: {'message': 'an error message'}), + userInfo: {'message': 'an error message'}), originalTransaction: null); } @@ -101,7 +101,7 @@ class FakeStoreKitPlatform { error: SKError( code: errorCode, domain: 'ios_domain', - userInfo: {'message': 'an error message'}), + userInfo: const {'message': 'an error message'}), originalTransaction: null); } @@ -124,18 +124,18 @@ class FakeStoreKitPlatform { if (queryProductException != null) { throw queryProductException!; } - List productIDS = - List.castFrom(call.arguments); - List invalidFound = []; - List products = []; - for (String productID in productIDS) { + final List productIDS = + List.castFrom(call.arguments as List); + final List invalidFound = []; + final List products = []; + for (final String productID in productIDS) { if (!validProductIDs.contains(productID)) { invalidFound.add(productID); } else { products.add(validProducts[productID]!); } } - SkProductResponseWrapper response = SkProductResponseWrapper( + final SkProductResponseWrapper response = SkProductResponseWrapper( products: products, invalidProductIdentifiers: invalidFound); return Future>.value( buildProductResponseMap(response)); @@ -158,7 +158,7 @@ class FakeStoreKitPlatform { return Future.sync(() {}); case '-[InAppPurchasePlugin retrieveReceiptData:result:]': if (receiptData != null) { - return Future.value(receiptData); + return Future.value(receiptData); } else { throw PlatformException(code: 'no_receipt_data'); } @@ -166,33 +166,34 @@ class FakeStoreKitPlatform { receiptData = 'refreshed receipt data'; return Future.sync(() {}); case '-[InAppPurchasePlugin addPayment:result:]': - String id = call.arguments['productIdentifier']; - SKPaymentTransactionWrapper transaction = createPendingTransaction(id); - InAppPurchaseStoreKitPlatform.observer - .updatedTransactions(transactions: [transaction]); + final String id = call.arguments['productIdentifier'] as String; + final SKPaymentTransactionWrapper transaction = + createPendingTransaction(id); + InAppPurchaseStoreKitPlatform.observer.updatedTransactions( + transactions: [transaction]); sleep(const Duration(milliseconds: 30)); if (testTransactionFail) { - SKPaymentTransactionWrapper transaction_failed = + final SKPaymentTransactionWrapper transactionFailed = createFailedTransaction(id); - InAppPurchaseStoreKitPlatform.observer - .updatedTransactions(transactions: [transaction_failed]); + InAppPurchaseStoreKitPlatform.observer.updatedTransactions( + transactions: [transactionFailed]); } else if (testTransactionCancel > 0) { - SKPaymentTransactionWrapper transaction_canceled = + final SKPaymentTransactionWrapper transactionCanceled = createCanceledTransaction(id, testTransactionCancel); - InAppPurchaseStoreKitPlatform.observer - .updatedTransactions(transactions: [transaction_canceled]); + InAppPurchaseStoreKitPlatform.observer.updatedTransactions( + transactions: [transactionCanceled]); } else { - SKPaymentTransactionWrapper transaction_finished = + final SKPaymentTransactionWrapper transactionFinished = createPurchasedTransaction( id, transaction.transactionIdentifier ?? ''); - InAppPurchaseStoreKitPlatform.observer - .updatedTransactions(transactions: [transaction_finished]); + InAppPurchaseStoreKitPlatform.observer.updatedTransactions( + transactions: [transactionFinished]); } break; case '-[InAppPurchasePlugin finishTransaction:result:]': finishedTransactions.add(createPurchasedTransaction( - call.arguments["productIdentifier"], - call.arguments["transactionIdentifier"])); + call.arguments['productIdentifier'] as String, + call.arguments['transactionIdentifier'] as String)); break; case '-[SKPaymentQueue startObservingTransactionQueue]': queueIsActive = true; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_addtion_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_addtion_test.dart index 967eeb73361b..dfdff5117091 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_addtion_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_addtion_test.dart @@ -4,8 +4,8 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart'; import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; +import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart'; import 'fakes/fake_storekit_platform.dart'; @@ -22,15 +22,14 @@ void main() { group('present code redemption sheet', () { test('null', () async { expect( - await InAppPurchaseStoreKitPlatformAddition() - .presentCodeRedemptionSheet(), - null); + InAppPurchaseStoreKitPlatformAddition().presentCodeRedemptionSheet(), + completes); }); }); group('refresh receipt data', () { test('should refresh receipt data', () async { - PurchaseVerificationData? receiptData = + final PurchaseVerificationData? receiptData = await InAppPurchaseStoreKitPlatformAddition() .refreshPurchaseVerificationData(); expect(receiptData, isNotNull); diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_test.dart index 12595bbf0ed0..e92d8487ed93 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_test.dart @@ -6,10 +6,10 @@ import 'dart:async'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart'; import 'package:in_app_purchase_storekit/src/store_kit_wrappers/enum_converters.dart'; import 'package:in_app_purchase_storekit/store_kit_wrappers.dart'; -import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; import 'fakes/fake_storekit_platform.dart'; import 'store_kit_wrappers/sk_test_stub_objects.dart'; @@ -44,12 +44,12 @@ void main() { test('should get product list and correct invalid identifiers', () async { final InAppPurchaseStoreKitPlatform connection = InAppPurchaseStoreKitPlatform(); - final ProductDetailsResponse response = await connection - .queryProductDetails(['123', '456', '789'].toSet()); - List products = response.productDetails; + final ProductDetailsResponse response = + await connection.queryProductDetails({'123', '456', '789'}); + final List products = response.productDetails; expect(products.first.id, '123'); expect(products[1].id, '456'); - expect(response.notFoundIDs, ['789']); + expect(response.notFoundIDs, ['789']); expect(response.error, isNull); expect(response.productDetails.first.currencySymbol, r'$'); expect(response.productDetails[1].currencySymbol, 'EUR'); @@ -61,18 +61,18 @@ void main() { fakeStoreKitPlatform.queryProductException = PlatformException( code: 'error_code', message: 'error_message', - details: {'info': 'error_info'}); + details: {'info': 'error_info'}); final InAppPurchaseStoreKitPlatform connection = InAppPurchaseStoreKitPlatform(); - final ProductDetailsResponse response = await connection - .queryProductDetails(['123', '456', '789'].toSet()); - expect(response.productDetails, []); - expect(response.notFoundIDs, ['123', '456', '789']); + final ProductDetailsResponse response = + await connection.queryProductDetails({'123', '456', '789'}); + expect(response.productDetails, []); + expect(response.notFoundIDs, ['123', '456', '789']); expect(response.error, isNotNull); expect(response.error!.source, kIAPSource); expect(response.error!.code, 'error_code'); expect(response.error!.message, 'error_message'); - expect(response.error!.details, {'info': 'error_info'}); + expect(response.error!.details, {'info': 'error_info'}); }); }); @@ -82,11 +82,13 @@ void main() { 0, fakeStoreKitPlatform.createRestoredTransaction('foo', 'RT1')); fakeStoreKitPlatform.transactions.insert( 1, fakeStoreKitPlatform.createRestoredTransaction('foo', 'RT2')); - Completer completer = Completer(); - Stream> stream = iapStoreKitPlatform.purchaseStream; + final Completer> completer = + Completer>(); + final Stream> stream = + iapStoreKitPlatform.purchaseStream; - late StreamSubscription subscription; - subscription = stream.listen((purchaseDetailsList) { + late StreamSubscription> subscription; + subscription = stream.listen((List purchaseDetailsList) { if (purchaseDetailsList.first.status == PurchaseStatus.restored) { subscription.cancel(); completer.complete(purchaseDetailsList); @@ -94,13 +96,13 @@ void main() { }); await iapStoreKitPlatform.restorePurchases(); - List details = await completer.future; + final List details = await completer.future; expect(details.length, 2); for (int i = 0; i < fakeStoreKitPlatform.transactions.length; i++) { - SKPaymentTransactionWrapper expected = + final SKPaymentTransactionWrapper expected = fakeStoreKitPlatform.transactions[i]; - PurchaseDetails actual = details[i]; + final PurchaseDetails actual = details[i]; expect(actual.purchaseID, expected.transactionIdentifier); expect(actual.verificationData, isNotNull); @@ -117,11 +119,13 @@ void main() { 'should emit empty transaction list on purchase stream when there is nothing to restore', () async { fakeStoreKitPlatform.testRestoredTransactionsNull = true; - Completer completer = Completer(); - Stream> stream = iapStoreKitPlatform.purchaseStream; + final Completer?> completer = + Completer?>(); + final Stream> stream = + iapStoreKitPlatform.purchaseStream; - late StreamSubscription subscription; - subscription = stream.listen((purchaseDetailsList) { + late StreamSubscription> subscription; + subscription = stream.listen((List purchaseDetailsList) { expect(purchaseDetailsList.isEmpty, true); subscription.cancel(); completer.complete(); @@ -138,29 +142,31 @@ void main() { 1, fakeStoreKitPlatform.createPurchasedTransaction('foo', 'bar')); fakeStoreKitPlatform.transactions.insert( 2, fakeStoreKitPlatform.createRestoredTransaction('foo', 'RT2')); - Completer completer = Completer(); - Stream> stream = iapStoreKitPlatform.purchaseStream; + final Completer> completer = + Completer>(); + final Stream> stream = + iapStoreKitPlatform.purchaseStream; - late StreamSubscription subscription; - subscription = stream.listen((purchaseDetailsList) { + late StreamSubscription> subscription; + subscription = stream.listen((List purchaseDetailsList) { if (purchaseDetailsList[1].status == PurchaseStatus.purchased) { completer.complete(purchaseDetailsList); subscription.cancel(); } }); await iapStoreKitPlatform.restorePurchases(); - List details = await completer.future; + final List details = await completer.future; expect(details.length, 3); for (int i = 0; i < fakeStoreKitPlatform.transactions.length; i++) { - SKPaymentTransactionWrapper expected = + final SKPaymentTransactionWrapper expected = fakeStoreKitPlatform.transactions[i]; - PurchaseDetails actual = details[i]; + final PurchaseDetails actual = details[i]; expect(actual.purchaseID, expected.transactionIdentifier); expect(actual.verificationData, isNotNull); expect( actual.status, - SKTransactionStatusConverter() + const SKTransactionStatusConverter() .toPurchaseStatus(expected.transactionState, expected.error), ); expect(actual.verificationData.localVerificationData, @@ -176,12 +182,15 @@ void main() { () async { fakeStoreKitPlatform.transactions.insert( 0, fakeStoreKitPlatform.createPurchasedTransaction('foo', 'bar')); - Completer completer = Completer(); - Stream> stream = iapStoreKitPlatform.purchaseStream; - List> purchaseDetails = []; - - late StreamSubscription subscription; - subscription = stream.listen((purchaseDetailsList) { + final Completer>> completer = + Completer>>(); + final Stream> stream = + iapStoreKitPlatform.purchaseStream; + final List> purchaseDetails = + >[]; + + late StreamSubscription> subscription; + subscription = stream.listen((List purchaseDetailsList) { purchaseDetails.add(purchaseDetailsList); if (purchaseDetails.length == 2) { @@ -190,19 +199,19 @@ void main() { } }); await iapStoreKitPlatform.restorePurchases(); - final details = await completer.future; + final List> details = await completer.future; expect(details.length, 2); - expect(details[0], []); + expect(details[0], >[]); for (int i = 0; i < fakeStoreKitPlatform.transactions.length; i++) { - SKPaymentTransactionWrapper expected = + final SKPaymentTransactionWrapper expected = fakeStoreKitPlatform.transactions[i]; - PurchaseDetails actual = details[1][i]; + final PurchaseDetails actual = details[1][i]; expect(actual.purchaseID, expected.transactionIdentifier); expect(actual.verificationData, isNotNull); expect( actual.status, - SKTransactionStatusConverter() + const SKTransactionStatusConverter() .toPurchaseStatus(expected.transactionState, expected.error), ); expect(actual.verificationData.localVerificationData, @@ -220,11 +229,13 @@ void main() { fakeStoreKitPlatform.transactions.insert( 1, fakeStoreKitPlatform.createRestoredTransaction('foo', 'RT2')); fakeStoreKitPlatform.receiptData = null; - Completer completer = Completer(); - Stream> stream = iapStoreKitPlatform.purchaseStream; + final Completer> completer = + Completer>(); + final Stream> stream = + iapStoreKitPlatform.purchaseStream; - late StreamSubscription subscription; - subscription = stream.listen((purchaseDetailsList) { + late StreamSubscription> subscription; + subscription = stream.listen((List purchaseDetailsList) { if (purchaseDetailsList.first.status == PurchaseStatus.restored) { completer.complete(purchaseDetailsList); subscription.cancel(); @@ -232,28 +243,28 @@ void main() { }); await iapStoreKitPlatform.restorePurchases(); - List details = await completer.future; + final List details = await completer.future; - for (PurchaseDetails purchase in details) { + for (final PurchaseDetails purchase in details) { expect(purchase.verificationData.localVerificationData, isEmpty); expect(purchase.verificationData.serverVerificationData, isEmpty); } }); test('test restore error', () { - fakeStoreKitPlatform.testRestoredError = SKError( + fakeStoreKitPlatform.testRestoredError = const SKError( code: 123, domain: 'error_test', - userInfo: {'message': 'errorMessage'}); + userInfo: {'message': 'errorMessage'}); expect( () => iapStoreKitPlatform.restorePurchases(), throwsA( isA() - .having((error) => error.code, 'code', 123) - .having((error) => error.domain, 'domain', 'error_test') - .having((error) => error.userInfo, 'userInfo', - {'message': 'errorMessage'}), + .having((SKError error) => error.code, 'code', 123) + .having((SKError error) => error.domain, 'domain', 'error_test') + .having((SKError error) => error.userInfo, 'userInfo', + {'message': 'errorMessage'}), )); }); }); @@ -262,12 +273,14 @@ void main() { test( 'buying non consumable, should get purchase objects in the purchase update callback', () async { - List details = []; - Completer completer = Completer(); - Stream> stream = iapStoreKitPlatform.purchaseStream; - - late StreamSubscription subscription; - subscription = stream.listen((purchaseDetailsList) { + final List details = []; + final Completer> completer = + Completer>(); + final Stream> stream = + iapStoreKitPlatform.purchaseStream; + + late StreamSubscription> subscription; + subscription = stream.listen((List purchaseDetailsList) { details.addAll(purchaseDetailsList); if (purchaseDetailsList.first.status == PurchaseStatus.purchased) { completer.complete(details); @@ -280,7 +293,7 @@ void main() { applicationUserName: 'appName'); await iapStoreKitPlatform.buyNonConsumable(purchaseParam: purchaseParam); - List result = await completer.future; + final List result = await completer.future; expect(result.length, 2); expect(result.first.productID, dummyProductWrapper.productIdentifier); }); @@ -288,12 +301,14 @@ void main() { test( 'buying consumable, should get purchase objects in the purchase update callback', () async { - List details = []; - Completer completer = Completer(); - Stream> stream = iapStoreKitPlatform.purchaseStream; - - late StreamSubscription subscription; - subscription = stream.listen((purchaseDetailsList) { + final List details = []; + final Completer> completer = + Completer>(); + final Stream> stream = + iapStoreKitPlatform.purchaseStream; + + late StreamSubscription> subscription; + subscription = stream.listen((List purchaseDetailsList) { details.addAll(purchaseDetailsList); if (purchaseDetailsList.first.status == PurchaseStatus.purchased) { completer.complete(details); @@ -306,7 +321,7 @@ void main() { applicationUserName: 'appName'); await iapStoreKitPlatform.buyConsumable(purchaseParam: purchaseParam); - List result = await completer.future; + final List result = await completer.future; expect(result.length, 2); expect(result.first.productID, dummyProductWrapper.productIdentifier); }); @@ -324,21 +339,22 @@ void main() { test('should get failed purchase status', () async { fakeStoreKitPlatform.testTransactionFail = true; - List details = []; - Completer completer = Completer(); + final List details = []; + final Completer completer = Completer(); late IAPError error; - Stream> stream = iapStoreKitPlatform.purchaseStream; - late StreamSubscription subscription; - subscription = stream.listen((purchaseDetailsList) { + final Stream> stream = + iapStoreKitPlatform.purchaseStream; + late StreamSubscription> subscription; + subscription = stream.listen((List purchaseDetailsList) { details.addAll(purchaseDetailsList); - purchaseDetailsList.forEach((purchaseDetails) { + for (final PurchaseDetails purchaseDetails in purchaseDetailsList) { if (purchaseDetails.status == PurchaseStatus.error) { error = purchaseDetails.error!; completer.complete(error); subscription.cancel(); } - }); + } }); final AppStorePurchaseParam purchaseParam = AppStorePurchaseParam( productDetails: @@ -346,30 +362,32 @@ void main() { applicationUserName: 'appName'); await iapStoreKitPlatform.buyNonConsumable(purchaseParam: purchaseParam); - IAPError completerError = await completer.future; + final IAPError completerError = await completer.future; expect(completerError.code, 'purchase_error'); expect(completerError.source, kIAPSource); expect(completerError.message, 'ios_domain'); - expect(completerError.details, {'message': 'an error message'}); + expect(completerError.details, + {'message': 'an error message'}); }); test( 'should get canceled purchase status when error code is SKErrorPaymentCancelled', () async { fakeStoreKitPlatform.testTransactionCancel = 2; - List details = []; - Completer completer = Completer(); + final List details = []; + final Completer completer = Completer(); - Stream> stream = iapStoreKitPlatform.purchaseStream; - late StreamSubscription subscription; - subscription = stream.listen((purchaseDetailsList) { + final Stream> stream = + iapStoreKitPlatform.purchaseStream; + late StreamSubscription> subscription; + subscription = stream.listen((List purchaseDetailsList) { details.addAll(purchaseDetailsList); - purchaseDetailsList.forEach((purchaseDetails) { + for (final PurchaseDetails purchaseDetails in purchaseDetailsList) { if (purchaseDetails.status == PurchaseStatus.canceled) { completer.complete(purchaseDetails.status); subscription.cancel(); } - }); + } }); final AppStorePurchaseParam purchaseParam = AppStorePurchaseParam( productDetails: @@ -377,7 +395,7 @@ void main() { applicationUserName: 'appName'); await iapStoreKitPlatform.buyNonConsumable(purchaseParam: purchaseParam); - PurchaseStatus purchaseStatus = await completer.future; + final PurchaseStatus purchaseStatus = await completer.future; expect(purchaseStatus, PurchaseStatus.canceled); }); @@ -385,19 +403,20 @@ void main() { 'should get canceled purchase status when error code is SKErrorOverlayCancelled', () async { fakeStoreKitPlatform.testTransactionCancel = 15; - List details = []; - Completer completer = Completer(); + final List details = []; + final Completer completer = Completer(); - Stream> stream = iapStoreKitPlatform.purchaseStream; - late StreamSubscription subscription; - subscription = stream.listen((purchaseDetailsList) { + final Stream> stream = + iapStoreKitPlatform.purchaseStream; + late StreamSubscription> subscription; + subscription = stream.listen((List purchaseDetailsList) { details.addAll(purchaseDetailsList); - purchaseDetailsList.forEach((purchaseDetails) { + for (final PurchaseDetails purchaseDetails in purchaseDetailsList) { if (purchaseDetails.status == PurchaseStatus.canceled) { completer.complete(purchaseDetails.status); subscription.cancel(); } - }); + } }); final AppStorePurchaseParam purchaseParam = AppStorePurchaseParam( productDetails: @@ -405,33 +424,35 @@ void main() { applicationUserName: 'appName'); await iapStoreKitPlatform.buyNonConsumable(purchaseParam: purchaseParam); - PurchaseStatus purchaseStatus = await completer.future; + final PurchaseStatus purchaseStatus = await completer.future; expect(purchaseStatus, PurchaseStatus.canceled); }); }); group('complete purchase', () { test('should complete purchase', () async { - List details = []; - Completer completer = Completer(); - Stream> stream = iapStoreKitPlatform.purchaseStream; - late StreamSubscription subscription; - subscription = stream.listen((purchaseDetailsList) { + final List details = []; + final Completer> completer = + Completer>(); + final Stream> stream = + iapStoreKitPlatform.purchaseStream; + late StreamSubscription> subscription; + subscription = stream.listen((List purchaseDetailsList) { details.addAll(purchaseDetailsList); - purchaseDetailsList.forEach((purchaseDetails) { + for (final PurchaseDetails purchaseDetails in purchaseDetailsList) { if (purchaseDetails.pendingCompletePurchase) { iapStoreKitPlatform.completePurchase(purchaseDetails); completer.complete(details); subscription.cancel(); } - }); + } }); final AppStorePurchaseParam purchaseParam = AppStorePurchaseParam( productDetails: AppStoreProductDetails.fromSKProduct(dummyProductWrapper), applicationUserName: 'appName'); await iapStoreKitPlatform.buyNonConsumable(purchaseParam: purchaseParam); - List result = await completer.future; + final List result = await completer.future; expect(result.length, 2); expect(result.first.productID, dummyProductWrapper.productIdentifier); expect(fakeStoreKitPlatform.finishedTransactions.length, 1); @@ -440,11 +461,14 @@ void main() { group('purchase stream', () { test('Should only have active queue when purchaseStream has listeners', () { - Stream> stream = iapStoreKitPlatform.purchaseStream; + final Stream> stream = + iapStoreKitPlatform.purchaseStream; expect(fakeStoreKitPlatform.queueIsActive, false); - StreamSubscription subscription1 = stream.listen((event) {}); + final StreamSubscription> subscription1 = + stream.listen((List event) {}); expect(fakeStoreKitPlatform.queueIsActive, true); - StreamSubscription subscription2 = stream.listen((event) {}); + final StreamSubscription> subscription2 = + stream.listen((List event) {}); expect(fakeStoreKitPlatform.queueIsActive, true); subscription1.cancel(); expect(fakeStoreKitPlatform.queueIsActive, true); diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart index c5cabd2dd965..2baf20892ab6 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart @@ -28,15 +28,15 @@ void main() { group('sk_request_maker', () { test('get products method channel', () async { - SkProductResponseWrapper productResponseWrapper = - await SKRequestMaker().startProductRequest(['xxx']); + final SkProductResponseWrapper productResponseWrapper = + await SKRequestMaker().startProductRequest(['xxx']); expect( productResponseWrapper.products, isNotEmpty, ); expect( productResponseWrapper.products.first.priceLocale.currencySymbol, - '\$', + r'$', ); expect( @@ -58,7 +58,7 @@ void main() { expect( fakeStoreKitPlatform.startProductRequestParam, - ['xxx'], + ['xxx'], ); }); @@ -72,25 +72,25 @@ void main() { }); test('refreshed receipt', () async { - int receiptCountBefore = fakeStoreKitPlatform.refreshReceipt; + final int receiptCountBefore = fakeStoreKitPlatform.refreshReceipt; await SKRequestMaker().startRefreshReceiptRequest( - receiptProperties: {"isExpired": true}); + receiptProperties: {'isExpired': true}); expect(fakeStoreKitPlatform.refreshReceipt, receiptCountBefore + 1); expect(fakeStoreKitPlatform.refreshReceiptParam, - {"isExpired": true}); + {'isExpired': true}); }); test('should get null receipt if any exceptions are raised', () async { fakeStoreKitPlatform.getReceiptFailTest = true; expect(() async => SKReceiptManager.retrieveReceiptData(), - throwsA(TypeMatcher())); + throwsA(const TypeMatcher())); }); }); group('sk_receipt_manager', () { test('should get receipt (faking it by returning a `receipt data` string)', () async { - String receiptData = await SKReceiptManager.retrieveReceiptData(); + final String receiptData = await SKReceiptManager.retrieveReceiptData(); expect(receiptData, 'receipt data'); }); }); @@ -118,8 +118,8 @@ void main() { }); test('should add payment to the payment queue', () async { - SKPaymentQueueWrapper queue = SKPaymentQueueWrapper(); - TestPaymentTransactionObserver observer = + final SKPaymentQueueWrapper queue = SKPaymentQueueWrapper(); + final TestPaymentTransactionObserver observer = TestPaymentTransactionObserver(); queue.setTransactionObserver(observer); await queue.addPayment(dummyPayment); @@ -127,8 +127,8 @@ void main() { }); test('should finish transaction', () async { - SKPaymentQueueWrapper queue = SKPaymentQueueWrapper(); - TestPaymentTransactionObserver observer = + final SKPaymentQueueWrapper queue = SKPaymentQueueWrapper(); + final TestPaymentTransactionObserver observer = TestPaymentTransactionObserver(); queue.setTransactionObserver(observer); await queue.finishTransaction(dummyTransaction); @@ -137,8 +137,8 @@ void main() { }); test('should restore transaction', () async { - SKPaymentQueueWrapper queue = SKPaymentQueueWrapper(); - TestPaymentTransactionObserver observer = + final SKPaymentQueueWrapper queue = SKPaymentQueueWrapper(); + final TestPaymentTransactionObserver observer = TestPaymentTransactionObserver(); queue.setTransactionObserver(observer); await queue.restoreTransactions(applicationUserName: 'aUserID'); @@ -188,7 +188,7 @@ class FakeStoreKitPlatform { channel.setMockMethodCallHandler(onMethodCall); } // get product request - List startProductRequestParam = []; + List startProductRequestParam = []; bool getProductRequestFailTest = false; bool testReturnNull = false; @@ -200,8 +200,8 @@ class FakeStoreKitPlatform { late Map refreshReceiptParam; // payment queue - List payments = []; - List> transactionsFinished = []; + List payments = []; + List> transactionsFinished = >[]; String applicationNameHasTransactionRestored = ''; // present Code Redemption @@ -220,7 +220,7 @@ class FakeStoreKitPlatform { switch (call.method) { // request makers case '-[InAppPurchasePlugin startProductRequest:result:]': - startProductRequestParam = call.arguments; + startProductRequestParam = call.arguments as List; if (getProductRequestFailTest) { return Future.value(null); } @@ -228,13 +228,13 @@ class FakeStoreKitPlatform { buildProductResponseMap(dummyProductResponseWrapper)); case '-[InAppPurchasePlugin refreshReceipt:result:]': refreshReceipt++; - refreshReceiptParam = - Map.castFrom(call.arguments); + refreshReceiptParam = Map.castFrom( + call.arguments as Map); return Future.sync(() {}); // receipt manager case '-[InAppPurchasePlugin retrieveReceiptData:result:]': if (getReceiptFailTest) { - throw ("some arbitrary error"); + throw 'some arbitrary error'; } return Future.value('receipt data'); // payment queue @@ -245,16 +245,17 @@ class FakeStoreKitPlatform { return Future.value(true); case '-[SKPaymentQueue transactions]': return Future>.value( - [buildTransactionMap(dummyTransaction)]); + [buildTransactionMap(dummyTransaction)]); case '-[InAppPurchasePlugin addPayment:result:]': - payments.add(SKPaymentWrapper.fromJson( - Map.from(call.arguments))); + payments.add(SKPaymentWrapper.fromJson(Map.from( + call.arguments as Map))); return Future.sync(() {}); case '-[InAppPurchasePlugin finishTransaction:result:]': - transactionsFinished.add(Map.from(call.arguments)); + transactionsFinished.add( + Map.from(call.arguments as Map)); return Future.sync(() {}); case '-[InAppPurchasePlugin restoreTransactions:result:]': - applicationNameHasTransactionRestored = call.arguments; + applicationNameHasTransactionRestored = call.arguments as String; return Future.sync(() {}); case '-[InAppPurchasePlugin presentCodeRedemptionSheet:result:]': presentCodeRedemption = true; @@ -275,23 +276,28 @@ class FakeStoreKitPlatform { showPriceConsentIfNeeded = true; return Future.sync(() {}); } - return Future.error('method not mocked'); + return Future.error('method not mocked'); } } class TestPaymentQueueDelegate extends SKPaymentQueueDelegateWrapper {} class TestPaymentTransactionObserver extends SKTransactionObserverWrapper { + @override void updatedTransactions( {required List transactions}) {} + @override void removedTransactions( {required List transactions}) {} + @override void restoreCompletedTransactionsFailed({required SKError error}) {} + @override void paymentQueueRestoreCompletedTransactionsFinished() {} + @override bool shouldAddStorePayment( {required SKPaymentWrapper payment, required SKProductWrapper product}) { return true; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_payment_queue_delegate_api_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_payment_queue_delegate_api_test.dart index 2e86ee49f627..df76254aabf7 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_payment_queue_delegate_api_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_payment_queue_delegate_api_test.dart @@ -20,8 +20,8 @@ void main() { test( 'handlePaymentQueueDelegateCallbacks should call SKPaymentQueueDelegateWrapper.shouldContinueTransaction', () async { - SKPaymentQueueWrapper queue = SKPaymentQueueWrapper(); - TestPaymentQueueDelegate testDelegate = TestPaymentQueueDelegate(); + final SKPaymentQueueWrapper queue = SKPaymentQueueWrapper(); + final TestPaymentQueueDelegate testDelegate = TestPaymentQueueDelegate(); await queue.setDelegate(testDelegate); final Map arguments = { @@ -36,7 +36,7 @@ void main() { }, }; - final result = await queue.handlePaymentQueueDelegateCallbacks( + final Object? result = await queue.handlePaymentQueueDelegateCallbacks( MethodCall('shouldContinueTransaction', arguments), ); @@ -52,13 +52,13 @@ void main() { test( 'handlePaymentQueueDelegateCallbacks should call SKPaymentQueueDelegateWrapper.shouldShowPriceConsent', () async { - SKPaymentQueueWrapper queue = SKPaymentQueueWrapper(); - TestPaymentQueueDelegate testDelegate = TestPaymentQueueDelegate(); + final SKPaymentQueueWrapper queue = SKPaymentQueueWrapper(); + final TestPaymentQueueDelegate testDelegate = TestPaymentQueueDelegate(); await queue.setDelegate(testDelegate); - final result = await queue.handlePaymentQueueDelegateCallbacks( - MethodCall('shouldShowPriceConsent'), - ); + final bool result = (await queue.handlePaymentQueueDelegateCallbacks( + const MethodCall('shouldShowPriceConsent'), + ))! as bool; expect(result, false); expect( @@ -72,12 +72,12 @@ void main() { test( 'handleObserverCallbacks should call SKTransactionObserverWrapper.restoreCompletedTransactionsFailed', () async { - SKPaymentQueueWrapper queue = SKPaymentQueueWrapper(); - TestTransactionObserverWrapper testObserver = + final SKPaymentQueueWrapper queue = SKPaymentQueueWrapper(); + final TestTransactionObserverWrapper testObserver = TestTransactionObserverWrapper(); queue.setTransactionObserver(testObserver); - final arguments = { + final Map arguments = { 'code': 100, 'domain': 'domain', 'userInfo': {'error': 'underlying_error'}, @@ -163,6 +163,6 @@ class FakeStoreKitPlatform { isPaymentQueueDelegateRegistered = false; return Future.sync(() {}); } - return Future.error('method not mocked'); + return Future.error('method not mocked'); } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_test.dart index 6f52a5fc5495..fdf80d68a3ea 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_test.dart @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:in_app_purchase_storekit/src/store_kit_wrappers/sk_product_wrapper.dart'; import 'package:in_app_purchase_storekit/src/types/app_store_product_details.dart'; import 'package:in_app_purchase_storekit/src/types/app_store_purchase_details.dart'; -import 'package:in_app_purchase_storekit/src/store_kit_wrappers/sk_product_wrapper.dart'; import 'package:in_app_purchase_storekit/store_kit_wrappers.dart'; import 'package:test/test.dart'; @@ -25,7 +25,8 @@ void main() { 'SKProductSubscriptionPeriodWrapper should have properties to be default values if map is empty', () { final SKProductSubscriptionPeriodWrapper wrapper = - SKProductSubscriptionPeriodWrapper.fromJson({}); + SKProductSubscriptionPeriodWrapper.fromJson( + const {}); expect(wrapper.numberOfUnits, 0); expect(wrapper.unit, SKSubscriptionPeriodUnit.day); }); @@ -42,7 +43,7 @@ void main() { 'SKProductDiscountWrapper should have properties to be default if map is empty', () { final SKProductDiscountWrapper wrapper = - SKProductDiscountWrapper.fromJson({}); + SKProductDiscountWrapper.fromJson(const {}); expect(wrapper.price, ''); expect( wrapper.priceLocale, @@ -70,7 +71,7 @@ void main() { 'SKProductWrapper should have properties to be default if map is empty', () { final SKProductWrapper wrapper = - SKProductWrapper.fromJson({}); + SKProductWrapper.fromJson(const {}); expect(wrapper.productIdentifier, ''); expect(wrapper.localizedTitle, ''); expect(wrapper.localizedDescription, ''); @@ -127,25 +128,25 @@ void main() { group('Payment queue related object tests', () { test('Should construct correct SKPaymentWrapper from json', () { - SKPaymentWrapper payment = + final SKPaymentWrapper payment = SKPaymentWrapper.fromJson(dummyPayment.toMap()); expect(payment, equals(dummyPayment)); }); test('Should construct correct SKError from json', () { - SKError error = SKError.fromJson(buildErrorMap(dummyError)); + final SKError error = SKError.fromJson(buildErrorMap(dummyError)); expect(error, equals(dummyError)); }); test('Should construct correct SKTransactionWrapper from json', () { - SKPaymentTransactionWrapper transaction = + final SKPaymentTransactionWrapper transaction = SKPaymentTransactionWrapper.fromJson( buildTransactionMap(dummyTransaction)); expect(transaction, equals(dummyTransaction)); }); test('toPurchaseDetails() should return correct PurchaseDetail object', () { - AppStorePurchaseDetails details = + final AppStorePurchaseDetails details = AppStorePurchaseDetails.fromSKTransaction( dummyTransaction, 'receipt data'); expect(dummyTransaction.transactionIdentifier, details.purchaseID); @@ -183,7 +184,7 @@ void main() { }); test('Should generate correct map of the payment object', () { - Map map = dummyPayment.toMap(); + final Map map = dummyPayment.toMap(); expect(map['productIdentifier'], dummyPayment.productIdentifier); expect(map['applicationUsername'], dummyPayment.applicationUsername); diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_test_stub_objects.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_test_stub_objects.dart index 276c3ff9f3fa..51d851cb79b5 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_test_stub_objects.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_test_stub_objects.dart @@ -4,14 +4,16 @@ import 'package:in_app_purchase_storekit/store_kit_wrappers.dart'; -final dummyPayment = SKPaymentWrapper( +const SKPaymentWrapper dummyPayment = SKPaymentWrapper( productIdentifier: 'prod-id', applicationUsername: 'app-user-name', requestData: 'fake-data-utf8', quantity: 2, simulatesAskToBuyInSandbox: true); -final SKError dummyError = - SKError(code: 111, domain: 'dummy-domain', userInfo: {'key': 'value'}); +const SKError dummyError = SKError( + code: 111, + domain: 'dummy-domain', + userInfo: {'key': 'value'}); final SKPaymentTransactionWrapper dummyOriginalTransaction = SKPaymentTransactionWrapper( @@ -34,7 +36,7 @@ final SKPaymentTransactionWrapper dummyTransaction = ); final SKPriceLocaleWrapper dollarLocale = SKPriceLocaleWrapper( - currencySymbol: '\$', + currencySymbol: r'$', currencyCode: 'USD', countryCode: 'US', ); @@ -68,17 +70,17 @@ final SKProductWrapper dummyProductWrapper = SKProductWrapper( price: '1.0', subscriptionPeriod: dummySubscription, introductoryPrice: dummyDiscount, - discounts: [dummyDiscount], + discounts: [dummyDiscount], ); final SkProductResponseWrapper dummyProductResponseWrapper = SkProductResponseWrapper( - products: [dummyProductWrapper], - invalidProductIdentifiers: ['123'], + products: [dummyProductWrapper], + invalidProductIdentifiers: const ['123'], ); Map buildLocaleMap(SKPriceLocaleWrapper local) { - return { + return { 'currencySymbol': local.currencySymbol, 'currencyCode': local.currencyCode, 'countryCode': local.countryCode, @@ -90,14 +92,14 @@ Map? buildSubscriptionPeriodMap( if (sub == null) { return null; } - return { + return { 'numberOfUnits': sub.numberOfUnits, 'unit': SKSubscriptionPeriodUnit.values.indexOf(sub.unit), }; } Map buildDiscountMap(SKProductDiscountWrapper discount) { - return { + return { 'price': discount.price, 'priceLocale': buildLocaleMap(discount.priceLocale), 'numberOfPeriods': discount.numberOfPeriods, @@ -109,7 +111,7 @@ Map buildDiscountMap(SKProductDiscountWrapper discount) { } Map buildProductMap(SKProductWrapper product) { - return { + return { 'productIdentifier': product.productIdentifier, 'localizedTitle': product.localizedTitle, 'localizedDescription': product.localizedDescription, @@ -119,23 +121,23 @@ Map buildProductMap(SKProductWrapper product) { 'subscriptionPeriod': buildSubscriptionPeriodMap(product.subscriptionPeriod), 'introductoryPrice': buildDiscountMap(product.introductoryPrice!), - 'discounts': [buildDiscountMap(product.introductoryPrice!)], + 'discounts': [buildDiscountMap(product.introductoryPrice!)], }; } Map buildProductResponseMap( SkProductResponseWrapper response) { - List productsMap = response.products + final List productsMap = response.products .map((SKProductWrapper product) => buildProductMap(product)) .toList(); - return { + return { 'products': productsMap, 'invalidProductIdentifiers': response.invalidProductIdentifiers }; } Map buildErrorMap(SKError error) { - return { + return { 'code': error.code, 'domain': error.domain, 'userInfo': error.userInfo, @@ -144,7 +146,7 @@ Map buildErrorMap(SKError error) { Map buildTransactionMap( SKPaymentTransactionWrapper transaction) { - Map map = { + final Map map = { 'transactionState': SKPaymentTransactionStateWrapper.values .indexOf(SKPaymentTransactionStateWrapper.purchased), 'payment': transaction.payment.toMap(), diff --git a/script/configs/custom_analysis.yaml b/script/configs/custom_analysis.yaml index 71f6f03de917..2c87f482bbd0 100644 --- a/script/configs/custom_analysis.yaml +++ b/script/configs/custom_analysis.yaml @@ -16,4 +16,3 @@ - google_maps_flutter/google_maps_flutter_web - in_app_purchase/in_app_purchase - in_app_purchase/in_app_purchase_android -- in_app_purchase/in_app_purchase_storekit From 06922c04f7381eae6af4d864b28ac39a5eaa99a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?The=20one=20with=20the=20braid=20=28she/her=29=20=7C=20D?= =?UTF-8?q?=D1=84=D2=BF=20mit=20dem=20Zopf=20=28sie/ihr=29?= <29313398+TheOneWithTheBraid@users.noreply.github.com> Date: Fri, 25 Feb 2022 21:56:23 +0100 Subject: [PATCH 234/600] [url_launcher] Fixed missing # in links href when opening in new tab on the web (#4221) --- AUTHORS | 1 + .../url_launcher/url_launcher_web/AUTHORS | 1 + .../url_launcher_web/CHANGELOG.md | 6 +++++- .../integration_test/link_widget_test.dart | 20 +++++++++++++++++++ .../url_launcher_web_test.mocks.dart | 6 +----- .../url_launcher_web/lib/src/link.dart | 11 ++++++++-- .../url_launcher_web/pubspec.yaml | 2 +- 7 files changed, 38 insertions(+), 9 deletions(-) diff --git a/AUTHORS b/AUTHORS index 0ca697b6a756..f5dc82340bc8 100644 --- a/AUTHORS +++ b/AUTHORS @@ -65,3 +65,4 @@ Anton Borries Alex Li Rahul Raj <64.rahulraj@gmail.com> Daniel Roek +TheOneWithTheBraid diff --git a/packages/url_launcher/url_launcher_web/AUTHORS b/packages/url_launcher/url_launcher_web/AUTHORS index 493a0b4ef9c2..2678aaba8101 100644 --- a/packages/url_launcher/url_launcher_web/AUTHORS +++ b/packages/url_launcher/url_launcher_web/AUTHORS @@ -64,3 +64,4 @@ Aleksandr Yurkovskiy Anton Borries Alex Li Rahul Raj <64.rahulraj@gmail.com> +TheOneWithTheBraid \ No newline at end of file diff --git a/packages/url_launcher/url_launcher_web/CHANGELOG.md b/packages/url_launcher/url_launcher_web/CHANGELOG.md index 7ac1402100bb..a434b7af70c2 100644 --- a/packages/url_launcher/url_launcher_web/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.9 + +- Fixes invalid routes when opening a `Link` in a new tab + ## 2.0.8 * Updates the minimum Flutter version to 2.10, which is required by the change @@ -17,7 +21,7 @@ ## 2.0.4 -* Add `implements` to pubspec. +- Add `implements` to pubspec. ## 2.0.3 diff --git a/packages/url_launcher/url_launcher_web/example/integration_test/link_widget_test.dart b/packages/url_launcher/url_launcher_web/example/integration_test/link_widget_test.dart index 3e2a025bbbb6..3b75e0556686 100644 --- a/packages/url_launcher/url_launcher_web/example/integration_test/link_widget_test.dart +++ b/packages/url_launcher/url_launcher_web/example/integration_test/link_widget_test.dart @@ -7,6 +7,7 @@ import 'dart:js_util'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:integration_test/integration_test.dart'; import 'package:url_launcher_platform_interface/link.dart'; import 'package:url_launcher_web/src/link.dart'; @@ -51,6 +52,25 @@ void main() { // Check that the same anchor has been updated. expect(anchor.getAttribute('href'), uri2.toString()); expect(anchor.getAttribute('target'), '_self'); + + final Uri uri3 = Uri.parse('/foobar'); + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: WebLinkDelegate(TestLinkInfo( + uri: uri3, + target: LinkTarget.self, + builder: (BuildContext context, FollowLink? followLink) { + return Container(width: 100, height: 100); + }, + )), + )); + await tester.pumpAndSettle(); + + // Check that internal route properly prepares using the default + // [UrlStrategy] + expect(anchor.getAttribute('href'), + urlStrategy?.prepareExternalUrl(uri3.toString())); + expect(anchor.getAttribute('target'), '_self'); }); testWidgets('sizes itself correctly', (WidgetTester tester) async { diff --git a/packages/url_launcher/url_launcher_web/example/integration_test/url_launcher_web_test.mocks.dart b/packages/url_launcher/url_launcher_web/example/integration_test/url_launcher_web_test.mocks.dart index 24d694ffa0cc..36903b0a4250 100644 --- a/packages/url_launcher/url_launcher_web/example/integration_test/url_launcher_web_test.mocks.dart +++ b/packages/url_launcher/url_launcher_web/example/integration_test/url_launcher_web_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.0.16 from annotations +// Mocks generated by Mockito 5.0.17 from annotations // in regular_integration_tests/integration_test/url_launcher_web_test.dart. // Do not manually edit this file. @@ -643,8 +643,6 @@ class MockWindow extends _i1.Mock implements _i2.Window { bool dispatchEvent(_i2.Event? event) => (super.noSuchMethod(Invocation.method(#dispatchEvent, [event]), returnValue: false) as bool); - @override - String toString() => super.toString(); } /// A class which mocks [Navigator]. @@ -749,6 +747,4 @@ class MockNavigator extends _i1.Mock implements _i2.Navigator { _i3.Future share([Map? data]) => (super.noSuchMethod(Invocation.method(#share, [data]), returnValue: Future.value()) as _i3.Future); - @override - String toString() => super.toString(); } diff --git a/packages/url_launcher/url_launcher_web/lib/src/link.dart b/packages/url_launcher/url_launcher_web/lib/src/link.dart index d3fa4a84164e..4498e74ea9ce 100644 --- a/packages/url_launcher/url_launcher_web/lib/src/link.dart +++ b/packages/url_launcher/url_launcher_web/lib/src/link.dart @@ -11,7 +11,7 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; - +import 'package:flutter_web_plugins/flutter_web_plugins.dart' show urlStrategy; import 'package:url_launcher_platform_interface/link.dart'; /// The unique identifier for the view type to be used for link platform views. @@ -163,6 +163,7 @@ class LinkViewController extends PlatformViewController { final BuildContext context; late html.Element _element; + bool get _isInitialized => _element != null; Future _initialize() async { @@ -221,7 +222,13 @@ class LinkViewController extends PlatformViewController { if (uri == null) { _element.removeAttribute('href'); } else { - _element.setAttribute('href', uri.toString()); + String href = uri.toString(); + // in case an internal uri is given, the url mus be properly encoded + // using the currently used [UrlStrategy] + if (!uri.hasScheme) { + href = urlStrategy?.prepareExternalUrl(href) ?? href; + } + _element.setAttribute('href', href); } } diff --git a/packages/url_launcher/url_launcher_web/pubspec.yaml b/packages/url_launcher/url_launcher_web/pubspec.yaml index 8860eebb58ba..c45c062255ad 100644 --- a/packages/url_launcher/url_launcher_web/pubspec.yaml +++ b/packages/url_launcher/url_launcher_web/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_web description: Web platform implementation of url_launcher repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 2.0.8 +version: 2.0.9 environment: sdk: ">=2.12.0 <3.0.0" From f96d633e7a54470208e43c9363b208293a41b623 Mon Sep 17 00:00:00 2001 From: Tran Giang Long Date: Sat, 26 Feb 2022 00:31:21 +0300 Subject: [PATCH 235/600] [image_picker] Support reading WebP images for iOS (#4448) --- .../image_picker/image_picker/CHANGELOG.md | 4 + .../ios/Runner.xcodeproj/project.pbxproj | 14 +++ .../PickerSaveImageToPathOperationTests.m | 100 ++++++++++++++++++ .../example/ios/TestImages/webpImage.webp | Bin 0 -> 2622 bytes .../FLTPHPickerSaveImageToPathOperation.m | 87 +++++++++------ .../image_picker/image_picker/pubspec.yaml | 2 +- 6 files changed, 171 insertions(+), 36 deletions(-) create mode 100644 packages/image_picker/image_picker/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m create mode 100644 packages/image_picker/image_picker/example/ios/TestImages/webpImage.webp diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index 382798f53f90..9ab762a3de13 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.4+10 + +* iOS: allows picking images with WebP format. + ## 0.8.4+9 * Internal code cleanup for stricter analysis options. diff --git a/packages/image_picker/image_picker/example/ios/Runner.xcodeproj/project.pbxproj b/packages/image_picker/image_picker/example/ios/Runner.xcodeproj/project.pbxproj index f8fe6683482a..2847bfd85046 100644 --- a/packages/image_picker/image_picker/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/image_picker/image_picker/example/ios/Runner.xcodeproj/project.pbxproj @@ -18,6 +18,11 @@ 680049382280F2B9006DD6AB /* pngImage.png in Resources */ = {isa = PBXBuildFile; fileRef = 680049352280F2B8006DD6AB /* pngImage.png */; }; 680049392280F2B9006DD6AB /* jpgImage.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 680049362280F2B8006DD6AB /* jpgImage.jpg */; }; 6801C8392555D726009DAF8D /* ImagePickerFromGalleryUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6801C8382555D726009DAF8D /* ImagePickerFromGalleryUITests.m */; }; + 86430DF9272D71E9002D9D6C /* gifImage.gif in Resources */ = {isa = PBXBuildFile; fileRef = 9FC8F0E8229FA49E00C8D58F /* gifImage.gif */; }; + 86E9A893272754860017E6E0 /* PickerSaveImageToPathOperationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 86E9A892272754860017E6E0 /* PickerSaveImageToPathOperationTests.m */; }; + 86E9A894272754A30017E6E0 /* webpImage.webp in Resources */ = {isa = PBXBuildFile; fileRef = 86E9A88F272747B90017E6E0 /* webpImage.webp */; }; + 86E9A895272769130017E6E0 /* pngImage.png in Resources */ = {isa = PBXBuildFile; fileRef = 680049352280F2B8006DD6AB /* pngImage.png */; }; + 86E9A896272769150017E6E0 /* jpgImage.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 680049362280F2B8006DD6AB /* jpgImage.jpg */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -79,6 +84,8 @@ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 86E9A88F272747B90017E6E0 /* webpImage.webp */ = {isa = PBXFileReference; lastKnownFileType = file; path = webpImage.webp; sourceTree = ""; }; + 86E9A892272754860017E6E0 /* PickerSaveImageToPathOperationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PickerSaveImageToPathOperationTests.m; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -132,6 +139,7 @@ F78AF3172342D9D7008449C7 /* ImagePickerTestImages.h */, F78AF3182342D9D7008449C7 /* ImagePickerTestImages.m */, 68B9AF71243E4B3F00927CE4 /* ImagePickerPluginTests.m */, + 86E9A892272754860017E6E0 /* PickerSaveImageToPathOperationTests.m */, 334733F62668136400DCC49E /* Info.plist */, ); path = RunnerTests; @@ -140,6 +148,7 @@ 680049282280E33D006DD6AB /* TestImages */ = { isa = PBXGroup; children = ( + 86E9A88F272747B90017E6E0 /* webpImage.webp */, 9FC8F0E8229FA49E00C8D58F /* gifImage.gif */, 680049362280F2B8006DD6AB /* jpgImage.jpg */, 680049352280F2B8006DD6AB /* pngImage.png */, @@ -352,6 +361,10 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 86430DF9272D71E9002D9D6C /* gifImage.gif in Resources */, + 86E9A894272754A30017E6E0 /* webpImage.webp in Resources */, + 86E9A895272769130017E6E0 /* pngImage.png in Resources */, + 86E9A896272769150017E6E0 /* jpgImage.jpg in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -456,6 +469,7 @@ files = ( 334733FD266813F100DCC49E /* MetaDataUtilTests.m in Sources */, 334733FF266813FA00DCC49E /* ImagePickerTestImages.m in Sources */, + 86E9A893272754860017E6E0 /* PickerSaveImageToPathOperationTests.m in Sources */, 334733FC266813EE00DCC49E /* ImageUtilTests.m in Sources */, 33473400266813FD00DCC49E /* ImagePickerPluginTests.m in Sources */, 334733FE266813F400DCC49E /* PhotoAssetUtilTests.m in Sources */, diff --git a/packages/image_picker/image_picker/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m b/packages/image_picker/image_picker/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m new file mode 100644 index 000000000000..f94db83d5696 --- /dev/null +++ b/packages/image_picker/image_picker/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m @@ -0,0 +1,100 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import + +@import image_picker; +@import image_picker.Test; +@import XCTest; + +@interface PickerSaveImageToPathOperationTests : XCTestCase + +@end + +@implementation PickerSaveImageToPathOperationTests + +- (void)testSaveWebPImage API_AVAILABLE(ios(14)) { + NSURL *imageURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"webpImage" + withExtension:@"webp"]; + NSItemProvider *itemProvider = [[NSItemProvider alloc] initWithContentsOfURL:imageURL]; + PHPickerResult *result = [self createPickerResultWithProvider:itemProvider + withIdentifier:UTTypeWebP.identifier]; + + [self verifySavingImageWithPickerResult:result]; +} + +- (void)testSavePNGImage API_AVAILABLE(ios(14)) { + NSURL *imageURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"pngImage" + withExtension:@"png"]; + NSItemProvider *itemProvider = [[NSItemProvider alloc] initWithContentsOfURL:imageURL]; + PHPickerResult *result = [self createPickerResultWithProvider:itemProvider + withIdentifier:UTTypeWebP.identifier]; + + [self verifySavingImageWithPickerResult:result]; +} + +- (void)testSaveJPGImage API_AVAILABLE(ios(14)) { + NSURL *imageURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"jpgImage" + withExtension:@"jpg"]; + NSItemProvider *itemProvider = [[NSItemProvider alloc] initWithContentsOfURL:imageURL]; + PHPickerResult *result = [self createPickerResultWithProvider:itemProvider + withIdentifier:UTTypeWebP.identifier]; + + [self verifySavingImageWithPickerResult:result]; +} + +- (void)testSaveGIFImage API_AVAILABLE(ios(14)) { + NSURL *imageURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"gifImage" + withExtension:@"gif"]; + NSItemProvider *itemProvider = [[NSItemProvider alloc] initWithContentsOfURL:imageURL]; + PHPickerResult *result = [self createPickerResultWithProvider:itemProvider + withIdentifier:UTTypeWebP.identifier]; + + [self verifySavingImageWithPickerResult:result]; +} + +/** + * Creates a mock picker result using NSItemProvider. + * + * @param itemProvider an item provider that will be used as picker result + * @param identifier local identifier of the asset + */ +- (PHPickerResult *)createPickerResultWithProvider:(NSItemProvider *)itemProvider + withIdentifier:(NSString *)identifier API_AVAILABLE(ios(14)) { + PHPickerResult *result = OCMClassMock([PHPickerResult class]); + + OCMStub([result itemProvider]).andReturn(itemProvider); + OCMStub([result assetIdentifier]).andReturn(identifier); + + return result; +} + +/** + * Validates a saving process of FLTPHPickerSaveImageToPathOperation. + * + * FLTPHPickerSaveImageToPathOperation is responsible for saving a picked image to the disk for + * later use. It is expected that the saving is always successful. + * + * @param result the picker result + */ +- (void)verifySavingImageWithPickerResult:(PHPickerResult *)result API_AVAILABLE(ios(14)) { + XCTestExpectation *pathExpectation = [self expectationWithDescription:@"Path was created"]; + + FLTPHPickerSaveImageToPathOperation *operation = [[FLTPHPickerSaveImageToPathOperation alloc] + initWithResult:result + maxHeight:@100 + maxWidth:@100 + desiredImageQuality:@100 + savedPathBlock:^(NSString *savedPath) { + if ([[NSFileManager defaultManager] fileExistsAtPath:savedPath]) { + [pathExpectation fulfill]; + } + }]; + + [operation start]; + [self waitForExpectations:@[ pathExpectation ] timeout:30]; +} + +@end diff --git a/packages/image_picker/image_picker/example/ios/TestImages/webpImage.webp b/packages/image_picker/image_picker/example/ios/TestImages/webpImage.webp new file mode 100644 index 0000000000000000000000000000000000000000..ab7d40d839681c96a745fbed51d81709efc5b222 GIT binary patch literal 2622 zcmeHJF>ljA6n>XbQXmr}WjI}hget}722v5LZl$IPw31PjA{wzd_BFAR_$>P>n@kKy z42&HZ5K>mQ&LFiCzkq*HHY6sH5al^C?vKD{o!xR%wtz?d!#H?p%> zo|7xZc`2{1&dWKjq_n=(r3W_vHn)?&cE&={ff#ze$-aL+XVh~|cG$3VD`<(4_hc4| zgW1z(&TQ;7UADbNHxnb722-JInoK8tWF#iDT{#d2;`Ep^x+qa&lci6!WGHAud#LFQ z*`a%6^maQL7c-NMlnM;a<2csh2bv6re7)Iha=p&$^%_>xqL;q1lbRo`{!`c$krR4> z@}y5wMSCERmC5ksMN7d*DjAJrkc<0~wrEmwRDE`tp%JmU1`| qjAPF{YdVGb-*3@k6%G^*6b=*)6b=*){C^Hy%}>I|p()1c2>t?)0hB5L literal 0 HcmV?d00001 diff --git a/packages/image_picker/image_picker/ios/Classes/FLTPHPickerSaveImageToPathOperation.m b/packages/image_picker/image_picker/ios/Classes/FLTPHPickerSaveImageToPathOperation.m index 5a084c455f22..9e7e7e87a30c 100644 --- a/packages/image_picker/image_picker/ios/Classes/FLTPHPickerSaveImageToPathOperation.m +++ b/packages/image_picker/image_picker/ios/Classes/FLTPHPickerSaveImageToPathOperation.m @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#import + #import "FLTPHPickerSaveImageToPathOperation.h" API_AVAILABLE(ios(14)) @@ -82,46 +84,24 @@ - (void)start { } if (@available(iOS 14, *)) { [self setExecuting:YES]; + + if ([self.result.itemProvider hasItemConformingToTypeIdentifier:UTTypeWebP.identifier]) { + [self.result.itemProvider + loadDataRepresentationForTypeIdentifier:UTTypeWebP.identifier + completionHandler:^(NSData *_Nullable data, + NSError *_Nullable error) { + UIImage *image = [[UIImage alloc] initWithData:data]; + [self processImage:image]; + }]; + return; + } + [self.result.itemProvider loadObjectOfClass:[UIImage class] completionHandler:^(__kindof id _Nullable image, NSError *_Nullable error) { if ([image isKindOfClass:[UIImage class]]) { - __block UIImage *localImage = image; - PHAsset *originalAsset = - [FLTImagePickerPhotoAssetUtil getAssetFromPHPickerResult:self.result]; - - if (self.maxWidth != nil || self.maxHeight != nil) { - localImage = [FLTImagePickerImageUtil scaledImage:localImage - maxWidth:self.maxWidth - maxHeight:self.maxHeight - isMetadataAvailable:originalAsset != nil]; - } - __block NSString *savedPath; - if (!originalAsset) { - // Image picked without an original asset (e.g. User pick image without permission) - savedPath = - [FLTImagePickerPhotoAssetUtil saveImageWithPickerInfo:nil - image:localImage - imageQuality:self.desiredImageQuality]; - [self completeOperationWithPath:savedPath]; - } else { - [[PHImageManager defaultManager] - requestImageDataForAsset:originalAsset - options:nil - resultHandler:^( - NSData *_Nullable imageData, NSString *_Nullable dataUTI, - UIImageOrientation orientation, NSDictionary *_Nullable info) { - // maxWidth and maxHeight are used only for GIF images. - savedPath = [FLTImagePickerPhotoAssetUtil - saveImageWithOriginalImageData:imageData - image:localImage - maxWidth:self.maxWidth - maxHeight:self.maxHeight - imageQuality:self.desiredImageQuality]; - [self completeOperationWithPath:savedPath]; - }]; - } + [self processImage:image]; } }]; } else { @@ -129,4 +109,41 @@ - (void)start { } } +/** + * Processes the image. + */ +- (void)processImage:(UIImage *)localImage API_AVAILABLE(ios(14)) { + PHAsset *originalAsset = [FLTImagePickerPhotoAssetUtil getAssetFromPHPickerResult:self.result]; + + if (self.maxWidth != nil || self.maxHeight != nil) { + localImage = [FLTImagePickerImageUtil scaledImage:localImage + maxWidth:self.maxWidth + maxHeight:self.maxHeight + isMetadataAvailable:originalAsset != nil]; + } + if (originalAsset) { + [[PHImageManager defaultManager] + requestImageDataForAsset:originalAsset + options:nil + resultHandler:^(NSData *_Nullable imageData, NSString *_Nullable dataUTI, + UIImageOrientation orientation, NSDictionary *_Nullable info) { + // maxWidth and maxHeight are used only for GIF images. + NSString *savedPath = [FLTImagePickerPhotoAssetUtil + saveImageWithOriginalImageData:imageData + image:localImage + maxWidth:self.maxWidth + maxHeight:self.maxHeight + imageQuality:self.desiredImageQuality]; + [self completeOperationWithPath:savedPath]; + }]; + } else { + // Image picked without an original asset (e.g. User pick image without permission) + NSString *savedPath = + [FLTImagePickerPhotoAssetUtil saveImageWithPickerInfo:nil + image:localImage + imageQuality:self.desiredImageQuality]; + [self completeOperationWithPath:savedPath]; + } +} + @end diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 1d280f16560b..4774711129da 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.4+9 +version: 0.8.4+10 environment: sdk: ">=2.14.0 <3.0.0" From 02b5b3fe3983dd06fd75ec01d9884474487a4a79 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 28 Feb 2022 14:56:23 -0500 Subject: [PATCH 236/600] [in_app_purchase] Update Android to new analysis options (#4937) --- .../in_app_purchase_android/CHANGELOG.md | 4 + .../analysis_options.yaml | 1 - .../example/lib/consumable_store.dart | 13 +- .../example/lib/main.dart | 140 +++---- .../example/pubspec.yaml | 4 +- .../billing_client_wrapper.dart | 35 +- .../purchase_wrapper.dart | 95 +++-- .../sku_details_wrapper.dart | 17 +- .../src/in_app_purchase_android_platform.dart | 38 +- ...pp_purchase_android_platform_addition.dart | 14 +- .../types/google_play_product_details.dart | 8 +- .../types/google_play_purchase_details.dart | 13 +- .../in_app_purchase_android/pubspec.yaml | 3 +- .../billing_client_wrapper_test.dart | 168 ++++---- .../purchase_wrapper_test.dart | 36 +- .../sku_details_wrapper_deprecated_test.dart | 4 +- .../sku_details_wrapper_test.dart | 24 +- ...rchase_android_platform_addition_test.dart | 45 ++- ...in_app_purchase_android_platform_test.dart | 369 ++++++++++-------- .../test/stub_in_app_purchase_platform.dart | 9 +- script/configs/custom_analysis.yaml | 1 - 21 files changed, 559 insertions(+), 482 deletions(-) delete mode 100644 packages/in_app_purchase/in_app_purchase_android/analysis_options.yaml diff --git a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md index ff0ce6e28243..a871cfafb4a2 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.2+2 + +* Internal code cleanup for stricter analysis options. + ## 0.2.2+1 * Removes the dependency on `meta`. diff --git a/packages/in_app_purchase/in_app_purchase_android/analysis_options.yaml b/packages/in_app_purchase/in_app_purchase_android/analysis_options.yaml deleted file mode 100644 index 5aeb4e7c5e21..000000000000 --- a/packages/in_app_purchase/in_app_purchase_android/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../../analysis_options_legacy.yaml diff --git a/packages/in_app_purchase/in_app_purchase_android/example/lib/consumable_store.dart b/packages/in_app_purchase/in_app_purchase_android/example/lib/consumable_store.dart index 4d10a50e1ee8..448efcf40b51 100644 --- a/packages/in_app_purchase/in_app_purchase_android/example/lib/consumable_store.dart +++ b/packages/in_app_purchase/in_app_purchase_android/example/lib/consumable_store.dart @@ -5,13 +5,14 @@ import 'dart:async'; import 'package:shared_preferences/shared_preferences.dart'; +// ignore: avoid_classes_with_only_static_members /// A store of consumable items. /// /// This is a development prototype tha stores consumables in the shared /// preferences. Do not use this in real world apps. class ConsumableStore { static const String _kPrefKey = 'consumables'; - static Future _writes = Future.value(); + static Future _writes = Future.value(); /// Adds a consumable with ID `id` to the store. /// @@ -32,19 +33,19 @@ class ConsumableStore { /// Returns the list of consumables from the store. static Future> load() async { return (await SharedPreferences.getInstance()).getStringList(_kPrefKey) ?? - []; + []; } static Future _doSave(String id) async { - List cached = await load(); - SharedPreferences prefs = await SharedPreferences.getInstance(); + final List cached = await load(); + final SharedPreferences prefs = await SharedPreferences.getInstance(); cached.add(id); await prefs.setStringList(_kPrefKey, cached); } static Future _doConsume(String id) async { - List cached = await load(); - SharedPreferences prefs = await SharedPreferences.getInstance(); + final List cached = await load(); + final SharedPreferences prefs = await SharedPreferences.getInstance(); cached.remove(id); await prefs.setStringList(_kPrefKey, cached); } diff --git a/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart index a53e9b3f2d02..939bc43bea63 100644 --- a/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart @@ -44,10 +44,10 @@ class _MyAppState extends State<_MyApp> { final InAppPurchasePlatform _inAppPurchasePlatform = InAppPurchasePlatform.instance; late StreamSubscription> _subscription; - List _notFoundIds = []; - List _products = []; - List _purchases = []; - List _consumables = []; + List _notFoundIds = []; + List _products = []; + List _purchases = []; + List _consumables = []; bool _isAvailable = false; bool _purchasePending = false; bool _loading = true; @@ -57,11 +57,12 @@ class _MyAppState extends State<_MyApp> { void initState() { final Stream> purchaseUpdated = _inAppPurchasePlatform.purchaseStream; - _subscription = purchaseUpdated.listen((purchaseDetailsList) { + _subscription = + purchaseUpdated.listen((List purchaseDetailsList) { _listenToPurchaseUpdated(purchaseDetailsList); }, onDone: () { _subscription.cancel(); - }, onError: (error) { + }, onError: (Object error) { // handle error here. }); initStoreInfo(); @@ -73,26 +74,26 @@ class _MyAppState extends State<_MyApp> { if (!isAvailable) { setState(() { _isAvailable = isAvailable; - _products = []; - _purchases = []; - _notFoundIds = []; - _consumables = []; + _products = []; + _purchases = []; + _notFoundIds = []; + _consumables = []; _purchasePending = false; _loading = false; }); return; } - ProductDetailsResponse productDetailResponse = + final ProductDetailsResponse productDetailResponse = await _inAppPurchasePlatform.queryProductDetails(_kProductIds.toSet()); if (productDetailResponse.error != null) { setState(() { _queryProductError = productDetailResponse.error!.message; _isAvailable = isAvailable; _products = productDetailResponse.productDetails; - _purchases = []; + _purchases = []; _notFoundIds = productDetailResponse.notFoundIDs; - _consumables = []; + _consumables = []; _purchasePending = false; _loading = false; }); @@ -104,9 +105,9 @@ class _MyAppState extends State<_MyApp> { _queryProductError = null; _isAvailable = isAvailable; _products = productDetailResponse.productDetails; - _purchases = []; + _purchases = []; _notFoundIds = productDetailResponse.notFoundIDs; - _consumables = []; + _consumables = []; _purchasePending = false; _loading = false; }); @@ -115,7 +116,7 @@ class _MyAppState extends State<_MyApp> { await _inAppPurchasePlatform.restorePurchases(); - List consumables = await ConsumableStore.load(); + final List consumables = await ConsumableStore.load(); setState(() { _isAvailable = isAvailable; _products = productDetailResponse.productDetails; @@ -134,11 +135,11 @@ class _MyAppState extends State<_MyApp> { @override Widget build(BuildContext context) { - List stack = []; + final List stack = []; if (_queryProductError == null) { stack.add( ListView( - children: [ + children: [ _buildConnectionCheckTile(), _buildProductList(), _buildConsumableBox(), @@ -154,10 +155,10 @@ class _MyAppState extends State<_MyApp> { if (_purchasePending) { stack.add( Stack( - children: [ + children: const [ Opacity( opacity: 0.3, - child: const ModalBarrier(dismissible: false, color: Colors.grey), + child: ModalBarrier(dismissible: false, color: Colors.grey), ), Center( child: CircularProgressIndicator(), @@ -181,7 +182,7 @@ class _MyAppState extends State<_MyApp> { Card _buildConnectionCheckTile() { if (_loading) { - return Card(child: ListTile(title: const Text('Trying to connect...'))); + return const Card(child: ListTile(title: Text('Trying to connect...'))); } final Widget storeHeader = ListTile( leading: Icon(_isAvailable ? Icons.check : Icons.block, @@ -192,8 +193,8 @@ class _MyAppState extends State<_MyApp> { final List children = [storeHeader]; if (!_isAvailable) { - children.addAll([ - Divider(), + children.addAll([ + const Divider(), ListTile( title: Text('Not connected', style: TextStyle(color: ThemeData.light().errorColor)), @@ -207,29 +208,30 @@ class _MyAppState extends State<_MyApp> { Card _buildProductList() { if (_loading) { - return Card( - child: (ListTile( + return const Card( + child: ListTile( leading: CircularProgressIndicator(), - title: Text('Fetching products...')))); + title: Text('Fetching products...'))); } if (!_isAvailable) { - return Card(); + return const Card(); } - final ListTile productHeader = ListTile(title: Text('Products for Sale')); - List productList = []; + const ListTile productHeader = ListTile(title: Text('Products for Sale')); + final List productList = []; if (_notFoundIds.isNotEmpty) { productList.add(ListTile( title: Text('[${_notFoundIds.join(", ")}] not found', style: TextStyle(color: ThemeData.light().errorColor)), - subtitle: Text( + subtitle: const Text( 'This app needs special configuration to run. Please see example/README.md for instructions.'))); } // This loading previous purchases code is just a demo. Please do not use this as it is. // In your app you should always verify the purchase data using the `verificationData` inside the [PurchaseDetails] object before trusting it. // We recommend that you use your own server to verify the purchase data. - Map purchases = - Map.fromEntries(_purchases.map((PurchaseDetails purchase) { + final Map purchases = + Map.fromEntries( + _purchases.map((PurchaseDetails purchase) { if (purchase.pendingCompletePurchase) { _inAppPurchasePlatform.completePurchase(purchase); } @@ -237,7 +239,7 @@ class _MyAppState extends State<_MyApp> { })); productList.addAll(_products.map( (ProductDetails productDetails) { - PurchaseDetails? previousPurchase = purchases[productDetails.id]; + final PurchaseDetails? previousPurchase = purchases[productDetails.id]; return ListTile( title: Text( productDetails.title, @@ -249,18 +251,18 @@ class _MyAppState extends State<_MyApp> { ? IconButton( onPressed: () { final InAppPurchaseAndroidPlatformAddition addition = - InAppPurchasePlatformAddition.instance + InAppPurchasePlatformAddition.instance! as InAppPurchaseAndroidPlatformAddition; - var skuDetails = + final SkuDetailsWrapper skuDetails = (productDetails as GooglePlayProductDetails) .skuDetails; addition .launchPriceChangeConfirmationFlow( sku: skuDetails.sku) - .then((value) => print( - "confirmationResponse: ${value.responseCode}")); + .then((BillingResultWrapper value) => print( + 'confirmationResponse: ${value.responseCode}')); }, - icon: Icon(Icons.upgrade)) + icon: const Icon(Icons.upgrade)) : TextButton( child: Text(productDetails.price), style: TextButton.styleFrom( @@ -272,10 +274,11 @@ class _MyAppState extends State<_MyApp> { // verify the latest status of you your subscription by using server side receipt validation // and update the UI accordingly. The subscription purchase status shown // inside the app may not be accurate. - final oldSubscription = _getOldSubscription( - productDetails as GooglePlayProductDetails, - purchases); - GooglePlayPurchaseParam purchaseParam = + final GooglePlayPurchaseDetails? oldSubscription = + _getOldSubscription( + productDetails as GooglePlayProductDetails, + purchases); + final GooglePlayPurchaseParam purchaseParam = GooglePlayPurchaseParam( productDetails: productDetails, applicationUserName: null, @@ -299,26 +302,26 @@ class _MyAppState extends State<_MyApp> { )); return Card( - child: - Column(children: [productHeader, Divider()] + productList)); + child: Column( + children: [productHeader, const Divider()] + productList)); } Card _buildConsumableBox() { if (_loading) { - return Card( - child: (ListTile( + return const Card( + child: ListTile( leading: CircularProgressIndicator(), - title: Text('Fetching consumables...')))); + title: Text('Fetching consumables...'))); } if (!_isAvailable || _notFoundIds.contains(_kConsumableId)) { - return Card(); + return const Card(); } - final ListTile consumableHeader = + const ListTile consumableHeader = ListTile(title: Text('Purchased consumables')); final List tokens = _consumables.map((String id) { return GridTile( child: IconButton( - icon: Icon( + icon: const Icon( Icons.stars, size: 42.0, color: Colors.orange, @@ -331,12 +334,12 @@ class _MyAppState extends State<_MyApp> { return Card( child: Column(children: [ consumableHeader, - Divider(), + const Divider(), GridView.count( crossAxisCount: 5, children: tokens, shrinkWrap: true, - padding: EdgeInsets.all(16.0), + padding: const EdgeInsets.all(16.0), ) ])); } @@ -355,11 +358,11 @@ class _MyAppState extends State<_MyApp> { }); } - void deliverProduct(PurchaseDetails purchaseDetails) async { + Future deliverProduct(PurchaseDetails purchaseDetails) async { // IMPORTANT!! Always verify purchase details before delivering the product. if (purchaseDetails.productID == _kConsumableId) { await ConsumableStore.save(purchaseDetails.purchaseID!); - List consumables = await ConsumableStore.load(); + final List consumables = await ConsumableStore.load(); setState(() { _purchasePending = false; _consumables = consumables; @@ -388,8 +391,9 @@ class _MyAppState extends State<_MyApp> { // handle invalid purchase here if _verifyPurchase` failed. } - void _listenToPurchaseUpdated(List purchaseDetailsList) { - purchaseDetailsList.forEach((PurchaseDetails purchaseDetails) async { + Future _listenToPurchaseUpdated( + List purchaseDetailsList) async { + for (final PurchaseDetails purchaseDetails in purchaseDetailsList) { if (purchaseDetails.status == PurchaseStatus.pending) { showPendingUI(); } else { @@ -397,7 +401,7 @@ class _MyAppState extends State<_MyApp> { handleError(purchaseDetails.error!); } else if (purchaseDetails.status == PurchaseStatus.purchased || purchaseDetails.status == PurchaseStatus.restored) { - bool valid = await _verifyPurchase(purchaseDetails); + final bool valid = await _verifyPurchase(purchaseDetails); if (valid) { deliverProduct(purchaseDetails); } else { @@ -408,7 +412,7 @@ class _MyAppState extends State<_MyApp> { if (!_kAutoConsume && purchaseDetails.productID == _kConsumableId) { final InAppPurchaseAndroidPlatformAddition addition = - InAppPurchasePlatformAddition.instance + InAppPurchasePlatformAddition.instance! as InAppPurchaseAndroidPlatformAddition; await addition.consumePurchase(purchaseDetails); @@ -418,7 +422,7 @@ class _MyAppState extends State<_MyApp> { await _inAppPurchasePlatform.completePurchase(purchaseDetails); } } - }); + } } GooglePlayPurchaseDetails? _getOldSubscription( @@ -435,31 +439,31 @@ class _MyAppState extends State<_MyApp> { if (productDetails.id == _kSilverSubscriptionId && purchases[_kGoldSubscriptionId] != null) { oldSubscription = - purchases[_kGoldSubscriptionId] as GooglePlayPurchaseDetails; + purchases[_kGoldSubscriptionId]! as GooglePlayPurchaseDetails; } else if (productDetails.id == _kGoldSubscriptionId && purchases[_kSilverSubscriptionId] != null) { oldSubscription = - purchases[_kSilverSubscriptionId] as GooglePlayPurchaseDetails; + purchases[_kSilverSubscriptionId]! as GooglePlayPurchaseDetails; } return oldSubscription; } } class _FeatureCard extends StatelessWidget { + _FeatureCard({Key? key}) : super(key: key); + final InAppPurchaseAndroidPlatformAddition addition = - InAppPurchasePlatformAddition.instance + InAppPurchasePlatformAddition.instance! as InAppPurchaseAndroidPlatformAddition; - _FeatureCard({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return Card( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - ListTile(title: Text('Available features')), - Divider(), + const ListTile(title: Text('Available features')), + const Divider(), for (BillingClientFeature feature in BillingClientFeature.values) _buildFeatureWidget(feature), ])); @@ -468,9 +472,9 @@ class _FeatureCard extends StatelessWidget { Widget _buildFeatureWidget(BillingClientFeature feature) { return FutureBuilder( future: addition.isFeatureSupported(feature), - builder: (context, snapshot) { + builder: (BuildContext context, AsyncSnapshot snapshot) { Color color = Colors.grey; - bool? data = snapshot.data; + final bool? data = snapshot.data; if (data != null) { color = data ? Colors.green : Colors.red; } diff --git a/packages/in_app_purchase/in_app_purchase_android/example/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_android/example/pubspec.yaml index f27261669438..9c16efc66e95 100644 --- a/packages/in_app_purchase/in_app_purchase_android/example/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_android/example/pubspec.yaml @@ -9,7 +9,6 @@ environment: dependencies: flutter: sdk: flutter - shared_preferences: ^2.0.0 in_app_purchase_android: # When depending on this package from a real application you should use: # in_app_purchase_android: ^x.y.z @@ -17,15 +16,14 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - in_app_purchase_platform_interface: ^1.0.0 + shared_preferences: ^2.0.0 dev_dependencies: flutter_driver: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.10.0 flutter: uses-material-design: true diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.dart index 29441fb1b921..416eb5680770 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.dart @@ -38,7 +38,8 @@ const String _kOnBillingServiceDisconnected = /// /// Wraps a /// [`PurchasesUpdatedListener`](https://developer.android.com/reference/com/android/billingclient/api/PurchasesUpdatedListener.html). -typedef void PurchasesUpdatedListener(PurchasesResultWrapper purchasesResult); +typedef PurchasesUpdatedListener = void Function( + PurchasesResultWrapper purchasesResult); /// This class can be used directly instead of [InAppPurchaseConnection] to call /// Play-specific billing APIs. @@ -56,7 +57,9 @@ class BillingClient { /// Creates a billing client. BillingClient(PurchasesUpdatedListener onPurchasesUpdated) { channel.setMethodCallHandler(callHandler); - _callbacks[kOnPurchasesUpdated] = [onPurchasesUpdated]; + _callbacks[kOnPurchasesUpdated] = [ + onPurchasesUpdated + ]; } // Occasionally methods in the native layer require a Dart callback to be @@ -67,7 +70,7 @@ class BillingClient { // matching callback here to remember, and then once its twin is triggered it // sends the handle back over the platform channel. We then access that handle // in this array and call it in Dart code. See also [_callHandler]. - Map> _callbacks = >{}; + final Map> _callbacks = >{}; /// Calls /// [`BillingClient#isReady()`](https://developer.android.com/reference/com/android/billingclient/api/BillingClient.html#isReady()) @@ -101,12 +104,12 @@ class BillingClient { Future startConnection( {required OnBillingServiceDisconnected onBillingServiceDisconnected}) async { - List disconnectCallbacks = - _callbacks[_kOnBillingServiceDisconnected] ??= []; + final List disconnectCallbacks = + _callbacks[_kOnBillingServiceDisconnected] ??= []; disconnectCallbacks.add(onBillingServiceDisconnected); return BillingResultWrapper.fromJson((await channel .invokeMapMethod( - "BillingClient#startConnection(BillingClientStateListener)", + 'BillingClient#startConnection(BillingClientStateListener)', { 'handle': disconnectCallbacks.length - 1, })) ?? @@ -121,7 +124,7 @@ class BillingClient { /// /// This triggers the destruction of the `BillingClient` instance in Java. Future endConnection() async { - return channel.invokeMethod("BillingClient#endConnection()", null); + return channel.invokeMethod('BillingClient#endConnection()', null); } /// Returns a list of [SkuDetailsWrapper]s that have [SkuDetailsWrapper.sku] @@ -136,7 +139,7 @@ class BillingClient { Future querySkuDetails( {required SkuType skuType, required List skusList}) async { final Map arguments = { - 'skuType': SkuTypeConverter().toJson(skuType), + 'skuType': const SkuTypeConverter().toJson(skuType), 'skusList': skusList }; return SkuDetailsResponseWrapper.fromJson((await channel.invokeMapMethod< @@ -197,7 +200,7 @@ class BillingClient { 'obfuscatedProfileId': obfuscatedProfileId, 'oldSku': oldSku, 'purchaseToken': purchaseToken, - 'prorationMode': ProrationModeConverter().toJson(prorationMode ?? + 'prorationMode': const ProrationModeConverter().toJson(prorationMode ?? ProrationMode.unknownSubscriptionUpgradeDowngradePolicy) }; return BillingResultWrapper.fromJson( @@ -223,7 +226,7 @@ class BillingClient { return PurchasesResultWrapper.fromJson((await channel .invokeMapMethod( 'BillingClient#queryPurchases(String)', { - 'skuType': SkuTypeConverter().toJson(skuType) + 'skuType': const SkuTypeConverter().toJson(skuType) })) ?? {}); } @@ -247,7 +250,7 @@ class BillingClient { String, dynamic>( 'BillingClient#queryPurchaseHistoryAsync(String, PurchaseHistoryResponseListener)', { - 'skuType': SkuTypeConverter().toJson(skuType) + 'skuType': const SkuTypeConverter().toJson(skuType) })) ?? {}); } @@ -300,9 +303,9 @@ class BillingClient { /// Checks if the specified feature or capability is supported by the Play Store. /// Call this to check if a [BillingClientFeature] is supported by the device. Future isFeatureSupported(BillingClientFeature feature) async { - var result = await channel.invokeMethod( + final bool? result = await channel.invokeMethod( 'BillingClient#isFeatureSupported(String)', { - 'feature': BillingClientFeatureConverter().toJson(feature), + 'feature': const BillingClientFeatureConverter().toJson(feature), }); return result ?? false; } @@ -337,10 +340,10 @@ class BillingClient { final PurchasesUpdatedListener listener = _callbacks[kOnPurchasesUpdated]!.first as PurchasesUpdatedListener; listener(PurchasesResultWrapper.fromJson( - call.arguments.cast())); + (call.arguments as Map).cast())); break; case _kOnBillingServiceDisconnected: - final int handle = call.arguments['handle']; + final int handle = call.arguments['handle'] as int; await _callbacks[_kOnBillingServiceDisconnected]![handle](); break; } @@ -352,7 +355,7 @@ class BillingClient { /// Wraps /// [`com.android.billingclient.api.BillingClientStateListener.onServiceDisconnected()`](https://developer.android.com/reference/com/android/billingclient/api/BillingClientStateListener.html#onBillingServiceDisconnected()) /// to call back on `BillingClient` disconnect. -typedef void OnBillingServiceDisconnected(); +typedef OnBillingServiceDisconnected = void Function(); /// Possible `BillingClient` response statuses. /// diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/purchase_wrapper.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/purchase_wrapper.dart index 742288e56412..653e5147f9b0 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/purchase_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/purchase_wrapper.dart @@ -25,10 +25,11 @@ part 'purchase_wrapper.g.dart'; /// This wraps [`com.android.billlingclient.api.Purchase`](https://developer.android.com/reference/com/android/billingclient/api/Purchase) @JsonSerializable() @PurchaseStateConverter() +@immutable class PurchaseWrapper { /// Creates a purchase wrapper with the given purchase details. @visibleForTesting - PurchaseWrapper({ + const PurchaseWrapper({ required this.orderId, required this.packageName, required this.purchaseTime, @@ -50,19 +51,23 @@ class PurchaseWrapper { @override bool operator ==(Object other) { - if (identical(other, this)) return true; - if (other.runtimeType != runtimeType) return false; - final PurchaseWrapper typedOther = other as PurchaseWrapper; - return typedOther.orderId == orderId && - typedOther.packageName == packageName && - typedOther.purchaseTime == purchaseTime && - typedOther.purchaseToken == purchaseToken && - typedOther.signature == signature && - typedOther.sku == sku && - typedOther.isAutoRenewing == isAutoRenewing && - typedOther.originalJson == originalJson && - typedOther.isAcknowledged == isAcknowledged && - typedOther.purchaseState == purchaseState; + if (identical(other, this)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + return other is PurchaseWrapper && + other.orderId == orderId && + other.packageName == packageName && + other.purchaseTime == purchaseTime && + other.purchaseToken == purchaseToken && + other.signature == signature && + other.sku == sku && + other.isAutoRenewing == isAutoRenewing && + other.originalJson == originalJson && + other.isAcknowledged == isAcknowledged && + other.purchaseState == purchaseState; } @override @@ -167,10 +172,11 @@ class PurchaseWrapper { // We can optionally make [PurchaseWrapper] extend or implement [PurchaseHistoryRecordWrapper]. // For now, we keep them separated classes to be consistent with Android's BillingClient implementation. @JsonSerializable() +@immutable class PurchaseHistoryRecordWrapper { /// Creates a [PurchaseHistoryRecordWrapper] with the given record details. @visibleForTesting - PurchaseHistoryRecordWrapper({ + const PurchaseHistoryRecordWrapper({ required this.purchaseTime, required this.purchaseToken, required this.signature, @@ -216,16 +222,19 @@ class PurchaseHistoryRecordWrapper { @override bool operator ==(Object other) { - if (identical(other, this)) return true; - if (other.runtimeType != runtimeType) return false; - final PurchaseHistoryRecordWrapper typedOther = - other as PurchaseHistoryRecordWrapper; - return typedOther.purchaseTime == purchaseTime && - typedOther.purchaseToken == purchaseToken && - typedOther.signature == signature && - typedOther.sku == sku && - typedOther.originalJson == originalJson && - typedOther.developerPayload == developerPayload; + if (identical(other, this)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + return other is PurchaseHistoryRecordWrapper && + other.purchaseTime == purchaseTime && + other.purchaseToken == purchaseToken && + other.signature == signature && + other.sku == sku && + other.originalJson == originalJson && + other.developerPayload == developerPayload; } @override @@ -242,9 +251,10 @@ class PurchaseHistoryRecordWrapper { /// Wraps [`com.android.billingclient.api.Purchase.PurchasesResult`](https://developer.android.com/reference/com/android/billingclient/api/Purchase.PurchasesResult). @JsonSerializable() @BillingResponseConverter() +@immutable class PurchasesResultWrapper { /// Creates a [PurchasesResultWrapper] with the given purchase result details. - PurchasesResultWrapper( + const PurchasesResultWrapper( {required this.responseCode, required this.billingResult, required this.purchasesList}); @@ -255,12 +265,16 @@ class PurchasesResultWrapper { @override bool operator ==(Object other) { - if (identical(other, this)) return true; - if (other.runtimeType != runtimeType) return false; - final PurchasesResultWrapper typedOther = other as PurchasesResultWrapper; - return typedOther.responseCode == responseCode && - typedOther.purchasesList == purchasesList && - typedOther.billingResult == billingResult; + if (identical(other, this)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + return other is PurchasesResultWrapper && + other.responseCode == responseCode && + other.purchasesList == purchasesList && + other.billingResult == billingResult; } @override @@ -288,9 +302,10 @@ class PurchasesResultWrapper { /// that contains a detailed description of the status. @JsonSerializable() @BillingResponseConverter() +@immutable class PurchasesHistoryResult { /// Creates a [PurchasesHistoryResult] with the provided history. - PurchasesHistoryResult( + const PurchasesHistoryResult( {required this.billingResult, required this.purchaseHistoryRecordList}); /// Factory for creating a [PurchasesHistoryResult] from a [Map] with the history result details. @@ -299,11 +314,15 @@ class PurchasesHistoryResult { @override bool operator ==(Object other) { - if (identical(other, this)) return true; - if (other.runtimeType != runtimeType) return false; - final PurchasesHistoryResult typedOther = other as PurchasesHistoryResult; - return typedOther.purchaseHistoryRecordList == purchaseHistoryRecordList && - typedOther.billingResult == billingResult; + if (identical(other, this)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + return other is PurchasesHistoryResult && + other.purchaseHistoryRecordList == purchaseHistoryRecordList && + other.billingResult == billingResult; } @override diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/sku_details_wrapper.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/sku_details_wrapper.dart index 8069a1f72281..53595c572901 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/sku_details_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/sku_details_wrapper.dart @@ -18,7 +18,7 @@ part 'sku_details_wrapper.g.dart'; /// /// This usually indicates a series underlining code issue in the plugin. @visibleForTesting -const kInvalidBillingResultErrorMessage = +const String kInvalidBillingResultErrorMessage = 'Invalid billing result map from method channel.'; /// Dart wrapper around [`com.android.billingclient.api.SkuDetails`](https://developer.android.com/reference/com/android/billingclient/api/SkuDetails). @@ -26,10 +26,11 @@ const kInvalidBillingResultErrorMessage = /// Contains the details of an available product in Google Play Billing. @JsonSerializable() @SkuTypeConverter() +@immutable class SkuDetailsWrapper { /// Creates a [SkuDetailsWrapper] with the given purchase details. @visibleForTesting - SkuDetailsWrapper({ + const SkuDetailsWrapper({ required this.description, required this.freeTrialPeriod, required this.introductoryPrice, @@ -50,8 +51,6 @@ class SkuDetailsWrapper { required this.originalPriceAmountMicros, }) : _introductoryPriceMicros = introductoryPriceMicros; - final String _introductoryPriceMicros; - /// Constructs an instance of this from a key value map of data. /// /// The map needs to have named string keys with values matching the names and @@ -60,6 +59,8 @@ class SkuDetailsWrapper { factory SkuDetailsWrapper.fromJson(Map map) => _$SkuDetailsWrapperFromJson(map); + final String _introductoryPriceMicros; + /// Textual description of the product. @JsonKey(defaultValue: '') final String description; @@ -182,10 +183,11 @@ class SkuDetailsWrapper { /// /// Returned by [BillingClient.querySkuDetails]. @JsonSerializable() +@immutable class SkuDetailsResponseWrapper { /// Creates a [SkuDetailsResponseWrapper] with the given purchase details. @visibleForTesting - SkuDetailsResponseWrapper( + const SkuDetailsResponseWrapper( {required this.billingResult, required this.skuDetailsList}); /// Constructs an instance of this from a key value map of data. @@ -220,9 +222,10 @@ class SkuDetailsResponseWrapper { /// Params containing the response code and the debug message from the Play Billing API response. @JsonSerializable() @BillingResponseConverter() +@immutable class BillingResultWrapper { /// Constructs the object with [responseCode] and [debugMessage]. - BillingResultWrapper({required this.responseCode, this.debugMessage}); + const BillingResultWrapper({required this.responseCode, this.debugMessage}); /// Constructs an instance of this from a key value map of data. /// @@ -230,7 +233,7 @@ class BillingResultWrapper { /// types of all of the members on this class. factory BillingResultWrapper.fromJson(Map? map) { if (map == null || map.isEmpty) { - return BillingResultWrapper( + return const BillingResultWrapper( responseCode: BillingResponse.error, debugMessage: kInvalidBillingResultErrorMessage); } diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform.dart index 7c8ca529c0f5..61af75688a01 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform.dart @@ -40,7 +40,8 @@ class InAppPurchaseAndroidPlatform extends InAppPurchasePlatform { InAppPurchaseAndroidPlatformAddition(billingClient); _readyFuture = _connect(); - _purchaseUpdatedController = StreamController.broadcast(); + _purchaseUpdatedController = + StreamController>.broadcast(); } /// Registers this class as the default instance of [InAppPurchasePlatform]. @@ -64,7 +65,7 @@ class InAppPurchaseAndroidPlatform extends InAppPurchasePlatform { late final BillingClient billingClient; late Future _readyFuture; - static Set _productIdsToConsume = Set(); + static final Set _productIdsToConsume = {}; @override Future isAvailable() async { @@ -78,7 +79,7 @@ class InAppPurchaseAndroidPlatform extends InAppPurchasePlatform { List responses; PlatformException? exception; try { - responses = await Future.wait([ + responses = await Future.wait(>[ billingClient.querySkuDetails( skuType: SkuType.inapp, skusList: identifiers.toList()), billingClient.querySkuDetails( @@ -86,30 +87,31 @@ class InAppPurchaseAndroidPlatform extends InAppPurchasePlatform { ]); } on PlatformException catch (e) { exception = e; - responses = [ + responses = [ // ignore: invalid_use_of_visible_for_testing_member SkuDetailsResponseWrapper( billingResult: BillingResultWrapper( responseCode: BillingResponse.error, debugMessage: e.code), - skuDetailsList: []), + skuDetailsList: const []), // ignore: invalid_use_of_visible_for_testing_member SkuDetailsResponseWrapper( billingResult: BillingResultWrapper( responseCode: BillingResponse.error, debugMessage: e.code), - skuDetailsList: []) + skuDetailsList: const []) ]; } - List productDetailsList = + final List productDetailsList = responses.expand((SkuDetailsResponseWrapper response) { return response.skuDetailsList; }).map((SkuDetailsWrapper skuDetailWrapper) { return GooglePlayProductDetails.fromSkuDetails(skuDetailWrapper); }).toList(); - Set successIDS = productDetailsList + final Set successIDS = productDetailsList .map((ProductDetails productDetails) => productDetails.id) .toSet(); - List notFoundIDS = identifiers.difference(successIDS).toList(); + final List notFoundIDS = + identifiers.difference(successIDS).toList(); return ProductDetailsResponse( productDetails: productDetailsList, notFoundIDs: notFoundIDS, @@ -130,7 +132,7 @@ class InAppPurchaseAndroidPlatform extends InAppPurchasePlatform { changeSubscriptionParam = purchaseParam.changeSubscriptionParam; } - BillingResultWrapper billingResultWrapper = + final BillingResultWrapper billingResultWrapper = await billingClient.launchBillingFlow( sku: purchaseParam.productDetails.id, accountId: purchaseParam.applicationUserName, @@ -158,11 +160,11 @@ class InAppPurchaseAndroidPlatform extends InAppPurchasePlatform { 'On Android, the `purchase` should always be of type `GooglePlayPurchaseDetails`.', ); - GooglePlayPurchaseDetails googlePurchase = + final GooglePlayPurchaseDetails googlePurchase = purchase as GooglePlayPurchaseDetails; if (googlePurchase.billingClientPurchase.isAcknowledged) { - return BillingResultWrapper(responseCode: BillingResponse.ok); + return const BillingResultWrapper(responseCode: BillingResponse.ok); } if (googlePurchase.verificationData == null) { @@ -180,22 +182,22 @@ class InAppPurchaseAndroidPlatform extends InAppPurchasePlatform { }) async { List responses; - responses = await Future.wait([ + responses = await Future.wait(>[ billingClient.queryPurchases(SkuType.inapp), billingClient.queryPurchases(SkuType.subs) ]); - Set errorCodeSet = responses + final Set errorCodeSet = responses .where((PurchasesResultWrapper response) => response.responseCode != BillingResponse.ok) .map((PurchasesResultWrapper response) => response.responseCode.toString()) .toSet(); - String errorMessage = + final String errorMessage = errorCodeSet.isNotEmpty ? errorCodeSet.join(', ') : ''; - List pastPurchases = + final List pastPurchases = responses.expand((PurchasesResultWrapper response) { return response.purchasesList; }).map((PurchaseWrapper purchaseWrapper) { @@ -229,7 +231,7 @@ class InAppPurchaseAndroidPlatform extends InAppPurchasePlatform { } final BillingResultWrapper billingResult = - await (InAppPurchasePlatformAddition.instance + await (InAppPurchasePlatformAddition.instance! as InAppPurchaseAndroidPlatformAddition) .consumePurchase(purchaseDetails); final BillingResponse consumedResponse = billingResult.responseCode; @@ -276,7 +278,7 @@ class InAppPurchaseAndroidPlatform extends InAppPurchasePlatform { } else if (resultWrapper.responseCode == BillingResponse.ok) { status = PurchaseStatus.purchased; } - return [ + return [ PurchaseDetails( purchaseID: '', productID: '', diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform_addition.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform_addition.dart index dd6fe37a1e88..9bcfc3d1b007 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform_addition.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform_addition.dart @@ -74,16 +74,16 @@ class InAppPurchaseAndroidPlatformAddition List responses; PlatformException? exception; try { - responses = await Future.wait([ + responses = await Future.wait(>[ _billingClient.queryPurchases(SkuType.inapp), _billingClient.queryPurchases(SkuType.subs) ]); } on PlatformException catch (e) { exception = e; - responses = [ + responses = [ PurchasesResultWrapper( responseCode: BillingResponse.error, - purchasesList: [], + purchasesList: const [], billingResult: BillingResultWrapper( responseCode: BillingResponse.error, debugMessage: e.details.toString(), @@ -91,7 +91,7 @@ class InAppPurchaseAndroidPlatformAddition ), PurchasesResultWrapper( responseCode: BillingResponse.error, - purchasesList: [], + purchasesList: const [], billingResult: BillingResultWrapper( responseCode: BillingResponse.error, debugMessage: e.details.toString(), @@ -100,17 +100,17 @@ class InAppPurchaseAndroidPlatformAddition ]; } - Set errorCodeSet = responses + final Set errorCodeSet = responses .where((PurchasesResultWrapper response) => response.responseCode != BillingResponse.ok) .map((PurchasesResultWrapper response) => response.responseCode.toString()) .toSet(); - String errorMessage = + final String errorMessage = errorCodeSet.isNotEmpty ? errorCodeSet.join(', ') : ''; - List pastPurchases = + final List pastPurchases = responses.expand((PurchasesResultWrapper response) { return response.purchasesList; }).map((PurchaseWrapper purchaseWrapper) { diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/types/google_play_product_details.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/types/google_play_product_details.dart index 59d33fe26223..58fd34e0ad55 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/types/google_play_product_details.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/types/google_play_product_details.dart @@ -29,10 +29,6 @@ class GooglePlayProductDetails extends ProductDetails { currencySymbol: currencySymbol, ); - /// Points back to the [SkuDetailsWrapper] object that was used to generate - /// this [GooglePlayProductDetails] object. - final SkuDetailsWrapper skuDetails; - /// Generate a [GooglePlayProductDetails] object based on an Android /// [SkuDetailsWrapper] object. factory GooglePlayProductDetails.fromSkuDetails( @@ -49,4 +45,8 @@ class GooglePlayProductDetails extends ProductDetails { skuDetails: skuDetails, ); } + + /// Points back to the [SkuDetailsWrapper] object that was used to generate + /// this [GooglePlayProductDetails] object. + final SkuDetailsWrapper skuDetails; } diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/types/google_play_purchase_details.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/types/google_play_purchase_details.dart index e607164e59f1..42c61a38ddd4 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/types/google_play_purchase_details.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/types/google_play_purchase_details.dart @@ -25,13 +25,9 @@ class GooglePlayPurchaseDetails extends PurchaseDetails { verificationData: verificationData, status: status, ) { - this.pendingCompletePurchase = !billingClientPurchase.isAcknowledged; + pendingCompletePurchase = !billingClientPurchase.isAcknowledged; } - /// Points back to the [PurchaseWrapper] which was used to generate this - /// [GooglePlayPurchaseDetails] object. - final PurchaseWrapper billingClientPurchase; - /// Generate a [PurchaseDetails] object based on an Android [Purchase] object. factory GooglePlayPurchaseDetails.fromPurchase(PurchaseWrapper purchase) { final GooglePlayPurchaseDetails purchaseDetails = GooglePlayPurchaseDetails( @@ -43,7 +39,8 @@ class GooglePlayPurchaseDetails extends PurchaseDetails { source: kIAPSource), transactionDate: purchase.purchaseTime.toString(), billingClientPurchase: purchase, - status: PurchaseStateConverter().toPurchaseStatus(purchase.purchaseState), + status: const PurchaseStateConverter() + .toPurchaseStatus(purchase.purchaseState), ); if (purchaseDetails.status == PurchaseStatus.error) { @@ -56,4 +53,8 @@ class GooglePlayPurchaseDetails extends PurchaseDetails { return purchaseDetails; } + + /// Points back to the [PurchaseWrapper] which was used to generate this + /// [GooglePlayPurchaseDetails] object. + final PurchaseWrapper billingClientPurchase; } diff --git a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml index 08e971ad863a..0a667c672945 100644 --- a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_android description: An implementation for the Android platform of the Flutter `in_app_purchase` plugin. This uses the Android BillingClient APIs. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.2.2+1 +version: 0.2.2+2 environment: sdk: ">=2.14.0 <3.0.0" @@ -28,5 +28,4 @@ dev_dependencies: flutter_test: sdk: flutter json_serializable: ^6.0.0 - pedantic: ^1.10.0 test: ^1.16.0 diff --git a/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/billing_client_wrapper_test.dart b/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/billing_client_wrapper_test.dart index ba6e755e8a4f..1e30ce41beda 100644 --- a/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/billing_client_wrapper_test.dart +++ b/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/billing_client_wrapper_test.dart @@ -40,7 +40,7 @@ void main() { // Make sure that the enum values are supported and that the converter call // does not fail test('response states', () async { - BillingResponseConverter converter = BillingResponseConverter(); + const BillingResponseConverter converter = BillingResponseConverter(); converter.fromJson(-3); converter.fromJson(-2); converter.fromJson(-1); @@ -56,20 +56,20 @@ void main() { }); group('startConnection', () { - final String methodName = + const String methodName = 'BillingClient#startConnection(BillingClientStateListener)'; test('returns BillingResultWrapper', () async { const String debugMessage = 'dummy message'; - final BillingResponse responseCode = BillingResponse.developerError; + const BillingResponse responseCode = BillingResponse.developerError; stubPlatform.addResponse( name: methodName, value: { - 'responseCode': BillingResponseConverter().toJson(responseCode), + 'responseCode': const BillingResponseConverter().toJson(responseCode), 'debugMessage': debugMessage, }, ); - BillingResultWrapper billingResult = BillingResultWrapper( + const BillingResultWrapper billingResult = BillingResultWrapper( responseCode: responseCode, debugMessage: debugMessage); expect( await billingClient.startConnection( @@ -79,11 +79,11 @@ void main() { test('passes handle to onBillingServiceDisconnected', () async { const String debugMessage = 'dummy message'; - final BillingResponse responseCode = BillingResponse.developerError; + const BillingResponse responseCode = BillingResponse.developerError; stubPlatform.addResponse( name: methodName, value: { - 'responseCode': BillingResponseConverter().toJson(responseCode), + 'responseCode': const BillingResponseConverter().toJson(responseCode), 'debugMessage': debugMessage, }, ); @@ -101,14 +101,14 @@ void main() { expect( await billingClient.startConnection( onBillingServiceDisconnected: () {}), - equals(BillingResultWrapper( + equals(const BillingResultWrapper( responseCode: BillingResponse.error, debugMessage: kInvalidBillingResultErrorMessage))); }); }); test('endConnection', () async { - final String endConnectionName = 'BillingClient#endConnection()'; + const String endConnectionName = 'BillingClient#endConnection()'; expect(stubPlatform.countPreviousCalls(endConnectionName), equals(0)); stubPlatform.addResponse(name: endConnectionName, value: null); await billingClient.endConnection(); @@ -116,15 +116,15 @@ void main() { }); group('querySkuDetails', () { - final String queryMethodName = + const String queryMethodName = 'BillingClient#querySkuDetailsAsync(SkuDetailsParams, SkuDetailsResponseListener)'; test('handles empty skuDetails', () async { const String debugMessage = 'dummy message'; - final BillingResponse responseCode = BillingResponse.developerError; + const BillingResponse responseCode = BillingResponse.developerError; stubPlatform.addResponse(name: queryMethodName, value: { 'billingResult': { - 'responseCode': BillingResponseConverter().toJson(responseCode), + 'responseCode': const BillingResponseConverter().toJson(responseCode), 'debugMessage': debugMessage, }, 'skuDetailsList': >[] @@ -134,7 +134,7 @@ void main() { .querySkuDetails( skuType: SkuType.inapp, skusList: ['invalid']); - BillingResultWrapper billingResult = BillingResultWrapper( + const BillingResultWrapper billingResult = BillingResultWrapper( responseCode: responseCode, debugMessage: debugMessage); expect(response.billingResult, equals(billingResult)); expect(response.skuDetailsList, isEmpty); @@ -142,10 +142,10 @@ void main() { test('returns SkuDetailsResponseWrapper', () async { const String debugMessage = 'dummy message'; - final BillingResponse responseCode = BillingResponse.ok; + const BillingResponse responseCode = BillingResponse.ok; stubPlatform.addResponse(name: queryMethodName, value: { 'billingResult': { - 'responseCode': BillingResponseConverter().toJson(responseCode), + 'responseCode': const BillingResponseConverter().toJson(responseCode), 'debugMessage': debugMessage, }, 'skuDetailsList': >[buildSkuMap(dummySkuDetails)] @@ -155,7 +155,7 @@ void main() { .querySkuDetails( skuType: SkuType.inapp, skusList: ['invalid']); - BillingResultWrapper billingResult = BillingResultWrapper( + const BillingResultWrapper billingResult = BillingResultWrapper( responseCode: responseCode, debugMessage: debugMessage); expect(response.billingResult, equals(billingResult)); expect(response.skuDetailsList, contains(dummySkuDetails)); @@ -168,7 +168,7 @@ void main() { .querySkuDetails( skuType: SkuType.inapp, skusList: ['invalid']); - BillingResultWrapper billingResult = BillingResultWrapper( + const BillingResultWrapper billingResult = BillingResultWrapper( responseCode: BillingResponse.error, debugMessage: kInvalidBillingResultErrorMessage); expect(response.billingResult, equals(billingResult)); @@ -177,21 +177,21 @@ void main() { }); group('launchBillingFlow', () { - final String launchMethodName = + const String launchMethodName = 'BillingClient#launchBillingFlow(Activity, BillingFlowParams)'; test('serializes and deserializes data', () async { const String debugMessage = 'dummy message'; - final BillingResponse responseCode = BillingResponse.ok; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResponse responseCode = BillingResponse.ok; + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: responseCode, debugMessage: debugMessage); stubPlatform.addResponse( name: launchMethodName, value: buildBillingResultMap(expectedBillingResult), ); - final SkuDetailsWrapper skuDetails = dummySkuDetails; - final String accountId = "hashedAccountId"; - final String profileId = "hashedProfileId"; + const SkuDetailsWrapper skuDetails = dummySkuDetails; + const String accountId = 'hashedAccountId'; + const String profileId = 'hashedProfileId'; expect( await billingClient.launchBillingFlow( @@ -199,8 +199,9 @@ void main() { accountId: accountId, obfuscatedProfileId: profileId), equals(expectedBillingResult)); - Map arguments = - stubPlatform.previousCallMatching(launchMethodName).arguments; + final Map arguments = stubPlatform + .previousCallMatching(launchMethodName) + .arguments as Map; expect(arguments['sku'], equals(skuDetails.sku)); expect(arguments['accountId'], equals(accountId)); expect(arguments['obfuscatedProfileId'], equals(profileId)); @@ -210,16 +211,16 @@ void main() { 'Change subscription throws assertion error `oldSku` and `purchaseToken` has different nullability', () async { const String debugMessage = 'dummy message'; - final BillingResponse responseCode = BillingResponse.ok; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResponse responseCode = BillingResponse.ok; + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: responseCode, debugMessage: debugMessage); stubPlatform.addResponse( name: launchMethodName, value: buildBillingResultMap(expectedBillingResult), ); - final SkuDetailsWrapper skuDetails = dummySkuDetails; - final String accountId = 'hashedAccountId'; - final String profileId = 'hashedProfileId'; + const SkuDetailsWrapper skuDetails = dummySkuDetails; + const String accountId = 'hashedAccountId'; + const String profileId = 'hashedProfileId'; expect( billingClient.launchBillingFlow( @@ -244,16 +245,16 @@ void main() { 'serializes and deserializes data on change subscription without proration', () async { const String debugMessage = 'dummy message'; - final BillingResponse responseCode = BillingResponse.ok; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResponse responseCode = BillingResponse.ok; + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: responseCode, debugMessage: debugMessage); stubPlatform.addResponse( name: launchMethodName, value: buildBillingResultMap(expectedBillingResult), ); - final SkuDetailsWrapper skuDetails = dummySkuDetails; - final String accountId = 'hashedAccountId'; - final String profileId = 'hashedProfileId'; + const SkuDetailsWrapper skuDetails = dummySkuDetails; + const String accountId = 'hashedAccountId'; + const String profileId = 'hashedProfileId'; expect( await billingClient.launchBillingFlow( @@ -263,8 +264,9 @@ void main() { oldSku: dummyOldPurchase.sku, purchaseToken: dummyOldPurchase.purchaseToken), equals(expectedBillingResult)); - Map arguments = - stubPlatform.previousCallMatching(launchMethodName).arguments; + final Map arguments = stubPlatform + .previousCallMatching(launchMethodName) + .arguments as Map; expect(arguments['sku'], equals(skuDetails.sku)); expect(arguments['accountId'], equals(accountId)); expect(arguments['oldSku'], equals(dummyOldPurchase.sku)); @@ -277,17 +279,18 @@ void main() { 'serializes and deserializes data on change subscription with proration', () async { const String debugMessage = 'dummy message'; - final BillingResponse responseCode = BillingResponse.ok; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResponse responseCode = BillingResponse.ok; + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: responseCode, debugMessage: debugMessage); stubPlatform.addResponse( name: launchMethodName, value: buildBillingResultMap(expectedBillingResult), ); - final SkuDetailsWrapper skuDetails = dummySkuDetails; - final String accountId = 'hashedAccountId'; - final String profileId = 'hashedProfileId'; - final prorationMode = ProrationMode.immediateAndChargeProratedPrice; + const SkuDetailsWrapper skuDetails = dummySkuDetails; + const String accountId = 'hashedAccountId'; + const String profileId = 'hashedProfileId'; + const ProrationMode prorationMode = + ProrationMode.immediateAndChargeProratedPrice; expect( await billingClient.launchBillingFlow( @@ -298,8 +301,9 @@ void main() { prorationMode: prorationMode, purchaseToken: dummyOldPurchase.purchaseToken), equals(expectedBillingResult)); - Map arguments = - stubPlatform.previousCallMatching(launchMethodName).arguments; + final Map arguments = stubPlatform + .previousCallMatching(launchMethodName) + .arguments as Map; expect(arguments['sku'], equals(skuDetails.sku)); expect(arguments['accountId'], equals(accountId)); expect(arguments['oldSku'], equals(dummyOldPurchase.sku)); @@ -307,24 +311,25 @@ void main() { expect( arguments['purchaseToken'], equals(dummyOldPurchase.purchaseToken)); expect(arguments['prorationMode'], - ProrationModeConverter().toJson(prorationMode)); + const ProrationModeConverter().toJson(prorationMode)); }); test('handles null accountId', () async { const String debugMessage = 'dummy message'; - final BillingResponse responseCode = BillingResponse.ok; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResponse responseCode = BillingResponse.ok; + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: responseCode, debugMessage: debugMessage); stubPlatform.addResponse( name: launchMethodName, value: buildBillingResultMap(expectedBillingResult), ); - final SkuDetailsWrapper skuDetails = dummySkuDetails; + const SkuDetailsWrapper skuDetails = dummySkuDetails; expect(await billingClient.launchBillingFlow(sku: skuDetails.sku), equals(expectedBillingResult)); - Map arguments = - stubPlatform.previousCallMatching(launchMethodName).arguments; + final Map arguments = stubPlatform + .previousCallMatching(launchMethodName) + .arguments as Map; expect(arguments['sku'], equals(skuDetails.sku)); expect(arguments['accountId'], isNull); }); @@ -334,10 +339,10 @@ void main() { name: launchMethodName, value: null, ); - final SkuDetailsWrapper skuDetails = dummySkuDetails; + const SkuDetailsWrapper skuDetails = dummySkuDetails; expect( await billingClient.launchBillingFlow(sku: skuDetails.sku), - equals(BillingResultWrapper( + equals(const BillingResultWrapper( responseCode: BillingResponse.error, debugMessage: kInvalidBillingResultErrorMessage))); }); @@ -348,17 +353,17 @@ void main() { 'BillingClient#queryPurchases(String)'; test('serializes and deserializes data', () async { - final BillingResponse expectedCode = BillingResponse.ok; + const BillingResponse expectedCode = BillingResponse.ok; final List expectedList = [ dummyPurchase ]; const String debugMessage = 'dummy message'; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: expectedCode, debugMessage: debugMessage); stubPlatform .addResponse(name: queryPurchasesMethodName, value: { 'billingResult': buildBillingResultMap(expectedBillingResult), - 'responseCode': BillingResponseConverter().toJson(expectedCode), + 'responseCode': const BillingResponseConverter().toJson(expectedCode), 'purchasesList': expectedList .map((PurchaseWrapper purchase) => buildPurchaseMap(purchase)) .toList(), @@ -373,15 +378,15 @@ void main() { }); test('handles empty purchases', () async { - final BillingResponse expectedCode = BillingResponse.userCanceled; + const BillingResponse expectedCode = BillingResponse.userCanceled; const String debugMessage = 'dummy message'; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: expectedCode, debugMessage: debugMessage); stubPlatform .addResponse(name: queryPurchasesMethodName, value: { 'billingResult': buildBillingResultMap(expectedBillingResult), - 'responseCode': BillingResponseConverter().toJson(expectedCode), - 'purchasesList': [], + 'responseCode': const BillingResponseConverter().toJson(expectedCode), + 'purchasesList': [], }); final PurchasesResultWrapper response = @@ -402,7 +407,7 @@ void main() { expect( response.billingResult, - equals(BillingResultWrapper( + equals(const BillingResultWrapper( responseCode: BillingResponse.error, debugMessage: kInvalidBillingResultErrorMessage))); expect(response.responseCode, BillingResponse.error); @@ -415,13 +420,13 @@ void main() { 'BillingClient#queryPurchaseHistoryAsync(String, PurchaseHistoryResponseListener)'; test('serializes and deserializes data', () async { - final BillingResponse expectedCode = BillingResponse.ok; + const BillingResponse expectedCode = BillingResponse.ok; final List expectedList = [ dummyPurchaseHistoryRecord, ]; const String debugMessage = 'dummy message'; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: expectedCode, debugMessage: debugMessage); stubPlatform.addResponse( name: queryPurchaseHistoryMethodName, @@ -440,14 +445,16 @@ void main() { }); test('handles empty purchases', () async { - final BillingResponse expectedCode = BillingResponse.userCanceled; + const BillingResponse expectedCode = BillingResponse.userCanceled; const String debugMessage = 'dummy message'; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: expectedCode, debugMessage: debugMessage); - stubPlatform.addResponse(name: queryPurchaseHistoryMethodName, value: { - 'billingResult': buildBillingResultMap(expectedBillingResult), - 'purchaseHistoryRecordList': [], - }); + stubPlatform.addResponse( + name: queryPurchaseHistoryMethodName, + value: { + 'billingResult': buildBillingResultMap(expectedBillingResult), + 'purchaseHistoryRecordList': [], + }); final PurchasesHistoryResult response = await billingClient.queryPurchaseHistory(SkuType.inapp); @@ -466,7 +473,7 @@ void main() { expect( response.billingResult, - equals(BillingResultWrapper( + equals(const BillingResultWrapper( responseCode: BillingResponse.error, debugMessage: kInvalidBillingResultErrorMessage))); expect(response.purchaseHistoryRecordList, isEmpty); @@ -477,9 +484,9 @@ void main() { const String consumeMethodName = 'BillingClient#consumeAsync(String, ConsumeResponseListener)'; test('consume purchase async success', () async { - final BillingResponse expectedCode = BillingResponse.ok; + const BillingResponse expectedCode = BillingResponse.ok; const String debugMessage = 'dummy message'; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: expectedCode, debugMessage: debugMessage); stubPlatform.addResponse( name: consumeMethodName, @@ -501,7 +508,7 @@ void main() { expect( billingResult, - equals(BillingResultWrapper( + equals(const BillingResultWrapper( responseCode: BillingResponse.error, debugMessage: kInvalidBillingResultErrorMessage))); }); @@ -511,9 +518,9 @@ void main() { const String acknowledgeMethodName = 'BillingClient#(AcknowledgePurchaseParams params, (AcknowledgePurchaseParams, AcknowledgePurchaseResponseListener)'; test('acknowledge purchase success', () async { - final BillingResponse expectedCode = BillingResponse.ok; + const BillingResponse expectedCode = BillingResponse.ok; const String debugMessage = 'dummy message'; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: expectedCode, debugMessage: debugMessage); stubPlatform.addResponse( name: acknowledgeMethodName, @@ -534,7 +541,7 @@ void main() { expect( billingResult, - equals(BillingResultWrapper( + equals(const BillingResultWrapper( responseCode: BillingResponse.error, debugMessage: kInvalidBillingResultErrorMessage))); }); @@ -548,7 +555,8 @@ void main() { stubPlatform.addResponse( name: isFeatureSupportedMethodName, value: false, - additionalStepBeforeReturn: (value) => arguments = value, + additionalStepBeforeReturn: (dynamic value) => + arguments = value as Map, ); final bool isSupported = await billingClient .isFeatureSupported(BillingClientFeature.subscriptions); @@ -561,7 +569,8 @@ void main() { stubPlatform.addResponse( name: isFeatureSupportedMethodName, value: true, - additionalStepBeforeReturn: (value) => arguments = value, + additionalStepBeforeReturn: (dynamic value) => + arguments = value as Map, ); final bool isSupported = await billingClient .isFeatureSupported(BillingClientFeature.subscriptions); @@ -574,7 +583,8 @@ void main() { const String launchPriceChangeConfirmationFlowMethodName = 'BillingClient#launchPriceChangeConfirmationFlow (Activity, PriceChangeFlowParams, PriceChangeConfirmationListener)'; - final expectedBillingResultPriceChangeConfirmation = BillingResultWrapper( + const BillingResultWrapper expectedBillingResultPriceChangeConfirmation = + BillingResultWrapper( responseCode: BillingResponse.ok, debugMessage: 'dummy message', ); diff --git a/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/purchase_wrapper_test.dart b/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/purchase_wrapper_test.dart index f51342045a7d..65c8bb213cc4 100644 --- a/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/purchase_wrapper_test.dart +++ b/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/purchase_wrapper_test.dart @@ -6,7 +6,7 @@ import 'package:in_app_purchase_android/billing_client_wrappers.dart'; import 'package:in_app_purchase_android/in_app_purchase_android.dart'; import 'package:test/test.dart'; -final PurchaseWrapper dummyPurchase = PurchaseWrapper( +const PurchaseWrapper dummyPurchase = PurchaseWrapper( orderId: 'orderId', packageName: 'packageName', purchaseTime: 0, @@ -22,7 +22,7 @@ final PurchaseWrapper dummyPurchase = PurchaseWrapper( obfuscatedProfileId: 'Profile103', ); -final PurchaseWrapper dummyUnacknowledgedPurchase = PurchaseWrapper( +const PurchaseWrapper dummyUnacknowledgedPurchase = PurchaseWrapper( orderId: 'orderId', packageName: 'packageName', purchaseTime: 0, @@ -36,7 +36,7 @@ final PurchaseWrapper dummyUnacknowledgedPurchase = PurchaseWrapper( purchaseState: PurchaseStateWrapper.purchased, ); -final PurchaseHistoryRecordWrapper dummyPurchaseHistoryRecord = +const PurchaseHistoryRecordWrapper dummyPurchaseHistoryRecord = PurchaseHistoryRecordWrapper( purchaseTime: 0, signature: 'signature', @@ -46,7 +46,7 @@ final PurchaseHistoryRecordWrapper dummyPurchaseHistoryRecord = developerPayload: 'dummy payload', ); -final PurchaseWrapper dummyOldPurchase = PurchaseWrapper( +const PurchaseWrapper dummyOldPurchase = PurchaseWrapper( orderId: 'oldOrderId', packageName: 'oldPackageName', purchaseTime: 0, @@ -63,7 +63,7 @@ final PurchaseWrapper dummyOldPurchase = PurchaseWrapper( void main() { group('PurchaseWrapper', () { test('converts from map', () { - final PurchaseWrapper expected = dummyPurchase; + const PurchaseWrapper expected = dummyPurchase; final PurchaseWrapper parsed = PurchaseWrapper.fromJson(buildPurchaseMap(expected)); @@ -109,7 +109,7 @@ void main() { group('PurchaseHistoryRecordWrapper', () { test('converts from map', () { - final PurchaseHistoryRecordWrapper expected = dummyPurchaseHistoryRecord; + const PurchaseHistoryRecordWrapper expected = dummyPurchaseHistoryRecord; final PurchaseHistoryRecordWrapper parsed = PurchaseHistoryRecordWrapper.fromJson( buildPurchaseHistoryRecordMap(expected)); @@ -120,13 +120,13 @@ void main() { group('PurchasesResultWrapper', () { test('parsed from map', () { - final BillingResponse responseCode = BillingResponse.ok; + const BillingResponse responseCode = BillingResponse.ok; final List purchases = [ dummyPurchase, dummyPurchase ]; const String debugMessage = 'dummy Message'; - final BillingResultWrapper billingResult = BillingResultWrapper( + const BillingResultWrapper billingResult = BillingResultWrapper( responseCode: responseCode, debugMessage: debugMessage); final PurchasesResultWrapper expected = PurchasesResultWrapper( billingResult: billingResult, @@ -135,7 +135,7 @@ void main() { final PurchasesResultWrapper parsed = PurchasesResultWrapper.fromJson({ 'billingResult': buildBillingResultMap(billingResult), - 'responseCode': BillingResponseConverter().toJson(responseCode), + 'responseCode': const BillingResponseConverter().toJson(responseCode), 'purchasesList': >[ buildPurchaseMap(dummyPurchase), buildPurchaseMap(dummyPurchase) @@ -148,10 +148,10 @@ void main() { test('parsed from empty map', () { final PurchasesResultWrapper parsed = - PurchasesResultWrapper.fromJson({}); + PurchasesResultWrapper.fromJson(const {}); expect( parsed.billingResult, - equals(BillingResultWrapper( + equals(const BillingResultWrapper( responseCode: BillingResponse.error, debugMessage: kInvalidBillingResultErrorMessage))); expect(parsed.responseCode, BillingResponse.error); @@ -161,14 +161,14 @@ void main() { group('PurchasesHistoryResult', () { test('parsed from map', () { - final BillingResponse responseCode = BillingResponse.ok; + const BillingResponse responseCode = BillingResponse.ok; final List purchaseHistoryRecordList = [ dummyPurchaseHistoryRecord, dummyPurchaseHistoryRecord ]; const String debugMessage = 'dummy Message'; - final BillingResultWrapper billingResult = BillingResultWrapper( + const BillingResultWrapper billingResult = BillingResultWrapper( responseCode: responseCode, debugMessage: debugMessage); final PurchasesHistoryResult expected = PurchasesHistoryResult( billingResult: billingResult, @@ -188,10 +188,10 @@ void main() { test('parsed from empty map', () { final PurchasesHistoryResult parsed = - PurchasesHistoryResult.fromJson({}); + PurchasesHistoryResult.fromJson(const {}); expect( parsed.billingResult, - equals(BillingResultWrapper( + equals(const BillingResultWrapper( responseCode: BillingResponse.error, debugMessage: kInvalidBillingResultErrorMessage))); expect(parsed.purchaseHistoryRecordList, isEmpty); @@ -210,7 +210,8 @@ Map buildPurchaseMap(PurchaseWrapper original) { 'isAutoRenewing': original.isAutoRenewing, 'originalJson': original.originalJson, 'developerPayload': original.developerPayload, - 'purchaseState': PurchaseStateConverter().toJson(original.purchaseState), + 'purchaseState': + const PurchaseStateConverter().toJson(original.purchaseState), 'isAcknowledged': original.isAcknowledged, 'obfuscatedAccountId': original.obfuscatedAccountId, 'obfuscatedProfileId': original.obfuscatedProfileId, @@ -231,7 +232,8 @@ Map buildPurchaseHistoryRecordMap( Map buildBillingResultMap(BillingResultWrapper original) { return { - 'responseCode': BillingResponseConverter().toJson(original.responseCode), + 'responseCode': + const BillingResponseConverter().toJson(original.responseCode), 'debugMessage': original.debugMessage, }; } diff --git a/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/sku_details_wrapper_deprecated_test.dart b/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/sku_details_wrapper_deprecated_test.dart index 3e29d92724ad..f27ea02209c4 100644 --- a/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/sku_details_wrapper_deprecated_test.dart +++ b/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/sku_details_wrapper_deprecated_test.dart @@ -13,7 +13,7 @@ void main() { test( 'Deprecated `introductoryPriceMicros` field reflects parameter from constructor', () { - final SkuDetailsWrapper skuDetails = SkuDetailsWrapper( + const SkuDetailsWrapper skuDetails = SkuDetailsWrapper( description: 'description', freeTrialPeriod: 'freeTrialPeriod', introductoryPrice: 'introductoryPrice', @@ -42,7 +42,7 @@ void main() { test( '`introductoryPriceAmoutMicros` constructor parameter is reflected by deprecated `introductoryPriceMicros` and `introductoryPriceAmountMicros` fields', () { - final SkuDetailsWrapper skuDetails = SkuDetailsWrapper( + const SkuDetailsWrapper skuDetails = SkuDetailsWrapper( description: 'description', freeTrialPeriod: 'freeTrialPeriod', introductoryPrice: 'introductoryPrice', diff --git a/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/sku_details_wrapper_test.dart b/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/sku_details_wrapper_test.dart index 08cde077be93..ecc399b27716 100644 --- a/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/sku_details_wrapper_test.dart +++ b/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/sku_details_wrapper_test.dart @@ -6,7 +6,7 @@ import 'package:in_app_purchase_android/billing_client_wrappers.dart'; import 'package:in_app_purchase_android/src/types/google_play_product_details.dart'; import 'package:test/test.dart'; -final SkuDetailsWrapper dummySkuDetails = SkuDetailsWrapper( +const SkuDetailsWrapper dummySkuDetails = SkuDetailsWrapper( description: 'description', freeTrialPeriod: 'freeTrialPeriod', introductoryPrice: 'introductoryPrice', @@ -28,7 +28,7 @@ final SkuDetailsWrapper dummySkuDetails = SkuDetailsWrapper( void main() { group('SkuDetailsWrapper', () { test('converts from map', () { - final SkuDetailsWrapper expected = dummySkuDetails; + const SkuDetailsWrapper expected = dummySkuDetails; final SkuDetailsWrapper parsed = SkuDetailsWrapper.fromJson(buildSkuMap(expected)); @@ -38,13 +38,13 @@ void main() { group('SkuDetailsResponseWrapper', () { test('parsed from map', () { - final BillingResponse responseCode = BillingResponse.ok; + const BillingResponse responseCode = BillingResponse.ok; const String debugMessage = 'dummy message'; final List skusDetails = [ dummySkuDetails, dummySkuDetails ]; - BillingResultWrapper result = BillingResultWrapper( + const BillingResultWrapper result = BillingResultWrapper( responseCode: responseCode, debugMessage: debugMessage); final SkuDetailsResponseWrapper expected = SkuDetailsResponseWrapper( billingResult: result, skuDetailsList: skusDetails); @@ -52,7 +52,7 @@ void main() { final SkuDetailsResponseWrapper parsed = SkuDetailsResponseWrapper.fromJson({ 'billingResult': { - 'responseCode': BillingResponseConverter().toJson(responseCode), + 'responseCode': const BillingResponseConverter().toJson(responseCode), 'debugMessage': debugMessage, }, 'skuDetailsList': >[ @@ -78,10 +78,10 @@ void main() { }); test('handles empty list of skuDetails', () { - final BillingResponse responseCode = BillingResponse.error; + const BillingResponse responseCode = BillingResponse.error; const String debugMessage = 'dummy message'; final List skusDetails = []; - BillingResultWrapper billingResult = BillingResultWrapper( + const BillingResultWrapper billingResult = BillingResultWrapper( responseCode: responseCode, debugMessage: debugMessage); final SkuDetailsResponseWrapper expected = SkuDetailsResponseWrapper( billingResult: billingResult, skuDetailsList: skusDetails); @@ -89,10 +89,10 @@ void main() { final SkuDetailsResponseWrapper parsed = SkuDetailsResponseWrapper.fromJson({ 'billingResult': { - 'responseCode': BillingResponseConverter().toJson(responseCode), + 'responseCode': const BillingResponseConverter().toJson(responseCode), 'debugMessage': debugMessage, }, - 'skuDetailsList': >[] + 'skuDetailsList': const >[] }); expect(parsed.billingResult, equals(expected.billingResult)); @@ -101,10 +101,10 @@ void main() { test('fromJson creates an object with default values', () { final SkuDetailsResponseWrapper skuDetails = - SkuDetailsResponseWrapper.fromJson({}); + SkuDetailsResponseWrapper.fromJson(const {}); expect( skuDetails.billingResult, - equals(BillingResultWrapper( + equals(const BillingResultWrapper( responseCode: BillingResponse.error, debugMessage: kInvalidBillingResultErrorMessage))); expect(skuDetails.skuDetailsList, isEmpty); @@ -114,7 +114,7 @@ void main() { group('BillingResultWrapper', () { test('fromJson on empty map creates an object with default values', () { final BillingResultWrapper billingResult = - BillingResultWrapper.fromJson({}); + BillingResultWrapper.fromJson(const {}); expect(billingResult.debugMessage, kInvalidBillingResultErrorMessage); expect(billingResult.responseCode, BillingResponse.error); }); diff --git a/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_addition_test.dart b/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_addition_test.dart index 112300d54d7e..9d2045b4c229 100644 --- a/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_addition_test.dart +++ b/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_addition_test.dart @@ -30,8 +30,8 @@ void main() { widgets.WidgetsFlutterBinding.ensureInitialized(); const String debugMessage = 'dummy message'; - final BillingResponse responseCode = BillingResponse.ok; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResponse responseCode = BillingResponse.ok; + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: responseCode, debugMessage: debugMessage); stubPlatform.addResponse( name: startConnectionCall, @@ -45,9 +45,9 @@ void main() { const String consumeMethodName = 'BillingClient#consumeAsync(String, ConsumeResponseListener)'; test('consume purchase async success', () async { - final BillingResponse expectedCode = BillingResponse.ok; + const BillingResponse expectedCode = BillingResponse.ok; const String debugMessage = 'dummy message'; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: expectedCode, debugMessage: debugMessage); stubPlatform.addResponse( name: consumeMethodName, @@ -66,14 +66,14 @@ void main() { const String queryMethodName = 'BillingClient#queryPurchases(String)'; test('handles error', () async { const String debugMessage = 'dummy message'; - final BillingResponse responseCode = BillingResponse.developerError; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResponse responseCode = BillingResponse.developerError; + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: responseCode, debugMessage: debugMessage); stubPlatform .addResponse(name: queryMethodName, value: { 'billingResult': buildBillingResultMap(expectedBillingResult), - 'responseCode': BillingResponseConverter().toJson(responseCode), + 'responseCode': const BillingResponseConverter().toJson(responseCode), 'purchasesList': >[] }); final QueryPurchaseDetailsResponse response = @@ -87,14 +87,14 @@ void main() { test('returns SkuDetailsResponseWrapper', () async { const String debugMessage = 'dummy message'; - final BillingResponse responseCode = BillingResponse.ok; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResponse responseCode = BillingResponse.ok; + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: responseCode, debugMessage: debugMessage); stubPlatform .addResponse(name: queryMethodName, value: { 'billingResult': buildBillingResultMap(expectedBillingResult), - 'responseCode': BillingResponseConverter().toJson(responseCode), + 'responseCode': const BillingResponseConverter().toJson(responseCode), 'purchasesList': >[ buildPurchaseMap(dummyPurchase), ] @@ -111,21 +111,22 @@ void main() { test('should store platform exception in the response', () async { const String debugMessage = 'dummy message'; - final BillingResponse responseCode = BillingResponse.developerError; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResponse responseCode = BillingResponse.developerError; + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: responseCode, debugMessage: debugMessage); stubPlatform.addResponse( name: queryMethodName, value: { - 'responseCode': BillingResponseConverter().toJson(responseCode), + 'responseCode': + const BillingResponseConverter().toJson(responseCode), 'billingResult': buildBillingResultMap(expectedBillingResult), 'purchasesList': >[] }, - additionalStepBeforeReturn: (_) { + additionalStepBeforeReturn: (dynamic _) { throw PlatformException( code: 'error_code', message: 'error_message', - details: {'info': 'error_info'}, + details: {'info': 'error_info'}, ); }); final QueryPurchaseDetailsResponse response = @@ -134,7 +135,8 @@ void main() { expect(response.error, isNotNull); expect(response.error!.code, 'error_code'); expect(response.error!.message, 'error_message'); - expect(response.error!.details, {'info': 'error_info'}); + expect( + response.error!.details, {'info': 'error_info'}); }); }); }); @@ -147,7 +149,8 @@ void main() { stubPlatform.addResponse( name: isFeatureSupportedMethodName, value: false, - additionalStepBeforeReturn: (value) => arguments = value, + additionalStepBeforeReturn: (dynamic value) => + arguments = value as Map, ); final bool isSupported = await iapAndroidPlatformAddition .isFeatureSupported(BillingClientFeature.subscriptions); @@ -160,7 +163,8 @@ void main() { stubPlatform.addResponse( name: isFeatureSupportedMethodName, value: true, - additionalStepBeforeReturn: (value) => arguments = value, + additionalStepBeforeReturn: (dynamic value) => + arguments = value as Map, ); final bool isSupported = await iapAndroidPlatformAddition .isFeatureSupported(BillingClientFeature.subscriptions); @@ -172,9 +176,10 @@ void main() { group('launchPriceChangeConfirmationFlow', () { const String launchPriceChangeConfirmationFlowMethodName = 'BillingClient#launchPriceChangeConfirmationFlow (Activity, PriceChangeFlowParams, PriceChangeConfirmationListener)'; - const dummySku = 'sku'; + const String dummySku = 'sku'; - final expectedBillingResultPriceChangeConfirmation = BillingResultWrapper( + const BillingResultWrapper expectedBillingResultPriceChangeConfirmation = + BillingResultWrapper( responseCode: BillingResponse.ok, debugMessage: 'dummy message', ); diff --git a/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_test.dart b/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_test.dart index 9c167e5ea3be..b19d631092e7 100644 --- a/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_test.dart +++ b/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_test.dart @@ -33,8 +33,8 @@ void main() { widgets.WidgetsFlutterBinding.ensureInitialized(); const String debugMessage = 'dummy message'; - final BillingResponse responseCode = BillingResponse.ok; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResponse responseCode = BillingResponse.ok; + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: responseCode, debugMessage: debugMessage); stubPlatform.addResponse( name: startConnectionCall, @@ -70,28 +70,28 @@ void main() { }); group('querySkuDetails', () { - final String queryMethodName = + const String queryMethodName = 'BillingClient#querySkuDetailsAsync(SkuDetailsParams, SkuDetailsResponseListener)'; test('handles empty skuDetails', () async { const String debugMessage = 'dummy message'; - final BillingResponse responseCode = BillingResponse.ok; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResponse responseCode = BillingResponse.ok; + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: responseCode, debugMessage: debugMessage); stubPlatform.addResponse(name: queryMethodName, value: { 'billingResult': buildBillingResultMap(expectedBillingResult), - 'skuDetailsList': [], + 'skuDetailsList': >[], }); final ProductDetailsResponse response = - await iapAndroidPlatform.queryProductDetails([''].toSet()); + await iapAndroidPlatform.queryProductDetails({''}); expect(response.productDetails, isEmpty); }); test('should get correct product details', () async { const String debugMessage = 'dummy message'; - final BillingResponse responseCode = BillingResponse.ok; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResponse responseCode = BillingResponse.ok; + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: responseCode, debugMessage: debugMessage); stubPlatform.addResponse(name: queryMethodName, value: { 'billingResult': buildBillingResultMap(expectedBillingResult), @@ -99,8 +99,8 @@ void main() { }); // Since queryProductDetails makes 2 platform method calls (one for each SkuType), the result will contain 2 dummyWrapper instead // of 1. - final ProductDetailsResponse response = await iapAndroidPlatform - .queryProductDetails(['valid'].toSet()); + final ProductDetailsResponse response = + await iapAndroidPlatform.queryProductDetails({'valid'}); expect(response.productDetails.first.title, dummySkuDetails.title); expect(response.productDetails.first.description, dummySkuDetails.description); @@ -110,8 +110,8 @@ void main() { test('should get the correct notFoundIDs', () async { const String debugMessage = 'dummy message'; - final BillingResponse responseCode = BillingResponse.ok; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResponse responseCode = BillingResponse.ok; + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: responseCode, debugMessage: debugMessage); stubPlatform.addResponse(name: queryMethodName, value: { 'billingResult': buildBillingResultMap(expectedBillingResult), @@ -119,41 +119,42 @@ void main() { }); // Since queryProductDetails makes 2 platform method calls (one for each SkuType), the result will contain 2 dummyWrapper instead // of 1. - final ProductDetailsResponse response = await iapAndroidPlatform - .queryProductDetails(['invalid'].toSet()); + final ProductDetailsResponse response = + await iapAndroidPlatform.queryProductDetails({'invalid'}); expect(response.notFoundIDs.first, 'invalid'); }); test( 'should have error stored in the response when platform exception is thrown', () async { - final BillingResponse responseCode = BillingResponse.ok; + const BillingResponse responseCode = BillingResponse.ok; stubPlatform.addResponse( name: queryMethodName, value: { - 'responseCode': BillingResponseConverter().toJson(responseCode), + 'responseCode': + const BillingResponseConverter().toJson(responseCode), 'skuDetailsList': >[ buildSkuMap(dummySkuDetails) ] }, - additionalStepBeforeReturn: (_) { + additionalStepBeforeReturn: (dynamic _) { throw PlatformException( code: 'error_code', message: 'error_message', - details: {'info': 'error_info'}, + details: {'info': 'error_info'}, ); }); // Since queryProductDetails makes 2 platform method calls (one for each SkuType), the result will contain 2 dummyWrapper instead // of 1. - final ProductDetailsResponse response = await iapAndroidPlatform - .queryProductDetails(['invalid'].toSet()); - expect(response.notFoundIDs, ['invalid']); + final ProductDetailsResponse response = + await iapAndroidPlatform.queryProductDetails({'invalid'}); + expect(response.notFoundIDs, ['invalid']); expect(response.productDetails, isEmpty); expect(response.error, isNotNull); expect(response.error!.source, kIAPSource); expect(response.error!.code, 'error_code'); expect(response.error!.message, 'error_message'); - expect(response.error!.details, {'info': 'error_info'}); + expect(response.error!.details, {'info': 'error_info'}); }); }); @@ -161,13 +162,13 @@ void main() { const String queryMethodName = 'BillingClient#queryPurchases(String)'; test('handles error', () async { const String debugMessage = 'dummy message'; - final BillingResponse responseCode = BillingResponse.developerError; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResponse responseCode = BillingResponse.developerError; + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: responseCode, debugMessage: debugMessage); stubPlatform.addResponse(name: queryMethodName, value: { 'billingResult': buildBillingResultMap(expectedBillingResult), - 'responseCode': BillingResponseConverter().toJson(responseCode), + 'responseCode': const BillingResponseConverter().toJson(responseCode), 'purchasesList': >[] }); @@ -175,9 +176,12 @@ void main() { iapAndroidPlatform.restorePurchases(), throwsA( isA() - .having((e) => e.source, 'source', kIAPSource) - .having((e) => e.code, 'code', kRestoredPurchaseErrorCode) - .having((e) => e.message, 'message', responseCode.toString()), + .having( + (InAppPurchaseException e) => e.source, 'source', kIAPSource) + .having((InAppPurchaseException e) => e.code, 'code', + kRestoredPurchaseErrorCode) + .having((InAppPurchaseException e) => e.message, 'message', + responseCode.toString()), ), ); }); @@ -185,21 +189,22 @@ void main() { test('should store platform exception in the response', () async { const String debugMessage = 'dummy message'; - final BillingResponse responseCode = BillingResponse.developerError; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResponse responseCode = BillingResponse.developerError; + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: responseCode, debugMessage: debugMessage); stubPlatform.addResponse( name: queryMethodName, value: { - 'responseCode': BillingResponseConverter().toJson(responseCode), + 'responseCode': + const BillingResponseConverter().toJson(responseCode), 'billingResult': buildBillingResultMap(expectedBillingResult), 'purchasesList': >[] }, - additionalStepBeforeReturn: (_) { + additionalStepBeforeReturn: (dynamic _) { throw PlatformException( code: 'error_code', message: 'error_message', - details: {'info': 'error_info'}, + details: {'info': 'error_info'}, ); }); @@ -207,19 +212,23 @@ void main() { iapAndroidPlatform.restorePurchases(), throwsA( isA() - .having((e) => e.code, 'code', 'error_code') - .having((e) => e.message, 'message', 'error_message') - .having((e) => e.details, 'details', {'info': 'error_info'}), + .having((PlatformException e) => e.code, 'code', 'error_code') + .having((PlatformException e) => e.message, 'message', + 'error_message') + .having((PlatformException e) => e.details, 'details', + {'info': 'error_info'}), ), ); }); test('returns SkuDetailsResponseWrapper', () async { - Completer completer = Completer(); - Stream> stream = iapAndroidPlatform.purchaseStream; + final Completer> completer = + Completer>(); + final Stream> stream = + iapAndroidPlatform.purchaseStream; - late StreamSubscription subscription; - subscription = stream.listen((purchaseDetailsList) { + late StreamSubscription> subscription; + subscription = stream.listen((List purchaseDetailsList) { if (purchaseDetailsList.first.status == PurchaseStatus.restored) { completer.complete(purchaseDetailsList); subscription.cancel(); @@ -227,13 +236,13 @@ void main() { }); const String debugMessage = 'dummy message'; - final BillingResponse responseCode = BillingResponse.ok; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResponse responseCode = BillingResponse.ok; + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: responseCode, debugMessage: debugMessage); stubPlatform.addResponse(name: queryMethodName, value: { 'billingResult': buildBillingResultMap(expectedBillingResult), - 'responseCode': BillingResponseConverter().toJson(responseCode), + 'responseCode': const BillingResponseConverter().toJson(responseCode), 'purchasesList': >[ buildPurchaseMap(dummyPurchase), ] @@ -246,8 +255,8 @@ void main() { final List restoredPurchases = await completer.future; expect(restoredPurchases.length, 2); - restoredPurchases.forEach((element) { - GooglePlayPurchaseDetails purchase = + for (final PurchaseDetails element in restoredPurchases) { + final GooglePlayPurchaseDetails purchase = element as GooglePlayPurchaseDetails; expect(purchase.productID, dummyPurchase.sku); @@ -260,40 +269,41 @@ void main() { expect(purchase.transactionDate, dummyPurchase.purchaseTime.toString()); expect(purchase.billingClientPurchase, dummyPurchase); expect(purchase.status, PurchaseStatus.restored); - }); + } }); }); group('make payment', () { - final String launchMethodName = + const String launchMethodName = 'BillingClient#launchBillingFlow(Activity, BillingFlowParams)'; const String consumeMethodName = 'BillingClient#consumeAsync(String, ConsumeResponseListener)'; test('buy non consumable, serializes and deserializes data', () async { - final SkuDetailsWrapper skuDetails = dummySkuDetails; - final String accountId = "hashedAccountId"; + const SkuDetailsWrapper skuDetails = dummySkuDetails; + const String accountId = 'hashedAccountId'; const String debugMessage = 'dummy message'; - final BillingResponse sentCode = BillingResponse.ok; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResponse sentCode = BillingResponse.ok; + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: sentCode, debugMessage: debugMessage); stubPlatform.addResponse( name: launchMethodName, value: buildBillingResultMap(expectedBillingResult), - additionalStepBeforeReturn: (_) { + additionalStepBeforeReturn: (dynamic _) { // Mock java update purchase callback. - MethodCall call = MethodCall(kOnPurchasesUpdated, { + final MethodCall call = + MethodCall(kOnPurchasesUpdated, { 'billingResult': buildBillingResultMap(expectedBillingResult), - 'responseCode': BillingResponseConverter().toJson(sentCode), - 'purchasesList': [ - { + 'responseCode': const BillingResponseConverter().toJson(sentCode), + 'purchasesList': [ + { 'orderId': 'orderID1', 'sku': skuDetails.sku, 'isAutoRenewing': false, - 'packageName': "package", + 'packageName': 'package', 'purchaseTime': 1231231231, - 'purchaseToken': "token", + 'purchaseToken': 'token', 'signature': 'sign', 'originalJson': 'json', 'developerPayload': 'dummy payload', @@ -304,10 +314,11 @@ void main() { }); iapAndroidPlatform.billingClient.callHandler(call); }); - Completer completer = Completer(); + final Completer completer = Completer(); PurchaseDetails purchaseDetails; - Stream purchaseStream = iapAndroidPlatform.purchaseStream; - late StreamSubscription subscription; + final Stream> purchaseStream = + iapAndroidPlatform.purchaseStream; + late StreamSubscription> subscription; subscription = purchaseStream.listen((_) { purchaseDetails = _.first; completer.complete(purchaseDetails); @@ -319,7 +330,7 @@ void main() { final bool launchResult = await iapAndroidPlatform.buyNonConsumable( purchaseParam: purchaseParam); - PurchaseDetails result = await completer.future; + final PurchaseDetails result = await completer.future; expect(launchResult, isTrue); expect(result.purchaseID, 'orderID1'); expect(result.status, PurchaseStatus.purchased); @@ -327,29 +338,31 @@ void main() { }); test('handles an error with an empty purchases list', () async { - final SkuDetailsWrapper skuDetails = dummySkuDetails; - final String accountId = "hashedAccountId"; + const SkuDetailsWrapper skuDetails = dummySkuDetails; + const String accountId = 'hashedAccountId'; const String debugMessage = 'dummy message'; - final BillingResponse sentCode = BillingResponse.error; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResponse sentCode = BillingResponse.error; + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: sentCode, debugMessage: debugMessage); stubPlatform.addResponse( name: launchMethodName, value: buildBillingResultMap(expectedBillingResult), - additionalStepBeforeReturn: (_) { + additionalStepBeforeReturn: (dynamic _) { // Mock java update purchase callback. - MethodCall call = MethodCall(kOnPurchasesUpdated, { + final MethodCall call = + MethodCall(kOnPurchasesUpdated, { 'billingResult': buildBillingResultMap(expectedBillingResult), - 'responseCode': BillingResponseConverter().toJson(sentCode), - 'purchasesList': [] + 'responseCode': const BillingResponseConverter().toJson(sentCode), + 'purchasesList': const [] }); iapAndroidPlatform.billingClient.callHandler(call); }); - Completer completer = Completer(); + final Completer completer = Completer(); PurchaseDetails purchaseDetails; - Stream purchaseStream = iapAndroidPlatform.purchaseStream; - late StreamSubscription subscription; + final Stream> purchaseStream = + iapAndroidPlatform.purchaseStream; + late StreamSubscription> subscription; subscription = purchaseStream.listen((_) { purchaseDetails = _.first; completer.complete(purchaseDetails); @@ -359,7 +372,7 @@ void main() { productDetails: GooglePlayProductDetails.fromSkuDetails(skuDetails), applicationUserName: accountId); await iapAndroidPlatform.buyNonConsumable(purchaseParam: purchaseParam); - PurchaseDetails result = await completer.future; + final PurchaseDetails result = await completer.future; expect(result.error, isNotNull); expect(result.error!.source, kIAPSource); @@ -369,29 +382,30 @@ void main() { test('buy consumable with auto consume, serializes and deserializes data', () async { - final SkuDetailsWrapper skuDetails = dummySkuDetails; - final String accountId = "hashedAccountId"; + const SkuDetailsWrapper skuDetails = dummySkuDetails; + const String accountId = 'hashedAccountId'; const String debugMessage = 'dummy message'; - final BillingResponse sentCode = BillingResponse.ok; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResponse sentCode = BillingResponse.ok; + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: sentCode, debugMessage: debugMessage); stubPlatform.addResponse( name: launchMethodName, value: buildBillingResultMap(expectedBillingResult), - additionalStepBeforeReturn: (_) { + additionalStepBeforeReturn: (dynamic _) { // Mock java update purchase callback. - MethodCall call = MethodCall(kOnPurchasesUpdated, { + final MethodCall call = + MethodCall(kOnPurchasesUpdated, { 'billingResult': buildBillingResultMap(expectedBillingResult), - 'responseCode': BillingResponseConverter().toJson(sentCode), - 'purchasesList': [ - { + 'responseCode': const BillingResponseConverter().toJson(sentCode), + 'purchasesList': [ + { 'orderId': 'orderID1', 'sku': skuDetails.sku, 'isAutoRenewing': false, - 'packageName': "package", + 'packageName': 'package', 'purchaseTime': 1231231231, - 'purchaseToken': "token", + 'purchaseToken': 'token', 'signature': 'sign', 'originalJson': 'json', 'developerPayload': 'dummy payload', @@ -402,24 +416,25 @@ void main() { }); iapAndroidPlatform.billingClient.callHandler(call); }); - Completer consumeCompleter = Completer(); + final Completer consumeCompleter = Completer(); // adding call back for consume purchase - final BillingResponse expectedCode = BillingResponse.ok; - final BillingResultWrapper expectedBillingResultForConsume = + const BillingResponse expectedCode = BillingResponse.ok; + const BillingResultWrapper expectedBillingResultForConsume = BillingResultWrapper( responseCode: expectedCode, debugMessage: debugMessage); stubPlatform.addResponse( name: consumeMethodName, value: buildBillingResultMap(expectedBillingResultForConsume), additionalStepBeforeReturn: (dynamic args) { - String purchaseToken = args['purchaseToken']; - consumeCompleter.complete((purchaseToken)); + final String purchaseToken = args['purchaseToken'] as String; + consumeCompleter.complete(purchaseToken); }); - Completer completer = Completer(); + final Completer completer = Completer(); PurchaseDetails purchaseDetails; - Stream purchaseStream = iapAndroidPlatform.purchaseStream; - late StreamSubscription subscription; + final Stream> purchaseStream = + iapAndroidPlatform.purchaseStream; + late StreamSubscription> subscription; subscription = purchaseStream.listen((_) { purchaseDetails = _.first; completer.complete(purchaseDetails); @@ -432,7 +447,8 @@ void main() { await iapAndroidPlatform.buyConsumable(purchaseParam: purchaseParam); // Verify that the result has succeeded - GooglePlayPurchaseDetails result = await completer.future; + final GooglePlayPurchaseDetails result = + await completer.future as GooglePlayPurchaseDetails; expect(launchResult, isTrue); expect(result.billingClientPurchase, isNotNull); expect(result.billingClientPurchase.purchaseToken, @@ -444,8 +460,8 @@ void main() { test('buyNonConsumable propagates failures to launch the billing flow', () async { const String debugMessage = 'dummy message'; - final BillingResponse sentCode = BillingResponse.error; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResponse sentCode = BillingResponse.error; + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: sentCode, debugMessage: debugMessage); stubPlatform.addResponse( name: launchMethodName, @@ -463,8 +479,8 @@ void main() { test('buyConsumable propagates failures to launch the billing flow', () async { const String debugMessage = 'dummy message'; - final BillingResponse sentCode = BillingResponse.developerError; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResponse sentCode = BillingResponse.developerError; + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: sentCode, debugMessage: debugMessage); stubPlatform.addResponse( name: launchMethodName, @@ -481,28 +497,29 @@ void main() { }); test('adds consumption failures to PurchaseDetails objects', () async { - final SkuDetailsWrapper skuDetails = dummySkuDetails; - final String accountId = "hashedAccountId"; + const SkuDetailsWrapper skuDetails = dummySkuDetails; + const String accountId = 'hashedAccountId'; const String debugMessage = 'dummy message'; - final BillingResponse sentCode = BillingResponse.ok; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResponse sentCode = BillingResponse.ok; + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: sentCode, debugMessage: debugMessage); stubPlatform.addResponse( name: launchMethodName, value: buildBillingResultMap(expectedBillingResult), - additionalStepBeforeReturn: (_) { + additionalStepBeforeReturn: (dynamic _) { // Mock java update purchase callback. - MethodCall call = MethodCall(kOnPurchasesUpdated, { + final MethodCall call = + MethodCall(kOnPurchasesUpdated, { 'billingResult': buildBillingResultMap(expectedBillingResult), - 'responseCode': BillingResponseConverter().toJson(sentCode), - 'purchasesList': [ - { + 'responseCode': const BillingResponseConverter().toJson(sentCode), + 'purchasesList': [ + { 'orderId': 'orderID1', 'sku': skuDetails.sku, 'isAutoRenewing': false, - 'packageName': "package", + 'packageName': 'package', 'purchaseTime': 1231231231, - 'purchaseToken': "token", + 'purchaseToken': 'token', 'signature': 'sign', 'originalJson': 'json', 'developerPayload': 'dummy payload', @@ -513,24 +530,25 @@ void main() { }); iapAndroidPlatform.billingClient.callHandler(call); }); - Completer consumeCompleter = Completer(); + final Completer consumeCompleter = Completer(); // adding call back for consume purchase - final BillingResponse expectedCode = BillingResponse.error; - final BillingResultWrapper expectedBillingResultForConsume = + const BillingResponse expectedCode = BillingResponse.error; + const BillingResultWrapper expectedBillingResultForConsume = BillingResultWrapper( responseCode: expectedCode, debugMessage: debugMessage); stubPlatform.addResponse( name: consumeMethodName, value: buildBillingResultMap(expectedBillingResultForConsume), additionalStepBeforeReturn: (dynamic args) { - String purchaseToken = args['purchaseToken']; + final String purchaseToken = args['purchaseToken'] as String; consumeCompleter.complete(purchaseToken); }); - Completer completer = Completer(); + final Completer completer = Completer(); PurchaseDetails purchaseDetails; - Stream purchaseStream = iapAndroidPlatform.purchaseStream; - late StreamSubscription subscription; + final Stream> purchaseStream = + iapAndroidPlatform.purchaseStream; + late StreamSubscription> subscription; subscription = purchaseStream.listen((_) { purchaseDetails = _.first; completer.complete(purchaseDetails); @@ -542,7 +560,8 @@ void main() { await iapAndroidPlatform.buyConsumable(purchaseParam: purchaseParam); // Verify that the result has an error for the failed consumption - GooglePlayPurchaseDetails result = await completer.future; + final GooglePlayPurchaseDetails result = + await completer.future as GooglePlayPurchaseDetails; expect(result.billingClientPurchase, isNotNull); expect(result.billingClientPurchase.purchaseToken, await consumeCompleter.future); @@ -554,29 +573,30 @@ void main() { test( 'buy consumable without auto consume, consume api should not receive calls', () async { - final SkuDetailsWrapper skuDetails = dummySkuDetails; - final String accountId = "hashedAccountId"; + const SkuDetailsWrapper skuDetails = dummySkuDetails; + const String accountId = 'hashedAccountId'; const String debugMessage = 'dummy message'; - final BillingResponse sentCode = BillingResponse.developerError; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResponse sentCode = BillingResponse.developerError; + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: sentCode, debugMessage: debugMessage); stubPlatform.addResponse( name: launchMethodName, value: buildBillingResultMap(expectedBillingResult), - additionalStepBeforeReturn: (_) { + additionalStepBeforeReturn: (dynamic _) { // Mock java update purchase callback. - MethodCall call = MethodCall(kOnPurchasesUpdated, { + final MethodCall call = + MethodCall(kOnPurchasesUpdated, { 'billingResult': buildBillingResultMap(expectedBillingResult), - 'responseCode': BillingResponseConverter().toJson(sentCode), - 'purchasesList': [ - { + 'responseCode': const BillingResponseConverter().toJson(sentCode), + 'purchasesList': [ + { 'orderId': 'orderID1', 'sku': skuDetails.sku, 'isAutoRenewing': false, - 'packageName': "package", + 'packageName': 'package', 'purchaseTime': 1231231231, - 'purchaseToken': "token", + 'purchaseToken': 'token', 'signature': 'sign', 'originalJson': 'json', 'developerPayload': 'dummy payload', @@ -587,22 +607,23 @@ void main() { }); iapAndroidPlatform.billingClient.callHandler(call); }); - Completer consumeCompleter = Completer(); + final Completer consumeCompleter = Completer(); // adding call back for consume purchase - final BillingResponse expectedCode = BillingResponse.ok; - final BillingResultWrapper expectedBillingResultForConsume = + const BillingResponse expectedCode = BillingResponse.ok; + const BillingResultWrapper expectedBillingResultForConsume = BillingResultWrapper( responseCode: expectedCode, debugMessage: debugMessage); stubPlatform.addResponse( name: consumeMethodName, value: buildBillingResultMap(expectedBillingResultForConsume), additionalStepBeforeReturn: (dynamic args) { - String purchaseToken = args['purchaseToken']; - consumeCompleter.complete((purchaseToken)); + final String purchaseToken = args['purchaseToken'] as String; + consumeCompleter.complete(purchaseToken); }); - Stream purchaseStream = iapAndroidPlatform.purchaseStream; - late StreamSubscription subscription; + final Stream> purchaseStream = + iapAndroidPlatform.purchaseStream; + late StreamSubscription> subscription; subscription = purchaseStream.listen((_) { consumeCompleter.complete(null); subscription.cancel(); @@ -618,28 +639,29 @@ void main() { test( 'should get canceled purchase status when response code is BillingResponse.userCanceled', () async { - final SkuDetailsWrapper skuDetails = dummySkuDetails; - final String accountId = "hashedAccountId"; + const SkuDetailsWrapper skuDetails = dummySkuDetails; + const String accountId = 'hashedAccountId'; const String debugMessage = 'dummy message'; - final BillingResponse sentCode = BillingResponse.userCanceled; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResponse sentCode = BillingResponse.userCanceled; + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: sentCode, debugMessage: debugMessage); stubPlatform.addResponse( name: launchMethodName, value: buildBillingResultMap(expectedBillingResult), - additionalStepBeforeReturn: (_) { + additionalStepBeforeReturn: (dynamic _) { // Mock java update purchase callback. - MethodCall call = MethodCall(kOnPurchasesUpdated, { + final MethodCall call = + MethodCall(kOnPurchasesUpdated, { 'billingResult': buildBillingResultMap(expectedBillingResult), - 'responseCode': BillingResponseConverter().toJson(sentCode), - 'purchasesList': [ - { + 'responseCode': const BillingResponseConverter().toJson(sentCode), + 'purchasesList': [ + { 'orderId': 'orderID1', 'sku': skuDetails.sku, 'isAutoRenewing': false, - 'packageName': "package", + 'packageName': 'package', 'purchaseTime': 1231231231, - 'purchaseToken': "token", + 'purchaseToken': 'token', 'signature': 'sign', 'originalJson': 'json', 'developerPayload': 'dummy payload', @@ -650,24 +672,25 @@ void main() { }); iapAndroidPlatform.billingClient.callHandler(call); }); - Completer consumeCompleter = Completer(); + final Completer consumeCompleter = Completer(); // adding call back for consume purchase - final BillingResponse expectedCode = BillingResponse.userCanceled; - final BillingResultWrapper expectedBillingResultForConsume = + const BillingResponse expectedCode = BillingResponse.userCanceled; + const BillingResultWrapper expectedBillingResultForConsume = BillingResultWrapper( responseCode: expectedCode, debugMessage: debugMessage); stubPlatform.addResponse( name: consumeMethodName, value: buildBillingResultMap(expectedBillingResultForConsume), additionalStepBeforeReturn: (dynamic args) { - String purchaseToken = args['purchaseToken']; + final String purchaseToken = args['purchaseToken'] as String; consumeCompleter.complete(purchaseToken); }); - Completer completer = Completer(); + final Completer completer = Completer(); PurchaseDetails purchaseDetails; - Stream purchaseStream = iapAndroidPlatform.purchaseStream; - late StreamSubscription subscription; + final Stream> purchaseStream = + iapAndroidPlatform.purchaseStream; + late StreamSubscription> subscription; subscription = purchaseStream.listen((_) { purchaseDetails = _.first; completer.complete(purchaseDetails); @@ -679,36 +702,39 @@ void main() { await iapAndroidPlatform.buyConsumable(purchaseParam: purchaseParam); // Verify that the result has an error for the failed consumption - GooglePlayPurchaseDetails result = await completer.future; + final GooglePlayPurchaseDetails result = + await completer.future as GooglePlayPurchaseDetails; expect(result.status, PurchaseStatus.canceled); }); test( 'should get purchased purchase status when upgrading subscription by deferred proration mode', () async { - final SkuDetailsWrapper skuDetails = dummySkuDetails; - final String accountId = "hashedAccountId"; + const SkuDetailsWrapper skuDetails = dummySkuDetails; + const String accountId = 'hashedAccountId'; const String debugMessage = 'dummy message'; - final BillingResponse sentCode = BillingResponse.ok; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResponse sentCode = BillingResponse.ok; + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: sentCode, debugMessage: debugMessage); stubPlatform.addResponse( name: launchMethodName, value: buildBillingResultMap(expectedBillingResult), - additionalStepBeforeReturn: (_) { + additionalStepBeforeReturn: (dynamic _) { // Mock java update purchase callback. - MethodCall call = MethodCall(kOnPurchasesUpdated, { + final MethodCall call = + MethodCall(kOnPurchasesUpdated, { 'billingResult': buildBillingResultMap(expectedBillingResult), - 'responseCode': BillingResponseConverter().toJson(sentCode), - 'purchasesList': [] + 'responseCode': const BillingResponseConverter().toJson(sentCode), + 'purchasesList': const [] }); iapAndroidPlatform.billingClient.callHandler(call); }); - Completer completer = Completer(); + final Completer completer = Completer(); PurchaseDetails purchaseDetails; - Stream purchaseStream = iapAndroidPlatform.purchaseStream; - late StreamSubscription subscription; + final Stream> purchaseStream = + iapAndroidPlatform.purchaseStream; + late StreamSubscription> subscription; subscription = purchaseStream.listen((_) { purchaseDetails = _.first; completer.complete(purchaseDetails); @@ -724,7 +750,7 @@ void main() { )); await iapAndroidPlatform.buyNonConsumable(purchaseParam: purchaseParam); - PurchaseDetails result = await completer.future; + final PurchaseDetails result = await completer.future; expect(result.status, PurchaseStatus.purchased); }); }); @@ -733,17 +759,18 @@ void main() { const String completeMethodName = 'BillingClient#(AcknowledgePurchaseParams params, (AcknowledgePurchaseParams, AcknowledgePurchaseResponseListener)'; test('complete purchase success', () async { - final BillingResponse expectedCode = BillingResponse.ok; + const BillingResponse expectedCode = BillingResponse.ok; const String debugMessage = 'dummy message'; - final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( responseCode: expectedCode, debugMessage: debugMessage); stubPlatform.addResponse( name: completeMethodName, value: buildBillingResultMap(expectedBillingResult), ); - PurchaseDetails purchaseDetails = + final PurchaseDetails purchaseDetails = GooglePlayPurchaseDetails.fromPurchase(dummyUnacknowledgedPurchase); - Completer completer = Completer(); + final Completer completer = + Completer(); purchaseDetails.status = PurchaseStatus.purchased; if (purchaseDetails.pendingCompletePurchase) { final BillingResultWrapper billingResultWrapper = diff --git a/packages/in_app_purchase/in_app_purchase_android/test/stub_in_app_purchase_platform.dart b/packages/in_app_purchase/in_app_purchase_android/test/stub_in_app_purchase_platform.dart index 11a3426335d5..75972e644faa 100644 --- a/packages/in_app_purchase/in_app_purchase_android/test/stub_in_app_purchase_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_android/test/stub_in_app_purchase_platform.dart @@ -5,11 +5,12 @@ import 'dart:async'; import 'package:flutter/services.dart'; -typedef void AdditionalSteps(dynamic args); +typedef AdditionalSteps = void Function(dynamic args); class StubInAppPurchasePlatform { - Map _expectedCalls = {}; - Map _additionalSteps = {}; + final Map _expectedCalls = {}; + final Map _additionalSteps = + {}; void addResponse( {required String name, dynamic value, @@ -18,7 +19,7 @@ class StubInAppPurchasePlatform { _expectedCalls[name] = value; } - List _previousCalls = []; + final List _previousCalls = []; List get previousCalls => _previousCalls; MethodCall previousCallMatching(String name) => _previousCalls.firstWhere((MethodCall call) => call.method == name); diff --git a/script/configs/custom_analysis.yaml b/script/configs/custom_analysis.yaml index 2c87f482bbd0..89d061f7a07b 100644 --- a/script/configs/custom_analysis.yaml +++ b/script/configs/custom_analysis.yaml @@ -15,4 +15,3 @@ - google_maps_flutter/google_maps_flutter_platform_interface - google_maps_flutter/google_maps_flutter_web - in_app_purchase/in_app_purchase -- in_app_purchase/in_app_purchase_android From ac8690bd2e27e5b785a4a72bd60c5778bea162e3 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 28 Feb 2022 18:01:23 -0500 Subject: [PATCH 237/600] Roll Flutter from 47b5ed3948e8 to 7959c3953dbe (52 revisions) (#4964) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 438032a7e219..7ab1cd3536c4 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -47b5ed3948e8ca3e79a120e220ad548d22f479bf +7959c3953dbed4db269a07ee9a239e632dc3f769 From 4eb17696ce2d71a611f029dfa1635f8c152d7e57 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 28 Feb 2022 19:06:22 -0500 Subject: [PATCH 238/600] Roll Flutter from 7959c3953dbe to 377d7d768df1 (2 revisions) (#4966) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 7ab1cd3536c4..b18e9773eda5 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -7959c3953dbed4db269a07ee9a239e632dc3f769 +377d7d768df19a730b8296165919773cc22be1fe From 868086b621ee005f62f963a17c955eb4e32d9196 Mon Sep 17 00:00:00 2001 From: Taha Tesser Date: Tue, 1 Mar 2022 23:11:26 +0200 Subject: [PATCH 239/600] [google_maps_flutter] Switch app-facing package to new analysis options (#4716) --- .../google_maps_flutter/AUTHORS | 1 + .../google_maps_flutter/CHANGELOG.md | 3 +- .../google_maps_flutter/analysis_options.yaml | 1 - .../google_map_inspector.dart | 4 +- .../integration_test/google_maps_test.dart | 102 +++++----- .../example/lib/animate_camera.dart | 2 +- .../example/lib/lite_mode.dart | 6 +- .../google_maps_flutter/example/lib/main.dart | 30 +-- .../example/lib/map_click.dart | 4 +- .../example/lib/map_coordinates.dart | 4 +- .../example/lib/map_ui.dart | 6 +- .../example/lib/marker_icons.dart | 10 +- .../example/lib/move_camera.dart | 2 +- .../example/lib/padding.dart | 28 +-- .../example/lib/place_circle.dart | 3 +- .../example/lib/place_marker.dart | 34 ++-- .../example/lib/place_polygon.dart | 3 +- .../example/lib/place_polyline.dart | 3 +- .../example/lib/scrolling_map.dart | 20 +- .../example/lib/snapshot.dart | 9 +- .../example/lib/tile_overlay.dart | 10 +- .../google_maps_flutter/example/pubspec.yaml | 6 +- .../lib/google_maps_flutter.dart | 1 - .../lib/src/controller.dart | 10 +- .../lib/src/google_map.dart | 27 ++- .../google_maps_flutter/pubspec.yaml | 2 +- .../test/circle_updates_test.dart | 61 +++--- .../test/fake_maps_controllers.dart | 181 ++++++++++-------- .../test/map_creation_test.dart | 15 +- .../test/marker_updates_test.dart | 66 +++---- .../test/polygon_updates_test.dart | 132 ++++++------- .../test/polyline_updates_test.dart | 72 +++---- .../test/tile_overlay_updates_test.dart | 86 ++++----- script/configs/custom_analysis.yaml | 1 - 34 files changed, 489 insertions(+), 456 deletions(-) delete mode 100644 packages/google_maps_flutter/google_maps_flutter/analysis_options.yaml diff --git a/packages/google_maps_flutter/google_maps_flutter/AUTHORS b/packages/google_maps_flutter/google_maps_flutter/AUTHORS index 493a0b4ef9c2..9f1b53ee2667 100644 --- a/packages/google_maps_flutter/google_maps_flutter/AUTHORS +++ b/packages/google_maps_flutter/google_maps_flutter/AUTHORS @@ -64,3 +64,4 @@ Aleksandr Yurkovskiy Anton Borries Alex Li Rahul Raj <64.rahulraj@gmail.com> +Taha Tesser diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index b9178a74bb70..565bf8412c61 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,7 +1,8 @@ -## NEXT +## 2.1.2 * Removes dependencies from `pubspec.yaml` that are only needed in `example/pubspec.yaml` * Updates Android compileSdkVersion to 31. +* Internal code cleanup for stricter analysis options. ## 2.1.1 diff --git a/packages/google_maps_flutter/google_maps_flutter/analysis_options.yaml b/packages/google_maps_flutter/google_maps_flutter/analysis_options.yaml deleted file mode 100644 index 5aeb4e7c5e21..000000000000 --- a/packages/google_maps_flutter/google_maps_flutter/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../../analysis_options_legacy.yaml diff --git a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_map_inspector.dart b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_map_inspector.dart index a4833fe8561d..34baa902ab1b 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_map_inspector.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_map_inspector.dart @@ -78,9 +78,9 @@ class GoogleMapInspector { } Future?> getTileOverlayInfo(String id) async { - return (await _channel.invokeMapMethod( + return await _channel.invokeMapMethod( 'map#getTileOverlayInfo', { 'tileOverlayId': id, - })); + }); } } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart index 8bafca15c344..a007dddd9188 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart @@ -7,11 +7,11 @@ import 'dart:io'; import 'dart:typed_data'; import 'dart:ui' as ui; -import 'package:integration_test/integration_test.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:integration_test/integration_test.dart'; import 'google_map_inspector.dart'; @@ -53,7 +53,7 @@ void main() { initialCameraPosition: _kInitialCameraPosition, compassEnabled: true, onMapCreated: (GoogleMapController controller) { - fail("OnMapCreated should get called only once."); + fail('OnMapCreated should get called only once.'); }, ), )); @@ -93,7 +93,7 @@ void main() { initialCameraPosition: _kInitialCameraPosition, mapToolbarEnabled: true, onMapCreated: (GoogleMapController controller) { - fail("OnMapCreated should get called only once."); + fail('OnMapCreated should get called only once.'); }, ), )); @@ -140,7 +140,8 @@ void main() { final GoogleMapInspector inspector = await inspectorCompleter.future; if (Platform.isIOS) { - MinMaxZoomPreference zoomLevel = await inspector.getMinMaxZoomLevels(); + final MinMaxZoomPreference zoomLevel = + await inspector.getMinMaxZoomLevels(); expect(zoomLevel, equals(initialZoomLevel)); } else if (Platform.isAndroid) { await controller.moveCamera(CameraUpdate.zoomTo(15)); @@ -161,13 +162,14 @@ void main() { initialCameraPosition: _kInitialCameraPosition, minMaxZoomPreference: finalZoomLevel, onMapCreated: (GoogleMapController controller) { - fail("OnMapCreated should get called only once."); + fail('OnMapCreated should get called only once.'); }, ), )); if (Platform.isIOS) { - MinMaxZoomPreference zoomLevel = await inspector.getMinMaxZoomLevels(); + final MinMaxZoomPreference zoomLevel = + await inspector.getMinMaxZoomLevels(); expect(zoomLevel, equals(finalZoomLevel)); } else { await controller.moveCamera(CameraUpdate.zoomTo(15)); @@ -213,7 +215,7 @@ void main() { initialCameraPosition: _kInitialCameraPosition, zoomGesturesEnabled: true, onMapCreated: (GoogleMapController controller) { - fail("OnMapCreated should get called only once."); + fail('OnMapCreated should get called only once.'); }, ), )); @@ -243,7 +245,7 @@ void main() { final GoogleMapInspector inspector = await inspectorCompleter.future; bool? zoomControlsEnabled = await inspector.isZoomControlsEnabled(); - expect(zoomControlsEnabled, Platform.isIOS ? false : true); + expect(zoomControlsEnabled, !Platform.isIOS); /// Zoom Controls functionality is not available on iOS at the moment. if (Platform.isAndroid) { @@ -254,7 +256,7 @@ void main() { initialCameraPosition: _kInitialCameraPosition, zoomControlsEnabled: false, onMapCreated: (GoogleMapController controller) { - fail("OnMapCreated should get called only once."); + fail('OnMapCreated should get called only once.'); }, ), )); @@ -295,7 +297,7 @@ void main() { initialCameraPosition: _kInitialCameraPosition, liteModeEnabled: true, onMapCreated: (GoogleMapController controller) { - fail("OnMapCreated should get called only once."); + fail('OnMapCreated should get called only once.'); }, ), )); @@ -335,7 +337,7 @@ void main() { initialCameraPosition: _kInitialCameraPosition, rotateGesturesEnabled: true, onMapCreated: (GoogleMapController controller) { - fail("OnMapCreated should get called only once."); + fail('OnMapCreated should get called only once.'); }, ), )); @@ -375,7 +377,7 @@ void main() { initialCameraPosition: _kInitialCameraPosition, tiltGesturesEnabled: true, onMapCreated: (GoogleMapController controller) { - fail("OnMapCreated should get called only once."); + fail('OnMapCreated should get called only once.'); }, ), )); @@ -415,7 +417,7 @@ void main() { initialCameraPosition: _kInitialCameraPosition, scrollGesturesEnabled: true, onMapCreated: (GoogleMapController controller) { - fail("OnMapCreated should get called only once."); + fail('OnMapCreated should get called only once.'); }, ), )); @@ -449,11 +451,11 @@ void main() { // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and `mapControllerCompleter.complete(controller)` above should happen // in `mapRendered`. // https://github.com/flutter/flutter/issues/54758 - await Future.delayed(Duration(seconds: 1)); + await Future.delayed(const Duration(seconds: 1)); - ScreenCoordinate coordinate = + final ScreenCoordinate coordinate = await mapController.getScreenCoordinate(_kInitialCameraPosition.target); - Rect rect = tester.getRect(find.byKey(key)); + final Rect rect = tester.getRect(find.byKey(key)); if (Platform.isIOS) { // On iOS, the coordinate value from the GoogleMapSdk doesn't include the devicePixelRatio`. // So we don't need to do the conversion like we did below for other platforms. @@ -525,7 +527,7 @@ void main() { // TODO(iskakaushik): non-zero padding is needed for some device configurations // https://github.com/flutter/flutter/issues/30575 - final double padding = 0; + const double padding = 0; await mapController .moveCamera(CameraUpdate.newLatLngBounds(latLngBounds, padding)); await tester.pumpAndSettle(const Duration(seconds: 3)); @@ -573,7 +575,7 @@ void main() { initialCameraPosition: _kInitialCameraPosition, trafficEnabled: false, onMapCreated: (GoogleMapController controller) { - fail("OnMapCreated should get called only once."); + fail('OnMapCreated should get called only once.'); }, ), )); @@ -641,7 +643,7 @@ void main() { myLocationButtonEnabled: false, myLocationEnabled: false, onMapCreated: (GoogleMapController controller) { - fail("OnMapCreated should get called only once."); + fail('OnMapCreated should get called only once.'); }, ), )); @@ -723,7 +725,7 @@ void main() { )); final GoogleMapController controller = await controllerCompleter.future; - final String mapStyle = + const String mapStyle = '[{"elementType":"geometry","stylers":[{"color":"#242f3e"}]}]'; await controller.setMapStyle(mapStyle); }); @@ -797,7 +799,7 @@ void main() { // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and `mapControllerCompleter.complete(controller)` above should happen // in `mapRendered`. // https://github.com/flutter/flutter/issues/54758 - await Future.delayed(Duration(seconds: 1)); + await Future.delayed(const Duration(seconds: 1)); final LatLngBounds visibleRegion = await controller.getVisibleRegion(); final LatLng topLeft = @@ -832,7 +834,7 @@ void main() { // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and `mapControllerCompleter.complete(controller)` above should happen // in `mapRendered`. // https://github.com/flutter/flutter/issues/54758 - await Future.delayed(Duration(seconds: 1)); + await Future.delayed(const Duration(seconds: 1)); double zoom = await controller.getZoomLevel(); expect(zoom, _kInitialZoomLevel); @@ -864,7 +866,7 @@ void main() { // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and `mapControllerCompleter.complete(controller)` above should happen // in `mapRendered`. // https://github.com/flutter/flutter/issues/54758 - await Future.delayed(Duration(seconds: 1)); + await Future.delayed(const Duration(seconds: 1)); final LatLngBounds visibleRegion = await controller.getVisibleRegion(); final LatLng northWest = LatLng( @@ -902,7 +904,7 @@ void main() { // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and `mapControllerCompleter.complete(controller)` above should happen // in `mapRendered`. // https://github.com/flutter/flutter/issues/54758 - await Future.delayed(Duration(seconds: 1)); + await Future.delayed(const Duration(seconds: 1)); // Simple call to make sure that the app hasn't crashed. final LatLngBounds bounds1 = await controller.getVisibleRegion(); @@ -911,12 +913,12 @@ void main() { }); testWidgets('testToggleInfoWindow', (WidgetTester tester) async { - final Marker marker = Marker( - markerId: MarkerId("marker"), - infoWindow: InfoWindow(title: "InfoWindow")); + const Marker marker = Marker( + markerId: MarkerId('marker'), + infoWindow: InfoWindow(title: 'InfoWindow')); final Set markers = {marker}; - Completer controllerCompleter = + final Completer controllerCompleter = Completer(); await tester.pumpWidget(Directionality( @@ -930,7 +932,7 @@ void main() { ), )); - GoogleMapController controller = await controllerCompleter.future; + final GoogleMapController controller = await controllerCompleter.future; bool iwVisibleStatus = await controller.isMarkerInfoWindowShown(marker.markerId); @@ -945,9 +947,9 @@ void main() { expect(iwVisibleStatus, false); }); - testWidgets("fromAssetImage", (WidgetTester tester) async { - double pixelRatio = 2; - final ImageConfiguration imageConfiguration = + testWidgets('fromAssetImage', (WidgetTester tester) async { + const double pixelRatio = 2; + const ImageConfiguration imageConfiguration = ImageConfiguration(devicePixelRatio: pixelRatio); final BitmapDescriptor mip = await BitmapDescriptor.fromAssetImage( imageConfiguration, 'red_square.png'); @@ -959,7 +961,7 @@ void main() { }); testWidgets('testTakeSnapshot', (WidgetTester tester) async { - Completer inspectorCompleter = + final Completer inspectorCompleter = Completer(); await tester.pumpWidget( @@ -990,10 +992,10 @@ void main() { testWidgets( 'set tileOverlay correctly', (WidgetTester tester) async { - Completer inspectorCompleter = + final Completer inspectorCompleter = Completer(); final TileOverlay tileOverlay1 = TileOverlay( - tileOverlayId: TileOverlayId('tile_overlay_1'), + tileOverlayId: const TileOverlayId('tile_overlay_1'), tileProvider: _DebugTileProvider(), zIndex: 2, visible: true, @@ -1002,7 +1004,7 @@ void main() { ); final TileOverlay tileOverlay2 = TileOverlay( - tileOverlayId: TileOverlayId('tile_overlay_2'), + tileOverlayId: const TileOverlayId('tile_overlay_2'), tileProvider: _DebugTileProvider(), zIndex: 1, visible: false, @@ -1028,9 +1030,9 @@ void main() { final GoogleMapInspector inspector = await inspectorCompleter.future; - Map tileOverlayInfo1 = + final Map tileOverlayInfo1 = (await inspector.getTileOverlayInfo('tile_overlay_1'))!; - Map tileOverlayInfo2 = + final Map tileOverlayInfo2 = (await inspector.getTileOverlayInfo('tile_overlay_2'))!; expect(tileOverlayInfo1['visible'], isTrue); @@ -1050,11 +1052,11 @@ void main() { testWidgets( 'update tileOverlays correctly', (WidgetTester tester) async { - Completer inspectorCompleter = + final Completer inspectorCompleter = Completer(); final Key key = GlobalKey(); final TileOverlay tileOverlay1 = TileOverlay( - tileOverlayId: TileOverlayId('tile_overlay_1'), + tileOverlayId: const TileOverlayId('tile_overlay_1'), tileProvider: _DebugTileProvider(), zIndex: 2, visible: true, @@ -1063,7 +1065,7 @@ void main() { ); final TileOverlay tileOverlay2 = TileOverlay( - tileOverlayId: TileOverlayId('tile_overlay_2'), + tileOverlayId: const TileOverlayId('tile_overlay_2'), tileProvider: _DebugTileProvider(), zIndex: 3, visible: true, @@ -1090,7 +1092,7 @@ void main() { final GoogleMapInspector inspector = await inspectorCompleter.future; final TileOverlay tileOverlay1New = TileOverlay( - tileOverlayId: TileOverlayId('tile_overlay_1'), + tileOverlayId: const TileOverlayId('tile_overlay_1'), tileProvider: _DebugTileProvider(), zIndex: 1, visible: false, @@ -1114,9 +1116,9 @@ void main() { await tester.pumpAndSettle(const Duration(seconds: 3)); - Map tileOverlayInfo1 = + final Map tileOverlayInfo1 = (await inspector.getTileOverlayInfo('tile_overlay_1'))!; - Map? tileOverlayInfo2 = + final Map? tileOverlayInfo2 = await inspector.getTileOverlayInfo('tile_overlay_2'); expect(tileOverlayInfo1['visible'], isFalse); @@ -1132,11 +1134,11 @@ void main() { testWidgets( 'remove tileOverlays correctly', (WidgetTester tester) async { - Completer inspectorCompleter = + final Completer inspectorCompleter = Completer(); final Key key = GlobalKey(); final TileOverlay tileOverlay1 = TileOverlay( - tileOverlayId: TileOverlayId('tile_overlay_1'), + tileOverlayId: const TileOverlayId('tile_overlay_1'), tileProvider: _DebugTileProvider(), zIndex: 2, visible: true, @@ -1177,7 +1179,7 @@ void main() { ); await tester.pumpAndSettle(const Duration(seconds: 3)); - Map? tileOverlayInfo1 = + final Map? tileOverlayInfo1 = await inspector.getTileOverlayInfo('tile_overlay_1'); expect(tileOverlayInfo1, isNull); @@ -1196,7 +1198,7 @@ class _DebugTileProvider implements TileProvider { static const int width = 100; static const int height = 100; static final Paint boxPaint = Paint(); - static final TextStyle textStyle = TextStyle( + static const TextStyle textStyle = TextStyle( color: Colors.red, fontSize: 20, ); @@ -1206,7 +1208,7 @@ class _DebugTileProvider implements TileProvider { final ui.PictureRecorder recorder = ui.PictureRecorder(); final Canvas canvas = Canvas(recorder); final TextSpan textSpan = TextSpan( - text: "$x,$y", + text: '$x,$y', style: textStyle, ); final TextPainter textPainter = TextPainter( @@ -1217,7 +1219,7 @@ class _DebugTileProvider implements TileProvider { minWidth: 0.0, maxWidth: width.toDouble(), ); - final Offset offset = const Offset(0, 0); + const Offset offset = Offset(0, 0); textPainter.paint(canvas, offset); canvas.drawRect( Rect.fromLTRB(0, 0, width.toDouble(), width.toDouble()), boxPaint); diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart index cc5fd257dfd3..f8072eee7c85 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart @@ -10,7 +10,7 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class AnimateCameraPage extends GoogleMapExampleAppPage { - AnimateCameraPage() + const AnimateCameraPage() : super(const Icon(Icons.map), 'Camera control, animated'); @override diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart index f6d6f54e135a..0ecc5ed38e87 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart @@ -13,7 +13,7 @@ const CameraPosition _kInitialPosition = CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); class LiteModePage extends GoogleMapExampleAppPage { - LiteModePage() : super(const Icon(Icons.map), 'Lite mode'); + const LiteModePage() : super(const Icon(Icons.map), 'Lite mode'); @override Widget build(BuildContext context) { @@ -26,9 +26,9 @@ class _LiteModeBody extends StatelessWidget { @override Widget build(BuildContext context) { - return Card( + return const Card( child: Padding( - padding: const EdgeInsets.symmetric(vertical: 30.0), + padding: EdgeInsets.symmetric(vertical: 30.0), child: Center( child: SizedBox( width: 300.0, diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart index f67e4ff8e563..f4d420a72f7c 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart @@ -26,21 +26,21 @@ import 'snapshot.dart'; import 'tile_overlay.dart'; final List _allPages = [ - MapUiPage(), - MapCoordinatesPage(), - MapClickPage(), - AnimateCameraPage(), - MoveCameraPage(), - PlaceMarkerPage(), - MarkerIconsPage(), - ScrollingMapPage(), - PlacePolylinePage(), - PlacePolygonPage(), - PlaceCirclePage(), - PaddingPage(), - SnapshotPage(), - LiteModePage(), - TileOverlayPage(), + const MapUiPage(), + const MapCoordinatesPage(), + const MapClickPage(), + const AnimateCameraPage(), + const MoveCameraPage(), + const PlaceMarkerPage(), + const MarkerIconsPage(), + const ScrollingMapPage(), + const PlacePolylinePage(), + const PlacePolygonPage(), + const PlaceCirclePage(), + const PaddingPage(), + const SnapshotPage(), + const LiteModePage(), + const TileOverlayPage(), ]; class MapsDemo extends StatelessWidget { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart index e44f3d9d6a2e..ef1bfe2b11dc 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart @@ -13,7 +13,7 @@ const CameraPosition _kInitialPosition = CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); class MapClickPage extends GoogleMapExampleAppPage { - MapClickPage() : super(const Icon(Icons.mouse), 'Map click'); + const MapClickPage() : super(const Icon(Icons.mouse), 'Map click'); @override Widget build(BuildContext context) { @@ -96,7 +96,7 @@ class _MapClickBodyState extends State<_MapClickBody> { ); } - void onMapCreated(GoogleMapController controller) async { + Future onMapCreated(GoogleMapController controller) async { setState(() { mapController = controller; }); diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart index 99ab16802fea..dc4376a19547 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart @@ -13,7 +13,7 @@ const CameraPosition _kInitialPosition = CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); class MapCoordinatesPage extends GoogleMapExampleAppPage { - MapCoordinatesPage() : super(const Icon(Icons.map), 'Map coordinates'); + const MapCoordinatesPage() : super(const Icon(Icons.map), 'Map coordinates'); @override Widget build(BuildContext context) { @@ -72,7 +72,7 @@ class _MapCoordinatesBodyState extends State<_MapCoordinatesBody> { ); } - void onMapCreated(GoogleMapController controller) async { + Future onMapCreated(GoogleMapController controller) async { final LatLngBounds visibleRegion = await controller.getVisibleRegion(); setState(() { mapController = controller; diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_ui.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_ui.dart index 2e0d2d188a3f..48ef1f570e02 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_ui.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_ui.dart @@ -16,7 +16,7 @@ final LatLngBounds sydneyBounds = LatLngBounds( ); class MapUiPage extends GoogleMapExampleAppPage { - MapUiPage() : super(const Icon(Icons.map), 'User interface'); + const MapUiPage() : super(const Icon(Icons.map), 'User interface'); @override Widget build(BuildContext context) { @@ -34,14 +34,14 @@ class MapUiBody extends StatefulWidget { class MapUiBodyState extends State { MapUiBodyState(); - static final CameraPosition _kInitialPosition = const CameraPosition( + static const CameraPosition _kInitialPosition = CameraPosition( target: LatLng(-33.852, 151.211), zoom: 11.0, ); CameraPosition _position = _kInitialPosition; bool _isMapCreated = false; - bool _isMoving = false; + final bool _isMoving = false; bool _compassEnabled = true; bool _mapToolbarEnabled = true; CameraTargetBounds _cameraTargetBounds = CameraTargetBounds.unbounded; diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart index da57b83a7e4f..95ace9d7c482 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart @@ -11,7 +11,7 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class MarkerIconsPage extends GoogleMapExampleAppPage { - MarkerIconsPage() : super(const Icon(Icons.image), 'Marker icons'); + const MarkerIconsPage() : super(const Icon(Icons.image), 'Marker icons'); @override Widget build(BuildContext context) { @@ -60,13 +60,13 @@ class MarkerIconsBodyState extends State { Marker _createMarker() { if (_markerIcon != null) { return Marker( - markerId: MarkerId("marker_1"), + markerId: const MarkerId('marker_1'), position: _kMapCenter, icon: _markerIcon!, ); } else { - return Marker( - markerId: MarkerId("marker_1"), + return const Marker( + markerId: MarkerId('marker_1'), position: _kMapCenter, ); } @@ -75,7 +75,7 @@ class MarkerIconsBodyState extends State { Future _createMarkerImageFromAsset(BuildContext context) async { if (_markerIcon == null) { final ImageConfiguration imageConfiguration = - createLocalImageConfiguration(context, size: Size.square(48)); + createLocalImageConfiguration(context, size: const Size.square(48)); BitmapDescriptor.fromAssetImage( imageConfiguration, 'assets/red_square.png') .then(_updateBitmap); diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart index f8274196770d..33da90f32c1b 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart @@ -10,7 +10,7 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class MoveCameraPage extends GoogleMapExampleAppPage { - MoveCameraPage() : super(const Icon(Icons.map), 'Camera control'); + const MoveCameraPage() : super(const Icon(Icons.map), 'Camera control'); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart index d90005fa6998..77091909e970 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart @@ -9,7 +9,7 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PaddingPage extends GoogleMapExampleAppPage { - PaddingPage() : super(const Icon(Icons.map), 'Add padding to the map'); + const PaddingPage() : super(const Icon(Icons.map), 'Add padding to the map'); @override Widget build(BuildContext context) { @@ -53,11 +53,11 @@ class MarkerIconsBodyState extends State { ), ), ), - Padding( - padding: const EdgeInsets.only(top: 20), + const Padding( + padding: EdgeInsets.only(top: 20), child: Center( child: Text( - "Enter Padding Below", + 'Enter Padding Below', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), ), @@ -79,10 +79,10 @@ class MarkerIconsBodyState extends State { }); } - TextEditingController _topController = TextEditingController(); - TextEditingController _bottomController = TextEditingController(); - TextEditingController _leftController = TextEditingController(); - TextEditingController _rightController = TextEditingController(); + final TextEditingController _topController = TextEditingController(); + final TextEditingController _bottomController = TextEditingController(); + final TextEditingController _leftController = TextEditingController(); + final TextEditingController _rightController = TextEditingController(); Widget _paddingInput() { return Padding( @@ -96,7 +96,7 @@ class MarkerIconsBodyState extends State { keyboardType: TextInputType.number, textAlign: TextAlign.center, decoration: const InputDecoration( - hintText: "Top", + hintText: 'Top', ), ), ), @@ -108,7 +108,7 @@ class MarkerIconsBodyState extends State { keyboardType: TextInputType.number, textAlign: TextAlign.center, decoration: const InputDecoration( - hintText: "Bottom", + hintText: 'Bottom', ), ), ), @@ -120,7 +120,7 @@ class MarkerIconsBodyState extends State { keyboardType: TextInputType.number, textAlign: TextAlign.center, decoration: const InputDecoration( - hintText: "Left", + hintText: 'Left', ), ), ), @@ -132,7 +132,7 @@ class MarkerIconsBodyState extends State { keyboardType: TextInputType.number, textAlign: TextAlign.center, decoration: const InputDecoration( - hintText: "Right", + hintText: 'Right', ), ), ), @@ -148,7 +148,7 @@ class MarkerIconsBodyState extends State { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ TextButton( - child: const Text("Set Padding"), + child: const Text('Set Padding'), onPressed: () { setState(() { _padding = EdgeInsets.fromLTRB( @@ -160,7 +160,7 @@ class MarkerIconsBodyState extends State { }, ), TextButton( - child: const Text("Reset Padding"), + child: const Text('Reset Padding'), onPressed: () { setState(() { _topController.clear(); diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart index a4953428f088..c6f1509af69f 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart @@ -10,7 +10,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PlaceCirclePage extends GoogleMapExampleAppPage { - PlaceCirclePage() : super(const Icon(Icons.linear_scale), 'Place circle'); + const PlaceCirclePage() + : super(const Icon(Icons.linear_scale), 'Place circle'); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart index 4b9496f69a63..4291cac6841e 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart @@ -6,8 +6,8 @@ import 'dart:async'; import 'dart:math'; -import 'dart:ui'; import 'dart:typed_data'; +import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; @@ -15,7 +15,7 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PlaceMarkerPage extends GoogleMapExampleAppPage { - PlaceMarkerPage() : super(const Icon(Icons.place), 'Place marker'); + const PlaceMarkerPage() : super(const Icon(Icons.place), 'Place marker'); @override Widget build(BuildContext context) { @@ -34,7 +34,7 @@ typedef MarkerUpdateAction = Marker Function(Marker marker); class PlaceMarkerBodyState extends State { PlaceMarkerBodyState(); - static final LatLng center = const LatLng(-33.86711, 151.1947171); + static const LatLng center = LatLng(-33.86711, 151.1947171); GoogleMapController? controller; Map markers = {}; @@ -74,17 +74,17 @@ class PlaceMarkerBodyState extends State { } } - void _onMarkerDrag(MarkerId markerId, LatLng newPosition) async { + Future _onMarkerDrag(MarkerId markerId, LatLng newPosition) async { setState(() { - this.markerPosition = newPosition; + markerPosition = newPosition; }); } - void _onMarkerDragEnd(MarkerId markerId, LatLng newPosition) async { + Future _onMarkerDragEnd(MarkerId markerId, LatLng newPosition) async { final Marker? tappedMarker = markers[markerId]; if (tappedMarker != null) { setState(() { - this.markerPosition = null; + markerPosition = null; }); await showDialog( context: context, @@ -289,7 +289,7 @@ class PlaceMarkerBodyState extends State { @override Widget build(BuildContext context) { final MarkerId? selectedId = selectedMarker; - return Stack(children: [ + return Stack(children: [ Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.stretch, @@ -397,17 +397,19 @@ class PlaceMarkerBodyState extends State { child: Container( color: Colors.white70, height: 30, - padding: EdgeInsets.only(left: 12, right: 12), + padding: const EdgeInsets.only(left: 12, right: 12), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisSize: MainAxisSize.max, - children: [ - markerPosition == null - ? Container() - : Expanded(child: Text("lat: ${markerPosition!.latitude}")), - markerPosition == null - ? Container() - : Expanded(child: Text("lng: ${markerPosition!.longitude}")), + children: [ + if (markerPosition == null) + Container() + else + Expanded(child: Text('lat: ${markerPosition!.latitude}')), + if (markerPosition == null) + Container() + else + Expanded(child: Text('lng: ${markerPosition!.longitude}')), ], ), ), diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart index 476084defa75..8d4fa3e07c36 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart @@ -10,7 +10,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PlacePolygonPage extends GoogleMapExampleAppPage { - PlacePolygonPage() : super(const Icon(Icons.linear_scale), 'Place polygon'); + const PlacePolygonPage() + : super(const Icon(Icons.linear_scale), 'Place polygon'); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart index aeb9bf1b11eb..434920d293be 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart @@ -11,7 +11,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PlacePolylinePage extends GoogleMapExampleAppPage { - PlacePolylinePage() : super(const Icon(Icons.linear_scale), 'Place polyline'); + const PlacePolylinePage() + : super(const Icon(Icons.linear_scale), 'Place polyline'); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart index 9611d36bc8e8..8d046fc6b387 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart @@ -12,8 +12,10 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; +const LatLng _center = LatLng(32.080664, 34.9563837); + class ScrollingMapPage extends GoogleMapExampleAppPage { - ScrollingMapPage() : super(const Icon(Icons.map), 'Scrolling map'); + const ScrollingMapPage() : super(const Icon(Icons.map), 'Scrolling map'); @override Widget build(BuildContext context) { @@ -24,8 +26,6 @@ class ScrollingMapPage extends GoogleMapExampleAppPage { class ScrollingMapBody extends StatelessWidget { const ScrollingMapBody(); - final LatLng center = const LatLng(32.080664, 34.9563837); - @override Widget build(BuildContext context) { return ListView( @@ -44,8 +44,8 @@ class ScrollingMapBody extends StatelessWidget { width: 300.0, height: 300.0, child: GoogleMap( - initialCameraPosition: CameraPosition( - target: center, + initialCameraPosition: const CameraPosition( + target: _center, zoom: 11.0, ), gestureRecognizers: // @@ -77,16 +77,16 @@ class ScrollingMapBody extends StatelessWidget { width: 300.0, height: 300.0, child: GoogleMap( - initialCameraPosition: CameraPosition( - target: center, + initialCameraPosition: const CameraPosition( + target: _center, zoom: 11.0, ), markers: { Marker( - markerId: MarkerId("test_marker_id"), + markerId: const MarkerId('test_marker_id'), position: LatLng( - center.latitude, - center.longitude, + _center.latitude, + _center.longitude, ), infoWindow: const InfoWindow( title: 'An interesting location', diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart index c85048f5b5aa..9afc28869490 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart @@ -15,7 +15,7 @@ const CameraPosition _kInitialPosition = CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); class SnapshotPage extends GoogleMapExampleAppPage { - SnapshotPage() + const SnapshotPage() : super(const Icon(Icons.camera_alt), 'Take a snapshot of the map'); @override @@ -39,7 +39,7 @@ class _SnapshotBodyState extends State<_SnapshotBody> { padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ + children: [ SizedBox( height: 180, child: GoogleMap( @@ -48,9 +48,10 @@ class _SnapshotBodyState extends State<_SnapshotBody> { ), ), TextButton( - child: Text('Take a snapshot'), + child: const Text('Take a snapshot'), onPressed: () async { - final imageBytes = await _mapController?.takeSnapshot(); + final Uint8List? imageBytes = + await _mapController?.takeSnapshot(); setState(() { _imageBytes = imageBytes; }); diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart index 1d6dd69c186b..dc7f7d1d6f33 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart @@ -13,7 +13,7 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class TileOverlayPage extends GoogleMapExampleAppPage { - TileOverlayPage() : super(const Icon(Icons.map), 'Tile overlay'); + const TileOverlayPage() : super(const Icon(Icons.map), 'Tile overlay'); @override Widget build(BuildContext context) { @@ -51,7 +51,7 @@ class TileOverlayBodyState extends State { void _addTileOverlay() { final TileOverlay tileOverlay = TileOverlay( - tileOverlayId: TileOverlayId('tile_overlay_1'), + tileOverlayId: const TileOverlayId('tile_overlay_1'), tileProvider: _DebugTileProvider(), ); setState(() { @@ -67,7 +67,7 @@ class TileOverlayBodyState extends State { @override Widget build(BuildContext context) { - Set overlays = { + final Set overlays = { if (_tileOverlay != null) _tileOverlay!, }; return Column( @@ -117,7 +117,7 @@ class _DebugTileProvider implements TileProvider { static const int width = 100; static const int height = 100; static final Paint boxPaint = Paint(); - static final TextStyle textStyle = TextStyle( + static const TextStyle textStyle = TextStyle( color: Colors.red, fontSize: 20, ); @@ -138,7 +138,7 @@ class _DebugTileProvider implements TileProvider { minWidth: 0.0, maxWidth: width.toDouble(), ); - final Offset offset = const Offset(0, 0); + const Offset offset = Offset(0, 0); textPainter.paint(canvas, offset); canvas.drawRect( Rect.fromLTRB(0, 0, width.toDouble(), width.toDouble()), boxPaint); diff --git a/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml index cd614c7e4384..dbc32b0ef2f8 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml @@ -7,10 +7,10 @@ environment: flutter: ">=2.5.0" dependencies: + cupertino_icons: ^0.1.0 flutter: sdk: flutter - - cupertino_icons: ^0.1.0 + flutter_plugin_android_lifecycle: ^2.0.1 google_maps_flutter: # When depending on this package from a real application you should use: # google_maps_flutter: ^x.y.z @@ -18,7 +18,6 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - flutter_plugin_android_lifecycle: ^2.0.1 dev_dependencies: espresso: ^0.1.0+2 @@ -26,7 +25,6 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.10.0 flutter: uses-material-design: true diff --git a/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart b/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart index 93bb0566dd1f..5b1e67c943dd 100644 --- a/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart +++ b/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart @@ -14,7 +14,6 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; -import 'package:google_maps_flutter_platform_interface/src/method_channel/method_channel_google_maps_flutter.dart'; export 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart' show diff --git a/packages/google_maps_flutter/google_maps_flutter/lib/src/controller.dart b/packages/google_maps_flutter/google_maps_flutter/lib/src/controller.dart index d57ac97b1663..088589e4a2ce 100644 --- a/packages/google_maps_flutter/google_maps_flutter/lib/src/controller.dart +++ b/packages/google_maps_flutter/google_maps_flutter/lib/src/controller.dart @@ -6,17 +6,16 @@ part of google_maps_flutter; /// Controller for a single GoogleMap instance running on the host platform. class GoogleMapController { - /// The mapId for this controller - final int mapId; - GoogleMapController._( - CameraPosition initialCameraPosition, this._googleMapState, { required this.mapId, }) { _connectStreams(mapId); } + /// The mapId for this controller + final int mapId; + /// Initialize control of a [GoogleMap] with [id]. /// /// Mainly for internal use when instantiating a [GoogleMapController] passed @@ -29,7 +28,6 @@ class GoogleMapController { assert(id != null); await GoogleMapsFlutterPlatform.instance.init(id); return GoogleMapController._( - initialCameraPosition, googleMapState, mapId: id, ); @@ -38,7 +36,7 @@ class GoogleMapController { /// Used to communicate with the native platform. /// /// Accessible only for testing. - // TODO(dit) https://github.com/flutter/flutter/issues/55504 Remove this getter. + // TODO(dit): Remove this getter, https://github.com/flutter/flutter/issues/55504. @visibleForTesting MethodChannel? get channel { if (GoogleMapsFlutterPlatform.instance is MethodChannelGoogleMapsFlutter) { diff --git a/packages/google_maps_flutter/google_maps_flutter/lib/src/google_map.dart b/packages/google_maps_flutter/google_maps_flutter/lib/src/google_map.dart index b153832426dd..8ecbfbbad499 100644 --- a/packages/google_maps_flutter/google_maps_flutter/lib/src/google_map.dart +++ b/packages/google_maps_flutter/google_maps_flutter/lib/src/google_map.dart @@ -25,11 +25,12 @@ class UnknownMapObjectIdError extends Error { final String objectType; /// The unknown maps object ID. - final MapsObjectId objectId; + final MapsObjectId objectId; /// The context where the error occurred. final String? context; + @override String toString() { if (context != null) { return 'Unknown $objectType ID "${objectId.value}" in $context'; @@ -40,6 +41,8 @@ class UnknownMapObjectIdError extends Error { /// Android specific settings for [GoogleMap]. class AndroidGoogleMapsFlutter { + AndroidGoogleMapsFlutter._(); + /// Whether to render [GoogleMap] with a [AndroidViewSurface] to build the Google Maps widget. /// /// This implementation uses hybrid composition to render the Google Maps @@ -282,7 +285,7 @@ class GoogleMap extends StatefulWidget { } class _GoogleMapState extends State { - final _mapId = _nextMapCreationId++; + final int _mapId = _nextMapCreationId++; final Completer _controller = Completer(); @@ -322,9 +325,13 @@ class _GoogleMapState extends State { } @override - void dispose() async { + void dispose() { + _disposeController(); super.dispose(); - GoogleMapController controller = await _controller.future; + } + + Future _disposeController() async { + final GoogleMapController controller = await _controller.future; controller.dispose(); } @@ -339,7 +346,7 @@ class _GoogleMapState extends State { _updateTileOverlays(); } - void _updateOptions() async { + Future _updateOptions() async { final _GoogleMapOptions newOptions = _GoogleMapOptions.fromWidget(widget); final Map updates = _googleMapOptions.updatesMap(newOptions); @@ -352,7 +359,7 @@ class _GoogleMapState extends State { _googleMapOptions = newOptions; } - void _updateMarkers() async { + Future _updateMarkers() async { final GoogleMapController controller = await _controller.future; // ignore: unawaited_futures controller._updateMarkers( @@ -360,7 +367,7 @@ class _GoogleMapState extends State { _markers = keyByMarkerId(widget.markers); } - void _updatePolygons() async { + Future _updatePolygons() async { final GoogleMapController controller = await _controller.future; // ignore: unawaited_futures controller._updatePolygons( @@ -368,7 +375,7 @@ class _GoogleMapState extends State { _polygons = keyByPolygonId(widget.polygons); } - void _updatePolylines() async { + Future _updatePolylines() async { final GoogleMapController controller = await _controller.future; // ignore: unawaited_futures controller._updatePolylines( @@ -376,7 +383,7 @@ class _GoogleMapState extends State { _polylines = keyByPolylineId(widget.polylines); } - void _updateCircles() async { + Future _updateCircles() async { final GoogleMapController controller = await _controller.future; // ignore: unawaited_futures controller._updateCircles( @@ -384,7 +391,7 @@ class _GoogleMapState extends State { _circles = keyByCircleId(widget.circles); } - void _updateTileOverlays() async { + Future _updateTileOverlays() async { final GoogleMapController controller = await _controller.future; // ignore: unawaited_futures controller._updateTileOverlays(widget.tileOverlays); diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index e8bd391810d7..849019cbdb2d 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter description: A Flutter plugin for integrating Google Maps in iOS and Android applications. repository: https://github.com/flutter/plugins/tree/main/packages/google_maps_flutter/google_maps_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 2.1.1 +version: 2.1.2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/google_maps_flutter/google_maps_flutter/test/circle_updates_test.dart b/packages/google_maps_flutter/google_maps_flutter/test/circle_updates_test.dart index e0d1180a0abb..6d650661c5e7 100644 --- a/packages/google_maps_flutter/google_maps_flutter/test/circle_updates_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/test/circle_updates_test.dart @@ -35,7 +35,7 @@ void main() { }); testWidgets('Initializing a circle', (WidgetTester tester) async { - final Circle c1 = Circle(circleId: CircleId("circle_1")); + const Circle c1 = Circle(circleId: CircleId('circle_1')); await tester.pumpWidget(_mapWithCircles({c1})); final FakePlatformGoogleMap platformGoogleMap = @@ -48,9 +48,9 @@ void main() { expect(platformGoogleMap.circlesToChange.isEmpty, true); }); - testWidgets("Adding a circle", (WidgetTester tester) async { - final Circle c1 = Circle(circleId: CircleId("circle_1")); - final Circle c2 = Circle(circleId: CircleId("circle_2")); + testWidgets('Adding a circle', (WidgetTester tester) async { + const Circle c1 = Circle(circleId: CircleId('circle_1')); + const Circle c2 = Circle(circleId: CircleId('circle_2')); await tester.pumpWidget(_mapWithCircles({c1})); await tester.pumpWidget(_mapWithCircles({c1, c2})); @@ -67,8 +67,8 @@ void main() { expect(platformGoogleMap.circlesToChange.isEmpty, true); }); - testWidgets("Removing a circle", (WidgetTester tester) async { - final Circle c1 = Circle(circleId: CircleId("circle_1")); + testWidgets('Removing a circle', (WidgetTester tester) async { + const Circle c1 = Circle(circleId: CircleId('circle_1')); await tester.pumpWidget(_mapWithCircles({c1})); await tester.pumpWidget(_mapWithCircles({})); @@ -82,9 +82,9 @@ void main() { expect(platformGoogleMap.circlesToAdd.isEmpty, true); }); - testWidgets("Updating a circle", (WidgetTester tester) async { - final Circle c1 = Circle(circleId: CircleId("circle_1")); - final Circle c2 = Circle(circleId: CircleId("circle_1"), radius: 10); + testWidgets('Updating a circle', (WidgetTester tester) async { + const Circle c1 = Circle(circleId: CircleId('circle_1')); + const Circle c2 = Circle(circleId: CircleId('circle_1'), radius: 10); await tester.pumpWidget(_mapWithCircles({c1})); await tester.pumpWidget(_mapWithCircles({c2})); @@ -98,9 +98,9 @@ void main() { expect(platformGoogleMap.circlesToAdd.isEmpty, true); }); - testWidgets("Updating a circle", (WidgetTester tester) async { - final Circle c1 = Circle(circleId: CircleId("circle_1")); - final Circle c2 = Circle(circleId: CircleId("circle_1"), radius: 10); + testWidgets('Updating a circle', (WidgetTester tester) async { + const Circle c1 = Circle(circleId: CircleId('circle_1')); + const Circle c2 = Circle(circleId: CircleId('circle_1'), radius: 10); await tester.pumpWidget(_mapWithCircles({c1})); await tester.pumpWidget(_mapWithCircles({c2})); @@ -114,12 +114,12 @@ void main() { expect(update.radius, 10); }); - testWidgets("Multi Update", (WidgetTester tester) async { - Circle c1 = Circle(circleId: CircleId("circle_1")); - Circle c2 = Circle(circleId: CircleId("circle_2")); + testWidgets('Multi Update', (WidgetTester tester) async { + Circle c1 = const Circle(circleId: CircleId('circle_1')); + Circle c2 = const Circle(circleId: CircleId('circle_2')); final Set prev = {c1, c2}; - c1 = Circle(circleId: CircleId("circle_1"), visible: false); - c2 = Circle(circleId: CircleId("circle_2"), radius: 10); + c1 = const Circle(circleId: CircleId('circle_1'), visible: false); + c2 = const Circle(circleId: CircleId('circle_2'), radius: 10); final Set cur = {c1, c2}; await tester.pumpWidget(_mapWithCircles(prev)); @@ -133,14 +133,14 @@ void main() { expect(platformGoogleMap.circlesToAdd.isEmpty, true); }); - testWidgets("Multi Update", (WidgetTester tester) async { - Circle c2 = Circle(circleId: CircleId("circle_2")); - final Circle c3 = Circle(circleId: CircleId("circle_3")); + testWidgets('Multi Update', (WidgetTester tester) async { + Circle c2 = const Circle(circleId: CircleId('circle_2')); + const Circle c3 = Circle(circleId: CircleId('circle_3')); final Set prev = {c2, c3}; // c1 is added, c2 is updated, c3 is removed. - final Circle c1 = Circle(circleId: CircleId("circle_1")); - c2 = Circle(circleId: CircleId("circle_2"), radius: 10); + const Circle c1 = Circle(circleId: CircleId('circle_1')); + c2 = const Circle(circleId: CircleId('circle_2'), radius: 10); final Set cur = {c1, c2}; await tester.pumpWidget(_mapWithCircles(prev)); @@ -158,12 +158,12 @@ void main() { expect(platformGoogleMap.circleIdsToRemove.first, equals(c3.circleId)); }); - testWidgets("Partial Update", (WidgetTester tester) async { - final Circle c1 = Circle(circleId: CircleId("circle_1")); - final Circle c2 = Circle(circleId: CircleId("circle_2")); - Circle c3 = Circle(circleId: CircleId("circle_3")); + testWidgets('Partial Update', (WidgetTester tester) async { + const Circle c1 = Circle(circleId: CircleId('circle_1')); + const Circle c2 = Circle(circleId: CircleId('circle_2')); + Circle c3 = const Circle(circleId: CircleId('circle_3')); final Set prev = {c1, c2, c3}; - c3 = Circle(circleId: CircleId("circle_3"), radius: 10); + c3 = const Circle(circleId: CircleId('circle_3'), radius: 10); final Set cur = {c1, c2, c3}; await tester.pumpWidget(_mapWithCircles(prev)); @@ -177,10 +177,11 @@ void main() { expect(platformGoogleMap.circlesToAdd.isEmpty, true); }); - testWidgets("Update non platform related attr", (WidgetTester tester) async { - Circle c1 = Circle(circleId: CircleId("circle_1")); + testWidgets('Update non platform related attr', (WidgetTester tester) async { + Circle c1 = const Circle(circleId: CircleId('circle_1')); final Set prev = {c1}; - c1 = Circle(circleId: CircleId("circle_1"), onTap: () => print("hello")); + c1 = Circle( + circleId: const CircleId('circle_1'), onTap: () => print('hello')); final Set cur = {c1}; await tester.pumpWidget(_mapWithCircles(prev)); diff --git a/packages/google_maps_flutter/google_maps_flutter/test/fake_maps_controllers.dart b/packages/google_maps_flutter/google_maps_flutter/test/fake_maps_controllers.dart index 37270ea34d29..bac3ceabc4de 100644 --- a/packages/google_maps_flutter/google_maps_flutter/test/fake_maps_controllers.dart +++ b/packages/google_maps_flutter/google_maps_flutter/test/fake_maps_controllers.dart @@ -15,7 +15,7 @@ class FakePlatformGoogleMap { channel = MethodChannel( 'plugins.flutter.io/google_maps_$id', const StandardMethodCodec()) { channel.setMockMethodCallHandler(onMethodCall); - updateOptions(params['options']); + updateOptions(params['options'] as Map); updateMarkers(params); updatePolygons(params); updatePolylines(params); @@ -94,23 +94,23 @@ class FakePlatformGoogleMap { Future onMethodCall(MethodCall call) { switch (call.method) { case 'map#update': - updateOptions(call.arguments['options']); + updateOptions(call.arguments['options'] as Map); return Future.sync(() {}); case 'markers#update': - updateMarkers(call.arguments); + updateMarkers(call.arguments as Map?); return Future.sync(() {}); case 'polygons#update': - updatePolygons(call.arguments); + updatePolygons(call.arguments as Map?); return Future.sync(() {}); case 'polylines#update': - updatePolylines(call.arguments); + updatePolylines(call.arguments as Map?); return Future.sync(() {}); case 'tileOverlays#update': - updateTileOverlays( - Map.castFrom(call.arguments)); + updateTileOverlays(Map.castFrom( + call.arguments as Map)); return Future.sync(() {}); case 'circles#update': - updateCircles(call.arguments); + updateCircles(call.arguments as Map?); return Future.sync(() {}); default: return Future.sync(() {}); @@ -122,8 +122,8 @@ class FakePlatformGoogleMap { return; } markersToAdd = _deserializeMarkers(markerUpdates['markersToAdd']); - markerIdsToRemove = - _deserializeMarkerIds(markerUpdates['markerIdsToRemove']); + markerIdsToRemove = _deserializeMarkerIds( + markerUpdates['markerIdsToRemove'] as List?); markersToChange = _deserializeMarkers(markerUpdates['markersToChange']); } @@ -131,29 +131,32 @@ class FakePlatformGoogleMap { if (markerIds == null) { return {}; } - return markerIds.map((dynamic markerId) => MarkerId(markerId)).toSet(); + return markerIds + .map((dynamic markerId) => MarkerId(markerId as String)) + .toSet(); } Set _deserializeMarkers(dynamic markers) { if (markers == null) { return {}; } - final List markersData = markers; + final List markersData = markers as List; final Set result = {}; - for (Map markerData + for (final Map markerData in markersData.cast>()) { - final String markerId = markerData['markerId']; - final double alpha = markerData['alpha']; - final bool draggable = markerData['draggable']; - final bool visible = markerData['visible']; + final String markerId = markerData['markerId'] as String; + final double alpha = markerData['alpha'] as double; + final bool draggable = markerData['draggable'] as bool; + final bool visible = markerData['visible'] as bool; final dynamic infoWindowData = markerData['infoWindow']; InfoWindow infoWindow = InfoWindow.noText; if (infoWindowData != null) { - final Map infoWindowMap = infoWindowData; + final Map infoWindowMap = + infoWindowData as Map; infoWindow = InfoWindow( - title: infoWindowMap['title'], - snippet: infoWindowMap['snippet'], + title: infoWindowMap['title'] as String?, + snippet: infoWindowMap['snippet'] as String?, ); } @@ -174,8 +177,8 @@ class FakePlatformGoogleMap { return; } polygonsToAdd = _deserializePolygons(polygonUpdates['polygonsToAdd']); - polygonIdsToRemove = - _deserializePolygonIds(polygonUpdates['polygonIdsToRemove']); + polygonIdsToRemove = _deserializePolygonIds( + polygonUpdates['polygonIdsToRemove'] as List?); polygonsToChange = _deserializePolygons(polygonUpdates['polygonsToChange']); } @@ -183,22 +186,26 @@ class FakePlatformGoogleMap { if (polygonIds == null) { return {}; } - return polygonIds.map((dynamic polygonId) => PolygonId(polygonId)).toSet(); + return polygonIds + .map((dynamic polygonId) => PolygonId(polygonId as String)) + .toSet(); } Set _deserializePolygons(dynamic polygons) { if (polygons == null) { return {}; } - final List polygonsData = polygons; + final List polygonsData = polygons as List; final Set result = {}; - for (Map polygonData + for (final Map polygonData in polygonsData.cast>()) { - final String polygonId = polygonData['polygonId']; - final bool visible = polygonData['visible']; - final bool geodesic = polygonData['geodesic']; - final List points = _deserializePoints(polygonData['points']); - final List> holes = _deserializeHoles(polygonData['holes']); + final String polygonId = polygonData['polygonId'] as String; + final bool visible = polygonData['visible'] as bool; + final bool geodesic = polygonData['geodesic'] as bool; + final List points = + _deserializePoints(polygonData['points'] as List); + final List> holes = + _deserializeHoles(polygonData['holes'] as List); result.add(Polygon( polygonId: PolygonId(polygonId), @@ -214,15 +221,15 @@ class FakePlatformGoogleMap { List _deserializePoints(List points) { return points.map((dynamic list) { - return LatLng(list[0], list[1]); + return LatLng(list[0] as double, list[1] as double); }).toList(); } List> _deserializeHoles(List holes) { return holes.map>((dynamic hole) { return hole.map((dynamic list) { - return LatLng(list[0], list[1]); - }).toList(); + return LatLng(list[0] as double, list[1] as double); + }).toList() as List; }).toList(); } @@ -231,8 +238,8 @@ class FakePlatformGoogleMap { return; } polylinesToAdd = _deserializePolylines(polylineUpdates['polylinesToAdd']); - polylineIdsToRemove = - _deserializePolylineIds(polylineUpdates['polylineIdsToRemove']); + polylineIdsToRemove = _deserializePolylineIds( + polylineUpdates['polylineIdsToRemove'] as List?); polylinesToChange = _deserializePolylines(polylineUpdates['polylinesToChange']); } @@ -242,7 +249,7 @@ class FakePlatformGoogleMap { return {}; } return polylineIds - .map((dynamic polylineId) => PolylineId(polylineId)) + .map((dynamic polylineId) => PolylineId(polylineId as String)) .toSet(); } @@ -250,14 +257,15 @@ class FakePlatformGoogleMap { if (polylines == null) { return {}; } - final List polylinesData = polylines; + final List polylinesData = polylines as List; final Set result = {}; - for (Map polylineData + for (final Map polylineData in polylinesData.cast>()) { - final String polylineId = polylineData['polylineId']; - final bool visible = polylineData['visible']; - final bool geodesic = polylineData['geodesic']; - final List points = _deserializePoints(polylineData['points']); + final String polylineId = polylineData['polylineId'] as String; + final bool visible = polylineData['visible'] as bool; + final bool geodesic = polylineData['geodesic'] as bool; + final List points = + _deserializePoints(polylineData['points'] as List); result.add(Polyline( polylineId: PolylineId(polylineId), @@ -275,8 +283,8 @@ class FakePlatformGoogleMap { return; } circlesToAdd = _deserializeCircles(circleUpdates['circlesToAdd']); - circleIdsToRemove = - _deserializeCircleIds(circleUpdates['circleIdsToRemove']); + circleIdsToRemove = _deserializeCircleIds( + circleUpdates['circleIdsToRemove'] as List?); circlesToChange = _deserializeCircles(circleUpdates['circlesToChange']); } @@ -287,17 +295,19 @@ class FakePlatformGoogleMap { final List>? tileOverlaysToAddList = updateTileOverlayUpdates['tileOverlaysToAdd'] != null ? List.castFrom>( - updateTileOverlayUpdates['tileOverlaysToAdd']) + updateTileOverlayUpdates['tileOverlaysToAdd'] as List) : null; final List? tileOverlayIdsToRemoveList = updateTileOverlayUpdates['tileOverlayIdsToRemove'] != null ? List.castFrom( - updateTileOverlayUpdates['tileOverlayIdsToRemove']) + updateTileOverlayUpdates['tileOverlayIdsToRemove'] + as List) : null; final List>? tileOverlaysToChangeList = updateTileOverlayUpdates['tileOverlaysToChange'] != null ? List.castFrom>( - updateTileOverlayUpdates['tileOverlaysToChange']) + updateTileOverlayUpdates['tileOverlaysToChange'] + as List) : null; tileOverlaysToAdd = _deserializeTileOverlays(tileOverlaysToAddList); tileOverlayIdsToRemove = @@ -309,20 +319,22 @@ class FakePlatformGoogleMap { if (circleIds == null) { return {}; } - return circleIds.map((dynamic circleId) => CircleId(circleId)).toSet(); + return circleIds + .map((dynamic circleId) => CircleId(circleId as String)) + .toSet(); } Set _deserializeCircles(dynamic circles) { if (circles == null) { return {}; } - final List circlesData = circles; + final List circlesData = circles as List; final Set result = {}; - for (Map circleData + for (final Map circleData in circlesData.cast>()) { - final String circleId = circleData['circleId']; - final bool visible = circleData['visible']; - final double radius = circleData['radius']; + final String circleId = circleData['circleId'] as String; + final bool visible = circleData['visible'] as bool; + final double radius = circleData['radius'] as double; result.add(Circle( circleId: CircleId(circleId), @@ -349,12 +361,12 @@ class FakePlatformGoogleMap { return {}; } final Set result = {}; - for (Map tileOverlayData in tileOverlays) { - final String tileOverlayId = tileOverlayData['tileOverlayId']; - final bool fadeIn = tileOverlayData['fadeIn']; - final double transparency = tileOverlayData['transparency']; - final int zIndex = tileOverlayData['zIndex']; - final bool visible = tileOverlayData['visible']; + for (final Map tileOverlayData in tileOverlays) { + final String tileOverlayId = tileOverlayData['tileOverlayId'] as String; + final bool fadeIn = tileOverlayData['fadeIn'] as bool; + final double transparency = tileOverlayData['transparency'] as double; + final int zIndex = tileOverlayData['zIndex'] as int; + final bool visible = tileOverlayData['visible'] as bool; result.add(TileOverlay( tileOverlayId: TileOverlayId(tileOverlayId), @@ -370,60 +382,62 @@ class FakePlatformGoogleMap { void updateOptions(Map options) { if (options.containsKey('compassEnabled')) { - compassEnabled = options['compassEnabled']; + compassEnabled = options['compassEnabled'] as bool?; } if (options.containsKey('mapToolbarEnabled')) { - mapToolbarEnabled = options['mapToolbarEnabled']; + mapToolbarEnabled = options['mapToolbarEnabled'] as bool?; } if (options.containsKey('cameraTargetBounds')) { - final List boundsList = options['cameraTargetBounds']; + final List boundsList = + options['cameraTargetBounds'] as List; cameraTargetBounds = boundsList[0] == null ? CameraTargetBounds.unbounded : CameraTargetBounds(LatLngBounds.fromList(boundsList[0])); } if (options.containsKey('mapType')) { - mapType = MapType.values[options['mapType']]; + mapType = MapType.values[options['mapType'] as int]; } if (options.containsKey('minMaxZoomPreference')) { - final List minMaxZoomList = options['minMaxZoomPreference']; - minMaxZoomPreference = - MinMaxZoomPreference(minMaxZoomList[0], minMaxZoomList[1]); + final List minMaxZoomList = + options['minMaxZoomPreference'] as List; + minMaxZoomPreference = MinMaxZoomPreference( + minMaxZoomList[0] as double?, minMaxZoomList[1] as double?); } if (options.containsKey('rotateGesturesEnabled')) { - rotateGesturesEnabled = options['rotateGesturesEnabled']; + rotateGesturesEnabled = options['rotateGesturesEnabled'] as bool?; } if (options.containsKey('scrollGesturesEnabled')) { - scrollGesturesEnabled = options['scrollGesturesEnabled']; + scrollGesturesEnabled = options['scrollGesturesEnabled'] as bool?; } if (options.containsKey('tiltGesturesEnabled')) { - tiltGesturesEnabled = options['tiltGesturesEnabled']; + tiltGesturesEnabled = options['tiltGesturesEnabled'] as bool?; } if (options.containsKey('trackCameraPosition')) { - trackCameraPosition = options['trackCameraPosition']; + trackCameraPosition = options['trackCameraPosition'] as bool?; } if (options.containsKey('zoomGesturesEnabled')) { - zoomGesturesEnabled = options['zoomGesturesEnabled']; + zoomGesturesEnabled = options['zoomGesturesEnabled'] as bool?; } if (options.containsKey('zoomControlsEnabled')) { - zoomControlsEnabled = options['zoomControlsEnabled']; + zoomControlsEnabled = options['zoomControlsEnabled'] as bool?; } if (options.containsKey('liteModeEnabled')) { - liteModeEnabled = options['liteModeEnabled']; + liteModeEnabled = options['liteModeEnabled'] as bool?; } if (options.containsKey('myLocationEnabled')) { - myLocationEnabled = options['myLocationEnabled']; + myLocationEnabled = options['myLocationEnabled'] as bool?; } if (options.containsKey('myLocationButtonEnabled')) { - myLocationButtonEnabled = options['myLocationButtonEnabled']; + myLocationButtonEnabled = options['myLocationButtonEnabled'] as bool?; } if (options.containsKey('trafficEnabled')) { - trafficEnabled = options['trafficEnabled']; + trafficEnabled = options['trafficEnabled'] as bool?; } if (options.containsKey('buildingsEnabled')) { - buildingsEnabled = options['buildingsEnabled']; + buildingsEnabled = options['buildingsEnabled'] as bool?; } if (options.containsKey('padding')) { - padding = options['padding']; + padding = options['padding'] as List?; } } } @@ -434,10 +448,12 @@ class FakePlatformViewsController { Future fakePlatformViewsMethodHandler(MethodCall call) { switch (call.method) { case 'create': - final Map args = call.arguments; - final Map params = _decodeParams(args['params'])!; + final Map args = + call.arguments as Map; + final Map params = + _decodeParams(args['params'] as Uint8List)!; lastCreatedView = FakePlatformGoogleMap( - args['id'], + args['id'] as int, params, ); return Future.sync(() => 1); @@ -457,5 +473,6 @@ Map? _decodeParams(Uint8List paramsMessage) { paramsMessage.offsetInBytes, paramsMessage.lengthInBytes, ); - return const StandardMessageCodec().decodeMessage(messageBytes); + return const StandardMessageCodec().decodeMessage(messageBytes) + as Map?; } diff --git a/packages/google_maps_flutter/google_maps_flutter/test/map_creation_test.dart b/packages/google_maps_flutter/google_maps_flutter/test/map_creation_test.dart index 6b3ac906802f..73e1e77646cd 100644 --- a/packages/google_maps_flutter/google_maps_flutter/test/map_creation_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/test/map_creation_test.dart @@ -32,7 +32,7 @@ void main() { Directionality( textDirection: TextDirection.ltr, child: Column( - children: const [ + children: const [ GoogleMap( initialCameraPosition: CameraPosition( target: LatLng(43.362, -5.849), @@ -57,7 +57,7 @@ void main() { testWidgets('Calls platform.dispose when GoogleMap is disposed of', ( WidgetTester tester, ) async { - await tester.pumpWidget(GoogleMap( + await tester.pumpWidget(const GoogleMap( initialCameraPosition: CameraPosition( target: LatLng(43.3608, -5.8702), ), @@ -81,8 +81,8 @@ class TestGoogleMapsFlutterPlatform extends GoogleMapsFlutterPlatform { bool disposed = false; // Stream controller to inject events for testing. - final StreamController mapEventStreamController = - StreamController.broadcast(); + final StreamController> mapEventStreamController = + StreamController>.broadcast(); @override Future init(int mapId) async {} @@ -151,7 +151,8 @@ class TestGoogleMapsFlutterPlatform extends GoogleMapsFlutterPlatform { Future getVisibleRegion({ required int mapId, }) async { - return LatLngBounds(southwest: LatLng(0, 0), northeast: LatLng(0, 0)); + return LatLngBounds( + southwest: const LatLng(0, 0), northeast: const LatLng(0, 0)); } @override @@ -159,7 +160,7 @@ class TestGoogleMapsFlutterPlatform extends GoogleMapsFlutterPlatform { LatLng latLng, { required int mapId, }) async { - return ScreenCoordinate(x: 0, y: 0); + return const ScreenCoordinate(x: 0, y: 0); } @override @@ -167,7 +168,7 @@ class TestGoogleMapsFlutterPlatform extends GoogleMapsFlutterPlatform { ScreenCoordinate screenCoordinate, { required int mapId, }) async { - return LatLng(0, 0); + return const LatLng(0, 0); } @override diff --git a/packages/google_maps_flutter/google_maps_flutter/test/marker_updates_test.dart b/packages/google_maps_flutter/google_maps_flutter/test/marker_updates_test.dart index e295393fe15a..b5bba55671c8 100644 --- a/packages/google_maps_flutter/google_maps_flutter/test/marker_updates_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/test/marker_updates_test.dart @@ -35,7 +35,7 @@ void main() { }); testWidgets('Initializing a marker', (WidgetTester tester) async { - final Marker m1 = Marker(markerId: MarkerId("marker_1")); + const Marker m1 = Marker(markerId: MarkerId('marker_1')); await tester.pumpWidget(_mapWithMarkers({m1})); final FakePlatformGoogleMap platformGoogleMap = @@ -48,9 +48,9 @@ void main() { expect(platformGoogleMap.markersToChange.isEmpty, true); }); - testWidgets("Adding a marker", (WidgetTester tester) async { - final Marker m1 = Marker(markerId: MarkerId("marker_1")); - final Marker m2 = Marker(markerId: MarkerId("marker_2")); + testWidgets('Adding a marker', (WidgetTester tester) async { + const Marker m1 = Marker(markerId: MarkerId('marker_1')); + const Marker m2 = Marker(markerId: MarkerId('marker_2')); await tester.pumpWidget(_mapWithMarkers({m1})); await tester.pumpWidget(_mapWithMarkers({m1, m2})); @@ -67,8 +67,8 @@ void main() { expect(platformGoogleMap.markersToChange.isEmpty, true); }); - testWidgets("Removing a marker", (WidgetTester tester) async { - final Marker m1 = Marker(markerId: MarkerId("marker_1")); + testWidgets('Removing a marker', (WidgetTester tester) async { + const Marker m1 = Marker(markerId: MarkerId('marker_1')); await tester.pumpWidget(_mapWithMarkers({m1})); await tester.pumpWidget(_mapWithMarkers({})); @@ -82,9 +82,9 @@ void main() { expect(platformGoogleMap.markersToAdd.isEmpty, true); }); - testWidgets("Updating a marker", (WidgetTester tester) async { - final Marker m1 = Marker(markerId: MarkerId("marker_1")); - final Marker m2 = Marker(markerId: MarkerId("marker_1"), alpha: 0.5); + testWidgets('Updating a marker', (WidgetTester tester) async { + const Marker m1 = Marker(markerId: MarkerId('marker_1')); + const Marker m2 = Marker(markerId: MarkerId('marker_1'), alpha: 0.5); await tester.pumpWidget(_mapWithMarkers({m1})); await tester.pumpWidget(_mapWithMarkers({m2})); @@ -98,11 +98,11 @@ void main() { expect(platformGoogleMap.markersToAdd.isEmpty, true); }); - testWidgets("Updating a marker", (WidgetTester tester) async { - final Marker m1 = Marker(markerId: MarkerId("marker_1")); - final Marker m2 = Marker( - markerId: MarkerId("marker_1"), - infoWindow: const InfoWindow(snippet: 'changed'), + testWidgets('Updating a marker', (WidgetTester tester) async { + const Marker m1 = Marker(markerId: MarkerId('marker_1')); + const Marker m2 = Marker( + markerId: MarkerId('marker_1'), + infoWindow: InfoWindow(snippet: 'changed'), ); await tester.pumpWidget(_mapWithMarkers({m1})); @@ -117,12 +117,12 @@ void main() { expect(update.infoWindow.snippet, 'changed'); }); - testWidgets("Multi Update", (WidgetTester tester) async { - Marker m1 = Marker(markerId: MarkerId("marker_1")); - Marker m2 = Marker(markerId: MarkerId("marker_2")); + testWidgets('Multi Update', (WidgetTester tester) async { + Marker m1 = const Marker(markerId: MarkerId('marker_1')); + Marker m2 = const Marker(markerId: MarkerId('marker_2')); final Set prev = {m1, m2}; - m1 = Marker(markerId: MarkerId("marker_1"), visible: false); - m2 = Marker(markerId: MarkerId("marker_2"), draggable: true); + m1 = const Marker(markerId: MarkerId('marker_1'), visible: false); + m2 = const Marker(markerId: MarkerId('marker_2'), draggable: true); final Set cur = {m1, m2}; await tester.pumpWidget(_mapWithMarkers(prev)); @@ -136,14 +136,14 @@ void main() { expect(platformGoogleMap.markersToAdd.isEmpty, true); }); - testWidgets("Multi Update", (WidgetTester tester) async { - Marker m2 = Marker(markerId: MarkerId("marker_2")); - final Marker m3 = Marker(markerId: MarkerId("marker_3")); + testWidgets('Multi Update', (WidgetTester tester) async { + Marker m2 = const Marker(markerId: MarkerId('marker_2')); + const Marker m3 = Marker(markerId: MarkerId('marker_3')); final Set prev = {m2, m3}; // m1 is added, m2 is updated, m3 is removed. - final Marker m1 = Marker(markerId: MarkerId("marker_1")); - m2 = Marker(markerId: MarkerId("marker_2"), draggable: true); + const Marker m1 = Marker(markerId: MarkerId('marker_1')); + m2 = const Marker(markerId: MarkerId('marker_2'), draggable: true); final Set cur = {m1, m2}; await tester.pumpWidget(_mapWithMarkers(prev)); @@ -161,12 +161,12 @@ void main() { expect(platformGoogleMap.markerIdsToRemove.first, equals(m3.markerId)); }); - testWidgets("Partial Update", (WidgetTester tester) async { - final Marker m1 = Marker(markerId: MarkerId("marker_1")); - final Marker m2 = Marker(markerId: MarkerId("marker_2")); - Marker m3 = Marker(markerId: MarkerId("marker_3")); + testWidgets('Partial Update', (WidgetTester tester) async { + const Marker m1 = Marker(markerId: MarkerId('marker_1')); + const Marker m2 = Marker(markerId: MarkerId('marker_2')); + Marker m3 = const Marker(markerId: MarkerId('marker_3')); final Set prev = {m1, m2, m3}; - m3 = Marker(markerId: MarkerId("marker_3"), draggable: true); + m3 = const Marker(markerId: MarkerId('marker_3'), draggable: true); final Set cur = {m1, m2, m3}; await tester.pumpWidget(_mapWithMarkers(prev)); @@ -180,12 +180,12 @@ void main() { expect(platformGoogleMap.markersToAdd.isEmpty, true); }); - testWidgets("Update non platform related attr", (WidgetTester tester) async { - Marker m1 = Marker(markerId: MarkerId("marker_1")); + testWidgets('Update non platform related attr', (WidgetTester tester) async { + Marker m1 = const Marker(markerId: MarkerId('marker_1')); final Set prev = {m1}; m1 = Marker( - markerId: MarkerId("marker_1"), - onTap: () => print("hello"), + markerId: const MarkerId('marker_1'), + onTap: () => print('hello'), onDragEnd: (LatLng latLng) => print(latLng)); final Set cur = {m1}; diff --git a/packages/google_maps_flutter/google_maps_flutter/test/polygon_updates_test.dart b/packages/google_maps_flutter/google_maps_flutter/test/polygon_updates_test.dart index 79c63c1c5459..cb7263c02e05 100644 --- a/packages/google_maps_flutter/google_maps_flutter/test/polygon_updates_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/test/polygon_updates_test.dart @@ -23,9 +23,9 @@ List _rectPoints({ required double size, LatLng center = const LatLng(0, 0), }) { - final halfSize = size / 2; + final double halfSize = size / 2; - return [ + return [ LatLng(center.latitude + halfSize, center.longitude + halfSize), LatLng(center.latitude - halfSize, center.longitude + halfSize), LatLng(center.latitude - halfSize, center.longitude - halfSize), @@ -38,7 +38,7 @@ Polygon _polygonWithPointsAndHole(PolygonId polygonId) { return Polygon( polygonId: polygonId, points: _rectPoints(size: 1), - holes: [_rectPoints(size: 0.5)], + holes: >[_rectPoints(size: 0.5)], ); } @@ -58,7 +58,7 @@ void main() { }); testWidgets('Initializing a polygon', (WidgetTester tester) async { - final Polygon p1 = Polygon(polygonId: PolygonId("polygon_1")); + const Polygon p1 = Polygon(polygonId: PolygonId('polygon_1')); await tester.pumpWidget(_mapWithPolygons({p1})); final FakePlatformGoogleMap platformGoogleMap = @@ -71,9 +71,9 @@ void main() { expect(platformGoogleMap.polygonsToChange.isEmpty, true); }); - testWidgets("Adding a polygon", (WidgetTester tester) async { - final Polygon p1 = Polygon(polygonId: PolygonId("polygon_1")); - final Polygon p2 = Polygon(polygonId: PolygonId("polygon_2")); + testWidgets('Adding a polygon', (WidgetTester tester) async { + const Polygon p1 = Polygon(polygonId: PolygonId('polygon_1')); + const Polygon p2 = Polygon(polygonId: PolygonId('polygon_2')); await tester.pumpWidget(_mapWithPolygons({p1})); await tester.pumpWidget(_mapWithPolygons({p1, p2})); @@ -90,8 +90,8 @@ void main() { expect(platformGoogleMap.polygonsToChange.isEmpty, true); }); - testWidgets("Removing a polygon", (WidgetTester tester) async { - final Polygon p1 = Polygon(polygonId: PolygonId("polygon_1")); + testWidgets('Removing a polygon', (WidgetTester tester) async { + const Polygon p1 = Polygon(polygonId: PolygonId('polygon_1')); await tester.pumpWidget(_mapWithPolygons({p1})); await tester.pumpWidget(_mapWithPolygons({})); @@ -105,10 +105,10 @@ void main() { expect(platformGoogleMap.polygonsToAdd.isEmpty, true); }); - testWidgets("Updating a polygon", (WidgetTester tester) async { - final Polygon p1 = Polygon(polygonId: PolygonId("polygon_1")); - final Polygon p2 = - Polygon(polygonId: PolygonId("polygon_1"), geodesic: true); + testWidgets('Updating a polygon', (WidgetTester tester) async { + const Polygon p1 = Polygon(polygonId: PolygonId('polygon_1')); + const Polygon p2 = + Polygon(polygonId: PolygonId('polygon_1'), geodesic: true); await tester.pumpWidget(_mapWithPolygons({p1})); await tester.pumpWidget(_mapWithPolygons({p2})); @@ -122,10 +122,11 @@ void main() { expect(platformGoogleMap.polygonsToAdd.isEmpty, true); }); - testWidgets("Mutate a polygon", (WidgetTester tester) async { + testWidgets('Mutate a polygon', (WidgetTester tester) async { + final List _points = [const LatLng(0.0, 0.0)]; final Polygon p1 = Polygon( - polygonId: PolygonId("polygon_1"), - points: [const LatLng(0.0, 0.0)], + polygonId: const PolygonId('polygon_1'), + points: _points, ); await tester.pumpWidget(_mapWithPolygons({p1})); @@ -141,12 +142,12 @@ void main() { expect(platformGoogleMap.polygonsToAdd.isEmpty, true); }); - testWidgets("Multi Update", (WidgetTester tester) async { - Polygon p1 = Polygon(polygonId: PolygonId("polygon_1")); - Polygon p2 = Polygon(polygonId: PolygonId("polygon_2")); + testWidgets('Multi Update', (WidgetTester tester) async { + Polygon p1 = const Polygon(polygonId: PolygonId('polygon_1')); + Polygon p2 = const Polygon(polygonId: PolygonId('polygon_2')); final Set prev = {p1, p2}; - p1 = Polygon(polygonId: PolygonId("polygon_1"), visible: false); - p2 = Polygon(polygonId: PolygonId("polygon_2"), geodesic: true); + p1 = const Polygon(polygonId: PolygonId('polygon_1'), visible: false); + p2 = const Polygon(polygonId: PolygonId('polygon_2'), geodesic: true); final Set cur = {p1, p2}; await tester.pumpWidget(_mapWithPolygons(prev)); @@ -160,14 +161,14 @@ void main() { expect(platformGoogleMap.polygonsToAdd.isEmpty, true); }); - testWidgets("Multi Update", (WidgetTester tester) async { - Polygon p2 = Polygon(polygonId: PolygonId("polygon_2")); - final Polygon p3 = Polygon(polygonId: PolygonId("polygon_3")); + testWidgets('Multi Update', (WidgetTester tester) async { + Polygon p2 = const Polygon(polygonId: PolygonId('polygon_2')); + const Polygon p3 = Polygon(polygonId: PolygonId('polygon_3')); final Set prev = {p2, p3}; // p1 is added, p2 is updated, p3 is removed. - final Polygon p1 = Polygon(polygonId: PolygonId("polygon_1")); - p2 = Polygon(polygonId: PolygonId("polygon_2"), geodesic: true); + const Polygon p1 = Polygon(polygonId: PolygonId('polygon_1')); + p2 = const Polygon(polygonId: PolygonId('polygon_2'), geodesic: true); final Set cur = {p1, p2}; await tester.pumpWidget(_mapWithPolygons(prev)); @@ -185,12 +186,12 @@ void main() { expect(platformGoogleMap.polygonIdsToRemove.first, equals(p3.polygonId)); }); - testWidgets("Partial Update", (WidgetTester tester) async { - final Polygon p1 = Polygon(polygonId: PolygonId("polygon_1")); - final Polygon p2 = Polygon(polygonId: PolygonId("polygon_2")); - Polygon p3 = Polygon(polygonId: PolygonId("polygon_3")); + testWidgets('Partial Update', (WidgetTester tester) async { + const Polygon p1 = Polygon(polygonId: PolygonId('polygon_1')); + const Polygon p2 = Polygon(polygonId: PolygonId('polygon_2')); + Polygon p3 = const Polygon(polygonId: PolygonId('polygon_3')); final Set prev = {p1, p2, p3}; - p3 = Polygon(polygonId: PolygonId("polygon_3"), geodesic: true); + p3 = const Polygon(polygonId: PolygonId('polygon_3'), geodesic: true); final Set cur = {p1, p2, p3}; await tester.pumpWidget(_mapWithPolygons(prev)); @@ -204,10 +205,11 @@ void main() { expect(platformGoogleMap.polygonsToAdd.isEmpty, true); }); - testWidgets("Update non platform related attr", (WidgetTester tester) async { - Polygon p1 = Polygon(polygonId: PolygonId("polygon_1")); + testWidgets('Update non platform related attr', (WidgetTester tester) async { + Polygon p1 = const Polygon(polygonId: PolygonId('polygon_1')); final Set prev = {p1}; - p1 = Polygon(polygonId: PolygonId("polygon_1"), onTap: () => print(2 + 2)); + p1 = Polygon( + polygonId: const PolygonId('polygon_1'), onTap: () => print(2 + 2)); final Set cur = {p1}; await tester.pumpWidget(_mapWithPolygons(prev)); @@ -223,7 +225,7 @@ void main() { testWidgets('Initializing a polygon with points and hole', (WidgetTester tester) async { - final Polygon p1 = _polygonWithPointsAndHole(PolygonId("polygon_1")); + final Polygon p1 = _polygonWithPointsAndHole(const PolygonId('polygon_1')); await tester.pumpWidget(_mapWithPolygons({p1})); final FakePlatformGoogleMap platformGoogleMap = @@ -236,10 +238,10 @@ void main() { expect(platformGoogleMap.polygonsToChange.isEmpty, true); }); - testWidgets("Adding a polygon with points and hole", + testWidgets('Adding a polygon with points and hole', (WidgetTester tester) async { - final Polygon p1 = Polygon(polygonId: PolygonId("polygon_1")); - final Polygon p2 = _polygonWithPointsAndHole(PolygonId("polygon_2")); + const Polygon p1 = Polygon(polygonId: PolygonId('polygon_1')); + final Polygon p2 = _polygonWithPointsAndHole(const PolygonId('polygon_2')); await tester.pumpWidget(_mapWithPolygons({p1})); await tester.pumpWidget(_mapWithPolygons({p1, p2})); @@ -256,9 +258,9 @@ void main() { expect(platformGoogleMap.polygonsToChange.isEmpty, true); }); - testWidgets("Removing a polygon with points and hole", + testWidgets('Removing a polygon with points and hole', (WidgetTester tester) async { - final Polygon p1 = _polygonWithPointsAndHole(PolygonId("polygon_1")); + final Polygon p1 = _polygonWithPointsAndHole(const PolygonId('polygon_1')); await tester.pumpWidget(_mapWithPolygons({p1})); await tester.pumpWidget(_mapWithPolygons({})); @@ -272,10 +274,10 @@ void main() { expect(platformGoogleMap.polygonsToAdd.isEmpty, true); }); - testWidgets("Updating a polygon by adding points and hole", + testWidgets('Updating a polygon by adding points and hole', (WidgetTester tester) async { - final Polygon p1 = Polygon(polygonId: PolygonId("polygon_1")); - final Polygon p2 = _polygonWithPointsAndHole(PolygonId("polygon_1")); + const Polygon p1 = Polygon(polygonId: PolygonId('polygon_1')); + final Polygon p2 = _polygonWithPointsAndHole(const PolygonId('polygon_1')); await tester.pumpWidget(_mapWithPolygons({p1})); await tester.pumpWidget(_mapWithPolygons({p2})); @@ -289,12 +291,12 @@ void main() { expect(platformGoogleMap.polygonsToAdd.isEmpty, true); }); - testWidgets("Mutate a polygon with points and holes", + testWidgets('Mutate a polygon with points and holes', (WidgetTester tester) async { final Polygon p1 = Polygon( - polygonId: PolygonId("polygon_1"), + polygonId: const PolygonId('polygon_1'), points: _rectPoints(size: 1), - holes: [_rectPoints(size: 0.5)], + holes: >[_rectPoints(size: 0.5)], ); await tester.pumpWidget(_mapWithPolygons({p1})); @@ -303,7 +305,7 @@ void main() { ..addAll(_rectPoints(size: 2)); p1.holes ..clear() - ..addAll([_rectPoints(size: 1)]); + ..addAll(>[_rectPoints(size: 1)]); await tester.pumpWidget(_mapWithPolygons({p1})); final FakePlatformGoogleMap platformGoogleMap = @@ -315,19 +317,19 @@ void main() { expect(platformGoogleMap.polygonsToAdd.isEmpty, true); }); - testWidgets("Multi Update polygons with points and hole", + testWidgets('Multi Update polygons with points and hole', (WidgetTester tester) async { - Polygon p1 = Polygon(polygonId: PolygonId("polygon_1")); + Polygon p1 = const Polygon(polygonId: PolygonId('polygon_1')); Polygon p2 = Polygon( - polygonId: PolygonId("polygon_2"), + polygonId: const PolygonId('polygon_2'), points: _rectPoints(size: 2), - holes: [_rectPoints(size: 1)], + holes: >[_rectPoints(size: 1)], ); final Set prev = {p1, p2}; - p1 = Polygon(polygonId: PolygonId("polygon_1"), visible: false); + p1 = const Polygon(polygonId: PolygonId('polygon_1'), visible: false); p2 = p2.copyWith( pointsParam: _rectPoints(size: 5), - holesParam: [_rectPoints(size: 2)], + holesParam: >[_rectPoints(size: 2)], ); final Set cur = {p1, p2}; @@ -342,21 +344,21 @@ void main() { expect(platformGoogleMap.polygonsToAdd.isEmpty, true); }); - testWidgets("Multi Update polygons with points and hole", + testWidgets('Multi Update polygons with points and hole', (WidgetTester tester) async { Polygon p2 = Polygon( - polygonId: PolygonId("polygon_2"), + polygonId: const PolygonId('polygon_2'), points: _rectPoints(size: 2), - holes: [_rectPoints(size: 1)], + holes: >[_rectPoints(size: 1)], ); - final Polygon p3 = Polygon(polygonId: PolygonId("polygon_3")); + const Polygon p3 = Polygon(polygonId: PolygonId('polygon_3')); final Set prev = {p2, p3}; // p1 is added, p2 is updated, p3 is removed. - final Polygon p1 = _polygonWithPointsAndHole(PolygonId("polygon_1")); + final Polygon p1 = _polygonWithPointsAndHole(const PolygonId('polygon_1')); p2 = p2.copyWith( pointsParam: _rectPoints(size: 5), - holesParam: [_rectPoints(size: 3)], + holesParam: >[_rectPoints(size: 3)], ); final Set cur = {p1, p2}; @@ -375,19 +377,19 @@ void main() { expect(platformGoogleMap.polygonIdsToRemove.first, equals(p3.polygonId)); }); - testWidgets("Partial Update polygons with points and hole", + testWidgets('Partial Update polygons with points and hole', (WidgetTester tester) async { - final Polygon p1 = _polygonWithPointsAndHole(PolygonId("polygon_1")); - final Polygon p2 = Polygon(polygonId: PolygonId("polygon_2")); + final Polygon p1 = _polygonWithPointsAndHole(const PolygonId('polygon_1')); + const Polygon p2 = Polygon(polygonId: PolygonId('polygon_2')); Polygon p3 = Polygon( - polygonId: PolygonId("polygon_3"), + polygonId: const PolygonId('polygon_3'), points: _rectPoints(size: 2), - holes: [_rectPoints(size: 1)], + holes: >[_rectPoints(size: 1)], ); final Set prev = {p1, p2, p3}; p3 = p3.copyWith( pointsParam: _rectPoints(size: 5), - holesParam: [_rectPoints(size: 3)], + holesParam: >[_rectPoints(size: 3)], ); final Set cur = {p1, p2, p3}; diff --git a/packages/google_maps_flutter/google_maps_flutter/test/polyline_updates_test.dart b/packages/google_maps_flutter/google_maps_flutter/test/polyline_updates_test.dart index 01eb2e2ce724..9cbba3a510ca 100644 --- a/packages/google_maps_flutter/google_maps_flutter/test/polyline_updates_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/test/polyline_updates_test.dart @@ -35,7 +35,7 @@ void main() { }); testWidgets('Initializing a polyline', (WidgetTester tester) async { - final Polyline p1 = Polyline(polylineId: PolylineId("polyline_1")); + const Polyline p1 = Polyline(polylineId: PolylineId('polyline_1')); await tester.pumpWidget(_mapWithPolylines({p1})); final FakePlatformGoogleMap platformGoogleMap = @@ -48,9 +48,9 @@ void main() { expect(platformGoogleMap.polylinesToChange.isEmpty, true); }); - testWidgets("Adding a polyline", (WidgetTester tester) async { - final Polyline p1 = Polyline(polylineId: PolylineId("polyline_1")); - final Polyline p2 = Polyline(polylineId: PolylineId("polyline_2")); + testWidgets('Adding a polyline', (WidgetTester tester) async { + const Polyline p1 = Polyline(polylineId: PolylineId('polyline_1')); + const Polyline p2 = Polyline(polylineId: PolylineId('polyline_2')); await tester.pumpWidget(_mapWithPolylines({p1})); await tester.pumpWidget(_mapWithPolylines({p1, p2})); @@ -67,8 +67,8 @@ void main() { expect(platformGoogleMap.polylinesToChange.isEmpty, true); }); - testWidgets("Removing a polyline", (WidgetTester tester) async { - final Polyline p1 = Polyline(polylineId: PolylineId("polyline_1")); + testWidgets('Removing a polyline', (WidgetTester tester) async { + const Polyline p1 = Polyline(polylineId: PolylineId('polyline_1')); await tester.pumpWidget(_mapWithPolylines({p1})); await tester.pumpWidget(_mapWithPolylines({})); @@ -82,10 +82,10 @@ void main() { expect(platformGoogleMap.polylinesToAdd.isEmpty, true); }); - testWidgets("Updating a polyline", (WidgetTester tester) async { - final Polyline p1 = Polyline(polylineId: PolylineId("polyline_1")); - final Polyline p2 = - Polyline(polylineId: PolylineId("polyline_1"), geodesic: true); + testWidgets('Updating a polyline', (WidgetTester tester) async { + const Polyline p1 = Polyline(polylineId: PolylineId('polyline_1')); + const Polyline p2 = + Polyline(polylineId: PolylineId('polyline_1'), geodesic: true); await tester.pumpWidget(_mapWithPolylines({p1})); await tester.pumpWidget(_mapWithPolylines({p2})); @@ -99,10 +99,10 @@ void main() { expect(platformGoogleMap.polylinesToAdd.isEmpty, true); }); - testWidgets("Updating a polyline", (WidgetTester tester) async { - final Polyline p1 = Polyline(polylineId: PolylineId("polyline_1")); - final Polyline p2 = - Polyline(polylineId: PolylineId("polyline_1"), geodesic: true); + testWidgets('Updating a polyline', (WidgetTester tester) async { + const Polyline p1 = Polyline(polylineId: PolylineId('polyline_1')); + const Polyline p2 = + Polyline(polylineId: PolylineId('polyline_1'), geodesic: true); await tester.pumpWidget(_mapWithPolylines({p1})); await tester.pumpWidget(_mapWithPolylines({p2})); @@ -116,10 +116,11 @@ void main() { expect(update.geodesic, true); }); - testWidgets("Mutate a polyline", (WidgetTester tester) async { + testWidgets('Mutate a polyline', (WidgetTester tester) async { + final List _points = [const LatLng(0.0, 0.0)]; final Polyline p1 = Polyline( - polylineId: PolylineId("polyline_1"), - points: [const LatLng(0.0, 0.0)], + polylineId: const PolylineId('polyline_1'), + points: _points, ); await tester.pumpWidget(_mapWithPolylines({p1})); @@ -135,12 +136,12 @@ void main() { expect(platformGoogleMap.polylinesToAdd.isEmpty, true); }); - testWidgets("Multi Update", (WidgetTester tester) async { - Polyline p1 = Polyline(polylineId: PolylineId("polyline_1")); - Polyline p2 = Polyline(polylineId: PolylineId("polyline_2")); + testWidgets('Multi Update', (WidgetTester tester) async { + Polyline p1 = const Polyline(polylineId: PolylineId('polyline_1')); + Polyline p2 = const Polyline(polylineId: PolylineId('polyline_2')); final Set prev = {p1, p2}; - p1 = Polyline(polylineId: PolylineId("polyline_1"), visible: false); - p2 = Polyline(polylineId: PolylineId("polyline_2"), geodesic: true); + p1 = const Polyline(polylineId: PolylineId('polyline_1'), visible: false); + p2 = const Polyline(polylineId: PolylineId('polyline_2'), geodesic: true); final Set cur = {p1, p2}; await tester.pumpWidget(_mapWithPolylines(prev)); @@ -154,14 +155,14 @@ void main() { expect(platformGoogleMap.polylinesToAdd.isEmpty, true); }); - testWidgets("Multi Update", (WidgetTester tester) async { - Polyline p2 = Polyline(polylineId: PolylineId("polyline_2")); - final Polyline p3 = Polyline(polylineId: PolylineId("polyline_3")); + testWidgets('Multi Update', (WidgetTester tester) async { + Polyline p2 = const Polyline(polylineId: PolylineId('polyline_2')); + const Polyline p3 = Polyline(polylineId: PolylineId('polyline_3')); final Set prev = {p2, p3}; // p1 is added, p2 is updated, p3 is removed. - final Polyline p1 = Polyline(polylineId: PolylineId("polyline_1")); - p2 = Polyline(polylineId: PolylineId("polyline_2"), geodesic: true); + const Polyline p1 = Polyline(polylineId: PolylineId('polyline_1')); + p2 = const Polyline(polylineId: PolylineId('polyline_2'), geodesic: true); final Set cur = {p1, p2}; await tester.pumpWidget(_mapWithPolylines(prev)); @@ -179,12 +180,12 @@ void main() { expect(platformGoogleMap.polylineIdsToRemove.first, equals(p3.polylineId)); }); - testWidgets("Partial Update", (WidgetTester tester) async { - final Polyline p1 = Polyline(polylineId: PolylineId("polyline_1")); - final Polyline p2 = Polyline(polylineId: PolylineId("polyline_2")); - Polyline p3 = Polyline(polylineId: PolylineId("polyline_3")); + testWidgets('Partial Update', (WidgetTester tester) async { + const Polyline p1 = Polyline(polylineId: PolylineId('polyline_1')); + const Polyline p2 = Polyline(polylineId: PolylineId('polyline_2')); + Polyline p3 = const Polyline(polylineId: PolylineId('polyline_3')); final Set prev = {p1, p2, p3}; - p3 = Polyline(polylineId: PolylineId("polyline_3"), geodesic: true); + p3 = const Polyline(polylineId: PolylineId('polyline_3'), geodesic: true); final Set cur = {p1, p2, p3}; await tester.pumpWidget(_mapWithPolylines(prev)); @@ -198,11 +199,12 @@ void main() { expect(platformGoogleMap.polylinesToAdd.isEmpty, true); }); - testWidgets("Update non platform related attr", (WidgetTester tester) async { - Polyline p1 = Polyline(polylineId: PolylineId("polyline_1"), onTap: null); + testWidgets('Update non platform related attr', (WidgetTester tester) async { + Polyline p1 = + const Polyline(polylineId: PolylineId('polyline_1'), onTap: null); final Set prev = {p1}; p1 = Polyline( - polylineId: PolylineId("polyline_1"), onTap: () => print(2 + 2)); + polylineId: const PolylineId('polyline_1'), onTap: () => print(2 + 2)); final Set cur = {p1}; await tester.pumpWidget(_mapWithPolylines(prev)); diff --git a/packages/google_maps_flutter/google_maps_flutter/test/tile_overlay_updates_test.dart b/packages/google_maps_flutter/google_maps_flutter/test/tile_overlay_updates_test.dart index 35732da29726..b4586f743296 100644 --- a/packages/google_maps_flutter/google_maps_flutter/test/tile_overlay_updates_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/test/tile_overlay_updates_test.dart @@ -33,8 +33,8 @@ void main() { }); testWidgets('Initializing a tile overlay', (WidgetTester tester) async { - final TileOverlay t1 = - TileOverlay(tileOverlayId: TileOverlayId("tile_overlay_1")); + const TileOverlay t1 = + TileOverlay(tileOverlayId: TileOverlayId('tile_overlay_1')); await tester.pumpWidget(_mapWithTileOverlays({t1})); final FakePlatformGoogleMap platformGoogleMap = @@ -48,11 +48,11 @@ void main() { expect(platformGoogleMap.tileOverlaysToChange.isEmpty, true); }); - testWidgets("Adding a tile overlay", (WidgetTester tester) async { - final TileOverlay t1 = - TileOverlay(tileOverlayId: TileOverlayId("tile_overlay_1")); - final TileOverlay t2 = - TileOverlay(tileOverlayId: TileOverlayId("tile_overlay_2")); + testWidgets('Adding a tile overlay', (WidgetTester tester) async { + const TileOverlay t1 = + TileOverlay(tileOverlayId: TileOverlayId('tile_overlay_1')); + const TileOverlay t2 = + TileOverlay(tileOverlayId: TileOverlayId('tile_overlay_2')); await tester.pumpWidget(_mapWithTileOverlays({t1})); await tester.pumpWidget(_mapWithTileOverlays({t1, t2})); @@ -69,9 +69,9 @@ void main() { expect(platformGoogleMap.tileOverlaysToChange.isEmpty, true); }); - testWidgets("Removing a tile overlay", (WidgetTester tester) async { - final TileOverlay t1 = - TileOverlay(tileOverlayId: TileOverlayId("tile_overlay_1")); + testWidgets('Removing a tile overlay', (WidgetTester tester) async { + const TileOverlay t1 = + TileOverlay(tileOverlayId: TileOverlayId('tile_overlay_1')); await tester.pumpWidget(_mapWithTileOverlays({t1})); await tester.pumpWidget(_mapWithTileOverlays({})); @@ -86,11 +86,11 @@ void main() { expect(platformGoogleMap.tileOverlaysToAdd.isEmpty, true); }); - testWidgets("Updating a tile overlay", (WidgetTester tester) async { - final TileOverlay t1 = - TileOverlay(tileOverlayId: TileOverlayId("tile_overlay_1")); - final TileOverlay t2 = - TileOverlay(tileOverlayId: TileOverlayId("tile_overlay_1"), zIndex: 10); + testWidgets('Updating a tile overlay', (WidgetTester tester) async { + const TileOverlay t1 = + TileOverlay(tileOverlayId: TileOverlayId('tile_overlay_1')); + const TileOverlay t2 = + TileOverlay(tileOverlayId: TileOverlayId('tile_overlay_1'), zIndex: 10); await tester.pumpWidget(_mapWithTileOverlays({t1})); await tester.pumpWidget(_mapWithTileOverlays({t2})); @@ -104,11 +104,11 @@ void main() { expect(platformGoogleMap.tileOverlaysToAdd.isEmpty, true); }); - testWidgets("Updating a tile overlay", (WidgetTester tester) async { - final TileOverlay t1 = - TileOverlay(tileOverlayId: TileOverlayId("tile_overlay_1")); - final TileOverlay t2 = - TileOverlay(tileOverlayId: TileOverlayId("tile_overlay_1"), zIndex: 10); + testWidgets('Updating a tile overlay', (WidgetTester tester) async { + const TileOverlay t1 = + TileOverlay(tileOverlayId: TileOverlayId('tile_overlay_1')); + const TileOverlay t2 = + TileOverlay(tileOverlayId: TileOverlayId('tile_overlay_1'), zIndex: 10); await tester.pumpWidget(_mapWithTileOverlays({t1})); await tester.pumpWidget(_mapWithTileOverlays({t2})); @@ -122,16 +122,16 @@ void main() { expect(update.zIndex, 10); }); - testWidgets("Multi Update", (WidgetTester tester) async { + testWidgets('Multi Update', (WidgetTester tester) async { TileOverlay t1 = - TileOverlay(tileOverlayId: TileOverlayId("tile_overlay_1")); + const TileOverlay(tileOverlayId: TileOverlayId('tile_overlay_1')); TileOverlay t2 = - TileOverlay(tileOverlayId: TileOverlayId("tile_overlay_2")); + const TileOverlay(tileOverlayId: TileOverlayId('tile_overlay_2')); final Set prev = {t1, t2}; - t1 = TileOverlay( - tileOverlayId: TileOverlayId("tile_overlay_1"), visible: false); - t2 = - TileOverlay(tileOverlayId: TileOverlayId("tile_overlay_2"), zIndex: 10); + t1 = const TileOverlay( + tileOverlayId: TileOverlayId('tile_overlay_1'), visible: false); + t2 = const TileOverlay( + tileOverlayId: TileOverlayId('tile_overlay_2'), zIndex: 10); final Set cur = {t1, t2}; await tester.pumpWidget(_mapWithTileOverlays(prev)); @@ -145,18 +145,18 @@ void main() { expect(platformGoogleMap.tileOverlaysToAdd.isEmpty, true); }); - testWidgets("Multi Update", (WidgetTester tester) async { + testWidgets('Multi Update', (WidgetTester tester) async { TileOverlay t2 = - TileOverlay(tileOverlayId: TileOverlayId("tile_overlay_2")); - final TileOverlay t3 = - TileOverlay(tileOverlayId: TileOverlayId("tile_overlay_3")); + const TileOverlay(tileOverlayId: TileOverlayId('tile_overlay_2')); + const TileOverlay t3 = + TileOverlay(tileOverlayId: TileOverlayId('tile_overlay_3')); final Set prev = {t2, t3}; // t1 is added, t2 is updated, t3 is removed. - final TileOverlay t1 = - TileOverlay(tileOverlayId: TileOverlayId("tile_overlay_1")); - t2 = - TileOverlay(tileOverlayId: TileOverlayId("tile_overlay_2"), zIndex: 10); + const TileOverlay t1 = + TileOverlay(tileOverlayId: TileOverlayId('tile_overlay_1')); + t2 = const TileOverlay( + tileOverlayId: TileOverlayId('tile_overlay_2'), zIndex: 10); final Set cur = {t1, t2}; await tester.pumpWidget(_mapWithTileOverlays(prev)); @@ -175,16 +175,16 @@ void main() { equals(t3.tileOverlayId)); }); - testWidgets("Partial Update", (WidgetTester tester) async { - final TileOverlay t1 = - TileOverlay(tileOverlayId: TileOverlayId("tile_overlay_1")); - final TileOverlay t2 = - TileOverlay(tileOverlayId: TileOverlayId("tile_overlay_2")); + testWidgets('Partial Update', (WidgetTester tester) async { + const TileOverlay t1 = + TileOverlay(tileOverlayId: TileOverlayId('tile_overlay_1')); + const TileOverlay t2 = + TileOverlay(tileOverlayId: TileOverlayId('tile_overlay_2')); TileOverlay t3 = - TileOverlay(tileOverlayId: TileOverlayId("tile_overlay_3")); + const TileOverlay(tileOverlayId: TileOverlayId('tile_overlay_3')); final Set prev = {t1, t2, t3}; - t3 = - TileOverlay(tileOverlayId: TileOverlayId("tile_overlay_3"), zIndex: 10); + t3 = const TileOverlay( + tileOverlayId: TileOverlayId('tile_overlay_3'), zIndex: 10); final Set cur = {t1, t2, t3}; await tester.pumpWidget(_mapWithTileOverlays(prev)); diff --git a/script/configs/custom_analysis.yaml b/script/configs/custom_analysis.yaml index 89d061f7a07b..e08024c22ecd 100644 --- a/script/configs/custom_analysis.yaml +++ b/script/configs/custom_analysis.yaml @@ -11,7 +11,6 @@ # TODO(ecosystem): Remove everything from this list. See: # https://github.com/flutter/flutter/issues/76229 -- google_maps_flutter/google_maps_flutter - google_maps_flutter/google_maps_flutter_platform_interface - google_maps_flutter/google_maps_flutter_web - in_app_purchase/in_app_purchase From 3a0e6ed41f4b055183f268ec4255e7a5387d0aa8 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 1 Mar 2022 16:56:22 -0500 Subject: [PATCH 240/600] Roll Flutter from 377d7d768df1 to 8b95cd6f35b7 (2 revisions) (#4968) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index b18e9773eda5..85b0a4d11413 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -377d7d768df19a730b8296165919773cc22be1fe +8b95cd6f35b7737199c2ea3067f9ea5c8daaa447 From f2e8244df1a6c993411288e79009b9c57d4d6d24 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 1 Mar 2022 18:01:20 -0500 Subject: [PATCH 241/600] Roll Flutter from 8b95cd6f35b7 to 1dd24b8af858 (9 revisions) (#4970) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 85b0a4d11413..587992816b7d 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -8b95cd6f35b7737199c2ea3067f9ea5c8daaa447 +1dd24b8af858e1c875da39649061cb5022811430 From bfd9887683c210c8b68fd51e43a10171c776c3d2 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 1 Mar 2022 19:06:25 -0500 Subject: [PATCH 242/600] Roll Flutter from 1dd24b8af858 to bf98767e8e55 (3 revisions) (#4971) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 587992816b7d..069d29d777d6 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -1dd24b8af858e1c875da39649061cb5022811430 +bf98767e8e5506d826eece89a4e63887f1d7baea From 6826dffca160395a2dc82708dbbf6ef2fcc34e14 Mon Sep 17 00:00:00 2001 From: Casey Hillers Date: Tue, 1 Mar 2022 16:58:30 -0800 Subject: [PATCH 243/600] [cirrus] Refresh GCP service account (#4965) --- .cirrus.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index f2372a91f2f5..b283e84f4b91 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -1,4 +1,4 @@ -gcp_credentials: ENCRYPTED[!cc769765170bebc37e0556e2da5915ca64ee37f4ec8c966ec147e2f59578b476c99e457eafce4e2f8b1a4e305f7096b8!] +gcp_credentials: ENCRYPTED[!2c88dee9c9d9805b214c9f7ad8f3bc8fae936cdb0f881d562101151c408c7e024a41222677d5831df90c60d2dd6cd80a!] # Don't run on release tags since it creates O(n^2) tasks where n is the # number of plugins From 3eb85495352c68b3c634fdce9019449d4b3ab7e8 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 1 Mar 2022 20:46:22 -0500 Subject: [PATCH 244/600] Roll Flutter from bf98767e8e55 to 6c818d772a71 (2 revisions) (#4972) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 069d29d777d6..4032c0612efb 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -bf98767e8e5506d826eece89a4e63887f1d7baea +6c818d772a71fb2b07b12274b71e14fd499adfd8 From 508469330a001fd4df5e8c6a769801377ef70a25 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 1 Mar 2022 21:51:19 -0500 Subject: [PATCH 245/600] Roll Flutter from 6c818d772a71 to 8f360c977a61 (2 revisions) (#4974) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 4032c0612efb..4c2362c64bab 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -6c818d772a71fb2b07b12274b71e14fd499adfd8 +8f360c977a618599e6507d6239083a341993d9c6 From 0283a99ce39d947766892cd8d682375bd4c3ce61 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 2 Mar 2022 02:46:23 -0500 Subject: [PATCH 246/600] [in_app_purchase] Update app-facing package to new analysis options (#4969) --- .../in_app_purchase/CHANGELOG.md | 4 + .../in_app_purchase/analysis_options.yaml | 1 - .../example/lib/consumable_store.dart | 13 +- .../in_app_purchase/example/lib/main.dart | 134 +++++++++--------- .../in_app_purchase/example/pubspec.yaml | 4 +- .../in_app_purchase/lib/in_app_purchase.dart | 2 +- .../in_app_purchase/pubspec.yaml | 5 +- .../test/in_app_purchase_test.dart | 58 ++++---- script/configs/custom_analysis.yaml | 1 - 9 files changed, 116 insertions(+), 106 deletions(-) delete mode 100644 packages/in_app_purchase/in_app_purchase/analysis_options.yaml diff --git a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md index 588993a01fd0..134fd9052a63 100644 --- a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.0.1 + +* Internal code cleanup for stricter analysis options. + ## 3.0.0 * **BREAKING CHANGE** Updates `restorePurchases` to emit an empty list of purchases on StoreKit when there are no purchases to restore (same as Android). diff --git a/packages/in_app_purchase/in_app_purchase/analysis_options.yaml b/packages/in_app_purchase/in_app_purchase/analysis_options.yaml deleted file mode 100644 index 5aeb4e7c5e21..000000000000 --- a/packages/in_app_purchase/in_app_purchase/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../../analysis_options_legacy.yaml diff --git a/packages/in_app_purchase/in_app_purchase/example/lib/consumable_store.dart b/packages/in_app_purchase/in_app_purchase/example/lib/consumable_store.dart index 4d10a50e1ee8..448efcf40b51 100644 --- a/packages/in_app_purchase/in_app_purchase/example/lib/consumable_store.dart +++ b/packages/in_app_purchase/in_app_purchase/example/lib/consumable_store.dart @@ -5,13 +5,14 @@ import 'dart:async'; import 'package:shared_preferences/shared_preferences.dart'; +// ignore: avoid_classes_with_only_static_members /// A store of consumable items. /// /// This is a development prototype tha stores consumables in the shared /// preferences. Do not use this in real world apps. class ConsumableStore { static const String _kPrefKey = 'consumables'; - static Future _writes = Future.value(); + static Future _writes = Future.value(); /// Adds a consumable with ID `id` to the store. /// @@ -32,19 +33,19 @@ class ConsumableStore { /// Returns the list of consumables from the store. static Future> load() async { return (await SharedPreferences.getInstance()).getStringList(_kPrefKey) ?? - []; + []; } static Future _doSave(String id) async { - List cached = await load(); - SharedPreferences prefs = await SharedPreferences.getInstance(); + final List cached = await load(); + final SharedPreferences prefs = await SharedPreferences.getInstance(); cached.add(id); await prefs.setStringList(_kPrefKey, cached); } static Future _doConsume(String id) async { - List cached = await load(); - SharedPreferences prefs = await SharedPreferences.getInstance(); + final List cached = await load(); + final SharedPreferences prefs = await SharedPreferences.getInstance(); cached.remove(id); await prefs.setStringList(_kPrefKey, cached); } diff --git a/packages/in_app_purchase/in_app_purchase/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase/example/lib/main.dart index 5d2f04ce8ff0..651652b40c3a 100644 --- a/packages/in_app_purchase/in_app_purchase/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase/example/lib/main.dart @@ -39,10 +39,10 @@ class _MyApp extends StatefulWidget { class _MyAppState extends State<_MyApp> { final InAppPurchase _inAppPurchase = InAppPurchase.instance; late StreamSubscription> _subscription; - List _notFoundIds = []; - List _products = []; - List _purchases = []; - List _consumables = []; + List _notFoundIds = []; + List _products = []; + List _purchases = []; + List _consumables = []; bool _isAvailable = false; bool _purchasePending = false; bool _loading = true; @@ -52,11 +52,12 @@ class _MyAppState extends State<_MyApp> { void initState() { final Stream> purchaseUpdated = _inAppPurchase.purchaseStream; - _subscription = purchaseUpdated.listen((purchaseDetailsList) { + _subscription = + purchaseUpdated.listen((List purchaseDetailsList) { _listenToPurchaseUpdated(purchaseDetailsList); }, onDone: () { _subscription.cancel(); - }, onError: (error) { + }, onError: (Object error) { // handle error here. }); initStoreInfo(); @@ -68,10 +69,10 @@ class _MyAppState extends State<_MyApp> { if (!isAvailable) { setState(() { _isAvailable = isAvailable; - _products = []; - _purchases = []; - _notFoundIds = []; - _consumables = []; + _products = []; + _purchases = []; + _notFoundIds = []; + _consumables = []; _purchasePending = false; _loading = false; }); @@ -79,21 +80,22 @@ class _MyAppState extends State<_MyApp> { } if (Platform.isIOS) { - var iosPlatformAddition = _inAppPurchase - .getPlatformAddition(); + final InAppPurchaseStoreKitPlatformAddition iosPlatformAddition = + _inAppPurchase + .getPlatformAddition(); await iosPlatformAddition.setDelegate(ExamplePaymentQueueDelegate()); } - ProductDetailsResponse productDetailResponse = + final ProductDetailsResponse productDetailResponse = await _inAppPurchase.queryProductDetails(_kProductIds.toSet()); if (productDetailResponse.error != null) { setState(() { _queryProductError = productDetailResponse.error!.message; _isAvailable = isAvailable; _products = productDetailResponse.productDetails; - _purchases = []; + _purchases = []; _notFoundIds = productDetailResponse.notFoundIDs; - _consumables = []; + _consumables = []; _purchasePending = false; _loading = false; }); @@ -105,16 +107,16 @@ class _MyAppState extends State<_MyApp> { _queryProductError = null; _isAvailable = isAvailable; _products = productDetailResponse.productDetails; - _purchases = []; + _purchases = []; _notFoundIds = productDetailResponse.notFoundIDs; - _consumables = []; + _consumables = []; _purchasePending = false; _loading = false; }); return; } - List consumables = await ConsumableStore.load(); + final List consumables = await ConsumableStore.load(); setState(() { _isAvailable = isAvailable; _products = productDetailResponse.productDetails; @@ -128,8 +130,9 @@ class _MyAppState extends State<_MyApp> { @override void dispose() { if (Platform.isIOS) { - var iosPlatformAddition = _inAppPurchase - .getPlatformAddition(); + final InAppPurchaseStoreKitPlatformAddition iosPlatformAddition = + _inAppPurchase + .getPlatformAddition(); iosPlatformAddition.setDelegate(null); } _subscription.cancel(); @@ -138,11 +141,11 @@ class _MyAppState extends State<_MyApp> { @override Widget build(BuildContext context) { - List stack = []; + final List stack = []; if (_queryProductError == null) { stack.add( ListView( - children: [ + children: [ _buildConnectionCheckTile(), _buildProductList(), _buildConsumableBox(), @@ -158,10 +161,10 @@ class _MyAppState extends State<_MyApp> { if (_purchasePending) { stack.add( Stack( - children: [ + children: const [ Opacity( opacity: 0.3, - child: const ModalBarrier(dismissible: false, color: Colors.grey), + child: ModalBarrier(dismissible: false, color: Colors.grey), ), Center( child: CircularProgressIndicator(), @@ -185,7 +188,7 @@ class _MyAppState extends State<_MyApp> { Card _buildConnectionCheckTile() { if (_loading) { - return Card(child: ListTile(title: const Text('Trying to connect...'))); + return const Card(child: ListTile(title: Text('Trying to connect...'))); } final Widget storeHeader = ListTile( leading: Icon(_isAvailable ? Icons.check : Icons.block, @@ -196,8 +199,8 @@ class _MyAppState extends State<_MyApp> { final List children = [storeHeader]; if (!_isAvailable) { - children.addAll([ - Divider(), + children.addAll([ + const Divider(), ListTile( title: Text('Not connected', style: TextStyle(color: ThemeData.light().errorColor)), @@ -211,29 +214,30 @@ class _MyAppState extends State<_MyApp> { Card _buildProductList() { if (_loading) { - return Card( - child: (ListTile( + return const Card( + child: ListTile( leading: CircularProgressIndicator(), - title: Text('Fetching products...')))); + title: Text('Fetching products...'))); } if (!_isAvailable) { - return Card(); + return const Card(); } - final ListTile productHeader = ListTile(title: Text('Products for Sale')); - List productList = []; + const ListTile productHeader = ListTile(title: Text('Products for Sale')); + final List productList = []; if (_notFoundIds.isNotEmpty) { productList.add(ListTile( title: Text('[${_notFoundIds.join(", ")}] not found', style: TextStyle(color: ThemeData.light().errorColor)), - subtitle: Text( + subtitle: const Text( 'This app needs special configuration to run. Please see example/README.md for instructions.'))); } // This loading previous purchases code is just a demo. Please do not use this as it is. // In your app you should always verify the purchase data using the `verificationData` inside the [PurchaseDetails] object before trusting it. // We recommend that you use your own server to verify the purchase data. - Map purchases = - Map.fromEntries(_purchases.map((PurchaseDetails purchase) { + final Map purchases = + Map.fromEntries( + _purchases.map((PurchaseDetails purchase) { if (purchase.pendingCompletePurchase) { _inAppPurchase.completePurchase(purchase); } @@ -241,7 +245,7 @@ class _MyAppState extends State<_MyApp> { })); productList.addAll(_products.map( (ProductDetails productDetails) { - PurchaseDetails? previousPurchase = purchases[productDetails.id]; + final PurchaseDetails? previousPurchase = purchases[productDetails.id]; return ListTile( title: Text( productDetails.title, @@ -252,7 +256,7 @@ class _MyAppState extends State<_MyApp> { trailing: previousPurchase != null ? IconButton( onPressed: () => confirmPriceChange(context), - icon: Icon(Icons.upgrade)) + icon: const Icon(Icons.upgrade)) : TextButton( child: Text(productDetails.price), style: TextButton.styleFrom( @@ -267,7 +271,7 @@ class _MyAppState extends State<_MyApp> { // verify the latest status of you your subscription by using server side receipt validation // and update the UI accordingly. The subscription purchase status shown // inside the app may not be accurate. - final oldSubscription = + final GooglePlayPurchaseDetails? oldSubscription = _getOldSubscription(productDetails, purchases); purchaseParam = GooglePlayPurchaseParam( @@ -301,26 +305,26 @@ class _MyAppState extends State<_MyApp> { )); return Card( - child: - Column(children: [productHeader, Divider()] + productList)); + child: Column( + children: [productHeader, const Divider()] + productList)); } Card _buildConsumableBox() { if (_loading) { - return Card( - child: (ListTile( + return const Card( + child: ListTile( leading: CircularProgressIndicator(), - title: Text('Fetching consumables...')))); + title: Text('Fetching consumables...'))); } if (!_isAvailable || _notFoundIds.contains(_kConsumableId)) { - return Card(); + return const Card(); } - final ListTile consumableHeader = + const ListTile consumableHeader = ListTile(title: Text('Purchased consumables')); final List tokens = _consumables.map((String id) { return GridTile( child: IconButton( - icon: Icon( + icon: const Icon( Icons.stars, size: 42.0, color: Colors.orange, @@ -333,12 +337,12 @@ class _MyAppState extends State<_MyApp> { return Card( child: Column(children: [ consumableHeader, - Divider(), + const Divider(), GridView.count( crossAxisCount: 5, children: tokens, shrinkWrap: true, - padding: EdgeInsets.all(16.0), + padding: const EdgeInsets.all(16.0), ) ])); } @@ -353,9 +357,9 @@ class _MyAppState extends State<_MyApp> { child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.end, - children: [ + children: [ TextButton( - child: Text('Restore purchases'), + child: const Text('Restore purchases'), style: TextButton.styleFrom( backgroundColor: Theme.of(context).primaryColor, primary: Colors.white, @@ -381,11 +385,11 @@ class _MyAppState extends State<_MyApp> { }); } - void deliverProduct(PurchaseDetails purchaseDetails) async { + Future deliverProduct(PurchaseDetails purchaseDetails) async { // IMPORTANT!! Always verify purchase details before delivering the product. if (purchaseDetails.productID == _kConsumableId) { await ConsumableStore.save(purchaseDetails.purchaseID!); - List consumables = await ConsumableStore.load(); + final List consumables = await ConsumableStore.load(); setState(() { _purchasePending = false; _consumables = consumables; @@ -414,8 +418,9 @@ class _MyAppState extends State<_MyApp> { // handle invalid purchase here if _verifyPurchase` failed. } - void _listenToPurchaseUpdated(List purchaseDetailsList) { - purchaseDetailsList.forEach((PurchaseDetails purchaseDetails) async { + Future _listenToPurchaseUpdated( + List purchaseDetailsList) async { + for (final PurchaseDetails purchaseDetails in purchaseDetailsList) { if (purchaseDetails.status == PurchaseStatus.pending) { showPendingUI(); } else { @@ -423,7 +428,7 @@ class _MyAppState extends State<_MyApp> { handleError(purchaseDetails.error!); } else if (purchaseDetails.status == PurchaseStatus.purchased || purchaseDetails.status == PurchaseStatus.restored) { - bool valid = await _verifyPurchase(purchaseDetails); + final bool valid = await _verifyPurchase(purchaseDetails); if (valid) { deliverProduct(purchaseDetails); } else { @@ -443,7 +448,7 @@ class _MyAppState extends State<_MyApp> { await _inAppPurchase.completePurchase(purchaseDetails); } } - }); + } } Future confirmPriceChange(BuildContext context) async { @@ -451,26 +456,27 @@ class _MyAppState extends State<_MyApp> { final InAppPurchaseAndroidPlatformAddition androidAddition = _inAppPurchase .getPlatformAddition(); - var priceChangeConfirmationResult = + final BillingResultWrapper priceChangeConfirmationResult = await androidAddition.launchPriceChangeConfirmationFlow( sku: 'purchaseId', ); if (priceChangeConfirmationResult.responseCode == BillingResponse.ok) { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text('Price change accepted'), )); } else { ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text( priceChangeConfirmationResult.debugMessage ?? - "Price change failed with code ${priceChangeConfirmationResult.responseCode}", + 'Price change failed with code ${priceChangeConfirmationResult.responseCode}', ), )); } } if (Platform.isIOS) { - var iapStoreKitPlatformAddition = _inAppPurchase - .getPlatformAddition(); + final InAppPurchaseStoreKitPlatformAddition iapStoreKitPlatformAddition = + _inAppPurchase + .getPlatformAddition(); await iapStoreKitPlatformAddition.showPriceConsentIfNeeded(); } } @@ -488,11 +494,11 @@ class _MyAppState extends State<_MyApp> { if (productDetails.id == _kSilverSubscriptionId && purchases[_kGoldSubscriptionId] != null) { oldSubscription = - purchases[_kGoldSubscriptionId] as GooglePlayPurchaseDetails; + purchases[_kGoldSubscriptionId]! as GooglePlayPurchaseDetails; } else if (productDetails.id == _kGoldSubscriptionId && purchases[_kSilverSubscriptionId] != null) { oldSubscription = - purchases[_kSilverSubscriptionId] as GooglePlayPurchaseDetails; + purchases[_kSilverSubscriptionId]! as GooglePlayPurchaseDetails; } return oldSubscription; } diff --git a/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml index a75aaa689eea..4a79b190bff9 100644 --- a/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml @@ -9,8 +9,6 @@ environment: dependencies: flutter: sdk: flutter - shared_preferences: ^2.0.0 - in_app_purchase: # When depending on this package from a real application you should use: # in_app_purchase: ^x.y.z @@ -18,6 +16,7 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ + shared_preferences: ^2.0.0 dev_dependencies: flutter_driver: @@ -25,7 +24,6 @@ dev_dependencies: integration_test: sdk: flutter - pedantic: ^1.10.0 flutter: uses-material-design: true diff --git a/packages/in_app_purchase/in_app_purchase/lib/in_app_purchase.dart b/packages/in_app_purchase/in_app_purchase/lib/in_app_purchase.dart index 2a14f2b26ed7..df05d8ce86c3 100644 --- a/packages/in_app_purchase/in_app_purchase/lib/in_app_purchase.dart +++ b/packages/in_app_purchase/in_app_purchase/lib/in_app_purchase.dart @@ -4,8 +4,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; import 'package:in_app_purchase_android/in_app_purchase_android.dart'; +import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart'; export 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart' diff --git a/packages/in_app_purchase/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/pubspec.yaml index 80c150d3f9e2..139d58318780 100644 --- a/packages/in_app_purchase/in_app_purchase/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase description: A Flutter plugin for in-app purchases. Exposes APIs for making in-app purchases through the App Store and Google Play. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 3.0.0 +version: 3.0.1 environment: sdk: ">=2.12.0 <3.0.0" @@ -19,8 +19,8 @@ flutter: dependencies: flutter: sdk: flutter - in_app_purchase_platform_interface: ^1.0.0 in_app_purchase_android: ^0.2.1 + in_app_purchase_platform_interface: ^1.0.0 in_app_purchase_storekit: ^0.3.0+1 dev_dependencies: @@ -30,6 +30,5 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.10.0 plugin_platform_interface: ^2.0.0 test: ^1.16.0 diff --git a/packages/in_app_purchase/in_app_purchase/test/in_app_purchase_test.dart b/packages/in_app_purchase/in_app_purchase/test/in_app_purchase_test.dart index b8c7bd89206b..644d26ed50ad 100644 --- a/packages/in_app_purchase/in_app_purchase/test/in_app_purchase_test.dart +++ b/packages/in_app_purchase/in_app_purchase/test/in_app_purchase_test.dart @@ -65,7 +65,7 @@ void main() { test('queryProductDetails', () async { final ProductDetailsResponse response = - await inAppPurchase.queryProductDetails(Set()); + await inAppPurchase.queryProductDetails({}); expect(response.notFoundIDs.isEmpty, true); expect(response.productDetails.isEmpty, true); expect(fakePlatform.log, [ @@ -87,22 +87,24 @@ void main() { }); test('buyConsumable', () async { - final purchaseParam = PurchaseParam(productDetails: productDetails); + final PurchaseParam purchaseParam = + PurchaseParam(productDetails: productDetails); final bool result = await inAppPurchase.buyConsumable( purchaseParam: purchaseParam, ); expect(result, true); expect(fakePlatform.log, [ - isMethodCall('buyConsumable', arguments: { - "purchaseParam": purchaseParam, - "autoConsume": true, + isMethodCall('buyConsumable', arguments: { + 'purchaseParam': purchaseParam, + 'autoConsume': true, }), ]); }); test('buyConsumable with autoConsume=false', () async { - final purchaseParam = PurchaseParam(productDetails: productDetails); + final PurchaseParam purchaseParam = + PurchaseParam(productDetails: productDetails); final bool result = await inAppPurchase.buyConsumable( purchaseParam: purchaseParam, autoConsume: false, @@ -110,9 +112,9 @@ void main() { expect(result, true); expect(fakePlatform.log, [ - isMethodCall('buyConsumable', arguments: { - "purchaseParam": purchaseParam, - "autoConsume": false, + isMethodCall('buyConsumable', arguments: { + 'purchaseParam': purchaseParam, + 'autoConsume': false, }), ]); }); @@ -138,31 +140,33 @@ void main() { class MockInAppPurchasePlatform extends Fake with MockPlatformInterfaceMixin implements InAppPurchasePlatform { - final List log = []; + final List log = []; @override Future isAvailable() { - log.add(MethodCall('isAvailable')); - return Future.value(true); + log.add(const MethodCall('isAvailable')); + return Future.value(true); } @override Stream> get purchaseStream { - log.add(MethodCall('purchaseStream')); - return Stream.empty(); + log.add(const MethodCall('purchaseStream')); + return const Stream>.empty(); } @override Future queryProductDetails(Set identifiers) { - log.add(MethodCall('queryProductDetails')); - return Future.value( - ProductDetailsResponse(productDetails: [], notFoundIDs: [])); + log.add(const MethodCall('queryProductDetails')); + return Future.value(ProductDetailsResponse( + productDetails: [], + notFoundIDs: [], + )); } @override Future buyNonConsumable({required PurchaseParam purchaseParam}) { - log.add(MethodCall('buyNonConsumable')); - return Future.value(true); + log.add(const MethodCall('buyNonConsumable')); + return Future.value(true); } @override @@ -170,22 +174,22 @@ class MockInAppPurchasePlatform extends Fake required PurchaseParam purchaseParam, bool autoConsume = true, }) { - log.add(MethodCall('buyConsumable', { - "purchaseParam": purchaseParam, - "autoConsume": autoConsume, + log.add(MethodCall('buyConsumable', { + 'purchaseParam': purchaseParam, + 'autoConsume': autoConsume, })); - return Future.value(true); + return Future.value(true); } @override Future completePurchase(PurchaseDetails purchase) { - log.add(MethodCall('completePurchase')); - return Future.value(null); + log.add(const MethodCall('completePurchase')); + return Future.value(null); } @override Future restorePurchases({String? applicationUserName}) { - log.add(MethodCall('restorePurchases')); - return Future.value(null); + log.add(const MethodCall('restorePurchases')); + return Future.value(null); } } diff --git a/script/configs/custom_analysis.yaml b/script/configs/custom_analysis.yaml index e08024c22ecd..23b7f7bbc35b 100644 --- a/script/configs/custom_analysis.yaml +++ b/script/configs/custom_analysis.yaml @@ -13,4 +13,3 @@ # https://github.com/flutter/flutter/issues/76229 - google_maps_flutter/google_maps_flutter_platform_interface - google_maps_flutter/google_maps_flutter_web -- in_app_purchase/in_app_purchase From 643445ed009fd8cdb6b46c2c62c5ed066d403772 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 2 Mar 2022 12:01:25 -0500 Subject: [PATCH 247/600] Roll Flutter from 8f360c977a61 to 03ed3c911cd1 (2 revisions) (#4976) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 4c2362c64bab..66fe604c7363 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -8f360c977a618599e6507d6239083a341993d9c6 +03ed3c911cd1ee20397d69a70048dcc881317008 From dcade2f81e89ccee9f2aa73d757d7d5c73f7614d Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 2 Mar 2022 13:06:24 -0500 Subject: [PATCH 248/600] Roll Flutter from 03ed3c911cd1 to c8a930b6967e (1 revision) (#4977) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 66fe604c7363..3f7f00e53458 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -03ed3c911cd1ee20397d69a70048dcc881317008 +c8a930b6967e9275e75b4b3efc54afe63d4b4aea From 73d349f61f92ca6261ed9e830b27cb2d27cc43e6 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 2 Mar 2022 14:11:12 -0500 Subject: [PATCH 249/600] Roll Flutter from c8a930b6967e to 7372ad17f654 (2 revisions) (#4978) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 3f7f00e53458..be9028f9ddcb 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c8a930b6967e9275e75b4b3efc54afe63d4b4aea +7372ad17f654db4b7fec865b3e5032536617a06b From fe7ace5f93c000305d6a50910cba577bb17d3637 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 2 Mar 2022 15:16:23 -0500 Subject: [PATCH 250/600] Roll Flutter from 7372ad17f654 to f8eee10f96fe (2 revisions) (#4979) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index be9028f9ddcb..d4cef290c277 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -7372ad17f654db4b7fec865b3e5032536617a06b +f8eee10f96fea79a80c2a404b78827f4a1feeb6a From e277c1a43f7d09a867c6f5f8b44d4504b66aa691 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 2 Mar 2022 16:21:24 -0500 Subject: [PATCH 251/600] Roll Flutter from f8eee10f96fe to a13f893d9fa8 (1 revision) (#4980) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index d4cef290c277..5c67005dd962 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -f8eee10f96fea79a80c2a404b78827f4a1feeb6a +a13f893d9fa8a0ae5625458c646d6c09651c729b From 6ed6b61eeb4ecc871a945cb73a93f6e901701b5d Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 2 Mar 2022 17:26:30 -0500 Subject: [PATCH 252/600] Roll Flutter from a13f893d9fa8 to 87f5f4685c62 (3 revisions) (#4981) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 5c67005dd962..f58be45ecad8 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -a13f893d9fa8a0ae5625458c646d6c09651c729b +87f5f4685c6239d66455f7253210de29a82182ab From c47c35d703dcd7598fff1ef628bb4f8d88fba22e Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 3 Mar 2022 22:31:22 -0500 Subject: [PATCH 253/600] Roll Flutter from 87f5f4685c62 to f63e0c8d4682 (1 revision) (#4982) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index f58be45ecad8..a76b27a3dd0f 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -87f5f4685c6239d66455f7253210de29a82182ab +f63e0c8d46826e79f671c9b1e9580ecddcd8f3d7 From 129be82cb39c909828afc1c30b9d215edf7a2118 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 3 Mar 2022 23:36:22 -0500 Subject: [PATCH 254/600] Roll Flutter from f63e0c8d4682 to ae9a796cfae1 (19 revisions) (#4993) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index a76b27a3dd0f..afacd760ffbc 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -f63e0c8d46826e79f671c9b1e9580ecddcd8f3d7 +ae9a796cfae1ee42e0242d2924dbc026ce1a7f18 From ab66a4bf3c4e43fb4a445222580314050ffc19cf Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 4 Mar 2022 08:01:24 -0500 Subject: [PATCH 255/600] Roll Flutter from ae9a796cfae1 to 646c22085e52 (2 revisions) (#4997) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index afacd760ffbc..70939b55e775 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ae9a796cfae1ee42e0242d2924dbc026ce1a7f18 +646c22085e52be308e1700650130f5d0940465fd From bed9aa79ca1050bbf6e152b49ab62c0194d434ac Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 4 Mar 2022 10:56:19 -0500 Subject: [PATCH 256/600] Roll Flutter from 646c22085e52 to fbf6a58bf19c (1 revision) (#4998) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 70939b55e775..57e681ad1a34 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -646c22085e52be308e1700650130f5d0940465fd +fbf6a58bf19c52b6b2dba2dd218f046eedeabc6d From 77e841d5c88178f35428be0c3d274d0f05301c6d Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 4 Mar 2022 11:01:52 -0500 Subject: [PATCH 257/600] [file_selector] Add macOS support (#4381) Brings file_selector_macos into flutter/plugins from FDE, with the following changes: - Refactored slightly to allow for unit tests of almost all of the native code - Added native unit test coverage - Translated to Swift, to follow repo conventions for macOS plugins - Added an in-package example (almost an exact duplicate of the app-facing version, but written against the platform interface, as is our current practice) - Moved to an in-package method channel. As part of that, moved the flattening of type groups from Swift to Dart. Does not currently include native UI tests to allow for end-to-end testing (since Flutter integration tests can't be used). They should be added later (they are currently blocked on https://github.com/flutter/flutter/issues/90673), but the unit tests give substantial coverage, making it substantially better to move the plugin now to get those tests running. macOS portion of https://github.com/flutter/flutter/issues/70221 --- .cirrus.yml | 2 +- .../file_selector_macos/.gitignore | 5 + .../file_selector_macos/.metadata | 10 + .../file_selector/file_selector_macos/AUTHORS | 6 + .../file_selector_macos/CHANGELOG.md | 26 + .../file_selector/file_selector_macos/LICENSE | 25 + .../file_selector_macos/README.md | 34 + .../file_selector_macos/example/.gitignore | 48 ++ .../file_selector_macos/example/.metadata | 10 + .../file_selector_macos/example/README.md | 4 + .../example/lib/get_directory_page.dart | 77 ++ .../example/lib/home_page.dart | 57 ++ .../file_selector_macos/example/lib/main.dart | 37 + .../example/lib/open_image_page.dart | 87 ++ .../lib/open_multiple_images_page.dart | 99 +++ .../example/lib/open_text_page.dart | 84 ++ .../example/lib/save_text_page.dart | 78 ++ .../example/macos/.gitignore | 6 + .../macos/Flutter/Flutter-Debug.xcconfig | 2 + .../macos/Flutter/Flutter-Release.xcconfig | 2 + .../file_selector_macos/example/macos/Podfile | 40 + .../macos/Runner.xcodeproj/project.pbxproj | 767 ++++++++++++++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 107 +++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/macos/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 68 ++ .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 46993 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 3276 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 1429 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 5933 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1243 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 14800 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 1874 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 339 ++++++++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 14 + .../example/macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 19 + .../example/macos/Runner/Release.entitlements | 10 + .../example/macos/RunnerTests/Info.plist | 22 + .../macos/RunnerTests/RunnerTests.swift | 283 +++++++ .../file_selector_macos/example/pubspec.yaml | 27 + .../lib/file_selector_macos.dart | 121 +++ .../macos/Classes/FileSelectorPlugin.swift | 218 +++++ .../macos/file_selector_macos.podspec | 21 + .../file_selector_macos/pubspec.yaml | 25 + .../test/file_selector_macos_test.dart | 288 +++++++ script/configs/exclude_integration_macos.yaml | 2 + 53 files changed, 3171 insertions(+), 1 deletion(-) create mode 100644 packages/file_selector/file_selector_macos/.gitignore create mode 100644 packages/file_selector/file_selector_macos/.metadata create mode 100644 packages/file_selector/file_selector_macos/AUTHORS create mode 100644 packages/file_selector/file_selector_macos/CHANGELOG.md create mode 100644 packages/file_selector/file_selector_macos/LICENSE create mode 100644 packages/file_selector/file_selector_macos/README.md create mode 100644 packages/file_selector/file_selector_macos/example/.gitignore create mode 100644 packages/file_selector/file_selector_macos/example/.metadata create mode 100644 packages/file_selector/file_selector_macos/example/README.md create mode 100644 packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart create mode 100644 packages/file_selector/file_selector_macos/example/lib/home_page.dart create mode 100644 packages/file_selector/file_selector_macos/example/lib/main.dart create mode 100644 packages/file_selector/file_selector_macos/example/lib/open_image_page.dart create mode 100644 packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart create mode 100644 packages/file_selector/file_selector_macos/example/lib/open_text_page.dart create mode 100644 packages/file_selector/file_selector_macos/example/lib/save_text_page.dart create mode 100644 packages/file_selector/file_selector_macos/example/macos/.gitignore create mode 100644 packages/file_selector/file_selector_macos/example/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 packages/file_selector/file_selector_macos/example/macos/Flutter/Flutter-Release.xcconfig create mode 100644 packages/file_selector/file_selector_macos/example/macos/Podfile create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner.xcodeproj/project.pbxproj create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/AppDelegate.swift create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Configs/Debug.xcconfig create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Configs/Release.xcconfig create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Configs/Warnings.xcconfig create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/DebugProfile.entitlements create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Info.plist create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/MainFlutterWindow.swift create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Release.entitlements create mode 100644 packages/file_selector/file_selector_macos/example/macos/RunnerTests/Info.plist create mode 100644 packages/file_selector/file_selector_macos/example/macos/RunnerTests/RunnerTests.swift create mode 100644 packages/file_selector/file_selector_macos/example/pubspec.yaml create mode 100644 packages/file_selector/file_selector_macos/lib/file_selector_macos.dart create mode 100644 packages/file_selector/file_selector_macos/macos/Classes/FileSelectorPlugin.swift create mode 100644 packages/file_selector/file_selector_macos/macos/file_selector_macos.podspec create mode 100644 packages/file_selector/file_selector_macos/pubspec.yaml create mode 100644 packages/file_selector/file_selector_macos/test/file_selector_macos_test.dart create mode 100644 script/configs/exclude_integration_macos.yaml diff --git a/.cirrus.yml b/.cirrus.yml index b283e84f4b91..1184ac7e00a1 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -338,4 +338,4 @@ task: native_test_script: - ./script/tool_runner.sh native-test --macos drive_script: - - ./script/tool_runner.sh drive-examples --macos + - ./script/tool_runner.sh drive-examples --macos --exclude=script/configs/exclude_integration_macos.yaml diff --git a/packages/file_selector/file_selector_macos/.gitignore b/packages/file_selector/file_selector_macos/.gitignore new file mode 100644 index 000000000000..0393a47ff732 --- /dev/null +++ b/packages/file_selector/file_selector_macos/.gitignore @@ -0,0 +1,5 @@ +.dart_tool +.packages +.flutter-plugins +.flutter-plugins-dependencies +pubspec.lock diff --git a/packages/file_selector/file_selector_macos/.metadata b/packages/file_selector/file_selector_macos/.metadata new file mode 100644 index 000000000000..720a4596c087 --- /dev/null +++ b/packages/file_selector/file_selector_macos/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 6d1c244b79f3a2747281f718297ce248bd5ad099 + channel: master + +project_type: plugin diff --git a/packages/file_selector/file_selector_macos/AUTHORS b/packages/file_selector/file_selector_macos/AUTHORS new file mode 100644 index 000000000000..557dff97933b --- /dev/null +++ b/packages/file_selector/file_selector_macos/AUTHORS @@ -0,0 +1,6 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. diff --git a/packages/file_selector/file_selector_macos/CHANGELOG.md b/packages/file_selector/file_selector_macos/CHANGELOG.md new file mode 100644 index 000000000000..794d056811f4 --- /dev/null +++ b/packages/file_selector/file_selector_macos/CHANGELOG.md @@ -0,0 +1,26 @@ +## 0.8.2 + +* Moves source to flutter/plugins. +* Adds native unit tests. +* Converts native implementation to Swift. +* Switches to an internal method channel implementation. + +## 0.0.4+1 + +* Update README + +## 0.0.4 + +* Treat empty filter lists the same as null. + +## 0.0.3 + +* Fix README + +## 0.0.2 + +* Update SDK constraint to signal compatibility with null safety. + +## 0.0.1 + +* Initial macOS implementation of `file_selector`. diff --git a/packages/file_selector/file_selector_macos/LICENSE b/packages/file_selector/file_selector_macos/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/file_selector/file_selector_macos/LICENSE @@ -0,0 +1,25 @@ +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/file_selector/file_selector_macos/README.md b/packages/file_selector/file_selector_macos/README.md new file mode 100644 index 000000000000..efa5272149be --- /dev/null +++ b/packages/file_selector/file_selector_macos/README.md @@ -0,0 +1,34 @@ +# file\_selector\_macos + +The macOS implementation of [`file_selector`][1]. + +## Usage + +### Importing the package + +This implementation has not yet been endorsed, meaning that you need to +[depend on `file_selector_macos`][2] in addition to +[depending on `file_selector`][3]. + +Once your pubspec includes the macOS implementation, you can use the +`file_selector` APIs normally. You should not use the `file_selector_macos` +APIs directly. + +### Entitlements + +You will need to [add an entitlement][4] for either read-only access: +``` + com.apple.security.files.user-selected.read-only + +``` +or read/write access: +``` + com.apple.security.files.user-selected.read-write + +``` +depending on your use case. + +[1]: https://pub.dev/packages/file_selector +[2]: https://pub.dev/packages/file_selector_macos/install +[3]: https://pub.dev/packages/file_selector/install +[4]: https://flutter.dev/desktop#entitlements-and-the-app-sandbox diff --git a/packages/file_selector/file_selector_macos/example/.gitignore b/packages/file_selector/file_selector_macos/example/.gitignore new file mode 100644 index 000000000000..7abd0753cfc3 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/.gitignore @@ -0,0 +1,48 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Currently only web supported +android/ +ios/ + +# Exceptions to above rules. +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages diff --git a/packages/file_selector/file_selector_macos/example/.metadata b/packages/file_selector/file_selector_macos/example/.metadata new file mode 100644 index 000000000000..897381f2373f --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 7736f3bc90270dcb0480db2ccffbf1d13c28db85 + channel: dev + +project_type: app diff --git a/packages/file_selector/file_selector_macos/example/README.md b/packages/file_selector/file_selector_macos/example/README.md new file mode 100644 index 000000000000..782fe679fcb0 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/README.md @@ -0,0 +1,4 @@ +# `file_selector_macos` example + +Demonstrates macOS implementation of the +[`file_selector` plugin](https://pub.dev/packages/file_selector). diff --git a/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart b/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart new file mode 100644 index 000000000000..0e55df8ce622 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart @@ -0,0 +1,77 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select a directory using `getDirectoryPath`, +/// then displays the selected directory in a dialog. +class GetDirectoryPage extends StatelessWidget { + Future _getDirectoryPath(BuildContext context) async { + const String confirmButtonText = 'Choose'; + final String? directoryPath = + await FileSelectorPlatform.instance.getDirectoryPath( + confirmButtonText: confirmButtonText, + ); + if (directoryPath == null) { + // Operation was canceled by the user. + return; + } + await showDialog( + context: context, + builder: (BuildContext context) => TextDisplay(directoryPath), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Open a text file'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Colors.blue, + onPrimary: Colors.white, + ), + child: const Text('Press to ask user to choose a directory'), + onPressed: () => _getDirectoryPath(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays a text file in a dialog. +class TextDisplay extends StatelessWidget { + /// Creates a `TextDisplay`. + const TextDisplay(this.directoryPath); + + /// The path selected in the dialog. + final String directoryPath; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text('Selected Directory'), + content: Scrollbar( + child: SingleChildScrollView( + child: Text(directoryPath), + ), + ), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () => Navigator.pop(context), + ), + ], + ); + } +} diff --git a/packages/file_selector/file_selector_macos/example/lib/home_page.dart b/packages/file_selector/file_selector_macos/example/lib/home_page.dart new file mode 100644 index 000000000000..958680be0e3b --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/lib/home_page.dart @@ -0,0 +1,57 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; + +/// Home Page of the application. +class HomePage extends StatelessWidget { + @override + Widget build(BuildContext context) { + final ButtonStyle style = ElevatedButton.styleFrom( + primary: Colors.blue, + onPrimary: Colors.white, + ); + return Scaffold( + appBar: AppBar( + title: const Text('File Selector Demo Home Page'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: style, + child: const Text('Open a text file'), + onPressed: () => Navigator.pushNamed(context, '/open/text'), + ), + const SizedBox(height: 10), + ElevatedButton( + style: style, + child: const Text('Open an image'), + onPressed: () => Navigator.pushNamed(context, '/open/image'), + ), + const SizedBox(height: 10), + ElevatedButton( + style: style, + child: const Text('Open multiple images'), + onPressed: () => Navigator.pushNamed(context, '/open/images'), + ), + const SizedBox(height: 10), + ElevatedButton( + style: style, + child: const Text('Save a file'), + onPressed: () => Navigator.pushNamed(context, '/save/text'), + ), + const SizedBox(height: 10), + ElevatedButton( + style: style, + child: const Text('Open a get directory dialog'), + onPressed: () => Navigator.pushNamed(context, '/directory'), + ), + ], + ), + ), + ); + } +} diff --git a/packages/file_selector/file_selector_macos/example/lib/main.dart b/packages/file_selector/file_selector_macos/example/lib/main.dart new file mode 100644 index 000000000000..a49ebac1aea5 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/lib/main.dart @@ -0,0 +1,37 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:example/get_directory_page.dart'; +import 'package:example/home_page.dart'; +import 'package:example/open_image_page.dart'; +import 'package:example/open_multiple_images_page.dart'; +import 'package:example/open_text_page.dart'; +import 'package:example/save_text_page.dart'; +import 'package:flutter/material.dart'; + +void main() { + runApp(MyApp()); +} + +/// MyApp is the Main Application. +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'File Selector Demo', + theme: ThemeData( + primarySwatch: Colors.blue, + visualDensity: VisualDensity.adaptivePlatformDensity, + ), + home: HomePage(), + routes: { + '/open/image': (BuildContext context) => OpenImagePage(), + '/open/images': (BuildContext context) => OpenMultipleImagesPage(), + '/open/text': (BuildContext context) => OpenTextPage(), + '/save/text': (BuildContext context) => SaveTextPage(), + '/directory': (BuildContext context) => GetDirectoryPage(), + }, + ); + } +} diff --git a/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart new file mode 100644 index 000000000000..aaf083603e72 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart @@ -0,0 +1,87 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io'; + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select an image file using +/// `openFiles`, then displays the selected images in a gallery dialog. +class OpenImagePage extends StatelessWidget { + Future _openImageFile(BuildContext context) async { + final XTypeGroup typeGroup = XTypeGroup( + label: 'images', + extensions: ['jpg', 'png'], + ); + final XFile? file = await FileSelectorPlatform.instance + .openFile(acceptedTypeGroups: [typeGroup]); + if (file == null) { + // Operation was canceled by the user. + return; + } + final String fileName = file.name; + final String filePath = file.path; + + await showDialog( + context: context, + builder: (BuildContext context) => ImageDisplay(fileName, filePath), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Open an image'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Colors.blue, + onPrimary: Colors.white, + ), + child: const Text('Press to open an image file(png, jpg)'), + onPressed: () => _openImageFile(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays an image in a dialog. +class ImageDisplay extends StatelessWidget { + /// Default Constructor. + const ImageDisplay(this.fileName, this.filePath); + + /// The name of the selected file. + final String fileName; + + /// The path to the selected file. + final String filePath; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(fileName), + // On web the filePath is a blob url + // while on other platforms it is a system path. + content: kIsWeb ? Image.network(filePath) : Image.file(File(filePath)), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ); + } +} diff --git a/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart new file mode 100644 index 000000000000..a030b8b4b10b --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart @@ -0,0 +1,99 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io'; + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select multiple image files using +/// `openFiles`, then displays the selected images in a gallery dialog. +class OpenMultipleImagesPage extends StatelessWidget { + Future _openImageFile(BuildContext context) async { + final XTypeGroup jpgsTypeGroup = XTypeGroup( + label: 'JPEGs', + extensions: ['jpg', 'jpeg'], + ); + final XTypeGroup pngTypeGroup = XTypeGroup( + label: 'PNGs', + extensions: ['png'], + ); + final List files = await FileSelectorPlatform.instance + .openFiles(acceptedTypeGroups: [ + jpgsTypeGroup, + pngTypeGroup, + ]); + if (files.isEmpty) { + // Operation was canceled by the user. + return; + } + await showDialog( + context: context, + builder: (BuildContext context) => MultipleImagesDisplay(files), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Open multiple images'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Colors.blue, + onPrimary: Colors.white, + ), + child: const Text('Press to open multiple images (png, jpg)'), + onPressed: () => _openImageFile(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays a text file in a dialog. +class MultipleImagesDisplay extends StatelessWidget { + /// Default Constructor. + const MultipleImagesDisplay(this.files); + + /// The files containing the images. + final List files; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text('Gallery'), + // On web the filePath is a blob url + // while on other platforms it is a system path. + content: Center( + child: Row( + children: [ + ...files.map( + (XFile file) => Flexible( + child: kIsWeb + ? Image.network(file.path) + : Image.file(File(file.path))), + ) + ], + ), + ), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ); + } +} diff --git a/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart new file mode 100644 index 000000000000..fa281a0020d5 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart @@ -0,0 +1,84 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select a text file using `openFile`, then +/// displays its contents in a dialog. +class OpenTextPage extends StatelessWidget { + Future _openTextFile(BuildContext context) async { + final XTypeGroup typeGroup = XTypeGroup( + label: 'text', + extensions: ['txt', 'json'], + ); + final XFile? file = await FileSelectorPlatform.instance + .openFile(acceptedTypeGroups: [typeGroup]); + if (file == null) { + // Operation was canceled by the user. + return; + } + final String fileName = file.name; + final String fileContent = await file.readAsString(); + + await showDialog( + context: context, + builder: (BuildContext context) => TextDisplay(fileName, fileContent), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Open a text file'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Colors.blue, + onPrimary: Colors.white, + ), + child: const Text('Press to open a text file (json, txt)'), + onPressed: () => _openTextFile(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays a text file in a dialog. +class TextDisplay extends StatelessWidget { + /// Default Constructor. + const TextDisplay(this.fileName, this.fileContent); + + /// The name of the selected file. + final String fileName; + + /// The contents of the text file. + final String fileContent; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(fileName), + content: Scrollbar( + child: SingleChildScrollView( + child: Text(fileContent), + ), + ), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () => Navigator.pop(context), + ), + ], + ); + } +} diff --git a/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart b/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart new file mode 100644 index 000000000000..3989c62b7442 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart @@ -0,0 +1,78 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:typed_data'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select a save location using `getSavePath`, +/// then writes text to a file at that location. +class SaveTextPage extends StatelessWidget { + final TextEditingController _nameController = TextEditingController(); + final TextEditingController _contentController = TextEditingController(); + + Future _saveFile() async { + final String fileName = _nameController.text; + final String? path = await FileSelectorPlatform.instance.getSavePath( + suggestedName: fileName, + ); + if (path == null) { + // Operation was canceled by the user. + return; + } + final String text = _contentController.text; + final Uint8List fileData = Uint8List.fromList(text.codeUnits); + const String fileMimeType = 'text/plain'; + final XFile textFile = + XFile.fromData(fileData, mimeType: fileMimeType, name: fileName); + await textFile.saveTo(path); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Save text into a file'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 300, + child: TextField( + minLines: 1, + maxLines: 12, + controller: _nameController, + decoration: const InputDecoration( + hintText: '(Optional) Suggest File Name', + ), + ), + ), + Container( + width: 300, + child: TextField( + minLines: 1, + maxLines: 12, + controller: _contentController, + decoration: const InputDecoration( + hintText: 'Enter File Contents', + ), + ), + ), + const SizedBox(height: 10), + ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Colors.blue, + onPrimary: Colors.white, + ), + child: const Text('Press to save a text file'), + onPressed: _saveFile, + ), + ], + ), + ), + ); + } +} diff --git a/packages/file_selector/file_selector_macos/example/macos/.gitignore b/packages/file_selector/file_selector_macos/example/macos/.gitignore new file mode 100644 index 000000000000..d2fd3772308c --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/.gitignore @@ -0,0 +1,6 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/xcuserdata/ diff --git a/packages/file_selector/file_selector_macos/example/macos/Flutter/Flutter-Debug.xcconfig b/packages/file_selector/file_selector_macos/example/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 000000000000..4b81f9b2d200 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/packages/file_selector/file_selector_macos/example/macos/Flutter/Flutter-Release.xcconfig b/packages/file_selector/file_selector_macos/example/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 000000000000..5caa9d1579e4 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/packages/file_selector/file_selector_macos/example/macos/Podfile b/packages/file_selector/file_selector_macos/example/macos/Podfile new file mode 100644 index 000000000000..dade8dfad0dc --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Podfile @@ -0,0 +1,40 @@ +platform :osx, '10.11' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner.xcodeproj/project.pbxproj b/packages/file_selector/file_selector_macos/example/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..fa8d272d4ee0 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,767 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 338EA5D426EFE72B0071837A /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 338EA5D326EFE72B0071837A /* RunnerTests.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 4CE8B69FE511476B98B4816C /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B61FB9CDECD72211FAB708CA /* Pods_Runner.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 338EA5D626EFE72B0071837A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC10EC2044A3C60003C045; + remoteInfo = Runner; + }; + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 338EA5D126EFE72B0071837A /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 338EA5D326EFE72B0071837A /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 338EA5D526EFE72B0071837A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + B61FB9CDECD72211FAB708CA /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BA03A11192D3E8EEA888D495 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + C03B6D624A05212E07A5D41E /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + D921BBD60B6562B7A5F559AC /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 338EA5CE26EFE72B0071837A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4CE8B69FE511476B98B4816C /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 338EA5D226EFE72B0071837A /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 338EA5D326EFE72B0071837A /* RunnerTests.swift */, + 338EA5D526EFE72B0071837A /* Info.plist */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 338EA5D226EFE72B0071837A /* RunnerTests */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + CAED34175B65FC224CC4F18C /* Pods */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* example.app */, + 338EA5D126EFE72B0071837A /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + CAED34175B65FC224CC4F18C /* Pods */ = { + isa = PBXGroup; + children = ( + D921BBD60B6562B7A5F559AC /* Pods-Runner.debug.xcconfig */, + C03B6D624A05212E07A5D41E /* Pods-Runner.release.xcconfig */, + BA03A11192D3E8EEA888D495 /* Pods-Runner.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + B61FB9CDECD72211FAB708CA /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 338EA5D026EFE72B0071837A /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 338EA5DB26EFE72B0071837A /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 338EA5CD26EFE72B0071837A /* Sources */, + 338EA5CE26EFE72B0071837A /* Frameworks */, + 338EA5CF26EFE72B0071837A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 338EA5D726EFE72B0071837A /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 338EA5D126EFE72B0071837A /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 8F1744F37738365955F17998 /* [CP] Check Pods Manifest.lock */, + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + A8D2084B0509A3B3053F3AF7 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1250; + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = "The Flutter Authors"; + TargetAttributes = { + 338EA5D026EFE72B0071837A = { + CreatedOnToolsVersion = 12.5; + TestTargetID = 33CC10EC2044A3C60003C045; + }; + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + 338EA5D026EFE72B0071837A /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 338EA5CF26EFE72B0071837A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; + 8F1744F37738365955F17998 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + A8D2084B0509A3B3053F3AF7 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 338EA5CD26EFE72B0071837A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 338EA5D426EFE72B0071837A /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 338EA5D726EFE72B0071837A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC10EC2044A3C60003C045 /* Runner */; + targetProxy = 338EA5D626EFE72B0071837A /* PBXContainerItemProxy */; + }; + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 338EA5D826EFE72B0071837A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = RunnerTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/Contents/MacOS/example"; + }; + name = Debug; + }; + 338EA5D926EFE72B0071837A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = RunnerTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/Contents/MacOS/example"; + }; + name = Release; + }; + 338EA5DA26EFE72B0071837A /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = RunnerTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/Contents/MacOS/example"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 338EA5DB26EFE72B0071837A /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 338EA5D826EFE72B0071837A /* Debug */, + 338EA5D926EFE72B0071837A /* Release */, + 338EA5DA26EFE72B0071837A /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/file_selector/file_selector_macos/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/file_selector/file_selector_macos/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000000..57d6538229d5 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/packages/file_selector/file_selector_macos/example/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..21a3cc14c74e --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/file_selector/file_selector_macos/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner/AppDelegate.swift b/packages/file_selector/file_selector_macos/example/macos/Runner/AppDelegate.swift new file mode 100644 index 000000000000..5cec4c48f620 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000000..a2ec33f19f11 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..3c4935a7ca84f0976aca34b7f2895d65fb94d1ea GIT binary patch literal 46993 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 literal 0 HcmV?d00001 diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..ed4cc16421680a50164ba74381b4b35ceaa0ccfc GIT binary patch literal 3276 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 N;OXk;vd$@?2>?>Ex^Vyi literal 0 HcmV?d00001 diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 0000000000000000000000000000000000000000..bcbf36df2f2aaaa0a63c7dabc94e600184229d0d GIT binary patch literal 5933 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 f$KZHO*s&vt|9^w-6P?|#0pRK8NSwWJ?9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!ItFh?!xdN1Q+aGJ{c&& zS>O>_%)r1c48n{Iv*t(u1=&kHeO=ifbFy+6aSK)V_AxLppYn8Z42d|rc6w}vOsL55 z`t&mC&y2@JTEyg!eDiFX^k#CC!jq%>erB=yHqUP0XcDOTw6ko}L zX;EmMrq(fKk*eygEuA616;0)>@A{TK|55PV@70 z$OfzS*(VJxQev3J?yY?O=ul(v`fp}?u9z`JK3ugibK>)DyCwImZOF4d{xK%%Ks1*} zv$oa)9anR%lXIBUqYnhLmT>VOzHfNP?ZwJNZ!5$s9M08RynIvaXw>@G^T9@r9^KH1 zVy??F&uuk)bH9Y4pQY!hP58i_H6 znl-NcuCpLV6ZWU;4C zu@9exF&OZi`Bovq_m%T+WhU2kvkz@^_LpycBvqm3bMpLw8X-Or5sL>0AKE1$(k_L=_Zc=CUq#=x1-QZf)G7nHu@fmsQ1eN_N3+nTEz`4HI4Z6uVlE zJH+X&det8JU?tO?upcM4Z=cV!JV;yF>FfL5Q$M|W_2Z!P`S=}Wzp|_1^#d%e?_H`> zV@%vA$+bFVqhw9`U;TfP|5|PD{||OiYdor8P*i??|NJcb%kzT_73*7WE?Ua5hAnR2 z=7WE=PhTlJ#ZeRznjTUb;`E(wkMZrj4e|Hilz-mK>9cZHQY**5TUPw~u}k;u73KI}xAx!0m-)GVia|x^d3p~s_9gh83jA&Ra<8rM%`>U3x69t&NzbwWY}7Ar?)FK#IZ0z|d0H0EkRO w3{9;}4Xg|ebq&m|3=9_N6z8I7$jwj5OsmAL;bP(Gi$Dzwp00i_>zopr02+f8CIA2c literal 0 HcmV?d00001 diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 0000000000000000000000000000000000000000..e71a726136a47ed24125c7efc79d68a4a01961b4 GIT binary patch literal 14800 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner/Configs/AppInfo.xcconfig b/packages/file_selector/file_selector_macos/example/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 000000000000..ef311e2bba6f --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = example + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.fileSelectorExample + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2020 The Flutter Authors. All rights reserved. diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner/Configs/Debug.xcconfig b/packages/file_selector/file_selector_macos/example/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 000000000000..36b0fd9464f4 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner/Configs/Release.xcconfig b/packages/file_selector/file_selector_macos/example/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 000000000000..dff4f49561c8 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner/Configs/Warnings.xcconfig b/packages/file_selector/file_selector_macos/example/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 000000000000..42bcbf4780b1 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner/DebugProfile.entitlements b/packages/file_selector/file_selector_macos/example/macos/Runner/DebugProfile.entitlements new file mode 100644 index 000000000000..d138bd5b0451 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + com.apple.security.files.user-selected.read-write + + + diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner/Info.plist b/packages/file_selector/file_selector_macos/example/macos/Runner/Info.plist new file mode 100644 index 000000000000..4789daa6a443 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner/MainFlutterWindow.swift b/packages/file_selector/file_selector_macos/example/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 000000000000..32aaeedceb1f --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,19 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController.init() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner/Release.entitlements b/packages/file_selector/file_selector_macos/example/macos/Runner/Release.entitlements new file mode 100644 index 000000000000..19afff14a08c --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner/Release.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.files.user-selected.read-write + + + diff --git a/packages/file_selector/file_selector_macos/example/macos/RunnerTests/Info.plist b/packages/file_selector/file_selector_macos/example/macos/RunnerTests/Info.plist new file mode 100644 index 000000000000..64d65ca49577 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/RunnerTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/packages/file_selector/file_selector_macos/example/macos/RunnerTests/RunnerTests.swift b/packages/file_selector/file_selector_macos/example/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 000000000000..bffc3452c49d --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,283 @@ +// 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. + +@testable import file_selector_macos +import FlutterMacOS +import XCTest + +class TestPanelController: NSObject, PanelController { + // The last panels that the relevant display methods were called on. + public var savePanel: NSSavePanel? + public var openPanel: NSOpenPanel? + + // Mock return values for the display methods. + public var saveURL: URL? + public var openURLs: [URL]? + + func display(_ panel: NSSavePanel, for window: NSWindow?, completionHandler handler: @escaping (URL?) -> Void) { + savePanel = panel + handler(saveURL) + } + + func display(_ panel: NSOpenPanel, for window: NSWindow?, completionHandler handler: @escaping ([URL]?) -> Void) { + openPanel = panel + handler(openURLs) + } +} + +class TestViewProvider: NSObject, ViewProvider { + var view: NSView? { + get { + window?.contentView + } + } + var window: NSWindow? = NSWindow() +} + +class exampleTests: XCTestCase { + + func testOpenSimple() throws { + let panelController = TestPanelController() + let plugin = FileSelectorPlugin( + viewProvider: TestViewProvider(), + panelController: panelController) + + let returnPath = "/foo/bar" + panelController.openURLs = [URL(fileURLWithPath: returnPath)] + + let called = XCTestExpectation() + let call = FlutterMethodCall(methodName: "openFile", arguments: [:]) + plugin.handle(call) { result in + XCTAssertEqual((result as! [String]?)![0], returnPath) + called.fulfill() + } + + wait(for: [called], timeout: 0.5) + XCTAssertNotNil(panelController.openPanel) + if let panel = panelController.openPanel { + XCTAssertTrue(panel.canChooseFiles) + // For consistency across platforms, directory selection is disabled. + XCTAssertFalse(panel.canChooseDirectories) + } + } + + func testOpenWithArguments() throws { + let panelController = TestPanelController() + let plugin = FileSelectorPlugin( + viewProvider: TestViewProvider(), + panelController: panelController) + + let returnPath = "/foo/bar" + panelController.openURLs = [URL(fileURLWithPath: returnPath)] + + let called = XCTestExpectation() + let call = FlutterMethodCall( + methodName: "openFile", + arguments: [ + "initialDirectory": "/some/dir", + "suggestedName": "a name", + "confirmButtonText": "Open it!", + ] + ) + plugin.handle(call) { result in + XCTAssertEqual((result as! [String]?)![0], returnPath) + called.fulfill() + } + + wait(for: [called], timeout: 0.5) + XCTAssertNotNil(panelController.openPanel) + if let panel = panelController.openPanel { + XCTAssertEqual(panel.directoryURL?.path, "/some/dir") + XCTAssertEqual(panel.nameFieldStringValue, "a name") + XCTAssertEqual(panel.prompt, "Open it!") + } + } + + func testOpenMultiple() throws { + let panelController = TestPanelController() + let plugin = FileSelectorPlugin( + viewProvider: TestViewProvider(), + panelController: panelController) + + let returnPaths = ["/foo/bar", "/foo/baz"] + panelController.openURLs = returnPaths.map({ path in URL(fileURLWithPath: path) }) + + let called = XCTestExpectation() + let call = FlutterMethodCall( + methodName: "openFile", + arguments: ["multiple": true] + ) + plugin.handle(call) { result in + let paths = (result as! [String]?)! + XCTAssertEqual(paths.count, returnPaths.count) + XCTAssertEqual(paths[0], returnPaths[0]) + XCTAssertEqual(paths[1], returnPaths[1]) + called.fulfill() + } + + wait(for: [called], timeout: 0.5) + XCTAssertNotNil(panelController.openPanel) + } + + func testOpenWithFilter() throws { + let panelController = TestPanelController() + let plugin = FileSelectorPlugin( + viewProvider: TestViewProvider(), + panelController: panelController) + + let returnPath = "/foo/bar" + panelController.openURLs = [URL(fileURLWithPath: returnPath)] + + let called = XCTestExpectation() + let call = FlutterMethodCall( + methodName: "openFile", + arguments: [ + "acceptedTypes": [ + "extensions": ["txt", "json"], + "UTIs": ["public.text", "public.image"], + ] + ] + ) + plugin.handle(call) { result in + XCTAssertEqual((result as! [String]?)![0], returnPath) + called.fulfill() + } + + wait(for: [called], timeout: 0.5) + XCTAssertNotNil(panelController.openPanel) + if let panel = panelController.openPanel { + XCTAssertEqual(panel.allowedFileTypes, ["txt", "json", "public.text", "public.image"]) + } + } + + func testOpenCancel() throws { + let panelController = TestPanelController() + let plugin = FileSelectorPlugin( + viewProvider: TestViewProvider(), + panelController: panelController) + + let called = XCTestExpectation() + let call = FlutterMethodCall(methodName: "openFile", arguments: [:]) + plugin.handle(call) { result in + XCTAssertNil(result) + called.fulfill() + } + + wait(for: [called], timeout: 0.5) + XCTAssertNotNil(panelController.openPanel) + } + + func testSaveSimple() throws { + let panelController = TestPanelController() + let plugin = FileSelectorPlugin( + viewProvider: TestViewProvider(), + panelController: panelController) + + let returnPath = "/foo/bar" + panelController.saveURL = URL(fileURLWithPath: returnPath) + + let called = XCTestExpectation() + let call = FlutterMethodCall(methodName: "getSavePath", arguments: [:]) + plugin.handle(call) { result in + XCTAssertEqual(result as! String?, returnPath) + called.fulfill() + } + + wait(for: [called], timeout: 0.5) + XCTAssertNotNil(panelController.savePanel) + } + + func testSaveWithArguments() throws { + let panelController = TestPanelController() + let plugin = FileSelectorPlugin( + viewProvider: TestViewProvider(), + panelController: panelController) + + let returnPath = "/foo/bar" + panelController.saveURL = URL(fileURLWithPath: returnPath) + + let called = XCTestExpectation() + let call = FlutterMethodCall( + methodName: "getSavePath", + arguments: [ + "initialDirectory": "/some/dir", + "confirmButtonText": "Save it!", + ] + ) + plugin.handle(call) { result in + XCTAssertEqual(result as! String?, returnPath) + called.fulfill() + } + + wait(for: [called], timeout: 0.5) + XCTAssertNotNil(panelController.savePanel) + if let panel = panelController.savePanel { + XCTAssertEqual(panel.directoryURL?.path, "/some/dir") + XCTAssertEqual(panel.prompt, "Save it!") + } + } + + func testSaveCancel() throws { + let panelController = TestPanelController() + let plugin = FileSelectorPlugin( + viewProvider: TestViewProvider(), + panelController: panelController) + + let called = XCTestExpectation() + let call = FlutterMethodCall(methodName: "getSavePath", arguments: [:]) + plugin.handle(call) { result in + XCTAssertNil(result) + called.fulfill() + } + + wait(for: [called], timeout: 0.5) + XCTAssertNotNil(panelController.savePanel) + } + + func testGetDirectorySimple() throws { + let panelController = TestPanelController() + let plugin = FileSelectorPlugin( + viewProvider: TestViewProvider(), + panelController: panelController) + + let returnPath = "/foo/bar" + panelController.openURLs = [URL(fileURLWithPath: returnPath)] + + let called = XCTestExpectation() + let call = FlutterMethodCall(methodName: "getDirectoryPath", arguments: [:]) + plugin.handle(call) { result in + XCTAssertEqual(result as! String?, returnPath) + called.fulfill() + } + + wait(for: [called], timeout: 0.5) + XCTAssertNotNil(panelController.openPanel) + if let panel = panelController.openPanel { + XCTAssertTrue(panel.canChooseDirectories) + // For consistency across platforms, file selection is disabled. + XCTAssertFalse(panel.canChooseFiles) + // The Dart API only allows a single directory to be returned, so users shouldn't be allowed + // to select multiple. + XCTAssertFalse(panel.allowsMultipleSelection) + } + } + + func testGetDirectoryCancel() throws { + let panelController = TestPanelController() + let plugin = FileSelectorPlugin( + viewProvider: TestViewProvider(), + panelController: panelController) + + let called = XCTestExpectation() + let call = FlutterMethodCall(methodName: "getDirectoryPath", arguments: [:]) + plugin.handle(call) { result in + XCTAssertNil(result) + called.fulfill() + } + + wait(for: [called], timeout: 0.5) + XCTAssertNotNil(panelController.openPanel) + } + +} diff --git a/packages/file_selector/file_selector_macos/example/pubspec.yaml b/packages/file_selector/file_selector_macos/example/pubspec.yaml new file mode 100644 index 000000000000..2a11958e85cb --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/pubspec.yaml @@ -0,0 +1,27 @@ +name: example +description: Example for file_selector_macos implementation. +publish_to: 'none' +version: 1.0.0 + +environment: + sdk: ">=2.12.0 <3.0.0" + flutter: ">=2.0.0" + +dependencies: + file_selector_macos: + # When depending on this package from a real application you should use: + # file_selector_macos: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: .. + file_selector_platform_interface: ^2.0.0 + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/file_selector/file_selector_macos/lib/file_selector_macos.dart b/packages/file_selector/file_selector_macos/lib/file_selector_macos.dart new file mode 100644 index 000000000000..e321d331961b --- /dev/null +++ b/packages/file_selector/file_selector_macos/lib/file_selector_macos.dart @@ -0,0 +1,121 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:cross_file/cross_file.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/foundation.dart' show visibleForTesting; +import 'package:flutter/services.dart'; + +const MethodChannel _channel = + MethodChannel('plugins.flutter.io/file_selector_macos'); + +/// An implementation of [FileSelectorPlatform] for macOS. +class FileSelectorMacOS extends FileSelectorPlatform { + /// The MethodChannel that is being used by this implementation of the plugin. + @visibleForTesting + MethodChannel get channel => _channel; + + /// Registers the macOS implementation. + static void registerWith() { + FileSelectorPlatform.instance = FileSelectorMacOS(); + } + + @override + Future openFile({ + List? acceptedTypeGroups, + String? initialDirectory, + String? confirmButtonText, + }) async { + final List? path = await _channel.invokeListMethod( + 'openFile', + { + 'acceptedTypes': _allowedTypeListFromTypeGroups(acceptedTypeGroups), + 'initialDirectory': initialDirectory, + 'confirmButtonText': confirmButtonText, + 'multiple': false, + }, + ); + return path == null ? null : XFile(path.first); + } + + @override + Future> openFiles({ + List? acceptedTypeGroups, + String? initialDirectory, + String? confirmButtonText, + }) async { + final List? pathList = await _channel.invokeListMethod( + 'openFile', + { + 'acceptedTypes': _allowedTypeListFromTypeGroups(acceptedTypeGroups), + 'initialDirectory': initialDirectory, + 'confirmButtonText': confirmButtonText, + 'multiple': true, + }, + ); + return pathList?.map((String path) => XFile(path)).toList() ?? []; + } + + @override + Future getSavePath({ + List? acceptedTypeGroups, + String? initialDirectory, + String? suggestedName, + String? confirmButtonText, + }) async { + return _channel.invokeMethod( + 'getSavePath', + { + 'acceptedTypes': _allowedTypeListFromTypeGroups(acceptedTypeGroups), + 'initialDirectory': initialDirectory, + 'suggestedName': suggestedName, + 'confirmButtonText': confirmButtonText, + }, + ); + } + + @override + Future getDirectoryPath({ + String? initialDirectory, + String? confirmButtonText, + }) async { + return _channel.invokeMethod( + 'getDirectoryPath', + { + 'initialDirectory': initialDirectory, + 'confirmButtonText': confirmButtonText, + }, + ); + } + + // Converts the type group list into a flat list of all allowed types, since + // macOS doesn't support filter groups. + Map>? _allowedTypeListFromTypeGroups( + List? typeGroups) { + const String extensionKey = 'extensions'; + const String mimeTypeKey = 'mimeTypes'; + const String utiKey = 'UTIs'; + if (typeGroups == null || typeGroups.isEmpty) { + return null; + } + final Map> allowedTypes = >{ + extensionKey: [], + mimeTypeKey: [], + utiKey: [], + }; + for (final XTypeGroup typeGroup in typeGroups) { + // If any group allows everything, no filtering should be done. + if ((typeGroup.extensions?.isEmpty ?? true) && + (typeGroup.macUTIs?.isEmpty ?? true) && + (typeGroup.mimeTypes?.isEmpty ?? true)) { + return null; + } + allowedTypes[extensionKey]!.addAll(typeGroup.extensions ?? []); + allowedTypes[mimeTypeKey]!.addAll(typeGroup.mimeTypes ?? []); + allowedTypes[utiKey]!.addAll(typeGroup.macUTIs ?? []); + } + + return allowedTypes; + } +} diff --git a/packages/file_selector/file_selector_macos/macos/Classes/FileSelectorPlugin.swift b/packages/file_selector/file_selector_macos/macos/Classes/FileSelectorPlugin.swift new file mode 100644 index 000000000000..9551671d1575 --- /dev/null +++ b/packages/file_selector/file_selector_macos/macos/Classes/FileSelectorPlugin.swift @@ -0,0 +1,218 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import FlutterMacOS +import Foundation + +/// Protocol for showing panels, allowing for depenedency injection in tests. +protocol PanelController { + /// Displays the given save panel, and provides the selected URL, or nil if the panel is + /// cancelled, to the handler. + /// - Parameters: + /// - panel: The panel to show. + /// - window: The window to display the panel for. + /// - completionHandler: The completion handler to receive the results. + func display( + _ panel: NSSavePanel, + for window: NSWindow?, + completionHandler: @escaping (URL?) -> Void); + + /// Displays the given open panel, and provides the selected URLs, or nil if the panel is + /// cancelled, to the handler. + /// - Parameters: + /// - panel: The panel to show. + /// - window: The window to display the panel for. + /// - completionHandler: The completion handler to receive the results. + func display( + _ panel: NSOpenPanel, + for window: NSWindow?, + completionHandler: @escaping ([URL]?) -> Void); +} + +/// Protocol to provide access to the Flutter view, allowing for dependency injection in tests. +/// +/// This is necessary because Swift doesn't allow for only partially implementing a protocol, so +/// a stub implementation of FlutterPluginRegistrar for tests would break any time something was +/// added to that protocol. +protocol ViewProvider { + /// Returns the view associated with the Flutter content. + var view: NSView? { get } +} + +public class FileSelectorPlugin: NSObject, FlutterPlugin { + private let viewProvider: ViewProvider + private let panelController: PanelController + + private let openMethod = "openFile" + private let openDirectoryMethod = "getDirectoryPath" + private let saveMethod = "getSavePath" + + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel( + name: "plugins.flutter.io/file_selector_macos", + binaryMessenger: registrar.messenger) + let instance = FileSelectorPlugin( + viewProvider: DefaultViewProvider(registrar: registrar), + panelController: DefaultPanelController()) + registrar.addMethodCallDelegate(instance, channel: channel) + } + + init(viewProvider: ViewProvider, panelController: PanelController) { + self.viewProvider = viewProvider + self.panelController = panelController + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + let arguments = (call.arguments ?? [:]) as! [String: Any] + switch call.method { + case openMethod, + openDirectoryMethod: + let choosingDirectory = call.method == openDirectoryMethod + let panel = NSOpenPanel() + configure(panel: panel, with: arguments) + configure(openPanel: panel, with: arguments, choosingDirectory: choosingDirectory) + panelController.display(panel, for: viewProvider.view?.window) { (selection: [URL]?) in + if (choosingDirectory) { + result(selection?.first?.path) + } else { + result(selection?.map({ item in item.path })) + } + } + case saveMethod: + let panel = NSSavePanel() + configure(panel: panel, with: arguments) + panelController.display(panel, for: viewProvider.view?.window) { (selection: URL?) in + result(selection?.path) + } + default: + result(FlutterMethodNotImplemented) + } + } + + /// Configures an NSSavePanel based on channel method call arguments. + /// - Parameters: + /// - panel: The panel to configure. + /// - arguments: The arguments dictionary from a FlutterMethodCall to this plugin. + private func configure(panel: NSSavePanel, with arguments: [String: Any]) { + if let initialDirectory = getNonNullStringValue(for: "initialDirectory", from: arguments) { + panel.directoryURL = URL(fileURLWithPath: initialDirectory) + } + if let suggestedName = getNonNullStringValue(for: "suggestedName", from: arguments) { + panel.nameFieldStringValue = suggestedName + } + if let confirmButtonText = getNonNullStringValue(for: "confirmButtonText", from: arguments) { + panel.prompt = confirmButtonText + } + + let acceptedTypes = getNonNullValue( + for: "acceptedTypes", + from: arguments + ) as! [String: Any]? + if let acceptedTypes = acceptedTypes { + var allowedTypes: [String] = [] + let extensions = getNonNullStringArrayValue(for: "extensions", from: acceptedTypes) + let UTIs = getNonNullStringArrayValue(for: "UTIs", from: acceptedTypes) + allowedTypes.append(contentsOf: extensions) + allowedTypes.append(contentsOf: UTIs) + // TODO: Add support for mimeTypes in macOS 11+. + + if !allowedTypes.isEmpty { + panel.allowedFileTypes = allowedTypes + } + } + } + + /// Configures an NSOpenPanel based on channel method call arguments. + /// - Parameters: + /// - panel: The panel to configure. + /// - arguments: The arguments dictionary from a FlutterMethodCall to this plugin. + /// - choosingDirectory: True if the panel should allow choosing directories rather than files. + private func configure( + openPanel panel: NSOpenPanel, + with arguments: [String: Any], + choosingDirectory: Bool + ) { + panel.allowsMultipleSelection = + getNonNullValue(for: "multiple", from: arguments) as! Bool? ?? false + panel.canChooseDirectories = choosingDirectory; + panel.canChooseFiles = !choosingDirectory; + } +} + +/// Non-test implementation of PanelController that calls the standard methods to display the panel +/// either as a sheet (if a window is provided) or modal (if not). +private class DefaultPanelController: PanelController { + func display( + _ panel: NSSavePanel, + for window: NSWindow?, + completionHandler: @escaping (URL?) -> Void + ) { + let completionAdapter = { response in + completionHandler((response == NSApplication.ModalResponse.OK) ? panel.url : nil) + } + if let window = window { + panel.beginSheetModal(for: window, completionHandler: completionAdapter) + } else { + completionAdapter(panel.runModal()) + } + } + + func display( + _ panel: NSOpenPanel, + for window: NSWindow?, + completionHandler: @escaping ([URL]?) -> Void + ) { + let completionAdapter = { response in + completionHandler((response == NSApplication.ModalResponse.OK) ? panel.urls : nil) + } + if let window = window { + panel.beginSheetModal(for: window, completionHandler: completionAdapter) + } else { + completionAdapter(panel.runModal()) + } + } +} + +/// Non-test implementation of PanelController that forwards to the plugin registrar. +private class DefaultViewProvider: ViewProvider { + private let registrar: FlutterPluginRegistrar + + init(registrar: FlutterPluginRegistrar) { + self.registrar = registrar + } + + var view: NSView? { + get { + registrar.view + } + } +} + +/// Returns the value for the given key from the provided dictionary, unless the value is NSNull +/// in which case it returns nil. +/// - Parameters: +/// - key: The key to get a value for. +/// - dictionary: The dictionary to get the value from. +/// - Returns: The value, or nil for NSNull. +private func getNonNullValue(for key: String, from dictionary: [String: Any]) -> Any? { + let value = dictionary[key]; + return value is NSNull ? nil : value; +} + +/// A convenience wrapper for getNonNullValue for string values. +private func getNonNullStringValue(for key: String, from dictionary: [String: Any]) -> String? { + return getNonNullValue(for: key, from: dictionary) as! String? +} + +/// A convenience wrapper for getNonNullValue for array-of-string values. +/// - Parameters: +/// - key: The key to get a value for. +/// - dictionary: The dictionary to get the value from. +/// - Returns: The value, or an empty array for nil for NSNull. +private func getNonNullStringArrayValue( + for key: String, + from dictionary: [String: Any] +) -> [String] { + return getNonNullValue(for: key, from: dictionary) as! [String]? ?? [] +} diff --git a/packages/file_selector/file_selector_macos/macos/file_selector_macos.podspec b/packages/file_selector/file_selector_macos/macos/file_selector_macos.podspec new file mode 100644 index 000000000000..3533c3a422ec --- /dev/null +++ b/packages/file_selector/file_selector_macos/macos/file_selector_macos.podspec @@ -0,0 +1,21 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html +# +Pod::Spec.new do |s| + s.name = 'file_selector_macos' + s.version = '0.0.1' + s.summary = 'macOS implementation of file_selector.' + s.description = <<-DESC +Displays native macOS open and save panels. + DESC + s.license = { :type => 'BSD', :file => '../LICENSE' } + s.homepage = 'https://github.com/flutter/plugins/tree/main/packages/file_selector' + s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } + s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_macos' } + s.source_files = 'Classes/**/*' + s.dependency 'FlutterMacOS' + + s.platform = :osx, '10.11' + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } + s.swift_version = '5.0' +end diff --git a/packages/file_selector/file_selector_macos/pubspec.yaml b/packages/file_selector/file_selector_macos/pubspec.yaml new file mode 100644 index 000000000000..071d261c4bf8 --- /dev/null +++ b/packages/file_selector/file_selector_macos/pubspec.yaml @@ -0,0 +1,25 @@ +name: file_selector_macos +description: macOS implementation of the file_selector plugin. +repository: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector_macos +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 +version: 0.8.2 + +environment: + sdk: ">=2.12.0 <3.0.0" + flutter: ">=2.0.0" + +flutter: + plugin: + implements: file_selector + platforms: + macos: + dartPluginClass: FileSelectorMacOS + pluginClass: FileSelectorPlugin + +dependencies: + cross_file: ^0.3.1 + file_selector_platform_interface: ^2.0.4 + flutter: + sdk: flutter + flutter_test: + sdk: flutter diff --git a/packages/file_selector/file_selector_macos/test/file_selector_macos_test.dart b/packages/file_selector/file_selector_macos/test/file_selector_macos_test.dart new file mode 100644 index 000000000000..1c1b9c11e069 --- /dev/null +++ b/packages/file_selector/file_selector_macos/test/file_selector_macos_test.dart @@ -0,0 +1,288 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file_selector_macos/file_selector_macos.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + final FileSelectorMacOS plugin = FileSelectorMacOS(); + + final List log = []; + + setUp(() { + plugin.channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + return null; + }); + + log.clear(); + }); + + test('registered instance', () { + FileSelectorMacOS.registerWith(); + expect(FileSelectorPlatform.instance, isA()); + }); + + group('openFile', () { + test('passes the accepted type groups correctly', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + extensions: ['txt'], + mimeTypes: ['text/plain'], + macUTIs: ['public.text'], + ); + + final XTypeGroup groupTwo = XTypeGroup( + label: 'image', + extensions: ['jpg'], + mimeTypes: ['image/jpg'], + macUTIs: ['public.image'], + webWildCards: ['image/*']); + + await plugin.openFile(acceptedTypeGroups: [group, groupTwo]); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypes': { + 'extensions': ['txt', 'jpg'], + 'mimeTypes': ['text/plain', 'image/jpg'], + 'UTIs': ['public.text', 'public.image'], + }, + 'initialDirectory': null, + 'confirmButtonText': null, + 'multiple': false, + }), + ], + ); + }); + test('passes initialDirectory correctly', () async { + await plugin.openFile(initialDirectory: '/example/directory'); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypes': null, + 'initialDirectory': '/example/directory', + 'confirmButtonText': null, + 'multiple': false, + }), + ], + ); + }); + test('passes confirmButtonText correctly', () async { + await plugin.openFile(confirmButtonText: 'Open File'); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypes': null, + 'initialDirectory': null, + 'confirmButtonText': 'Open File', + 'multiple': false, + }), + ], + ); + }); + }); + group('openFiles', () { + test('passes the accepted type groups correctly', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + extensions: ['txt'], + mimeTypes: ['text/plain'], + macUTIs: ['public.text'], + ); + + final XTypeGroup groupTwo = XTypeGroup( + label: 'image', + extensions: ['jpg'], + mimeTypes: ['image/jpg'], + macUTIs: ['public.image'], + webWildCards: ['image/*']); + + await plugin.openFiles(acceptedTypeGroups: [group, groupTwo]); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypes': >{ + 'extensions': ['txt', 'jpg'], + 'mimeTypes': ['text/plain', 'image/jpg'], + 'UTIs': ['public.text', 'public.image'], + }, + 'initialDirectory': null, + 'confirmButtonText': null, + 'multiple': true, + }), + ], + ); + }); + test('passes initialDirectory correctly', () async { + await plugin.openFiles(initialDirectory: '/example/directory'); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypes': null, + 'initialDirectory': '/example/directory', + 'confirmButtonText': null, + 'multiple': true, + }), + ], + ); + }); + test('passes confirmButtonText correctly', () async { + await plugin.openFiles(confirmButtonText: 'Open File'); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypes': null, + 'initialDirectory': null, + 'confirmButtonText': 'Open File', + 'multiple': true, + }), + ], + ); + }); + }); + + group('getSavePath', () { + test('passes the accepted type groups correctly', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + extensions: ['txt'], + mimeTypes: ['text/plain'], + macUTIs: ['public.text'], + ); + + final XTypeGroup groupTwo = XTypeGroup( + label: 'image', + extensions: ['jpg'], + mimeTypes: ['image/jpg'], + macUTIs: ['public.image'], + webWildCards: ['image/*']); + + await plugin + .getSavePath(acceptedTypeGroups: [group, groupTwo]); + + expect( + log, + [ + isMethodCall('getSavePath', arguments: { + 'acceptedTypes': >{ + 'extensions': ['txt', 'jpg'], + 'mimeTypes': ['text/plain', 'image/jpg'], + 'UTIs': ['public.text', 'public.image'], + }, + 'initialDirectory': null, + 'suggestedName': null, + 'confirmButtonText': null, + }), + ], + ); + }); + test('passes initialDirectory correctly', () async { + await plugin.getSavePath(initialDirectory: '/example/directory'); + + expect( + log, + [ + isMethodCall('getSavePath', arguments: { + 'acceptedTypes': null, + 'initialDirectory': '/example/directory', + 'suggestedName': null, + 'confirmButtonText': null, + }), + ], + ); + }); + test('passes confirmButtonText correctly', () async { + await plugin.getSavePath(confirmButtonText: 'Open File'); + + expect( + log, + [ + isMethodCall('getSavePath', arguments: { + 'acceptedTypes': null, + 'initialDirectory': null, + 'suggestedName': null, + 'confirmButtonText': 'Open File', + }), + ], + ); + }); + group('getDirectoryPath', () { + test('passes initialDirectory correctly', () async { + await plugin.getDirectoryPath(initialDirectory: '/example/directory'); + + expect( + log, + [ + isMethodCall('getDirectoryPath', arguments: { + 'initialDirectory': '/example/directory', + 'confirmButtonText': null, + }), + ], + ); + }); + test('passes confirmButtonText correctly', () async { + await plugin.getDirectoryPath(confirmButtonText: 'Open File'); + + expect( + log, + [ + isMethodCall('getDirectoryPath', arguments: { + 'initialDirectory': null, + 'confirmButtonText': 'Open File', + }), + ], + ); + }); + }); + }); + + test('ignores all type groups if any of them is a wildcard', () async { + await plugin.getSavePath(acceptedTypeGroups: [ + XTypeGroup( + label: 'text', + extensions: ['txt'], + mimeTypes: ['text/plain'], + macUTIs: ['public.text'], + ), + XTypeGroup( + label: 'image', + extensions: ['jpg'], + mimeTypes: ['image/jpg'], + macUTIs: ['public.image'], + ), + XTypeGroup( + label: 'any', + ), + ]); + + expect( + log, + [ + isMethodCall('getSavePath', arguments: { + 'acceptedTypes': null, + 'initialDirectory': null, + 'suggestedName': null, + 'confirmButtonText': null, + }), + ], + ); + }); +} diff --git a/script/configs/exclude_integration_macos.yaml b/script/configs/exclude_integration_macos.yaml new file mode 100644 index 000000000000..bb58d463f7d9 --- /dev/null +++ b/script/configs/exclude_integration_macos.yaml @@ -0,0 +1,2 @@ +# Can't use Flutter integration tests due to native modal UI. +- file_selector_macos From f32730db20f5c44c99f4be8de7bcdbdddbd04482 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 4 Mar 2022 13:46:17 -0500 Subject: [PATCH 258/600] [file_selector] Endorse macOS (#5001) --- .../file_selector/file_selector/CHANGELOG.md | 4 + .../file_selector/example/macos/.gitignore | 7 + .../macos/Flutter/Flutter-Debug.xcconfig | 2 + .../macos/Flutter/Flutter-Release.xcconfig | 2 + .../file_selector/example/macos/Podfile | 40 ++ .../macos/Runner.xcodeproj/project.pbxproj | 572 ++++++++++++++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 87 +++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/macos/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 68 +++ .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 46993 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 3276 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 1429 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 5933 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1243 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 14800 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 1874 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 343 +++++++++++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 12 + .../example/macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 19 + .../example/macos/Runner/Release.entitlements | 8 + .../file_selector/file_selector/pubspec.yaml | 5 +- script/configs/exclude_integration_macos.yaml | 1 + 30 files changed, 1268 insertions(+), 1 deletion(-) create mode 100644 packages/file_selector/file_selector/example/macos/.gitignore create mode 100644 packages/file_selector/file_selector/example/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 packages/file_selector/file_selector/example/macos/Flutter/Flutter-Release.xcconfig create mode 100644 packages/file_selector/file_selector/example/macos/Podfile create mode 100644 packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.pbxproj create mode 100644 packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/file_selector/file_selector/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/file_selector/file_selector/example/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/file_selector/file_selector/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/file_selector/file_selector/example/macos/Runner/AppDelegate.swift create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Configs/Debug.xcconfig create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Configs/Release.xcconfig create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Configs/Warnings.xcconfig create mode 100644 packages/file_selector/file_selector/example/macos/Runner/DebugProfile.entitlements create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Info.plist create mode 100644 packages/file_selector/file_selector/example/macos/Runner/MainFlutterWindow.swift create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Release.entitlements diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index 65c41651935c..dde818dbce52 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.4 + +* Adds an endorsed macOS implementation. + ## 0.8.3 * Adds an endorsed Windows implementation. diff --git a/packages/file_selector/file_selector/example/macos/.gitignore b/packages/file_selector/file_selector/example/macos/.gitignore new file mode 100644 index 000000000000..746adbb6b9e1 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/packages/file_selector/file_selector/example/macos/Flutter/Flutter-Debug.xcconfig b/packages/file_selector/file_selector/example/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 000000000000..4b81f9b2d200 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/packages/file_selector/file_selector/example/macos/Flutter/Flutter-Release.xcconfig b/packages/file_selector/file_selector/example/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 000000000000..5caa9d1579e4 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/packages/file_selector/file_selector/example/macos/Podfile b/packages/file_selector/file_selector/example/macos/Podfile new file mode 100644 index 000000000000..dade8dfad0dc --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Podfile @@ -0,0 +1,40 @@ +platform :osx, '10.11' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.pbxproj b/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..c84862c67576 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,572 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* example.app */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000000..fb7259e17785 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/file_selector/file_selector/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/packages/file_selector/file_selector/example/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..1d526a16ed0f --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/file_selector/file_selector/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/file_selector/file_selector/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/file_selector/file_selector/example/macos/Runner/AppDelegate.swift b/packages/file_selector/file_selector/example/macos/Runner/AppDelegate.swift new file mode 100644 index 000000000000..5cec4c48f620 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000000..a2ec33f19f11 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..3c4935a7ca84f0976aca34b7f2895d65fb94d1ea GIT binary patch literal 46993 zcmZ5|3p`X?`~OCwR3s6~xD(})N~M}fiXn6%NvKp3QYhuNN0*apqmfHdR7#ShNQ99j zQi+P9nwlXbmnktZ_WnO>bl&&<{m*;O=RK!cd#$zCdM@AR`#jH%+2~+BeX7b-48x|= zZLBt9*d+MZNtpCx_&asa{+CselLUV<<&ceQ5QfRjLjQDSL-t4eq}5znmIXDtfA|D+VRV$*2jxU)JopC)!37FtD<6L^&{ia zgVf1p(e;c3|HY;%uD5<-oSFkC2JRh- z&2RTL)HBG`)j5di8ys|$z_9LSm^22*uH-%MmUJs|nHKLHxy4xTmG+)JoA`BN7#6IN zK-ylvs+~KN#4NWaH~o5Wuwd@W?H@diExdcTl0!JJq9ZOA24b|-TkkeG=Q(pJw7O;i z`@q+n|@eeW7@ z&*NP+)wOyu^5oNJ=yi4~s_+N)#M|@8nfw=2#^BpML$~dJ6yu}2JNuq!)!;Uwxic(z zM@Wa-v|U{v|GX4;P+s#=_1PD7h<%8ey$kxVsS1xt&%8M}eOF98&Rx7W<)gY(fCdmo{y*FPC{My!t`i=PS1cdV7DD=3S1J?b2<5BevW7!rWJ%6Q?D9UljULd*7SxX05PP^5AklWu^y` z-m9&Oq-XNSRjd|)hZ44DK?3>G%kFHSJ8|ZXbAcRb`gH~jk}Iwkl$@lqg!vu)ihSl= zjhBh%%Hq|`Vm>T7+SYyf4bI-MgiBq4mZlZmsKv+S>p$uAOoNxPT)R6owU%t*#aV}B z5@)X8nhtaBhH=={w;Du=-S*xvcPz26EI!gt{(hf;TllHrvku`^8wMj7-9=By>n{b= zHzQ?Wn|y=;)XM#St@o%#8idxfc`!oVz@Lv_=y(t-kUC`W)c0H2TX}Lop4121;RHE(PPHKfe_e_@DoHiPbVP%JzNudGc$|EnIv`qww1F5HwF#@l(=V zyM!JQO>Rt_PTRF1hI|u^2Uo#w*rdF*LXJky0?|fhl4-M%zN_2RP#HFhSATE3&{sos zIE_?MdIn!sUH*vjs(teJ$7^7#|M_7m`T>r>qHw>TQh?yhhc8=TJk2B;KNXw3HhnQs za(Uaz2VwP;82rTy(T3FJNKA86Y7;L(K=~BW_Q=jjRh=-k_=wh-$`nY+#au+v^C4VV z)U?X(v-_#i=3bAylP1S*pM_y*DB z2fR!imng6Dk$>dl*K@AIj<~zw_f$T!-xLO8r{OkE(l?W#W<={460Y02*K#)O4xp?W zAN+isO}!*|mN7B#jUt&!KNyFOpUxv&ybM>jmkfn8z^llBslztv!!`TBEPwu;#eR3d z@_VDa)|ByvXx1V=^Up4{;M8ji3FC7gm(C7Ty-#1gs+U<{Ouc(iV67{< zam#KwvR&s=k4W<13`}DxzJ9{TUa97N-cgWkCDc+C339)EEnC@^HQK6OvKDSCvNz(S zOFAF_6omgG!+zaPC8fBO3kH8YVBx9_AoM?->pv~@$saf(Myo|e@onD`a=;kO*Utem ze=eUH&;JB2I4}?Pm@=VnE+yb$PD~sA5+)|iH3bi|s?ExIePeoAMd(Z4Z%$mCu{t;B9(sgdG~Q}0ShAwe!l8nw0tJn zJ+m?ogrgty$3=T&6+JJa!1oS3AtQQ1gJ z3gR1<=hXU>{SB-zq!okl4c+V9N;vo4{fyGeqtgBIt%TPC1P&k!pR-GZ7O8b}9=%>3 zQrV%FQdB+CcCRKK)0}v>U25rbQk(1^9Ax|WcAo5?L(H&H@%zAoT2RH$iN6boyXpsYqME}WJZI6T%OMlkWXK>R`^7AHG&31 z&MIU}igQ7$;)7AEm#dXA+!I&6ymb7n6D;F7c$tO3Ql(`ht z1sFrzIk_q5#=!#D(e~#SdWz5K;tPF*R883Yu>*@jTeOGUjQekw zM+7HlfP{y8p}jA9bLfyKC_Ti8k#;AVp@RML^9MQp-E+Ns-Y zKA!aAZV-sfm<23fy#@TZZlQVQxH%R7rD}00LxHPUF!Yg3%OX ziDe4m<4fp{7ivBS?*AlJz$~vw5m)Ei8`|+~xOSqJ$waA0+Yys$z$9iN9TIXu8 zaYacjd09uRAsU|)g|03w`F|b1Xg#K~*Mp2X^K^)r3P^juoc}-me&YhkW3#G|H<~jK zoKD?lE@jOw7>4cpKkh!8qU!bF(i~Oa8a!EGy-j46eZYbKUvF=^^nq`EtWFK}gwrsB zeu<6~?mk+;+$whP)8ud8vjqh+NofU+Nu`~|pb&CN1y_idxxf6cGbT=fBZR_hl&G)GgnW$*oDrN-zz;cKs18n+dAn95w z)Y>l6!5eYpebJGw7it~Q5m}8$7@%p&KS=VtydFj4HPJ{xqUVS_Ih}c(^4nUdwG|0% zw8Fnm{IT`8MqoL(1BNtu_#7alS@3WSUUOFT@U*`V!zrPIeCbbO=pE%|g92$EU|lw; z^;^AqMVWVf-R5^OI79TzIyYf}HX%0Y)=aYH;EKo}?=R~ZM&s&F;W>u%hFUfNafb;- z8OkmkK3k||J#3`xdLuMJAhj9oPI?Cjt}cDN7hw26n7irWS0hsy`fs&Y?Y&(QF*Nu! z!p`NggHXaBU6$P42LkqnKsPG@363DHYGXg{!|z6VMAQt??>FK1B4x4{j;iY8A+7o% z*!0qt&w+w#Ob@pQp;q)u0;v^9FlY=AK>2!qku)!%TO<^lNBr!6R8X)iXgXi^1p`T8 z6sU@Y_Fsp6E89E1*jz~Tm2kF=mjYz_q99r^v0h-l7SP6azzL%woM6!7>IFWyizrNwAqoia3nN0q343q zFztMPh0)?ugQg5Izbk{5$EGcMzt*|=S8ZFK%O&^YV@V;ZRL>f!iG?s5z{(*Xq20c^ z(hkk~PljBo%U`$q>mz!ir7chKlE-oHA2&0i@hn4O5scsI&nIWsM>sYg;Ph5IO~VpT z%c-3_{^N>4kECzk?2~Z@V|jWio&a&no;boiNxqXOpS;ph)gEDFJ6E=zPJ$>y5w`U0 z;h9_6ncIEY?#j1+IDUuixRg&(hw+QSSEmFi%_$ua$^K%(*jUynGU@FlvsyThxqMRw z7_ALpqTj~jOSu2_(@wc_Z?>X&(5jezB6w-@0X_34f&cZ=cA-t%#}>L7Q3QRx1$qyh zG>NF=Ts>)wA)fZIlk-kz%Xa;)SE(PLu(oEC8>9GUBgd$(^_(G6Y((Hi{fsV; zt*!IBWx_$5D4D&ezICAdtEU!WS3`YmC_?+o&1RDSfTbuOx<*v`G<2SP;5Q4TqFV&q zJL=90Lcm^TL7a9xck}XPMRnQ`l0%w-fi@bRI&c*VDj!W4nj=qaQd$2U?^9RTT{*qS_)Q9OL>s}2P3&da^Pf(*?> z#&2bt;Q7N2`P{{KH@>)Tf5&za?crRmQ%8xZi<9f=EV3={K zwMet=oA0-@`8F;u`8j-!8G~0TiH5yKemY+HU@Zw3``1nT>D ziK465-m?Nm^~@G@RW2xH&*C#PrvCWU)#M4jQ`I*>_^BZB_c!z5Wn9W&eCBE(oc1pw zmMr)iu74Xl5>pf&D7Ml>%uhpFGJGyj6Mx=t#`}Mt3tDZQDn~K`gp0d)P>>4{FGiP$sPK*ExVs!1)aGgAX z6eA;-9@@Muti3xYv$8U{?*NxlHxs?)(6%!Iw&&l79K86h+Z8;)m9+(zzX?cS zH*~)yk)X^H1?AfL!xctY-8T0G0Vh~kcP=8%Wg*zZxm*;eb)TEh&lGuNkqJib_}i;l z*35qQ@}I#v;EwCGM2phE1{=^T4gT63m`;UEf5x2Get-WSWmt6%T6NJM`|tk-~4<#HHwCXuduB4+vW!BywlH8murH@|32CNxx7} zAoF?Gu02vpSl|q1IFO0tNEvKwyH5V^3ZtEO(su1sIYOr{t@Tr-Ot@&N*enq;Je38} zOY+C1bZ?P~1=Qb%oStI-HcO#|WHrpgIDR0GY|t)QhhTg*pMA|%C~>;R4t_~H1J3!i zyvQeDi&|930wZlA$`Wa9)m(cB!lPKD>+Ag$5v-}9%87`|7mxoNbq7r^U!%%ctxiNS zM6pV6?m~jCQEKtF3vLnpag``|bx+eJ8h=(8b;R+8rzueQvXgFhAW*9y$!DgSJgJj% zWIm~}9(R6LdlXEg{Y3g_i7dP^98=-3qa z$*j&xC_$5btF!80{D&2*mp(`rNLAM$JhkB@3al3s=1k^Ud6HHontlcZw&y?`uPT#a za8$RD%e8!ph8Ow7kqI@_vd7lgRhkMvpzp@4XJ`9dA@+Xk1wYf`0Dk!hIrBxhnRR(_ z%jd(~x^oqA>r>`~!TEyhSyrwNA(i}={W+feUD^8XtX^7^Z#c7att{ot#q6B;;t~oq zct7WAa?UK0rj0yhRuY$7RPVoO29JV$o1Z|sJzG5<%;7pCu%L-deUon-X_wAtzY@_d z6S}&5xXBtsf8TZ13chR&vOMYs0F1?SJcvPn>SFe#+P3r=6=VIqcCU7<6-vxR*BZUm zO^DkE{(r8!e56)2U;+8jH4tuD2c(ptk0R{@wWK?%Wz?fJckr9vpIU27^UN*Q$}VyHWx)reWgmEls}t+2#Zm z_I5?+htcQl)}OTqF<`wht89>W*2f6e)-ewk^XU5!sW2A2VtaI=lggR&I z;Rw{xd)WMqw`VUPbhrx!!1Eg_*O0Si6t@ny)~X^Gu8wZZDockr)5)6tm+<=z+rYu? zCof+;!nq6r9MAfh zp4|^2w^-3vFK~{JFX|F5BIWecBJkkEuE%iP8AZ z^&e|C+VEH&i(4Y|oWPCa#C3T$129o5xaJa=y8f(!k&q+x=M|rq{?Zw_n?1X-bt&bP zD{*>Io`F4(i+5eE2oEo6iF}jNAZ52VN&Cp>LD{MyB=mCeiwP+v#gRvr%W)}?JBTMY z_hc2r8*SksC%(pp$KGmWSa|fx;r^9c;~Q(Jqw1%;$#azZf}#Fca9NZOh{*YxV9(1ivVA^2Wz>!A&Xvmm-~{y8n!^Jdl8c>`J#=2~!P{ zC1g_5Ye3={{fB`R%Q|%9<1p1;XmPo5lH5PHvX$bCIYzQhGqj7hZ?@P4M0^mkejD|H zVzARm7LRy|8`jSG^GpxRIs=aD>Y{Cb>^IwGEKCMd5LAoI;b{Q<-G}x*e>86R8dNAV z<@jb1q%@QQanW1S72kOQ$9_E#O?o}l{mHd=%Dl{WQcPio$baXZN!j{2m)TH1hfAp{ zM`EQ=4J`fMj4c&T+xKT!I0CfT^UpcgJK22vC962ulgV7FrUrII5!rx1;{@FMg(dIf zAC}stNqooiVol%%TegMuWnOkWKKA}hg6c)ssp~EnTUVUI98;a}_8UeTgT|<%G3J=n zKL;GzAhIQ_@$rDqqc1PljwpfUwiB)w!#cLAkgR_af;>}(BhnC9N zqL|q8-?jsO&Srv54TxVuJ=rfcX=C7{JNV zSmW@s0;$(#!hNuU0|YyXLs{9$_y2^fRmM&g#toh}!K8P}tlJvYyrs6yjTtHU>TB0} zNy9~t5F47ocE_+%V1(D!mKNBQc{bnrAbfPC2KO?qdnCv8DJzEBeDbW}gd!g2pyRyK`H6TVU^~K# z488@^*&{foHKthLu?AF6l-wEE&g1CTKV|hN7nP+KJnkd0sagHm&k{^SE-woW9^fYD z7y?g*jh+ELt;$OgP>Se3o#~w9qS}!%#vBvB?|I-;GM63oYrJ}HFRW6D+{54v@PN8K z2kG8`!VVc+DHl^8y#cevo4VCnTaPTzCB%*)sr&+=p{Hh#(MwaJbeuvvd!5fd67J_W za`oKxTR=mtM7P}i2qHG8=A(39l)_rHHKduDVA@^_Ueb7bq1A5#zHAi**|^H@fD`_W z#URdSG86hhQ#&S-Vf_8b`TIAmM55XhaHX7}Ci-^(ZDs*yb-WrWV&(oAQu3vMv%u$5 zc;!ADkeNBN_@47r!;%G3iFzo;?k)xTS-;1D-YeS5QXN7`p2PzGK~e6ib;8COBa5)p zfMn}dA--&A12~zr&GVk?qnBGfIEo`5yir;-Q;ZLn{Fimdrk;e!)q`sAkYh^~^>4Q@ zN5RT>s38+`V{|6@k&vZW!W0*BEqV&~34d+Ev8h)ObYL7Bd_hgbUzjdJaXP=S@Dp6X z)i013q3K4Gr5d%2YIp>218pYK!xwH;k)j?uUrT-yVKLg*L3y~=a+qd!RWGTL`z>29 z-Zb4Y{%pT%`R-iA#?T58c-i@?jf-Ckol9O>HAZPUxN%Z=<4ad9BL7n`_kH0i#E(m& zaNb039+z~ONUCLsf_a|x*&ptU?`=R*n}rm-tOdCDrS!@>>xBg)B3Sy8?x^e=U=i8< zy7H-^BPfM}$hf*d_`Qhk_V$dRYZw<)_mbC~gPPxf0$EeXhl-!(ZH3rkDnf`Nrf4$+ zh?jsRS+?Zc9Cx7Vzg?q53ffpp43po22^8i1Obih&$oBufMR;cT2bHlSZ#fDMZZr~u zXIfM5SRjBj4N1}#0Ez|lHjSPQoL&QiT4mZn=SxHJg~R`ZjP!+hJ?&~tf$N!spvKPi zfY;x~laI9X`&#i#Z}RJ`0+MO_j^3#3TQJu2r;A-maLD8xfI+2Y*iDf4LsQ$9xiu?~ z?^wHEf^qlgtjdj(u_(W5sbGx1;maVPDHvI-76u2uUywf;>()=e>0le;bO0LIvs)iy z*lJTO+7gyf^)2uS-PhS_O-+RToQmc6VT>ej^y^stNkwIxUg?E|YMAAwQ}U!dC&cXL ziXKU?zT~xbh6C};rICGbdX~;8Z%L~Jdg|`senVEJo-CiDsX47Kc`;EiXWO<9o)(`4 zGj(9@c+Me=F~y(HUehcAy!tkoM&e1y#(qqCkE(0lik_U>wg8vOhGR(=gBGFSbR`mh zn-%j3VTD4 zwA1Kqw!OSgi_v0;6?=Bk4Z{l-7Fl4`ZT535OC{73{rBwpNHMPH>((4G`sh zZhr!v{zM@4Q$5?8)Jm;v$A2v$Yp9qFG7y`9j7O-zhzC+7wr3Cb8sS$O{yOFOODdL) zV2pU{=nHne51{?^kh%a$WEro~o(rKQmM!p?#>5Pt`;!{0$2jkmVzsl|Nr^UF^IHxG z8?HmZEVMY~ec%Ow6hjfg6!9hCC4xY?V;5Ipo-myV=3TmfT^@XkKME`+=_inm4h7ki z->K~a+20?)zic^zc&7h=0)T{Aa24FU_}(O|9DMW3Bf>MW=O%~8{unFxp4}B+>>_KN zU%rKs3Va&&27&OX4-o&y2ie|sN2p-=S^V<2wa2NUQ4)?0e|hgna*1R7(#R_ys3xmG zE#(ry+q=O~&t|RX@ZMD`-)0QmE*x%SBc(Yvq60JtCQ4RL(gdA(@=}0rYo5yKz36bW zkvLOosP6I?7qH!rce(}q@cH-{oM2ThKV2RZe+{{25hkc?T>=Tky12xHr0jmfH@SZi zLHPJ@^Oo^Zo%`gZk_hrbCzS+t|=O!Bt zWi|>M8mz~sD|Z>C1ZPf_Cs&R!S5E2qK+@j*UpP>;5_|+h+y{gb=zub7#QKSUabet# zFH2H0ul;zO+uc+V=W_W@_Ig-791T7J9&=5)wrBE?JEHS_A6P~VQ)u6s1)Pu|VxP(aYJV*(e<)(42R zm3AK>dr1QLbC1RMoQ|M5k+TWBjY9q+_vY=K-tUte35m4RWl51A<4O0ptqV3)KzL7U z0gpp-I1)|zvtA8V7-e-o9H)lB_Rx6;Bu7A2yE)6)SuDqWDs}~Ojfk?DFwI% z3E1(>LbbB7I(&E@B7nlulhvY=Wa1mGXD@ijD7WF^y@L1e55h)-hzoq}eWe!fh9m3V{)x^6F8?ed1z>+4;qW6A4hYYj zZCYP=c#I8+$pAIVyiY*#%!j3ySAnH`tp|=^lh{)#JimWaP_rXK40A0WcsEUj`G1}O zG?XQ~qK4F!lqauv6-BL_Up3+-l1=kVfD;D*C)yr>o9>W=%mIyATtn_OBLK+h@p)j5jRAb;m&Ok?TZH-5Q)~#UwdYFp~rEE{judWa9E)z zE>135C-xMdHYY&AZGR)tb`K}s0CK9 z1!))p^ZaUC*e50t`sL+)@`)#kJ}?C_cCMH@k{f4wh~0`OFnGQ2nzUuuu;=r4BYRcI z){G#a6Y$S(mIc6B#YS;jFcU{0`c)Raa$nG+hV(K|2|^ZWOI566zlF0N;t~$jD<_AX zjnD?HN-G>xRmHwtL3BcJX7)Q^YGfc?cS4Nj=yYl5MB(uBD?r@VTB|mIYs=au$e)e{ zLHWd!+EN*v2*(=y%G1JzyQdY&%|?~R5NPb)`S2dw1AJW8O;L=p?yVxJs=X?U#-l1O zk6xh8yyY;OTR7aF{P=kQ>y`*EFivnw%rQioA-I67WS+~hVamG4_sI)(Jo4vHS|@F@ zqrBHbxHd_Y8+?8Gfq=Z1O^Fs5moGayCHVUHY^8)^j)Aj*RB!S2-FA?4#-`puwBW`` zJ_6OQj(FGo8DotHYRKq;;$4xDn9=4rgw}5xvxhi)?n?W5{*%4%h9Tg)zlQl&fN~Z1)gL(Dn7X!P428I zwA+U-x5!cQ57g1N=2bLqAWF z!&cbvsD)dvYoqP5vaQz%rL@kv*J>0AMzWAKn~Mxi5g2GlI7qvVZo)Z5oj=#O!M&*O z`3O3)uvrjNTeremC}nW@(m%#E-sITB>j-!yBM#(=FN`~c#@XjL3e)SjR9&%QO%tUg zzGv=SLH()`ZIt?Ayym;9VG1Muq+a+7Zo+59?SuRu_`k>@S4!yS3roMnq+SDO?`C7V#2 z8vHf4&0k;{kLT)fa==7EILSu3e|ZnxtFO;1 zGqP-;Xo(>_QKcYUhsi-X72BqH#7Zb-TsiNIF>G9xOHT3XoA*qX^10+#XCU0)UO4_%A_s_vO=uDd3_Q%D{OsvLMW9wGvuuRnF52{2vH06D~7N672!bIMt@it_D}& zwjZ7gV!RzZ86*wbEB5cnMJRbEqMM{G!K)bfJjyPH^9nGnrOI9S{~!dm4~P#&b*~)h zCMwM8mR+y5i~E5*JAopwZ>F`=ORfA&IF%O8(aS<}^H6wcY1g^=lYLPtFpyvW9F z3;FCS-TGFYPr#Y$ue>}?rTYrmWr^VbUu>!eL$cEdh1e>5_UDnZ@Mu$l*KVo_NDEu^ zBn*!qVnzYv>t|<(>nt8%CoNPhN!qGP|sANRN^#+2YSSYHa>R1mss->c0f=#g@U58@? zA4sUbrA7)&KrTddS0M6pTSRaz)wqUgsT3&8-0eG|d;ULOUztdaiD3~>!10H`rRHWY z1iNu6=UaA8LUBoaH9G*;m`Mzm6d1d+A#I8sdkl*zfvbmV0}+u` zDMv=HJJm?IOwbP;f~yn|AI_J7`~+5&bPq6Iv?ILo2kk$%vIlGsI0%nf1z9Mth8cy! zWumMn=RL1O9^~bVEFJ}QVvss?tHIwci#ldC`~&KFS~DU5K5zzneq_Q91T~%-SVU4S zJ6nVI5jeqfh~*2{AY#b(R*Ny95RQBGIp^fxDK{I9nG0uHCqc-Ib;pUUh$t0-4wX*< z=RzW~;iR3xfRnW<>5Jr5O1MP)brA3+ei@H8Hjkt7yuYIpd7c-4j%U=8vn8HD#TPJo zSe+7~Db}4U3Y^4dl1)4XuKZ67f(ZP;?TYg9te>hbAr4R_0K$oq3y5m-gb?fR$UtF9 zS~S^=aDyFSE}9W2;Okj%uoG-Um^&Qo^bB#!W?|%=6+P>``bumeA2E7ti7Aj%Fr~qm z2gbOY{WTyX$!s5_0jPGPQQ0#&zQ0Zj0=_74X8|(#FMzl`&9G_zX*j$NMf?i3M;FCU z6EUr4vnUOnZd`*)Uw#6yI!hSIXr%OF5H z5QlF8$-|yjc^Y89Qfl!Er_H$@khM6&N*VKjIZ15?&DB?);muI`r;7r0{mI03v9#31 z#4O*vNqb=1b}TjLY`&ww@u^SE{4ZiO=jOP3!|6cKUV2*@kI9Aw0ASwn-OAV~0843$1_FGl7}eF6C57dJb3grW)*jtoUd zpqXvfJSCIv4G*_@XZE?> z4Lt=jTSc*hG3`qVq!PVMR2~G-1P{%amYoIg!8Odf4~nv6wnEVrBt-R5Au=g~4=X|n zHRJGVd|$>4@y#w;g!wz>+z%x?XM^xY%iw%QoqY@`vSqg0c>n_}g^lrV))+9n$zGOP zs%d&JWT2Jjxaz`_V%XtANP$#kLLlW=OG2?!Q%#ThY#Sj}*XzMsYis2HiU2OlfeC>d z8n8j-{Npr1ri$Jv2E_QqKsbc$6vedBiugD~S`_0QjTTtX(mS}j6)6e;xdh*sp5U0aMpuN}qTP=^_Qn zh~0padPWs&aXmf6b~}{7Raglc)$~p?G89N4)&a}`izf|bA)IUmFLQ8UM$T!6siQxr z=%)pPsWYXWCNdGMS3fK6cxVuhp7>mug|>DVtxGd~O8v@NFz<+l`8^#e^KS3})bovWb^ zILp4a_9#%Y*b6m$VH8#)2NL@6a9|q!@#XOXyU-oAe)RR$Auj6?p2LEp*lD!KP{%(- z@5}`S$R)Kxf@m68b}Tr7eUTO=dh2wBjlx;PuO~gbbS2~9KK1szxbz$R|Frl8NqGn= z2RDp@$u5Obk&sxp!<;h=C=ZKPZB+jk zBxrCc_gxabNnh6Gl;RR6>Yt8c$vkv>_o@KDMFW1bM-3krWm|>RG>U`VedjCz2lAB1 zg(qb_C@Z~^cR=_BmGB@f;-Is3Z=*>wR2?r({x}qymVe?YnczkKG%k?McZ2v3OVpT* z(O$vnv}*Tle9WVK_@X@%tR^Z!3?FT_3s@jb3KBVf#)4!p~AFGgmn%1fBbZe3T53$_+UX_A!@Kz63qSLeH@8(augJDJ;RA>6rNxQYkd6t(sqK=*zv4j;O#N(%*2cdD z3FjN6`owjbF%UFbCO=haP<;Y1KozVgUy(nnnoV7{_l5OYK>DKEgy%~)Rjb0meL49X z7Fg;d!~;Wh63AcY--x{1XWn^J%DQMg*;dLKxs$;db`_0so$qO!>~yPDNd-CrdN!ea zMgHt24mD%(w>*7*z-@bNFaTJlz;N0SU4@J(zDH*@!0V00y{QfFTt>Vx7y5o2Mv9*( z1J#J27gHPEI3{!^cbKr^;T8 z{knt%bS@nrExJq1{mz2x~tc$Dm+yw=~vZD|A3q>d534za^{X9e7qF29H5yu};J)vlJkKq}< zXObu*@ioXGp!F=WVG3eUtfIA$GGgv0N?d&3C47`Zo)ms*qO}A9BAEke!nh#AfQ0d_ z&_N)E>5BsoR0rPqZb)YN}b~6Ppjyev;MMis-HkWF!az%G? z#&it84hv!%_Q>bnwch!nZKxB05M=jgiFaB^M=e-sj1xR?dPYUzZ#jua`ggyCAcWY> z-L$r#a{=;JP5X}9(ZPC&PdG~h5>_8SueX($_)Qu(;()N3*ZQH(VGnkWq^C}0r)~G3_?a10y*LsFz zokU5AKsW9DUr-ylK61shLS#4@vPcteK-Ga9xvRnPq=xSD_zC=Q_%6IuM?GpL(9aDx z|8d_;^6_D4{IQ1ndMAcFz5ZaT+Ww0wWN`xP(U#^=POs(BpKm;(H(lmYp+XCb7Kaw0 z;LT945Ev3IkhP6$lQBiMgr+vAL}{8xO&IObqJBEP4Y^x&V?iGC=1lVIbH^Z!eXxr@ zz)D7Fon`z~N|Pq>Bsue&_T9d;G+d8#@k^cq~F^I8ETsZ*cGOf*gZ4ghlAzW|aZ;WA13^B!Tlr0sWA zosgXD-%zvO-*GLU@hVV(bbQ`s@f~Ux=4}(@7O)%o5EH((gYflccBC@jbLF3IgPozv zglX2IL}kL1rtn4mu~`J(MMY83Rz6gc1}cX4RB+tZO2~;3FI# z@dU(xa5J_KvL0)oSkvwz9|!QcEA$jKR@a-4^SU3O449TrO+x$1fkBU<<=E_IHnF6> zPmZ7I2E+9A_>j6og$>Nih~b2F_^@6ef|Hm-K2(>`6ag{Vpd`g35n`yW|Jme78-cSy z2Jz7V#5=~u#0eLSh3U4uM3Smk31>xEh^-Os%&5tK6hSAX83jJi%5l!MmL4E?=FerNG#3lj^;-F1VISY!4E)__J~gY zP{o~Xo!8DW{5lsBFKL~OJiQoH>yBZ+b^};UL&UUs!Hbu7Gsf<9sLAsOPD4?-3CP{Q zIDu8jLk6(U3VQPyTP{Esf)1-trW5Mi#zfpgoc-!H>F$J#8uDRwDwOaohB(_I%SuHg zGP)11((V9rRAG>80NrW}d`=G(Kh>nzPa1M?sP;UNfGQaOMG1@_D0EMIWhIn#$u2_$ zlG-ED(PU+v<1Dd?q-O#bsA)LwrwL>q#_&75H)_X4sJK{n%SGvVsWH7@1QZqq|LM`l zDhX8m%Pe5`p1qR{^wuQ&>A+{{KWhXs<4RD< z=qU6)+btESL>kZWH8w}Q%=>NJTj=b%SKV3q%jSW>r*Qv1j$bX>}sQ%KO7Il zm?7>4%Q6Nk!2^z})Kchu%6lv-7i=rS26q7)-02q?2$yNt7Y={z<^<+wy6ja-_X6P4 zoqZ1PW#`qSqD4qH&UR57+z0-hm1lRO2-*(xN-42|%wl2i^h8I{d8lS+b=v9_>2C2> zz(-(%#s*fpe18pFi+EIHHeQvxJT*^HFj2QyP0cHJw?Kg+hC?21K&4>=jmwcu-dOqEs{%c+yaQ z2z6rB>nPdwuUR*j{BvM-)_XMd^S1U|6kOQ$rR`lHO3z~*QZ71(y(42g`csRZ1M@K7 zGeZ27hWA%v`&zQExDnc@cm9?ZO?$?0mWaO7E(Js|3_MAlXFB$^4#Zpo;x~xOEbay( zq=N;ZD9RVV7`dZNzz+p@YqH@dW*ij8g053Cbd=Mo!Ad8*L<5m1c4Kk ziuca5CyQ05z7gOMecqu!vU=y93p+$+;m=;s-(45taf_P(2%vER<8q3}actBuhfk)( zf7nccmO{8zL?N5oynmJM4T?8E))e;;+HfHZHr` zdK}~!JG}R#5Bk%M5FlTSPv}Eb9qs1r0ZH{tSk@I{KB|$|16@&`0h3m7S+)$k*3QbQ zasW2`9>hwc)dVNgx46{Io zZ}aJHHNf1?!K|P;>g7(>TefcLJk%!vM`gH8V3!b= z>YS+)1nw9U(G&;7;PV4eIl{=6DT^Vw<2Elnox;u@xF5ad*9Fo|yKgq<>*?C$jaG2j z|29>K)fI^U!v?55+kQ*d2#3}*libC4>Dl4 zIo3Jvsk?)edMnpH<|*l<*0Pf{2#KedIt>~-QiB{4+KEpSjUAYOhGDpn3H_N9$lxaP ztZwagSRY~x@81bqe^3fb;|_A7{FmMBvwHN*Xu006qKo{1i!RbN__2q!Q*A;U*g-Mz zg)-3FZ`VJdognZ~WrWW^2J$ArQAr1&jl~kWhn+osG5wAlE5W&V%GI{8iMQ!5lmV~# zeb3SKZ@?7p;?7{uviY6`Oz16t0=B70`im=`D@xJa16j2eHoCtElU*~7={YUzN41sE z#Th>DvJq-#UwEpJGKx;;wfDhShgO0cM|e!Ej){RX#~>a?)c2|7Hjhh2d=)VUVJL<^Aq|>_df4DX>b9W2$_DM zTjF#j(9?Co`yor?pK<16@{h#F&F8~1PG|qQNZPX^b!L*L&?PH#W8za0c~v6I2W($Jderl%4gufl z#s;C*7APQJP46xHqw;mUyKp3}W^hjJ-Dj>h%`^XS7WAab^C^aRu1?*vh-k2df&y9E z=0p*sn0<83UL4w30FqnZ0EvXCBIMVSY9Zf?H1%IrwQybOvn~4*NKYubcyVkBZ4F$z zkqcP*S>k6!_MiTKIdGlG+pfw>o{ni`;Z7pup#g z4tDx3Kl$)-msHd1r(YpVz7`VW=fx9{ zP}U8rJ-IP)m}~5t&0Y$~Quyjflm!-eXC?_LMGCkZtNDZf0?w<{f^zp&@U@sQxcPOZ zBbfQTFDWL_>HytC*QQG_=K7ZRbL!`q{m8IjE0cz(t`V0Ee}v!C74^!Fy~-~?@}rdn zABORRmgOLz8{r!anhFgghZc>0l7EpqWKU|tG$`VM=141@!EQ$=@Zmjc zTs`)!A&yNGY6WfKa?)h>zHn!)=Jd73@T^(m_j|Z;f?avJ{EOr~O~Q2gox6dkyY@%M zBU+#=T?P8tvGG|D5JTR}XXwjgbH(uwnW%W?9<-OQU9|6H{09v#+jmnxwaQ-V;q{v% zA8srmJX7Fn@7mr*ZQ@)haPjWVN@e3K z_`+@X$k*ocx*uF^_mTqJpwpuhBX~CSu=zPE(Sy%fYz&lzZmz3xo4~-xBBvU0Ao?;I-81*Z%8Do+*}pqg>bt^{w-`V6Sj>{Znj+ z70GS2evXinf|S#9=NNoXoS;$BTW*G0!xuTSZUY45yPE+~*&a-XC+3_YPqhd*&aQ>f z$oMUq^jjA;x#?iJKrpAqa<2<21h*_lx9a}VMib;a6c$~=PJOj6XJXJ|+rc7O7PEN5uE7!4n9nllo@BI4$VW2Nf_jqnkz%cvU4O4umV z#n6oXGWOt3tuIjmX*b!!$t~94@a@QgybLpQo3icAyU`iNbY~XNAArFAn$nFJ()d-U zFaO#nxxVF-%J{UB**uRo0*+?S>=^il)1m7v-u`PDy*ln%|3E-{3U~R=QcE&zhiG_c zDnGMgf1}3h1gWz8IV0Oc7FmEt>6W?Eva;J`(!;IIny}PvD?vztz`F6su_tUO`M%K5 z%C#=nXbX})#uE!zcq2mB;hPUVU1!`9^2K303XfOIVS{mlnMqJyt}FV=$&fgoquO+N zU6!gWoL%3N1kyrhd^3!u>?l6|cIl*t4$Z$=ihyzD7FFY~U~{RaZmfyO4+$kC7+m zo+-*f-VwpUjTi_Idyl~efx)!$GpE!h+in4G1WQkoUr<#2BtxLNn*2A>a-2BL#z%QO@w0v^{s=`*I6=ew2nUj1=mvi%^U@2#Wf& zs1@q6l8WqrqGm!)Yr|*``||#A+4#du6`mR^_#?CymIr}O!8Zm?(XY$u-RGH;?HFMGIEYVuA1& z`3RlG_y0%Mo5w@-_W$E&#>g6j5|y1)2$hg(6k<{&NsACgQQ0c8&8Tdth-{@srKE*I zAW64%AvJJ+Z-|I~8`+eWv&+k8vhdJk5%jolc%e`^%_vul0~U8t)>=bU&^ z6qXW&GDP%~1{L1-nKK>IsFgDJrh>!wr3?Vu-cmi#wn`;F`$GNc_>D|>RSuC8Vh21N z|G;J1%1YxwLZDD400Ggw+FirsoXVWYtOwg-srm}6woBb!8@OIc`P$!?kH>E55zbMB z8rdpODYfVmf>cF`1;>9N>Fl(Rov!pm=okW>I(GNJoNZ6jfIunKna-h6zXZPoZ9E2PythpyYk3HRN%xhq2c?gT$?4}Ybl42kip$QiA+ab zf-!EqBXkT1OLW>C4;|irG4sMfh;hYVSD_t6!MISn-IW)w#8kgY0cI>A`yl?j@x)hc z=wMU^=%71lcELG|Q-og8R{RC9cZ%6f7a#815zaPmyWPN*LS3co#vcvJ%G+>a3sYE`9Xc&ucfU0bB}c_3*W#V7btcG|iC>LctSZUfMOK zlIUt>NBmx6Ed}w_WQARG+9fLiRjS1;g49srN1Xi&DRd|r+zz*OPLWOu>M?V>@!i49 zPLZ3Q(99%(t|l%5=+9=t$slX0Pq(K@S`^n|MKTZL_Sj+DUZY?GU8sG=*6xu)k5V3v zd-flrufs*;j-rU9;qM zyJMlz(uBh0IkV<(HkUxJ747~|gDR6xFu?QvXn`Kr|IWY-Y!UsDCEqsE#Jp*RQpnc# z8y3RX%c2lY9D*aL!VS`xgQ^u0rvl#61yjg03CBER7-#t7Z++5h_4pw{ZZ~j0n_S_g zR=eVrlZDiH4y2}EZMq2(0#uU|XHnU!+}(H*l~J&)BUDN~&$ju@&a=s$tH5L`_wLeB z944k;)JIH^T9GEFlXiNJ6JRymqtLGZc?#Mqk2XIWMuGIt#z#*kJtnk+uS;Gp}zp$(O%LOC|U4ibw%ce-6>id$j5^y?wv zp1At~Sp7Fp_z24oIbOREU!Mji-M;a|15$#ZnBpa^h+HS&4TCU-ul0{^n1aPzkSi1i zuGcMSC@(3Ac6tdQ&TkMI|5n7(6P4(qUTCr)vt5F&iIj9_%tlb|fQ{DyVu!X(gn<3c zCN6?RwFjgCJ2EfV&6mjcfgKQ^rpUedLTsEu8z7=q;WsYb>)E}8qeLhxjhj9K**-Ti z9Z2A=gg+}6%r9HXF!Z~du|jPz&{zgWHpcE+j@p0WhyHpkA6`@q{wXl6g6rL5Z|j~G zbBS~X7QXr3Pq0$@mUH1Snk^1WJ0Fx2nTyCGkWKok$bJZV0*W?kjT|mkUpK<)_!_K^OoTjMc+CWc^~{ZP8vgm`f&=ppzKtw}cxwV^gppu}^df1|va7Q?@=(076-( z4KJVmu?l(aQwmQ*y_mke>YLW^^Rsj@diLY$uUBHL3yGMwNwb7OR3VD%%4tDW(nC984jBWCd90yY(GEdE8s(j>(uPfknLwh!i6*LX}@vvrRCG`c?EdB8uYU zqgsI4=akCeC+&iMNpVu56Fj2xZQHs6SdWssIF#Q@u@f9kab0&y*PlG+PynjHy`}GT zg%aTjRs2+7CknhTQKI%YZhFq1quSM{u24Oy2As@4g(bpbi%y1i0^TwI)%1Whpa~qE zX4MD(PgFEK@jZBPXkFd437aL6#COs$WrNT#U=er-X1FX{{v9!0AS$HR{!_u;zldwY zKko!`w2u@($c&k_3uLFE0Z*2vms?uw1A{AqZw^jwg$|D7jAY20j`s*l##=4Ne_K5) zOtu6_kziEF@vPsS7+@UwqOW6>OUwF$j{r4=nOSf-{UC(rEKidie7IUn>5`UoNJ9k) zxJXXEBQifng+Pte3mPQ76pVlZ<`jnI##F1*YFA*)ZCEncvgF-%)0dUXV*pXTT^L`n zL=?A5Vty#{R9W4K)m$`me~*_(&a88M?Eon$P-YdVG}#Gq4=hh#w=`>8f`9}}zhv;~ za?I=Gb3v$Ln?-SDTBow0J5Tt&xPlw|%`*VTyVee1Oh<-&;mA|;$ zoPl;^f7Q~}km#_#HT2|!;LEqORn%~KJaM)r#x_{PstSGOiZ!zX2c}^!ea3+HSWrwE z=6SJ!7sNDPdbVr#vnUf}hr&g@7_Yj&=sY=q(v^BwLKQm|oSB}172GpPlj?a3GqX#B zJko4zRRttIY>Fv#2b#A<_DLx=T@eUj+f}!u?p)hmN)u4(Jp(`9j58ze{&~rV?WVbP z%A=|J96mQjtD037%>=yk3lkF5EOIYwcE;uQ5J6wRfI^P3{9U$(b>BlcJF$2O;>-{+a1l4;FSlb z_LRpoy$L%S<&ATf#SE z;L?-lQlUDX_s&jz;Q1Lr@5>p_RPPReGnBNxgpD!5R#3)#thAI3ufgc^L)u%Rr+Hlb zT(pLDt%wP7<%z(utq=l%1M78jveI@T$dF#su(&>JkE(#=f4;D54l*%(-^(nfbCUQe)FV9non9F%K+KZ(4_`uOciy82CO)OolxisUd0m^cqueIRnY< z;BgA4S1&XC3uUP?U$}4o&r|0VCC7fkuMZBa|2n4asR>*5`zBaOJPWT$bNn(W_CK%L$c2AsfSlwq?A8Q6 zhK&USSV=^-4vZ^5<}pnAOb&IKseHNxv_!|B{g@d^&w%{?x;i3iSo)+vt^VnMmS!v) zM)W)05vXqzH5^hOWWw~$#&7HoIw}}DD3bCQgc=I8Rv|G5fM8O^58?--_-*>%Nwk)j zIfvfok0n05!w%tZ=-dpffezI7(+}yX5XhwYk#0@KW%PkR;%#t|P6Ze_K*N6ns%jOt zNeW(bRsv0BK7ah~9U~UBAVA_L34F+;14x6-;I|o=%>?sS3@dpRv|GKxilsa#7N#@! z!RX~>&JX&r{A^^>S~n_hPKkPR_(~~g>SuPj5Kx6VI%8BOa(Iit&xSMU8B#EY-Wr?9 zOaRPw0PEbVSW@Wk{8kkVn34;D1pV2mUXnXWp{V-M9+d}|qfb6F`!a9JQO_-wlH?zf z4Sn0F4-q-tzkaJ?1fV0+cJBF$f0g6*DL6U3y`Tr`1wzCiwY#muw7Q-Ki)uN}{MoCWP%tQ@~J4}tyr1^_bV9PScNKQHK=BZFV!`0gRe?mVxhcA4hW5?p0B<5oK+?vG^NM%B%NDOvu0FMq#)u&zt_-g&2 z7?z%~p&32OAUSQV{<=pc_j2^<;)`8$zxCEomh=rvMiliShS?ahdYI1grE-M&+qkK_ zD=5Hexi<&8qb4hgtgj81OD(tfX3EJSqy9KFcxpeBerG`apI4!#93xpEFT??vLt>kf zac28;86CpMu=BWIe$NOT~+Es!y#+$ zvm2s*c`J9Gy*ERvLSI<9<=j*O=0xUG>7rYh^R4bGsvz;j-SBO|P^OQ1>G9_akF}D; zlRmB@k3c5!s|Vz3OMZ8M*n0AMTiSt5ZpRy+R1|ckna&w`UQjklt9f&0Z~=->XImVA zLXizO2h=<|wM~w>%}3q1!E{oSq7LBPwQ~93p-peDq-W?wCm8NOKgTSz-P)|cm}S5&HBsx#C@Ba5;hzi#Yw@y-kC~)@u4}Rf?KV0$lPjv}} zcFpNy=YJfsS||9&!-JFjw=@NU96ESzU^gme0_oNy?})II`>Sy>bUCHs_(m&)vn^&isCl+`F~qu8elAO z)-ZP7`gYE2H(1)5tKalz&NJbcutAU&&JFV~$Jrai31^j>vZ|HV1f}#C1<5>F8 zS1RWIzM%b{@2dAF^$+i4p>TC8-weiLAPN+Aa#(bxXo9%Vz2NEkgF&s#_>V?YPye^_ z`` z-h3Cv^m6K%28I$e2i=cFdhZN?JTWhqJC{Q9mg0Vg|FiPEWDl&K)_;Bz_K`jH7W7QX^d$WQF*iF@#4_P*D36w9&iJr2E{w?LRFapwZIIVHGH ziTp*5>T{=;(E}z{1VL4;_H`BAXA~&zpeWX!gN9m|AfcJ{`!XVz48O^&+0Gd|w;udP zzU|DbGTS|7qZoEoDZEH9Kb0%DZvCaWDzuJ=8jZz}pqPn+I!c_+*~>m>BQqN2560*< z$6sx_y8WRqj$SugYGip+et$;iJ!SQAx=HgVSh_3e)MOFHuXD@sg>Yi_p8Sh`{lP=5 zo?AFv1h;KqR`Yj!8Pjji3lr+qae2|a1GmlxE*su%_V)K0Xu0(#2LcO!*k11w*V12$ z;f~i{kI#9PzvFLZ3pz@d558HeK2BTvk*JvS^J8L^_?q4q z);;4Z!DsV!P*M>F>FiF*{|p_nUgy;pDh?J8vwO;emgOAAcxrgDXiSDS5ag?0l*jj< z(khZ3-)>eiwPwpb6T9meeL)!2C-K@z9fF`0j|t@;^f5+dx86R3ZM{bnx9Hm1O$s)N zk$OvZR0u2`Z^QP8V%{8sEhW~_xbZMad2jtz&0+ekxmp;9`ae;_f%-ltk5E%)VT*a6 zRbMnpCLPnalu+1TafJ4M0xNV8g}U4Mjk{le6MA|0y0rk)is}M%Z9tUU22SvIAh7`w zTysd{Pztfkk=jD^*!lA+rBcqb)Fx`A5iaU2tl&XdL1D)U@pLEXdu%#YB*ol1N?4ti zHBQcU#_%UqiQ1)J^u-ovU@-7l?`YzYFvA2#tM0mEh3?CpyEh_NUuVajD16t zyg$C*5du9R=K~6mCJ`W+dFI$9WZZauO)p2H)*SKpHVsIu2CxfJvi2>; zcit#57RP7DpSwMF-VBm|4V5d=tRgX7RM9%KQ0JRo6d<)RmiIPWe2zh6tmswP`fs^) zwy};#jk|NXMqCSfwIR3QZ#W2`(%sJ>qvk=53CYoLmQt9q|2Gm$sB;rEuBqGJA1OUM zoyl4Wy-HYn0J6L=cad8o)R!Ea^;`rSMg9hYo3?Fw6B9dUq75a-MSb56n8~AAsS(JP zZ!1khPu}!GRpsj+jvl`N1tDD8m1myJCI3c-c<9U-1Vg`xJO~}5_wvPXYh^=Boo^|V z3Tp}|lH!9m4Ipa_$p;b8fjUd=zc4iO7vr)M&Xs0_m$fgY@+hB9%K~4*9$p0d)m2bO ze5JH`W0fnIKdcW!oO#^g1YceSQ4u->{>u@>tLi!fky)o&$h(=he?Fe_6?}O~iSf(F zV&(P~*5h>BW{3e1H%8*7#_%L1#>W97b0@jHtliES^w6w5oldI7QL+?I(Pl$DaN>~d5nXx z;CO1E+S?3E2PLq~)-?ygkHAO1m&hOYmj7?;2XM!$D^f0l9K4P{n}mgb{CoYH6RJ8o ztydc6dNqA)`CG?=Gd~EIbi`UM)eyzGF^+i?&TOdyW~mFH_^Gye(D}clDVFQ@V2Tvy z7rQIaq8Xx`kC;AO-_{k%VI2e6X@bIy^mupEX%{u0=KDUGu~r6lS*7GOeppy{&I&Ly zjOTz=9~jC|qWXznRbrfjg!1`cE!Hzyjzw6l{%>X)TK(UEGi9Uy3f9D6bbn0gT-s`< z8%$Msh!^8WidX7S;)n2jh_n1-QCtSyOAKcPQc(Xlf0*Q|5CSBjo(I-u!R0GJgzTkL z|6QdQRrUMbUO|q0dQ%+d^4)*Mjbm$R}RUcz(7|E0Bq-bAYY@)OsM<+2>}CV zzPBgeD~kBHE(Y+@l2orJrdtV7XXq_V8IETas%7OCYo`oi)+h&v#YN!Qpp7drXFS>6 z?r-q7px+(rIy+bo1uU#I2A5s@ASe01FgGMbouFkhbkm-9yZ8Q2@Q1vuhDQ3D3L+zA z(uz8^rc24VmE5r0Gbd;yOrXnQKAEBfa3@T7fcF$#QYv^00)VZPYehpSc@?^8we}o{ zlX0~o_I<`xSfI8xF(WXO-DX1>wJ`XN?4rw@}_RLD*${$}UaXL=oM(=SDMIxZj1Ji#jAcrH7nYG`r z#ewodj>F5Bf9j(j`a;>)=*2j_ZN}vf!~Hq`2Eyt;9UH1_(yjq1OUO(1M0lI3FZ2j-fU9)L59v&OiQ>5$;d!jg?Fo{Svf5t5FCZbb?)* zJN=Q!?2BztV$7)CWtG0MO~Lr4E5>aoHD5N4(+@~gQEbZTc4s3HrIl_G23PCng4Y3f zbLZK1A-x9x!)WwuI=UBkQ5QyE^&Nrw?@fsRKK41G9-xq=#VyO%CEo`{_eioDj%M!3x=>I zfOPFiFX{1t-|+3E@?UuK=0miGN04hW0=JnJrEyWw{Bg-jMvAA}cg<5LN1c5BQdrIZ z#+bxj9Jbu`11@IUjU|RKfL(UzRlVB4XT ze|(WaxL$KiRqkgCr3^Al(19!_Y7b=E(4Xm7LCO$y5+k;Fu6B#=OSzW`-7p{zRv-_) zPr!|km?8aF}+3hm)QG92YaI+jctX&5IrvTUGf{Y$)TK6)s9v!SMhU=HIpEC~2 z4>o14mG$El2sTA(Ct?xS!l*x7^)oo}|3+BF8QNe;bBHcqdHVmb?#cbS*NqZ%mYS~z z`KLoq7B#KULt%9a#DE%VTEo4TV03T2nr`FK5jUTA$FP0JH6F9oD*|0z1Yf2b5?H0_ zD|K|_5Zk`uu?ZN0U! z_mL>>F;mnHU=@to!Vv*s4;TQr9y)L@1BXXz^a85NSifPTL4h6I>+m_S3~FkXB{N?E zS<3ue_(wqaIS5;4e9{HB`Okl9Y}iFiju+oTqb)BY)QT?~3Oag7nGu-NB5VCOFsiRs zs@m%Ruwl^FuJ1b}g^=*_R?=SYJQ@7o>c9j>)1HgB zyN9LI9ifwu{Shlb6QO2#MWhxq~IG!U^I!6%5}(sbi>=bq8!8@s;4Iaun#kvh7NPwX34Rjbp2f!D)cF&sNIO%9~;C`cs&ZY2=d@c3PpN$YZjUT}X7rY`dlWX$yc znw(7=fzWapI=KzQnJ(6!o0K_aDk!^dZ#)pSTif+jQtQXga$bPApM z=);jZ5c*?*GoeGMnV0=RrZucRRYBjx>tx`A3OuY)#tp2w7mh}&kj)SKoAvbbf;uO! z?+RItUow0xc*6StuO4D--+qY!o}Isy}s;ts5aM5X~eJUZoLOq@dGv=a4hHJD<* z5q{dZSN{bv_(Vj#pFm7Q<$C;MwL|Qizm~QCFx~xQyJoCOZ$`sYD}}q>PwRZjb<=E< zAeMP?qVfM>xu2}Il2xT6={KBdDIstxY-`5IWXN zUiWV&Oiy5R_=2X9Y$ug9Ee=ZSCaza!>dWBMYWrq7uqp>25`btLn^@ydwz?+v?-?2V z?yVwD=rAO!JEABUU1hQ|cY+_OZ14Hb-Ef`qemxp+ZSK?Z;r!gDkJ}&ayJBx+7>#~^ zTm<>LzxR^t-P;1x3$h;-xzQgveY$^C28?jNM6@8$uJiY81sCwNi~+F=78qJZ@bIsz1CO! zgtPM~p6kaCR~-M>zpRCpQI}kUfaiZS`ez6%P6%*!$YCfF=sn}dg!593GFRw>OV2nQ ztTF6uB&}1J`r>gJuBP(z%KW{I^Uz%(^r5#$SK~%w1agl)Gg9Zy9fSK0kyLE24Z(34 zYtihZMQO^*=eY=<5R6LztHaB1AcuIrXoFuQ=7&C}L{c?Z$rto$%n=!whqoqG>#vvC z2%J5LVkU%Ta8hoM($p1WqN}wurA!d@#mQGU5Nb>~#XC84EYH)Zf&DZR!uY+-;VqS< z@q?$ggdX#auS#%%%oS^EN)?JhSR4JYpSgGRQZD<9!YvvF+zp0>C#$!x*x}l8U|Bb& zv?v*im5Bq_(5Wi40b1^nKun$XTST(a8yOAcqQZmKTgGLo)Ig6JuEh5J9NnqJXin@Gxzz-k6xXWYJ&@=JZw=$+ zFPGde%HsR`gI+y`rtiPaMYwbtyp!sVb!pX~;c3zLoPO0eaZSV+O_z z%9H@UhqNowzBTPcMfL6kC>LRaFF6KVaSv1R@%4}rtleX!EMnL`rethYrhTLj1x$tj z;)H!fKo08&T(;i|FT&rPgZ*D0d=B2dXuO_(Uaoi9+vEhs4%{AD{Fl@4^|`X=PvH(s zI7$6bWJiWndP$;&!kSCIR1l57F2?yzmZm~lA5%JKVb;1rQwj*O=^WW~`+n*+fQkK0 zydInOU1Be2`jhA!rnk1iRWR=1SOZpzFoU5{OPpc&A#j6Oc?D&>fAw=>x@H7?SN;d^ z-o&}WR;E|OR`QKItu(y4mT)%Pgqju-3uyH?Y@5>oSLO2Y(0(P!?_xOL=@5+R7rWw# z3J8%Hb@%Pzf^`=J6fEJ_aG6+e7>OUnhaO1(R1<6>f}L z?d@Wnqw9?^;2?q(b@?Wd=T6r_8a@Z4)*_@Q7A`+ zW3w?j!HW0KbhxF%D`9d2HpvIrBxM!36W3Yh5=8_0qYfnHm*yiLB?Ay|V10N%F9XYq zanaDtDk$rS+|_H_r|a${C}C7b{E)Ii20-a?Grff$E?&|gWF<#Ern2GqhCiS0~Y%knIi8zY^lE4qLaR-3M;_Rkz(s;wu z9207W1PXIe#4h4Zw}dvdV&FYcnUlD5_C4hzJ@bPSBVBLpl$&52mi+wwH;svyVIzAB zoA+NQ;Hpqh?A}^Et~xhl>YQNQwh20!muW{ zq}|Pg3jHZWnDBN?r1KhiVG$%Sm-4+=Q2MZzlNr3{#Abqb9j}KK%sHZj{Vr2y4~GIQ zA3Mz1DjQ3q(CC~OyCaZn0M2!){)S!!L~t>-wA&%01?-*H5?nzW?LJB`{r&)vLB4!K zrSm({8SeZ0w(bL9%ZZAZ*^jf=8mAjK^ZR0q9004|3%73z#`-Npqx*X^Ozbja!C1MW z-M~84#=rU1r>p{+h9JU<#K_x$eWqJ+aP%e?7KTSK&1>dlxwhQmkr69uG~0iD@y|L- zlY0vSR2|IhZoS6PpfUai_AhKo2HfdD&mhv#k51CX;T z*sU)XbDyfKjxYC$*_^(U)2-c0>GJ(zVm$CihHKlFSw&1A$mq$vsRt-!$jJe3GTaZ6 z3GcVvmwZ0D>`U+f3i*pQ>${p1UeyF~G9g~g-n{ThVOuC#9=ok`Zgz@qKCSN!1&P`N z=pdlGNwal%9;)ujwWH*#K6CQG*fJDAQiKlO2vKJHeA1lj&WQC+VU^@ea8$#~UOX$*Q!V^8L- zL0$W5(Y3=??%&j_WUq6*x>=?BfmI*d8fmDF*-!XVvxL8p7$r+}Igd_(&`|D*;Z#GE zqm{tHx&aHBpXw&~l6>7-FlyiSPJtTJblAjLU5Ho$FeN0mDguFAq?r+6^~o6|b+rfE zGVcZ&O-X~tE3liGcdI~hHSCT+&F&uH8rr&f{6pr^1y5061`fu~=^_|Idrgti5+*U7 zQOb9G?Rz$j-G0Y}x+i{HB0!4ZmKzykB<0;Rbmo2)T4|VdcwujI_otLG@@8OOKg3kw zP|0ST0D4@zT?O=(0Pikp)Rpwxw_VsmW4!^j^sFd6r5l zw}SG_HQPs>ae%Bq{sye_SaBX%|F-}&^)Wz@Xi<)YNbO?lPs7z@3c;$b^Aw@>E%mOj zW^c%IdtC(Kk@s*}9NbKxEf8SZtP+32ZTxjnrNWS7;W&D~ft{QY?oqOmxlV7JP!kW!Yj`Ur{QbbM1h=0KMaIAmWiISb7TKd4=gMeo+Tcz2>e#NihnOV%iNdx` zeiuoOK^{}D+M+p(Y7EC=&-`$B0F< zQ=zHaM;&QQR4jM$sG=N&sqOvD_Bx*drQ6c@u0()g05cwl`Xm{!S_Nuaa2KlL*rmmk z51yPE)q?Bl$sNM474Y!=zZ zc{EVGpdJ!Su{Qq%llR5O6#zK8l(ld*UVl87@|iaH@C3+*;XBxjEg&fsQrzpMo3EEG zv*Tpms7a;7!|iz8WY7={0a$0ItO-(ajXl;wX_$$yzEF5k9nc>L3wv!p{8h2)G0W?h z{v6vH=7+>$Ho^+)9hDtCd+S_yh8pzS9$)hYev-=eDu?lGIR;-fgz+dr+wcmM-^dZp z9}`&kAf$~z1ovF)>Hgxc!Xe3cju-jQRluCm;c_1=PYQygb?Oxe z!QG0L3sT_k=WpfOPL#|EPlD^t;ENCC39O?tHd<(kfx7SOcxl+E#;ff19_+{vbkZSvbS$I{#>31KZj^$n%ayX0jj}EvsgnHg16P z_A6Y)pdp>kLW<;PtR*Vs#mVb%)ao7AXw{O&hBDmD;?mc3iMH;Ac@rZZ_BQa8CQ~|0 z&d1L{in-z--lBO|pxqc%bqy^~LAGv=E*eaVU~OeuVV{d`Vv#-_W7EYdTDzVraG9H+LC_dWcgZMn~KcP)XvKWbcr5&d+=a>{*(Ha6Y1$==bR z{O-?$7H;`2dt0B%Vm?6`_?ZOjJkyu9ZJsh^WH*+es&^@KDcR%Zej%3PJ*XovgyhTbaH(!H1H_OF~=*f55Jr8A%uW zz5IoAB~1e2-tDGp9}`MnavAMy?jgPM5F%y`%$}dFLrz_* zIrO=afT8+AkK5B1s3{ZDVP$g6y$-*U*=?-fh!cNyn3q6YhNhfRxW&GLIJ2#>9bYMD7-F%{|Iw%@a=DoAAU;3k9p$`V zImKm{5HU~wq|nQFwab)_7lNckW#1z2$|oW5x7vDbBURVjw8674P?L1ogMKpHoV>;# zO%*1OwI|($UOr#hL(*M~qsn3PF%_|15uc%Hy9@D>_~N|?<%lig6yKX0a#1s$o(^Laj8bF#5fGPOFMGmMiUaxSwE}Qf#SG_f79d2Iv=TFBXzTpr$^avJ?=|arh2<+ce}&248Kw0} zhlva`wD6X~s7|37la4FnFOgIHhBiFo`lw~?lSbk{>)P(3jyVhM4O)a=GX3(sW1vIC zz0mJ>;J{!eN5#nf2>$u=3Kq>`7u9QnChi8>CjONBN-b+W_UQIuN#{N$Q<$}IOvpQP zB&5ZrY{V&D=4)voh;6<1U`PFA>V%XUW73S9D^J>cQYfzIyIV5i35WNb5K9c^|M}=* zN_C3rnjCZP1^v{;EaGK7Tp5z~B#?f5NZaAsFUOLK)mI~bJTaL8DF_eRikE{%^J?y9-n_U32EKHPCkB^ZN2*zk{bC=GM%_I z61}nkr+Plg6S0V=mY>H_KQU&)P~=y3$#$*U8FunXkb_e1O-7t@m$5re%u!_G%^?_| zRIJzg+lX$}+ba|qx)Ec6c^ip;`_QfQrD~SPa4MoyRUOtX&~^XWcO^a}KBkXK9J{ZFOA~rovYa0!7btTC*=xNQrwJ)$Eu`TT$;%V&2@y@$ISdNn ztbM7|nO+U9r;ae{{;QiNEYpe4nrFq_x3 z4Tvf^b(I@_3odwhVe!aC0X&~inrYFu# zh)+eF__8ly&nLr4KlLWl%B_ZMo=zCH2QfO^$lJ zBvU*LQ#M(5HQ}2Z9_^y~i@C#h)1C*?N3v68pY+7DD09nxowdG#_AAM5z&*|-9NcB{ z_xKUY>Ya7>TO#Bat}yM}o(~8Ck^!QHnIj8N9}c*uyIs}IEqGn`xP;q3vhW6gsqUe>`m1 z)~ad@y1=?H`1SNl?ANCs5ZD`8tG&Hi=j|R%pP(%gB8pd)Q--E?hWU@)e?>SLV4s(- z!_I^oVC0x97@I(;cnEm$ttKBnI3gXE>>`K?vAq~SK?0YSBsx{@s1ZdiKfFb|zf}ju z7@rJb3mC{U`$R`YS(Z#KyxQx_*nU`kf;}QL%bw17%5~6!mMao^-{FFmX}|ItFuR~F zAAvTF%f4XKYo>2-PJ~ro@Ly#t@Sf69CrA+rmMRpihqH7V&SXX+$Sw`HZF`I*_3Vjz z%kPMyN0J3sl>X{-h12)j&XRhAAI;Aou%%z}gI>G+32z*qpZg{m`CezFrzg#&yc<1` z%j~}PN!F5Ddq(>R{+t0v{j6v^0XwWGu@5+`-$m`_>pCzM`r}wz*8Qv=$|P0R$%tJp z>D+N4GZ|Tg>XL<6XP9_wQRGDs^1icY*5GP4>*7mGMr;V zI%kT_^_SQml6$#uRE4Ps>}?ES)_XI8m-%GN{o^itb^S7e_bM$-wo_Ws)W? zx4_6#*X;T$n2N==N0#xzb~BQU#%^NF6|~898JGDbQxjK(ex;Q}_Qn@?Y>!kkUYUeY z&VclG1#eDPU78K@^p3tAUvZi1(nFfk6AAVHWt)Wbi7dPbjA4isOY~?*1&asp!wg#Q zSpSI6*!TGn3|-%vuJE<9V_1EKkz_0%z}Mb7;E!uz)+0^k;@x+<5tzj5 z!InbRtc`YwNCbCac{plY&Y}hWp#PC{o@5UsBj#tv3f^ns^`;$MVN?>q!pW+MYeC7= zkWr1kAX(0xVQ<{qny&CO*|g1{Mk_yE>1t}_YT<5#p8P7QXf;o|s>XQ#SoA&!ddE+8 zOM&VsxsRGS(Spli?P$^pK7Ty{v86RP_6h|MU^J z`J>vn0|BG3Vf!uR0zM|GwtiTPZNb;a@@1+V5+$P4GI_&$%6m!YRGL=lz5kh?z#5f55 z76COi1`R(5p69;ThuQnJ$R3w?I?jigai2arApagd=^tT~oMUWp^u|H_@zXBjpI)Dv zEFc^_`mVu5U*;ClT?x-t9{#fto_+92GF^dotz0sFWTDwZ`s40AY@mv+Qh5c-Ts8Zp z!(v7!zPvFhUZ-xkR!IvaW`{PqN|k)L4*anbtmK+UU&K*awl?DhxRalbtmDw`$#VzK zYFaG}?$F)1j`Qx7wbn|XzMJ&g@3Ai#u5M?%CLPghk;lD^)-|21{Sr+M(suBU4}6CMTMxc_tD;X;z<1-{FeHte=kh1B9O6Hl z!v2i$d1VFC&z&58zU0`G#7^K3Cs@9LYN16O%Vz)?-iQL!G6&sg6aaX>DBZmm@lFrRJpcL{K3(;+`$9GDFDw62Mud@LZjabzVC=w$dx>TQa}U z-{dhKYTYx*C=Fio`ez@wrzx+p%Fk3i&v?6ENXMb3p^?;_&huLLueDwr zpRqHbU%i;9TmexFxCS8F1rPo-ea3!}!ew7{(($76Rdnfa`~$9{8H@f7U&0&HjZ3TZ zuBc||%FljS_e&wNZ$1ezT$*})XAfm??$_cY_?13vM^tT0EKY2ptb+v5P10}a%aTk_ zh8@_T{ns2@jTFhv`)-Vxh}u(0DiL0MUi(We_eic$;gCoqj(T_S{jDo^PahnKJUp3@ zMOk+%weP*c%K6VFXR2icY`J~-&fVMYUg6fsFI->jlA|9`+07y~$Fsz}^;w;mNk$ms zu?y)VA@QH__tvYDudhEWuDD20H&uvrf_boY{($?5{s-SDjyRxSC%%2Xs5d2dpjdk$ zU*NURD#ovwIfd^H{fXR@UuaooJtQr7$d0+(K+1UEwtG9_T?sb$ExV$e-bpf}a@YUe zuzInI59w!x;<)>Be;a7ukLW>V=8~J6nKU<0@H+SQ!Be;1Za_pw#hiuW_PMPBo8W2G z*WDtiIAN<>HQOmh)DMi{s-0H^GmV3QMf4Zu(zXT!-c;2)uv4gUwt(-}-N*|KUOo$h z+Ak^R)h8yB5UD8 zsSjHgY}KguNi?xV=tdCWqJR!~dDpFQoRJOwxrWH^vfRq4%)v;sDfIjsLXF^)uy>!i z*S8Njd7yfa`+7(|8H9j73Rh|TwFpF(8H-p;RLLIU>k<*qI%A*SL{u$%<=X@Jm1QFe zVkQ(X8P4Tohl?_tSO__^aqaI?k$CC8uNLv2mp_zD@4oDaZfEN5;3#XY!L{8B!;Dtt zb~Zge@JF|#Gsk^5$-|(OPI73po|WZh<`UxaH#Y2!&p05Ph?H)d3Bc3J4sDi$f(6K`?&D&~eHVuE@_Prkt>_&8&aq=OzoN!ANkvho;qIX(g|d#EKQbJ@;-%_iARmgSF1fEK z@B4W@5mDME7AzfL**c&2#B7xO9>rA4x$rM{N=%0=goumK1kL{TF@CSk0yvqR2oo&m z)?nyiL$9~Jt(qnEuWt9Hc_duim%|zJQYiaF*~orVNDvJB;`%ZW_2x%Uu01LeX-JP& zD&fas6d3=igAgcfeki79{5!XPHHYR#nfLYRKv^wkv~cnEbLHMwQ8%yCZI^rK!D2qT zk40Vg;e!_!3d56&umIuidN?6MTZFzHot}AdqKzDh#w0s`)cV!2A74RSH1@lDXtC38 z+UhO4A9?oZEOV{bIgGd1{2qMR&xT+}q!=I8m)W23v!W2WPC?Tf!F!e%_(m^lQZtq* zYwi}gY(KZ*Y^OWRNj$Ph#uEEBM+wtN8QFQ@^`GDOln^ioNrmtvzNNi*qS5lPHxI96#sMil*teLVaa%$msF>@5p#SjT%q8|<4ZOUB#!-kG+|eFSED z!|3c8fXaym9qH`L;pmqTWcG}WE$(h1sZ3seM>)E3ptoP<;~h~qe6XA)lGVanf&->P zjZwi;_;Dt+bYdAeD_XSQ-DgXRXqLv`3Wcgl}myA-JlzBBIh zWq4Q*9#(zjAk_H8VS_AJ`?OS*^gB-rp|~qt;v(C5ef=SErv;~zL64hW`#g!UZQcvZ zF6Ra@S@YhVSkSWVAY=Z1w)w-hfJDRwKTUH0o-OG5TlW0HDH36hIjnP=?A+8u1)Qyy5U8Gi$! zt^!vy|f=YHfQ`ZRK?D zXXn*kItRg50vr2+_hV5kjOleg#s~z(J2p#`=1Tq4#JS`MC^e4p&s7Ir=3m(K$LW#` z=ULCoWtna!so+QQ*JHb~6Ps9_&Ag>9qsUskp0pKbi`n?(u3&@QT!?}N}rXn z>1eHi6(@LicU*AR1obe+nbzTCD#VTJ`PFLRT(nc$NWrhsgRwFni*D(#?W^x=J6?|b zENSc^D}s>Y55)PzFs2d_2;yh89E0ZIgs&>6JV=pL6k9g_(`$04EoY+Zjn}}8e#n83 zJ=zB>BU<253Erdo$wE4^+@QQJFZyAj#(InFlN;!UGg96R@{Y&%OlGG;dM)^X8=Ddw@&2Vx?zui$tO z-{zgaU7&F!xs=e`Mn}r+xrdIAmkraRN_7P1?qu1|TZ%1QR(Mn?k+pq`Xys2v9Gs=a z?r@g&;UKcM#?36r9k*eVD(}9qe8?irotsn0+eHH8*4 zPX@Lusr)$J%8jarx5ssEJ?twFyu4kAbrf`96_z{6at^&UkyDzFa69RXP>PeK+dAWqE5<5P+aHa zs<<*+OO_2ObTXau%y)Nn{(p5`XIPWlvi|asjYcui;E@)Ig{YKBXi}spqC!-P5owwL z3L*+9;0C0G!xoN;4KNfDaElv>1#DMDglI&MAVoK2+c2Pr8&sl*1dYj=^>NRS`{O&%YV25@5*eoOvpD_(xdKsnqb^`T}bm;n0BN9ben1Ynyi*OOf;qLpf^ z!T{}GzkXSszN_Xqzp>}S*Im)_Y8~2|B*ybw(U=Q)5_NcMkT;)1&52YQJB)Tn%kPK! z@3;^AI){B(&UOv<{v9KKJrInkdcXV0%O1%1=7vYV*j?v(Kp~arZio$#(A@$kYB3aM zRdm4!^Je15%66($EkCIWGhi@=kNAyLJ3ydlJnCpPuxH0+OA}J)+t8d7nT->##Nz4w-L=S7ExQt=Rx}S*mpT91(>t~qe7tM%e|O)TIO^dP zfo61GNS=cJbLutqUh84?7X#bq)bv57s&D_zm{+xNv7vHjb=_}j-Lrj-Ss*pcD@ts$ z)5Dol8Z_&*1@JdAQE7SL$*!TXI|YE7q=YGkIiUeLvT0)14Q-ivs|+cqeT6DTi9eQ)h?Pu9pqmH51B* zFMd|;l2@D4*56|EhMFlDxl2i<8qq=c+AhMYS3(A28#3DZ;_Ln>RA3q#IAdJq7M#N> zTZ8t=_>lq0=W&w|bdQ^sy&m^@KR)mNi3|1<6|OL(0KLtP#I6ix$2b{-Y9GP5I7 z8AJUSCnlia5vWawX%ZLWTC2UV$cn^sfv68W!6)QO;ZjnX=7#`$ZPRG~irfl)ZUJ^D z{lUk?(*SU7XIiS^H{Lpxn%542#PgxdeG)Ociej#(uvX)z;Z3)<16Yhd z-sv?qQ5D4a)ZYoYPRep2Zvom@U)HKq*54ZEwdaEq^FZG#(CyG!=Vw(0j8CCmP~`_z z=OR^i&WkDCf2cLvWm@d?)mEgme{hA(o#xAL023LZ3(82SGRg6jJF7$kZ4! z6*FTm4y6v~CP!3$+fxg{QeFo24<3iucgI!oyjV|9Dsx}r~4X@lt^VaH$u zD?87}1Jh=?G8OYg*ts2k;X9{f*Za?yu8IUUfyuQ**wbcWT+KncjD^qQ3h&w2+S(Mj zZM~?Ot%ggTIHwkBkL-4&jI5R=B+MCOR42bKzC2M>l?1%x2Iv7amIfQ1B#wwfD`z|m z+E?G+o(tde*Ws?;Wo4p#Yy>Nnf|*b<nj@-s(rZ)-U@ z(Xe(qZ1(_dH|J3yWu|bAPINK}DwF(kZ>FKx(?ZmU^KFC6*bh$;FKGh~pH1 zozA+kgcIk9@2aAwEJ=VYizT!sxDXX$N?XDiGKaaT-OU@Ib=~4DmgEk&{2D@IvyjF* zuF@sDcuuqx_FAgx;B@@8gqjMh!kQeEKA*y4+q+^4&uc0|>M;$Xb+ z@X%eUx1m%$WSP}Qchx68NQ?dO!h`6;Quq+A1(RORsQ-;6bZ90vj#^0(7>cLR+-_;9 zCd@b~B5V>$tpjkQU#BD%9^zu7-l>U8nzt+XuX5cYDCHYaX5t~~3?lpa;)Mr>q;5XW zu(Th;fr}-GkP`K)u97(#UB|L3f;H7Cd#Pox+auV`=m?a=mSv1v)(V!E=$%gkIJZ;` zZj{Lb@bhs%bRa znZw9cD$cDFVHPtpXwY1K)wys@LS~;!qdqkR>@&RtP>?M^>xe{4N#EtZy4zZ5Ar$ZF zV=X=(!xin-58MC<+b~;jk8Q|3B3THGIA$cM8Bg)Yd6ygP#i?4VrX3OvP_k5i{Cppw z-{$XwrJ-+X$ccJ(Q{|?T@U9=-?qlsfA43%8t247KZn?`+C4e`b-e^(df*iW66=Oc2 z3w9UhohfdY@pH1MZ}vc<1osV(2CGG)Ree$E-T;8>$zw*>x-505b&4(shMGIjbAfLS zEZ3ys(`SmCWc(75)^=aKer}>67qj^nGKtCK{35I|tA}wQa!uM!suX%Gb~ylORGGc( ze^|m|N!}G0#Ph|;wSXz`SByQM>lPM#8>mdSQs`7RxkXaSAADYA24u6xWqkIXY?o%z z%TEFL+wNW^&nrvaA1_#P%&Hbzrjl!*hIft>F0@g0IVydUU4MJgS3_3Js8{*>|G2jC z4%n#cOy9b2Xf&Pw=14;0Dtf00C^Z$I-v05OqtvN9>sAC&oV1Tk;;ku7VR`sQK4oFq zQ8)yoZNuTwV$t13|GCUIC{ID_r7M5&R*zhsxbrkg;EgMtL|9ne=^}BM!dxV!KDeXkWA^MfQTkQEt8~t>JznNh%ULvn@dbQ2cyf} z|C%ns#NJU}SHU(7Pg$<&8uDK>d5GZJ&`;CcfGP(~b-#UusXevc^q!km1X6_wVMqGk z^m&ZS6#42?p4c_t1TA$_+}h1L2c<<=$k%;v+D!<@j5hs|{>d18>~~v#oq4yGyS@QP zgTX2oJbEy@eJbo-f{ZQ>-nmB-#AqWcHbMQXFi*T)0n!(HIexz=pp<(O*DMh7CMupX z)ei1ZYuIW~E={-ND*nD;okiZdm!?^|LjLZhs*FHZvWld5TDj zcvWB)`-1Me9bu`*4M=CO6ye=pMgxlgYvsh2rV#5Z$hFKw0GX30%oufb=hJ0BFIJH` z+Fii4gQ+7!)8K^yc*PVEW^#f!|BW0Q5*`IewQ5YDFh?{x1L7tlaUAX@3Y+D>6FPVf zJzOGex~H34`8eq+TL$FsHm+27RS>3$CG;>0Jj4*1ukX$za})*b^S5p}I2jbFCHLsA zzYwAyftMz`uo2c8ieQcy-p&9iP3fMk(uRw+OlBPm`KCLei6g!|Vnk*-kjs>A25MTE z5GLDMV$70AC0j-tx*0sCruvKh{fSM)3X}13U>m|KeaOb`9^}v^44!$`06-JHf@L4EKyxV)M!8cL zi5p9kF97RiAT92!e?%9CP=qX3wyv^A8q!w%07d(9f-U))uDgsr4FDVL;|%r)fw}-@ zlB$F79X^EKYF%8J7mU?3VzJoYQ0<;NczW1jH4=4kEh_)q|^9wj zIsn-SsmRx0_EJ7(6WypwptIwZ)-T<__UgUu?BXt zoIf|a!5`?&JEb$w2PZSqhA>J;GIA^rJ-Cpz8MKX~bcqZNOUzPtu|NMvEP>+cO;V*W zNQ8YPENkr!)lN+tlxB79RUD20$)+_P6Jc`+4q@%Kno{F+#1qR*zrj%T>nTSceO?a5 zyqGDa59#G6k*RXu6+#=e=e!~i1Y&15!cHmE6sLh_K%Ppv$tFE-Le3RQs-nx5LB>gy z5A))kwkxWSy73{@I{%{DY8X+2o{CLJb~R$3r=oT^P~Xo$2lKz8?Z!3QLn$5l#L2k2 zb1=?UT&c<8!&9gW1M&jI!5%dhJbD3nQXpaeNJ>=zR+EL!4iY(nMBQI+|2J+Hw-WMr z08Mt9h8(PGbY?zKtk=cqw(yW}1A#htn* z8&}5Y>$uc>Lv!bSuWQ5UB&ct7*jiZAFpxz|%xO&5kg zzlf?6xy7H3G^*wvP5scW*Wf(<&eP!YIUf%&HT?K)RWmKg$G^=mSoi~;&9dU%{o}WV z#BX;9+q)fpVU`>Vdo~AtYK)`7z*H;dc-e|q6Qt;3J0APUL!~g&Q literal 0 HcmV?d00001 diff --git a/packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..ed4cc16421680a50164ba74381b4b35ceaa0ccfc GIT binary patch literal 3276 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 N;OXk;vd$@?2>?>Ex^Vyi literal 0 HcmV?d00001 diff --git a/packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 0000000000000000000000000000000000000000..bcbf36df2f2aaaa0a63c7dabc94e600184229d0d GIT binary patch literal 5933 zcmZ{Idpwix|Np(&m_yAF>K&UIn{t*2ZOdsShYs(MibU!|=pZCJq~7E>B$QJr)hC5| zmk?V?ES039lQ~RC!kjkl-TU4?|NZ{>J$CPLUH9vHy`Hbhhnc~SD_vpzBp6Xw4`$%jfmPw(;etLCccvfU-s)1A zLl8-RiSx!#?Kwzd0E&>h;Fc z^;S84cUH7gMe#2}MHYcDXgbkI+Qh^X4BV~6y<@s`gMSNX!4@g8?ojjj5hZj5X4g9D zavr_NoeZ=4vim%!Y`GnF-?2_Gb)g$xAo>#zCOLB-jPww8a%c|r&DC=eVdE;y+HwH@ zy`JK(oq+Yw^-hLvWO4B8orWwLiKT!hX!?xw`kz%INd5f)>k1PZ`ZfM&&Ngw)HiXA| ze=+%KkiLe1hd>h!ZO2O$45alH0O|E+>G2oCiJ|3y2c$;XedBozx93BprOr$#d{W5sb*hQQ~M@+v_m!8s?9+{Q0adM?ip3qQ*P5$R~dFvP+5KOH_^A+l-qu5flE*KLJp!rtjqTVqJsmpc1 zo>T>*ja-V&ma7)K?CE9RTsKQKk7lhx$L`9d6-Gq`_zKDa6*>csToQ{&0rWf$mD7x~S3{oA z1wUZl&^{qbX>y*T71~3NWd1Wfgjg)<~BnK96Ro#om&~8mU{}D!Fu# zTrKKSM8gY^*47b2Vr|ZZe&m9Y`n+Y8lHvtlBbIjNl3pGxU{!#Crl5RPIO~!L5Y({ym~8%Ox-9g>IW8 zSz2G6D#F|L^lcotrZx4cFdfw6f){tqITj6>HSW&ijlgTJTGbc7Q#=)*Be0-s0$fCk z^YaG;7Q1dfJq#p|EJ~YYmqjs`M0jPl=E`Id{+h%Lo*|8xp6K7yfgjqiH7{61$4x~A zNnH+65?QCtL;_w(|mDNJXybin=rOy-i7A@lXEu z&jY(5jhjlP{TsjMe$*b^2kp8LeAXu~*q&5;|3v|4w4Ij_4c{4GG8={;=K#lh{#C8v z&t9d7bf{@9aUaE94V~4wtQ|LMT*Ruuu0Ndjj*vh2pWW@|KeeXi(vt!YXi~I6?r5PG z$_{M*wrccE6x42nPaJUO#tBu$l#MInrZhej_Tqki{;BT0VZeb$Ba%;>L!##cvieb2 zwn(_+o!zhMk@l~$$}hivyebloEnNQmOy6biopy`GL?=hN&2)hsA0@fj=A^uEv~TFE z<|ZJIWplBEmufYI)<>IXMv(c+I^y6qBthESbAnk?0N(PI>4{ASayV1ErZ&dsM4Z@E-)F&V0>tIF+Oubl zin^4Qx@`Un4kRiPq+LX5{4*+twI#F~PE7g{FpJ`{)K()FH+VG^>)C-VgK>S=PH!m^ zE$+Cfz!Ja`s^Vo(fd&+U{W|K$e(|{YG;^9{D|UdadmUW;j;&V!rU)W_@kqQj*Frp~ z7=kRxk)d1$$38B03-E_|v=<*~p3>)2w*eXo(vk%HCXeT5lf_Z+D}(Uju=(WdZ4xa( zg>98lC^Z_`s-=ra9ZC^lAF?rIvQZpAMz8-#EgX;`lc6*53ckpxG}(pJp~0XBd9?RP zq!J-f`h0dC*nWxKUh~8YqN{SjiJ6vLBkMRo?;|eA(I!akhGm^}JXoL_sHYkGEQWWf zTR_u*Ga~Y!hUuqb`h|`DS-T)yCiF#s<KR}hC~F%m)?xjzj6w#Za%~XsXFS@P0E3t*qs)tR43%!OUxs(|FTR4Sjz(N zppN>{Ip2l3esk9rtB#+To92s~*WGK`G+ECt6D>Bvm|0`>Img`jUr$r@##&!1Ud{r| zgC@cPkNL_na`74%fIk)NaP-0UGq`|9gB}oHRoRU7U>Uqe!U61fY7*Nj(JiFa-B7Av z;VNDv7Xx&CTwh(C2ZT{ot`!E~1i1kK;VtIh?;a1iLWifv8121n6X!{C%kw|h-Z8_U z9Y8M38M2QG^=h+dW*$CJFmuVcrvD*0hbFOD=~wU?C5VqNiIgAs#4axofE*WFYd|K;Et18?xaI|v-0hN#D#7j z5I{XH)+v0)ZYF=-qloGQ>!)q_2S(Lg3<=UsLn%O)V-mhI-nc_cJZu(QWRY)*1il%n zOR5Kdi)zL-5w~lOixilSSF9YQ29*H+Br2*T2lJ?aSLKBwv7}*ZfICEb$t>z&A+O3C z^@_rpf0S7MO<3?73G5{LWrDWfhy-c7%M}E>0!Q(Iu71MYB(|gk$2`jH?!>ND0?xZu z1V|&*VsEG9U zm)!4#oTcgOO6Hqt3^vcHx>n}%pyf|NSNyTZX*f+TODT`F%IyvCpY?BGELP#s<|D{U z9lUTj%P6>^0Y$fvIdSj5*=&VVMy&nms=!=2y<5DP8x;Z13#YXf7}G)sc$_TQQ=4BD zQ1Le^y+BwHl7T6)`Q&9H&A2fJ@IPa;On5n!VNqWUiA*XXOnvoSjEIKW<$V~1?#zts>enlSTQaG2A|Ck4WkZWQoeOu(te znV;souKbA2W=)YWldqW@fV^$6EuB`lFmXYm%WqI}X?I1I7(mQ8U-pm+Ya* z|7o6wac&1>GuQfIvzU7YHIz_|V;J*CMLJolXMx^9CI;I+{Nph?sf2pX@%OKT;N@Uz9Y zzuNq11Ccdwtr(TDLx}N!>?weLLkv~i!xfI0HGWff*!12E*?7QzzZT%TX{5b7{8^*A z3ut^C4uxSDf=~t4wZ%L%gO_WS7SR4Ok7hJ;tvZ9QBfVE%2)6hE>xu9y*2%X5y%g$8 z*8&(XxwN?dO?2b4VSa@On~5A?zZZ{^s3rXm54Cfi-%4hBFSk|zY9u(3d1ButJuZ1@ zfOHtpSt)uJnL`zg9bBvUkjbPO0xNr{^{h0~$I$XQzel_OIEkgT5L!dW1uSnKsEMVp z9t^dfkxq=BneR9`%b#nWSdj)u1G=Ehv0$L@xe_eG$Ac%f7 zy`*X(p0r3FdCTa1AX^BtmPJNR4%S1nyu-AM-8)~t-KII9GEJU)W^ng7C@3%&3lj$2 z4niLa8)fJ2g>%`;;!re+Vh{3V^}9osx@pH8>b0#d8p`Dgm{I?y@dUJ4QcSB<+FAuT)O9gMlwrERIy z6)DFLaEhJkQ7S4^Qr!JA6*SYni$THFtE)0@%!vAw%X7y~!#k0?-|&6VIpFY9>5GhK zr;nM-Z`Omh>1>7;&?VC5JQoKi<`!BU_&GLzR%92V$kMohNpMDB=&NzMB&w-^SF~_# zNsTca>J{Y555+z|IT75yW;wi5A1Z zyzv|4l|xZ-Oy8r8_c8X)h%|a8#(oWcgS5P6gtuCA_vA!t=)IFTL{nnh8iW!B$i=Kd zj1ILrL;ht_4aRKF(l1%^dUyVxgK!2QsL)-{x$`q5wWjjN6B!Cj)jB=bii;9&Ee-;< zJfVk(8EOrbM&5mUciP49{Z43|TLoE#j(nQN_MaKt16dp#T6jF7z?^5*KwoT-Y`rs$ z?}8)#5Dg-Rx!PTa2R5; zx0zhW{BOpx_wKPlTu;4ev-0dUwp;g3qqIi|UMC@A?zEb3RXY`z_}gbwju zzlNht0WR%g@R5CVvg#+fb)o!I*Zpe?{_+oGq*wOmCWQ=(Ra-Q9mx#6SsqWAp*-Jzb zKvuPthpH(Fn_k>2XPu!=+C{vZsF8<9p!T}U+ICbNtO}IAqxa57*L&T>M6I0ogt&l> z^3k#b#S1--$byAaU&sZL$6(6mrf)OqZXpUPbVW%T|4T}20q9SQ&;3?oRz6rSDP4`b z(}J^?+mzbp>MQDD{ziSS0K(2^V4_anz9JV|Y_5{kF3spgW%EO6JpJ(rnnIN%;xkKf zn~;I&OGHKII3ZQ&?sHlEy)jqCyfeusjPMo7sLVr~??NAknqCbuDmo+7tp8vrKykMb z(y`R)pVp}ZgTErmi+z`UyQU*G5stQRsx*J^XW}LHi_af?(bJ8DPho0b)^PT|(`_A$ zFCYCCF={BknK&KYTAVaHE{lqJs4g6B@O&^5oTPLkmqAB#T#m!l9?wz!C}#a6w)Z~Z z6jx{dsXhI(|D)x%Yu49%ioD-~4}+hCA8Q;w_A$79%n+X84jbf?Nh?kRNRzyAi{_oV zU)LqH-yRdPxp;>vBAWqH4E z(WL)}-rb<_R^B~fI%ddj?Qxhp^5_~)6-aB`D~Nd$S`LY_O&&Fme>Id)+iI>%9V-68 z3crl=15^%0qA~}ksw@^dpZ`p;m=ury;-OV63*;zQyRs4?1?8lbUL!bR+C~2Zz1O+E@6ZQW!wvv z|NLqSP0^*J2Twq@yws%~V0^h05B8BMNHv_ZZT+=d%T#i{faiqN+ut5Bc`uQPM zgO+b1uj;)i!N94RJ>5RjTNXN{gAZel|L8S4r!NT{7)_=|`}D~ElU#2er}8~UE$Q>g zZryBhOd|J-U72{1q;Lb!^3mf+H$x6(hJHn$ZJRqCp^In_PD+>6KWnCnCXA35(}g!X z;3YI1luR&*1IvESL~*aF8(?4deU`9!cxB{8IO?PpZ{O5&uY<0DIERh2wEoAP@bayv z#$WTjR*$bN8^~AGZu+85uHo&AulFjmh*pupai?o?+>rZ7@@Xk4muI}ZqH`n&<@_Vn zvT!GF-_Ngd$B7kLge~&3qC;TE=tEid(nQB*qzXI0m46ma*2d(Sd*M%@Zc{kCFcs;1 zky%U)Pyg3wm_g12J`lS4n+Sg=L)-Y`bU705E5wk&zVEZw`eM#~AHHW96@D>bz#7?- zV`xlac^e`Zh_O+B5-kO=$04{<cKUG?R&#bnF}-?4(Jq+?Ph!9g zx@s~F)Uwub>Ratv&v85!6}3{n$bYb+p!w(l8Na6cSyEx#{r7>^YvIj8L?c*{mcB^x zqnv*lu-B1ORFtrmhfe}$I8~h*3!Ys%FNQv!P2tA^wjbH f$KZHO*s&vt|9^w-6P?|#0pRK8NSwWJ?9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!ItFh?!xdN1Q+aGJ{c&& zS>O>_%)r1c48n{Iv*t(u1=&kHeO=ifbFy+6aSK)V_AxLppYn8Z42d|rc6w}vOsL55 z`t&mC&y2@JTEyg!eDiFX^k#CC!jq%>erB=yHqUP0XcDOTw6ko}L zX;EmMrq(fKk*eygEuA616;0)>@A{TK|55PV@70 z$OfzS*(VJxQev3J?yY?O=ul(v`fp}?u9z`JK3ugibK>)DyCwImZOF4d{xK%%Ks1*} zv$oa)9anR%lXIBUqYnhLmT>VOzHfNP?ZwJNZ!5$s9M08RynIvaXw>@G^T9@r9^KH1 zVy??F&uuk)bH9Y4pQY!hP58i_H6 znl-NcuCpLV6ZWU;4C zu@9exF&OZi`Bovq_m%T+WhU2kvkz@^_LpycBvqm3bMpLw8X-Or5sL>0AKE1$(k_L=_Zc=CUq#=x1-QZf)G7nHu@fmsQ1eN_N3+nTEz`4HI4Z6uVlE zJH+X&det8JU?tO?upcM4Z=cV!JV;yF>FfL5Q$M|W_2Z!P`S=}Wzp|_1^#d%e?_H`> zV@%vA$+bFVqhw9`U;TfP|5|PD{||OiYdor8P*i??|NJcb%kzT_73*7WE?Ua5hAnR2 z=7WE=PhTlJ#ZeRznjTUb;`E(wkMZrj4e|Hilz-mK>9cZHQY**5TUPw~u}k;u73KI}xAx!0m-)GVia|x^d3p~s_9gh83jA&Ra<8rM%`>U3x69t&NzbwWY}7Ar?)FK#IZ0z|d0H0EkRO w3{9;}4Xg|ebq&m|3=9_N6z8I7$jwj5OsmAL;bP(Gi$Dzwp00i_>zopr02+f8CIA2c literal 0 HcmV?d00001 diff --git a/packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 0000000000000000000000000000000000000000..e71a726136a47ed24125c7efc79d68a4a01961b4 GIT binary patch literal 14800 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/file_selector/file_selector/example/macos/Runner/Configs/AppInfo.xcconfig b/packages/file_selector/file_selector/example/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 000000000000..8b42559e8758 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = example + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.example + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2022 com.example. All rights reserved. diff --git a/packages/file_selector/file_selector/example/macos/Runner/Configs/Debug.xcconfig b/packages/file_selector/file_selector/example/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 000000000000..36b0fd9464f4 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/packages/file_selector/file_selector/example/macos/Runner/Configs/Release.xcconfig b/packages/file_selector/file_selector/example/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 000000000000..dff4f49561c8 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/packages/file_selector/file_selector/example/macos/Runner/Configs/Warnings.xcconfig b/packages/file_selector/file_selector/example/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 000000000000..42bcbf4780b1 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/packages/file_selector/file_selector/example/macos/Runner/DebugProfile.entitlements b/packages/file_selector/file_selector/example/macos/Runner/DebugProfile.entitlements new file mode 100644 index 000000000000..dddb8a30c851 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/packages/file_selector/file_selector/example/macos/Runner/Info.plist b/packages/file_selector/file_selector/example/macos/Runner/Info.plist new file mode 100644 index 000000000000..4789daa6a443 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/packages/file_selector/file_selector/example/macos/Runner/MainFlutterWindow.swift b/packages/file_selector/file_selector/example/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 000000000000..32aaeedceb1f --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,19 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController.init() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/packages/file_selector/file_selector/example/macos/Runner/Release.entitlements b/packages/file_selector/file_selector/example/macos/Runner/Release.entitlements new file mode 100644 index 000000000000..852fa1a4728a --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index 41ff35307867..14ff92997c00 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for opening and saving files, or selecting directories, using native file selection UI. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.3 +version: 0.8.4 environment: sdk: ">=2.12.0 <3.0.0" @@ -12,12 +12,15 @@ environment: flutter: plugin: platforms: + macos: + default_package: file_selector_macos web: default_package: file_selector_web windows: default_package: file_selector_windows dependencies: + file_selector_macos: ^0.8.2 file_selector_platform_interface: ^2.0.0 file_selector_web: ^0.8.1 file_selector_windows: ^0.8.2 diff --git a/script/configs/exclude_integration_macos.yaml b/script/configs/exclude_integration_macos.yaml index bb58d463f7d9..7a9e287da05f 100644 --- a/script/configs/exclude_integration_macos.yaml +++ b/script/configs/exclude_integration_macos.yaml @@ -1,2 +1,3 @@ # Can't use Flutter integration tests due to native modal UI. +- file_selector - file_selector_macos From 35295552b8a4a74536cb8cc585e108a9bc89ea37 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 4 Mar 2022 15:01:24 -0500 Subject: [PATCH 259/600] Roll Flutter from fbf6a58bf19c to 163a7ac116f4 (3 revisions) (#5003) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 57e681ad1a34..2c1ddd66becc 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -fbf6a58bf19c52b6b2dba2dd218f046eedeabc6d +163a7ac116f438c101ffbd06da515dd7d50d16a0 From c0db021f1d4061c5f9e401379bcfdf3d62885569 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 4 Mar 2022 17:11:23 -0500 Subject: [PATCH 260/600] Roll Flutter from 163a7ac116f4 to 671aa9e95e97 (5 revisions) (#5006) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 2c1ddd66becc..675770f0cdd2 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -163a7ac116f438c101ffbd06da515dd7d50d16a0 +671aa9e95e979f09318cf34a8637733a6eccba79 From 30075f294c46c2733705f8f8ed6b47d195d321ba Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 4 Mar 2022 17:51:23 -0500 Subject: [PATCH 261/600] [quick_actions] Publish federated version (#5005) --- .../quick_actions/quick_actions/CHANGELOG.md | 4 ++++ .../quick_actions/quick_actions/pubspec.yaml | 18 +++++------------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/packages/quick_actions/quick_actions/CHANGELOG.md b/packages/quick_actions/quick_actions/CHANGELOG.md index 33e80498a63c..fe596087b990 100644 --- a/packages/quick_actions/quick_actions/CHANGELOG.md +++ b/packages/quick_actions/quick_actions/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.0+10 + +* Moves Android and iOS implementations to federated packages. + ## 0.6.0+9 * Updates Android compileSdkVersion to 31. diff --git a/packages/quick_actions/quick_actions/pubspec.yaml b/packages/quick_actions/quick_actions/pubspec.yaml index b2a9f7498a95..8ef2d3ab4e02 100644 --- a/packages/quick_actions/quick_actions/pubspec.yaml +++ b/packages/quick_actions/quick_actions/pubspec.yaml @@ -3,15 +3,11 @@ description: Flutter plugin for creating shortcuts on home screen, also known as Quick Actions on iOS and App Shortcuts on Android. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+quick_actions%22 -version: 0.6.0+9 - -# Temporarily disable publishing to allow moving Android and iOS -# implementations. -publish_to: none +version: 0.6.0+10 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: @@ -24,11 +20,8 @@ flutter: dependencies: flutter: sdk: flutter - # Temporary path dependencies to allow moving Android and iOS implementations. - quick_actions_android: - path: ../quick_actions_android - quick_actions_ios: - path: ../quick_actions_ios + quick_actions_android: ^0.6.0+9 + quick_actions_ios: ^0.6.0+9 quick_actions_platform_interface: ^1.0.0 dev_dependencies: @@ -36,6 +29,5 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - mockito: ^5.0.0-nullsafety.7 - pedantic: ^1.11.0 + mockito: ^5.0.0 plugin_platform_interface: ^2.0.0 From 675f91b3a3e2c7bf2754c921667dd589f9010d92 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 4 Mar 2022 18:31:20 -0500 Subject: [PATCH 262/600] Roll Flutter from 671aa9e95e97 to 513df67edb69 (2 revisions) (#5007) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 675770f0cdd2..7f2096675aba 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -671aa9e95e979f09318cf34a8637733a6eccba79 +513df67edb690f913caf83c31311317cbc8ddc99 From 091e0cf8a9f5b2519f1caacf1865dfc274fdddfc Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 7 Mar 2022 15:59:40 -0500 Subject: [PATCH 263/600] [ci] Update FTL key (#5012) Replaces the expired key with the updated key so that Android tests work again. --- .cirrus.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index 1184ac7e00a1..f9454cb9f315 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -197,7 +197,7 @@ task: CHANNEL: "master" CHANNEL: "stable" MAPS_API_KEY: ENCRYPTED[596a9f6bca436694625ac50851dc5da6b4d34cba8025f7db5bc9465142e8cd44e15f69e3507787753accebfc4910d550] - GCLOUD_FIREBASE_TESTLAB_KEY: ENCRYPTED[de02374f8d2d14d50792c6b521af2dfb86cbb522efed104f905002e4332546104d387d2bb8710956b729b4bd6533bba0] + GCLOUD_FIREBASE_TESTLAB_KEY: ENCRYPTED[4c11f1a80a5741d51e92ab609bc7214ab2aa015e68a490e4d6777ebdf84f9c899b97c0ded2f4b2e6adf2c8b5ead1e3c5] build_script: # Unsetting CIRRUS_CHANGE_MESSAGE and CIRRUS_COMMIT_MESSAGE as they # might include non-ASCII characters which makes Gradle crash. From 18b269e3a1430bf9d1abeb57f6661573dc610547 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 7 Mar 2022 16:36:23 -0500 Subject: [PATCH 264/600] Roll Flutter from 513df67edb69 to 03c92a99fdf8 (5 revisions) (#5010) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 7f2096675aba..4eb3f36a7157 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -513df67edb690f913caf83c31311317cbc8ddc99 +03c92a99fdf8d91db89fa8dde19912b7ef341b67 From 90e741c88c1231ad959b65eb9d6f6699d0ae51b1 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 7 Mar 2022 17:41:22 -0500 Subject: [PATCH 265/600] Roll Flutter from 03c92a99fdf8 to a491a81f7873 (26 revisions) (#5014) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 4eb3f36a7157..d34ceb4a0991 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -03c92a99fdf8d91db89fa8dde19912b7ef341b67 +a491a81f78739dc1c801529b98239adc6a2dd361 From b906ea50c90072a24cca3ddea78fa8d163bac4fe Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Mon, 7 Mar 2022 15:41:23 -0800 Subject: [PATCH 266/600] [camera]remove QueueHelper class (#4983) --- packages/camera/camera/CHANGELOG.md | 4 ++++ .../example/ios/Runner.xcodeproj/project.pbxproj | 10 +++++----- .../{QueueHelperTests.m => QueueUtilsTests.m} | 12 ++++++------ packages/camera/camera/ios/Classes/CameraPlugin.m | 2 +- .../camera/ios/Classes/CameraPlugin.modulemap | 2 +- packages/camera/camera/ios/Classes/FLTCam.m | 2 +- .../camera/ios/Classes/FLTThreadSafeEventChannel.m | 6 +++--- .../ios/Classes/FLTThreadSafeFlutterResult.m | 6 +++--- .../ios/Classes/FLTThreadSafeMethodChannel.m | 6 +++--- .../ios/Classes/FLTThreadSafeTextureRegistry.m | 14 +++++++------- .../ios/Classes/{QueueHelper.h => QueueUtils.h} | 14 +++++--------- .../ios/Classes/{QueueHelper.m => QueueUtils.m} | 8 ++------ 12 files changed, 41 insertions(+), 45 deletions(-) rename packages/camera/camera/example/ios/RunnerTests/{QueueHelperTests.m => QueueUtilsTests.m} (84%) rename packages/camera/camera/ios/Classes/{QueueHelper.h => QueueUtils.h} (61%) rename packages/camera/camera/ios/Classes/{QueueHelper.m => QueueUtils.m} (75%) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 9ef626a8ad2b..c088bd5b5373 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Minor iOS internal code cleanup related to queue helper functions. + ## 0.9.4+14 * Restores compatibility with Flutter 2.5 and 2.8. diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj index 5f788fc9b9f9..8c102bfa38b1 100644 --- a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ @@ -20,7 +20,7 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - E01EE4A82799F3A5008C1950 /* QueueHelperTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E01EE4A72799F3A5008C1950 /* QueueHelperTests.m */; }; + E01EE4A82799F3A5008C1950 /* QueueUtilsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E01EE4A72799F3A5008C1950 /* QueueUtilsTests.m */; }; E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */; }; E04F108627A87CA600573D0C /* FLTSavePhotoDelegateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */; }; E071CF7227B3061B006EF3BA /* FLTCamPhotoCaptureTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */; }; @@ -83,7 +83,7 @@ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9C5CC6CAD53AD388B2694F3A /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; A24F9E418BA48BCC7409B117 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; - E01EE4A72799F3A5008C1950 /* QueueHelperTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QueueHelperTests.m; sourceTree = ""; }; + E01EE4A72799F3A5008C1950 /* QueueUtilsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QueueUtilsTests.m; sourceTree = ""; }; E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CameraCaptureSessionQueueRaceConditionTests.m; sourceTree = ""; }; E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTSavePhotoDelegateTests.m; sourceTree = ""; }; E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTCamPhotoCaptureTests.m; sourceTree = ""; }; @@ -131,7 +131,7 @@ E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */, E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */, E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */, - E01EE4A72799F3A5008C1950 /* QueueHelperTests.m */, + E01EE4A72799F3A5008C1950 /* QueueUtilsTests.m */, E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */, F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */, F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */, @@ -413,7 +413,7 @@ E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */, E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */, E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */, - E01EE4A82799F3A5008C1950 /* QueueHelperTests.m in Sources */, + E01EE4A82799F3A5008C1950 /* QueueUtilsTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/packages/camera/camera/example/ios/RunnerTests/QueueHelperTests.m b/packages/camera/camera/example/ios/RunnerTests/QueueUtilsTests.m similarity index 84% rename from packages/camera/camera/example/ios/RunnerTests/QueueHelperTests.m rename to packages/camera/camera/example/ios/RunnerTests/QueueUtilsTests.m index c5f377f7efa9..de11b4f6961f 100644 --- a/packages/camera/camera/example/ios/RunnerTests/QueueHelperTests.m +++ b/packages/camera/camera/example/ios/RunnerTests/QueueUtilsTests.m @@ -5,20 +5,20 @@ @import camera; @import XCTest; -@interface QueueHelperTests : XCTestCase +@interface QueueUtilsTests : XCTestCase @end -@implementation QueueHelperTests +@implementation QueueUtilsTests - (void)testShouldStayOnMainQueueIfCalledFromMainQueue { XCTestExpectation *expectation = [self expectationWithDescription:@"Block must be run on the main queue."]; - [QueueHelper ensureToRunOnMainQueue:^{ + FLTEnsureToRunOnMainQueue(^{ if (NSThread.isMainThread) { [expectation fulfill]; } - }]; + }); [self waitForExpectationsWithTimeout:1 handler:nil]; } @@ -26,11 +26,11 @@ - (void)testShouldDispatchToMainQueueIfCalledFromBackgroundQueue { XCTestExpectation *expectation = [self expectationWithDescription:@"Block must be run on the main queue."]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [QueueHelper ensureToRunOnMainQueue:^{ + FLTEnsureToRunOnMainQueue(^{ if (NSThread.isMainThread) { [expectation fulfill]; } - }]; + }); }); [self waitForExpectationsWithTimeout:1 handler:nil]; } diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.m b/packages/camera/camera/ios/Classes/CameraPlugin.m index fcea190de705..634aa699a01a 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin.m +++ b/packages/camera/camera/ios/Classes/CameraPlugin.m @@ -13,7 +13,7 @@ #import "FLTThreadSafeFlutterResult.h" #import "FLTThreadSafeMethodChannel.h" #import "FLTThreadSafeTextureRegistry.h" -#import "QueueHelper.h" +#import "QueueUtils.h" @interface CameraPlugin () @property(readonly, nonatomic) FLTThreadSafeTextureRegistry *registry; diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.modulemap b/packages/camera/camera/ios/Classes/CameraPlugin.modulemap index 529c6580e908..a23848aaccfc 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin.modulemap +++ b/packages/camera/camera/ios/Classes/CameraPlugin.modulemap @@ -14,6 +14,6 @@ framework module camera { header "FLTThreadSafeFlutterResult.h" header "FLTThreadSafeMethodChannel.h" header "FLTThreadSafeTextureRegistry.h" - header "QueueHelper.h" + header "QueueUtils.h" } } diff --git a/packages/camera/camera/ios/Classes/FLTCam.m b/packages/camera/camera/ios/Classes/FLTCam.m index 31a9decd59b9..669ce334059e 100644 --- a/packages/camera/camera/ios/Classes/FLTCam.m +++ b/packages/camera/camera/ios/Classes/FLTCam.m @@ -5,7 +5,7 @@ #import "FLTCam.h" #import "FLTCam_Test.h" #import "FLTSavePhotoDelegate.h" -#import "QueueHelper.h" +#import "QueueUtils.h" @import CoreMotion; #import diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.m b/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.m index 02a36f152bd8..46941bb18dd6 100644 --- a/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.m +++ b/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.m @@ -3,7 +3,7 @@ // found in the LICENSE file. #import "FLTThreadSafeEventChannel.h" -#import "QueueHelper.h" +#import "QueueUtils.h" @interface FLTThreadSafeEventChannel () @property(nonatomic, strong) FlutterEventChannel *channel; @@ -21,10 +21,10 @@ - (instancetype)initWithEventChannel:(FlutterEventChannel *)channel { - (void)setStreamHandler:(NSObject *)handler completion:(void (^)(void))completion { - [QueueHelper ensureToRunOnMainQueue:^{ + FLTEnsureToRunOnMainQueue(^{ [self.channel setStreamHandler:handler]; completion(); - }]; + }); } @end diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.m b/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.m index 821d7561f782..58c2e788cdc0 100644 --- a/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.m +++ b/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.m @@ -4,7 +4,7 @@ #import "FLTThreadSafeFlutterResult.h" #import -#import "QueueHelper.h" +#import "QueueUtils.h" @implementation FLTThreadSafeFlutterResult { } @@ -47,9 +47,9 @@ - (void)sendNotImplemented { * Sends result to flutterResult on the main thread. */ - (void)send:(id _Nullable)result { - [QueueHelper ensureToRunOnMainQueue:^{ + FLTEnsureToRunOnMainQueue(^{ self.flutterResult(result); - }]; + }); } @end diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.m b/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.m index ad4da87c8e91..5b29b70ee432 100644 --- a/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.m +++ b/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.m @@ -3,7 +3,7 @@ // found in the LICENSE file. #import "FLTThreadSafeMethodChannel.h" -#import "QueueHelper.h" +#import "QueueUtils.h" @interface FLTThreadSafeMethodChannel () @property(nonatomic, strong) FlutterMethodChannel *channel; @@ -20,9 +20,9 @@ - (instancetype)initWithMethodChannel:(FlutterMethodChannel *)channel { } - (void)invokeMethod:(NSString *)method arguments:(id)arguments { - [QueueHelper ensureToRunOnMainQueue:^{ + FLTEnsureToRunOnMainQueue(^{ [self.channel invokeMethod:method arguments:arguments]; - }]; + }); } @end diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.m b/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.m index 5eb2443e4ee2..349b89fdc117 100644 --- a/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.m +++ b/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.m @@ -3,7 +3,7 @@ // found in the LICENSE file. #import "FLTThreadSafeTextureRegistry.h" -#import "QueueHelper.h" +#import "QueueUtils.h" @interface FLTThreadSafeTextureRegistry () @property(nonatomic, strong) NSObject *registry; @@ -21,21 +21,21 @@ - (instancetype)initWithTextureRegistry:(NSObject *)regi - (void)registerTexture:(NSObject *)texture completion:(void (^)(int64_t))completion { - [QueueHelper ensureToRunOnMainQueue:^{ + FLTEnsureToRunOnMainQueue(^{ completion([self.registry registerTexture:texture]); - }]; + }); } - (void)textureFrameAvailable:(int64_t)textureId { - [QueueHelper ensureToRunOnMainQueue:^{ + FLTEnsureToRunOnMainQueue(^{ [self.registry textureFrameAvailable:textureId]; - }]; + }); } - (void)unregisterTexture:(int64_t)textureId { - [QueueHelper ensureToRunOnMainQueue:^{ + FLTEnsureToRunOnMainQueue(^{ [self.registry unregisterTexture:textureId]; - }]; + }); } @end diff --git a/packages/camera/camera/ios/Classes/QueueHelper.h b/packages/camera/camera/ios/Classes/QueueUtils.h similarity index 61% rename from packages/camera/camera/ios/Classes/QueueHelper.h rename to packages/camera/camera/ios/Classes/QueueUtils.h index dc7373220521..a7e22da716d0 100644 --- a/packages/camera/camera/ios/Classes/QueueHelper.h +++ b/packages/camera/camera/ios/Classes/QueueUtils.h @@ -7,17 +7,13 @@ NS_ASSUME_NONNULL_BEGIN /// Queue-specific context data to be associated with the capture session queue. -extern const char *FLTCaptureSessionQueueSpecific; - -/// A class that contains dispatch queue related helper functions. -@interface QueueHelper : NSObject +extern const char* FLTCaptureSessionQueueSpecific; /// Ensures the given block to be run on the main queue. -/// If caller site is already on the main queue, the block will be run synchronously. Otherwise, the -/// block will be dispatched asynchronously to the main queue. +/// If caller site is already on the main queue, the block will be run +/// synchronously. Otherwise, the block will be dispatched asynchronously to the +/// main queue. /// @param block the block to be run on the main queue. -+ (void)ensureToRunOnMainQueue:(void (^)(void))block; - -@end +extern void FLTEnsureToRunOnMainQueue(dispatch_block_t block); NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera/ios/Classes/QueueHelper.m b/packages/camera/camera/ios/Classes/QueueUtils.m similarity index 75% rename from packages/camera/camera/ios/Classes/QueueHelper.m rename to packages/camera/camera/ios/Classes/QueueUtils.m index 2cef7b677bfa..1fd54cd52cb3 100644 --- a/packages/camera/camera/ios/Classes/QueueHelper.m +++ b/packages/camera/camera/ios/Classes/QueueUtils.m @@ -2,18 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "QueueHelper.h" +#import "QueueUtils.h" const char *FLTCaptureSessionQueueSpecific = "capture_session_queue"; -@implementation QueueHelper - -+ (void)ensureToRunOnMainQueue:(void (^)(void))block { +void FLTEnsureToRunOnMainQueue(dispatch_block_t block) { if (!NSThread.isMainThread) { dispatch_async(dispatch_get_main_queue(), block); } else { block(); } } - -@end From 4e1c90e08200ff09125201331fef7fa2d3894d5a Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Tue, 8 Mar 2022 11:35:21 -0800 Subject: [PATCH 267/600] [camera]remove CAS operation and use dispatch queue instead (#4973) --- packages/camera/camera/CHANGELOG.md | 3 +- .../ios/Runner.xcodeproj/project.pbxproj | 6 ++ .../camera/camera/example/ios/Runner/main.m | 2 +- .../example/ios/RunnerTests/CameraTestUtils.h | 18 ++++++ .../example/ios/RunnerTests/CameraTestUtils.m | 44 ++++++++++++++ .../ios/RunnerTests/FLTCamPhotoCaptureTests.m | 25 ++------ .../ios/RunnerTests/FLTCamSampleBufferTests.m | 36 ++++++------ .../RunnerTests/FLTSavePhotoDelegateTests.m | 6 +- packages/camera/camera/ios/Classes/FLTCam.h | 7 +++ packages/camera/camera/ios/Classes/FLTCam.m | 57 ++++++++++++++----- .../camera/camera/ios/Classes/FLTCam_Test.h | 18 +++++- packages/camera/camera/pubspec.yaml | 2 +- 12 files changed, 166 insertions(+), 58 deletions(-) create mode 100644 packages/camera/camera/example/ios/RunnerTests/CameraTestUtils.h create mode 100644 packages/camera/camera/example/ios/RunnerTests/CameraTestUtils.m diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index c088bd5b5373..4b96fd6493e5 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.9.4+15 +* Uses dispatch queue for pixel buffer synchronization on iOS. * Minor iOS internal code cleanup related to queue helper functions. ## 0.9.4+14 diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj index 8c102bfa38b1..6c171505a8ca 100644 --- a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj @@ -28,6 +28,7 @@ E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */; }; E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */; }; E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */; }; + E0CDBAC227CD9729002561D9 /* CameraTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */; }; E0F95E3D27A32AB900699390 /* CameraPropertiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */; }; E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */; }; F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */ = {isa = PBXBuildFile; fileRef = F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */; }; @@ -91,6 +92,8 @@ E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeMethodChannelTests.m; sourceTree = ""; }; E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeTextureRegistryTests.m; sourceTree = ""; }; E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeEventChannelTests.m; sourceTree = ""; }; + E0CDBAC027CD9729002561D9 /* CameraTestUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CameraTestUtils.h; sourceTree = ""; }; + E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraTestUtils.m; sourceTree = ""; }; E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPropertiesTests.m; sourceTree = ""; }; E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPreviewPauseTests.m; sourceTree = ""; }; F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockFLTThreadSafeFlutterResult.h; sourceTree = ""; }; @@ -132,6 +135,8 @@ E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */, E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */, E01EE4A72799F3A5008C1950 /* QueueUtilsTests.m */, + E0CDBAC027CD9729002561D9 /* CameraTestUtils.h */, + E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */, E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */, F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */, F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */, @@ -408,6 +413,7 @@ E071CF7427B31DE4006EF3BA /* FLTCamSampleBufferTests.m in Sources */, E04F108627A87CA600573D0C /* FLTSavePhotoDelegateTests.m in Sources */, F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */, + E0CDBAC227CD9729002561D9 /* CameraTestUtils.m in Sources */, 334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */, E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */, E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */, diff --git a/packages/camera/camera/example/ios/Runner/main.m b/packages/camera/camera/example/ios/Runner/main.m index 8b3fb8d5b361..d1224fea37ed 100644 --- a/packages/camera/camera/example/ios/Runner/main.m +++ b/packages/camera/camera/example/ios/Runner/main.m @@ -11,7 +11,7 @@ int main(int argc, char *argv[]) { // The setup logic in `AppDelegate::didFinishLaunchingWithOptions:` eventually sends camera // operations on the background queue, which would run concurrently with the test cases during // unit tests, making the debugging process confusing. This setup is actually not necessary for - // the unit tests, so here we want to skip the AppDelegate when running unit tests. + // the unit tests, so it is better to skip the AppDelegate when running unit tests. BOOL isTesting = NSClassFromString(@"XCTestCase") != nil; return UIApplicationMain(argc, argv, nil, isTesting ? nil : NSStringFromClass([AppDelegate class])); diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraTestUtils.h b/packages/camera/camera/example/ios/RunnerTests/CameraTestUtils.h new file mode 100644 index 000000000000..9fe67dc650e2 --- /dev/null +++ b/packages/camera/camera/example/ios/RunnerTests/CameraTestUtils.h @@ -0,0 +1,18 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import camera; + +NS_ASSUME_NONNULL_BEGIN + +/// Creates an `FLTCam` that runs its capture session operations on a given queue. +/// @param captureSessionQueue the capture session queue +/// @return an FLTCam object. +extern FLTCam *FLTCreateCamWithCaptureSessionQueue(dispatch_queue_t captureSessionQueue); + +/// Creates a test sample buffer. +/// @return a test sample buffer. +extern CMSampleBufferRef FLTCreateTestSampleBuffer(void); + +NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraTestUtils.m b/packages/camera/camera/example/ios/RunnerTests/CameraTestUtils.m new file mode 100644 index 000000000000..0ae4887eb631 --- /dev/null +++ b/packages/camera/camera/example/ios/RunnerTests/CameraTestUtils.m @@ -0,0 +1,44 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "CameraTestUtils.h" +#import +@import AVFoundation; + +FLTCam *FLTCreateCamWithCaptureSessionQueue(dispatch_queue_t captureSessionQueue) { + id inputMock = OCMClassMock([AVCaptureDeviceInput class]); + OCMStub([inputMock deviceInputWithDevice:[OCMArg any] error:[OCMArg setTo:nil]]) + .andReturn(inputMock); + + id sessionMock = OCMClassMock([AVCaptureSession class]); + OCMStub([sessionMock addInputWithNoConnections:[OCMArg any]]); // no-op + OCMStub([sessionMock canSetSessionPreset:[OCMArg any]]).andReturn(YES); + + return [[FLTCam alloc] initWithCameraName:@"camera" + resolutionPreset:@"medium" + enableAudio:true + orientation:UIDeviceOrientationPortrait + captureSession:sessionMock + captureSessionQueue:captureSessionQueue + error:nil]; +} + +CMSampleBufferRef FLTCreateTestSampleBuffer(void) { + CVPixelBufferRef pixelBuffer; + CVPixelBufferCreate(kCFAllocatorDefault, 100, 100, kCVPixelFormatType_32BGRA, NULL, &pixelBuffer); + + CMFormatDescriptionRef formatDescription; + CMVideoFormatDescriptionCreateForImageBuffer(kCFAllocatorDefault, pixelBuffer, + &formatDescription); + + CMSampleTimingInfo timingInfo = {CMTimeMake(1, 44100), kCMTimeZero, kCMTimeInvalid}; + + CMSampleBufferRef sampleBuffer; + CMSampleBufferCreateReadyWithImageBuffer(kCFAllocatorDefault, pixelBuffer, formatDescription, + &timingInfo, &sampleBuffer); + + CFRelease(pixelBuffer); + CFRelease(formatDescription); + return sampleBuffer; +} diff --git a/packages/camera/camera/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m b/packages/camera/camera/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m index fdb2abd4933e..ed3e6a9793fd 100644 --- a/packages/camera/camera/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m +++ b/packages/camera/camera/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m @@ -7,7 +7,9 @@ @import AVFoundation; @import XCTest; #import +#import "CameraTestUtils.h" +/// Includes test cases related to photo capture operations for FLTCam class. @interface FLTCamPhotoCaptureTests : XCTestCase @end @@ -22,7 +24,7 @@ - (void)testCaptureToFile_mustReportErrorToResultIfSavePhotoDelegateCompletionsW dispatch_queue_t captureSessionQueue = dispatch_queue_create("capture_session_queue", NULL); dispatch_queue_set_specific(captureSessionQueue, FLTCaptureSessionQueueSpecific, (void *)FLTCaptureSessionQueueSpecific, NULL); - FLTCam *cam = [self createFLTCamWithCaptureSessionQueue:captureSessionQueue]; + FLTCam *cam = FLTCreateCamWithCaptureSessionQueue(captureSessionQueue); AVCapturePhotoSettings *settings = [AVCapturePhotoSettings photoSettings]; id mockSettings = OCMClassMock([AVCapturePhotoSettings class]); OCMStub([mockSettings photoSettings]).andReturn(settings); @@ -61,7 +63,7 @@ - (void)testCaptureToFile_mustReportPathToResultIfSavePhotoDelegateCompletionsWi dispatch_queue_t captureSessionQueue = dispatch_queue_create("capture_session_queue", NULL); dispatch_queue_set_specific(captureSessionQueue, FLTCaptureSessionQueueSpecific, (void *)FLTCaptureSessionQueueSpecific, NULL); - FLTCam *cam = [self createFLTCamWithCaptureSessionQueue:captureSessionQueue]; + FLTCam *cam = FLTCreateCamWithCaptureSessionQueue(captureSessionQueue); AVCapturePhotoSettings *settings = [AVCapturePhotoSettings photoSettings]; id mockSettings = OCMClassMock([AVCapturePhotoSettings class]); @@ -92,23 +94,4 @@ - (void)testCaptureToFile_mustReportPathToResultIfSavePhotoDelegateCompletionsWi [self waitForExpectationsWithTimeout:1 handler:nil]; } -/// Creates an `FLTCam` that runs its operations on a given capture session queue. -- (FLTCam *)createFLTCamWithCaptureSessionQueue:(dispatch_queue_t)captureSessionQueue { - id inputMock = OCMClassMock([AVCaptureDeviceInput class]); - OCMStub([inputMock deviceInputWithDevice:[OCMArg any] error:[OCMArg setTo:nil]]) - .andReturn(inputMock); - - id sessionMock = OCMClassMock([AVCaptureSession class]); - OCMStub([sessionMock alloc]).andReturn(sessionMock); - OCMStub([sessionMock addInputWithNoConnections:[OCMArg any]]); // no-op - OCMStub([sessionMock canSetSessionPreset:[OCMArg any]]).andReturn(YES); - - return [[FLTCam alloc] initWithCameraName:@"camera" - resolutionPreset:@"medium" - enableAudio:true - orientation:UIDeviceOrientationPortrait - captureSessionQueue:captureSessionQueue - error:nil]; -} - @end diff --git a/packages/camera/camera/example/ios/RunnerTests/FLTCamSampleBufferTests.m b/packages/camera/camera/example/ios/RunnerTests/FLTCamSampleBufferTests.m index ccc8de5b23bd..8f65c4e9eaa5 100644 --- a/packages/camera/camera/example/ios/RunnerTests/FLTCamSampleBufferTests.m +++ b/packages/camera/camera/example/ios/RunnerTests/FLTCamSampleBufferTests.m @@ -7,7 +7,9 @@ @import AVFoundation; @import XCTest; #import +#import "CameraTestUtils.h" +/// Includes test cases related to sample buffer handling for FLTCam class. @interface FLTCamSampleBufferTests : XCTestCase @end @@ -15,23 +17,25 @@ @interface FLTCamSampleBufferTests : XCTestCase @implementation FLTCamSampleBufferTests - (void)testSampleBufferCallbackQueueMustBeCaptureSessionQueue { - id inputMock = OCMClassMock([AVCaptureDeviceInput class]); - OCMStub([inputMock deviceInputWithDevice:[OCMArg any] error:[OCMArg setTo:nil]]) - .andReturn(inputMock); - - id sessionMock = OCMClassMock([AVCaptureSession class]); - OCMStub([sessionMock alloc]).andReturn(sessionMock); - OCMStub([sessionMock addInputWithNoConnections:[OCMArg any]]); // no-op - OCMStub([sessionMock canSetSessionPreset:[OCMArg any]]).andReturn(YES); - dispatch_queue_t captureSessionQueue = dispatch_queue_create("testing", NULL); - FLTCam *cam = [[FLTCam alloc] initWithCameraName:@"camera" - resolutionPreset:@"medium" - enableAudio:true - orientation:UIDeviceOrientationPortrait - captureSessionQueue:captureSessionQueue - error:nil]; - XCTAssertEqual(captureSessionQueue, cam.captureVideoOutput.sampleBufferCallbackQueue); + FLTCam *cam = FLTCreateCamWithCaptureSessionQueue(captureSessionQueue); + XCTAssertEqual(captureSessionQueue, cam.captureVideoOutput.sampleBufferCallbackQueue, + @"Sample buffer callback queue must be the capture session queue."); +} + +- (void)testCopyPixelBuffer { + FLTCam *cam = FLTCreateCamWithCaptureSessionQueue(dispatch_queue_create("test", NULL)); + CMSampleBufferRef capturedSampleBuffer = FLTCreateTestSampleBuffer(); + CVPixelBufferRef capturedPixelBuffer = CMSampleBufferGetImageBuffer(capturedSampleBuffer); + // Mimic sample buffer callback when captured a new video sample + [cam captureOutput:cam.captureVideoOutput + didOutputSampleBuffer:capturedSampleBuffer + fromConnection:OCMClassMock([AVCaptureConnection class])]; + CVPixelBufferRef deliveriedPixelBuffer = [cam copyPixelBuffer]; + XCTAssertEqual(deliveriedPixelBuffer, capturedPixelBuffer, + @"FLTCam must deliver the latest captured pixel buffer to copyPixelBuffer API."); + CFRelease(capturedSampleBuffer); + CFRelease(deliveriedPixelBuffer); } @end diff --git a/packages/camera/camera/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m b/packages/camera/camera/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m index 9e8e2441f0b9..a70a572ac0f2 100644 --- a/packages/camera/camera/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m +++ b/packages/camera/camera/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m @@ -53,7 +53,7 @@ - (void)testHandlePhotoCaptureResult_mustCompleteWithErrorIfFailedToWrite { [completionExpectation fulfill]; }]; - // We can't use OCMClassMock for NSData because some XCTest APIs uses NSData (e.g. + // Do not use OCMClassMock for NSData because some XCTest APIs uses NSData (e.g. // `XCTRunnerIDESession::logDebugMessage:`) on a private queue. id mockData = OCMPartialMock([NSData data]); OCMStub([mockData writeToFile:OCMOCK_ANY @@ -82,7 +82,7 @@ - (void)testHandlePhotoCaptureResult_mustCompleteWithFilePathIfSuccessToWrite { [completionExpectation fulfill]; }]; - // We can't use OCMClassMock for NSData because some XCTest APIs uses NSData (e.g. + // Do not use OCMClassMock for NSData because some XCTest APIs uses NSData (e.g. // `XCTRunnerIDESession::logDebugMessage:`) on a private queue. id mockData = OCMPartialMock([NSData data]); OCMStub([mockData writeToFile:filePath options:NSDataWritingAtomic error:[OCMArg setTo:nil]]) @@ -107,7 +107,7 @@ - (void)testHandlePhotoCaptureResult_bothProvideDataAndSaveFileMustRunOnIOQueue const char *ioQueueSpecific = "io_queue_specific"; dispatch_queue_set_specific(ioQueue, ioQueueSpecific, (void *)ioQueueSpecific, NULL); - // We can't use OCMClassMock for NSData because some XCTest APIs uses NSData (e.g. + // Do not use OCMClassMock for NSData because some XCTest APIs uses NSData (e.g. // `XCTRunnerIDESession::logDebugMessage:`) on a private queue. id mockData = OCMPartialMock([NSData data]); OCMStub([mockData writeToFile:OCMOCK_ANY options:NSDataWritingAtomic error:[OCMArg setTo:nil]]) diff --git a/packages/camera/camera/ios/Classes/FLTCam.h b/packages/camera/camera/ios/Classes/FLTCam.h index 417a1d74db21..0cd135e0e41f 100644 --- a/packages/camera/camera/ios/Classes/FLTCam.h +++ b/packages/camera/camera/ios/Classes/FLTCam.h @@ -31,6 +31,13 @@ NS_ASSUME_NONNULL_BEGIN // Format used for video and image streaming. @property(assign, nonatomic) FourCharCode videoFormat; +/// Initializes an `FLTCam` instance. +/// @param cameraName a name used to uniquely identify the camera. +/// @param resolutionPreset the resolution preset +/// @param enableAudio YES if audio should be enabled for video capturing; NO otherwise. +/// @param orientation the orientation of camera +/// @param captureSessionQueue the queue on which camera's capture session operations happen. +/// @param error report to the caller if any error happened creating the camera. - (instancetype)initWithCameraName:(NSString *)cameraName resolutionPreset:(NSString *)resolutionPreset enableAudio:(BOOL)enableAudio diff --git a/packages/camera/camera/ios/Classes/FLTCam.m b/packages/camera/camera/ios/Classes/FLTCam.m index 669ce334059e..30c177bd4b2a 100644 --- a/packages/camera/camera/ios/Classes/FLTCam.m +++ b/packages/camera/camera/ios/Classes/FLTCam.m @@ -52,7 +52,9 @@ @interface FLTCam () *inProgressSavePhotoDelegates; +/// Delegate callback when receiving a new video or audio sample. +/// Exposed for unit tests. +- (void)captureOutput:(AVCaptureOutput *)output + didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer + fromConnection:(AVCaptureConnection *)connection; + +/// Initializes a camera instance. +/// Allows for injecting dependencies that are usually internal. +- (instancetype)initWithCameraName:(NSString *)cameraName + resolutionPreset:(NSString *)resolutionPreset + enableAudio:(BOOL)enableAudio + orientation:(UIDeviceOrientation)orientation + captureSession:(AVCaptureSession *)captureSession + captureSessionQueue:(dispatch_queue_t)captureSessionQueue + error:(NSError **)error; + @end diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 7bc0e452bb29..fcdce02e947f 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+14 +version: 0.9.4+15 environment: sdk: ">=2.14.0 <3.0.0" From 715fdbe82394c90799532cdec0f775198d44e1bd Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 8 Mar 2022 15:15:27 -0500 Subject: [PATCH 268/600] Roll Flutter from a491a81f7873 to 10805ca3fe6b (4 revisions) (#5016) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index d34ceb4a0991..ec4b42230e12 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -a491a81f78739dc1c801529b98239adc6a2dd361 +10805ca3fe6bf22bba2454a8c2d26e1bbe2cd2c3 From 5691d52d93f65bd5408f202149af1779b427e868 Mon Sep 17 00:00:00 2001 From: carman247 <31341463+carman247@users.noreply.github.com> Date: Tue, 8 Mar 2022 22:20:23 +0100 Subject: [PATCH 269/600] [camera] Fix for CameraAccessException that prevents image capture on certain devices running Android 7/8 (#4572) --- packages/camera/camera/CHANGELOG.md | 7 ++- .../io/flutter/plugins/camera/Camera.java | 61 ++++++++++++++++++- .../io/flutter/plugins/camera/CameraTest.java | 55 +++++++++++++++++ packages/camera/camera/pubspec.yaml | 2 +- 4 files changed, 120 insertions(+), 5 deletions(-) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 4b96fd6493e5..35a958e026ff 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,7 +1,12 @@ +## 0.9.4+16 + +* Fixes a bug resulting in a `CameraAccessException` that prevents image + capture on some Android devices. + ## 0.9.4+15 * Uses dispatch queue for pixel buffer synchronization on iOS. -* Minor iOS internal code cleanup related to queue helper functions. +* Minor iOS internal code cleanup related to queue helper functions. ## 0.9.4+14 diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java index 6a70ea0d10ea..0521c422d794 100644 --- a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -79,6 +79,24 @@ interface ErrorCallback { void onError(String errorCode, String errorMessage); } +/** A mockable wrapper for CameraDevice calls. */ +interface CameraDeviceWrapper { + @NonNull + CaptureRequest.Builder createCaptureRequest(int templateType) throws CameraAccessException; + + @TargetApi(VERSION_CODES.P) + void createCaptureSession(SessionConfiguration config) throws CameraAccessException; + + @TargetApi(VERSION_CODES.LOLLIPOP) + void createCaptureSession( + @NonNull List outputs, + @NonNull CameraCaptureSession.StateCallback callback, + @Nullable Handler handler) + throws CameraAccessException; + + void close(); +} + class Camera implements CameraCaptureCallback.CameraCaptureStateListener, ImageReader.OnImageAvailableListener { @@ -114,7 +132,7 @@ class Camera /** An additional thread for running tasks that shouldn't block the UI. */ private HandlerThread backgroundHandlerThread; - private CameraDevice cameraDevice; + private CameraDeviceWrapper cameraDevice; private CameraCaptureSession captureSession; private ImageReader pictureImageReader; private ImageReader imageStreamReader; @@ -136,6 +154,44 @@ class Camera private MethodChannel.Result flutterResult; + /** A CameraDeviceWrapper implementation that forwards calls to a CameraDevice. */ + private class DefaultCameraDeviceWrapper implements CameraDeviceWrapper { + private final CameraDevice cameraDevice; + + private DefaultCameraDeviceWrapper(CameraDevice cameraDevice) { + this.cameraDevice = cameraDevice; + } + + @NonNull + @Override + public CaptureRequest.Builder createCaptureRequest(int templateType) + throws CameraAccessException { + return cameraDevice.createCaptureRequest(templateType); + } + + @TargetApi(VERSION_CODES.P) + @Override + public void createCaptureSession(SessionConfiguration config) throws CameraAccessException { + cameraDevice.createCaptureSession(config); + } + + @TargetApi(VERSION_CODES.LOLLIPOP) + @SuppressWarnings("deprecation") + @Override + public void createCaptureSession( + @NonNull List outputs, + @NonNull CameraCaptureSession.StateCallback callback, + @Nullable Handler handler) + throws CameraAccessException { + cameraDevice.createCaptureSession(outputs, callback, backgroundHandler); + } + + @Override + public void close() { + cameraDevice.close(); + } + } + public Camera( final Activity activity, final SurfaceTextureEntry flutterTexture, @@ -261,7 +317,7 @@ public void open(String imageFormatGroup) throws CameraAccessException { new CameraDevice.StateCallback() { @Override public void onOpened(@NonNull CameraDevice device) { - cameraDevice = device; + cameraDevice = new DefaultCameraDeviceWrapper(device); try { startPreview(); dartMessenger.sendCameraInitializedEvent( @@ -584,7 +640,6 @@ public void onCaptureCompleted( try { captureSession.stopRepeating(); - captureSession.abortCaptures(); Log.i(TAG, "sending capture request"); captureSession.capture(stillBuilder.build(), captureCallback, backgroundHandler); } catch (CameraAccessException e) { diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraTest.java b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraTest.java index 1ed2e4c11d7b..167733b9dcca 100644 --- a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraTest.java +++ b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraTest.java @@ -22,11 +22,15 @@ import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraMetadata; import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.params.SessionConfiguration; +import android.media.ImageReader; import android.media.MediaRecorder; import android.os.Build; import android.os.Handler; import android.os.HandlerThread; +import android.view.Surface; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.lifecycle.LifecycleObserver; import io.flutter.embedding.engine.systemchannels.PlatformChannel; import io.flutter.plugin.common.MethodChannel; @@ -50,11 +54,39 @@ import io.flutter.plugins.camera.features.zoomlevel.ZoomLevelFeature; import io.flutter.plugins.camera.utils.TestUtils; import io.flutter.view.TextureRegistry; +import java.util.ArrayList; +import java.util.List; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.MockedStatic; +class FakeCameraDeviceWrapper implements CameraDeviceWrapper { + final List captureRequests; + + FakeCameraDeviceWrapper(List captureRequests) { + this.captureRequests = captureRequests; + } + + @NonNull + @Override + public CaptureRequest.Builder createCaptureRequest(int var1) { + return captureRequests.remove(0); + } + + @Override + public void createCaptureSession(SessionConfiguration config) {} + + @Override + public void createCaptureSession( + @NonNull List outputs, + @NonNull CameraCaptureSession.StateCallback callback, + @Nullable Handler handler) {} + + @Override + public void close() {} +} + public class CameraTest { private CameraProperties mockCameraProperties; private CameraFeatureFactory mockCameraFeatureFactory; @@ -801,6 +833,29 @@ public void startBackgroundThread_shouldNotStartNewThreadWhenAlreadyCreated() { verify(mockHandlerThread, times(1)).start(); } + @Test + public void onConverge_shouldTakePictureWithoutAbortingSession() throws CameraAccessException { + ArrayList mockRequestBuilders = new ArrayList<>(); + mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); + CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders); + // Stub out other features used by the flow. + TestUtils.setPrivateField(camera, "cameraDevice", fakeCamera); + TestUtils.setPrivateField(camera, "pictureImageReader", mock(ImageReader.class)); + SensorOrientationFeature mockSensorOrientationFeature = + mockCameraFeatureFactory.createSensorOrientationFeature(mockCameraProperties, null, null); + DeviceOrientationManager mockDeviceOrientationManager = mock(DeviceOrientationManager.class); + when(mockSensorOrientationFeature.getDeviceOrientationManager()) + .thenReturn(mockDeviceOrientationManager); + + // Simulate a post-precapture flow. + camera.onConverged(); + // A picture should be taken. + verify(mockCaptureSession, times(1)).capture(any(), any(), any()); + // The session shuold not be aborted as part of this flow, as this breaks capture on some + // devices, and causes delays on others. + verify(mockCaptureSession, never()).abortCaptures(); + } + private static class TestCameraFeatureFactory implements CameraFeatureFactory { private final AutoFocusFeature mockAutoFocusFeature; private final ExposureLockFeature mockExposureLockFeature; diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index fcdce02e947f..2baab09c5dcb 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+15 +version: 0.9.4+16 environment: sdk: ">=2.14.0 <3.0.0" From b6dea96eb18db6ae393d077aabf625e632a9a5f9 Mon Sep 17 00:00:00 2001 From: EconomicEgret <101129562+EconomicEgret@users.noreply.github.com> Date: Tue, 8 Mar 2022 17:05:20 -0500 Subject: [PATCH 270/600] [video_player] Add new option for allowBackgroundPlayback (#5013) --- .../video_player/video_player/CHANGELOG.md | 4 + .../video_player/lib/video_player.dart | 12 ++- .../video_player/video_player/pubspec.yaml | 4 +- .../video_player/test/video_player_test.dart | 77 +++++++++++++++++-- 4 files changed, 83 insertions(+), 14 deletions(-) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index ee0accb22c79..a44f0e352e66 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.0 + +* Adds `allowBackgroundPlayback` to `VideoPlayerOptions`. + ## 2.2.19 * Internal code cleanup for stricter analysis options. diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index 672ba2efcde3..b77c530b5831 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -295,7 +295,7 @@ class VideoPlayerController extends ValueNotifier { bool _isDisposed = false; Completer? _creatingCompleter; StreamSubscription? _eventSubscription; - late _VideoAppLifeCycleObserver _lifeCycleObserver; + _VideoAppLifeCycleObserver? _lifeCycleObserver; /// The id of a texture that hasn't been initialized. @visibleForTesting @@ -309,8 +309,12 @@ class VideoPlayerController extends ValueNotifier { /// Attempts to open the given [dataSource] and load metadata about the video. Future initialize() async { - _lifeCycleObserver = _VideoAppLifeCycleObserver(this); - _lifeCycleObserver.initialize(); + final bool allowBackgroundPlayback = + videoPlayerOptions?.allowBackgroundPlayback ?? false; + if (!allowBackgroundPlayback) { + _lifeCycleObserver = _VideoAppLifeCycleObserver(this); + } + _lifeCycleObserver?.initialize(); _creatingCompleter = Completer(); late DataSource dataSourceDescription; @@ -423,7 +427,7 @@ class VideoPlayerController extends ValueNotifier { await _eventSubscription?.cancel(); await _videoPlayerPlatform.dispose(_textureId); } - _lifeCycleObserver.dispose(); + _lifeCycleObserver?.dispose(); } _isDisposed = true; super.dispose(); diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 2f6e28919d00..d58de120541b 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for displaying inline video with other Flutter widgets on Android, iOS, and web. repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.2.19 +version: 2.3.0 environment: sdk: ">=2.14.0 <3.0.0" @@ -25,7 +25,7 @@ dependencies: html: ^0.15.0 video_player_android: ^2.2.17 video_player_avfoundation: ^2.2.17 - video_player_platform_interface: ">=4.2.0 <6.0.0" + video_player_platform_interface: ^5.1.0 video_player_web: ^2.0.0 dev_dependencies: diff --git a/packages/video_player/video_player/test/video_player_test.dart b/packages/video_player/video_player/test/video_player_test.dart index 07afd0f65402..5e31e22c7010 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -98,6 +98,19 @@ class _FakeClosedCaptionFile extends ClosedCaptionFile { } void main() { + void _verifyPlayStateRespondsToLifecycle( + VideoPlayerController controller, { + required bool shouldPlayInBackground, + }) { + expect(controller.value.isPlaying, true); + _ambiguate(WidgetsBinding.instance)! + .handleAppLifecycleStateChanged(AppLifecycleState.paused); + expect(controller.value.isPlaying, shouldPlayInBackground); + _ambiguate(WidgetsBinding.instance)! + .handleAppLifecycleStateChanged(AppLifecycleState.resumed); + expect(controller.value.isPlaying, true); + } + testWidgets('update texture', (WidgetTester tester) async { final FakeController controller = FakeController(); await tester.pumpWidget(VideoPlayer(controller)); @@ -194,6 +207,16 @@ void main() { }); group('initialize', () { + test('started app lifecycle observing', () async { + final VideoPlayerController controller = VideoPlayerController.network( + 'https://127.0.0.1', + ); + await controller.initialize(); + await controller.play(); + _verifyPlayStateRespondsToLifecycle(controller, + shouldPlayInBackground: false); + }); + test('asset', () async { final VideoPlayerController controller = VideoPlayerController.asset( 'a.avi', @@ -900,6 +923,46 @@ void main() { }); }); + group('VideoPlayerOptions', () { + test('setMixWithOthers', () async { + final VideoPlayerController controller = VideoPlayerController.file( + File(''), + videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true)); + await controller.initialize(); + expect(controller.videoPlayerOptions!.mixWithOthers, true); + }); + + test('true allowBackgroundPlayback continues playback', () async { + final VideoPlayerController controller = VideoPlayerController.file( + File(''), + videoPlayerOptions: VideoPlayerOptions( + allowBackgroundPlayback: true, + ), + ); + await controller.initialize(); + await controller.play(); + _verifyPlayStateRespondsToLifecycle( + controller, + shouldPlayInBackground: true, + ); + }); + + test('false allowBackgroundPlayback pauses playback', () async { + final VideoPlayerController controller = VideoPlayerController.file( + File(''), + videoPlayerOptions: VideoPlayerOptions( + allowBackgroundPlayback: false, + ), + ); + await controller.initialize(); + await controller.play(); + _verifyPlayStateRespondsToLifecycle( + controller, + shouldPlayInBackground: false, + ); + }); + }); + test('VideoProgressColors', () { const Color playedColor = Color.fromRGBO(0, 0, 255, 0.75); const Color bufferedColor = Color.fromRGBO(0, 255, 0, 0.5); @@ -914,14 +977,6 @@ void main() { expect(colors.bufferedColor, bufferedColor); expect(colors.backgroundColor, backgroundColor); }); - - test('setMixWithOthers', () async { - final VideoPlayerController controller = VideoPlayerController.file( - File(''), - videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true)); - await controller.initialize(); - expect(controller.videoPlayerOptions!.mixWithOthers, true); - }); } class FakeVideoPlayerPlatform extends VideoPlayerPlatform { @@ -1010,3 +1065,9 @@ class FakeVideoPlayerPlatform extends VideoPlayerPlatform { calls.add('setMixWithOthers'); } } + +/// This allows a value of type T or T? to be treated as a value of type T?. +/// +/// We use this so that APIs that have become non-nullable can still be used +/// with `!` and `?` on the stable branch. +T? _ambiguate(T? value) => value; From 19634a20b70591a8f7b9864e886296543ed54dc7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 8 Mar 2022 17:45:15 -0500 Subject: [PATCH 271/600] Roll Flutter from 10805ca3fe6b to 06fb863ae5a4 (13 revisions) (#5018) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index ec4b42230e12..9611fc14409b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -10805ca3fe6bf22bba2454a8c2d26e1bbe2cd2c3 +06fb863ae5a4be5e0b9b3ad604ce79d6619a58be From 71ee4a6ad1fda6d95c8c07d07acd900b143795a3 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 8 Mar 2022 18:50:25 -0500 Subject: [PATCH 272/600] Roll Flutter from 06fb863ae5a4 to bb1b2fd07277 (1 revision) (#5019) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 9611fc14409b..cffb9e8fa21b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -06fb863ae5a4be5e0b9b3ad604ce79d6619a58be +bb1b2fd07277a596e9e3d7a63ffa8e475e8d5ece From e8739ac83136ebe982aa2510cf8f0cb9cbd0c681 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 8 Mar 2022 19:55:23 -0500 Subject: [PATCH 273/600] Roll Flutter from bb1b2fd07277 to 9240c9a02a87 (3 revisions) (#5020) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index cffb9e8fa21b..73c99045dccb 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -bb1b2fd07277a596e9e3d7a63ffa8e475e8d5ece +9240c9a02a87af35361c7accaa82b09fa31e539c From c7feb7d4e2fe61c3de3d5cacea95aa523a2440aa Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 8 Mar 2022 22:35:23 -0500 Subject: [PATCH 274/600] Roll Flutter from 9240c9a02a87 to c8538873c80e (1 revision) (#5021) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 73c99045dccb..de430e2ba568 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -9240c9a02a87af35361c7accaa82b09fa31e539c +c8538873c80e34231d7a93f9ca1e5ec22c8804b1 From eb6ad34c32b71dd7c2ba03fd6d1f8b4ef9b4124c Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 8 Mar 2022 23:40:17 -0500 Subject: [PATCH 275/600] Roll Flutter from c8538873c80e to 3172065fbab0 (3 revisions) (#5022) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index de430e2ba568..c029efbdcf05 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c8538873c80e34231d7a93f9ca1e5ec22c8804b1 +3172065fbab0cbd180187396d4033dd8432fe575 From 6852d0a311a8c501b1718ca54b382b97c4db1a22 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 9 Mar 2022 11:35:23 -0500 Subject: [PATCH 276/600] [ci] Run analysis with older versions of Flutter (#5000) --- .cirrus.yml | 24 +++++-- packages/camera/camera_web/CHANGELOG.md | 4 ++ packages/camera/camera_web/pubspec.yaml | 2 +- .../google_sign_in_web/CHANGELOG.md | 4 ++ .../google_sign_in_web/example/pubspec.yaml | 2 +- .../google_sign_in_web/pubspec.yaml | 2 +- .../quick_actions/quick_actions/CHANGELOG.md | 4 ++ .../video_player/video_player/CHANGELOG.md | 4 ++ .../video_player/example/pubspec.yaml | 2 +- .../video_player/video_player/pubspec.yaml | 2 +- script/tool/CHANGELOG.md | 3 + script/tool/lib/src/analyze_command.dart | 49 ++++--------- .../src/common/package_looping_command.dart | 41 ++++++++++- .../tool/lib/src/common/plugin_command.dart | 3 + script/tool/test/analyze_command_test.dart | 8 +-- .../common/package_looping_command_test.dart | 28 ++++++++ .../tool/test/common/plugin_command_test.dart | 31 +++++++- script/tool/test/util.dart | 70 ++++++++++++++----- 18 files changed, 213 insertions(+), 70 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index f9454cb9f315..8f6a6f7f6ebe 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -28,10 +28,10 @@ flutter_upgrade_template: &FLUTTER_UPGRADE_TEMPLATE - git fetch origin # Switch to the requested channel. - git checkout $TARGET_TREEISH - # When using a branch rather than a hash, reset to the upstream branch - # rather than using pull, since the base image can sometimes be in a state - # where it has diverged from upstream (!). - - if [[ "$TARGET_TREEISH" == "$CHANNEL" ]]; then + # When using a branch rather than a hash or version tag, reset to the + # upstream branch rather than using pull, since the base image can sometimes + # be in a state where it has diverged from upstream (!). + - if [[ "$TARGET_TREEISH" == "$CHANNEL" ]] && [[ "$CHANNEL" != *"."* ]]; then - git reset --hard @{u} - fi # Run doctor to allow auditing of what version of Flutter the run is using. @@ -138,6 +138,22 @@ task: # Restore the tree to a clean state, to avoid accidental issues if # other script steps are added to this task. - git checkout . + # Does a sanity check that plugins at least pass analysis on the N-1 and N-2 + # versions of Flutter stable if the plugin claims to support that version. + # This is to minimize accidentally making changes that break old versions + # (which we don't commit to supporting, but don't want to actively break) + # without updating the constraints. + # Note: The versions below should be manually updated after a new stable + # version comes out. + - name: legacy-version-analyze + depends_on: analyze + env: + matrix: + CHANNEL: "2.5.3" + CHANNEL: "2.8.1" + analyze_script: + - ./script/tool_runner.sh analyze --skip-if-not-supporting-flutter-version="$CHANNEL" --custom-analysis=script/configs/custom_analysis.yaml + - echo "If this test fails, the minumum Flutter version should be updated" ### Web tasks ### - name: web-build_all_plugins env: diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index c72e31fc0f18..9e486667c103 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version for changes in 0.2.1+3. + ## 0.2.1+3 * Internal code cleanup for stricter analysis options. diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index 6d6f110157bc..cb6aa19c49f1 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.2.1+3 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md index c1a1606d3a2f..e35caf75ea1f 100644 --- a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.8. + ## 0.10.0+5 * Internal code cleanup for stricter analysis options. diff --git a/packages/google_sign_in/google_sign_in_web/example/pubspec.yaml b/packages/google_sign_in/google_sign_in_web/example/pubspec.yaml index 5a51cd567275..1bdb2f09c465 100644 --- a/packages/google_sign_in/google_sign_in_web/example/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_web/example/pubspec.yaml @@ -3,7 +3,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.2.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/google_sign_in/google_sign_in_web/pubspec.yaml b/packages/google_sign_in/google_sign_in_web/pubspec.yaml index 3bc05d14e34e..d97a7c4da6f1 100644 --- a/packages/google_sign_in/google_sign_in_web/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_web/pubspec.yaml @@ -7,7 +7,7 @@ version: 0.10.0+5 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/quick_actions/quick_actions/CHANGELOG.md b/packages/quick_actions/quick_actions/CHANGELOG.md index fe596087b990..e95d56c53e9d 100644 --- a/packages/quick_actions/quick_actions/CHANGELOG.md +++ b/packages/quick_actions/quick_actions/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.8. + ## 0.6.0+10 * Moves Android and iOS implementations to federated packages. diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index a44f0e352e66..5d56b1585a80 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 2.3.0 * Adds `allowBackgroundPlayback` to `VideoPlayerOptions`. diff --git a/packages/video_player/video_player/example/pubspec.yaml b/packages/video_player/video_player/example/pubspec.yaml index 6032f3ceed65..e6b51f99e115 100644 --- a/packages/video_player/video_player/example/pubspec.yaml +++ b/packages/video_player/video_player/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.12.13+hotfix.5" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index d58de120541b..88e45c5be057 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -7,7 +7,7 @@ version: 2.3.0 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 8f2807f0dd03..8e5ae210ce0d 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -19,6 +19,9 @@ `flutter` behavior. - Validates `default_package` entries in plugins. - Removes `allow-warnings` from the `podspecs` command. +- Adds `skip-if-not-supporting-flutter-version` to allow running tests using a + version of Flutter that not all packages support. (E.g., to allow for running + some tests against old versions of Flutter to help avoid accidental breakage.) ## 0.7.3 diff --git a/script/tool/lib/src/analyze_command.dart b/script/tool/lib/src/analyze_command.dart index faad7f4736eb..d8b17bfd5626 100644 --- a/script/tool/lib/src/analyze_command.dart +++ b/script/tool/lib/src/analyze_command.dart @@ -10,12 +10,9 @@ import 'package:yaml/yaml.dart'; import 'common/core.dart'; import 'common/package_looping_command.dart'; -import 'common/plugin_command.dart'; import 'common/process_runner.dart'; import 'common/repository_package.dart'; -const int _exitPackagesGetFailed = 3; - /// A command to run Dart analysis on packages. class AnalyzeCommand extends PackageLoopingCommand { /// Creates a analysis command instance. @@ -84,41 +81,8 @@ class AnalyzeCommand extends PackageLoopingCommand { return false; } - /// Ensures that the dependent packages have been fetched for all packages - /// (including their sub-packages) that will be analyzed. - Future _runPackagesGetOnTargetPackages() async { - final List packageDirectories = - await getTargetPackagesAndSubpackages() - .map((PackageEnumerationEntry entry) => entry.package.directory) - .toList(); - final Set packagePaths = - packageDirectories.map((Directory dir) => dir.path).toSet(); - packageDirectories.removeWhere((Directory directory) { - // Remove the 'example' subdirectories; 'flutter packages get' - // automatically runs 'pub get' there as part of handling the parent - // directory. - return directory.basename == 'example' && - packagePaths.contains(directory.parent.path); - }); - for (final Directory package in packageDirectories) { - final int exitCode = await processRunner.runAndStream( - flutterCommand, ['packages', 'get'], - workingDir: package); - if (exitCode != 0) { - return false; - } - } - return true; - } - @override Future initializeRun() async { - print('Fetching dependencies...'); - if (!await _runPackagesGetOnTargetPackages()) { - printError('Unable to get dependencies.'); - throw ToolExit(_exitPackagesGetFailed); - } - _allowedCustomAnalysisDirectories = getStringListArg(_customAnalysisFlag).expand((String item) { if (item.endsWith('.yaml')) { @@ -138,6 +102,19 @@ class AnalyzeCommand extends PackageLoopingCommand { @override Future runForPackage(RepositoryPackage package) async { + // For non-example packages, fetch dependencies. 'flutter packages get' + // automatically runs 'pub get' in examples as part of handling the parent + // directory, which is guaranteed to come first in the package enumeration. + if (package.directory.basename != 'example' || + !RepositoryPackage(package.directory.parent).pubspecFile.existsSync()) { + final int exitCode = await processRunner.runAndStream( + flutterCommand, ['packages', 'get'], + workingDir: package.directory); + if (exitCode != 0) { + return PackageResult.fail(['Unable to get dependencies']); + } + } + if (_hasUnexpecetdAnalysisOptions(package)) { return PackageResult.fail(['Unexpected local analysis options']); } diff --git a/script/tool/lib/src/common/package_looping_command.dart b/script/tool/lib/src/common/package_looping_command.dart index bfee71a0f4c2..b75aaa4a4a49 100644 --- a/script/tool/lib/src/common/package_looping_command.dart +++ b/script/tool/lib/src/common/package_looping_command.dart @@ -9,6 +9,8 @@ import 'package:file/file.dart'; import 'package:git/git.dart'; import 'package:path/path.dart' as p; import 'package:platform/platform.dart'; +import 'package:pub_semver/pub_semver.dart'; +import 'package:pubspec_parse/pubspec_parse.dart'; import 'core.dart'; import 'plugin_command.dart'; @@ -75,7 +77,16 @@ abstract class PackageLoopingCommand extends PluginCommand { Platform platform = const LocalPlatform(), GitDir? gitDir, }) : super(packagesDir, - processRunner: processRunner, platform: platform, gitDir: gitDir); + processRunner: processRunner, platform: platform, gitDir: gitDir) { + argParser.addOption( + _skipByFlutterVersionArg, + help: 'Skip any packages that require a Flutter version newer than ' + 'the provided version.', + ); + } + + static const String _skipByFlutterVersionArg = + 'skip-if-not-supporting-flutter-version'; /// Packages that had at least one [logWarning] call. final Set _packagesWithWarnings = @@ -219,6 +230,11 @@ abstract class PackageLoopingCommand extends PluginCommand { _otherWarningCount = 0; _currentPackageEntry = null; + final String minFlutterVersionArg = getStringArg(_skipByFlutterVersionArg); + final Version? minFlutterVersion = minFlutterVersionArg.isEmpty + ? null + : Version.parse(minFlutterVersionArg); + final DateTime runStart = DateTime.now(); await initializeRun(); @@ -242,7 +258,8 @@ abstract class PackageLoopingCommand extends PluginCommand { PackageResult result; try { - result = await runForPackage(entry.package); + result = await _runForPackageIfSupported(entry.package, + minFlutterVersion: minFlutterVersion); } catch (e, stack) { printError(e.toString()); printError(stack.toString()); @@ -285,6 +302,26 @@ abstract class PackageLoopingCommand extends PluginCommand { return true; } + /// Returns the result of running [runForPackage] if the package is supported + /// by any run constraints, or a skip result if it is not. + Future _runForPackageIfSupported( + RepositoryPackage package, { + Version? minFlutterVersion, + }) async { + if (minFlutterVersion != null) { + final Pubspec pubspec = package.parsePubspec(); + final VersionConstraint? flutterConstraint = + pubspec.environment?['flutter']; + if (flutterConstraint != null && + !flutterConstraint.allows(minFlutterVersion)) { + return PackageResult.skip( + 'Does not support Flutter ${minFlutterVersion.toString()}'); + } + } + + return await runForPackage(package); + } + void _printSuccess(String message) { captureOutput ? print(message) : printSuccess(message); } diff --git a/script/tool/lib/src/common/plugin_command.dart b/script/tool/lib/src/common/plugin_command.dart index 184663568224..fcc87c94ef91 100644 --- a/script/tool/lib/src/common/plugin_command.dart +++ b/script/tool/lib/src/common/plugin_command.dart @@ -409,6 +409,9 @@ abstract class PluginCommand extends Command { /// /// By default, packages excluded via --exclude will not be in the stream, but /// they can be included by passing false for [filterExcluded]. + /// + /// Subpackages are guaranteed to be after the containing package in the + /// stream. Stream getTargetPackagesAndSubpackages( {bool filterExcluded = true}) async* { await for (final PackageEnumerationEntry plugin diff --git a/script/tool/test/analyze_command_test.dart b/script/tool/test/analyze_command_test.dart index 878facd83c06..087e5ada37b0 100644 --- a/script/tool/test/analyze_command_test.dart +++ b/script/tool/test/analyze_command_test.dart @@ -47,10 +47,10 @@ void main() { orderedEquals([ ProcessCall( 'flutter', const ['packages', 'get'], plugin1Dir.path), - ProcessCall( - 'flutter', const ['packages', 'get'], plugin2Dir.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], plugin1Dir.path), + ProcessCall( + 'flutter', const ['packages', 'get'], plugin2Dir.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], plugin2Dir.path), ])); @@ -82,10 +82,10 @@ void main() { orderedEquals([ ProcessCall( 'flutter', const ['packages', 'get'], plugin1Dir.path), - ProcessCall( - 'flutter', const ['packages', 'get'], plugin2Dir.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], plugin1Dir.path), + ProcessCall( + 'flutter', const ['packages', 'get'], plugin2Dir.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], plugin2Dir.path), ])); diff --git a/script/tool/test/common/package_looping_command_test.dart b/script/tool/test/common/package_looping_command_test.dart index 6e46a3330cc8..ea02bd4ea387 100644 --- a/script/tool/test/common/package_looping_command_test.dart +++ b/script/tool/test/common/package_looping_command_test.dart @@ -236,6 +236,34 @@ void main() { expect(command.checkedPackages, isNot(contains(excluded.childDirectory('example2').path))); }); + + test('skips unsupported versions when requested', () async { + final Directory excluded = createFakePlugin('a_plugin', packagesDir, + flutterConstraint: '>=2.10.0'); + final Directory included = createFakePackage('a_package', packagesDir); + + final TestPackageLoopingCommand command = + createTestCommand(includeSubpackages: true, hasLongOutput: false); + final List output = await runCommand(command, arguments: [ + '--skip-if-not-supporting-flutter-version=2.5.0' + ]); + + expect( + command.checkedPackages, + unorderedEquals([ + included.path, + included.childDirectory('example').path, + ])); + expect(command.checkedPackages, isNot(contains(excluded.path))); + + expect( + output, + containsAllInOrder([ + '${_startHeadingColor}Running for a_package...$_endColor', + '${_startHeadingColor}Running for a_plugin...$_endColor', + '$_startSkipColor SKIPPING: Does not support Flutter 2.5.0$_endColor', + ])); + }); }); group('output', () { diff --git a/script/tool/test/common/plugin_command_test.dart b/script/tool/test/common/plugin_command_test.dart index 28a03c61d59f..782ea7267ae7 100644 --- a/script/tool/test/common/plugin_command_test.dart +++ b/script/tool/test/common/plugin_command_test.dart @@ -253,6 +253,29 @@ packages/plugin1/plugin1/plugin1.dart unorderedEquals([platformInterfacePackage.path])); }); + test('returns subpackages after the enclosing package', () async { + final SamplePluginCommand localCommand = SamplePluginCommand( + packagesDir, + processRunner: processRunner, + platform: mockPlatform, + gitDir: MockGitDir(), + includeSubpackages: true, + ); + final CommandRunner localRunner = + CommandRunner('common_command', 'subpackage testing'); + localRunner.addCommand(localCommand); + + final Directory package = createFakePackage('apackage', packagesDir); + + await runCapturingPrint(localRunner, ['sample']); + expect( + localCommand.plugins, + containsAllInOrder([ + package.path, + package.childDirectory('example').path, + ])); + }); + group('conflicting package selection', () { test('does not allow --packages with --run-on-changed-packages', () async { @@ -893,11 +916,14 @@ class SamplePluginCommand extends PluginCommand { ProcessRunner processRunner = const ProcessRunner(), Platform platform = const LocalPlatform(), GitDir? gitDir, + this.includeSubpackages = false, }) : super(packagesDir, processRunner: processRunner, platform: platform, gitDir: gitDir); final List plugins = []; + final bool includeSubpackages; + @override final String name = 'sample'; @@ -906,7 +932,10 @@ class SamplePluginCommand extends PluginCommand { @override Future run() async { - await for (final PackageEnumerationEntry entry in getTargetPackages()) { + final Stream packages = includeSubpackages + ? getTargetPackagesAndSubpackages() + : getTargetPackages(); + await for (final PackageEnumerationEntry entry in packages) { plugins.add(entry.package.path); } } diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart index 91d21c1d9bb3..6f7d86e054e9 100644 --- a/script/tool/test/util.dart +++ b/script/tool/test/util.dart @@ -85,12 +85,14 @@ Directory createFakePlugin( Map platformSupport = const {}, String? version = '0.0.1', + String flutterConstraint = '>=2.5.0', }) { final Directory pluginDirectory = createFakePackage(name, parentDirectory, isFlutter: true, examples: examples, extraFiles: extraFiles, - version: version); + version: version, + flutterConstraint: flutterConstraint); createFakePubspec( pluginDirectory, @@ -99,6 +101,7 @@ Directory createFakePlugin( isPlugin: true, platformSupport: platformSupport, version: version, + flutterConstraint: flutterConstraint, ); return pluginDirectory; @@ -116,12 +119,16 @@ Directory createFakePackage( List extraFiles = const [], bool isFlutter = false, String? version = '0.0.1', + String flutterConstraint = '>=2.5.0', }) { final Directory packageDirectory = parentDirectory.childDirectory(name); packageDirectory.createSync(recursive: true); createFakePubspec(packageDirectory, - name: name, isFlutter: isFlutter, version: version); + name: name, + isFlutter: isFlutter, + version: version, + flutterConstraint: flutterConstraint); createFakeCHANGELOG(packageDirectory, ''' ## $version * Some changes. @@ -132,7 +139,10 @@ Directory createFakePackage( final Directory exampleDir = packageDirectory.childDirectory(examples.first) ..createSync(); createFakePubspec(exampleDir, - name: '${name}_example', isFlutter: isFlutter, publishTo: 'none'); + name: '${name}_example', + isFlutter: isFlutter, + publishTo: 'none', + flutterConstraint: flutterConstraint); } else if (examples.isNotEmpty) { final Directory exampleDir = packageDirectory.childDirectory('example') ..createSync(); @@ -140,7 +150,10 @@ Directory createFakePackage( final Directory currentExample = exampleDir.childDirectory(example) ..createSync(); createFakePubspec(currentExample, - name: example, isFlutter: isFlutter, publishTo: 'none'); + name: example, + isFlutter: isFlutter, + publishTo: 'none', + flutterConstraint: flutterConstraint); } } @@ -172,40 +185,61 @@ void createFakePubspec( const {}, String publishTo = 'http://no_pub_server.com', String? version, + String dartConstraint = '>=2.0.0 <3.0.0', + String flutterConstraint = '>=2.5.0', }) { isPlugin |= platformSupport.isNotEmpty; - parent.childFile('pubspec.yaml').createSync(); - String yaml = ''' -name: $name + + String environmentSection = ''' +environment: + sdk: "$dartConstraint" '''; + String dependenciesSection = ''' +dependencies: +'''; + String pluginSection = ''; + + // Add Flutter-specific entries if requested. if (isFlutter) { + environmentSection += ''' + flutter: "$flutterConstraint" +'''; + dependenciesSection += ''' + flutter: + sdk: flutter +'''; + if (isPlugin) { - yaml += ''' + pluginSection += ''' flutter: plugin: platforms: '''; for (final MapEntry platform in platformSupport.entries) { - yaml += _pluginPlatformSection(platform.key, platform.value, name); + pluginSection += + _pluginPlatformSection(platform.key, platform.value, name); } } - yaml += ''' -dependencies: - flutter: - sdk: flutter -'''; } - if (version != null) { - yaml += ''' -version: $version + + String yaml = ''' +name: $name +${(version != null) ? 'version: $version' : ''} + +$environmentSection + +$dependenciesSection + +$pluginSection '''; - } + if (publishTo.isNotEmpty) { yaml += ''' publish_to: $publishTo # Hardcoded safeguard to prevent this from somehow being published by a broken test. '''; } + parent.childFile('pubspec.yaml').createSync(); parent.childFile('pubspec.yaml').writeAsStringSync(yaml); } From 430bb19e5cf2d6060ad1dae0d91a3968f56a58e1 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 9 Mar 2022 12:15:16 -0500 Subject: [PATCH 277/600] Roll Flutter from 3172065fbab0 to bf6fcee17345 (2 revisions) (#5024) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index c029efbdcf05..ce6c88411e98 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -3172065fbab0cbd180187396d4033dd8432fe575 +bf6fcee173451b27cace6f1fecf44856315e1888 From 4e75d75374a04a1f3d7d1e3e61d6ce883c5e3ef7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 9 Mar 2022 13:20:23 -0500 Subject: [PATCH 278/600] Roll Flutter from bf6fcee17345 to 9cfaabaed09e (6 revisions) (#5025) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index ce6c88411e98..30773b6f7886 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -bf6fcee173451b27cace6f1fecf44856315e1888 +9cfaabaed09e1c6606c9148b0f716a78146ec4e6 From e4a4da45710e31eac7612a9a027ad57e79b8434d Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 9 Mar 2022 16:35:25 -0500 Subject: [PATCH 279/600] Roll Flutter from 9cfaabaed09e to e8af40f06b24 (6 revisions) (#5030) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 30773b6f7886..6bff39fd9372 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -9cfaabaed09e1c6606c9148b0f716a78146ec4e6 +e8af40f06b24a0b4bba2bd6e08679e7fc95b0153 From 197af08482bc74a61c5487ef411807c6788e1626 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Wed, 9 Mar 2022 17:39:25 -0800 Subject: [PATCH 280/600] [webview_flutter_wkwebview] Add support for WKNavigationDelegate (#4893) --- .../lib/src/foundation/foundation.dart | 24 +++ .../lib/src/web_kit/web_kit.dart | 121 +++++++++++++- .../lib/src/web_kit_webview_widget.dart | 92 +++++++++++ .../webview_flutter_wkwebview/pubspec.yaml | 2 +- .../test/src/web_kit_webview_widget_test.dart | 151 ++++++++++++++++++ .../web_kit_webview_widget_test.mocks.dart | 74 ++++++++- 6 files changed, 458 insertions(+), 6 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart index 9615fc5a9a51..dc933a072b7f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart @@ -33,3 +33,27 @@ class NSUrlRequest { /// All of the HTTP header fields for a request. final Map allHttpHeaderFields; } + +/// Information about an error condition. +/// +/// Wraps [NSError](https://developer.apple.com/documentation/foundation/nserror?language=objc). +@immutable +class NSError { + /// Constructs an [NSError]. + const NSError({ + required this.code, + required this.domain, + required this.localizedDescription, + }); + + /// The error code. + /// + /// Note that errors are domain-specific. + final int code; + + /// A string containing the error domain. + final String domain; + + /// A string containing the localized description of the error. + final String localizedDescription; +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart index d71509597f3f..85881e0e512e 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart @@ -46,6 +46,53 @@ enum WKAudiovisualMediaType { all, } +/// Indicate whether to allow or cancel navigation to a webpage. +/// +/// Wraps [WKNavigationActionPolicy](https://developer.apple.com/documentation/webkit/wknavigationactionpolicy?language=objc). +enum WKNavigationActionPolicy { + /// Allow navigation to continue. + /// + /// See https://developer.apple.com/documentation/webkit/wknavigationactionpolicy/wknavigationactionpolicyallow?language=objc. + allow, + + /// Cancel navigation. + /// + /// See https://developer.apple.com/documentation/webkit/wknavigationactionpolicy/wknavigationactionpolicycancel?language=objc. + cancel, +} + +/// Possible error values that WebKit APIs can return. +/// +/// See https://developer.apple.com/documentation/webkit/wkerrorcode. +class WKErrorCode { + WKErrorCode._(); + + /// Indicates an unknown issue occurred. + /// + /// See https://developer.apple.com/documentation/webkit/wkerrorcode/wkerrorunknown. + static const int unknown = 1; + + /// Indicates the web process that contains the content is no longer running. + /// + /// See https://developer.apple.com/documentation/webkit/wkerrorcode/wkerrorwebcontentprocessterminated. + static const int webContentProcessTerminated = 2; + + /// Indicates the web view was invalidated. + /// + /// See https://developer.apple.com/documentation/webkit/wkerrorcode/wkerrorwebviewinvalidated. + static const int webViewInvalidated = 3; + + /// Indicates a JavaScript exception occurred. + /// + /// See https://developer.apple.com/documentation/webkit/wkerrorcode/wkerrorjavascriptexceptionoccurred. + static const int javaScriptExceptionOccurred = 4; + + /// Indicates the result of JavaScript execution could not be returned. + /// + /// See https://developer.apple.com/documentation/webkit/wkerrorcode/wkerrorjavascriptresulttypeisunsupported. + static const int javaScriptResultTypeIsUnsupported = 5; +} + /// An object that contains information about an action that causes navigation to occur. /// /// Wraps [WKNavigationAction](https://developer.apple.com/documentation/webkit/wknavigationaction?language=objc). @@ -128,7 +175,7 @@ class WKScriptMessageHandler { void Function( WKUserContentController userContentController, WKScriptMessage message, - ) + )? didReceiveScriptMessage, ) { throw UnimplementedError(); @@ -247,13 +294,71 @@ class WKUIDelegate { void Function( WKWebViewConfiguration configuration, WKNavigationAction navigationAction, - ) + )? onCreateeWebView, ) { throw UnimplementedError(); } } +/// Methods for handling navigation changes and tracking navigation requests. +/// +/// Set the methods of the [WKNavigationDelegate] in the object you use to +/// coordinate changes in your web view’s main frame. +/// +/// Wraps [WKNavigationDelegate](https://developer.apple.com/documentation/webkit/wknavigationdelegate?language=objc). +class WKNavigationDelegate { + /// Called when navigation from the main frame has started. + set didStartProvisionalNavigation( + void Function( + WKWebView webView, + String? url, + )? + didStartProvisionalNavigation, + ) { + throw UnimplementedError(); + } + + /// Called when navigation is complete. + set didFinishNavigation( + void Function(WKWebView webView, String? url)? didFinishNavigation, + ) { + throw UnimplementedError(); + } + + /// Called when permission is needed to navigate to new content. + set decidePolicyForNavigationAction( + Future Function( + WKWebView webView, + WKNavigationAction navigationAction, + )? + decidePolicyForNavigationAction) { + throw UnimplementedError(); + } + + /// Called when an error occurred during navigation. + set didFailNavigation( + void Function(WKWebView webView, NSError error)? didFailNavigation, + ) { + throw UnimplementedError(); + } + + /// Called when an error occurred during the early navigation process. + set didFailProvisionalNavigation( + void Function(WKWebView webView, NSError error)? + didFailProvisionalNavigation, + ) { + throw UnimplementedError(); + } + + /// Called when the web view’s content process was terminated. + set webViewWebContentProcessDidTerminate( + void Function(WKWebView webView)? webViewWebContentProcessDidTerminate, + ) { + throw UnimplementedError(); + } +} + /// Object that displays interactive web content, such as for an in-app browser. /// /// Wraps [WKWebView](https://developer.apple.com/documentation/webkit/wkwebview?language=objc). @@ -286,7 +391,17 @@ class WKWebView { WKWebViewConfiguration._fromWebView(this); /// Used to integrate custom user interface elements into web view interactions. - set uiDelegate(WKUIDelegate delegate) { + set uiDelegate(WKUIDelegate? delegate) { + throw UnimplementedError(); + } + + /// The object you use to manage navigation behavior for the web view. + set navigationDelegate(WKNavigationDelegate? delegate) { + throw UnimplementedError(); + } + + /// The URL for the current webpage. + Future get url { throw UnimplementedError(); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index d44dd7bcc148..89a258e75767 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -8,6 +8,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; +import 'foundation/foundation.dart'; import 'web_kit/web_kit.dart'; /// A [Widget] that displays a [WKWebView]. @@ -120,6 +121,32 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { @visibleForTesting late final WKUIDelegate uiDelegate = webViewProxy.createUIDelgate(); + /// Methods for handling navigation changes and tracking navigation requests. + @visibleForTesting + late final WKNavigationDelegate navigationDelegate = + webViewProxy.createNavigationDelegate() + ..didStartProvisionalNavigation = (WKWebView webView, String? url) { + callbacksHandler.onPageStarted(url ?? ''); + } + ..didFinishNavigation = (WKWebView webView, String? url) { + callbacksHandler.onPageFinished(url ?? ''); + } + ..didFailNavigation = (WKWebView webView, NSError error) { + callbacksHandler.onWebResourceError(_toWebResourceError(error)); + } + ..didFailProvisionalNavigation = (WKWebView webView, NSError error) { + callbacksHandler.onWebResourceError(_toWebResourceError(error)); + } + ..webViewWebContentProcessDidTerminate = (WKWebView webView) { + callbacksHandler.onWebResourceError(WebResourceError( + errorCode: WKErrorCode.webContentProcessTerminated, + // Value from https://developer.apple.com/documentation/webkit/wkerrordomain?language=objc. + domain: 'WKErrorDomain', + description: '', + errorType: WebResourceErrorType.webContentProcessTerminated, + )); + }; + Future _setCreationParams( CreationParams params, { required WKWebViewConfiguration configuration, @@ -133,6 +160,12 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { webView = webViewProxy.createWebView(configuration); await addJavascriptChannels(params.javascriptChannelNames); + + webView.navigationDelegate = navigationDelegate; + + if (params.webSettings != null) { + updateSettings(params.webSettings!); + } } void _setWebViewConfiguration( @@ -161,6 +194,13 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { }; } + @override + Future updateSettings(WebSettings setting) async { + if (setting.hasNavigationDelegate != null) { + _setHasNavigationDelegate(setting.hasNavigationDelegate!); + } + } + @override Future addJavascriptChannels(Set javascriptChannelNames) async { await Future.wait( @@ -224,6 +264,53 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { await addJavascriptChannels(remainingNames); } + + void _setHasNavigationDelegate(bool hasNavigationDelegate) { + if (hasNavigationDelegate) { + navigationDelegate.decidePolicyForNavigationAction = + (WKWebView webView, WKNavigationAction action) async { + final bool allow = await callbacksHandler.onNavigationRequest( + url: action.request.url, + isForMainFrame: action.targetFrame.isMainFrame, + ); + + return allow + ? WKNavigationActionPolicy.allow + : WKNavigationActionPolicy.cancel; + }; + } else { + navigationDelegate.decidePolicyForNavigationAction = null; + } + } + + static WebResourceError _toWebResourceError(NSError error) { + WebResourceErrorType? errorType; + + switch (error.code) { + case WKErrorCode.unknown: + errorType = WebResourceErrorType.unknown; + break; + case WKErrorCode.webContentProcessTerminated: + errorType = WebResourceErrorType.webContentProcessTerminated; + break; + case WKErrorCode.webViewInvalidated: + errorType = WebResourceErrorType.webViewInvalidated; + break; + case WKErrorCode.javaScriptExceptionOccurred: + errorType = WebResourceErrorType.javaScriptExceptionOccurred; + break; + case WKErrorCode.javaScriptResultTypeIsUnsupported: + errorType = WebResourceErrorType.javaScriptResultTypeIsUnsupported; + break; + } + + return WebResourceError( + errorCode: error.code, + domain: error.domain, + description: error.localizedDescription, + errorType: errorType, + ); + } } /// Handles constructing objects and calling static methods. @@ -248,4 +335,9 @@ class WebViewWidgetProxy { WKUIDelegate createUIDelgate() { return WKUIDelegate(); } + + /// Constructs a [WKNavigationDelegate]. + WKNavigationDelegate createNavigationDelegate() { + return WKNavigationDelegate(); + } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 26d14ea9d468..91760df02c66 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -26,5 +26,5 @@ dev_dependencies: sdk: flutter flutter_test: sdk: flutter - mockito: ^5.0.16 + mockito: ^5.1.0 pedantic: ^1.10.0 diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index 30ba3ee9cb8c..6642b4561cfc 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -16,6 +16,7 @@ import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart'; import 'web_kit_webview_widget_test.mocks.dart'; @GenerateMocks([ + WKNavigationDelegate, WKScriptMessageHandler, WKWebView, WKWebViewConfiguration, @@ -34,6 +35,7 @@ void main() { late MockWKUserContentController mockUserContentController; late MockWKWebViewConfiguration mockWebViewConfiguration; late MockWKUIDelegate mockUIDelegate; + late MockWKNavigationDelegate mockNavigationDelegate; late MockWebViewPlatformCallbacksHandler mockCallbacksHandler; late MockJavascriptChannelRegistry mockJavascriptChannelRegistry; @@ -45,10 +47,13 @@ void main() { mockWebViewConfiguration = MockWKWebViewConfiguration(); mockUserContentController = MockWKUserContentController(); mockUIDelegate = MockWKUIDelegate(); + mockNavigationDelegate = MockWKNavigationDelegate(); mockWebViewWidgetProxy = MockWebViewWidgetProxy(); when(mockWebViewWidgetProxy.createWebView(any)).thenReturn(mockWebView); when(mockWebViewWidgetProxy.createUIDelgate()).thenReturn(mockUIDelegate); + when(mockWebViewWidgetProxy.createNavigationDelegate()) + .thenReturn(mockNavigationDelegate); when(mockWebView.configuration).thenReturn(mockWebViewConfiguration); when(mockWebViewConfiguration.userContentController).thenReturn( mockUserContentController, @@ -280,6 +285,152 @@ void main() { }); }); + group('$WebViewPlatformCallbacksHandler', () { + testWidgets('onPageStarted', (WidgetTester tester) async { + await buildWidget(tester); + + final dynamic didStartProvisionalNavigation = verify( + mockNavigationDelegate.didStartProvisionalNavigation = + captureAny) + .captured + .single as void Function(WKWebView, String); + didStartProvisionalNavigation(mockWebView, 'https://google.com'); + + verify(mockCallbacksHandler.onPageStarted('https://google.com')); + }); + + testWidgets('onPageFinished', (WidgetTester tester) async { + await buildWidget(tester); + + final dynamic didFinishNavigation = + verify(mockNavigationDelegate.didFinishNavigation = captureAny) + .captured + .single as void Function(WKWebView, String); + didFinishNavigation(mockWebView, 'https://google.com'); + + verify(mockCallbacksHandler.onPageFinished('https://google.com')); + }); + + testWidgets('onWebResourceError from didFailNavigation', + (WidgetTester tester) async { + await buildWidget(tester); + + final dynamic didFailNavigation = + verify(mockNavigationDelegate.didFailNavigation = captureAny) + .captured + .single as void Function(WKWebView, NSError); + + didFailNavigation( + mockWebView, + const NSError( + code: WKErrorCode.webViewInvalidated, + domain: 'domain', + localizedDescription: 'my desc', + ), + ); + + final WebResourceError error = + verify(mockCallbacksHandler.onWebResourceError(captureAny)) + .captured + .single as WebResourceError; + expect(error.description, 'my desc'); + expect(error.errorCode, WKErrorCode.webViewInvalidated); + expect(error.domain, 'domain'); + expect(error.errorType, WebResourceErrorType.webViewInvalidated); + }); + + testWidgets('onWebResourceError from didFailProvisionalNavigation', + (WidgetTester tester) async { + await buildWidget(tester); + + final dynamic didFailProvisionalNavigation = verify( + mockNavigationDelegate.didFailProvisionalNavigation = + captureAny) + .captured + .single as void Function(WKWebView, NSError); + + didFailProvisionalNavigation( + mockWebView, + const NSError( + code: WKErrorCode.webContentProcessTerminated, + domain: 'domain', + localizedDescription: 'my desc', + ), + ); + + final WebResourceError error = + verify(mockCallbacksHandler.onWebResourceError(captureAny)) + .captured + .single as WebResourceError; + expect(error.description, 'my desc'); + expect(error.errorCode, WKErrorCode.webContentProcessTerminated); + expect(error.domain, 'domain'); + expect( + error.errorType, + WebResourceErrorType.webContentProcessTerminated, + ); + }); + + testWidgets( + 'onWebResourceError from webViewWebContentProcessDidTerminate', + (WidgetTester tester) async { + await buildWidget(tester); + + final dynamic webViewWebContentProcessDidTerminate = verify( + mockNavigationDelegate.webViewWebContentProcessDidTerminate = + captureAny) + .captured + .single as void Function(WKWebView); + webViewWebContentProcessDidTerminate(mockWebView); + + final WebResourceError error = + verify(mockCallbacksHandler.onWebResourceError(captureAny)) + .captured + .single as WebResourceError; + expect(error.description, ''); + expect(error.errorCode, WKErrorCode.webContentProcessTerminated); + expect(error.domain, 'WKErrorDomain'); + expect( + error.errorType, + WebResourceErrorType.webContentProcessTerminated, + ); + }); + + testWidgets('onNavigationRequest from decidePolicyForNavigationAction', + (WidgetTester tester) async { + await buildWidget(tester, hasNavigationDelegate: true); + + final dynamic decidePolicyForNavigationAction = verify( + mockNavigationDelegate.decidePolicyForNavigationAction = + captureAny) + .captured + .single + as Future Function( + WKWebView, WKNavigationAction); + + when(mockCallbacksHandler.onNavigationRequest( + isForMainFrame: argThat(isFalse, named: 'isForMainFrame'), + url: 'https://google.com', + )).thenReturn(true); + + expect( + decidePolicyForNavigationAction( + mockWebView, + const WKNavigationAction( + request: NSUrlRequest(url: 'https://google.com'), + targetFrame: WKFrameInfo(isMainFrame: false), + ), + ), + completion(WKNavigationActionPolicy.allow), + ); + + verify(mockCallbacksHandler.onNavigationRequest( + url: 'https://google.com', + isForMainFrame: false, + )); + }); + }); + group('$JavascriptChannelRegistry', () { testWidgets('onJavascriptChannelMessage', (WidgetTester tester) async { when(mockWebViewWidgetProxy.createScriptMessageHandler()).thenReturn( diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index 0138995fd148..9579b23c84a0 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -1,5 +1,5 @@ -// Mocks generated by Mockito 5.0.17 from annotations -// in webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. +// Mocks generated by Mockito 5.1.0 from annotations +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. // Do not manually edit this file. import 'dart:async' as _i3; @@ -16,6 +16,7 @@ import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart' as _i2; import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart' as _i8; +// ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references @@ -38,6 +39,64 @@ class _FakeWKScriptMessageHandler_3 extends _i1.Fake class _FakeWKUIDelegate_4 extends _i1.Fake implements _i2.WKUIDelegate {} +class _FakeWKNavigationDelegate_5 extends _i1.Fake + implements _i2.WKNavigationDelegate {} + +/// A class which mocks [WKNavigationDelegate]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockWKNavigationDelegate extends _i1.Mock + implements _i2.WKNavigationDelegate { + MockWKNavigationDelegate() { + _i1.throwOnMissingStub(this); + } + + @override + set didStartProvisionalNavigation( + void Function(_i2.WKWebView, String?)? + didStartProvisionalNavigation) => + super.noSuchMethod( + Invocation.setter( + #didStartProvisionalNavigation, didStartProvisionalNavigation), + returnValueForMissingStub: null); + @override + set didFinishNavigation( + void Function(_i2.WKWebView, String?)? didFinishNavigation) => + super.noSuchMethod( + Invocation.setter(#didFinishNavigation, didFinishNavigation), + returnValueForMissingStub: null); + @override + set decidePolicyForNavigationAction( + _i3.Future<_i2.WKNavigationActionPolicy> Function( + _i2.WKWebView, _i2.WKNavigationAction)? + decidePolicyForNavigationAction) => + super.noSuchMethod( + Invocation.setter(#decidePolicyForNavigationAction, + decidePolicyForNavigationAction), + returnValueForMissingStub: null); + @override + set didFailNavigation( + void Function(_i2.WKWebView, _i4.NSError)? didFailNavigation) => + super.noSuchMethod( + Invocation.setter(#didFailNavigation, didFailNavigation), + returnValueForMissingStub: null); + @override + set didFailProvisionalNavigation( + void Function(_i2.WKWebView, _i4.NSError)? + didFailProvisionalNavigation) => + super.noSuchMethod( + Invocation.setter( + #didFailProvisionalNavigation, didFailProvisionalNavigation), + returnValueForMissingStub: null); + @override + set webViewWebContentProcessDidTerminate( + void Function(_i2.WKWebView)? webViewWebContentProcessDidTerminate) => + super.noSuchMethod( + Invocation.setter(#webViewWebContentProcessDidTerminate, + webViewWebContentProcessDidTerminate), + returnValueForMissingStub: null); +} + /// A class which mocks [WKScriptMessageHandler]. /// /// See the documentation for Mockito's code generation for more information. @@ -74,6 +133,13 @@ class MockWKWebView extends _i1.Mock implements _i2.WKWebView { super.noSuchMethod(Invocation.setter(#uiDelegate, delegate), returnValueForMissingStub: null); @override + set navigationDelegate(_i2.WKNavigationDelegate? delegate) => + super.noSuchMethod(Invocation.setter(#navigationDelegate, delegate), + returnValueForMissingStub: null); + @override + _i3.Future get url => (super.noSuchMethod(Invocation.getter(#url), + returnValue: Future.value()) as _i3.Future); + @override _i3.Future loadRequest(_i4.NSUrlRequest? request) => (super.noSuchMethod(Invocation.method(#loadRequest, [request]), returnValue: Future.value(), @@ -247,4 +313,8 @@ class MockWebViewWidgetProxy extends _i1.Mock _i2.WKUIDelegate createUIDelgate() => (super.noSuchMethod(Invocation.method(#createUIDelgate, []), returnValue: _FakeWKUIDelegate_4()) as _i2.WKUIDelegate); + @override + _i2.WKNavigationDelegate createNavigationDelegate() => (super.noSuchMethod( + Invocation.method(#createNavigationDelegate, []), + returnValue: _FakeWKNavigationDelegate_5()) as _i2.WKNavigationDelegate); } From 2fefbd7885c93ca6e2f062de61e58aa6c5b749f6 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Wed, 9 Mar 2022 18:20:15 -0800 Subject: [PATCH 281/600] [webview_flutter_android] Change relative path to uri in generated pigeon test file (#5029) --- .../webview_flutter_android/CHANGELOG.md | 4 +++ .../webview_flutter_android/README.md | 15 ++++++++++ .../webview_flutter_android/pubspec.yaml | 2 +- .../test/android_webview.pigeon.dart | 2 +- .../test/android_webview_test.mocks.dart | 29 ++----------------- ...iew_android_cookie_manager_test.mocks.dart | 5 ++-- .../webview_android_widget_test.mocks.dart | 23 ++------------- 7 files changed, 27 insertions(+), 53 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 26fb5ab624f8..12d20b00f534 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Fixes bug preventing `mockito` code generation for tests. + ## 2.8.3 * Fixes a bug causing `debuggingEnabled` to always be set to true. diff --git a/packages/webview_flutter/webview_flutter_android/README.md b/packages/webview_flutter/webview_flutter_android/README.md index 492ef913bbc7..020f1fcfbbf1 100644 --- a/packages/webview_flutter/webview_flutter_android/README.md +++ b/packages/webview_flutter/webview_flutter_android/README.md @@ -11,6 +11,21 @@ normally. This package will be automatically included in your app when you do. This package uses [pigeon][3] to generate the communication layer between Flutter and the host platform (Android). The communication interface is defined in the `pigeons/android_webview.dart` file. After editing the communication interface regenerate the communication layer by running the `./generatePigeons.sh` shell script. +Due to [flutter/flutter#97744](https://github.com/flutter/flutter/issues/97744), the generated test +pigeon file needs one of its imports updated to properly work with `mockito`. + +In `test/android_webview.pigeon.dart`, change + +```dart +import '../lib/src/android_webview.pigeon.dart'; +``` + +to + +```dart +import 'package:webview_flutter_android/src/android_webview.pigeon.dart'; +``` + Besides [pigeon][3] this package also uses [mockito][4] to generate mock objects for testing purposes. To generate the mock objects run the following command: ```bash flutter packages pub run build_runner build --delete-conflicting-outputs diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 704dc6719a7e..35f0e2f2a5a0 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -27,6 +27,6 @@ dev_dependencies: sdk: flutter flutter_test: sdk: flutter - mockito: ^5.0.16 + mockito: ^5.1.0 pedantic: ^1.10.0 pigeon: 1.0.9 diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart index 720fe408d96c..746126346750 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart @@ -12,7 +12,7 @@ import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import '../lib/src/android_webview.pigeon.dart'; +import 'package:webview_flutter_android/src/android_webview.pigeon.dart'; class _TestWebViewHostApiCodec extends StandardMessageCodec { const _TestWebViewHostApiCodec(); diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart index 2134de54a415..d6023d7de3aa 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.0.16 from annotations +// Mocks generated by Mockito 5.1.0 from annotations // in webview_flutter_android/test/android_webview_test.dart. // Do not manually edit this file. @@ -12,6 +12,7 @@ import 'package:webview_flutter_android/src/android_webview.pigeon.dart' as _i3; import 'android_webview.pigeon.dart' as _i5; +// ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references @@ -41,8 +42,6 @@ class MockCookieManagerHostApi extends _i1.Mock (super.noSuchMethod(Invocation.method(#setCookie, [arg_url, arg_value]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i4.Future); - @override - String toString() => super.toString(); } /// A class which mocks [DownloadListener]. @@ -60,8 +59,6 @@ class MockDownloadListener extends _i1.Mock implements _i2.DownloadListener { Invocation.method(#onDownloadStart, [url, userAgent, contentDisposition, mimetype, contentLength]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [JavaScriptChannel]. @@ -80,8 +77,6 @@ class MockJavaScriptChannel extends _i1.Mock implements _i2.JavaScriptChannel { void postMessage(String? message) => super.noSuchMethod(Invocation.method(#postMessage, [message]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [TestDownloadListenerHostApi]. @@ -97,8 +92,6 @@ class MockTestDownloadListenerHostApi extends _i1.Mock void create(int? instanceId) => super.noSuchMethod(Invocation.method(#create, [instanceId]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [TestJavaScriptChannelHostApi]. @@ -114,8 +107,6 @@ class MockTestJavaScriptChannelHostApi extends _i1.Mock void create(int? instanceId, String? channelName) => super.noSuchMethod(Invocation.method(#create, [instanceId, channelName]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [TestWebChromeClientHostApi]. @@ -132,8 +123,6 @@ class MockTestWebChromeClientHostApi extends _i1.Mock super.noSuchMethod( Invocation.method(#create, [instanceId, webViewClientInstanceId]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [TestWebSettingsHostApi]. @@ -210,8 +199,6 @@ class MockTestWebSettingsHostApi extends _i1.Mock void setAllowFileAccess(int? instanceId, bool? enabled) => super.noSuchMethod( Invocation.method(#setAllowFileAccess, [instanceId, enabled]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [TestWebViewClientHostApi]. @@ -228,8 +215,6 @@ class MockTestWebViewClientHostApi extends _i1.Mock super.noSuchMethod( Invocation.method(#create, [instanceId, shouldOverrideUrlLoading]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [TestWebViewHostApi]. @@ -368,8 +353,6 @@ class MockTestWebViewHostApi extends _i1.Mock void setBackgroundColor(int? instanceId, int? color) => super.noSuchMethod( Invocation.method(#setBackgroundColor, [instanceId, color]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [TestAssetManagerHostApi]. @@ -389,8 +372,6 @@ class MockTestAssetManagerHostApi extends _i1.Mock String getAssetFilePathByName(String? name) => (super.noSuchMethod(Invocation.method(#getAssetFilePathByName, [name]), returnValue: '') as String); - @override - String toString() => super.toString(); } /// A class which mocks [WebChromeClient]. @@ -405,8 +386,6 @@ class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient { void onProgressChanged(_i2.WebView? webView, int? progress) => super .noSuchMethod(Invocation.method(#onProgressChanged, [webView, progress]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [WebView]. @@ -557,8 +536,6 @@ class MockWebView extends _i1.Mock implements _i2.WebView { (super.noSuchMethod(Invocation.method(#release, []), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i4.Future); - @override - String toString() => super.toString(); } /// A class which mocks [WebViewClient]. @@ -602,6 +579,4 @@ class MockWebViewClient extends _i1.Mock implements _i2.WebViewClient { void urlLoading(_i2.WebView? webView, String? url) => super.noSuchMethod(Invocation.method(#urlLoading, [webView, url]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.mocks.dart index 131977bd3dfd..ff9974b1a10b 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.0.16 from annotations +// Mocks generated by Mockito 5.1.0 from annotations // in webview_flutter_android/test/webview_android_cookie_manager_test.dart. // Do not manually edit this file. @@ -7,6 +7,7 @@ import 'dart:async' as _i3; import 'package:mockito/mockito.dart' as _i1; import 'package:webview_flutter_android/src/android_webview.dart' as _i2; +// ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references @@ -33,6 +34,4 @@ class MockCookieManager extends _i1.Mock implements _i2.CookieManager { _i3.Future clearCookies() => (super.noSuchMethod(Invocation.method(#clearCookies, []), returnValue: Future.value(false)) as _i3.Future); - @override - String toString() => super.toString(); } diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart index 12e993bafa31..f4d9abbd2d3c 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.0.16 from annotations +// Mocks generated by Mockito 5.1.0 from annotations // in webview_flutter_android/test/webview_android_widget_test.dart. // Do not manually edit this file. @@ -12,6 +12,7 @@ import 'package:webview_flutter_android/webview_android_widget.dart' as _i7; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart' as _i3; +// ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references @@ -46,8 +47,6 @@ class MockFlutterAssetManager extends _i1.Mock _i4.Future getAssetFilePathByName(String? name) => (super.noSuchMethod(Invocation.method(#getAssetFilePathByName, [name]), returnValue: Future.value('')) as _i4.Future); - @override - String toString() => super.toString(); } /// A class which mocks [WebSettings]. @@ -120,8 +119,6 @@ class MockWebSettings extends _i1.Mock implements _i2.WebSettings { (super.noSuchMethod(Invocation.method(#setAllowFileAccess, [enabled]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i4.Future); - @override - String toString() => super.toString(); } /// A class which mocks [WebView]. @@ -272,8 +269,6 @@ class MockWebView extends _i1.Mock implements _i2.WebView { (super.noSuchMethod(Invocation.method(#release, []), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i4.Future); - @override - String toString() => super.toString(); } /// A class which mocks [WebViewAndroidDownloadListener]. @@ -298,8 +293,6 @@ class MockWebViewAndroidDownloadListener extends _i1.Mock Invocation.method(#onDownloadStart, [url, userAgent, contentDisposition, mimetype, contentLength]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [WebViewAndroidJavaScriptChannel]. @@ -324,8 +317,6 @@ class MockWebViewAndroidJavaScriptChannel extends _i1.Mock void postMessage(String? message) => super.noSuchMethod(Invocation.method(#postMessage, [message]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [WebViewAndroidWebChromeClient]. @@ -341,8 +332,6 @@ class MockWebViewAndroidWebChromeClient extends _i1.Mock void onProgressChanged(_i2.WebView? webView, int? progress) => super .noSuchMethod(Invocation.method(#onProgressChanged, [webView, progress]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [WebViewAndroidWebViewClient]. @@ -411,8 +400,6 @@ class MockWebViewAndroidWebViewClient extends _i1.Mock void requestLoading(_i2.WebView? webView, _i2.WebResourceRequest? request) => super.noSuchMethod(Invocation.method(#requestLoading, [webView, request]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [JavascriptChannelRegistry]. @@ -439,8 +426,6 @@ class MockJavascriptChannelRegistry extends _i1.Mock super.noSuchMethod( Invocation.method(#updateJavascriptChannelsFromSet, [channels]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [WebViewPlatformCallbacksHandler]. @@ -474,8 +459,6 @@ class MockWebViewPlatformCallbacksHandler extends _i1.Mock void onWebResourceError(_i3.WebResourceError? error) => super.noSuchMethod(Invocation.method(#onWebResourceError, [error]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [WebViewProxy]. @@ -498,6 +481,4 @@ class MockWebViewProxy extends _i1.Mock implements _i7.WebViewProxy { Invocation.method(#setWebContentsDebuggingEnabled, [enabled]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i4.Future); - @override - String toString() => super.toString(); } From f4aaeeb5f2260e4c7a31b7c67f2690ea0f99b146 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 9 Mar 2022 21:55:15 -0500 Subject: [PATCH 282/600] Roll Flutter from e8af40f06b24 to bb2cca5f59a1 (6 revisions) (#5032) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 6bff39fd9372..5e8e64c6c6b7 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -e8af40f06b24a0b4bba2bd6e08679e7fc95b0153 +bb2cca5f59a1f559a5d686e68fb89dfffe7a05e0 From 20e231b0529cbe94e2f372cc53020512873d9c66 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 10 Mar 2022 00:35:22 -0500 Subject: [PATCH 283/600] Roll Flutter from bb2cca5f59a1 to 57aa49028db7 (4 revisions) (#5033) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 5e8e64c6c6b7..5afb34058b93 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -bb2cca5f59a1f559a5d686e68fb89dfffe7a05e0 +57aa49028db77a4edf0fbc6efc70cceea2d2c6ed From 6ff862fa711b0e58040d879b8feb6acc7e933872 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rulong=20Chen=EF=BC=88=E9=99=88=E6=B1=9D=E9=BE=99=EF=BC=89?= Date: Fri, 11 Mar 2022 00:00:24 +0800 Subject: [PATCH 284/600] [image_picker] Fixes activity leak (#4439) --- AUTHORS | 1 + .../image_picker/image_picker/CHANGELOG.md | 4 + .../imagepicker/ImagePickerPlugin.java | 148 +++++++++++++----- .../imagepicker/ImagePickerPluginTest.java | 33 +++- .../image_picker/image_picker/pubspec.yaml | 2 +- 5 files changed, 143 insertions(+), 45 deletions(-) diff --git a/AUTHORS b/AUTHORS index f5dc82340bc8..41a31ed39cd4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -66,3 +66,4 @@ Alex Li Rahul Raj <64.rahulraj@gmail.com> Daniel Roek TheOneWithTheBraid +Rulong Chen(陈汝龙) diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index 9ab762a3de13..2ba5a2ce4ce7 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.4+11 + +* Fixes Activity leak. + ## 0.8.4+10 * iOS: allows picking images with WebP format. diff --git a/packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java b/packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java index 577675bd433a..311ef19103ac 100644 --- a/packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java +++ b/packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java @@ -85,11 +85,98 @@ public void onActivityDestroyed(Activity activity) { @Override public void onActivityStopped(Activity activity) { if (thisActivity == activity) { - delegate.saveStateBeforeResult(); + activityState.getDelegate().saveStateBeforeResult(); } } } + /** + * Move all activity-lifetime-bound states into this helper object, so that {@code setup} and + * {@code tearDown} would just become constructor and finalize calls of the helper object. + */ + private class ActivityState { + private Application application; + private Activity activity; + private ImagePickerDelegate delegate; + private MethodChannel channel; + private LifeCycleObserver observer; + private ActivityPluginBinding activityBinding; + + // This is null when not using v2 embedding; + private Lifecycle lifecycle; + + // Default constructor + ActivityState( + final Application application, + final Activity activity, + final BinaryMessenger messenger, + final MethodChannel.MethodCallHandler handler, + final PluginRegistry.Registrar registrar, + final ActivityPluginBinding activityBinding) { + this.application = application; + this.activity = activity; + this.activityBinding = activityBinding; + + delegate = constructDelegate(activity); + channel = new MethodChannel(messenger, CHANNEL); + channel.setMethodCallHandler(handler); + observer = new LifeCycleObserver(activity); + if (registrar != null) { + // V1 embedding setup for activity listeners. + application.registerActivityLifecycleCallbacks(observer); + registrar.addActivityResultListener(delegate); + registrar.addRequestPermissionsResultListener(delegate); + } else { + // V2 embedding setup for activity listeners. + activityBinding.addActivityResultListener(delegate); + activityBinding.addRequestPermissionsResultListener(delegate); + lifecycle = FlutterLifecycleAdapter.getActivityLifecycle(activityBinding); + lifecycle.addObserver(observer); + } + } + + // Only invoked by {@link #ImagePickerPlugin(ImagePickerDelegate, Activity)} for testing. + ActivityState(final ImagePickerDelegate delegate, final Activity activity) { + this.activity = activity; + this.delegate = delegate; + } + + void release() { + if (activityBinding != null) { + activityBinding.removeActivityResultListener(delegate); + activityBinding.removeRequestPermissionsResultListener(delegate); + activityBinding = null; + } + + if (lifecycle != null) { + lifecycle.removeObserver(observer); + lifecycle = null; + } + + if (channel != null) { + channel.setMethodCallHandler(null); + channel = null; + } + + if (application != null) { + application.unregisterActivityLifecycleCallbacks(observer); + application = null; + } + + activity = null; + observer = null; + delegate = null; + } + + Activity getActivity() { + return activity; + } + + ImagePickerDelegate getDelegate() { + return delegate; + } + } + static final String METHOD_CALL_IMAGE = "pickImage"; static final String METHOD_CALL_MULTI_IMAGE = "pickMultiImage"; static final String METHOD_CALL_VIDEO = "pickVideo"; @@ -101,15 +188,8 @@ public void onActivityStopped(Activity activity) { private static final int SOURCE_CAMERA = 0; private static final int SOURCE_GALLERY = 1; - private MethodChannel channel; - private ImagePickerDelegate delegate; private FlutterPluginBinding pluginBinding; - private ActivityPluginBinding activityBinding; - private Application application; - private Activity activity; - // This is null when not using v2 embedding; - private Lifecycle lifecycle; - private LifeCycleObserver observer; + private ActivityState activityState; @SuppressWarnings("deprecation") public static void registerWith(io.flutter.plugin.common.PluginRegistry.Registrar registrar) { @@ -137,8 +217,12 @@ public ImagePickerPlugin() {} @VisibleForTesting ImagePickerPlugin(final ImagePickerDelegate delegate, final Activity activity) { - this.delegate = delegate; - this.activity = activity; + activityState = new ActivityState(delegate, activity); + } + + @VisibleForTesting + final ActivityState getActivityState() { + return activityState; } @Override @@ -153,13 +237,12 @@ public void onDetachedFromEngine(FlutterPluginBinding binding) { @Override public void onAttachedToActivity(ActivityPluginBinding binding) { - activityBinding = binding; setup( pluginBinding.getBinaryMessenger(), (Application) pluginBinding.getApplicationContext(), - activityBinding.getActivity(), + binding.getActivity(), null, - activityBinding); + binding); } @Override @@ -183,37 +266,15 @@ private void setup( final Activity activity, final PluginRegistry.Registrar registrar, final ActivityPluginBinding activityBinding) { - this.activity = activity; - this.application = application; - this.delegate = constructDelegate(activity); - channel = new MethodChannel(messenger, CHANNEL); - channel.setMethodCallHandler(this); - observer = new LifeCycleObserver(activity); - if (registrar != null) { - // V1 embedding setup for activity listeners. - application.registerActivityLifecycleCallbacks(observer); - registrar.addActivityResultListener(delegate); - registrar.addRequestPermissionsResultListener(delegate); - } else { - // V2 embedding setup for activity listeners. - activityBinding.addActivityResultListener(delegate); - activityBinding.addRequestPermissionsResultListener(delegate); - lifecycle = FlutterLifecycleAdapter.getActivityLifecycle(activityBinding); - lifecycle.addObserver(observer); - } + activityState = + new ActivityState(application, activity, messenger, this, registrar, activityBinding); } private void tearDown() { - activityBinding.removeActivityResultListener(delegate); - activityBinding.removeRequestPermissionsResultListener(delegate); - activityBinding = null; - lifecycle.removeObserver(observer); - lifecycle = null; - delegate = null; - channel.setMethodCallHandler(null); - channel = null; - application.unregisterActivityLifecycleCallbacks(observer); - application = null; + if (activityState != null) { + activityState.release(); + activityState = null; + } } @VisibleForTesting @@ -273,12 +334,13 @@ public void run() { @Override public void onMethodCall(MethodCall call, MethodChannel.Result rawResult) { - if (activity == null) { + if (activityState == null || activityState.getActivity() == null) { rawResult.error("no_activity", "image_picker plugin requires a foreground activity.", null); return; } MethodChannel.Result result = new MethodResultWrapper(rawResult); int imageSource; + ImagePickerDelegate delegate = activityState.getDelegate(); if (call.argument("cameraDevice") != null) { CameraDevice device; int deviceIntValue = call.argument("cameraDevice"); diff --git a/packages/image_picker/image_picker/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java b/packages/image_picker/image_picker/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java index 422b8be74f7c..ce41343e3d2c 100644 --- a/packages/image_picker/image_picker/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java +++ b/packages/image_picker/image_picker/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java @@ -5,10 +5,13 @@ package io.flutter.plugins.imagepicker; import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; @@ -16,6 +19,11 @@ import android.app.Activity; import android.app.Application; +import androidx.lifecycle.Lifecycle; +import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding; +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; +import io.flutter.embedding.engine.plugins.lifecycle.HiddenLifecycleReference; +import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import java.io.File; @@ -41,6 +49,9 @@ public class ImagePickerPluginTest { @Mock io.flutter.plugin.common.PluginRegistry.Registrar mockRegistrar; + @Mock ActivityPluginBinding mockActivityBinding; + @Mock FlutterPluginBinding mockPluginBinding; + @Mock Activity mockActivity; @Mock Application mockApplication; @Mock ImagePickerDelegate mockImagePickerDelegate; @@ -52,7 +63,8 @@ public class ImagePickerPluginTest { public void setUp() { MockitoAnnotations.initMocks(this); when(mockRegistrar.context()).thenReturn(mockApplication); - + when(mockActivityBinding.getActivity()).thenReturn(mockActivity); + when(mockPluginBinding.getApplicationContext()).thenReturn(mockApplication); plugin = new ImagePickerPlugin(mockImagePickerDelegate, mockActivity); } @@ -176,6 +188,25 @@ public void constructDelegate_ShouldUseInternalCacheDirectory() { equalTo(mockDirectory)); } + @Test + public void onDetachedFromActivity_ShouldReleaseActivityState() { + final BinaryMessenger mockBinaryMessenger = mock(BinaryMessenger.class); + when(mockPluginBinding.getBinaryMessenger()).thenReturn(mockBinaryMessenger); + + final HiddenLifecycleReference mockLifecycleReference = mock(HiddenLifecycleReference.class); + when(mockActivityBinding.getLifecycle()).thenReturn(mockLifecycleReference); + + final Lifecycle mockLifecycle = mock(Lifecycle.class); + when(mockLifecycleReference.getLifecycle()).thenReturn(mockLifecycle); + + plugin.onAttachedToEngine(mockPluginBinding); + plugin.onAttachedToActivity(mockActivityBinding); + assertNotNull(plugin.getActivityState()); + + plugin.onDetachedFromActivity(); + assertNull(plugin.getActivityState()); + } + private MethodCall buildMethodCall(String method, final int source) { final Map arguments = new HashMap<>(); arguments.put("source", source); diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 4774711129da..553599f7306f 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.4+10 +version: 0.8.4+11 environment: sdk: ">=2.14.0 <3.0.0" From 73ab699a4a8ab9e80179fa201d4c1a059538f065 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 10 Mar 2022 13:40:24 -0500 Subject: [PATCH 285/600] Roll Flutter from 57aa49028db7 to 48172589e5ac (2 revisions) (#5037) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 5afb34058b93..be362674817b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -57aa49028db77a4edf0fbc6efc70cceea2d2c6ed +48172589e5acfc5eb1da7edb13b4f351e6e66275 From 0ade756afed0d9c0f3b4df58ad9871ead9abda50 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 10 Mar 2022 16:55:21 -0500 Subject: [PATCH 286/600] Roll Flutter from 48172589e5ac to 2ed7cdf35a0e (5 revisions) (#5040) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index be362674817b..fe665f2a53de 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -48172589e5acfc5eb1da7edb13b4f351e6e66275 +2ed7cdf35a0eb3cb25eb3a54a002339aad1fdaec From d3638944ba6f778349cfbceff1f34fe08062705e Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 11 Mar 2022 15:15:26 -0500 Subject: [PATCH 287/600] [flutter_plugin_tools] Fix subpackage analysis (#5027) --- script/tool/CHANGELOG.md | 5 +++ script/tool/lib/src/analyze_command.dart | 29 +++++++++------ .../tool/lib/src/common/plugin_command.dart | 20 +++++++---- .../lib/src/make_deps_path_based_command.dart | 4 +++ script/tool/pubspec.yaml | 2 +- script/tool/test/analyze_command_test.dart | 25 +++++++++++++ .../make_deps_path_based_command_test.dart | 36 +++++++++++++++++++ 7 files changed, 103 insertions(+), 18 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 8e5ae210ce0d..35786f4a8b1b 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.8.1 + +- Fixes an `analyze` regression in 0.8.0 with packages that have non-`example` + sub-packages. + ## 0.8.0 - Ensures that `firebase-test-lab` runs include an `integration_test` runner. diff --git a/script/tool/lib/src/analyze_command.dart b/script/tool/lib/src/analyze_command.dart index d8b17bfd5626..824d766f4ebd 100644 --- a/script/tool/lib/src/analyze_command.dart +++ b/script/tool/lib/src/analyze_command.dart @@ -102,16 +102,25 @@ class AnalyzeCommand extends PackageLoopingCommand { @override Future runForPackage(RepositoryPackage package) async { - // For non-example packages, fetch dependencies. 'flutter packages get' - // automatically runs 'pub get' in examples as part of handling the parent - // directory, which is guaranteed to come first in the package enumeration. - if (package.directory.basename != 'example' || - !RepositoryPackage(package.directory.parent).pubspecFile.existsSync()) { - final int exitCode = await processRunner.runAndStream( - flutterCommand, ['packages', 'get'], - workingDir: package.directory); - if (exitCode != 0) { - return PackageResult.fail(['Unable to get dependencies']); + // Analysis runs over the package and all subpackages, so all of them need + // `flutter packages get` run before analyzing. `example` packages can be + // skipped since 'flutter packages get' automatically runs `pub get` in + // examples as part of handling the parent directory. + final List packagesToGet = [ + package, + ...await getSubpackages(package).toList(), + ]; + for (final RepositoryPackage packageToGet in packagesToGet) { + if (packageToGet.directory.basename != 'example' || + !RepositoryPackage(packageToGet.directory.parent) + .pubspecFile + .existsSync()) { + final int exitCode = await processRunner.runAndStream( + flutterCommand, ['packages', 'get'], + workingDir: packageToGet.directory); + if (exitCode != 0) { + return PackageResult.fail(['Unable to get dependencies']); + } } } diff --git a/script/tool/lib/src/common/plugin_command.dart b/script/tool/lib/src/common/plugin_command.dart index fcc87c94ef91..d21002899669 100644 --- a/script/tool/lib/src/common/plugin_command.dart +++ b/script/tool/lib/src/common/plugin_command.dart @@ -417,16 +417,22 @@ abstract class PluginCommand extends Command { await for (final PackageEnumerationEntry plugin in getTargetPackages(filterExcluded: filterExcluded)) { yield plugin; - yield* plugin.package.directory - .list(recursive: true, followLinks: false) - .where(_isDartPackage) - .map((FileSystemEntity directory) => PackageEnumerationEntry( - // _isDartPackage guarantees that this cast is valid. - RepositoryPackage(directory as Directory), - excluded: plugin.excluded)); + yield* getSubpackages(plugin.package).map((RepositoryPackage package) => + PackageEnumerationEntry(package, excluded: plugin.excluded)); } } + /// Returns all Dart package folders (e.g., examples) under the given package. + Stream getSubpackages(RepositoryPackage package, + {bool filterExcluded = true}) async* { + yield* package.directory + .list(recursive: true, followLinks: false) + .where(_isDartPackage) + .map((FileSystemEntity directory) => + // _isDartPackage guarantees that this cast is valid. + RepositoryPackage(directory as Directory)); + } + /// Returns the files contained, recursively, within the packages /// involved in this command execution. Stream getFiles() { diff --git a/script/tool/lib/src/make_deps_path_based_command.dart b/script/tool/lib/src/make_deps_path_based_command.dart index 5ee42848a81b..c09060310e97 100644 --- a/script/tool/lib/src/make_deps_path_based_command.dart +++ b/script/tool/lib/src/make_deps_path_based_command.dart @@ -207,6 +207,10 @@ dependency_overrides: allComponents.contains('example')) { continue; } + if (!allComponents.contains(packagesDir.basename)) { + print(' Skipping $changedPath; not in packages directory.'); + continue; + } final RepositoryPackage package = RepositoryPackage(packagesDir.fileSystem.file(changedPath).parent); // Ignored deleted packages, as they won't be published. diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index 9ca5e2b77580..93a1a87ca337 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.8.0 +version: 0.8.1 dependencies: args: ^2.1.0 diff --git a/script/tool/test/analyze_command_test.dart b/script/tool/test/analyze_command_test.dart index 087e5ada37b0..56f4ab9576c6 100644 --- a/script/tool/test/analyze_command_test.dart +++ b/script/tool/test/analyze_command_test.dart @@ -71,6 +71,31 @@ void main() { ])); }); + test('runs flutter pub get for non-example subpackages', () async { + final Directory mainPackageDir = createFakePackage('a', packagesDir); + final Directory otherPackages = + mainPackageDir.childDirectory('other_packages'); + final Directory subpackage1 = + createFakePackage('subpackage1', otherPackages); + final Directory subpackage2 = + createFakePackage('subpackage2', otherPackages); + + await runCapturingPrint(runner, ['analyze']); + + expect( + processRunner.recordedCalls, + orderedEquals([ + ProcessCall('flutter', const ['packages', 'get'], + mainPackageDir.path), + ProcessCall( + 'flutter', const ['packages', 'get'], subpackage1.path), + ProcessCall( + 'flutter', const ['packages', 'get'], subpackage2.path), + ProcessCall('dart', const ['analyze', '--fatal-infos'], + mainPackageDir.path), + ])); + }); + test('don\'t elide a non-contained example package', () async { final Directory plugin1Dir = createFakePlugin('a', packagesDir); final Directory plugin2Dir = createFakePlugin('example', packagesDir); diff --git a/script/tool/test/make_deps_path_based_command_test.dart b/script/tool/test/make_deps_path_based_command_test.dart index bdd2139b237c..2021f24079e3 100644 --- a/script/tool/test/make_deps_path_based_command_test.dart +++ b/script/tool/test/make_deps_path_based_command_test.dart @@ -323,5 +323,41 @@ void main() { ]), ); }); + + test('skips anything outside of the packages directory', () async { + final Directory toolDir = packagesDir.parent.childDirectory('tool'); + const String newVersion = '1.1.0'; + final Directory package = createFakePackage( + 'flutter_plugin_tools', toolDir, + version: newVersion); + + // Simulate a minor version change so it would be a target. + final File pubspecFile = RepositoryPackage(package).pubspecFile; + final String changedFileOutput = [ + pubspecFile, + ].map((File file) => file.path).join('\n'); + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: changedFileOutput), + ]; + final String gitPubspecContents = + pubspecFile.readAsStringSync().replaceAll(newVersion, '1.0.0'); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: gitPubspecContents), + ]; + + final List output = await runCapturingPrint(runner, [ + 'make-deps-path-based', + '--target-dependencies-with-non-breaking-updates' + ]); + + expect( + output, + containsAllInOrder([ + contains( + 'Skipping /tool/flutter_plugin_tools/pubspec.yaml; not in packages directory.'), + contains('No target dependencies'), + ]), + ); + }); }); } From 0d5cf74c437e7f922ae27b82bb580e5043f57ceb Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 11 Mar 2022 16:00:23 -0500 Subject: [PATCH 288/600] Roll Flutter from 2ed7cdf35a0e to 201a64c98f35 (5 revisions) (#5043) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index fe665f2a53de..d17adfe06468 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2ed7cdf35a0eb3cb25eb3a54a002339aad1fdaec +201a64c98f350dbc88c6fd3f8a6050864f2259cf From 8189a2ceae92973bd1378d049292f3a832958b72 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 14 Mar 2022 16:10:23 -0400 Subject: [PATCH 289/600] [file_selector] Update macOS README and example (#5034) --- .../file_selector/file_selector/CHANGELOG.md | 5 ++ .../file_selector/file_selector/README.md | 17 ++++- .../macos/Runner.xcodeproj/project.pbxproj | 62 ++++++++++++++++++- .../contents.xcworkspacedata | 3 + .../macos/Runner/DebugProfile.entitlements | 2 + .../example/macos/Runner/Release.entitlements | 2 + .../file_selector/file_selector/pubspec.yaml | 2 +- 7 files changed, 90 insertions(+), 3 deletions(-) diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index dde818dbce52..65b3a64475e9 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.8.4+1 + +* Adds README information about macOS entitlements. +* Adds necessary entitlement to macOS example. + ## 0.8.4 * Adds an endorsed macOS implementation. diff --git a/packages/file_selector/file_selector/README.md b/packages/file_selector/file_selector/README.md index 22ae7073ca2d..863164ebf286 100644 --- a/packages/file_selector/file_selector/README.md +++ b/packages/file_selector/file_selector/README.md @@ -7,8 +7,22 @@ A Flutter plugin that manages files and interactions with file dialogs. ## Usage To use this plugin, add `file_selector` as a [dependency in your pubspec.yaml file](https://flutter.dev/platform-plugins/). +### macOS + +You will need to [add an entitlement][entitlement] for either read-only access: +``` + com.apple.security.files.user-selected.read-only + +``` +or read/write access: +``` + com.apple.security.files.user-selected.read-write + +``` +depending on your use case. + ### Examples -Here are small examples that show you how to use the API. +Here are small examples that show you how to use the API. Please also take a look at our [example][example] app. #### Open a single file @@ -34,3 +48,4 @@ await file.saveTo(path); ``` [example]:./example +[entitlement]: https://docs.flutter.dev/desktop#entitlements-and-the-app-sandbox diff --git a/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.pbxproj b/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.pbxproj index c84862c67576..c450a1d06cf5 100644 --- a/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.pbxproj @@ -26,6 +26,7 @@ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 6BA632E5BE2B856B0D473EBF /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C6D20B684858422917AB21A6 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -52,9 +53,10 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 17DF935FF296A265D8BE378B /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; @@ -66,8 +68,11 @@ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 453A41FF685B9AACDF48F0C6 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 6FA96861AA2D76C12832F6C9 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + C6D20B684858422917AB21A6 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -75,6 +80,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 6BA632E5BE2B856B0D473EBF /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -99,6 +105,7 @@ 33CEB47122A05771004F2AC0 /* Flutter */, 33CC10EE2044A3C60003C045 /* Products */, D73912EC22F37F3D000D13A0 /* Frameworks */, + 58708F6C9D1522F09C51DA54 /* Pods */, ); sourceTree = ""; }; @@ -145,9 +152,21 @@ path = Runner; sourceTree = ""; }; + 58708F6C9D1522F09C51DA54 /* Pods */ = { + isa = PBXGroup; + children = ( + 453A41FF685B9AACDF48F0C6 /* Pods-Runner.debug.xcconfig */, + 17DF935FF296A265D8BE378B /* Pods-Runner.release.xcconfig */, + 6FA96861AA2D76C12832F6C9 /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; D73912EC22F37F3D000D13A0 /* Frameworks */ = { isa = PBXGroup; children = ( + C6D20B684858422917AB21A6 /* Pods_Runner.framework */, ); name = Frameworks; sourceTree = ""; @@ -159,11 +178,13 @@ isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + A778864BDDD7B12C41D66FBB /* [CP] Check Pods Manifest.lock */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, + 028A8DA36859BD4F05694F96 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -233,6 +254,23 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 028A8DA36859BD4F05694F96 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 3399D490228B24CF009A79C7 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -270,6 +308,28 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; + A778864BDDD7B12C41D66FBB /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ diff --git a/packages/file_selector/file_selector/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/packages/file_selector/file_selector/example/macos/Runner.xcworkspace/contents.xcworkspacedata index 1d526a16ed0f..21a3cc14c74e 100644 --- a/packages/file_selector/file_selector/example/macos/Runner.xcworkspace/contents.xcworkspacedata +++ b/packages/file_selector/file_selector/example/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/packages/file_selector/file_selector/example/macos/Runner/DebugProfile.entitlements b/packages/file_selector/file_selector/example/macos/Runner/DebugProfile.entitlements index dddb8a30c851..d138bd5b0451 100644 --- a/packages/file_selector/file_selector/example/macos/Runner/DebugProfile.entitlements +++ b/packages/file_selector/file_selector/example/macos/Runner/DebugProfile.entitlements @@ -8,5 +8,7 @@ com.apple.security.network.server + com.apple.security.files.user-selected.read-write + diff --git a/packages/file_selector/file_selector/example/macos/Runner/Release.entitlements b/packages/file_selector/file_selector/example/macos/Runner/Release.entitlements index 852fa1a4728a..19afff14a08c 100644 --- a/packages/file_selector/file_selector/example/macos/Runner/Release.entitlements +++ b/packages/file_selector/file_selector/example/macos/Runner/Release.entitlements @@ -4,5 +4,7 @@ com.apple.security.app-sandbox + com.apple.security.files.user-selected.read-write + diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index 14ff92997c00..c05900f650cf 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for opening and saving files, or selecting directories, using native file selection UI. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.4 +version: 0.8.4+1 environment: sdk: ">=2.12.0 <3.0.0" From dcacf4c66a51b408b6e3c279686ae6822586c737 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 14 Mar 2022 21:20:22 -0400 Subject: [PATCH 290/600] [video_player] Rename internal method channels (#5045) --- .../video_player_android/CHANGELOG.md | 6 + .../flutter/plugins/videoplayer/Messages.java | 69 ++++--- .../videoplayer/VideoPlayerPlugin.java | 9 +- .../lib/src/android_video_player.dart | 2 +- .../lib/src/messages.g.dart | 77 ++++--- .../pigeons/messages.dart | 2 +- .../video_player_android/pubspec.yaml | 4 +- .../video_player_android/test/test_api.dart | 65 +++--- .../video_player_avfoundation/CHANGELOG.md | 6 + .../ios/RunnerTests/VideoPlayerTests.m | 2 +- .../ios/Classes/FLTVideoPlayerPlugin.m | 6 +- .../ios/Classes/messages.g.h | 15 +- .../ios/Classes/messages.g.m | 194 ++++++++++-------- .../lib/src/avfoundation_video_player.dart | 2 +- .../lib/src/messages.g.dart | 77 ++++--- .../pigeons/messages.dart | 2 +- .../video_player_avfoundation/pubspec.yaml | 4 +- .../test/test_api.dart | 66 +++--- 18 files changed, 327 insertions(+), 281 deletions(-) diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index ec526ee1c7c9..774589c05f84 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,3 +1,9 @@ +## 2.3.1 + +* Renames internal method channels to avoid potential confusion with the + default implementation's method channel. +* Updates Pigeon to 2.0.1. + ## 2.3.0 * Updates Pigeon to ^1.0.16. diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java index 0cdf3564d334..6593ebf9c22a 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v1.0.16), do not edit directly. +// Autogenerated from Pigeon (v2.0.1), do not edit directly. // See also: https://pub.dev/packages/pigeon package io.flutter.plugins.videoplayer; @@ -557,10 +557,10 @@ Map toMap() { } } - private static class VideoPlayerApiCodec extends StandardMessageCodec { - public static final VideoPlayerApiCodec INSTANCE = new VideoPlayerApiCodec(); + private static class AndroidVideoPlayerApiCodec extends StandardMessageCodec { + public static final AndroidVideoPlayerApiCodec INSTANCE = new AndroidVideoPlayerApiCodec(); - private VideoPlayerApiCodec() {} + private AndroidVideoPlayerApiCodec() {} @Override protected Object readValueOfType(byte type, ByteBuffer buffer) { @@ -621,40 +621,45 @@ protected void writeValue(ByteArrayOutputStream stream, Object value) { } /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ - public interface VideoPlayerApi { + public interface AndroidVideoPlayerApi { void initialize(); - TextureMessage create(CreateMessage msg); + @NonNull + TextureMessage create(@NonNull CreateMessage msg); - void dispose(TextureMessage msg); + void dispose(@NonNull TextureMessage msg); - void setLooping(LoopingMessage msg); + void setLooping(@NonNull LoopingMessage msg); - void setVolume(VolumeMessage msg); + void setVolume(@NonNull VolumeMessage msg); - void setPlaybackSpeed(PlaybackSpeedMessage msg); + void setPlaybackSpeed(@NonNull PlaybackSpeedMessage msg); - void play(TextureMessage msg); + void play(@NonNull TextureMessage msg); - PositionMessage position(TextureMessage msg); + @NonNull + PositionMessage position(@NonNull TextureMessage msg); - void seekTo(PositionMessage msg); + void seekTo(@NonNull PositionMessage msg); - void pause(TextureMessage msg); + void pause(@NonNull TextureMessage msg); - void setMixWithOthers(MixWithOthersMessage msg); + void setMixWithOthers(@NonNull MixWithOthersMessage msg); - /** The codec used by VideoPlayerApi. */ + /** The codec used by AndroidVideoPlayerApi. */ static MessageCodec getCodec() { - return VideoPlayerApiCodec.INSTANCE; + return AndroidVideoPlayerApiCodec.INSTANCE; } - /** Sets up an instance of `VideoPlayerApi` to handle messages through the `binaryMessenger`. */ - static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { + /** + * Sets up an instance of `AndroidVideoPlayerApi` to handle messages through the + * `binaryMessenger`. + */ + static void setup(BinaryMessenger binaryMessenger, AndroidVideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.initialize", getCodec()); + binaryMessenger, "dev.flutter.pigeon.AndroidVideoPlayerApi.initialize", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -674,7 +679,7 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.create", getCodec()); + binaryMessenger, "dev.flutter.pigeon.AndroidVideoPlayerApi.create", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -699,7 +704,7 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.dispose", getCodec()); + binaryMessenger, "dev.flutter.pigeon.AndroidVideoPlayerApi.dispose", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -724,7 +729,7 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.setLooping", getCodec()); + binaryMessenger, "dev.flutter.pigeon.AndroidVideoPlayerApi.setLooping", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -749,7 +754,7 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.setVolume", getCodec()); + binaryMessenger, "dev.flutter.pigeon.AndroidVideoPlayerApi.setVolume", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -774,7 +779,9 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed", getCodec()); + binaryMessenger, + "dev.flutter.pigeon.AndroidVideoPlayerApi.setPlaybackSpeed", + getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -799,7 +806,7 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.play", getCodec()); + binaryMessenger, "dev.flutter.pigeon.AndroidVideoPlayerApi.play", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -824,7 +831,7 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.position", getCodec()); + binaryMessenger, "dev.flutter.pigeon.AndroidVideoPlayerApi.position", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -849,7 +856,7 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.seekTo", getCodec()); + binaryMessenger, "dev.flutter.pigeon.AndroidVideoPlayerApi.seekTo", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -874,7 +881,7 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.pause", getCodec()); + binaryMessenger, "dev.flutter.pigeon.AndroidVideoPlayerApi.pause", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -899,7 +906,9 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers", getCodec()); + binaryMessenger, + "dev.flutter.pigeon.AndroidVideoPlayerApi.setMixWithOthers", + getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java index 168d90d8a57a..56fabecd3a96 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java @@ -12,13 +12,13 @@ import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.EventChannel; +import io.flutter.plugins.videoplayer.Messages.AndroidVideoPlayerApi; import io.flutter.plugins.videoplayer.Messages.CreateMessage; import io.flutter.plugins.videoplayer.Messages.LoopingMessage; import io.flutter.plugins.videoplayer.Messages.MixWithOthersMessage; import io.flutter.plugins.videoplayer.Messages.PlaybackSpeedMessage; import io.flutter.plugins.videoplayer.Messages.PositionMessage; import io.flutter.plugins.videoplayer.Messages.TextureMessage; -import io.flutter.plugins.videoplayer.Messages.VideoPlayerApi; import io.flutter.plugins.videoplayer.Messages.VolumeMessage; import io.flutter.view.TextureRegistry; import java.security.KeyManagementException; @@ -27,7 +27,7 @@ import javax.net.ssl.HttpsURLConnection; /** Android platform implementation of the VideoPlayerPlugin. */ -public class VideoPlayerPlugin implements FlutterPlugin, VideoPlayerApi { +public class VideoPlayerPlugin implements FlutterPlugin, AndroidVideoPlayerApi { private static final String TAG = "VideoPlayerPlugin"; private final LongSparseArray videoPlayers = new LongSparseArray<>(); private FlutterState flutterState; @@ -61,7 +61,6 @@ public static void registerWith(io.flutter.plugin.common.PluginRegistry.Registra @Override public void onAttachedToEngine(FlutterPluginBinding binding) { - if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { try { HttpsURLConnection.setDefaultSSLSocketFactory(new CustomSSLSocketFactory()); @@ -241,11 +240,11 @@ private static final class FlutterState { } void startListening(VideoPlayerPlugin methodCallHandler, BinaryMessenger messenger) { - VideoPlayerApi.setup(messenger, methodCallHandler); + AndroidVideoPlayerApi.setup(messenger, methodCallHandler); } void stopListening(BinaryMessenger messenger) { - VideoPlayerApi.setup(messenger, null); + AndroidVideoPlayerApi.setup(messenger, null); } } } diff --git a/packages/video_player/video_player_android/lib/src/android_video_player.dart b/packages/video_player/video_player_android/lib/src/android_video_player.dart index d713b282d197..31d0744e51dc 100644 --- a/packages/video_player/video_player_android/lib/src/android_video_player.dart +++ b/packages/video_player/video_player_android/lib/src/android_video_player.dart @@ -14,7 +14,7 @@ import 'messages.g.dart'; /// An Android implementation of [VideoPlayerPlatform] that uses the /// Pigeon-generated [VideoPlayerApi]. class AndroidVideoPlayer extends VideoPlayerPlatform { - final VideoPlayerApi _api = VideoPlayerApi(); + final AndroidVideoPlayerApi _api = AndroidVideoPlayerApi(); /// Registers this class as the default instance of [PathProviderPlatform]. static void registerWith() { diff --git a/packages/video_player/video_player_android/lib/src/messages.g.dart b/packages/video_player/video_player_android/lib/src/messages.g.dart index 5fa09e33bc7e..0dadd2efc67e 100644 --- a/packages/video_player/video_player_android/lib/src/messages.g.dart +++ b/packages/video_player/video_player_android/lib/src/messages.g.dart @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v1.0.16), do not edit directly. +// Autogenerated from Pigeon (v2.0.1), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name // @dart = 2.12 @@ -191,8 +191,8 @@ class MixWithOthersMessage { } } -class _VideoPlayerApiCodec extends StandardMessageCodec { - const _VideoPlayerApiCodec(); +class _AndroidVideoPlayerApiCodec extends StandardMessageCodec { + const _AndroidVideoPlayerApiCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { if (value is CreateMessage) { @@ -251,20 +251,20 @@ class _VideoPlayerApiCodec extends StandardMessageCodec { } } -class VideoPlayerApi { - /// Constructor for [VideoPlayerApi]. The [binaryMessenger] named argument is +class AndroidVideoPlayerApi { + /// Constructor for [AndroidVideoPlayerApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - VideoPlayerApi({BinaryMessenger? binaryMessenger}) + AndroidVideoPlayerApi({BinaryMessenger? binaryMessenger}) : _binaryMessenger = binaryMessenger; final BinaryMessenger? _binaryMessenger; - static const MessageCodec codec = _VideoPlayerApiCodec(); + static const MessageCodec codec = _AndroidVideoPlayerApiCodec(); Future initialize() async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.initialize', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.initialize', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send(null) as Map?; @@ -272,7 +272,6 @@ class VideoPlayerApi { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -289,15 +288,14 @@ class VideoPlayerApi { Future create(CreateMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.create', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.create', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -307,6 +305,11 @@ class VideoPlayerApi { message: error['message'] as String?, details: error['details'], ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { return (replyMap['result'] as TextureMessage?)!; } @@ -314,15 +317,14 @@ class VideoPlayerApi { Future dispose(TextureMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.dispose', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.dispose', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -339,15 +341,14 @@ class VideoPlayerApi { Future setLooping(LoopingMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setLooping', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.setLooping', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -364,15 +365,14 @@ class VideoPlayerApi { Future setVolume(VolumeMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setVolume', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.setVolume', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -389,15 +389,14 @@ class VideoPlayerApi { Future setPlaybackSpeed(PlaybackSpeedMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.setPlaybackSpeed', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -414,15 +413,14 @@ class VideoPlayerApi { Future play(TextureMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.play', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.play', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -439,15 +437,14 @@ class VideoPlayerApi { Future position(TextureMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.position', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.position', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -457,6 +454,11 @@ class VideoPlayerApi { message: error['message'] as String?, details: error['details'], ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { return (replyMap['result'] as PositionMessage?)!; } @@ -464,15 +466,14 @@ class VideoPlayerApi { Future seekTo(PositionMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.seekTo', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.seekTo', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -489,15 +490,14 @@ class VideoPlayerApi { Future pause(TextureMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.pause', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.pause', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -514,15 +514,14 @@ class VideoPlayerApi { Future setMixWithOthers(MixWithOthersMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.setMixWithOthers', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = diff --git a/packages/video_player/video_player_android/pigeons/messages.dart b/packages/video_player/video_player_android/pigeons/messages.dart index 9efbfc947b5c..bf552f9369df 100644 --- a/packages/video_player/video_player_android/pigeons/messages.dart +++ b/packages/video_player/video_player_android/pigeons/messages.dart @@ -57,7 +57,7 @@ class MixWithOthersMessage { } @HostApi(dartHostTestHandler: 'TestHostVideoPlayerApi') -abstract class VideoPlayerApi { +abstract class AndroidVideoPlayerApi { void initialize(); TextureMessage create(CreateMessage msg); void dispose(TextureMessage msg); diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index c40d5185e854..15c063f83d40 100644 --- a/packages/video_player/video_player_android/pubspec.yaml +++ b/packages/video_player/video_player_android/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_android description: Android implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.0 +version: 2.3.1 environment: sdk: ">=2.14.0 <3.0.0" @@ -25,4 +25,4 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - pigeon: ^1.0.16 + pigeon: ^2.0.1 diff --git a/packages/video_player/video_player_android/test/test_api.dart b/packages/video_player/video_player_android/test/test_api.dart index e8bc9d85283f..c0ff23de9c22 100644 --- a/packages/video_player/video_player_android/test/test_api.dart +++ b/packages/video_player/video_player_android/test/test_api.dart @@ -1,9 +1,10 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v1.0.16), do not edit directly. +// Autogenerated from Pigeon (v2.0.1), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis +// ignore_for_file: avoid_relative_lib_imports // @dart = 2.12 import 'dart:async'; import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; @@ -92,7 +93,7 @@ abstract class TestHostVideoPlayerApi { {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.initialize', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.initialize', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); @@ -106,18 +107,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.create', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.create', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.create was null.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.create was null.'); final List args = (message as List?)!; final CreateMessage? arg_msg = (args[0] as CreateMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.create was null, expected non-null CreateMessage.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.create was null, expected non-null CreateMessage.'); final TextureMessage output = api.create(arg_msg!); return {'result': output}; }); @@ -125,18 +126,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.dispose', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.dispose', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.dispose was null.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.dispose was null.'); final List args = (message as List?)!; final TextureMessage? arg_msg = (args[0] as TextureMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.dispose was null, expected non-null TextureMessage.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.dispose was null, expected non-null TextureMessage.'); api.dispose(arg_msg!); return {}; }); @@ -144,18 +145,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setLooping', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.setLooping', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setLooping was null.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.setLooping was null.'); final List args = (message as List?)!; final LoopingMessage? arg_msg = (args[0] as LoopingMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setLooping was null, expected non-null LoopingMessage.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.setLooping was null, expected non-null LoopingMessage.'); api.setLooping(arg_msg!); return {}; }); @@ -163,18 +164,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setVolume', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.setVolume', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setVolume was null.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.setVolume was null.'); final List args = (message as List?)!; final VolumeMessage? arg_msg = (args[0] as VolumeMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setVolume was null, expected non-null VolumeMessage.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.setVolume was null, expected non-null VolumeMessage.'); api.setVolume(arg_msg!); return {}; }); @@ -182,19 +183,19 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.setPlaybackSpeed', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed was null.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.setPlaybackSpeed was null.'); final List args = (message as List?)!; final PlaybackSpeedMessage? arg_msg = (args[0] as PlaybackSpeedMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed was null, expected non-null PlaybackSpeedMessage.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.setPlaybackSpeed was null, expected non-null PlaybackSpeedMessage.'); api.setPlaybackSpeed(arg_msg!); return {}; }); @@ -202,18 +203,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.play', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.play', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.play was null.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.play was null.'); final List args = (message as List?)!; final TextureMessage? arg_msg = (args[0] as TextureMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.play was null, expected non-null TextureMessage.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.play was null, expected non-null TextureMessage.'); api.play(arg_msg!); return {}; }); @@ -221,18 +222,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.position', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.position', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.position was null.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.position was null.'); final List args = (message as List?)!; final TextureMessage? arg_msg = (args[0] as TextureMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.position was null, expected non-null TextureMessage.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.position was null, expected non-null TextureMessage.'); final PositionMessage output = api.position(arg_msg!); return {'result': output}; }); @@ -240,18 +241,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.seekTo', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.seekTo', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.seekTo was null.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.seekTo was null.'); final List args = (message as List?)!; final PositionMessage? arg_msg = (args[0] as PositionMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.seekTo was null, expected non-null PositionMessage.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.seekTo was null, expected non-null PositionMessage.'); api.seekTo(arg_msg!); return {}; }); @@ -259,18 +260,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.pause', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.pause', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.pause was null.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.pause was null.'); final List args = (message as List?)!; final TextureMessage? arg_msg = (args[0] as TextureMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.pause was null, expected non-null TextureMessage.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.pause was null, expected non-null TextureMessage.'); api.pause(arg_msg!); return {}; }); @@ -278,19 +279,19 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.setMixWithOthers', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers was null.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.setMixWithOthers was null.'); final List args = (message as List?)!; final MixWithOthersMessage? arg_msg = (args[0] as MixWithOthersMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers was null, expected non-null MixWithOthersMessage.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.setMixWithOthers was null, expected non-null MixWithOthersMessage.'); api.setMixWithOthers(arg_msg!); return {}; }); diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index d6ecabd4b9ad..c36c9878d1a1 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,3 +1,9 @@ +## 2.3.1 + +* Renames internal method channels to avoid potential confusion with the + default implementation's method channel. +* Updates Pigeon to 2.0.1. + ## 2.3.0 * Updates Pigeon to ^1.0.16. diff --git a/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m b/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m index 6d8b3965fd94..cbf2866aa071 100644 --- a/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m +++ b/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m @@ -12,7 +12,7 @@ @interface FLTVideoPlayer : NSObject @property(readonly, nonatomic) AVPlayer *player; @end -@interface FLTVideoPlayerPlugin (Test) +@interface FLTVideoPlayerPlugin (Test) @property(readonly, strong, nonatomic) NSMutableDictionary *playersByTextureId; @end diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m index 026b576cdb10..2fb9f7f55600 100644 --- a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m +++ b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m @@ -499,7 +499,7 @@ - (void)dispose { @end -@interface FLTVideoPlayerPlugin () +@interface FLTVideoPlayerPlugin () @property(readonly, weak, nonatomic) NSObject *registry; @property(readonly, weak, nonatomic) NSObject *messenger; @property(readonly, strong, nonatomic) @@ -511,7 +511,7 @@ @implementation FLTVideoPlayerPlugin + (void)registerWithRegistrar:(NSObject *)registrar { FLTVideoPlayerPlugin *instance = [[FLTVideoPlayerPlugin alloc] initWithRegistrar:registrar]; [registrar publish:instance]; - FLTVideoPlayerApiSetup(registrar.messenger, instance); + FLTAVFoundationVideoPlayerApiSetup(registrar.messenger, instance); } - (instancetype)initWithRegistrar:(NSObject *)registrar { @@ -530,7 +530,7 @@ - (void)detachFromEngineForRegistrar:(NSObject *)registr // TODO(57151): This should be commented out when 57151's fix lands on stable. // This is the correct behavior we never did it in the past and the engine // doesn't currently support it. - // FLTVideoPlayerApiSetup(registrar.messenger, nil); + // FLTAVFoundationVideoPlayerApiSetup(registrar.messenger, nil); } - (FLTTextureMessage *)onPlayerSetup:(FLTVideoPlayer *)player diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.h b/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.h index 96d02d2f7360..130d4849f372 100644 --- a/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.h +++ b/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.h @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v1.0.17), do not edit directly. +// Autogenerated from Pigeon (v2.0.1), do not edit directly. // See also: https://pub.dev/packages/pigeon #import @protocol FlutterBinaryMessenger; @@ -80,11 +80,12 @@ NS_ASSUME_NONNULL_BEGIN @property(nonatomic, strong) NSNumber *mixWithOthers; @end -/// The codec used by FLTVideoPlayerApi. -NSObject *FLTVideoPlayerApiGetCodec(void); +/// The codec used by FLTAVFoundationVideoPlayerApi. +NSObject *FLTAVFoundationVideoPlayerApiGetCodec(void); -@protocol FLTVideoPlayerApi +@protocol FLTAVFoundationVideoPlayerApi - (void)initialize:(FlutterError *_Nullable *_Nonnull)error; +/// @return `nil` only when `error != nil`. - (nullable FLTTextureMessage *)create:(FLTCreateMessage *)msg error:(FlutterError *_Nullable *_Nonnull)error; - (void)dispose:(FLTTextureMessage *)msg error:(FlutterError *_Nullable *_Nonnull)error; @@ -93,6 +94,7 @@ NSObject *FLTVideoPlayerApiGetCodec(void); - (void)setPlaybackSpeed:(FLTPlaybackSpeedMessage *)msg error:(FlutterError *_Nullable *_Nonnull)error; - (void)play:(FLTTextureMessage *)msg error:(FlutterError *_Nullable *_Nonnull)error; +/// @return `nil` only when `error != nil`. - (nullable FLTPositionMessage *)position:(FLTTextureMessage *)msg error:(FlutterError *_Nullable *_Nonnull)error; - (void)seekTo:(FLTPositionMessage *)msg error:(FlutterError *_Nullable *_Nonnull)error; @@ -101,7 +103,8 @@ NSObject *FLTVideoPlayerApiGetCodec(void); error:(FlutterError *_Nullable *_Nonnull)error; @end -extern void FLTVideoPlayerApiSetup(id binaryMessenger, - NSObject *_Nullable api); +extern void FLTAVFoundationVideoPlayerApiSetup( + id binaryMessenger, + NSObject *_Nullable api); NS_ASSUME_NONNULL_END diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.m b/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.m index c9acee44593c..d82dc386878d 100644 --- a/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.m +++ b/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.m @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v1.0.17), do not edit directly. +// Autogenerated from Pigeon (v2.0.1), do not edit directly. // See also: https://pub.dev/packages/pigeon #import "messages.g.h" #import @@ -28,6 +28,10 @@ static id GetNullableObject(NSDictionary *dict, id key) { id result = dict[key]; return (result == [NSNull null]) ? nil : result; } +static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { + id result = array[key]; + return (result == [NSNull null]) ? nil : result; +} @interface FLTTextureMessage () + (FLTTextureMessage *)fromMap:(NSDictionary *)dict; @@ -223,9 +227,9 @@ - (NSDictionary *)toMap { } @end -@interface FLTVideoPlayerApiCodecReader : FlutterStandardReader +@interface FLTAVFoundationVideoPlayerApiCodecReader : FlutterStandardReader @end -@implementation FLTVideoPlayerApiCodecReader +@implementation FLTAVFoundationVideoPlayerApiCodecReader - (nullable id)readValueOfType:(UInt8)type { switch (type) { case 128: @@ -255,9 +259,9 @@ - (nullable id)readValueOfType:(UInt8)type { } @end -@interface FLTVideoPlayerApiCodecWriter : FlutterStandardWriter +@interface FLTAVFoundationVideoPlayerApiCodecWriter : FlutterStandardWriter @end -@implementation FLTVideoPlayerApiCodecWriter +@implementation FLTAVFoundationVideoPlayerApiCodecWriter - (void)writeValue:(id)value { if ([value isKindOfClass:[FLTCreateMessage class]]) { [self writeByte:128]; @@ -286,38 +290,39 @@ - (void)writeValue:(id)value { } @end -@interface FLTVideoPlayerApiCodecReaderWriter : FlutterStandardReaderWriter +@interface FLTAVFoundationVideoPlayerApiCodecReaderWriter : FlutterStandardReaderWriter @end -@implementation FLTVideoPlayerApiCodecReaderWriter +@implementation FLTAVFoundationVideoPlayerApiCodecReaderWriter - (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { - return [[FLTVideoPlayerApiCodecWriter alloc] initWithData:data]; + return [[FLTAVFoundationVideoPlayerApiCodecWriter alloc] initWithData:data]; } - (FlutterStandardReader *)readerWithData:(NSData *)data { - return [[FLTVideoPlayerApiCodecReader alloc] initWithData:data]; + return [[FLTAVFoundationVideoPlayerApiCodecReader alloc] initWithData:data]; } @end -NSObject *FLTVideoPlayerApiGetCodec() { +NSObject *FLTAVFoundationVideoPlayerApiGetCodec() { static dispatch_once_t sPred = 0; static FlutterStandardMessageCodec *sSharedObject = nil; dispatch_once(&sPred, ^{ - FLTVideoPlayerApiCodecReaderWriter *readerWriter = - [[FLTVideoPlayerApiCodecReaderWriter alloc] init]; + FLTAVFoundationVideoPlayerApiCodecReaderWriter *readerWriter = + [[FLTAVFoundationVideoPlayerApiCodecReaderWriter alloc] init]; sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; }); return sSharedObject; } -void FLTVideoPlayerApiSetup(id binaryMessenger, - NSObject *api) { +void FLTAVFoundationVideoPlayerApiSetup(id binaryMessenger, + NSObject *api) { { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.initialize" - binaryMessenger:binaryMessenger - codec:FLTVideoPlayerApiGetCodec()]; + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.AVFoundationVideoPlayerApi.initialize" + binaryMessenger:binaryMessenger + codec:FLTAVFoundationVideoPlayerApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(initialize:)], - @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(initialize:)", api); + @"FLTAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(initialize:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; [api initialize:&error]; @@ -328,16 +333,18 @@ void FLTVideoPlayerApiSetup(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.create" - binaryMessenger:binaryMessenger - codec:FLTVideoPlayerApiGetCodec()]; + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.AVFoundationVideoPlayerApi.create" + binaryMessenger:binaryMessenger + codec:FLTAVFoundationVideoPlayerApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(create:error:)], - @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(create:error:)", api); + NSCAssert( + [api respondsToSelector:@selector(create:error:)], + @"FLTAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(create:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - FLTCreateMessage *arg_msg = args[0]; + FLTCreateMessage *arg_msg = GetNullableObjectAtIndex(args, 0); FlutterError *error; FLTTextureMessage *output = [api create:arg_msg error:&error]; callback(wrapResult(output, error)); @@ -347,16 +354,18 @@ void FLTVideoPlayerApiSetup(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.dispose" - binaryMessenger:binaryMessenger - codec:FLTVideoPlayerApiGetCodec()]; + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.AVFoundationVideoPlayerApi.dispose" + binaryMessenger:binaryMessenger + codec:FLTAVFoundationVideoPlayerApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(dispose:error:)], - @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(dispose:error:)", api); + NSCAssert( + [api respondsToSelector:@selector(dispose:error:)], + @"FLTAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(dispose:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - FLTTextureMessage *arg_msg = args[0]; + FLTTextureMessage *arg_msg = GetNullableObjectAtIndex(args, 0); FlutterError *error; [api dispose:arg_msg error:&error]; callback(wrapResult(nil, error)); @@ -366,16 +375,18 @@ void FLTVideoPlayerApiSetup(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.setLooping" - binaryMessenger:binaryMessenger - codec:FLTVideoPlayerApiGetCodec()]; + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.AVFoundationVideoPlayerApi.setLooping" + binaryMessenger:binaryMessenger + codec:FLTAVFoundationVideoPlayerApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(setLooping:error:)], - @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(setLooping:error:)", api); + NSCAssert( + [api respondsToSelector:@selector(setLooping:error:)], + @"FLTAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(setLooping:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - FLTLoopingMessage *arg_msg = args[0]; + FLTLoopingMessage *arg_msg = GetNullableObjectAtIndex(args, 0); FlutterError *error; [api setLooping:arg_msg error:&error]; callback(wrapResult(nil, error)); @@ -385,16 +396,18 @@ void FLTVideoPlayerApiSetup(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.setVolume" - binaryMessenger:binaryMessenger - codec:FLTVideoPlayerApiGetCodec()]; + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.AVFoundationVideoPlayerApi.setVolume" + binaryMessenger:binaryMessenger + codec:FLTAVFoundationVideoPlayerApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(setVolume:error:)], - @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(setVolume:error:)", api); + NSCAssert( + [api respondsToSelector:@selector(setVolume:error:)], + @"FLTAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(setVolume:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - FLTVolumeMessage *arg_msg = args[0]; + FLTVolumeMessage *arg_msg = GetNullableObjectAtIndex(args, 0); FlutterError *error; [api setVolume:arg_msg error:&error]; callback(wrapResult(nil, error)); @@ -404,17 +417,18 @@ void FLTVideoPlayerApiSetup(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed" - binaryMessenger:binaryMessenger - codec:FLTVideoPlayerApiGetCodec()]; + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.AVFoundationVideoPlayerApi.setPlaybackSpeed" + binaryMessenger:binaryMessenger + codec:FLTAVFoundationVideoPlayerApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(setPlaybackSpeed:error:)], - @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(setPlaybackSpeed:error:)", + @"FLTAVFoundationVideoPlayerApi api (%@) doesn't respond to " + @"@selector(setPlaybackSpeed:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - FLTPlaybackSpeedMessage *arg_msg = args[0]; + FLTPlaybackSpeedMessage *arg_msg = GetNullableObjectAtIndex(args, 0); FlutterError *error; [api setPlaybackSpeed:arg_msg error:&error]; callback(wrapResult(nil, error)); @@ -424,16 +438,17 @@ void FLTVideoPlayerApiSetup(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = - [FlutterBasicMessageChannel messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.play" - binaryMessenger:binaryMessenger - codec:FLTVideoPlayerApiGetCodec()]; + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.AVFoundationVideoPlayerApi.play" + binaryMessenger:binaryMessenger + codec:FLTAVFoundationVideoPlayerApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(play:error:)], - @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(play:error:)", api); + @"FLTAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(play:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - FLTTextureMessage *arg_msg = args[0]; + FLTTextureMessage *arg_msg = GetNullableObjectAtIndex(args, 0); FlutterError *error; [api play:arg_msg error:&error]; callback(wrapResult(nil, error)); @@ -443,16 +458,18 @@ void FLTVideoPlayerApiSetup(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.position" - binaryMessenger:binaryMessenger - codec:FLTVideoPlayerApiGetCodec()]; + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.AVFoundationVideoPlayerApi.position" + binaryMessenger:binaryMessenger + codec:FLTAVFoundationVideoPlayerApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(position:error:)], - @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(position:error:)", api); + NSCAssert( + [api respondsToSelector:@selector(position:error:)], + @"FLTAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(position:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - FLTTextureMessage *arg_msg = args[0]; + FLTTextureMessage *arg_msg = GetNullableObjectAtIndex(args, 0); FlutterError *error; FLTPositionMessage *output = [api position:arg_msg error:&error]; callback(wrapResult(output, error)); @@ -462,16 +479,18 @@ void FLTVideoPlayerApiSetup(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.seekTo" - binaryMessenger:binaryMessenger - codec:FLTVideoPlayerApiGetCodec()]; + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.AVFoundationVideoPlayerApi.seekTo" + binaryMessenger:binaryMessenger + codec:FLTAVFoundationVideoPlayerApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(seekTo:error:)], - @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(seekTo:error:)", api); + NSCAssert( + [api respondsToSelector:@selector(seekTo:error:)], + @"FLTAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(seekTo:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - FLTPositionMessage *arg_msg = args[0]; + FLTPositionMessage *arg_msg = GetNullableObjectAtIndex(args, 0); FlutterError *error; [api seekTo:arg_msg error:&error]; callback(wrapResult(nil, error)); @@ -481,16 +500,18 @@ void FLTVideoPlayerApiSetup(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.pause" - binaryMessenger:binaryMessenger - codec:FLTVideoPlayerApiGetCodec()]; + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.AVFoundationVideoPlayerApi.pause" + binaryMessenger:binaryMessenger + codec:FLTAVFoundationVideoPlayerApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(pause:error:)], - @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(pause:error:)", api); + NSCAssert( + [api respondsToSelector:@selector(pause:error:)], + @"FLTAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(pause:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - FLTTextureMessage *arg_msg = args[0]; + FLTTextureMessage *arg_msg = GetNullableObjectAtIndex(args, 0); FlutterError *error; [api pause:arg_msg error:&error]; callback(wrapResult(nil, error)); @@ -500,17 +521,18 @@ void FLTVideoPlayerApiSetup(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers" - binaryMessenger:binaryMessenger - codec:FLTVideoPlayerApiGetCodec()]; + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.AVFoundationVideoPlayerApi.setMixWithOthers" + binaryMessenger:binaryMessenger + codec:FLTAVFoundationVideoPlayerApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(setMixWithOthers:error:)], - @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(setMixWithOthers:error:)", + @"FLTAVFoundationVideoPlayerApi api (%@) doesn't respond to " + @"@selector(setMixWithOthers:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - FLTMixWithOthersMessage *arg_msg = args[0]; + FLTMixWithOthersMessage *arg_msg = GetNullableObjectAtIndex(args, 0); FlutterError *error; [api setMixWithOthers:arg_msg error:&error]; callback(wrapResult(nil, error)); diff --git a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart index bf1518dfa32a..5dc6862c41df 100644 --- a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart +++ b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart @@ -14,7 +14,7 @@ import 'messages.g.dart'; /// An iOS implementation of [VideoPlayerPlatform] that uses the /// Pigeon-generated [VideoPlayerApi]. class AVFoundationVideoPlayer extends VideoPlayerPlatform { - final VideoPlayerApi _api = VideoPlayerApi(); + final AVFoundationVideoPlayerApi _api = AVFoundationVideoPlayerApi(); /// Registers this class as the default instance of [VideoPlayerPlatform]. static void registerWith() { diff --git a/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart b/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart index 1a679d5915b4..a745c66322d4 100644 --- a/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart +++ b/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v1.0.17), do not edit directly. +// Autogenerated from Pigeon (v2.0.1), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name // @dart = 2.12 @@ -191,8 +191,8 @@ class MixWithOthersMessage { } } -class _VideoPlayerApiCodec extends StandardMessageCodec { - const _VideoPlayerApiCodec(); +class _AVFoundationVideoPlayerApiCodec extends StandardMessageCodec { + const _AVFoundationVideoPlayerApiCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { if (value is CreateMessage) { @@ -251,20 +251,20 @@ class _VideoPlayerApiCodec extends StandardMessageCodec { } } -class VideoPlayerApi { - /// Constructor for [VideoPlayerApi]. The [binaryMessenger] named argument is +class AVFoundationVideoPlayerApi { + /// Constructor for [AVFoundationVideoPlayerApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - VideoPlayerApi({BinaryMessenger? binaryMessenger}) + AVFoundationVideoPlayerApi({BinaryMessenger? binaryMessenger}) : _binaryMessenger = binaryMessenger; final BinaryMessenger? _binaryMessenger; - static const MessageCodec codec = _VideoPlayerApiCodec(); + static const MessageCodec codec = _AVFoundationVideoPlayerApiCodec(); Future initialize() async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.initialize', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.initialize', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send(null) as Map?; @@ -272,7 +272,6 @@ class VideoPlayerApi { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -289,15 +288,14 @@ class VideoPlayerApi { Future create(CreateMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.create', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.create', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -307,6 +305,11 @@ class VideoPlayerApi { message: error['message'] as String?, details: error['details'], ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { return (replyMap['result'] as TextureMessage?)!; } @@ -314,15 +317,14 @@ class VideoPlayerApi { Future dispose(TextureMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.dispose', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.dispose', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -339,15 +341,14 @@ class VideoPlayerApi { Future setLooping(LoopingMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setLooping', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.setLooping', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -364,15 +365,14 @@ class VideoPlayerApi { Future setVolume(VolumeMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setVolume', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.setVolume', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -389,15 +389,14 @@ class VideoPlayerApi { Future setPlaybackSpeed(PlaybackSpeedMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.setPlaybackSpeed', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -414,15 +413,14 @@ class VideoPlayerApi { Future play(TextureMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.play', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.play', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -439,15 +437,14 @@ class VideoPlayerApi { Future position(TextureMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.position', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.position', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -457,6 +454,11 @@ class VideoPlayerApi { message: error['message'] as String?, details: error['details'], ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { return (replyMap['result'] as PositionMessage?)!; } @@ -464,15 +466,14 @@ class VideoPlayerApi { Future seekTo(PositionMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.seekTo', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.seekTo', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -489,15 +490,14 @@ class VideoPlayerApi { Future pause(TextureMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.pause', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.pause', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -514,15 +514,14 @@ class VideoPlayerApi { Future setMixWithOthers(MixWithOthersMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.setMixWithOthers', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = diff --git a/packages/video_player/video_player_avfoundation/pigeons/messages.dart b/packages/video_player/video_player_avfoundation/pigeons/messages.dart index d357caf81e3d..e6eda5960f29 100644 --- a/packages/video_player/video_player_avfoundation/pigeons/messages.dart +++ b/packages/video_player/video_player_avfoundation/pigeons/messages.dart @@ -58,7 +58,7 @@ class MixWithOthersMessage { } @HostApi(dartHostTestHandler: 'TestHostVideoPlayerApi') -abstract class VideoPlayerApi { +abstract class AVFoundationVideoPlayerApi { @ObjCSelector('initialize') void initialize(); @ObjCSelector('create:') diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index 044d15b890b2..0e42c26de829 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_avfoundation description: iOS implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.0 +version: 2.3.1 environment: sdk: ">=2.14.0 <3.0.0" @@ -24,4 +24,4 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - pigeon: ^1.0.17 + pigeon: ^2.0.1 diff --git a/packages/video_player/video_player_avfoundation/test/test_api.dart b/packages/video_player/video_player_avfoundation/test/test_api.dart index 191358e23024..af6d072107c8 100644 --- a/packages/video_player/video_player_avfoundation/test/test_api.dart +++ b/packages/video_player/video_player_avfoundation/test/test_api.dart @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v1.0.17), do not edit directly. +// Autogenerated from Pigeon (v2.0.1), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis // ignore_for_file: avoid_relative_lib_imports @@ -93,7 +93,7 @@ abstract class TestHostVideoPlayerApi { {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.initialize', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.initialize', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); @@ -107,18 +107,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.create', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.create', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.create was null.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.create was null.'); final List args = (message as List?)!; final CreateMessage? arg_msg = (args[0] as CreateMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.create was null, expected non-null CreateMessage.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.create was null, expected non-null CreateMessage.'); final TextureMessage output = api.create(arg_msg!); return {'result': output}; }); @@ -126,18 +126,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.dispose', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.dispose', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.dispose was null.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.dispose was null.'); final List args = (message as List?)!; final TextureMessage? arg_msg = (args[0] as TextureMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.dispose was null, expected non-null TextureMessage.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.dispose was null, expected non-null TextureMessage.'); api.dispose(arg_msg!); return {}; }); @@ -145,18 +145,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setLooping', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.setLooping', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setLooping was null.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.setLooping was null.'); final List args = (message as List?)!; final LoopingMessage? arg_msg = (args[0] as LoopingMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setLooping was null, expected non-null LoopingMessage.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.setLooping was null, expected non-null LoopingMessage.'); api.setLooping(arg_msg!); return {}; }); @@ -164,18 +164,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setVolume', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.setVolume', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setVolume was null.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.setVolume was null.'); final List args = (message as List?)!; final VolumeMessage? arg_msg = (args[0] as VolumeMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setVolume was null, expected non-null VolumeMessage.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.setVolume was null, expected non-null VolumeMessage.'); api.setVolume(arg_msg!); return {}; }); @@ -183,19 +183,20 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.setPlaybackSpeed', + codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed was null.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.setPlaybackSpeed was null.'); final List args = (message as List?)!; final PlaybackSpeedMessage? arg_msg = (args[0] as PlaybackSpeedMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed was null, expected non-null PlaybackSpeedMessage.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.setPlaybackSpeed was null, expected non-null PlaybackSpeedMessage.'); api.setPlaybackSpeed(arg_msg!); return {}; }); @@ -203,18 +204,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.play', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.play', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.play was null.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.play was null.'); final List args = (message as List?)!; final TextureMessage? arg_msg = (args[0] as TextureMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.play was null, expected non-null TextureMessage.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.play was null, expected non-null TextureMessage.'); api.play(arg_msg!); return {}; }); @@ -222,18 +223,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.position', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.position', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.position was null.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.position was null.'); final List args = (message as List?)!; final TextureMessage? arg_msg = (args[0] as TextureMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.position was null, expected non-null TextureMessage.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.position was null, expected non-null TextureMessage.'); final PositionMessage output = api.position(arg_msg!); return {'result': output}; }); @@ -241,18 +242,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.seekTo', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.seekTo', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.seekTo was null.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.seekTo was null.'); final List args = (message as List?)!; final PositionMessage? arg_msg = (args[0] as PositionMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.seekTo was null, expected non-null PositionMessage.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.seekTo was null, expected non-null PositionMessage.'); api.seekTo(arg_msg!); return {}; }); @@ -260,18 +261,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.pause', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.pause', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.pause was null.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.pause was null.'); final List args = (message as List?)!; final TextureMessage? arg_msg = (args[0] as TextureMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.pause was null, expected non-null TextureMessage.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.pause was null, expected non-null TextureMessage.'); api.pause(arg_msg!); return {}; }); @@ -279,19 +280,20 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.setMixWithOthers', + codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers was null.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.setMixWithOthers was null.'); final List args = (message as List?)!; final MixWithOthersMessage? arg_msg = (args[0] as MixWithOthersMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers was null, expected non-null MixWithOthersMessage.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.setMixWithOthers was null, expected non-null MixWithOthersMessage.'); api.setMixWithOthers(arg_msg!); return {}; }); From 5cd65fb4a6265277a83433e7e6511b7d9eac7c36 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 14 Mar 2022 22:00:21 -0400 Subject: [PATCH 291/600] Roll Flutter from 201a64c98f35 to 2f73173c36f8 (3 revisions) (#5047) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index d17adfe06468..46c80834c439 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -201a64c98f350dbc88c6fd3f8a6050864f2259cf +2f73173c36f806c7f30e705a90722082f85a2c8b From 3becd1c22c9c3ac18123421fd16b0067bf95e5b1 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 15 Mar 2022 08:40:16 -0400 Subject: [PATCH 292/600] Roll Flutter from 2f73173c36f8 to 2bd3e0d91485 (16 revisions) (#5055) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 46c80834c439..fe8efe52a540 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2f73173c36f806c7f30e705a90722082f85a2c8b +2bd3e0d914854aa8c12e933f25c5fd8532ae5571 From f62168dd52612649f57de0d3b8bb21e4bbe5a0f3 Mon Sep 17 00:00:00 2001 From: godofredoc Date: Tue, 15 Mar 2022 08:40:17 -0700 Subject: [PATCH 293/600] Update scorecard versions. (#5050) --- .github/workflows/scorecards-analysis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index 5148f2e19028..32c80cd247a2 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -20,12 +20,12 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0 + uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 with: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@b614d455ee90608b5e36e3299cd50d457eb37d5f # v1.0.3 + uses: ossf/scorecard-action@c1aec4ac820532bab364f02a81873c555a0ba3a1 with: results_file: results.sarif results_format: sarif @@ -40,7 +40,7 @@ jobs: # Upload the results as artifacts (optional). - name: "Upload artifact" - uses: actions/upload-artifact@82c141cc518b40d92cc801eee768e7aafc9c2fa2 # v2.3.1 + uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 with: name: SARIF file path: results.sarif @@ -48,6 +48,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@5f532563584d71fdef14ee64d17bafb34f751ce5 # v1.0.26 + uses: github/codeql-action/upload-sarif@f5d822707ee6e8fb81b04a5c0040b736da22e587 with: sarif_file: results.sarif From 6f6cd7826849b88fbfb36b740fa603f36315d452 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Tue, 15 Mar 2022 21:30:08 +0100 Subject: [PATCH 294/600] [in_app_purchase] Adds additional explanation on the importance of completing a purchase (#5017) --- .../in_app_purchase/CHANGELOG.md | 4 ++++ .../in_app_purchase/in_app_purchase/README.md | 20 +++++++++++++------ .../in_app_purchase/pubspec.yaml | 2 +- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md index 134fd9052a63..6253a23f48db 100644 --- a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.0.2 + +* Adds additional explanation on why it is important to complete a purchase. + ## 3.0.1 * Internal code cleanup for stricter analysis options. diff --git a/packages/in_app_purchase/in_app_purchase/README.md b/packages/in_app_purchase/in_app_purchase/README.md index b67f9e5b52ac..dbc8e8a17bc4 100644 --- a/packages/in_app_purchase/in_app_purchase/README.md +++ b/packages/in_app_purchase/in_app_purchase/README.md @@ -32,6 +32,10 @@ your app with each store. Both stores have extensive guides: * [App Store documentation](https://developer.apple.com/in-app-purchase/) * [Google Play documentation](https://developer.android.com/google/play/billing/billing_overview) +> NOTE: Further in this document the App Store and Google Play will be referred +> to as "the store" or "the underlying store", except when a feature is specific +> to a particular store. + For a list of steps for configuring in-app purchases in both stores, see the [example app README](https://github.com/flutter/plugins/blob/master/packages/in_app_purchase/in_app_purchase/example/README.md). @@ -190,11 +194,15 @@ if (_isConsumable(productDetails)) { ### Completing a purchase -The `InAppPurchase.purchaseStream` will send purchase updates after -you initiate the purchase flow using `InAppPurchase.buyConsumable` -or `InAppPurchase.buyNonConsumable`. After delivering the content to -the user, call `InAppPurchase.completePurchase` to tell the App Store -and Google Play that the purchase has been finished. +The `InAppPurchase.purchaseStream` will send purchase updates after initiating +the purchase flow using `InAppPurchase.buyConsumable` or +`InAppPurchase.buyNonConsumable`. After verifying the purchase receipt and the +delivering the content to the user it is important to call +`InAppPurchase.completePurchase` to tell the underlying store that the +purchase has been completed. Calling `InAppPurchase.completePurchase` will +inform the underlying store that the app verified and processed the +purchase and the store can proceed to finalize the transaction and bill +the end user's payment account. > **Warning:** Failure to call `InAppPurchase.completePurchase` and > get a successful response within 3 days of the purchase will result a refund. @@ -229,7 +237,7 @@ InAppPurchase.instance When the price of a subscription is changed the consumer will need to confirm that price change. If the consumer does not confirm the price change the subscription will not be auto-renewed. By default on both iOS and Android the consumer will -automatically get a popup to confirm the price change, but App developers can override this mechanism and show the popup on a later moment so it doesn't interrupt the critical flow of the App. This works different on the Apple App Store and on the Google Play Store. +automatically get a popup to confirm the price change, but App developers can override this mechanism and show the popup on a later moment so it doesn't interrupt the critical flow of the App. This works different for each of the stores. #### Google Play Store (Android) When the subscription price is raised, the consumer should approve the price change within 7 days. The official diff --git a/packages/in_app_purchase/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/pubspec.yaml index 139d58318780..af8f5f3c2773 100644 --- a/packages/in_app_purchase/in_app_purchase/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase description: A Flutter plugin for in-app purchases. Exposes APIs for making in-app purchases through the App Store and Google Play. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 3.0.1 +version: 3.0.2 environment: sdk: ">=2.12.0 <3.0.0" From 43cf7608f867a4f88fdba6925ae2c4197a6fbf7a Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 15 Mar 2022 17:15:07 -0400 Subject: [PATCH 295/600] [flutter_plugin_tools] packages get -> pub get (#5046) --- script/tool/CHANGELOG.md | 4 +++ script/tool/lib/src/analyze_command.dart | 4 +-- script/tool/test/analyze_command_test.dart | 35 ++++++++----------- .../test/build_examples_command_test.dart | 4 +-- 4 files changed, 23 insertions(+), 24 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 35786f4a8b1b..7e950352facb 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +- Switches from deprecated `flutter packages` alias to `flutter pub`. + ## 0.8.1 - Fixes an `analyze` regression in 0.8.0 with packages that have non-`example` diff --git a/script/tool/lib/src/analyze_command.dart b/script/tool/lib/src/analyze_command.dart index 824d766f4ebd..1cd85af076fc 100644 --- a/script/tool/lib/src/analyze_command.dart +++ b/script/tool/lib/src/analyze_command.dart @@ -103,7 +103,7 @@ class AnalyzeCommand extends PackageLoopingCommand { @override Future runForPackage(RepositoryPackage package) async { // Analysis runs over the package and all subpackages, so all of them need - // `flutter packages get` run before analyzing. `example` packages can be + // `flutter pub get` run before analyzing. `example` packages can be // skipped since 'flutter packages get' automatically runs `pub get` in // examples as part of handling the parent directory. final List packagesToGet = [ @@ -116,7 +116,7 @@ class AnalyzeCommand extends PackageLoopingCommand { .pubspecFile .existsSync()) { final int exitCode = await processRunner.runAndStream( - flutterCommand, ['packages', 'get'], + flutterCommand, ['pub', 'get'], workingDir: packageToGet.directory); if (exitCode != 0) { return PackageResult.fail(['Unable to get dependencies']); diff --git a/script/tool/test/analyze_command_test.dart b/script/tool/test/analyze_command_test.dart index 56f4ab9576c6..98fb9382a3aa 100644 --- a/script/tool/test/analyze_command_test.dart +++ b/script/tool/test/analyze_command_test.dart @@ -45,12 +45,10 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - ProcessCall( - 'flutter', const ['packages', 'get'], plugin1Dir.path), + ProcessCall('flutter', const ['pub', 'get'], plugin1Dir.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], plugin1Dir.path), - ProcessCall( - 'flutter', const ['packages', 'get'], plugin2Dir.path), + ProcessCall('flutter', const ['pub', 'get'], plugin2Dir.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], plugin2Dir.path), ])); @@ -64,8 +62,7 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - ProcessCall( - 'flutter', const ['packages', 'get'], plugin1Dir.path), + ProcessCall('flutter', const ['pub', 'get'], plugin1Dir.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], plugin1Dir.path), ])); @@ -85,12 +82,12 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - ProcessCall('flutter', const ['packages', 'get'], - mainPackageDir.path), ProcessCall( - 'flutter', const ['packages', 'get'], subpackage1.path), + 'flutter', const ['pub', 'get'], mainPackageDir.path), ProcessCall( - 'flutter', const ['packages', 'get'], subpackage2.path), + 'flutter', const ['pub', 'get'], subpackage1.path), + ProcessCall( + 'flutter', const ['pub', 'get'], subpackage2.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], mainPackageDir.path), ])); @@ -105,12 +102,10 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - ProcessCall( - 'flutter', const ['packages', 'get'], plugin1Dir.path), + ProcessCall('flutter', const ['pub', 'get'], plugin1Dir.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], plugin1Dir.path), - ProcessCall( - 'flutter', const ['packages', 'get'], plugin2Dir.path), + ProcessCall('flutter', const ['pub', 'get'], plugin2Dir.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], plugin2Dir.path), ])); @@ -127,7 +122,7 @@ void main() { orderedEquals([ ProcessCall( 'flutter', - const ['packages', 'get'], + const ['pub', 'get'], pluginDir.path, ), ProcessCall( @@ -195,7 +190,7 @@ void main() { processRunner.recordedCalls, orderedEquals([ ProcessCall( - 'flutter', const ['packages', 'get'], pluginDir.path), + 'flutter', const ['pub', 'get'], pluginDir.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], pluginDir.path), ])); @@ -214,7 +209,7 @@ void main() { processRunner.recordedCalls, orderedEquals([ ProcessCall( - 'flutter', const ['packages', 'get'], pluginDir.path), + 'flutter', const ['pub', 'get'], pluginDir.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], pluginDir.path), ])); @@ -232,11 +227,11 @@ void main() { }); }); - test('fails if "packages get" fails', () async { + test('fails if "pub get" fails', () async { createFakePlugin('foo', packagesDir); processRunner.mockProcessesForExecutable['flutter'] = [ - MockProcess(exitCode: 1) // flutter packages get + MockProcess(exitCode: 1) // flutter pub get ]; Error? commandError; @@ -304,7 +299,7 @@ void main() { orderedEquals([ ProcessCall( 'flutter', - const ['packages', 'get'], + const ['pub', 'get'], pluginDir.path, ), ProcessCall( diff --git a/script/tool/test/build_examples_command_test.dart b/script/tool/test/build_examples_command_test.dart index 6d8f0b9d6486..29a879071657 100644 --- a/script/tool/test/build_examples_command_test.dart +++ b/script/tool/test/build_examples_command_test.dart @@ -63,7 +63,7 @@ void main() { processRunner .mockProcessesForExecutable[getFlutterCommand(mockPlatform)] = [ - MockProcess(exitCode: 1) // flutter packages get + MockProcess(exitCode: 1) // flutter pub get ]; Error? commandError; @@ -92,7 +92,7 @@ void main() { processRunner .mockProcessesForExecutable[getFlutterCommand(mockPlatform)] = [ - MockProcess(exitCode: 1) // flutter packages get + MockProcess(exitCode: 1) // flutter pub get ]; Error? commandError; From 35ee5989ead0957ec4b26b0aca79bd4f34fd436e Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 15 Mar 2022 17:55:18 -0400 Subject: [PATCH 296/600] Roll Flutter from 2bd3e0d91485 to c9755336785e (2 revisions) (#5059) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index fe8efe52a540..d59ed45b30df 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2bd3e0d914854aa8c12e933f25c5fd8532ae5571 +c9755336785ecf5f8adc7bbdc50420b7ea3b0d81 From 62caf7493c8d61f5662b56d07a01acf7af8b4e02 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 15 Mar 2022 19:00:16 -0400 Subject: [PATCH 297/600] Roll Flutter from c9755336785e to 722cbc52b85b (3 revisions) (#5060) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index d59ed45b30df..9e90835c985e 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c9755336785ecf5f8adc7bbdc50420b7ea3b0d81 +722cbc52b85b7365396aac59e6006d13855c1e60 From f552e59875982f6b9fa0f8d2ee02f6cadf42ce6f Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 15 Mar 2022 21:10:20 -0400 Subject: [PATCH 298/600] Roll Flutter from 722cbc52b85b to fd25dc848324 (3 revisions) (#5062) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 9e90835c985e..7ef2641e3fb8 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -722cbc52b85b7365396aac59e6006d13855c1e60 +fd25dc84832469523768377c826b5549085c9636 From f25e370447f654a4480a8ee84635ab79cad53804 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 16 Mar 2022 01:35:21 -0400 Subject: [PATCH 299/600] Roll Flutter from fd25dc848324 to 509ddfda5c1b (5 revisions) (#5066) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 7ef2641e3fb8..09a4cea57f76 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -fd25dc84832469523768377c826b5549085c9636 +509ddfda5c1bda7c01cf51fe24607da19423d5f9 From 04f64ec89b65f135fc43e8856af95b7d66e3e38a Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Thu, 17 Mar 2022 17:25:29 +0100 Subject: [PATCH 300/600] [in_app_purchase] Implements transaction caching for StoreKit (#4985) --- .../in_app_purchase_storekit/CHANGELOG.md | 4 + .../ios/Runner.xcodeproj/project.pbxproj | 8 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- .../RunnerTests/FIATransactionCacheTests.m | 63 ++++ .../RunnerTests/InAppPurchasePluginTests.m | 355 +++++++++--------- .../ios/RunnerTests/PaymentQueueTests.m | 232 +++++++++++- .../ios/Classes/FIAPaymentQueueHandler.h | 74 +++- .../ios/Classes/FIAPaymentQueueHandler.m | 101 ++++- .../ios/Classes/FIATransactionCache.h | 31 ++ .../ios/Classes/FIATransactionCache.m | 40 ++ .../ios/Classes/InAppPurchasePlugin.m | 3 +- .../in_app_purchase_storekit/pubspec.yaml | 2 +- 12 files changed, 712 insertions(+), 203 deletions(-) create mode 100644 packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/FIATransactionCacheTests.m create mode 100644 packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIATransactionCache.h create mode 100644 packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIATransactionCache.m diff --git a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md index c5df52151411..087c6e3095db 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.0+3 + +* Implements transaction caching for StoreKit ensuring transactions are delivered to the Flutter client. + ## 0.3.0+2 * Internal code cleanup for stricter analysis options. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj index a88050193053..3977d549af12 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ @@ -22,6 +22,7 @@ A5279298219369C600FF69E6 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A5279297219369C600FF69E6 /* StoreKit.framework */; }; A59001A721E69658004A3E5E /* InAppPurchasePluginTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A59001A621E69658004A3E5E /* InAppPurchasePluginTests.m */; }; F67646F82681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F67646F72681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m */; }; + F6995BDD27CF73000050EA78 /* FIATransactionCacheTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F6995BDC27CF73000050EA78 /* FIATransactionCacheTests.m */; }; F78AF3142342BC89008449C7 /* PaymentQueueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F78AF3132342BC89008449C7 /* PaymentQueueTests.m */; }; /* End PBXBuildFile section */ @@ -78,6 +79,7 @@ A59001A821E69658004A3E5E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; E4F9651425A612301059769C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; F67646F72681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIAPPaymentQueueDeleteTests.m; sourceTree = ""; }; + F6995BDC27CF73000050EA78 /* FIATransactionCacheTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIATransactionCacheTests.m; sourceTree = ""; }; F6E5D5F926131C4800C68BED /* Configuration.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = Configuration.storekit; sourceTree = ""; }; F78AF3132342BC89008449C7 /* PaymentQueueTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PaymentQueueTests.m; sourceTree = ""; }; /* End PBXFileReference section */ @@ -190,6 +192,7 @@ F78AF3132342BC89008449C7 /* PaymentQueueTests.m */, 688DE35021F2A5A100EA2684 /* TranslatorTests.m */, F67646F72681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m */, + F6995BDC27CF73000050EA78 /* FIATransactionCacheTests.m */, ); path = RunnerTests; sourceTree = ""; @@ -254,7 +257,7 @@ isa = PBXProject; attributes = { DefaultBuildSystemTypeForWorkspace = Original; - LastUpgradeCheck = 1100; + LastUpgradeCheck = 1300; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -406,6 +409,7 @@ F67646F82681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m in Sources */, 6896B34621E9363700D37AEF /* ProductRequestHandlerTests.m in Sources */, 688DE35121F2A5A100EA2684 /* TranslatorTests.m in Sources */, + F6995BDD27CF73000050EA78 /* FIATransactionCacheTests.m in Sources */, A59001A721E69658004A3E5E /* InAppPurchasePluginTests.m in Sources */, 6896B34C21EEB4B800D37AEF /* Stubs.m in Sources */, ); diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 3bd47ecb9ec0..a8adf88572cd 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ + +@import in_app_purchase_storekit; + +@interface FIATransactionCacheTests : XCTestCase + +@end + +@implementation FIATransactionCacheTests + +- (void)testAddObjectsForNewKey { + NSArray *dummyArray = @[ @1, @2, @3 ]; + FIATransactionCache *cache = [[FIATransactionCache alloc] init]; + [cache addObjects:dummyArray forKey:TransactionCacheKeyUpdatedTransactions]; + + XCTAssertEqual(dummyArray, [cache getObjectsForKey:TransactionCacheKeyUpdatedTransactions]); +} + +- (void)testAddObjectsForExistingKey { + NSArray *dummyArray = @[ @1, @2, @3 ]; + FIATransactionCache *cache = [[FIATransactionCache alloc] init]; + [cache addObjects:dummyArray forKey:TransactionCacheKeyUpdatedTransactions]; + + XCTAssertEqual(dummyArray, [cache getObjectsForKey:TransactionCacheKeyUpdatedTransactions]); + + [cache addObjects:@[ @4, @5, @6 ] forKey:TransactionCacheKeyUpdatedTransactions]; + + NSArray *expected = @[ @1, @2, @3, @4, @5, @6 ]; + XCTAssertEqualObjects(expected, [cache getObjectsForKey:TransactionCacheKeyUpdatedTransactions]); +} + +- (void)testGetObjectsForNonExistingKey { + FIATransactionCache *cache = [[FIATransactionCache alloc] init]; + XCTAssertNil([cache getObjectsForKey:TransactionCacheKeyUpdatedTransactions]); +} + +- (void)testClear { + NSArray *fakeUpdatedTransactions = @[ @1, @2, @3 ]; + NSArray *fakeRemovedTransactions = @[ @"Remove 1", @"Remove 2", @"Remove 3" ]; + NSArray *fakeUpdatedDownloads = @[ @"Download 1", @"Download 2" ]; + FIATransactionCache *cache = [[FIATransactionCache alloc] init]; + [cache addObjects:fakeUpdatedTransactions forKey:TransactionCacheKeyUpdatedTransactions]; + [cache addObjects:fakeRemovedTransactions forKey:TransactionCacheKeyRemovedTransactions]; + [cache addObjects:fakeUpdatedDownloads forKey:TransactionCacheKeyUpdatedDownloads]; + + XCTAssertEqual(fakeUpdatedTransactions, + [cache getObjectsForKey:TransactionCacheKeyUpdatedTransactions]); + XCTAssertEqual(fakeRemovedTransactions, + [cache getObjectsForKey:TransactionCacheKeyRemovedTransactions]); + XCTAssertEqual(fakeUpdatedDownloads, + [cache getObjectsForKey:TransactionCacheKeyUpdatedDownloads]); + + [cache clear]; + + XCTAssertNil([cache getObjectsForKey:TransactionCacheKeyUpdatedTransactions]); + XCTAssertNil([cache getObjectsForKey:TransactionCacheKeyRemovedTransactions]); + XCTAssertNil([cache getObjectsForKey:TransactionCacheKeyUpdatedDownloads]); +} +@end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/InAppPurchasePluginTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/InAppPurchasePluginTests.m index fcb2c9425d69..c89589c6a9e5 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/InAppPurchasePluginTests.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/InAppPurchasePluginTests.m @@ -74,138 +74,84 @@ - (void)testGetProductResponse { XCTAssertTrue([resultArray.firstObject[@"productIdentifier"] isEqualToString:@"123"]); } -- (void)testAddPaymentFailure { +- (void)testAddPaymentShouldReturnFlutterErrorWhenArgumentsAreInvalid { XCTestExpectation *expectation = - [self expectationWithDescription:@"result should return failed state"]; + [self expectationWithDescription: + @"Result should contain a FlutterError when invalid parameters are passed in."]; + NSString *argument = @"Invalid argument"; FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin addPayment:result:]" - arguments:@{ - @"productIdentifier" : @"123", - @"quantity" : @(1), - @"simulatesAskToBuyInSandbox" : @YES, - }]; - SKPaymentQueueStub *queue = [SKPaymentQueueStub new]; - queue.testState = SKPaymentTransactionStateFailed; - __block SKPaymentTransaction *transactionForUpdateBlock; - self.plugin.paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue - transactionsUpdated:^(NSArray *_Nonnull transactions) { - SKPaymentTransaction *transaction = transactions[0]; - if (transaction.transactionState == SKPaymentTransactionStateFailed) { - transactionForUpdateBlock = transaction; - [expectation fulfill]; - } - } - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { - return YES; - } - updatedDownloads:nil]; - [queue addTransactionObserver:self.plugin.paymentQueueHandler]; - + arguments:argument]; [self.plugin handleMethodCall:call - result:^(id r){ + result:^(id _Nullable result) { + FlutterError *error = result; + XCTAssertEqualObjects(@"storekit_invalid_argument", error.code); + XCTAssertEqualObjects(@"Argument type of addPayment is not a Dictionary", + error.message); + XCTAssertEqualObjects(argument, error.details); + [expectation fulfill]; }]; + [self waitForExpectations:@[ expectation ] timeout:5]; - XCTAssertEqual(transactionForUpdateBlock.transactionState, SKPaymentTransactionStateFailed); } -- (void)testAddPaymentSuccessWithoutPaymentDiscount { +- (void)testAddPaymentShouldReturnFlutterErrorWhenPaymentFails { + NSDictionary *arguments = @{ + @"productIdentifier" : @"123", + @"quantity" : @(1), + @"simulatesAskToBuyInSandbox" : @YES, + }; XCTestExpectation *expectation = - [self expectationWithDescription:@"result should return success state"]; + [self expectationWithDescription:@"Result should return failed state."]; FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin addPayment:result:]" - arguments:@{ - @"productIdentifier" : @"123", - @"quantity" : @(1), - @"simulatesAskToBuyInSandbox" : @YES, - }]; - SKPaymentQueueStub *queue = [SKPaymentQueueStub new]; - queue.testState = SKPaymentTransactionStatePurchased; - __block SKPaymentTransaction *transactionForUpdateBlock; - self.plugin.paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue - transactionsUpdated:^(NSArray *_Nonnull transactions) { - SKPaymentTransaction *transaction = transactions[0]; - if (transaction.transactionState == SKPaymentTransactionStatePurchased) { - transactionForUpdateBlock = transaction; - if (@available(iOS 12.2, *)) { - XCTAssertNil(transaction.payment.paymentDiscount); - } - [expectation fulfill]; - } - } - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { - return YES; - } - updatedDownloads:nil]; - [queue addTransactionObserver:self.plugin.paymentQueueHandler]; + arguments:arguments]; + + FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); + OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(NO); + self.plugin.paymentQueueHandler = mockHandler; + [self.plugin handleMethodCall:call - result:^(id r){ + result:^(id _Nullable result) { + FlutterError *error = result; + XCTAssertEqualObjects(@"storekit_duplicate_product_object", error.code); + XCTAssertEqualObjects( + @"There is a pending transaction for the same product identifier. " + @"Please either wait for it to be finished or finish it manually " + @"using `completePurchase` to avoid edge cases.", + error.message); + XCTAssertEqualObjects(arguments, error.details); + [expectation fulfill]; }]; + [self waitForExpectations:@[ expectation ] timeout:5]; - XCTAssertEqual(transactionForUpdateBlock.transactionState, SKPaymentTransactionStatePurchased); + OCMVerify(times(1), [mockHandler addPayment:[OCMArg any]]); } -- (void)testAddPaymentSuccessWithPaymentDiscount { +- (void)testAddPaymentSuccessWithoutPaymentDiscount { XCTestExpectation *expectation = - [self expectationWithDescription:@"result should return success state"]; + [self expectationWithDescription:@"Result should return success state"]; FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin addPayment:result:]" arguments:@{ @"productIdentifier" : @"123", @"quantity" : @(1), @"simulatesAskToBuyInSandbox" : @YES, - @"paymentDiscount" : @{ - @"identifier" : @"test_identifier", - @"keyIdentifier" : @"test_key_identifier", - @"nonce" : @"4a11a9cc-3bc3-11ec-8d3d-0242ac130003", - @"signature" : @"test_signature", - @"timestamp" : @(1635847102), - } }]; - SKPaymentQueueStub *queue = [SKPaymentQueueStub new]; - queue.testState = SKPaymentTransactionStatePurchased; - __block SKPaymentTransaction *transactionForUpdateBlock; - self.plugin.paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue - transactionsUpdated:^(NSArray *_Nonnull transactions) { - SKPaymentTransaction *transaction = transactions[0]; - if (transaction.transactionState == SKPaymentTransactionStatePurchased) { - transactionForUpdateBlock = transaction; - if (@available(iOS 12.2, *)) { - SKPaymentDiscount *paymentDiscount = transaction.payment.paymentDiscount; - XCTAssertEqual(paymentDiscount.identifier, @"test_identifier"); - XCTAssertEqual(paymentDiscount.keyIdentifier, @"test_key_identifier"); - XCTAssertEqualObjects( - paymentDiscount.nonce, - [[NSUUID alloc] initWithUUIDString:@"4a11a9cc-3bc3-11ec-8d3d-0242ac130003"]); - XCTAssertEqual(paymentDiscount.signature, @"test_signature"); - XCTAssertEqual(paymentDiscount.timestamp, @(1635847102)); - } - [expectation fulfill]; - } - } - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { - return YES; - } - updatedDownloads:nil]; - [queue addTransactionObserver:self.plugin.paymentQueueHandler]; + FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); + OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(YES); + self.plugin.paymentQueueHandler = mockHandler; [self.plugin handleMethodCall:call - result:^(id r){ + result:^(id _Nullable result) { + XCTAssertNil(result); + [expectation fulfill]; }]; [self waitForExpectations:@[ expectation ] timeout:5]; - XCTAssertEqual(transactionForUpdateBlock.transactionState, SKPaymentTransactionStatePurchased); } -- (void)testAddPaymentFailureWithInvalidPaymentDiscount { +- (void)testAddPaymentSuccessWithPaymentDiscount { XCTestExpectation *expectation = - [self expectationWithDescription:@"result should return success state"]; + [self expectationWithDescription:@"Result should return success state"]; FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin addPayment:result:]" arguments:@{ @@ -213,6 +159,7 @@ - (void)testAddPaymentFailureWithInvalidPaymentDiscount { @"quantity" : @(1), @"simulatesAskToBuyInSandbox" : @YES, @"paymentDiscount" : @{ + @"identifier" : @"test_identifier", @"keyIdentifier" : @"test_key_identifier", @"nonce" : @"4a11a9cc-3bc3-11ec-8d3d-0242ac130003", @"signature" : @"test_signature", @@ -220,28 +167,85 @@ - (void)testAddPaymentFailureWithInvalidPaymentDiscount { } }]; - [self.plugin - handleMethodCall:call - result:^(id r) { - XCTAssertTrue([r isKindOfClass:FlutterError.class]); - FlutterError *result = r; - XCTAssertEqualObjects(result.code, @"storekit_invalid_payment_discount_object"); - XCTAssertEqualObjects(result.message, - @"You have requested a payment and specified a payment " - @"discount with invalid properties. When specifying a " - @"payment discount the 'identifier' field is mandatory."); - XCTAssertEqualObjects(result.details, call.arguments); - [expectation fulfill]; - }]; - + FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); + OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(YES); + self.plugin.paymentQueueHandler = mockHandler; + [self.plugin handleMethodCall:call + result:^(id _Nullable result) { + XCTAssertNil(result); + [expectation fulfill]; + }]; [self waitForExpectations:@[ expectation ] timeout:5]; + OCMVerify( + times(1), + [mockHandler + addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { + SKPayment *payment = obj; + if (@available(iOS 12.2, *)) { + SKPaymentDiscount *discount = payment.paymentDiscount; + + return [discount.identifier isEqual:@"test_identifier"] && + [discount.keyIdentifier isEqual:@"test_key_identifier"] && + [discount.nonce + isEqual:[[NSUUID alloc] + initWithUUIDString:@"4a11a9cc-3bc3-11ec-8d3d-0242ac130003"]] && + [discount.signature isEqual:@"test_signature"] && + [discount.timestamp isEqual:@(1635847102)]; + } + + return YES; + }]]); +} + +- (void)testAddPaymentFailureWithInvalidPaymentDiscount { + // Support for payment discount is only available on iOS 12.2 and higher. + if (@available(iOS 12.2, *)) { + XCTestExpectation *expectation = + [self expectationWithDescription:@"Result should return success state"]; + NSDictionary *arguments = @{ + @"productIdentifier" : @"123", + @"quantity" : @(1), + @"simulatesAskToBuyInSandbox" : @YES, + @"paymentDiscount" : @{ + @"keyIdentifier" : @"test_key_identifier", + @"nonce" : @"4a11a9cc-3bc3-11ec-8d3d-0242ac130003", + @"signature" : @"test_signature", + @"timestamp" : @(1635847102), + } + }; + FlutterMethodCall *call = + [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin addPayment:result:]" + arguments:arguments]; + + FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); + id translator = OCMClassMock(FIAObjectTranslator.class); + + NSString *error = @"Some error occurred"; + OCMStub(ClassMethod([translator + getSKPaymentDiscountFromMap:[OCMArg any] + withError:(NSString __autoreleasing **)[OCMArg setTo:error]])) + .andReturn(nil); + self.plugin.paymentQueueHandler = mockHandler; + [self.plugin + handleMethodCall:call + result:^(id _Nullable result) { + FlutterError *error = result; + XCTAssertEqualObjects(@"storekit_invalid_payment_discount_object", error.code); + XCTAssertEqualObjects( + @"You have requested a payment and specified a " + @"payment discount with invalid properties. Some error occurred", + error.message); + XCTAssertEqualObjects(arguments, error.details); + [expectation fulfill]; + }]; + [self waitForExpectations:@[ expectation ] timeout:5]; + OCMVerify(never(), [mockHandler addPayment:[OCMArg any]]); + } } - (void)testAddPaymentWithNullSandboxArgument { XCTestExpectation *expectation = [self expectationWithDescription:@"result should return success state"]; - XCTestExpectation *simulatesAskToBuyInSandboxExpectation = - [self expectationWithDescription:@"payment isn't simulatesAskToBuyInSandbox"]; FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin addPayment:result:]" arguments:@{ @@ -249,33 +253,19 @@ - (void)testAddPaymentWithNullSandboxArgument { @"quantity" : @(1), @"simulatesAskToBuyInSandbox" : [NSNull null], }]; - SKPaymentQueueStub *queue = [SKPaymentQueueStub new]; - queue.testState = SKPaymentTransactionStatePurchased; - __block SKPaymentTransaction *transactionForUpdateBlock; - self.plugin.paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue - transactionsUpdated:^(NSArray *_Nonnull transactions) { - SKPaymentTransaction *transaction = transactions[0]; - if (transaction.transactionState == SKPaymentTransactionStatePurchased) { - transactionForUpdateBlock = transaction; - [expectation fulfill]; - } - if (!transaction.payment.simulatesAskToBuyInSandbox) { - [simulatesAskToBuyInSandboxExpectation fulfill]; - } - } - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { - return YES; - } - updatedDownloads:nil]; - [queue addTransactionObserver:self.plugin.paymentQueueHandler]; + FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); + OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(YES); + self.plugin.paymentQueueHandler = mockHandler; [self.plugin handleMethodCall:call - result:^(id r){ + result:^(id _Nullable result) { + XCTAssertNil(result); + [expectation fulfill]; }]; - [self waitForExpectations:@[ expectation, simulatesAskToBuyInSandboxExpectation ] timeout:5]; - XCTAssertEqual(transactionForUpdateBlock.transactionState, SKPaymentTransactionStatePurchased); + [self waitForExpectations:@[ expectation ] timeout:5]; + OCMVerify(times(1), [mockHandler addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { + SKPayment *payment = obj; + return !payment.simulatesAskToBuyInSandbox; + }]]); } - (void)testRestoreTransactions { @@ -297,7 +287,8 @@ - (void)testRestoreTransactions { [expectation fulfill]; } shouldAddStorePayment:nil - updatedDownloads:nil]; + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; [queue addTransactionObserver:self.plugin.paymentQueueHandler]; [self.plugin handleMethodCall:call result:^(id r){ @@ -393,13 +384,15 @@ - (void)testGetPendingTransactions { initWithMap:transactionMap] ]); __block NSArray *resultArray; - self.plugin.paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue - transactionsUpdated:nil - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:nil - updatedDownloads:nil]; + self.plugin.paymentQueueHandler = + [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue + transactionsUpdated:nil + transactionRemoved:nil + restoreTransactionFailed:nil + restoreCompletedTransactionsFinished:nil + shouldAddStorePayment:nil + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; [self.plugin handleMethodCall:call result:^(id r) { resultArray = r; @@ -409,46 +402,40 @@ - (void)testGetPendingTransactions { XCTAssertEqualObjects(resultArray, @[ transactionMap ]); } -- (void)testStartAndStopObservingPaymentQueue { +- (void)testStartObservingPaymentQueue { + XCTestExpectation *expectation = + [self expectationWithDescription:@"Should return success result"]; FlutterMethodCall *startCall = [FlutterMethodCall methodCallWithMethodName:@"-[SKPaymentQueue startObservingTransactionQueue]" arguments:nil]; - FlutterMethodCall *stopCall = - [FlutterMethodCall methodCallWithMethodName:@"-[SKPaymentQueue stopObservingTransactionQueue]" - arguments:nil]; - - SKPaymentQueueStub *queue = [SKPaymentQueueStub new]; - - self.plugin.paymentQueueHandler = - [[FIAPaymentQueueHandler alloc] initWithQueue:queue - transactionsUpdated:nil - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, - SKProduct *_Nonnull product) { - return YES; - } - updatedDownloads:nil]; - - // Check that there is no observer to start with. - XCTAssertNil(queue.observer); - - // Start observing + FIAPaymentQueueHandler *mockHandler = OCMClassMock([FIAPaymentQueueHandler class]); + self.plugin.paymentQueueHandler = mockHandler; [self.plugin handleMethodCall:startCall - result:^(id r){ + result:^(id _Nullable result) { + XCTAssertNil(result); + [expectation fulfill]; }]; - // Observer should be set - XCTAssertNotNil(queue.observer); + [self waitForExpectations:@[ expectation ] timeout:5]; + OCMVerify(times(1), [mockHandler startObservingPaymentQueue]); +} - // Stop observing +- (void)testStopObservingPaymentQueue { + XCTestExpectation *expectation = + [self expectationWithDescription:@"Should return success result"]; + FlutterMethodCall *stopCall = + [FlutterMethodCall methodCallWithMethodName:@"-[SKPaymentQueue stopObservingTransactionQueue]" + arguments:nil]; + FIAPaymentQueueHandler *mockHandler = OCMClassMock([FIAPaymentQueueHandler class]); + self.plugin.paymentQueueHandler = mockHandler; [self.plugin handleMethodCall:stopCall - result:^(id r){ + result:^(id _Nullable result) { + XCTAssertNil(result); + [expectation fulfill]; }]; - // No observer should be set - XCTAssertNil(queue.observer); + [self waitForExpectations:@[ expectation ] timeout:5]; + OCMVerify(times(1), [mockHandler stopObservingPaymentQueue]); } - (void)testRegisterPaymentQueueDelegate { @@ -464,7 +451,8 @@ - (void)testRegisterPaymentQueueDelegate { restoreTransactionFailed:nil restoreCompletedTransactionsFinished:nil shouldAddStorePayment:nil - updatedDownloads:nil]; + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; // Verify the delegate is nil before we register one. XCTAssertNil(self.plugin.paymentQueueHandler.delegate); @@ -491,7 +479,8 @@ - (void)testRemovePaymentQueueDelegate { restoreTransactionFailed:nil restoreCompletedTransactionsFinished:nil shouldAddStorePayment:nil - updatedDownloads:nil]; + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; self.plugin.paymentQueueHandler.delegate = OCMProtocolMock(@protocol(SKPaymentQueueDelegate)); // Verify the delegate is not nil before removing it. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/PaymentQueueTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/PaymentQueueTests.m index e267be1da54b..2f8d5857c8d8 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/PaymentQueueTests.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/PaymentQueueTests.m @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#import #import #import "Stubs.h" @@ -59,10 +60,11 @@ - (void)testTransactionPurchased { shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { return YES; } - updatedDownloads:nil]; - [queue addTransactionObserver:handler]; + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; SKPayment *payment = [SKPayment paymentWithProduct:[[SKProductStub alloc] initWithMap:self.productResponseMap]]; + [handler startObservingPaymentQueue]; [handler addPayment:payment]; [self waitForExpectations:@[ expectation ] timeout:5]; XCTAssertEqual(tran.transactionState, SKPaymentTransactionStatePurchased); @@ -87,10 +89,12 @@ - (void)testTransactionFailed { shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { return YES; } - updatedDownloads:nil]; - [queue addTransactionObserver:handler]; + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; + SKPayment *payment = [SKPayment paymentWithProduct:[[SKProductStub alloc] initWithMap:self.productResponseMap]]; + [handler startObservingPaymentQueue]; [handler addPayment:payment]; [self waitForExpectations:@[ expectation ] timeout:5]; XCTAssertEqual(tran.transactionState, SKPaymentTransactionStateFailed); @@ -115,10 +119,12 @@ - (void)testTransactionRestored { shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { return YES; } - updatedDownloads:nil]; - [queue addTransactionObserver:handler]; + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; + SKPayment *payment = [SKPayment paymentWithProduct:[[SKProductStub alloc] initWithMap:self.productResponseMap]]; + [handler startObservingPaymentQueue]; [handler addPayment:payment]; [self waitForExpectations:@[ expectation ] timeout:5]; XCTAssertEqual(tran.transactionState, SKPaymentTransactionStateRestored); @@ -143,10 +149,12 @@ - (void)testTransactionPurchasing { shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { return YES; } - updatedDownloads:nil]; - [queue addTransactionObserver:handler]; + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; + SKPayment *payment = [SKPayment paymentWithProduct:[[SKProductStub alloc] initWithMap:self.productResponseMap]]; + [handler startObservingPaymentQueue]; [handler addPayment:payment]; [self waitForExpectations:@[ expectation ] timeout:5]; XCTAssertEqual(tran.transactionState, SKPaymentTransactionStatePurchasing); @@ -171,10 +179,11 @@ - (void)testTransactionDeferred { shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { return YES; } - updatedDownloads:nil]; - [queue addTransactionObserver:handler]; + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; SKPayment *payment = [SKPayment paymentWithProduct:[[SKProductStub alloc] initWithMap:self.productResponseMap]]; + [handler startObservingPaymentQueue]; [handler addPayment:payment]; [self waitForExpectations:@[ expectation ] timeout:5]; XCTAssertEqual(tran.transactionState, SKPaymentTransactionStateDeferred); @@ -201,12 +210,211 @@ - (void)testFinishTransaction { shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { return YES; } - updatedDownloads:nil]; - [queue addTransactionObserver:handler]; + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; SKPayment *payment = [SKPayment paymentWithProduct:[[SKProductStub alloc] initWithMap:self.productResponseMap]]; + [handler startObservingPaymentQueue]; [handler addPayment:payment]; [self waitForExpectations:@[ expectation ] timeout:5]; } +- (void)testStartObservingPaymentQueueShouldNotProcessTransactionsWhenCacheIsEmpty { + FIATransactionCache *mockCache = OCMClassMock(FIATransactionCache.class); + FIAPaymentQueueHandler *handler = + [[FIAPaymentQueueHandler alloc] initWithQueue:[[SKPaymentQueueStub alloc] init] + transactionsUpdated:^(NSArray *_Nonnull transactions) { + XCTFail("transactionsUpdated callback should not be called when cache is empty."); + } + transactionRemoved:^(NSArray *_Nonnull transactions) { + XCTFail("transactionRemoved callback should not be called when cache is empty."); + } + restoreTransactionFailed:nil + restoreCompletedTransactionsFinished:nil + shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { + return YES; + } + updatedDownloads:^(NSArray *_Nonnull downloads) { + XCTFail("updatedDownloads callback should not be called when cache is empty."); + } + transactionCache:mockCache]; + + [handler startObservingPaymentQueue]; + + OCMVerify(times(1), [mockCache getObjectsForKey:TransactionCacheKeyUpdatedTransactions]); + OCMVerify(times(1), [mockCache getObjectsForKey:TransactionCacheKeyUpdatedDownloads]); + OCMVerify(times(1), [mockCache getObjectsForKey:TransactionCacheKeyRemovedTransactions]); +} + +- (void) + testStartObservingPaymentQueueShouldNotProcessTransactionsWhenCacheContainsEmptyTransactionArrays { + FIATransactionCache *mockCache = OCMClassMock(FIATransactionCache.class); + FIAPaymentQueueHandler *handler = + [[FIAPaymentQueueHandler alloc] initWithQueue:[[SKPaymentQueueStub alloc] init] + transactionsUpdated:^(NSArray *_Nonnull transactions) { + XCTFail("transactionsUpdated callback should not be called when cache is empty."); + } + transactionRemoved:^(NSArray *_Nonnull transactions) { + XCTFail("transactionRemoved callback should not be called when cache is empty."); + } + restoreTransactionFailed:nil + restoreCompletedTransactionsFinished:nil + shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { + return YES; + } + updatedDownloads:^(NSArray *_Nonnull downloads) { + XCTFail("updatedDownloads callback should not be called when cache is empty."); + } + transactionCache:mockCache]; + + OCMStub([mockCache getObjectsForKey:TransactionCacheKeyUpdatedTransactions]).andReturn(@[]); + OCMStub([mockCache getObjectsForKey:TransactionCacheKeyUpdatedDownloads]).andReturn(@[]); + OCMStub([mockCache getObjectsForKey:TransactionCacheKeyRemovedTransactions]).andReturn(@[]); + + [handler startObservingPaymentQueue]; + + OCMVerify(times(1), [mockCache getObjectsForKey:TransactionCacheKeyUpdatedTransactions]); + OCMVerify(times(1), [mockCache getObjectsForKey:TransactionCacheKeyUpdatedDownloads]); + OCMVerify(times(1), [mockCache getObjectsForKey:TransactionCacheKeyRemovedTransactions]); +} + +- (void)testStartObservingPaymentQueueShouldProcessTransactionsForItemsInCache { + XCTestExpectation *updateTransactionsExpectation = + [self expectationWithDescription: + @"transactionsUpdated callback should be called with one transaction."]; + XCTestExpectation *removeTransactionsExpectation = + [self expectationWithDescription: + @"transactionsRemoved callback should be called with one transaction."]; + XCTestExpectation *updateDownloadsExpectation = + [self expectationWithDescription: + @"downloadsUpdated callback should be called with one transaction."]; + SKPaymentTransaction *mockTransaction = OCMClassMock(SKPaymentTransaction.class); + SKDownload *mockDownload = OCMClassMock(SKDownload.class); + FIATransactionCache *mockCache = OCMClassMock(FIATransactionCache.class); + FIAPaymentQueueHandler *handler = + [[FIAPaymentQueueHandler alloc] initWithQueue:[[SKPaymentQueueStub alloc] init] + transactionsUpdated:^(NSArray *_Nonnull transactions) { + XCTAssertEqualObjects(transactions, @[ mockTransaction ]); + [updateTransactionsExpectation fulfill]; + } + transactionRemoved:^(NSArray *_Nonnull transactions) { + XCTAssertEqualObjects(transactions, @[ mockTransaction ]); + [removeTransactionsExpectation fulfill]; + } + restoreTransactionFailed:nil + restoreCompletedTransactionsFinished:nil + shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { + return YES; + } + updatedDownloads:^(NSArray *_Nonnull downloads) { + XCTAssertEqualObjects(downloads, @[ mockDownload ]); + [updateDownloadsExpectation fulfill]; + } + transactionCache:mockCache]; + + OCMStub([mockCache getObjectsForKey:TransactionCacheKeyUpdatedTransactions]).andReturn(@[ + mockTransaction + ]); + OCMStub([mockCache getObjectsForKey:TransactionCacheKeyUpdatedDownloads]).andReturn(@[ + mockDownload + ]); + OCMStub([mockCache getObjectsForKey:TransactionCacheKeyRemovedTransactions]).andReturn(@[ + mockTransaction + ]); + + [handler startObservingPaymentQueue]; + + [self waitForExpectations:@[ + updateTransactionsExpectation, removeTransactionsExpectation, updateDownloadsExpectation + ] + timeout:5]; + OCMVerify(times(1), [mockCache getObjectsForKey:TransactionCacheKeyUpdatedTransactions]); + OCMVerify(times(1), [mockCache getObjectsForKey:TransactionCacheKeyUpdatedDownloads]); + OCMVerify(times(1), [mockCache getObjectsForKey:TransactionCacheKeyRemovedTransactions]); + OCMVerify(times(1), [mockCache clear]); +} + +- (void)testTransactionsShouldBeCachedWhenNotObserving { + SKPaymentQueueStub *queue = [[SKPaymentQueueStub alloc] init]; + FIATransactionCache *mockCache = OCMClassMock(FIATransactionCache.class); + FIAPaymentQueueHandler *handler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue + transactionsUpdated:^(NSArray *_Nonnull transactions) { + XCTFail("transactionsUpdated callback should not be called when cache is empty."); + } + transactionRemoved:^(NSArray *_Nonnull transactions) { + XCTFail("transactionRemoved callback should not be called when cache is empty."); + } + restoreTransactionFailed:nil + restoreCompletedTransactionsFinished:nil + shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { + return YES; + } + updatedDownloads:^(NSArray *_Nonnull downloads) { + XCTFail("updatedDownloads callback should not be called when cache is empty."); + } + transactionCache:mockCache]; + + SKPayment *payment = + [SKPayment paymentWithProduct:[[SKProductStub alloc] initWithMap:self.productResponseMap]]; + [handler addPayment:payment]; + + OCMVerify(times(1), [mockCache addObjects:[OCMArg any] + forKey:TransactionCacheKeyUpdatedTransactions]); + OCMVerify(never(), [mockCache addObjects:[OCMArg any] + forKey:TransactionCacheKeyUpdatedDownloads]); + OCMVerify(never(), [mockCache addObjects:[OCMArg any] + forKey:TransactionCacheKeyRemovedTransactions]); +} + +- (void)testTransactionsShouldNotBeCachedWhenObserving { + XCTestExpectation *updateTransactionsExpectation = + [self expectationWithDescription: + @"transactionsUpdated callback should be called with one transaction."]; + XCTestExpectation *removeTransactionsExpectation = + [self expectationWithDescription: + @"transactionsRemoved callback should be called with one transaction."]; + XCTestExpectation *updateDownloadsExpectation = + [self expectationWithDescription: + @"downloadsUpdated callback should be called with one transaction."]; + SKPaymentTransaction *mockTransaction = OCMClassMock(SKPaymentTransaction.class); + SKDownload *mockDownload = OCMClassMock(SKDownload.class); + SKPaymentQueueStub *queue = [[SKPaymentQueueStub alloc] init]; + queue.testState = SKPaymentTransactionStatePurchased; + FIATransactionCache *mockCache = OCMClassMock(FIATransactionCache.class); + FIAPaymentQueueHandler *handler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue + transactionsUpdated:^(NSArray *_Nonnull transactions) { + XCTAssertEqualObjects(transactions, @[ mockTransaction ]); + [updateTransactionsExpectation fulfill]; + } + transactionRemoved:^(NSArray *_Nonnull transactions) { + XCTAssertEqualObjects(transactions, @[ mockTransaction ]); + [removeTransactionsExpectation fulfill]; + } + restoreTransactionFailed:nil + restoreCompletedTransactionsFinished:nil + shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { + return YES; + } + updatedDownloads:^(NSArray *_Nonnull downloads) { + XCTAssertEqualObjects(downloads, @[ mockDownload ]); + [updateDownloadsExpectation fulfill]; + } + transactionCache:mockCache]; + + [handler startObservingPaymentQueue]; + [handler paymentQueue:queue updatedTransactions:@[ mockTransaction ]]; + [handler paymentQueue:queue removedTransactions:@[ mockTransaction ]]; + [handler paymentQueue:queue updatedDownloads:@[ mockDownload ]]; + + [self waitForExpectations:@[ + updateTransactionsExpectation, removeTransactionsExpectation, updateDownloadsExpectation + ] + timeout:5]; + OCMVerify(never(), [mockCache addObjects:[OCMArg any] + forKey:TransactionCacheKeyUpdatedTransactions]); + OCMVerify(never(), [mockCache addObjects:[OCMArg any] + forKey:TransactionCacheKeyUpdatedDownloads]); + OCMVerify(never(), [mockCache addObjects:[OCMArg any] + forKey:TransactionCacheKeyRemovedTransactions]); +} @end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAPaymentQueueHandler.h b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAPaymentQueueHandler.h index 8019831d6355..bb074aa6c577 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAPaymentQueueHandler.h +++ b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAPaymentQueueHandler.h @@ -4,6 +4,7 @@ #import #import +#import "FIATransactionCache.h" @class SKPaymentTransaction; @@ -21,6 +22,27 @@ typedef void (^UpdatedDownloads)(NSArray *downloads); @property(NS_NONATOMIC_IOSONLY, weak, nullable) id delegate API_AVAILABLE( ios(13.0), macos(10.15), watchos(6.2)); +/// Creates a new FIAPaymentQueueHandler initialized with an empty +/// FIATransactionCache. +/// +/// @param queue The SKPaymentQueue instance connected to the App Store and +/// responsible for processing transactions. +/// @param transactionsUpdated Callback method that is called each time the App +/// Store indicates transactions are updated. +/// @param transactionsRemoved Callback method that is called each time the App +/// Store indicates transactions are removed. +/// @param restoreTransactionFailed Callback method that is called each time +/// the App Store indicates transactions failed +/// to restore. +/// @param restoreCompletedTransactionsFinished Callback method that is called +/// each time the App Store +/// indicates restoring of +/// transactions has finished. +/// @param shouldAddStorePayment Callback method that is called each time an +/// in-app purchase has been initiated from the +/// App Store. +/// @param updatedDownloads Callback method that is called each time the App +/// Store indicates downloads are updated. - (instancetype)initWithQueue:(nonnull SKPaymentQueue *)queue transactionsUpdated:(nullable TransactionsUpdated)transactionsUpdated transactionRemoved:(nullable TransactionsRemoved)transactionsRemoved @@ -28,7 +50,57 @@ typedef void (^UpdatedDownloads)(NSArray *downloads); restoreCompletedTransactionsFinished: (nullable RestoreCompletedTransactionsFinished)restoreCompletedTransactionsFinished shouldAddStorePayment:(nullable ShouldAddStorePayment)shouldAddStorePayment - updatedDownloads:(nullable UpdatedDownloads)updatedDownloads; + updatedDownloads:(nullable UpdatedDownloads)updatedDownloads + DEPRECATED_MSG_ATTRIBUTE( + "Use the " + "'initWithQueue:transactionsUpdated:transactionsRemoved:restoreTransactionsFinished:" + "shouldAddStorePayment:updatedDownloads:transactionCache:' message instead."); + +/// Creates a new FIAPaymentQueueHandler. +/// +/// The "transactionsUpdated", "transactionsRemoved" and "updatedDownloads" +/// callbacks are only called while actively observing transactions. To start +/// observing transactions send the "startObservingPaymentQueue" message. +/// Sending the "stopObservingPaymentQueue" message will stop actively +/// observing transactions. When transactions are not observed they are cached +/// to the "transactionCache" and will be delivered via the +/// "transactionsUpdated", "transactionsRemoved" and "updatedDownloads" +/// callbacks as soon as the "startObservingPaymentQueue" message arrives. +/// +/// Note: cached transactions that are not processed when the application is +/// killed will be delivered again by the App Store as soon as the application +/// starts again. +/// +/// @param queue The SKPaymentQueue instance connected to the App Store and +/// responsible for processing transactions. +/// @param transactionsUpdated Callback method that is called each time the App +/// Store indicates transactions are updated. +/// @param transactionsRemoved Callback method that is called each time the App +/// Store indicates transactions are removed. +/// @param restoreTransactionFailed Callback method that is called each time +/// the App Store indicates transactions failed +/// to restore. +/// @param restoreCompletedTransactionsFinished Callback method that is called +/// each time the App Store +/// indicates restoring of +/// transactions has finished. +/// @param shouldAddStorePayment Callback method that is called each time an +/// in-app purchase has been initiated from the +/// App Store. +/// @param updatedDownloads Callback method that is called each time the App +/// Store indicates downloads are updated. +/// @param transactionCache An empty [FIATransactionCache] instance that is +/// responsible for keeping track of transactions that +/// arrive when not actively observing transactions. +- (instancetype)initWithQueue:(nonnull SKPaymentQueue *)queue + transactionsUpdated:(nullable TransactionsUpdated)transactionsUpdated + transactionRemoved:(nullable TransactionsRemoved)transactionsRemoved + restoreTransactionFailed:(nullable RestoreTransactionFailed)restoreTransactionFailed + restoreCompletedTransactionsFinished: + (nullable RestoreCompletedTransactionsFinished)restoreCompletedTransactionsFinished + shouldAddStorePayment:(nullable ShouldAddStorePayment)shouldAddStorePayment + updatedDownloads:(nullable UpdatedDownloads)updatedDownloads + transactionCache:(nonnull FIATransactionCache *)transactionCache; // Can throw exceptions if the transaction type is purchasing, should always used in a @try block. - (void)finishTransaction:(nonnull SKPaymentTransaction *)transaction; - (void)restoreTransactions:(nullable NSString *)applicationName; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAPaymentQueueHandler.m b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAPaymentQueueHandler.m index 21667954cf8d..59fdceded2bc 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAPaymentQueueHandler.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAPaymentQueueHandler.m @@ -4,18 +4,49 @@ #import "FIAPaymentQueueHandler.h" #import "FIAPPaymentQueueDelegate.h" +#import "FIATransactionCache.h" @interface FIAPaymentQueueHandler () +/// The SKPaymentQueue instance connected to the App Store and responsible for processing +/// transactions. @property(strong, nonatomic) SKPaymentQueue *queue; + +/// Callback method that is called each time the App Store indicates transactions are updated. @property(nullable, copy, nonatomic) TransactionsUpdated transactionsUpdated; + +/// Callback method that is called each time the App Store indicates transactions are removed. @property(nullable, copy, nonatomic) TransactionsRemoved transactionsRemoved; + +/// Callback method that is called each time the App Store indicates transactions failed to restore. @property(nullable, copy, nonatomic) RestoreTransactionFailed restoreTransactionFailed; + +/// Callback method that is called each time the App Store indicates restoring of transactions has +/// finished. @property(nullable, copy, nonatomic) RestoreCompletedTransactionsFinished paymentQueueRestoreCompletedTransactionsFinished; + +/// Callback method that is called each time an in-app purchase has been initiated from the App +/// Store. @property(nullable, copy, nonatomic) ShouldAddStorePayment shouldAddStorePayment; + +/// Callback method that is called each time the App Store indicates downloads are updated. @property(nullable, copy, nonatomic) UpdatedDownloads updatedDownloads; +/// The transaction cache responsible for caching transactions. +/// +/// Keeps track of transactions that arrive when the Flutter client is not +/// actively observing for transactions. +@property(strong, nonatomic, nonnull) FIATransactionCache *transactionCache; + +/// Indicates if the Flutter client is observing transactions. +/// +/// When the client is not observing, transactions are cached and send to the +/// client as soon as it starts observing. The Flutter client can start +/// observing by sending a startObservingPaymentQueue message and stop by +/// sending a stopObservingPaymentQueue message. +@property(atomic, assign, readwrite, getter=isObservingTransactions) BOOL observingTransactions; + @end @implementation FIAPaymentQueueHandler @@ -28,6 +59,25 @@ - (instancetype)initWithQueue:(nonnull SKPaymentQueue *)queue (nullable RestoreCompletedTransactionsFinished)restoreCompletedTransactionsFinished shouldAddStorePayment:(nullable ShouldAddStorePayment)shouldAddStorePayment updatedDownloads:(nullable UpdatedDownloads)updatedDownloads { + return [[FIAPaymentQueueHandler alloc] initWithQueue:queue + transactionsUpdated:transactionsUpdated + transactionRemoved:transactionsRemoved + restoreTransactionFailed:restoreTransactionFailed + restoreCompletedTransactionsFinished:restoreCompletedTransactionsFinished + shouldAddStorePayment:shouldAddStorePayment + updatedDownloads:updatedDownloads + transactionCache:[[FIATransactionCache alloc] init]]; +} + +- (instancetype)initWithQueue:(nonnull SKPaymentQueue *)queue + transactionsUpdated:(nullable TransactionsUpdated)transactionsUpdated + transactionRemoved:(nullable TransactionsRemoved)transactionsRemoved + restoreTransactionFailed:(nullable RestoreTransactionFailed)restoreTransactionFailed + restoreCompletedTransactionsFinished: + (nullable RestoreCompletedTransactionsFinished)restoreCompletedTransactionsFinished + shouldAddStorePayment:(nullable ShouldAddStorePayment)shouldAddStorePayment + updatedDownloads:(nullable UpdatedDownloads)updatedDownloads + transactionCache:(nonnull FIATransactionCache *)transactionCache { self = [super init]; if (self) { _queue = queue; @@ -37,7 +87,9 @@ - (instancetype)initWithQueue:(nonnull SKPaymentQueue *)queue _paymentQueueRestoreCompletedTransactionsFinished = restoreCompletedTransactionsFinished; _shouldAddStorePayment = shouldAddStorePayment; _updatedDownloads = updatedDownloads; + _transactionCache = transactionCache; + [_queue addTransactionObserver:self]; if (@available(iOS 13.0, macOS 10.15, *)) { queue.delegate = self.delegate; } @@ -46,11 +98,43 @@ - (instancetype)initWithQueue:(nonnull SKPaymentQueue *)queue } - (void)startObservingPaymentQueue { - [_queue addTransactionObserver:self]; + self.observingTransactions = YES; + + [self processCachedTransactions]; } - (void)stopObservingPaymentQueue { - [_queue removeTransactionObserver:self]; + // When the client stops observing transaction, the transaction observer is + // not removed from the SKPaymentQueue. The FIAPaymentQueueHandler will cache + // trasnactions in memory when the client is not observing, allowing the app + // to process these transactions if it starts observing again during the same + // lifetime of the app. + // + // If the app is killed, cached transactions will be removed from memory; + // however, the App Store will re-deliver the transactions as soon as the app + // is started again, since the cached transactions have not been acknowledged + // by the client (by sending the `finishTransaction` message). + self.observingTransactions = NO; +} + +- (void)processCachedTransactions { + NSArray *cachedObjects = + [self.transactionCache getObjectsForKey:TransactionCacheKeyUpdatedTransactions]; + if (cachedObjects.count != 0) { + self.transactionsUpdated(cachedObjects); + } + + cachedObjects = [self.transactionCache getObjectsForKey:TransactionCacheKeyUpdatedDownloads]; + if (cachedObjects.count != 0) { + self.updatedDownloads(cachedObjects); + } + + cachedObjects = [self.transactionCache getObjectsForKey:TransactionCacheKeyRemovedTransactions]; + if (cachedObjects.count != 0) { + self.transactionsRemoved(cachedObjects); + } + + [self.transactionCache clear]; } - (BOOL)addPayment:(SKPayment *)payment { @@ -93,6 +177,11 @@ - (void)showPriceConsentIfNeeded { // state of transactions and finish as appropriate. - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { + if (!self.observingTransactions) { + [_transactionCache addObjects:transactions forKey:TransactionCacheKeyUpdatedTransactions]; + return; + } + // notify dart through callbacks. self.transactionsUpdated(transactions); } @@ -100,6 +189,10 @@ - (void)paymentQueue:(SKPaymentQueue *)queue // Sent when transactions are removed from the queue (via finishTransaction:). - (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions { + if (!self.observingTransactions) { + [_transactionCache addObjects:transactions forKey:TransactionCacheKeyRemovedTransactions]; + return; + } self.transactionsRemoved(transactions); } @@ -118,6 +211,10 @@ - (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue // Sent when the download state has changed. - (void)paymentQueue:(SKPaymentQueue *)queue updatedDownloads:(NSArray *)downloads { + if (!self.observingTransactions) { + [_transactionCache addObjects:downloads forKey:TransactionCacheKeyUpdatedDownloads]; + return; + } self.updatedDownloads(downloads); } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIATransactionCache.h b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIATransactionCache.h new file mode 100644 index 000000000000..dea3c2d85d14 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIATransactionCache.h @@ -0,0 +1,31 @@ +// 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. + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSUInteger, TransactionCacheKey) { + TransactionCacheKeyUpdatedDownloads, + TransactionCacheKeyUpdatedTransactions, + TransactionCacheKeyRemovedTransactions +}; + +@interface FIATransactionCache : NSObject + +/// Adds objects to the transaction cache. +/// +/// If the cache already contains an array of objects on the specified key, the supplied +/// array will be appended to the existing array. +- (void)addObjects:(NSArray *)objects forKey:(TransactionCacheKey)key; + +/// Gets the array of objects stored at the given key. +/// +/// If there are no objects associated with the given key nil is returned. +- (NSArray *)getObjectsForKey:(TransactionCacheKey)key; + +/// Removes all objects from the transaction cache. +- (void)clear; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIATransactionCache.m b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIATransactionCache.m new file mode 100644 index 000000000000..f80b9c40c7bc --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIATransactionCache.m @@ -0,0 +1,40 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FIATransactionCache.h" + +@interface FIATransactionCache () + +/// A NSMutableDictionary storing the objects that are cached. +@property(nonatomic, strong, nonnull) NSMutableDictionary *cache; + +@end + +@implementation FIATransactionCache + +- (instancetype)init { + self = [super init]; + if (self) { + self.cache = [[NSMutableDictionary alloc] init]; + } + + return self; +} + +- (void)addObjects:(NSArray *)objects forKey:(TransactionCacheKey)key { + NSArray *cachedObjects = self.cache[@(key)]; + + self.cache[@(key)] = + cachedObjects ? [cachedObjects arrayByAddingObjectsFromArray:objects] : objects; +} + +- (NSArray *)getObjectsForKey:(TransactionCacheKey)key { + return self.cache[@(key)]; +} + +- (void)clear { + [self.cache removeAllObjects]; +} + +@end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/InAppPurchasePlugin.m b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/InAppPurchasePlugin.m index 661f57f432d8..a580a46b011d 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/InAppPurchasePlugin.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/InAppPurchasePlugin.m @@ -79,7 +79,8 @@ - (instancetype)initWithRegistrar:(NSObject *)registrar } updatedDownloads:^void(NSArray *_Nonnull downloads) { [weakSelf updatedDownloads:downloads]; - }]; + } + transactionCache:[[FIATransactionCache alloc] init]]; _transactionObserverCallbackChannel = [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/in_app_purchase" diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml index 7e701686e00d..51b5ce7f1614 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_storekit description: An implementation for the iOS platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase_storekit issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.3.0+2 +version: 0.3.0+3 environment: sdk: ">=2.14.0 <3.0.0" From fef72e08f12e43a3ac1a7f90eea142bd683fe2c7 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 18 Mar 2022 11:10:25 -0400 Subject: [PATCH 301/600] [flutter_plugin_tool] Add custom-test command (#5058) --- script/tool/CHANGELOG.md | 3 +- script/tool/lib/src/custom_test_command.dart | 77 +++++ script/tool/lib/src/main.dart | 2 + script/tool/pubspec.yaml | 2 +- .../tool/test/custom_test_command_test.dart | 281 ++++++++++++++++++ 5 files changed, 363 insertions(+), 2 deletions(-) create mode 100644 script/tool/lib/src/custom_test_command.dart create mode 100644 script/tool/test/custom_test_command_test.dart diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 7e950352facb..214eb68b2f21 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.8.2 +- Adds a new `custom-test` command. - Switches from deprecated `flutter packages` alias to `flutter pub`. ## 0.8.1 diff --git a/script/tool/lib/src/custom_test_command.dart b/script/tool/lib/src/custom_test_command.dart new file mode 100644 index 000000000000..1c1dfc068a11 --- /dev/null +++ b/script/tool/lib/src/custom_test_command.dart @@ -0,0 +1,77 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file/file.dart'; +import 'package:platform/platform.dart'; + +import 'common/package_looping_command.dart'; +import 'common/process_runner.dart'; +import 'common/repository_package.dart'; + +const String _scriptName = 'run_tests.dart'; +const String _legacyScriptName = 'run_tests.sh'; + +/// A command to run custom, package-local tests on packages. +/// +/// This is an escape hatch for adding tests that this tooling doesn't support. +/// It should be used sparingly; prefer instead to add functionality to this +/// tooling to eliminate the need for bespoke tests. +class CustomTestCommand extends PackageLoopingCommand { + /// Creates a custom test command instance. + CustomTestCommand( + Directory packagesDir, { + ProcessRunner processRunner = const ProcessRunner(), + Platform platform = const LocalPlatform(), + }) : super(packagesDir, processRunner: processRunner, platform: platform); + + @override + final String name = 'custom-test'; + + @override + final String description = 'Runs package-specific custom tests defined in ' + 'a package\'s tool/$_scriptName file.\n\n' + 'This command requires "dart" to be in your path.'; + + @override + Future runForPackage(RepositoryPackage package) async { + final File script = + package.directory.childDirectory('tool').childFile(_scriptName); + final File legacyScript = package.directory.childFile(_legacyScriptName); + String? customSkipReason; + bool ranTests = false; + + // Run the custom Dart script if presest. + if (script.existsSync()) { + final int exitCode = await processRunner.runAndStream( + 'dart', ['run', 'tool/$_scriptName'], + workingDir: package.directory); + if (exitCode != 0) { + return PackageResult.fail(); + } + ranTests = true; + } + + // Run the legacy script if present. + if (legacyScript.existsSync()) { + if (platform.isWindows) { + customSkipReason = '$_legacyScriptName is not supported on Windows. ' + 'Please migrate to $_scriptName.'; + } else { + final int exitCode = await processRunner.runAndStream( + legacyScript.path, [], + workingDir: package.directory); + if (exitCode != 0) { + return PackageResult.fail(); + } + ranTests = true; + } + } + + if (!ranTests) { + return PackageResult.skip(customSkipReason ?? 'No custom tests'); + } + + return PackageResult.success(); + } +} diff --git a/script/tool/lib/src/main.dart b/script/tool/lib/src/main.dart index 3e8f19b044dd..5a71a0a8889d 100644 --- a/script/tool/lib/src/main.dart +++ b/script/tool/lib/src/main.dart @@ -12,6 +12,7 @@ import 'analyze_command.dart'; import 'build_examples_command.dart'; import 'common/core.dart'; import 'create_all_plugins_app_command.dart'; +import 'custom_test_command.dart'; import 'drive_examples_command.dart'; import 'federation_safety_check_command.dart'; import 'firebase_test_lab_command.dart'; @@ -50,6 +51,7 @@ void main(List args) { ..addCommand(AnalyzeCommand(packagesDir)) ..addCommand(BuildExamplesCommand(packagesDir)) ..addCommand(CreateAllPluginsAppCommand(packagesDir)) + ..addCommand(CustomTestCommand(packagesDir)) ..addCommand(DriveExamplesCommand(packagesDir)) ..addCommand(FederationSafetyCheckCommand(packagesDir)) ..addCommand(FirebaseTestLabCommand(packagesDir)) diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index 93a1a87ca337..f005c565be17 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.8.1 +version: 0.8.2 dependencies: args: ^2.1.0 diff --git a/script/tool/test/custom_test_command_test.dart b/script/tool/test/custom_test_command_test.dart new file mode 100644 index 000000000000..6a34c2689f6b --- /dev/null +++ b/script/tool/test/custom_test_command_test.dart @@ -0,0 +1,281 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io' as io; + +import 'package:args/command_runner.dart'; +import 'package:file/file.dart'; +import 'package:file/memory.dart'; +import 'package:flutter_plugin_tools/src/common/core.dart'; +import 'package:flutter_plugin_tools/src/custom_test_command.dart'; +import 'package:test/test.dart'; + +import 'mocks.dart'; +import 'util.dart'; + +void main() { + late FileSystem fileSystem; + late MockPlatform mockPlatform; + late Directory packagesDir; + late RecordingProcessRunner processRunner; + late CommandRunner runner; + + group('posix', () { + setUp(() { + fileSystem = MemoryFileSystem(); + mockPlatform = MockPlatform(); + packagesDir = createPackagesDirectory(fileSystem: fileSystem); + processRunner = RecordingProcessRunner(); + final CustomTestCommand analyzeCommand = CustomTestCommand( + packagesDir, + processRunner: processRunner, + platform: mockPlatform, + ); + + runner = CommandRunner( + 'custom_test_command', 'Test for custom_test_command'); + runner.addCommand(analyzeCommand); + }); + + test('runs both new and legacy when both are present', () async { + final Directory package = + createFakePlugin('a_package', packagesDir, extraFiles: [ + 'tool/run_tests.dart', + 'run_tests.sh', + ]); + + final List output = + await runCapturingPrint(runner, ['custom-test']); + + expect( + processRunner.recordedCalls, + containsAll([ + ProcessCall(package.childFile('run_tests.sh').path, + const [], package.path), + ProcessCall('dart', const ['run', 'tool/run_tests.dart'], + package.path), + ])); + + expect( + output, + containsAllInOrder([ + contains('Ran for 1 package(s)'), + ])); + }); + + test('runs when only new is present', () async { + final Directory package = createFakePlugin('a_package', packagesDir, + extraFiles: ['tool/run_tests.dart']); + + final List output = + await runCapturingPrint(runner, ['custom-test']); + + expect( + processRunner.recordedCalls, + containsAll([ + ProcessCall('dart', const ['run', 'tool/run_tests.dart'], + package.path), + ])); + + expect( + output, + containsAllInOrder([ + contains('Ran for 1 package(s)'), + ])); + }); + + test('runs when only legacy is present', () async { + final Directory package = createFakePlugin('a_package', packagesDir, + extraFiles: ['run_tests.sh']); + + final List output = + await runCapturingPrint(runner, ['custom-test']); + + expect( + processRunner.recordedCalls, + containsAll([ + ProcessCall(package.childFile('run_tests.sh').path, + const [], package.path), + ])); + + expect( + output, + containsAllInOrder([ + contains('Ran for 1 package(s)'), + ])); + }); + + test('skips when neither is present', () async { + createFakePlugin('a_package', packagesDir); + + final List output = + await runCapturingPrint(runner, ['custom-test']); + + expect(processRunner.recordedCalls, isEmpty); + + expect( + output, + containsAllInOrder([ + contains('Skipped 1 package(s)'), + ])); + }); + + test('fails if new fails', () async { + createFakePlugin('a_package', packagesDir, extraFiles: [ + 'tool/run_tests.dart', + 'run_tests.sh', + ]); + + processRunner.mockProcessesForExecutable['dart'] = [ + MockProcess(exitCode: 1), + ]; + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['custom-test'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The following packages had errors:'), + contains('a_package') + ])); + }); + + test('fails if legacy fails', () async { + final Directory package = + createFakePlugin('a_package', packagesDir, extraFiles: [ + 'tool/run_tests.dart', + 'run_tests.sh', + ]); + + processRunner.mockProcessesForExecutable[ + package.childFile('run_tests.sh').path] = [ + MockProcess(exitCode: 1), + ]; + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['custom-test'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The following packages had errors:'), + contains('a_package') + ])); + }); + }); + + group('Windows', () { + setUp(() { + fileSystem = MemoryFileSystem(style: FileSystemStyle.windows); + mockPlatform = MockPlatform(isWindows: true); + packagesDir = createPackagesDirectory(fileSystem: fileSystem); + processRunner = RecordingProcessRunner(); + final CustomTestCommand analyzeCommand = CustomTestCommand( + packagesDir, + processRunner: processRunner, + platform: mockPlatform, + ); + + runner = CommandRunner( + 'custom_test_command', 'Test for custom_test_command'); + runner.addCommand(analyzeCommand); + }); + + test('runs new and skips old when both are present', () async { + final Directory package = + createFakePlugin('a_package', packagesDir, extraFiles: [ + 'tool/run_tests.dart', + 'run_tests.sh', + ]); + + final List output = + await runCapturingPrint(runner, ['custom-test']); + + expect( + processRunner.recordedCalls, + containsAll([ + ProcessCall('dart', const ['run', 'tool/run_tests.dart'], + package.path), + ])); + + expect( + output, + containsAllInOrder([ + contains('Ran for 1 package(s)'), + ])); + }); + + test('runs when only new is present', () async { + final Directory package = createFakePlugin('a_package', packagesDir, + extraFiles: ['tool/run_tests.dart']); + + final List output = + await runCapturingPrint(runner, ['custom-test']); + + expect( + processRunner.recordedCalls, + containsAll([ + ProcessCall('dart', const ['run', 'tool/run_tests.dart'], + package.path), + ])); + + expect( + output, + containsAllInOrder([ + contains('Ran for 1 package(s)'), + ])); + }); + + test('skips package when only legacy is present', () async { + createFakePlugin('a_package', packagesDir, + extraFiles: ['run_tests.sh']); + + final List output = + await runCapturingPrint(runner, ['custom-test']); + + expect(processRunner.recordedCalls, isEmpty); + + expect( + output, + containsAllInOrder([ + contains('run_tests.sh is not supported on Windows'), + contains('Skipped 1 package(s)'), + ])); + }); + + test('fails if new fails', () async { + createFakePlugin('a_package', packagesDir, extraFiles: [ + 'tool/run_tests.dart', + 'run_tests.sh', + ]); + + processRunner.mockProcessesForExecutable['dart'] = [ + MockProcess(exitCode: 1), + ]; + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['custom-test'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The following packages had errors:'), + contains('a_package') + ])); + }); + }); +} From d922d349154b1306b5a6d04848184cda19b41c37 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 18 Mar 2022 12:00:24 -0400 Subject: [PATCH 302/600] Roll Flutter from 509ddfda5c1b to 1c2c9421121f (1 revision) (#5067) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 09a4cea57f76..4270a8dd49ef 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -509ddfda5c1bda7c01cf51fe24607da19423d5f9 +1c2c9421121f345392162e3dde0a784d0cbdb69b From 466dc0f04829a6fba14e5b8a2f5455c0e9bfeb90 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 18 Mar 2022 15:15:18 -0400 Subject: [PATCH 303/600] Roll Flutter from 1c2c9421121f to a217bbb6defe (46 revisions) (#5072) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 4270a8dd49ef..ab7480eb27b4 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -1c2c9421121f345392162e3dde0a784d0cbdb69b +a217bbb6defed97c84bc26372193f5617ea683e3 From ef314fe762f86b691f68634f53f2f87e9618b81d Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 18 Mar 2022 17:25:23 -0400 Subject: [PATCH 304/600] Roll Flutter from a217bbb6defe to 5b3e42afd019 (5 revisions) (#5074) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index ab7480eb27b4..02d26f6161c8 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -a217bbb6defed97c84bc26372193f5617ea683e3 +5b3e42afd0195fb7e633b751ec39eeb9169efa72 From 9f7e31236d8a8bf643e55f8d4691053c4799f10d Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 19 Mar 2022 02:15:15 -0400 Subject: [PATCH 305/600] Roll Flutter from 5b3e42afd019 to 1de874e7364a (6 revisions) (#5080) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 02d26f6161c8..48198acf04f3 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -5b3e42afd0195fb7e633b751ec39eeb9169efa72 +1de874e7364ade61c7735a96969ea1e4419e325a From 085a5dc19e80a077404a5f7b92030360275ee2e9 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 19 Mar 2022 11:50:23 -0400 Subject: [PATCH 306/600] Roll Flutter from 1de874e7364a to 2c1dbba9344c (2 revisions) (#5083) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 48198acf04f3..07d3daa46075 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -1de874e7364ade61c7735a96969ea1e4419e325a +2c1dbba9344ce96128cedd69015f1cea56948f9d From 12af8a349c3b74c6dbd3aeef7f8d9b5101d6f73e Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 19 Mar 2022 16:40:13 -0400 Subject: [PATCH 307/600] Roll Flutter from 2c1dbba9344c to d1c11213493c (2 revisions) (#5085) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 07d3daa46075..18689a2bdcc2 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2c1dbba9344ce96128cedd69015f1cea56948f9d +d1c11213493c4dd1add018dd26b6238ad40efb41 From 1c61a217014c32a7d1d09436becd39f84504b5d2 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 20 Mar 2022 02:50:25 -0400 Subject: [PATCH 308/600] Roll Flutter from d1c11213493c to 6af40a7004f8 (1 revision) (#5089) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 18689a2bdcc2..655584a08576 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -d1c11213493c4dd1add018dd26b6238ad40efb41 +6af40a7004f886c8b8b87475a40107611bc5bb0a From 5b49d0f76c9f9b3aa8d578a4bb0282981d226653 Mon Sep 17 00:00:00 2001 From: Guy Kogus Date: Mon, 21 Mar 2022 19:55:19 +0100 Subject: [PATCH 309/600] [google_maps_flutter] Fix iOS crash by observing map frame change only once (#3426) --- .../google_maps_flutter/CHANGELOG.md | 4 +++ .../google_maps_flutter/example/ios/Podfile | 2 ++ .../ios/Runner.xcodeproj/project.pbxproj | 10 +++++- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- .../example/ios/RunnerTests/GoogleMapsTests.m | 24 +++++++++++++ .../ios/RunnerTests/PartiallyMockedMapView.h | 17 ++++++++++ .../ios/RunnerTests/PartiallyMockedMapView.m | 34 +++++++++++++++++++ .../ios/Classes/GoogleMapController.m | 24 +++++++------ .../ios/Classes/GoogleMapController_Test.h | 27 +++++++++++++++ .../Classes/google_maps_flutter-umbrella.h | 11 ++++++ .../ios/Classes/google_maps_flutter.modulemap | 10 ++++++ .../ios/google_maps_flutter.podspec | 3 +- .../google_maps_flutter/pubspec.yaml | 2 +- 13 files changed, 155 insertions(+), 15 deletions(-) create mode 100644 packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/PartiallyMockedMapView.h create mode 100644 packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/PartiallyMockedMapView.m create mode 100644 packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController_Test.h create mode 100644 packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter-umbrella.h create mode 100644 packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter.modulemap diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index 565bf8412c61..f05de47b3036 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.3 + +* Fixes iOS crash on `EXC_BAD_ACCESS KERN_PROTECTION_FAILURE` if the map frame changes long after creation. + ## 2.1.2 * Removes dependencies from `pubspec.yaml` that are only needed in `example/pubspec.yaml` diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/Podfile b/packages/google_maps_flutter/google_maps_flutter/example/ios/Podfile index 9686afaf3c99..29bfe631a3e7 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/ios/Podfile +++ b/packages/google_maps_flutter/google_maps_flutter/example/ios/Podfile @@ -31,6 +31,8 @@ target 'Runner' do flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) target 'RunnerTests' do inherit! :search_paths + + pod 'OCMock', '~> 3.9.1' end end diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj b/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj index fbb006aeded0..6a0466c3c6d9 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + 982F2A6C27BADE17003C81F4 /* PartiallyMockedMapView.m in Sources */ = {isa = PBXBuildFile; fileRef = 982F2A6B27BADE17003C81F4 /* PartiallyMockedMapView.m */; }; F7151F13265D7ED70028CB91 /* GoogleMapsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F7151F12265D7ED70028CB91 /* GoogleMapsTests.m */; }; F7151F21265D7EE50028CB91 /* GoogleMapsUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = F7151F20265D7EE50028CB91 /* GoogleMapsUITests.m */; }; FC8F35FC8CD533B128950487 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F267F68029D1A4E2E4C572A7 /* libPods-RunnerTests.a */; }; @@ -67,6 +68,8 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 982F2A6A27BADE17003C81F4 /* PartiallyMockedMapView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PartiallyMockedMapView.h; sourceTree = ""; }; + 982F2A6B27BADE17003C81F4 /* PartiallyMockedMapView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PartiallyMockedMapView.m; sourceTree = ""; }; B7AFC65E3DD5AC60D834D83D /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; E52C6A6210A56F027C582EF9 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; EA0E91726245EDC22B97E8B9 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; @@ -188,6 +191,8 @@ isa = PBXGroup; children = ( F7151F12265D7ED70028CB91 /* GoogleMapsTests.m */, + 982F2A6A27BADE17003C81F4 /* PartiallyMockedMapView.h */, + 982F2A6B27BADE17003C81F4 /* PartiallyMockedMapView.m */, F7151F14265D7ED70028CB91 /* Info.plist */, ); path = RunnerTests; @@ -270,7 +275,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1100; + LastUpgradeCheck = 1320; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -441,6 +446,7 @@ buildActionMask = 2147483647; files = ( F7151F13265D7ED70028CB91 /* GoogleMapsTests.m in Sources */, + 982F2A6C27BADE17003C81F4 /* PartiallyMockedMapView.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -511,6 +517,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -567,6 +574,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index afdb55fdfbdd..c983bfc640ff 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ +#import "PartiallyMockedMapView.h" + @interface GoogleMapsTests : XCTestCase @end @@ -15,4 +19,24 @@ - (void)testPlugin { XCTAssertNotNil(plugin); } +- (void)testFrameObserver { + id registrar = OCMProtocolMock(@protocol(FlutterPluginRegistrar)); + CGRect frame = CGRectMake(0, 0, 100, 100); + PartiallyMockedMapView *mapView = [[PartiallyMockedMapView alloc] + initWithFrame:frame + camera:[[GMSCameraPosition alloc] initWithLatitude:0 longitude:0 zoom:0]]; + FLTGoogleMapController *controller = [[FLTGoogleMapController alloc] initWithMapView:mapView + viewIdentifier:0 + arguments:nil + registrar:registrar]; + + for (NSInteger i = 0; i < 10; ++i) { + [controller view]; + } + XCTAssertEqual(mapView.frameObserverCount, 1); + + mapView.frame = frame; + XCTAssertEqual(mapView.frameObserverCount, 0); +} + @end diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/PartiallyMockedMapView.h b/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/PartiallyMockedMapView.h new file mode 100644 index 000000000000..4288401cf90d --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/PartiallyMockedMapView.h @@ -0,0 +1,17 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import GoogleMaps; + +/** + * Defines a map view used for testing key-value observing. + */ +@interface PartiallyMockedMapView : GMSMapView + +/** + * The number of times that the `frame` KVO has been added. + */ +@property(nonatomic, assign, readonly) NSInteger frameObserverCount; + +@end diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/PartiallyMockedMapView.m b/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/PartiallyMockedMapView.m new file mode 100644 index 000000000000..202a18d128c0 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/PartiallyMockedMapView.m @@ -0,0 +1,34 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "PartiallyMockedMapView.h" + +@interface PartiallyMockedMapView () + +@property(nonatomic, assign) NSInteger frameObserverCount; + +@end + +@implementation PartiallyMockedMapView + +- (void)addObserver:(NSObject *)observer + forKeyPath:(NSString *)keyPath + options:(NSKeyValueObservingOptions)options + context:(void *)context { + [super addObserver:observer forKeyPath:keyPath options:options context:context]; + + if ([keyPath isEqualToString:@"frame"]) { + ++self.frameObserverCount; + } +} + +- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath { + [super removeObserver:observer forKeyPath:keyPath]; + + if ([keyPath isEqualToString:@"frame"]) { + --self.frameObserverCount; + } +} + +@end diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m index df4e8761e6b2..ca8068129566 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m @@ -51,7 +51,6 @@ @implementation FLTGoogleMapController { FlutterMethodChannel *_channel; BOOL _trackCameraPosition; NSObject *_registrar; - BOOL _cameraDidInitialSetup; FLTMarkersController *_markersController; FLTPolygonsController *_polygonsController; FLTPolylinesController *_polylinesController; @@ -63,11 +62,19 @@ - (instancetype)initWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args registrar:(NSObject *)registrar { + GMSCameraPosition *camera = ToOptionalCameraPosition(args[@"initialCameraPosition"]); + GMSMapView *mapView = [GMSMapView mapWithFrame:frame camera:camera]; + return [self initWithMapView:mapView viewIdentifier:viewId arguments:args registrar:registrar]; +} + +- (instancetype)initWithMapView:(GMSMapView *_Nonnull)mapView + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args + registrar:(NSObject *_Nonnull)registrar { if (self = [super init]) { + _mapView = mapView; _viewId = viewId; - GMSCameraPosition *camera = ToOptionalCameraPosition(args[@"initialCameraPosition"]); - _mapView = [GMSMapView mapWithFrame:frame camera:camera]; _mapView.accessibilityElementsHidden = NO; _trackCameraPosition = NO; InterpretMapOptions(args[@"options"], self); @@ -83,7 +90,6 @@ - (instancetype)initWithFrame:(CGRect)frame }]; _mapView.delegate = weakSelf; _registrar = registrar; - _cameraDidInitialSetup = NO; _markersController = [[FLTMarkersController alloc] init:_channel mapView:_mapView registrar:registrar]; @@ -119,12 +125,13 @@ - (instancetype)initWithFrame:(CGRect)frame if ([tileOverlaysToAdd isKindOfClass:[NSArray class]]) { [_tileOverlaysController addTileOverlays:tileOverlaysToAdd]; } + + [_mapView addObserver:self forKeyPath:@"frame" options:0 context:nil]; } return self; } - (UIView *)view { - [_mapView addObserver:self forKeyPath:@"frame" options:0 context:nil]; return _mapView; } @@ -132,11 +139,6 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - if (_cameraDidInitialSetup) { - // We only observe the frame for initial setup. - [_mapView removeObserver:self forKeyPath:@"frame"]; - return; - } if (object == _mapView && [keyPath isEqualToString:@"frame"]) { CGRect bounds = _mapView.bounds; if (CGRectEqualToRect(bounds, CGRectZero)) { @@ -146,7 +148,7 @@ - (void)observeValueForKeyPath:(NSString *)keyPath // zero. return; } - _cameraDidInitialSetup = YES; + // We only observe the frame for initial setup. [_mapView removeObserver:self forKeyPath:@"frame"]; [_mapView moveCamera:[GMSCameraUpdate setCamera:_mapView.camera]]; } else { diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController_Test.h b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController_Test.h new file mode 100644 index 000000000000..84f6f7ca485f --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController_Test.h @@ -0,0 +1,27 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface FLTGoogleMapController (Test) + +/** + * Initializes a map controller with a concrete map view. + * + * @param mapView A map view that will be displayed by the controller + * @param viewId A unique identifier for the controller. + * @param args Parameters for initialising the map view. + * @param registrar The plugin registrar passed from Flutter. + */ +- (instancetype)initWithMapView:(GMSMapView *)mapView + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args + registrar:(NSObject *)registrar; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter-umbrella.h b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter-umbrella.h new file mode 100644 index 000000000000..50880a2b9e9d --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter-umbrella.h @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import +#import +#import + +FOUNDATION_EXPORT double google_maps_flutterVersionNumber; +FOUNDATION_EXPORT const unsigned char google_maps_flutterVersionString[]; diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter.modulemap b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter.modulemap new file mode 100644 index 000000000000..19513f4a7602 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter.modulemap @@ -0,0 +1,10 @@ +framework module google_maps_flutter { + umbrella header "google_maps_flutter-umbrella.h" + + export * + module * { export * } + + explicit module Test { + header "GoogleMapController_Test.h" + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/google_maps_flutter.podspec b/packages/google_maps_flutter/google_maps_flutter/ios/google_maps_flutter.podspec index f2ed5fc56ea0..e34919c30484 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/google_maps_flutter.podspec +++ b/packages/google_maps_flutter/google_maps_flutter/ios/google_maps_flutter.podspec @@ -14,8 +14,9 @@ Downloaded by pub (not CocoaPods). s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/google_maps_flutter/google_maps_flutter' } s.documentation_url = 'https://pub.dev/packages/google_maps_flutter' - s.source_files = 'Classes/**/*' + s.source_files = 'Classes/**/*.{h,m}' s.public_header_files = 'Classes/**/*.h' + s.module_map = 'Classes/google_maps_flutter.modulemap' s.dependency 'Flutter' s.dependency 'GoogleMaps' s.static_framework = true diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index 849019cbdb2d..741fe6910037 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter description: A Flutter plugin for integrating Google Maps in iOS and Android applications. repository: https://github.com/flutter/plugins/tree/main/packages/google_maps_flutter/google_maps_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 2.1.2 +version: 2.1.3 environment: sdk: ">=2.14.0 <3.0.0" From 941641198fdfbe57e21e93bb4c477ab39dd3a916 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 21 Mar 2022 15:35:26 -0400 Subject: [PATCH 310/600] Roll Flutter from 6af40a7004f8 to 8c1c2f6af555 (1 revision) (#5092) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 655584a08576..7a43a6594b72 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -6af40a7004f886c8b8b87475a40107611bc5bb0a +8c1c2f6af5558c27946e8376541649090073d50c From ada413d49d8743e495148a47b73e784742314329 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 21 Mar 2022 17:45:24 -0400 Subject: [PATCH 311/600] Roll Flutter from 8c1c2f6af555 to 97258979df13 (8 revisions) (#5094) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 7a43a6594b72..116f41f89d39 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -8c1c2f6af5558c27946e8376541649090073d50c +97258979df13c5ea40490cd2615803f26ae6e507 From 3069bc3b84104d642ec2f69a9b0584719fe34e01 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 21 Mar 2022 18:50:25 -0400 Subject: [PATCH 312/600] Roll Flutter from 97258979df13 to 9671b7ddee8c (1 revision) (#5095) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 116f41f89d39..3bf43fd4c61a 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -97258979df13c5ea40490cd2615803f26ae6e507 +9671b7ddee8c5110cf6adcf7b7697991512218e9 From 52995e83c87947d8e0ca0f79fe86a120415dcf81 Mon Sep 17 00:00:00 2001 From: Kyle Finlinson Date: Tue, 22 Mar 2022 14:00:22 -0600 Subject: [PATCH 313/600] [video_player] Platform interface changes to fix Android rotation for videos recorded in landscapeRight (#4634) --- .../CHANGELOG.md | 4 ++++ .../lib/method_channel_video_player.dart | 1 + .../lib/video_player_platform_interface.dart | 12 +++++++++-- .../pubspec.yaml | 2 +- .../method_channel_video_player_test.dart | 21 +++++++++++++++++++ 5 files changed, 37 insertions(+), 3 deletions(-) diff --git a/packages/video_player/video_player_platform_interface/CHANGELOG.md b/packages/video_player/video_player_platform_interface/CHANGELOG.md index 52612207d8f3..6595a5ce55db 100644 --- a/packages/video_player/video_player_platform_interface/CHANGELOG.md +++ b/packages/video_player/video_player_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 5.1.1 + +* Adds `rotationCorrection` (for Android playing videos recorded in landscapeRight [#60327](https://github.com/flutter/flutter/issues/60327)). + ## 5.1.0 * Adds `allowBackgroundPlayback` to `VideoPlayerOptions`. diff --git a/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart b/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart index 2aa7fb30e5f2..097ab29c48a3 100644 --- a/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart +++ b/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart @@ -115,6 +115,7 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { duration: Duration(milliseconds: map['duration']! as int), size: Size((map['width'] as num?)?.toDouble() ?? 0.0, (map['height'] as num?)?.toDouble() ?? 0.0), + rotationCorrection: map['rotationCorrection'] as int? ?? 0, ); case 'completed': return VideoEvent( diff --git a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart index 8a61005c429e..706ec61abf7c 100644 --- a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart +++ b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart @@ -199,8 +199,8 @@ class VideoEvent { /// /// The [eventType] argument is required. /// - /// Depending on the [eventType], the [duration], [size] and [buffered] - /// arguments can be null. + /// Depending on the [eventType], the [duration], [size], + /// [rotationCorrection], and [buffered] arguments can be null. // TODO(stuartmorgan): Temporarily suppress warnings about not using const // in all of the other video player packages, fix this, and then update // the other packages to use const. @@ -209,6 +209,7 @@ class VideoEvent { required this.eventType, this.duration, this.size, + this.rotationCorrection, this.buffered, }); @@ -225,6 +226,11 @@ class VideoEvent { /// Only used if [eventType] is [VideoEventType.initialized]. final Size? size; + /// Degrees to rotate the video (clockwise) so it is displayed correctly. + /// + /// Only used if [eventType] is [VideoEventType.initialized]. + final int? rotationCorrection; + /// Buffered parts of the video. /// /// Only used if [eventType] is [VideoEventType.bufferingUpdate]. @@ -238,6 +244,7 @@ class VideoEvent { eventType == other.eventType && duration == other.duration && size == other.size && + rotationCorrection == other.rotationCorrection && listEquals(buffered, other.buffered); } @@ -246,6 +253,7 @@ class VideoEvent { eventType.hashCode ^ duration.hashCode ^ size.hashCode ^ + rotationCorrection.hashCode ^ buffered.hashCode; } diff --git a/packages/video_player/video_player_platform_interface/pubspec.yaml b/packages/video_player/video_player_platform_interface/pubspec.yaml index b66fa0b46d75..ea3b17be1ea5 100644 --- a/packages/video_player/video_player_platform_interface/pubspec.yaml +++ b/packages/video_player/video_player_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/video_player/v issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 5.1.0 +version: 5.1.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart b/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart index 75baba45f763..ca17196cf523 100644 --- a/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart +++ b/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart @@ -257,6 +257,20 @@ void main() { }), (ByteData? data) {}); + await _ambiguate(ServicesBinding.instance) + ?.defaultBinaryMessenger + .handlePlatformMessage( + 'flutter.io/videoPlayer/videoEvents123', + const StandardMethodCodec() + .encodeSuccessEnvelope({ + 'event': 'initialized', + 'duration': 98765, + 'width': 1920, + 'height': 1080, + 'rotationCorrection': 180, + }), + (ByteData? data) {}); + await _ambiguate(ServicesBinding.instance) ?.defaultBinaryMessenger .handlePlatformMessage( @@ -316,6 +330,13 @@ void main() { eventType: VideoEventType.initialized, duration: const Duration(milliseconds: 98765), size: const Size(1920, 1080), + rotationCorrection: 0, + ), + VideoEvent( + eventType: VideoEventType.initialized, + duration: const Duration(milliseconds: 98765), + size: const Size(1920, 1080), + rotationCorrection: 180, ), VideoEvent(eventType: VideoEventType.completed), VideoEvent( From 1bb37bd57d7cdfe98e964f55134c8cc561e0ccaf Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Tue, 22 Mar 2022 17:20:45 -0400 Subject: [PATCH 314/600] [webview_flutter_wkwebview] Add support to clear cache (#4991) --- .../lib/src/web_kit/web_kit.dart | 60 +++++++++++++++++++ .../lib/src/web_kit_webview_widget.dart | 13 ++++ .../test/src/web_kit_webview_widget_test.dart | 21 +++++++ .../web_kit_webview_widget_test.mocks.dart | 45 +++++++++++--- 4 files changed, 131 insertions(+), 8 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart index 85881e0e512e..d7fdf1db68c6 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart @@ -46,6 +46,35 @@ enum WKAudiovisualMediaType { all, } +/// Types of data that websites store. +/// +/// See https://developer.apple.com/documentation/webkit/wkwebsitedatarecord/data_store_record_types?language=objc. +enum WKWebsiteDataTypes { + /// Cookies. + cookies, + + /// In-memory caches. + memoryCache, + + /// On-disk caches. + diskCache, + + /// HTML offline web app caches. + offlineWebApplicationCache, + + /// HTML local storage. + localStroage, + + /// HTML session storage. + sessionStorage, + + /// WebSQL databases. + sqlDatabases, + + /// IndexedDB databases. + indexedDBDatabases, +} + /// Indicate whether to allow or cancel navigation to a webpage. /// /// Wraps [WKNavigationActionPolicy](https://developer.apple.com/documentation/webkit/wknavigationactionpolicy?language=objc). @@ -162,6 +191,25 @@ class WKScriptMessage { final Object? body; } +/// Manages cookies, disk and memory caches, and other types of data for a web view. +/// +/// Wraps [WKWebsiteDataStore](https://developer.apple.com/documentation/webkit/wkwebsitedatastore?language=objc). +class WKWebsiteDataStore { + WKWebsiteDataStore._fromWebViewConfiguration( + // TODO(bparrishMines): Remove ignore once constructor is implemented. + // ignore: avoid_unused_constructor_parameters + WKWebViewConfiguration configuration, + ); + + /// Removes website data that changed after the specified date. + Future removeDataOfTypes( + Set dataTypes, + DateTime since, + ) { + throw UnimplementedError(); + } +} + /// An interface for receiving messages from JavaScript code running in a webpage. /// /// Wraps [WKScriptMessageHandler](https://developer.apple.com/documentation/webkit/wkscriptmessagehandler?language=objc) @@ -268,6 +316,18 @@ class WKWebViewConfiguration { /// Coordinates interactions between your app’s code and the webpage’s scripts and other content. late final WKUserContentController userContentController; + late WKWebsiteDataStore _websiteDataStore = + WKWebsiteDataStore._fromWebViewConfiguration(this); + + /// Used to get and set the site’s cookies and to track the cached data objects. + WKWebsiteDataStore get webSiteDataStore => _websiteDataStore; + + /// Used to get and set the site’s cookies and to track the cached data objects. + set webSiteDataStore(WKWebsiteDataStore websiteDataStore) { + _websiteDataStore = websiteDataStore; + throw UnimplementedError(); + } + /// Indicates whether HTML5 videos play inline or use the native full-screen controller. set allowsInlineMediaPlayback(bool allow) { throw UnimplementedError(); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 89a258e75767..87baeb45c368 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -194,6 +194,19 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { }; } + @override + Future clearCache() { + return webView.configuration.webSiteDataStore.removeDataOfTypes( + { + WKWebsiteDataTypes.memoryCache, + WKWebsiteDataTypes.diskCache, + WKWebsiteDataTypes.offlineWebApplicationCache, + WKWebsiteDataTypes.localStroage, + }, + DateTime.fromMillisecondsSinceEpoch(0), + ); + } + @override Future updateSettings(WebSettings setting) async { if (setting.hasNavigationDelegate != null) { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index 6642b4561cfc..358df898403a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -20,6 +20,7 @@ import 'web_kit_webview_widget_test.mocks.dart'; WKScriptMessageHandler, WKWebView, WKWebViewConfiguration, + WKWebsiteDataStore, WKUIDelegate, WKUserContentController, JavascriptChannelRegistry, @@ -35,6 +36,7 @@ void main() { late MockWKUserContentController mockUserContentController; late MockWKWebViewConfiguration mockWebViewConfiguration; late MockWKUIDelegate mockUIDelegate; + late MockWKWebsiteDataStore mockWebsiteDataStore; late MockWKNavigationDelegate mockNavigationDelegate; late MockWebViewPlatformCallbacksHandler mockCallbacksHandler; @@ -47,6 +49,7 @@ void main() { mockWebViewConfiguration = MockWKWebViewConfiguration(); mockUserContentController = MockWKUserContentController(); mockUIDelegate = MockWKUIDelegate(); + mockWebsiteDataStore = MockWKWebsiteDataStore(); mockNavigationDelegate = MockWKNavigationDelegate(); mockWebViewWidgetProxy = MockWebViewWidgetProxy(); @@ -58,6 +61,9 @@ void main() { when(mockWebViewConfiguration.userContentController).thenReturn( mockUserContentController, ); + when(mockWebViewConfiguration.webSiteDataStore).thenReturn( + mockWebsiteDataStore, + ); mockCallbacksHandler = MockWebViewPlatformCallbacksHandler(); mockJavascriptChannelRegistry = MockJavascriptChannelRegistry(); @@ -206,6 +212,21 @@ void main() { }); group('$WebKitWebViewPlatformController', () { + testWidgets('clearCache', (WidgetTester tester) async { + await buildWidget(tester); + + await testController.clearCache(); + verify(mockWebsiteDataStore.removeDataOfTypes( + { + WKWebsiteDataTypes.memoryCache, + WKWebsiteDataTypes.diskCache, + WKWebsiteDataTypes.offlineWebApplicationCache, + WKWebsiteDataTypes.localStroage, + }, + DateTime.fromMillisecondsSinceEpoch(0), + )); + }); + testWidgets('addJavascriptChannels', (WidgetTester tester) async { when(mockWebViewWidgetProxy.createScriptMessageHandler()).thenReturn( MockWKScriptMessageHandler(), diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index 9579b23c84a0..32f1731701a9 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -32,14 +32,17 @@ class _FakeWKWebViewConfiguration_0 extends _i1.Fake class _FakeWKUserContentController_1 extends _i1.Fake implements _i2.WKUserContentController {} -class _FakeWKWebView_2 extends _i1.Fake implements _i2.WKWebView {} +class _FakeWKWebsiteDataStore_2 extends _i1.Fake + implements _i2.WKWebsiteDataStore {} -class _FakeWKScriptMessageHandler_3 extends _i1.Fake +class _FakeWKWebView_3 extends _i1.Fake implements _i2.WKWebView {} + +class _FakeWKScriptMessageHandler_4 extends _i1.Fake implements _i2.WKScriptMessageHandler {} -class _FakeWKUIDelegate_4 extends _i1.Fake implements _i2.WKUIDelegate {} +class _FakeWKUIDelegate_5 extends _i1.Fake implements _i2.WKUIDelegate {} -class _FakeWKNavigationDelegate_5 extends _i1.Fake +class _FakeWKNavigationDelegate_6 extends _i1.Fake implements _i2.WKNavigationDelegate {} /// A class which mocks [WKNavigationDelegate]. @@ -167,6 +170,14 @@ class MockWKWebViewConfiguration extends _i1.Mock Invocation.setter(#userContentController, _userContentController), returnValueForMissingStub: null); @override + _i2.WKWebsiteDataStore get webSiteDataStore => + (super.noSuchMethod(Invocation.getter(#webSiteDataStore), + returnValue: _FakeWKWebsiteDataStore_2()) as _i2.WKWebsiteDataStore); + @override + set webSiteDataStore(_i2.WKWebsiteDataStore? websiteDataStore) => + super.noSuchMethod(Invocation.setter(#webSiteDataStore, websiteDataStore), + returnValueForMissingStub: null); + @override set allowsInlineMediaPlayback(bool? allow) => super.noSuchMethod(Invocation.setter(#allowsInlineMediaPlayback, allow), returnValueForMissingStub: null); @@ -178,6 +189,24 @@ class MockWKWebViewConfiguration extends _i1.Mock returnValueForMissingStub: null); } +/// A class which mocks [WKWebsiteDataStore]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockWKWebsiteDataStore extends _i1.Mock + implements _i2.WKWebsiteDataStore { + MockWKWebsiteDataStore() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.Future removeDataOfTypes( + Set<_i2.WKWebsiteDataTypes>? dataTypes, DateTime? since) => + (super.noSuchMethod( + Invocation.method(#removeDataOfTypes, [dataTypes, since]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i3.Future); +} + /// A class which mocks [WKUIDelegate]. /// /// See the documentation for Mockito's code generation for more information. @@ -303,18 +332,18 @@ class MockWebViewWidgetProxy extends _i1.Mock @override _i2.WKWebView createWebView(_i2.WKWebViewConfiguration? configuration) => (super.noSuchMethod(Invocation.method(#createWebView, [configuration]), - returnValue: _FakeWKWebView_2()) as _i2.WKWebView); + returnValue: _FakeWKWebView_3()) as _i2.WKWebView); @override _i2.WKScriptMessageHandler createScriptMessageHandler() => (super.noSuchMethod(Invocation.method(#createScriptMessageHandler, []), - returnValue: _FakeWKScriptMessageHandler_3()) + returnValue: _FakeWKScriptMessageHandler_4()) as _i2.WKScriptMessageHandler); @override _i2.WKUIDelegate createUIDelgate() => (super.noSuchMethod(Invocation.method(#createUIDelgate, []), - returnValue: _FakeWKUIDelegate_4()) as _i2.WKUIDelegate); + returnValue: _FakeWKUIDelegate_5()) as _i2.WKUIDelegate); @override _i2.WKNavigationDelegate createNavigationDelegate() => (super.noSuchMethod( Invocation.method(#createNavigationDelegate, []), - returnValue: _FakeWKNavigationDelegate_5()) as _i2.WKNavigationDelegate); + returnValue: _FakeWKNavigationDelegate_6()) as _i2.WKNavigationDelegate); } From 8624a5f50852f853f5d98468a930d116f2eff01e Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 23 Mar 2022 16:12:36 -0400 Subject: [PATCH 315/600] [video_player] Reduce server dependencies in tests (#5103) Reduce test dependencies on servers that the Flutter team doesn't control: - Switches webm URL to the GitHub URL for the example asset, matching the existing mp4 setup - Switches m3u8 URL to the assets-for-api-docs URL that was recently added for a native iOS video_player test Ideally these would use local web severs, but initial attempts to serve binary assets via the local web server didn't work, and I won't have time to investigate further in the short term. This is an incremental improvement, and adds TODOs to fully convert. --- .../integration_test/video_player_test.dart | 16 +++++++--------- .../integration_test/video_player_test.dart | 7 +++++-- .../integration_test/video_player_test.dart | 7 +++++-- 3 files changed, 17 insertions(+), 13 deletions(-) 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 746c63fcbfd6..151eb93149ee 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 @@ -21,10 +21,13 @@ const String _videoAssetKey = kIsWeb ? 'assets/Butterfly-209.webm' : 'assets/Butterfly-209.mp4'; // Returns the URL to load an asset from this example app as a network source. +// +// TODO(stuartmorgan): Convert this to a local `HttpServer` that vends the +// assets directly, https://github.com/flutter/flutter/issues/95420 String getUrlForAssetAsNetworkSource(String assetKey) { return 'https://github.com/flutter/plugins/blob/' // This hash can be rolled forward to pick up newly-added assets. - 'cba393233e559c925a4daf71b06b4bb01c606762' + 'cb381ced070d356799dddf24aca38ce0579d3d7b' '/packages/video_player/video_player/example/' '$assetKey' '?raw=true'; @@ -56,7 +59,7 @@ void main() { (WidgetTester tester) async { final VideoPlayerController networkController = VideoPlayerController.network( - 'https://cph-p2p-msl.akamaized.net/hls/live/2000341/test/master.m3u8', + 'https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8', ); await networkController.initialize(); @@ -229,13 +232,8 @@ void main() { group('network videos', () { setUp(() { - // TODO(stuartmorgan): Remove this conditional and update the hash in - // getUrlForAssetAsNetworkSource as a follow-up, once the webm asset is - // checked in. - final String videoUrl = kIsWeb - ? 'https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm' - : getUrlForAssetAsNetworkSource(_videoAssetKey); - _controller = VideoPlayerController.network(videoUrl); + _controller = VideoPlayerController.network( + getUrlForAssetAsNetworkSource(_videoAssetKey)); }); testWidgets( diff --git a/packages/video_player/video_player_android/example/integration_test/video_player_test.dart b/packages/video_player/video_player_android/example/integration_test/video_player_test.dart index b80ed745a6f9..77a618bbbdfc 100644 --- a/packages/video_player/video_player_android/example/integration_test/video_player_test.dart +++ b/packages/video_player/video_player_android/example/integration_test/video_player_test.dart @@ -24,10 +24,13 @@ const Duration _playDuration = Duration(seconds: 1); const String _videoAssetKey = 'assets/Butterfly-209.mp4'; // Returns the URL to load an asset from this example app as a network source. +// +// TODO(stuartmorgan): Convert this to a local `HttpServer` that vends the +// assets directly, https://github.com/flutter/flutter/issues/95420 String getUrlForAssetAsNetworkSource(String assetKey) { return 'https://github.com/flutter/plugins/blob/' // This hash can be rolled forward to pick up newly-added assets. - 'cba393233e559c925a4daf71b06b4bb01c606762' + 'cb381ced070d356799dddf24aca38ce0579d3d7b' '/packages/video_player/video_player/example/' '$assetKey' '?raw=true'; @@ -155,7 +158,7 @@ void main() { testWidgets('live stream duration != 0', (WidgetTester tester) async { final MiniController livestreamController = MiniController.network( - 'https://cph-p2p-msl.akamaized.net/hls/live/2000341/test/master.m3u8', + 'https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8', ); await livestreamController.initialize(); diff --git a/packages/video_player/video_player_avfoundation/example/integration_test/video_player_test.dart b/packages/video_player/video_player_avfoundation/example/integration_test/video_player_test.dart index a457d9226e3e..528723d092b4 100644 --- a/packages/video_player/video_player_avfoundation/example/integration_test/video_player_test.dart +++ b/packages/video_player/video_player_avfoundation/example/integration_test/video_player_test.dart @@ -24,10 +24,13 @@ const Duration _playDuration = Duration(seconds: 1); const String _videoAssetKey = 'assets/Butterfly-209.mp4'; // Returns the URL to load an asset from this example app as a network source. +// +// TODO(stuartmorgan): Convert this to a local `HttpServer` that vends the +// assets directly, https://github.com/flutter/flutter/issues/95420 String getUrlForAssetAsNetworkSource(String assetKey) { return 'https://github.com/flutter/plugins/blob/' // This hash can be rolled forward to pick up newly-added assets. - 'cba393233e559c925a4daf71b06b4bb01c606762' + 'cb381ced070d356799dddf24aca38ce0579d3d7b' '/packages/video_player/video_player/example/' '$assetKey' '?raw=true'; @@ -167,7 +170,7 @@ void main() { testWidgets('live stream duration != 0', (WidgetTester tester) async { final MiniController livestreamController = MiniController.network( - 'https://cph-p2p-msl.akamaized.net/hls/live/2000341/test/master.m3u8', + 'https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8', ); await livestreamController.initialize(); From 86b36210ed03e18f2b9f900671618c803443da26 Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Wed, 23 Mar 2022 17:25:10 -0700 Subject: [PATCH 316/600] [ci] Update Chrome install script and version. (#5098) --- script/install_chromium.sh | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/script/install_chromium.sh b/script/install_chromium.sh index 1cb38af05496..b7d787626d55 100755 --- a/script/install_chromium.sh +++ b/script/install_chromium.sh @@ -10,24 +10,35 @@ readonly TARGET_DIR=$1 # The build of Chromium used to test web functionality. # # Chromium builds can be located here: https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Linux_x64/ -readonly CHROMIUM_BUILD=768968 -# The ChromeDriver version corresponding to the build above. See -# https://chromedriver.chromium.org/downloads -# for versions mappings when updating Chromium. -readonly CHROME_DRIVER_VERSION=84.0.4147.30 +# +# Check: https://github.com/flutter/engine/blob/master/lib/web_ui/dev/browser_lock.yaml +readonly CHROMIUM_BUILD=929514 + +# The correct ChromeDriver is distributed alongside the chromium build above, as +# `chromedriver_linux64.zip`, so no need to hardcode any extra info about it. +readonly DOWNLOAD_ROOT="https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64%2F${CHROMIUM_BUILD}%2F" # Install Chromium. mkdir "$TARGET_DIR" -wget --no-verbose "https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64%2F${CHROMIUM_BUILD}%2Fchrome-linux.zip?alt=media" -O "$TARGET_DIR"/chromium.zip -unzip "$TARGET_DIR"/chromium.zip -d "$TARGET_DIR"/ +readonly CHROMIUM_ZIP_FILE="$TARGET_DIR/chromium.zip" +wget --no-verbose "${DOWNLOAD_ROOT}chrome-linux.zip?alt=media" -O "$CHROMIUM_ZIP_FILE" +unzip -q "$CHROMIUM_ZIP_FILE" -d "$TARGET_DIR/" # Install ChromeDriver. readonly DRIVER_ZIP_FILE="$TARGET_DIR/chromedriver.zip" -wget --no-verbose "https://chromedriver.storage.googleapis.com/$CHROME_DRIVER_VERSION/chromedriver_linux64.zip" -O "$DRIVER_ZIP_FILE" -unzip "$DRIVER_ZIP_FILE" -d "$TARGET_DIR/chromedriver" +wget --no-verbose "${DOWNLOAD_ROOT}chromedriver_linux64.zip?alt=media" -O "$DRIVER_ZIP_FILE" +unzip -q "$DRIVER_ZIP_FILE" -d "$TARGET_DIR/" +# Rename TARGET_DIR/chromedriver_linux64 to the expected TARGET_DIR/chromedriver +mv -T "$TARGET_DIR/chromedriver_linux64" "$TARGET_DIR/chromedriver" + +export CHROME_EXECUTABLE="$TARGET_DIR/chrome-linux/chrome" # Echo info at the end for ease of debugging. -export CHROME_EXECUTABLE="$TARGET_DIR"/chrome-linux/chrome -echo $CHROME_EXECUTABLE -$CHROME_EXECUTABLE --version -echo "ChromeDriver $CHROME_DRIVER_VERSION" +set +x +echo +readonly CHROMEDRIVER_EXECUTABLE="$TARGET_DIR/chromedriver/chromedriver" +echo "$CHROME_EXECUTABLE" +"$CHROME_EXECUTABLE" --version +echo "$CHROMEDRIVER_EXECUTABLE" +"$CHROMEDRIVER_EXECUTABLE" --version +echo From 431cac92ab1b3821a3fd5d89ceee62a834da4999 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 24 Mar 2022 11:45:09 -0400 Subject: [PATCH 317/600] [camera] Update README (#4988) --- packages/camera/camera/CHANGELOG.md | 4 ++++ packages/camera/camera/README.md | 19 ++++++++++--------- packages/camera/camera/pubspec.yaml | 2 +- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 35a958e026ff..d19b500af2d7 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.4+17 + +* Removes obsolete information from README, and adds OS support table. + ## 0.9.4+16 * Fixes a bug resulting in a `CameraAccessException` that prevents image diff --git a/packages/camera/camera/README.md b/packages/camera/camera/README.md index 6cca254f8690..a313c3a1655f 100644 --- a/packages/camera/camera/README.md +++ b/packages/camera/camera/README.md @@ -4,7 +4,9 @@ A Flutter plugin for iOS, Android and Web allowing access to the device cameras. -*Note*: This plugin is still under development, and some APIs might not be available yet. We are working on a refactor which can be followed here: [issue](https://github.com/flutter/flutter/issues/31225) +| | Android | iOS | Web | +|----------------|---------|----------|------------------------| +| **Support** | SDK 21+ | iOS 10+* | [See `camera_web `][1] | ## Features @@ -19,8 +21,9 @@ First, add `camera` as a [dependency in your pubspec.yaml file](https://flutter. ### iOS -The camera plugin functionality works on iOS 10.0 or higher. If compiling for any version lower than 10.0, -make sure to programmatically check the version of iOS running on the device before using any camera plugin features. +\* The camera plugin compiles for any version of iOS, but its functionality +requires iOS 10 or higher. If compiling for iOS 9, make sure to programmatically +check the version of iOS running on the device before using any camera plugin features. The [device_info_plus](https://pub.dev/packages/device_info_plus) plugin, for example, can be used to check the iOS version. Add two rows to the `ios/Runner/Info.plist`: @@ -28,13 +31,13 @@ Add two rows to the `ios/Runner/Info.plist`: * one with the key `Privacy - Camera Usage Description` and a usage description. * and one with the key `Privacy - Microphone Usage Description` and a usage description. -Or in text format add the key: +If editing `Info.plist` as text, add: ```xml NSCameraUsageDescription -Can I use the camera please? +your usage description here NSMicrophoneUsageDescription -Can I use the mic please? +your usage description here ``` ### Android @@ -132,6 +135,4 @@ class _CameraAppState extends State { For a more elaborate usage example see [here](https://github.com/flutter/plugins/tree/main/packages/camera/camera/example). -*Note*: This plugin is still under development, and some APIs might not be available yet. -[Feedback welcome](https://github.com/flutter/flutter/issues) and -[Pull Requests](https://github.com/flutter/plugins/pulls) are most welcome! +[1]: https://pub.dev/packages/camera_web#limitations-on-the-web-platform diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 2baab09c5dcb..0e684a743ba3 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+16 +version: 0.9.4+17 environment: sdk: ">=2.14.0 <3.0.0" From 4ec6e57632b651d5fc35fede038199e3ab0efb68 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 24 Mar 2022 10:27:26 -0700 Subject: [PATCH 318/600] [webview_flutter_wkwebview] Add WKWebView method stubs (#4990) --- .../lib/src/ui_kit/ui_kit.dart | 38 ++ .../lib/src/web_kit/web_kit.dart | 79 ++++ .../lib/src/web_kit_webview_widget.dart | 163 ++++++++ .../webview_flutter_wkwebview/pubspec.yaml | 1 + .../test/src/web_kit_webview_widget_test.dart | 349 ++++++++++++++++++ .../web_kit_webview_widget_test.mocks.dart | 265 ++++++++----- 6 files changed, 810 insertions(+), 85 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart new file mode 100644 index 000000000000..def52b78f6b5 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart @@ -0,0 +1,38 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:math'; + +import '../web_kit/web_kit.dart'; + +/// A view that allows the scrolling and zooming of its contained views. +/// +/// Wraps [UIScrollView](https://developer.apple.com/documentation/uikit/uiscrollview?language=objc). +class UIScrollView { + /// Constructs a [UIScrollView] that is owned by [webView]. + // TODO(bparrishMines): Remove ignore once constructor is implemented. + // ignore: avoid_unused_constructor_parameters + UIScrollView.fromWebView(WKWebView webView); + + /// Point at which the origin of the content view is offset from the origin of the scroll view. + Future> get contentOffset { + throw UnimplementedError(); + } + + /// Move the scrolled position of this view. + /// + /// This method is not a part of UIKit and is only a helper method to make + /// scrollBy atomic. + Future scrollBy(Point offset) { + throw UnimplementedError(); + } + + /// Set point at which the origin of the content view is offset from the origin of the scroll view. + /// + /// The default value is `Point(0.0, 0.0)`. + set contentOffset(FutureOr> offset) { + throw UnimplementedError(); + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart index d7fdf1db68c6..cd7e4a9aadcb 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart @@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart'; import '../foundation/foundation.dart'; +import '../ui_kit/ui_kit.dart'; /// Times at which to inject script content into a webpage. /// @@ -450,6 +451,9 @@ class WKWebView { late final WKWebViewConfiguration configuration = WKWebViewConfiguration._fromWebView(this); + /// The scrollable view associated with the web view. + late final UIScrollView scrollView = UIScrollView.fromWebView(this); + /// Used to integrate custom user interface elements into web view interactions. set uiDelegate(WKUIDelegate? delegate) { throw UnimplementedError(); @@ -472,4 +476,79 @@ class WKWebView { Future loadRequest(NSUrlRequest request) { throw UnimplementedError(); } + + /// Loads the contents of the specified HTML string and navigates to it. + Future loadHtmlString(String string, {String? baseUrl}) { + throw UnimplementedError(); + } + + /// Loads the web content from the specified file and navigates to it. + Future loadFileUrl(String url, {required String readAccessUrl}) { + throw UnimplementedError(); + } + + /// Loads the Flutter asset specified in the pubspec.yaml file. + /// + /// This method is not a part of WebKit and is only a Flutter specific helper + /// method. + Future loadFlutterAsset(String key) { + throw UnimplementedError(); + } + + /// Indicates whether there is a valid back item in the back-forward list. + Future get canGoBack { + throw UnimplementedError(); + } + + /// Indicates whether there is a valid forward item in the back-forward list. + Future get canGoForward { + throw UnimplementedError(); + } + + /// Navigates to the back item in the back-forward list. + Future goBack() { + throw UnimplementedError(); + } + + /// Navigates to the forward item in the back-forward list. + Future goForward() { + throw UnimplementedError(); + } + + /// Reloads the current webpage. + Future reload() { + throw UnimplementedError(); + } + + /// The page title. + Future get title { + throw UnimplementedError(); + } + + /// An estimate of what fraction of the current navigation has been loaded. + Future get estimatedProgress { + throw UnimplementedError(); + } + + /// Indicates whether horizontal swipe gestures trigger page navigation. + /// + /// The default value is false. + set allowsBackForwardNavigationGestures(bool allow) { + throw UnimplementedError(); + } + + /// The custom user agent string. + /// + /// The default value of this property is null. + set customUserAgent(String? userAgent) { + throw UnimplementedError(); + } + + /// Evaluates the specified JavaScript string. + /// + /// Throws a `PlatformException` if an error occurs or return value is not + /// supported. + Future evaluateJavaScript(String javaScriptString) { + throw UnimplementedError(); + } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 87baeb45c368..67ea6a936e42 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -3,9 +3,12 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:math'; import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; +import 'package:path/path.dart' as path; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; import 'foundation/foundation.dart'; @@ -194,6 +197,19 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { }; } + @override + Future loadHtmlString(String html, {String? baseUrl}) { + return webView.loadHtmlString(html, baseUrl: baseUrl); + } + + @override + Future loadFile(String absoluteFilePath) async { + await webView.loadFileUrl( + absoluteFilePath, + readAccessUrl: path.dirname(absoluteFilePath), + ); + } + @override Future clearCache() { return webView.configuration.webSiteDataStore.removeDataOfTypes( @@ -207,6 +223,124 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { ); } + @override + Future loadFlutterAsset(String key) async { + assert(key.isNotEmpty); + return webView.loadFlutterAsset(key); + } + + @override + Future loadUrl(String url, Map? headers) async { + final NSUrlRequest request = NSUrlRequest( + url: url, + allHttpHeaderFields: headers ?? {}, + ); + return webView.loadRequest(request); + } + + @override + Future loadRequest(WebViewRequest request) async { + if (!request.uri.hasScheme) { + throw ArgumentError('WebViewRequest#uri is required to have a scheme.'); + } + + final NSUrlRequest urlRequest = NSUrlRequest( + url: request.uri.toString(), + allHttpHeaderFields: request.headers, + httpMethod: describeEnum(request.method), + httpBody: request.body, + ); + + return webView.loadRequest(urlRequest); + } + + @override + Future canGoBack() => webView.canGoBack; + + @override + Future canGoForward() => webView.canGoForward; + + @override + Future goBack() => webView.goBack(); + + @override + Future goForward() => webView.goForward(); + + @override + Future reload() => webView.reload(); + + @override + Future evaluateJavascript(String javascript) async { + final Object? result = await webView.evaluateJavaScript(javascript); + // The legacy implementation of webview_flutter_wkwebview would convert + // objects to strings before returning them to Dart. This method attempts + // to converts Dart objects to Strings the way it is done in Objective-C + // to avoid breaking users expecting the same String format. + return _asObjectiveCString(result); + } + + @override + Future runJavascript(String javascript) async { + try { + await webView.evaluateJavaScript(javascript); + } on PlatformException catch (exception) { + // WebKit will throw an error when the type of the evaluated value is + // unsupported. This also goes for `null` and `undefined` on iOS 14+. For + // example, when running a void function. For ease of use, this specific + // error is ignored when no return value is expected. + // TODO(bparrishMines): Ensure the platform code includes the NSError in + // the FlutterError.details. + if (exception.details is! NSError || + exception.details.code != + WKErrorCode.javaScriptResultTypeIsUnsupported) { + rethrow; + } + } + } + + @override + Future runJavascriptReturningResult(String javascript) async { + final Object? result = await webView.evaluateJavaScript(javascript); + if (result == null) { + throw ArgumentError( + 'Result of JavaScript execution returned a `null` value. ' + 'Use `runJavascript` when expecting a null return value.', + ); + } + return result.toString(); + } + + @override + Future getTitle() => webView.title; + + @override + Future scrollTo(int x, int y) async { + webView.scrollView.contentOffset = Point( + x.toDouble(), + y.toDouble(), + ); + } + + @override + Future scrollBy(int x, int y) async { + await webView.scrollView.scrollBy(Point( + x.toDouble(), + y.toDouble(), + )); + } + + @override + Future getScrollX() async { + final Point offset = await webView.scrollView.contentOffset; + return offset.x.toInt(); + } + + @override + Future getScrollY() async { + final Point offset = await webView.scrollView.contentOffset; + return offset.y.toInt(); + } + @override Future updateSettings(WebSettings setting) async { if (setting.hasNavigationDelegate != null) { @@ -324,6 +458,35 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { errorType: errorType, ); } + + String _asObjectiveCString(Object? value, {bool inContainer = false}) { + if (value == null) { + // An NSNull inside an NSArray or NSDictionary is represented as a String + // differently than a nil. + if (inContainer) { + return '""'; + } + return '(null)'; + } else if (value is List) { + final List stringValues = []; + for (final Object? listValue in value) { + stringValues.add(_asObjectiveCString(listValue, inContainer: true)); + } + return '(${stringValues.join(',')})'; + } else if (value is Map) { + final List stringValues = []; + for (final MapEntry entry in value.entries) { + stringValues.add( + '${_asObjectiveCString(entry.key, inContainer: true)} ' + '= ' + '${_asObjectiveCString(entry.value, inContainer: true)}', + ); + } + return '{${stringValues.join(';')}}'; + } + + return value.toString(); + } } /// Handles constructing objects and calling static methods. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 91760df02c66..219d6ce49f4f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -18,6 +18,7 @@ flutter: dependencies: flutter: sdk: flutter + path: ^1.8.0 webview_flutter_platform_interface: ^1.8.0 dev_dependencies: diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index 358df898403a..e30e027734d0 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -3,19 +3,24 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:math'; +import 'dart:typed_data'; +import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; +import 'package:webview_flutter_wkwebview/src/ui_kit/ui_kit.dart'; import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart'; import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart'; import 'web_kit_webview_widget_test.mocks.dart'; @GenerateMocks([ + UIScrollView, WKNavigationDelegate, WKScriptMessageHandler, WKWebView, @@ -36,6 +41,7 @@ void main() { late MockWKUserContentController mockUserContentController; late MockWKWebViewConfiguration mockWebViewConfiguration; late MockWKUIDelegate mockUIDelegate; + late MockUIScrollView mockScrollView; late MockWKWebsiteDataStore mockWebsiteDataStore; late MockWKNavigationDelegate mockNavigationDelegate; @@ -49,6 +55,7 @@ void main() { mockWebViewConfiguration = MockWKWebViewConfiguration(); mockUserContentController = MockWKUserContentController(); mockUIDelegate = MockWKUIDelegate(); + mockScrollView = MockUIScrollView(); mockWebsiteDataStore = MockWKWebsiteDataStore(); mockNavigationDelegate = MockWKNavigationDelegate(); mockWebViewWidgetProxy = MockWebViewWidgetProxy(); @@ -61,6 +68,9 @@ void main() { when(mockWebViewConfiguration.userContentController).thenReturn( mockUserContentController, ); + + when(mockWebView.scrollView).thenReturn(mockScrollView); + when(mockWebViewConfiguration.webSiteDataStore).thenReturn( mockWebsiteDataStore, ); @@ -212,6 +222,345 @@ void main() { }); group('$WebKitWebViewPlatformController', () { + testWidgets('loadFile', (WidgetTester tester) async { + await buildWidget(tester); + + await testController.loadFile('/path/to/file.html'); + verify(mockWebView.loadFileUrl( + '/path/to/file.html', + readAccessUrl: '/path/to', + )); + }); + + testWidgets('loadFlutterAsset', (WidgetTester tester) async { + await buildWidget(tester); + + await testController.loadFlutterAsset('test_assets/index.html'); + verify(mockWebView.loadFlutterAsset('test_assets/index.html')); + }); + + testWidgets('loadHtmlString', (WidgetTester tester) async { + await buildWidget(tester); + + const String htmlString = 'Test data.'; + await testController.loadHtmlString(htmlString, baseUrl: 'baseUrl'); + + verify(mockWebView.loadHtmlString( + 'Test data.', + baseUrl: 'baseUrl', + )); + }); + + testWidgets('loadUrl', (WidgetTester tester) async { + await buildWidget(tester); + + await testController.loadUrl( + 'https://www.google.com', + {'a': 'header'}, + ); + + final NSUrlRequest request = verify(mockWebView.loadRequest(captureAny)) + .captured + .single as NSUrlRequest; + expect(request.url, 'https://www.google.com'); + expect(request.allHttpHeaderFields, {'a': 'header'}); + }); + + group('loadRequest', () { + testWidgets('Throws ArgumentError for empty scheme', + (WidgetTester tester) async { + await buildWidget(tester); + + expect( + () async => await testController.loadRequest( + WebViewRequest( + uri: Uri.parse('www.google.com'), + method: WebViewRequestMethod.get, + ), + ), + throwsA(const TypeMatcher())); + }); + + testWidgets('GET without headers', (WidgetTester tester) async { + await buildWidget(tester); + + await testController.loadRequest(WebViewRequest( + uri: Uri.parse('https://www.google.com'), + method: WebViewRequestMethod.get, + )); + + final NSUrlRequest request = + verify(mockWebView.loadRequest(captureAny)).captured.single + as NSUrlRequest; + expect(request.url, 'https://www.google.com'); + expect(request.allHttpHeaderFields, {}); + expect(request.httpMethod, 'get'); + }); + + testWidgets('GET with headers', (WidgetTester tester) async { + await buildWidget(tester); + + await testController.loadRequest(WebViewRequest( + uri: Uri.parse('https://www.google.com'), + method: WebViewRequestMethod.get, + headers: {'a': 'header'}, + )); + + final NSUrlRequest request = + verify(mockWebView.loadRequest(captureAny)).captured.single + as NSUrlRequest; + expect(request.url, 'https://www.google.com'); + expect(request.allHttpHeaderFields, {'a': 'header'}); + expect(request.httpMethod, 'get'); + }); + + testWidgets('POST without body', (WidgetTester tester) async { + await buildWidget(tester); + + await testController.loadRequest(WebViewRequest( + uri: Uri.parse('https://www.google.com'), + method: WebViewRequestMethod.post, + )); + + final NSUrlRequest request = + verify(mockWebView.loadRequest(captureAny)).captured.single + as NSUrlRequest; + expect(request.url, 'https://www.google.com'); + expect(request.httpMethod, 'post'); + }); + + testWidgets('POST with body', (WidgetTester tester) async { + await buildWidget(tester); + + await testController.loadRequest(WebViewRequest( + uri: Uri.parse('https://www.google.com'), + method: WebViewRequestMethod.post, + body: Uint8List.fromList('Test Body'.codeUnits))); + + final NSUrlRequest request = + verify(mockWebView.loadRequest(captureAny)).captured.single + as NSUrlRequest; + expect(request.url, 'https://www.google.com'); + expect(request.httpMethod, 'post'); + expect( + request.httpBody, + Uint8List.fromList('Test Body'.codeUnits), + ); + }); + }); + + testWidgets('canGoBack', (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.canGoBack).thenAnswer( + (_) => Future.value(false), + ); + expect(testController.canGoBack(), completion(false)); + }); + + testWidgets('canGoForward', (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.canGoForward).thenAnswer( + (_) => Future.value(true), + ); + expect(testController.canGoForward(), completion(true)); + }); + + testWidgets('goBack', (WidgetTester tester) async { + await buildWidget(tester); + + await testController.goBack(); + verify(mockWebView.goBack()); + }); + + testWidgets('goForward', (WidgetTester tester) async { + await buildWidget(tester); + + await testController.goForward(); + verify(mockWebView.goForward()); + }); + + testWidgets('reload', (WidgetTester tester) async { + await buildWidget(tester); + + await testController.reload(); + verify(mockWebView.reload()); + }); + + testWidgets('evaluateJavascript', (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + (_) => Future.value('returnString'), + ); + expect( + testController.evaluateJavascript('runJavaScript'), + completion('returnString'), + ); + }); + + testWidgets('evaluateJavascript with null return value', + (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + (_) => Future.value(), + ); + // The legacy implementation of webview_flutter_wkwebview would convert + // objects to strings before returning them to Dart. This verifies null + // is represented the way it is in Objective-C. + expect( + testController.evaluateJavascript('runJavaScript'), + completion('(null)'), + ); + }); + + testWidgets('evaluateJavascript with list return value', + (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + (_) => Future.value([1, 'string', null]), + ); + // The legacy implementation of webview_flutter_wkwebview would convert + // objects to strings before returning them to Dart. This verifies list + // is represented the way it is in Objective-C. + expect( + testController.evaluateJavascript('runJavaScript'), + completion('(1,string,"")'), + ); + }); + + testWidgets('evaluateJavascript with map return value', + (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + (_) => Future.value({ + 1: 'string', + null: null, + }), + ); + // The legacy implementation of webview_flutter_wkwebview would convert + // objects to strings before returning them to Dart. This verifies map + // is represented the way it is in Objective-C. + expect( + testController.evaluateJavascript('runJavaScript'), + completion('{1 = string;"" = ""}'), + ); + }); + + testWidgets('evaluateJavascript throws exception', + (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.evaluateJavaScript('runJavaScript')) + .thenThrow(Error()); + expect( + testController.evaluateJavascript('runJavaScript'), + throwsA(isA()), + ); + }); + + testWidgets('runJavascriptReturningResult', (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + (_) => Future.value('returnString'), + ); + expect( + testController.runJavascriptReturningResult('runJavaScript'), + completion('returnString'), + ); + }); + + testWidgets( + 'runJavascriptReturningResult throws error on null return value', + (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + (_) => Future.value(null), + ); + expect( + () => testController.runJavascriptReturningResult('runJavaScript'), + throwsArgumentError, + ); + }); + + testWidgets('runJavascript', (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + (_) => Future.value('returnString'), + ); + expect( + testController.runJavascript('runJavaScript'), + completes, + ); + }); + + testWidgets( + 'runJavascript ignores exception with unsupported javascript type', + (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.evaluateJavaScript('runJavaScript')) + .thenThrow(PlatformException( + code: '', + details: const NSError( + code: WKErrorCode.javaScriptResultTypeIsUnsupported, + domain: '', + localizedDescription: '', + ), + )); + expect( + testController.runJavascript('runJavaScript'), + completes, + ); + }); + + testWidgets('getTitle', (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.title) + .thenAnswer((_) => Future.value('Web Title')); + expect(testController.getTitle(), completion('Web Title')); + }); + + testWidgets('scrollTo', (WidgetTester tester) async { + await buildWidget(tester); + + await testController.scrollTo(2, 4); + verify(mockScrollView.contentOffset = const Point(2.0, 4.0)); + }); + + testWidgets('scrollBy', (WidgetTester tester) async { + await buildWidget(tester); + + await testController.scrollBy(2, 4); + verify(mockScrollView.scrollBy(const Point(2.0, 4.0))); + }); + + testWidgets('getScrollX', (WidgetTester tester) async { + await buildWidget(tester); + + when(mockScrollView.contentOffset).thenAnswer( + (_) => Future>.value(const Point(8.0, 16.0))); + expect(testController.getScrollX(), completion(8.0)); + }); + + testWidgets('getScrollY', (WidgetTester tester) async { + await buildWidget(tester); + + await buildWidget(tester); + + when(mockScrollView.contentOffset).thenAnswer( + (_) => Future>.value(const Point(8.0, 16.0))); + expect(testController.getScrollY(), completion(16.0)); + }); + testWidgets('clearCache', (WidgetTester tester) async { await buildWidget(tester); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index 32f1731701a9..f200870ca98b 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -2,19 +2,21 @@ // in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. // Do not manually edit this file. -import 'dart:async' as _i3; +import 'dart:async' as _i5; +import 'dart:math' as _i2; import 'package:mockito/mockito.dart' as _i1; import 'package:webview_flutter_platform_interface/src/types/javascript_channel.dart' - as _i6; -import 'package:webview_flutter_platform_interface/src/types/types.dart' as _i7; + as _i8; +import 'package:webview_flutter_platform_interface/src/types/types.dart' as _i9; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart' - as _i5; + as _i7; import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart' - as _i4; -import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart' as _i2; + as _i6; +import 'package:webview_flutter_wkwebview/src/ui_kit/ui_kit.dart' as _i4; +import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart' as _i3; import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart' - as _i8; + as _i10; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -26,37 +28,65 @@ import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart' // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types -class _FakeWKWebViewConfiguration_0 extends _i1.Fake - implements _i2.WKWebViewConfiguration {} +class _FakePoint_0 extends _i1.Fake implements _i2.Point {} + +class _FakeWKWebViewConfiguration_1 extends _i1.Fake + implements _i3.WKWebViewConfiguration {} + +class _FakeUIScrollView_2 extends _i1.Fake implements _i4.UIScrollView {} -class _FakeWKUserContentController_1 extends _i1.Fake - implements _i2.WKUserContentController {} +class _FakeWKUserContentController_3 extends _i1.Fake + implements _i3.WKUserContentController {} -class _FakeWKWebsiteDataStore_2 extends _i1.Fake - implements _i2.WKWebsiteDataStore {} +class _FakeWKWebsiteDataStore_4 extends _i1.Fake + implements _i3.WKWebsiteDataStore {} -class _FakeWKWebView_3 extends _i1.Fake implements _i2.WKWebView {} +class _FakeWKWebView_5 extends _i1.Fake implements _i3.WKWebView {} -class _FakeWKScriptMessageHandler_4 extends _i1.Fake - implements _i2.WKScriptMessageHandler {} +class _FakeWKScriptMessageHandler_6 extends _i1.Fake + implements _i3.WKScriptMessageHandler {} -class _FakeWKUIDelegate_5 extends _i1.Fake implements _i2.WKUIDelegate {} +class _FakeWKUIDelegate_7 extends _i1.Fake implements _i3.WKUIDelegate {} -class _FakeWKNavigationDelegate_6 extends _i1.Fake - implements _i2.WKNavigationDelegate {} +class _FakeWKNavigationDelegate_8 extends _i1.Fake + implements _i3.WKNavigationDelegate {} + +/// A class which mocks [UIScrollView]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockUIScrollView extends _i1.Mock implements _i4.UIScrollView { + MockUIScrollView() { + _i1.throwOnMissingStub(this); + } + + @override + _i5.Future<_i2.Point> get contentOffset => (super.noSuchMethod( + Invocation.getter(#contentOffset), + returnValue: Future<_i2.Point>.value(_FakePoint_0())) + as _i5.Future<_i2.Point>); + @override + set contentOffset(_i5.FutureOr<_i2.Point>? offset) => + super.noSuchMethod(Invocation.setter(#contentOffset, offset), + returnValueForMissingStub: null); + @override + _i5.Future scrollBy(_i2.Point? offset) => + (super.noSuchMethod(Invocation.method(#scrollBy, [offset]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); +} /// A class which mocks [WKNavigationDelegate]. /// /// See the documentation for Mockito's code generation for more information. class MockWKNavigationDelegate extends _i1.Mock - implements _i2.WKNavigationDelegate { + implements _i3.WKNavigationDelegate { MockWKNavigationDelegate() { _i1.throwOnMissingStub(this); } @override set didStartProvisionalNavigation( - void Function(_i2.WKWebView, String?)? + void Function(_i3.WKWebView, String?)? didStartProvisionalNavigation) => super.noSuchMethod( Invocation.setter( @@ -64,14 +94,14 @@ class MockWKNavigationDelegate extends _i1.Mock returnValueForMissingStub: null); @override set didFinishNavigation( - void Function(_i2.WKWebView, String?)? didFinishNavigation) => + void Function(_i3.WKWebView, String?)? didFinishNavigation) => super.noSuchMethod( Invocation.setter(#didFinishNavigation, didFinishNavigation), returnValueForMissingStub: null); @override set decidePolicyForNavigationAction( - _i3.Future<_i2.WKNavigationActionPolicy> Function( - _i2.WKWebView, _i2.WKNavigationAction)? + _i5.Future<_i3.WKNavigationActionPolicy> Function( + _i3.WKWebView, _i3.WKNavigationAction)? decidePolicyForNavigationAction) => super.noSuchMethod( Invocation.setter(#decidePolicyForNavigationAction, @@ -79,13 +109,13 @@ class MockWKNavigationDelegate extends _i1.Mock returnValueForMissingStub: null); @override set didFailNavigation( - void Function(_i2.WKWebView, _i4.NSError)? didFailNavigation) => + void Function(_i3.WKWebView, _i6.NSError)? didFailNavigation) => super.noSuchMethod( Invocation.setter(#didFailNavigation, didFailNavigation), returnValueForMissingStub: null); @override set didFailProvisionalNavigation( - void Function(_i2.WKWebView, _i4.NSError)? + void Function(_i3.WKWebView, _i6.NSError)? didFailProvisionalNavigation) => super.noSuchMethod( Invocation.setter( @@ -93,7 +123,7 @@ class MockWKNavigationDelegate extends _i1.Mock returnValueForMissingStub: null); @override set webViewWebContentProcessDidTerminate( - void Function(_i2.WKWebView)? webViewWebContentProcessDidTerminate) => + void Function(_i3.WKWebView)? webViewWebContentProcessDidTerminate) => super.noSuchMethod( Invocation.setter(#webViewWebContentProcessDidTerminate, webViewWebContentProcessDidTerminate), @@ -104,14 +134,14 @@ class MockWKNavigationDelegate extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockWKScriptMessageHandler extends _i1.Mock - implements _i2.WKScriptMessageHandler { + implements _i3.WKScriptMessageHandler { MockWKScriptMessageHandler() { _i1.throwOnMissingStub(this); } @override set didReceiveScriptMessage( - void Function(_i2.WKUserContentController, _i2.WKScriptMessage)? + void Function(_i3.WKUserContentController, _i3.WKScriptMessage)? didReceiveScriptMessage) => super.noSuchMethod( Invocation.setter(#didReceiveScriptMessage, didReceiveScriptMessage), @@ -121,60 +151,125 @@ class MockWKScriptMessageHandler extends _i1.Mock /// A class which mocks [WKWebView]. /// /// See the documentation for Mockito's code generation for more information. -class MockWKWebView extends _i1.Mock implements _i2.WKWebView { +class MockWKWebView extends _i1.Mock implements _i3.WKWebView { MockWKWebView() { _i1.throwOnMissingStub(this); } @override - _i2.WKWebViewConfiguration get configuration => + _i3.WKWebViewConfiguration get configuration => (super.noSuchMethod(Invocation.getter(#configuration), - returnValue: _FakeWKWebViewConfiguration_0()) - as _i2.WKWebViewConfiguration); + returnValue: _FakeWKWebViewConfiguration_1()) + as _i3.WKWebViewConfiguration); + @override + _i4.UIScrollView get scrollView => + (super.noSuchMethod(Invocation.getter(#scrollView), + returnValue: _FakeUIScrollView_2()) as _i4.UIScrollView); @override - set uiDelegate(_i2.WKUIDelegate? delegate) => + set uiDelegate(_i3.WKUIDelegate? delegate) => super.noSuchMethod(Invocation.setter(#uiDelegate, delegate), returnValueForMissingStub: null); @override - set navigationDelegate(_i2.WKNavigationDelegate? delegate) => + set navigationDelegate(_i3.WKNavigationDelegate? delegate) => super.noSuchMethod(Invocation.setter(#navigationDelegate, delegate), returnValueForMissingStub: null); @override - _i3.Future get url => (super.noSuchMethod(Invocation.getter(#url), - returnValue: Future.value()) as _i3.Future); + _i5.Future get url => (super.noSuchMethod(Invocation.getter(#url), + returnValue: Future.value()) as _i5.Future); + @override + _i5.Future get canGoBack => + (super.noSuchMethod(Invocation.getter(#canGoBack), + returnValue: Future.value(false)) as _i5.Future); + @override + _i5.Future get canGoForward => + (super.noSuchMethod(Invocation.getter(#canGoForward), + returnValue: Future.value(false)) as _i5.Future); @override - _i3.Future loadRequest(_i4.NSUrlRequest? request) => + _i5.Future get title => + (super.noSuchMethod(Invocation.getter(#title), + returnValue: Future.value()) as _i5.Future); + @override + _i5.Future get estimatedProgress => + (super.noSuchMethod(Invocation.getter(#estimatedProgress), + returnValue: Future.value(0.0)) as _i5.Future); + @override + set allowsBackForwardNavigationGestures(bool? allow) => super.noSuchMethod( + Invocation.setter(#allowsBackForwardNavigationGestures, allow), + returnValueForMissingStub: null); + @override + set customUserAgent(String? userAgent) => + super.noSuchMethod(Invocation.setter(#customUserAgent, userAgent), + returnValueForMissingStub: null); + @override + _i5.Future loadRequest(_i6.NSUrlRequest? request) => (super.noSuchMethod(Invocation.method(#loadRequest, [request]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i3.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future loadHtmlString(String? string, {String? baseUrl}) => + (super.noSuchMethod( + Invocation.method(#loadHtmlString, [string], {#baseUrl: baseUrl}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future loadFileUrl(String? url, {String? readAccessUrl}) => + (super.noSuchMethod( + Invocation.method( + #loadFileUrl, [url], {#readAccessUrl: readAccessUrl}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future loadFlutterAsset(String? key) => + (super.noSuchMethod(Invocation.method(#loadFlutterAsset, [key]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future goBack() => + (super.noSuchMethod(Invocation.method(#goBack, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future goForward() => + (super.noSuchMethod(Invocation.method(#goForward, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future reload() => + (super.noSuchMethod(Invocation.method(#reload, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future evaluateJavaScript(String? javaScriptString) => (super + .noSuchMethod(Invocation.method(#evaluateJavaScript, [javaScriptString]), + returnValue: Future.value()) as _i5.Future); } /// A class which mocks [WKWebViewConfiguration]. /// /// See the documentation for Mockito's code generation for more information. class MockWKWebViewConfiguration extends _i1.Mock - implements _i2.WKWebViewConfiguration { + implements _i3.WKWebViewConfiguration { MockWKWebViewConfiguration() { _i1.throwOnMissingStub(this); } @override - _i2.WKUserContentController get userContentController => + _i3.WKUserContentController get userContentController => (super.noSuchMethod(Invocation.getter(#userContentController), - returnValue: _FakeWKUserContentController_1()) - as _i2.WKUserContentController); + returnValue: _FakeWKUserContentController_3()) + as _i3.WKUserContentController); @override set userContentController( - _i2.WKUserContentController? _userContentController) => + _i3.WKUserContentController? _userContentController) => super.noSuchMethod( Invocation.setter(#userContentController, _userContentController), returnValueForMissingStub: null); @override - _i2.WKWebsiteDataStore get webSiteDataStore => + _i3.WKWebsiteDataStore get webSiteDataStore => (super.noSuchMethod(Invocation.getter(#webSiteDataStore), - returnValue: _FakeWKWebsiteDataStore_2()) as _i2.WKWebsiteDataStore); + returnValue: _FakeWKWebsiteDataStore_4()) as _i3.WKWebsiteDataStore); @override - set webSiteDataStore(_i2.WKWebsiteDataStore? websiteDataStore) => + set webSiteDataStore(_i3.WKWebsiteDataStore? websiteDataStore) => super.noSuchMethod(Invocation.setter(#webSiteDataStore, websiteDataStore), returnValueForMissingStub: null); @override @@ -183,7 +278,7 @@ class MockWKWebViewConfiguration extends _i1.Mock returnValueForMissingStub: null); @override set mediaTypesRequiringUserActionForPlayback( - Set<_i2.WKAudiovisualMediaType>? types) => + Set<_i3.WKAudiovisualMediaType>? types) => super.noSuchMethod( Invocation.setter(#mediaTypesRequiringUserActionForPlayback, types), returnValueForMissingStub: null); @@ -193,31 +288,31 @@ class MockWKWebViewConfiguration extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockWKWebsiteDataStore extends _i1.Mock - implements _i2.WKWebsiteDataStore { + implements _i3.WKWebsiteDataStore { MockWKWebsiteDataStore() { _i1.throwOnMissingStub(this); } @override - _i3.Future removeDataOfTypes( - Set<_i2.WKWebsiteDataTypes>? dataTypes, DateTime? since) => + _i5.Future removeDataOfTypes( + Set<_i3.WKWebsiteDataTypes>? dataTypes, DateTime? since) => (super.noSuchMethod( Invocation.method(#removeDataOfTypes, [dataTypes, since]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i3.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [WKUIDelegate]. /// /// See the documentation for Mockito's code generation for more information. -class MockWKUIDelegate extends _i1.Mock implements _i2.WKUIDelegate { +class MockWKUIDelegate extends _i1.Mock implements _i3.WKUIDelegate { MockWKUIDelegate() { _i1.throwOnMissingStub(this); } @override set onCreateWebView( - void Function(_i2.WKWebViewConfiguration, _i2.WKNavigationAction)? + void Function(_i3.WKWebViewConfiguration, _i3.WKNavigationAction)? onCreateeWebView) => super.noSuchMethod(Invocation.setter(#onCreateWebView, onCreateeWebView), returnValueForMissingStub: null); @@ -227,61 +322,61 @@ class MockWKUIDelegate extends _i1.Mock implements _i2.WKUIDelegate { /// /// See the documentation for Mockito's code generation for more information. class MockWKUserContentController extends _i1.Mock - implements _i2.WKUserContentController { + implements _i3.WKUserContentController { MockWKUserContentController() { _i1.throwOnMissingStub(this); } @override - _i3.Future addScriptMessageHandler( - _i2.WKScriptMessageHandler? handler, String? name) => + _i5.Future addScriptMessageHandler( + _i3.WKScriptMessageHandler? handler, String? name) => (super.noSuchMethod( Invocation.method(#addScriptMessageHandler, [handler, name]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i3.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i3.Future removeScriptMessageHandler(String? name) => (super + _i5.Future removeScriptMessageHandler(String? name) => (super .noSuchMethod(Invocation.method(#removeScriptMessageHandler, [name]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i3.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i3.Future removeAllScriptMessageHandlers() => (super.noSuchMethod( + _i5.Future removeAllScriptMessageHandlers() => (super.noSuchMethod( Invocation.method(#removeAllScriptMessageHandlers, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i3.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i3.Future addUserScript(_i2.WKUserScript? userScript) => + _i5.Future addUserScript(_i3.WKUserScript? userScript) => (super.noSuchMethod(Invocation.method(#addUserScript, [userScript]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i3.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i3.Future removeAllUserScripts() => + _i5.Future removeAllUserScripts() => (super.noSuchMethod(Invocation.method(#removeAllUserScripts, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i3.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [JavascriptChannelRegistry]. /// /// See the documentation for Mockito's code generation for more information. class MockJavascriptChannelRegistry extends _i1.Mock - implements _i5.JavascriptChannelRegistry { + implements _i7.JavascriptChannelRegistry { MockJavascriptChannelRegistry() { _i1.throwOnMissingStub(this); } @override - Map get channels => + Map get channels => (super.noSuchMethod(Invocation.getter(#channels), - returnValue: {}) - as Map); + returnValue: {}) + as Map); @override void onJavascriptChannelMessage(String? channel, String? message) => super.noSuchMethod( Invocation.method(#onJavascriptChannelMessage, [channel, message]), returnValueForMissingStub: null); @override - void updateJavascriptChannelsFromSet(Set<_i6.JavascriptChannel>? channels) => + void updateJavascriptChannelsFromSet(Set<_i8.JavascriptChannel>? channels) => super.noSuchMethod( Invocation.method(#updateJavascriptChannelsFromSet, [channels]), returnValueForMissingStub: null); @@ -291,17 +386,17 @@ class MockJavascriptChannelRegistry extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockWebViewPlatformCallbacksHandler extends _i1.Mock - implements _i5.WebViewPlatformCallbacksHandler { + implements _i7.WebViewPlatformCallbacksHandler { MockWebViewPlatformCallbacksHandler() { _i1.throwOnMissingStub(this); } @override - _i3.FutureOr onNavigationRequest({String? url, bool? isForMainFrame}) => + _i5.FutureOr onNavigationRequest({String? url, bool? isForMainFrame}) => (super.noSuchMethod( Invocation.method(#onNavigationRequest, [], {#url: url, #isForMainFrame: isForMainFrame}), - returnValue: Future.value(false)) as _i3.FutureOr); + returnValue: Future.value(false)) as _i5.FutureOr); @override void onPageStarted(String? url) => super.noSuchMethod(Invocation.method(#onPageStarted, [url]), @@ -315,7 +410,7 @@ class MockWebViewPlatformCallbacksHandler extends _i1.Mock super.noSuchMethod(Invocation.method(#onProgress, [progress]), returnValueForMissingStub: null); @override - void onWebResourceError(_i7.WebResourceError? error) => + void onWebResourceError(_i9.WebResourceError? error) => super.noSuchMethod(Invocation.method(#onWebResourceError, [error]), returnValueForMissingStub: null); } @@ -324,26 +419,26 @@ class MockWebViewPlatformCallbacksHandler extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockWebViewWidgetProxy extends _i1.Mock - implements _i8.WebViewWidgetProxy { + implements _i10.WebViewWidgetProxy { MockWebViewWidgetProxy() { _i1.throwOnMissingStub(this); } @override - _i2.WKWebView createWebView(_i2.WKWebViewConfiguration? configuration) => + _i3.WKWebView createWebView(_i3.WKWebViewConfiguration? configuration) => (super.noSuchMethod(Invocation.method(#createWebView, [configuration]), - returnValue: _FakeWKWebView_3()) as _i2.WKWebView); + returnValue: _FakeWKWebView_5()) as _i3.WKWebView); @override - _i2.WKScriptMessageHandler createScriptMessageHandler() => + _i3.WKScriptMessageHandler createScriptMessageHandler() => (super.noSuchMethod(Invocation.method(#createScriptMessageHandler, []), - returnValue: _FakeWKScriptMessageHandler_4()) - as _i2.WKScriptMessageHandler); + returnValue: _FakeWKScriptMessageHandler_6()) + as _i3.WKScriptMessageHandler); @override - _i2.WKUIDelegate createUIDelgate() => + _i3.WKUIDelegate createUIDelgate() => (super.noSuchMethod(Invocation.method(#createUIDelgate, []), - returnValue: _FakeWKUIDelegate_5()) as _i2.WKUIDelegate); + returnValue: _FakeWKUIDelegate_7()) as _i3.WKUIDelegate); @override - _i2.WKNavigationDelegate createNavigationDelegate() => (super.noSuchMethod( + _i3.WKNavigationDelegate createNavigationDelegate() => (super.noSuchMethod( Invocation.method(#createNavigationDelegate, []), - returnValue: _FakeWKNavigationDelegate_6()) as _i2.WKNavigationDelegate); + returnValue: _FakeWKNavigationDelegate_8()) as _i3.WKNavigationDelegate); } From c9668c4dde62c5291b84b38d6a196a3416ed7830 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 24 Mar 2022 12:08:58 -0700 Subject: [PATCH 319/600] [webview_flutter_wkwebview] Add progress tracking support for flutter/flutter#93732 (#5101) --- .../lib/src/foundation/foundation.dart | 110 ++++++++++++++++++ .../lib/src/web_kit/web_kit.dart | 12 +- .../lib/src/web_kit_webview_widget.dart | 26 +++++ .../test/src/web_kit_webview_widget_test.dart | 27 +++++ .../web_kit_webview_widget_test.mocks.dart | 25 ++++ 5 files changed, 199 insertions(+), 1 deletion(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart index dc933a072b7f..2d80ea20b636 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart @@ -6,6 +6,86 @@ import 'dart:typed_data'; import 'package:flutter/foundation.dart'; +/// The values that can be returned in a change map. +/// +/// Wraps [NSKeyValueObservingOptions](https://developer.apple.com/documentation/foundation/nskeyvalueobservingoptions?language=objc). +enum NSKeyValueObservingOptions { + /// Indicates that the change map should provide the new attribute value, if applicable. + /// + /// See https://developer.apple.com/documentation/foundation/nskeyvalueobservingoptions/nskeyvalueobservingoptionnew?language=objc. + newValue, + + /// Indicates that the change map should contain the old attribute value, if applicable. + /// + /// See https://developer.apple.com/documentation/foundation/nskeyvalueobservingoptions/nskeyvalueobservingoptionold?language=objc. + oldValue, + + /// Indicates a notification should be sent to the observer immediately. + /// + /// See https://developer.apple.com/documentation/foundation/nskeyvalueobservingoptions/nskeyvalueobservingoptioninitial?language=objc. + initialValue, + + /// Whether separate notifications should be sent to the observer before and after each change. + /// + /// See https://developer.apple.com/documentation/foundation/nskeyvalueobservingoptions/nskeyvalueobservingoptionprior?language=objc. + priorNotification, +} + +/// The kinds of changes that can be observed. +/// +/// Wraps [NSKeyValueChange](https://developer.apple.com/documentation/foundation/nskeyvaluechange?language=objc). +enum NSKeyValueChange { + /// Indicates that the value of the observed key path was set to a new value. + /// + /// See https://developer.apple.com/documentation/foundation/nskeyvaluechange/nskeyvaluechangesetting?language=objc. + setting, + + /// Indicates that an object has been inserted into the to-many relationship that is being observed. + /// + /// See https://developer.apple.com/documentation/foundation/nskeyvaluechange/nskeyvaluechangeinsertion?language=objc. + insertion, + + /// Indicates that an object has been removed from the to-many relationship that is being observed. + /// + /// See https://developer.apple.com/documentation/foundation/nskeyvaluechange/nskeyvaluechangeremoval?language=objc. + removal, + + /// Indicates that an object has been replaced in the to-many relationship that is being observed. + /// + /// See https://developer.apple.com/documentation/foundation/nskeyvaluechange/nskeyvaluechangereplacement?language=objc. + replacement, +} + +/// The keys that can appear in the change map. +/// +/// Wraps [NSKeyValueChangeKey](https://developer.apple.com/documentation/foundation/nskeyvaluechangekey?language=objc). +enum NSKeyValueChangeKey { + /// Indicates changes made in a collection. + /// + /// See https://developer.apple.com/documentation/foundation/nskeyvaluechangeindexeskey?language=objc. + indexes, + + /// Indicates what sort of change has occurred. + /// + /// See https://developer.apple.com/documentation/foundation/nskeyvaluechangekindkey?language=objc. + kind, + + /// Indicates the new value for the attribute. + /// + /// See https://developer.apple.com/documentation/foundation/nskeyvaluechangenewkey?language=objc. + newValue, + + /// Indicates a notification is sent prior to a change. + /// + /// See https://developer.apple.com/documentation/foundation/nskeyvaluechangenotificationispriorkey?language=objc. + notificationIsPrior, + + /// Indicates the value of this key is the value before the attribute was changed. + /// + /// https://developer.apple.com/documentation/foundation/nskeyvaluechangeoldkey?language=objc. + oldValue, +} + /// A URL load request that is independent of protocol or URL scheme. /// /// Wraps [NSUrlRequest](https://developer.apple.com/documentation/foundation/nsurlrequest?language=objc). @@ -57,3 +137,33 @@ class NSError { /// A string containing the localized description of the error. final String localizedDescription; } + +/// The root class of most Objective-C class hierarchies. +class NSObject { + /// Registers the observer object to receive KVO notifications. + Future addObserver( + NSObject observer, { + required String keyPath, + required Set options, + }) { + assert(options.isNotEmpty); + throw UnimplementedError(); + } + + /// Stops the observer object from receiving change notifications for the property. + Future removeObserver(NSObject observer, {required String keyPath}) { + throw UnimplementedError(); + } + + /// Informs the observing object when the value at the specified key path has changed. + set observeValue( + void Function( + String keyPath, + NSObject object, + Map change, + )? + observeValue, + ) { + throw UnimplementedError(); + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart index cd7e4a9aadcb..94b1533fb3b2 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart @@ -423,7 +423,7 @@ class WKNavigationDelegate { /// Object that displays interactive web content, such as for an in-app browser. /// /// Wraps [WKWebView](https://developer.apple.com/documentation/webkit/wkwebview?language=objc). -class WKWebView { +class WKWebView extends NSObject { /// Constructs a [WKWebView]. /// /// [configuration] contains the configuration details for the web view. This @@ -469,6 +469,16 @@ class WKWebView { throw UnimplementedError(); } + /// An estimate of what fraction of the current navigation has been loaded. + /// + /// This value ranges from 0.0 to 1.0. + /// + /// This method represents + /// [WKWebView.estimatedProgress](https://developer.apple.com/documentation/webkit/wkwebview/1415007-estimatedprogress?language=objc). + Future getEstimatedProgress() { + throw UnimplementedError(); + } + /// Loads the web content referenced by the specified URL request object and navigates to it. /// /// Use this method to load a page from a local or network-based URL. For diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 67ea6a936e42..58c279cd56ba 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -346,6 +346,9 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { if (setting.hasNavigationDelegate != null) { _setHasNavigationDelegate(setting.hasNavigationDelegate!); } + if (setting.hasProgressTracking != null) { + _setHasProgressTracking(setting.hasProgressTracking!); + } } @override @@ -430,6 +433,29 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { } } + Future _setHasProgressTracking(bool hasProgressTracking) { + if (hasProgressTracking) { + webView.observeValue = ( + String keyPath, + NSObject object, + Map change, + ) { + final double progress = change[NSKeyValueChangeKey.newValue]! as double; + callbacksHandler.onProgress((progress * 100).round()); + }; + return webView.addObserver( + webView, + keyPath: 'estimatedProgress', + options: { + NSKeyValueObservingOptions.newValue, + }, + ); + } else { + webView.observeValue = null; + return webView.removeObserver(webView, keyPath: 'estimatedProgress'); + } + } + static WebResourceError _toWebResourceError(NSError error) { WebResourceErrorType? errorType; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index e30e027734d0..9d8c810b9edc 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -799,6 +799,33 @@ void main() { isForMainFrame: false, )); }); + + testWidgets('onProgress', (WidgetTester tester) async { + await buildWidget(tester, hasProgressTracking: true); + final dynamic observeValue = + verify(mockWebView.observeValue = captureAny).captured.single + as void Function( + String keyPath, + NSObject object, + Map change, + ); + + verify(mockWebView.addObserver( + mockWebView, + keyPath: 'estimatedProgress', + options: { + NSKeyValueObservingOptions.newValue, + }, + )); + + observeValue( + 'estimatedProgress', + mockWebView, + {NSKeyValueChangeKey.newValue: 0.32}, + ); + + verify(mockCallbacksHandler.onProgress(32)); + }); }); group('$JavascriptChannelRegistry', () { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index f200870ca98b..c63aef9026a3 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -201,6 +201,17 @@ class MockWKWebView extends _i1.Mock implements _i3.WKWebView { super.noSuchMethod(Invocation.setter(#customUserAgent, userAgent), returnValueForMissingStub: null); @override + set observeValue( + void Function( + String, _i6.NSObject, Map<_i6.NSKeyValueChangeKey, Object?>)? + observeValue) => + super.noSuchMethod(Invocation.setter(#observeValue, observeValue), + returnValueForMissingStub: null); + @override + _i5.Future getEstimatedProgress() => + (super.noSuchMethod(Invocation.method(#getEstimatedProgress, []), + returnValue: Future.value(0.0)) as _i5.Future); + @override _i5.Future loadRequest(_i6.NSUrlRequest? request) => (super.noSuchMethod(Invocation.method(#loadRequest, [request]), returnValue: Future.value(), @@ -242,6 +253,20 @@ class MockWKWebView extends _i1.Mock implements _i3.WKWebView { _i5.Future evaluateJavaScript(String? javaScriptString) => (super .noSuchMethod(Invocation.method(#evaluateJavaScript, [javaScriptString]), returnValue: Future.value()) as _i5.Future); + @override + _i5.Future addObserver(_i6.NSObject? observer, + {String? keyPath, Set<_i6.NSKeyValueObservingOptions>? options}) => + (super.noSuchMethod( + Invocation.method( + #addObserver, [observer], {#keyPath: keyPath, #options: options}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future removeObserver(_i6.NSObject? observer, {String? keyPath}) => + (super.noSuchMethod( + Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [WKWebViewConfiguration]. From bb9dc2254b70a0b0bc2d797c7cc067105bf602f8 Mon Sep 17 00:00:00 2001 From: Abdelaziz Mahdy Date: Thu, 24 Mar 2022 22:15:12 +0200 Subject: [PATCH 320/600] [Video_player] Upgraded to exoPlayer 2.17.0 (#5011) --- .../video_player_android/CHANGELOG.md | 4 ++++ .../video_player_android/android/build.gradle | 8 +++---- .../plugins/videoplayer/VideoPlayer.java | 23 +++++++++---------- .../video_player_android/pubspec.yaml | 2 +- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index 774589c05f84..1f0f06f739b2 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.2 + +* Updates ExoPlayer to 2.17.0. + ## 2.3.1 * Renames internal method channels to avoid potential confusion with the diff --git a/packages/video_player/video_player_android/android/build.gradle b/packages/video_player/video_player_android/android/build.gradle index 6e6e8c792150..e565a9364bd8 100644 --- a/packages/video_player/video_player_android/android/build.gradle +++ b/packages/video_player/video_player_android/android/build.gradle @@ -43,10 +43,10 @@ android { } dependencies { - implementation 'com.google.android.exoplayer:exoplayer-core:2.14.1' - implementation 'com.google.android.exoplayer:exoplayer-hls:2.14.1' - implementation 'com.google.android.exoplayer:exoplayer-dash:2.14.1' - implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.14.1' + implementation 'com.google.android.exoplayer:exoplayer-core:2.17.0' + implementation 'com.google.android.exoplayer:exoplayer-hls:2.17.0' + implementation 'com.google.android.exoplayer:exoplayer-dash:2.17.0' + implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.17.0' testImplementation 'junit:junit:4.12' testImplementation 'org.mockito:mockito-inline:3.9.0' } diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java index 33593267338c..dc7c88144583 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java @@ -12,13 +12,13 @@ import android.view.Surface; import androidx.annotation.NonNull; import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.ExoPlaybackException; +import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.MediaItem; +import com.google.android.exoplayer2.PlaybackException; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player.Listener; -import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.audio.AudioAttributes; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.ProgressiveMediaSource; @@ -28,7 +28,7 @@ import com.google.android.exoplayer2.source.smoothstreaming.DefaultSsChunkSource; import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource; import com.google.android.exoplayer2.upstream.DataSource; -import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; +import com.google.android.exoplayer2.upstream.DefaultDataSource; import com.google.android.exoplayer2.upstream.DefaultHttpDataSource; import com.google.android.exoplayer2.util.Util; import io.flutter.plugin.common.EventChannel; @@ -45,7 +45,7 @@ final class VideoPlayer { private static final String FORMAT_HLS = "hls"; private static final String FORMAT_OTHER = "other"; - private SimpleExoPlayer exoPlayer; + private ExoPlayer exoPlayer; private Surface surface; @@ -71,10 +71,9 @@ final class VideoPlayer { this.textureEntry = textureEntry; this.options = options; - exoPlayer = new SimpleExoPlayer.Builder(context).build(); + exoPlayer = new ExoPlayer.Builder(context).build(); Uri uri = Uri.parse(dataSource); - DataSource.Factory dataSourceFactory; if (isHTTP(uri)) { DefaultHttpDataSource.Factory httpDataSourceFactory = @@ -87,7 +86,7 @@ final class VideoPlayer { } dataSourceFactory = httpDataSourceFactory; } else { - dataSourceFactory = new DefaultDataSourceFactory(context, "ExoPlayer"); + dataSourceFactory = new DefaultDataSource.Factory(context); } MediaSource mediaSource = buildMediaSource(uri, dataSourceFactory, formatHint, context); @@ -107,6 +106,7 @@ private static boolean isHTTP(Uri uri) { private MediaSource buildMediaSource( Uri uri, DataSource.Factory mediaDataSourceFactory, String formatHint, Context context) { + int type; if (formatHint == null) { type = Util.inferContentType(uri.getLastPathSegment()); @@ -133,12 +133,12 @@ private MediaSource buildMediaSource( case C.TYPE_SS: return new SsMediaSource.Factory( new DefaultSsChunkSource.Factory(mediaDataSourceFactory), - new DefaultDataSourceFactory(context, null, mediaDataSourceFactory)) + new DefaultDataSource.Factory(context, mediaDataSourceFactory)) .createMediaSource(MediaItem.fromUri(uri)); case C.TYPE_DASH: return new DashMediaSource.Factory( new DefaultDashChunkSource.Factory(mediaDataSourceFactory), - new DefaultDataSourceFactory(context, null, mediaDataSourceFactory)) + new DefaultDataSource.Factory(context, mediaDataSourceFactory)) .createMediaSource(MediaItem.fromUri(uri)); case C.TYPE_HLS: return new HlsMediaSource.Factory(mediaDataSourceFactory) @@ -207,7 +207,7 @@ public void onPlaybackStateChanged(final int playbackState) { } @Override - public void onPlayerError(final ExoPlaybackException error) { + public void onPlayerError(final PlaybackException error) { setBuffering(false); if (eventSink != null) { eventSink.error("VideoError", "Video player had error " + error, null); @@ -225,8 +225,7 @@ void sendBufferingUpdate() { eventSink.success(event); } - @SuppressWarnings("deprecation") - private static void setAudioAttributes(SimpleExoPlayer exoPlayer, boolean isMixMode) { + private static void setAudioAttributes(ExoPlayer exoPlayer, boolean isMixMode) { exoPlayer.setAudioAttributes( new AudioAttributes.Builder().setContentType(C.CONTENT_TYPE_MOVIE).build(), !isMixMode); } diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index 15c063f83d40..aa288ed71eac 100644 --- a/packages/video_player/video_player_android/pubspec.yaml +++ b/packages/video_player/video_player_android/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_android description: Android implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.1 +version: 2.3.2 environment: sdk: ">=2.14.0 <3.0.0" From 62be2dcb94ac52e1d5808e2c8a171480136371e4 Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Thu, 24 Mar 2022 15:35:09 -0700 Subject: [PATCH 321/600] [tool] Fix typo in `publish-plugin` readme (#5107) --- script/tool/CHANGELOG.md | 4 ++++ script/tool/README.md | 17 ++++++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 214eb68b2f21..45f7f527e22c 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +- Updates `publish-plugin` command documentation. + ## 0.8.2 - Adds a new `custom-test` command. diff --git a/script/tool/README.md b/script/tool/README.md index 265d3868fc37..05be917014f2 100644 --- a/script/tool/README.md +++ b/script/tool/README.md @@ -109,11 +109,18 @@ dart run ./script/tool/bin/flutter_plugin_tools.dart native-test --macos --packa ### Publish a Release -``sh +**Releases are automated for `flutter/plugins` and `flutter/packages`.** + +The manual procedure described here is _deprecated_, and should only be used when +the automated process fails. Please, read +[Releasing a Plugin or Package](https://github.com/flutter/flutter/wiki/Releasing-a-Plugin-or-Package) +on the Flutter Wiki first. + +```sh cd git checkout -dart run ./script/tool/bin/flutter_plugin_tools.dart publish-plugin --package -`` +dart run ./script/tool/bin/flutter_plugin_tools.dart publish-plugin --packages +``` By default the tool tries to push tags to the `upstream` remote, but some additional settings can be configured. Run `dart run ./script/tool/bin/flutter_plugin_tools.dart @@ -127,10 +134,6 @@ _everything_, including untracked or uncommitted files in version control. directory and refuse to publish if there are any mismatched files with version control present. -Automated publishing is under development. Follow -[flutter/flutter#27258](https://github.com/flutter/flutter/issues/27258) -for updates. - ## Updating the Tool For flutter/plugins, just changing the source here is all that's needed. From 794fa825043173a38f2ae0bcd229d3d394f5b686 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Mon, 28 Mar 2022 23:57:45 -0700 Subject: [PATCH 322/600] [espresso] [video_player] Update espresso guava version (#5112) --- packages/espresso/CHANGELOG.md | 3 ++- packages/espresso/android/build.gradle | 2 +- packages/espresso/pubspec.yaml | 2 +- packages/image_picker/image_picker/example/pubspec.yaml | 4 +++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/espresso/CHANGELOG.md b/packages/espresso/CHANGELOG.md index 3f19984b9437..a141b23bd4f0 100644 --- a/packages/espresso/CHANGELOG.md +++ b/packages/espresso/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 0.2.0 * Updates compileSdkVersion to 31. +* **Breaking Change** Update guava version to latest stable: `com.google.guava:guava:31.1-android`. ## 0.1.0+4 diff --git a/packages/espresso/android/build.gradle b/packages/espresso/android/build.gradle index e5a042095c2f..097e53f1c90b 100644 --- a/packages/espresso/android/build.gradle +++ b/packages/espresso/android/build.gradle @@ -49,7 +49,7 @@ android { } dependencies { - implementation 'com.google.guava:guava:28.1-android' + implementation 'com.google.guava:guava:31.1-android' implementation 'com.squareup.okhttp3:okhttp:3.12.1' implementation 'com.google.code.gson:gson:2.8.6' androidTestImplementation 'org.hamcrest:hamcrest:2.2' diff --git a/packages/espresso/pubspec.yaml b/packages/espresso/pubspec.yaml index 1836e7afb575..0bbf3c01158e 100644 --- a/packages/espresso/pubspec.yaml +++ b/packages/espresso/pubspec.yaml @@ -3,7 +3,7 @@ description: Java classes for testing Flutter apps using Espresso. Allows driving Flutter widgets from a native Espresso test. repository: https://github.com/flutter/plugins/tree/main/packages/espresso issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+espresso%22 -version: 0.1.0+4 +version: 0.2.0 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/image_picker/image_picker/example/pubspec.yaml b/packages/image_picker/image_picker/example/pubspec.yaml index 28b37197d8ff..92db04501f83 100755 --- a/packages/image_picker/image_picker/example/pubspec.yaml +++ b/packages/image_picker/image_picker/example/pubspec.yaml @@ -20,7 +20,9 @@ dependencies: video_player: ^2.1.4 dev_dependencies: - espresso: ^0.1.0+2 + # TODO(bparrishMines): Change version to `0.2.0` once published. + espresso: + path: ../../../espresso flutter_driver: sdk: flutter integration_test: From 972e942c00bc9847c4823c438572d52edce014eb Mon Sep 17 00:00:00 2001 From: zuvola Date: Tue, 29 Mar 2022 18:30:14 +0900 Subject: [PATCH 323/600] [camera] Fixed a crash when streaming on iOS (#4520) --- packages/camera/camera/CHANGELOG.md | 4 + .../ios/Runner.xcodeproj/project.pbxproj | 4 + .../example/ios/RunnerTests/StreamingTest.m | 85 +++++++++++++++++++ .../camera/camera/ios/Classes/CameraPlugin.m | 3 + packages/camera/camera/ios/Classes/FLTCam.h | 8 ++ packages/camera/camera/ios/Classes/FLTCam.m | 39 ++++++--- .../camera/camera/ios/Classes/FLTCam_Test.h | 20 +++++ .../camera/lib/src/camera_controller.dart | 7 ++ packages/camera/camera/pubspec.yaml | 2 +- 9 files changed, 159 insertions(+), 13 deletions(-) create mode 100644 packages/camera/camera/example/ios/RunnerTests/StreamingTest.m diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index d19b500af2d7..03b9293b6893 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.4+18 + +* Fixes a crash in iOS when streaming on low-performance devices. + ## 0.9.4+17 * Removes obsolete information from README, and adds OS support table. diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj index 6c171505a8ca..37f56d0ed52e 100644 --- a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ 25C3919135C3D981E6F800D0 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1944D8072499F3B5E7653D44 /* libPods-RunnerTests.a */; }; 334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03BB767226653ABE00CE5A93 /* CameraOrientationTests.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 788A065A27B0E02900533D74 /* StreamingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 788A065927B0E02900533D74 /* StreamingTest.m */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -70,6 +71,7 @@ 1944D8072499F3B5E7653D44 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 59848A7CA98C1FADF8840207 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 788A065927B0E02900533D74 /* StreamingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StreamingTest.m; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -142,6 +144,7 @@ F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */, E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */, E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */, + 788A065927B0E02900533D74 /* StreamingTest.m */, ); path = RunnerTests; sourceTree = ""; @@ -416,6 +419,7 @@ E0CDBAC227CD9729002561D9 /* CameraTestUtils.m in Sources */, 334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */, E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */, + 788A065A27B0E02900533D74 /* StreamingTest.m in Sources */, E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */, E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */, E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */, diff --git a/packages/camera/camera/example/ios/RunnerTests/StreamingTest.m b/packages/camera/camera/example/ios/RunnerTests/StreamingTest.m new file mode 100644 index 000000000000..1843cce12152 --- /dev/null +++ b/packages/camera/camera/example/ios/RunnerTests/StreamingTest.m @@ -0,0 +1,85 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import camera; +@import camera.Test; +@import XCTest; +@import AVFoundation; +#import +#import "CameraTestUtils.h" + +@interface StreamingTests : XCTestCase +@property(readonly, nonatomic) FLTCam *camera; +@property(readonly, nonatomic) CMSampleBufferRef sampleBuffer; +@end + +@implementation StreamingTests + +- (void)setUp { + dispatch_queue_t captureSessionQueue = dispatch_queue_create("testing", NULL); + _camera = FLTCreateCamWithCaptureSessionQueue(captureSessionQueue); + _sampleBuffer = FLTCreateTestSampleBuffer(); +} + +- (void)tearDown { + CFRelease(_sampleBuffer); +} + +- (void)testExceedMaxStreamingPendingFramesCount { + XCTestExpectation *streamingExpectation = [self + expectationWithDescription:@"Must not call handler over maxStreamingPendingFramesCount"]; + + id handlerMock = OCMClassMock([FLTImageStreamHandler class]); + OCMStub([handlerMock eventSink]).andReturn(^(id event) { + [streamingExpectation fulfill]; + }); + + id messenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); + [_camera startImageStreamWithMessenger:messenger imageStreamHandler:handlerMock]; + + XCTKVOExpectation *expectation = [[XCTKVOExpectation alloc] initWithKeyPath:@"isStreamingImages" + object:_camera + expectedValue:@YES]; + XCTWaiterResult result = [XCTWaiter waitForExpectations:@[ expectation ] timeout:1]; + XCTAssertEqual(result, XCTWaiterResultCompleted); + + streamingExpectation.expectedFulfillmentCount = 4; + for (int i = 0; i < 10; i++) { + [_camera captureOutput:nil didOutputSampleBuffer:self.sampleBuffer fromConnection:nil]; + } + + [self waitForExpectationsWithTimeout:1.0 handler:nil]; +} + +- (void)testReceivedImageStreamData { + XCTestExpectation *streamingExpectation = + [self expectationWithDescription: + @"Must be able to call the handler again when receivedImageStreamData is called"]; + + id handlerMock = OCMClassMock([FLTImageStreamHandler class]); + OCMStub([handlerMock eventSink]).andReturn(^(id event) { + [streamingExpectation fulfill]; + }); + + id messenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); + [_camera startImageStreamWithMessenger:messenger imageStreamHandler:handlerMock]; + + XCTKVOExpectation *expectation = [[XCTKVOExpectation alloc] initWithKeyPath:@"isStreamingImages" + object:_camera + expectedValue:@YES]; + XCTWaiterResult result = [XCTWaiter waitForExpectations:@[ expectation ] timeout:1]; + XCTAssertEqual(result, XCTWaiterResultCompleted); + + streamingExpectation.expectedFulfillmentCount = 5; + for (int i = 0; i < 10; i++) { + [_camera captureOutput:nil didOutputSampleBuffer:self.sampleBuffer fromConnection:nil]; + } + + [_camera receivedImageStreamData]; + [_camera captureOutput:nil didOutputSampleBuffer:self.sampleBuffer fromConnection:nil]; + + [self waitForExpectationsWithTimeout:1.0 handler:nil]; +} + +@end diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.m b/packages/camera/camera/ios/Classes/CameraPlugin.m index 634aa699a01a..c0a3833dcd64 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin.m +++ b/packages/camera/camera/ios/Classes/CameraPlugin.m @@ -162,6 +162,9 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call } else if ([@"stopImageStream" isEqualToString:call.method]) { [_camera stopImageStream]; [result sendSuccess]; + } else if ([@"receivedImageStreamData" isEqualToString:call.method]) { + [_camera receivedImageStreamData]; + [result sendSuccess]; } else { NSDictionary *argsMap = call.arguments; NSUInteger cameraId = ((NSNumber *)argsMap[@"cameraId"]).unsignedIntegerValue; diff --git a/packages/camera/camera/ios/Classes/FLTCam.h b/packages/camera/camera/ios/Classes/FLTCam.h index 0cd135e0e41f..8a5dafaf8354 100644 --- a/packages/camera/camera/ios/Classes/FLTCam.h +++ b/packages/camera/camera/ios/Classes/FLTCam.h @@ -61,6 +61,14 @@ NS_ASSUME_NONNULL_BEGIN - (void)setFocusModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSString *)modeStr; - (void)applyFocusMode; +/** + * Acknowledges the receipt of one image stream frame. + * + * This should be called each time a frame is received. Failing to call it may + * cause later frames to be dropped instead of streamed. + */ +- (void)receivedImageStreamData; + /** * Applies FocusMode on the AVCaptureDevice. * diff --git a/packages/camera/camera/ios/Classes/FLTCam.m b/packages/camera/camera/ios/Classes/FLTCam.m index 30c177bd4b2a..7af505b249cb 100644 --- a/packages/camera/camera/ios/Classes/FLTCam.m +++ b/packages/camera/camera/ios/Classes/FLTCam.m @@ -10,14 +10,6 @@ @import CoreMotion; #import -@interface FLTImageStreamHandler : NSObject -// The queue on which `eventSink` property should be accessed -@property(nonatomic, strong) dispatch_queue_t captureSessionQueue; -// `eventSink` property should be accessed on `captureSessionQueue`. -// The block itself should be invoked on the main queue. -@property FlutterEventSink eventSink; -@end - @implementation FLTImageStreamHandler - (instancetype)initWithCaptureSessionQueue:(dispatch_queue_t)captureSessionQueue { @@ -68,7 +60,13 @@ @interface FLTCam () *)messenger { + [self startImageStreamWithMessenger:messenger + imageStreamHandler:[[FLTImageStreamHandler alloc] + initWithCaptureSessionQueue:_captureSessionQueue]]; +} + +- (void)startImageStreamWithMessenger:(NSObject *)messenger + imageStreamHandler:(FLTImageStreamHandler *)imageStreamHandler { if (!_isStreamingImages) { FlutterEventChannel *eventChannel = [FlutterEventChannel eventChannelWithName:@"plugins.flutter.io/camera/imageStream" @@ -905,12 +916,12 @@ - (void)startImageStreamWithMessenger:(NSObject *)messen FLTThreadSafeEventChannel *threadSafeEventChannel = [[FLTThreadSafeEventChannel alloc] initWithEventChannel:eventChannel]; - _imageStreamHandler = - [[FLTImageStreamHandler alloc] initWithCaptureSessionQueue:_captureSessionQueue]; + _imageStreamHandler = imageStreamHandler; [threadSafeEventChannel setStreamHandler:_imageStreamHandler completion:^{ dispatch_async(self->_captureSessionQueue, ^{ self.isStreamingImages = YES; + self.streamingPendingFramesCount = 0; }); }]; } else { @@ -928,6 +939,10 @@ - (void)stopImageStream { } } +- (void)receivedImageStreamData { + self.streamingPendingFramesCount--; +} + - (void)getMaxZoomLevelWithResult:(FLTThreadSafeFlutterResult *)result { CGFloat maxZoomFactor = [self getMaxAvailableZoomFactor]; diff --git a/packages/camera/camera/ios/Classes/FLTCam_Test.h b/packages/camera/camera/ios/Classes/FLTCam_Test.h index a1f9f2b65981..19e284227f4f 100644 --- a/packages/camera/camera/ios/Classes/FLTCam_Test.h +++ b/packages/camera/camera/ios/Classes/FLTCam_Test.h @@ -5,6 +5,19 @@ #import "FLTCam.h" #import "FLTSavePhotoDelegate.h" +@interface FLTImageStreamHandler : NSObject + +/// The queue on which `eventSink` property should be accessed. +@property(nonatomic, strong) dispatch_queue_t captureSessionQueue; + +/// The event sink to stream camera events to Dart. +/// +/// The property should only be accessed on `captureSessionQueue`. +/// The block itself should be invoked on the main queue. +@property FlutterEventSink eventSink; + +@end + // APIs exposed for unit testing. @interface FLTCam () @@ -14,6 +27,9 @@ /// The output for photo capturing. Exposed setter for unit tests. @property(strong, nonatomic) AVCapturePhotoOutput *capturePhotoOutput API_AVAILABLE(ios(10)); +/// True when images from the camera are being streamed. +@property(assign, nonatomic) BOOL isStreamingImages; + /// A dictionary to retain all in-progress FLTSavePhotoDelegates. The key of the dictionary is the /// AVCapturePhotoSettings's uniqueID for each photo capture operation, and the value is the /// FLTSavePhotoDelegate that handles the result of each photo capture operation. Note that photo @@ -38,4 +54,8 @@ captureSessionQueue:(dispatch_queue_t)captureSessionQueue error:(NSError **)error; +/// Start streaming images. +- (void)startImageStreamWithMessenger:(NSObject *)messenger + imageStreamHandler:(FLTImageStreamHandler *)imageStreamHandler; + @end diff --git a/packages/camera/camera/lib/src/camera_controller.dart b/packages/camera/camera/lib/src/camera_controller.dart index 30e6221697c9..1492ca193761 100644 --- a/packages/camera/camera/lib/src/camera_controller.dart +++ b/packages/camera/camera/lib/src/camera_controller.dart @@ -448,6 +448,13 @@ class CameraController extends ValueNotifier { _imageStreamSubscription = cameraEventChannel.receiveBroadcastStream().listen( (dynamic imageData) { + if (defaultTargetPlatform == TargetPlatform.iOS) { + try { + _channel.invokeMethod('receivedImageStreamData'); + } on PlatformException catch (e) { + throw CameraException(e.code, e.message); + } + } onAvailable( CameraImage.fromPlatformData(imageData as Map)); }, diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 0e684a743ba3..064eb919c96a 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+17 +version: 0.9.4+18 environment: sdk: ">=2.14.0 <3.0.0" From 1ddfcc044615138f93f8aa0e45b7ad41e329a7c2 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 29 Mar 2022 06:30:12 -0400 Subject: [PATCH 324/600] Roll Flutter from 9671b7ddee8c to b4325b68a260 (121 revisions) (#5120) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 3bf43fd4c61a..df0a28879011 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -9671b7ddee8c5110cf6adcf7b7697991512218e9 +b4325b68a2602335263eadec484256136b059703 From 8a5f9bf6c931120aed11305b70f1ea4e272a3755 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Tue, 29 Mar 2022 09:44:28 -0700 Subject: [PATCH 325/600] [webview_flutter_wkwebview] Update setters and getters to follow Flutter Style Guide (#5104) --- .../lib/src/ui_kit/ui_kit.dart | 8 +- .../lib/src/web_kit/web_kit.dart | 66 +++--- .../lib/src/web_kit_webview_widget.dart | 60 +++--- .../test/src/web_kit_webview_widget_test.dart | 54 ++--- .../web_kit_webview_widget_test.mocks.dart | 193 ++++++++++-------- 5 files changed, 208 insertions(+), 173 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart index def52b78f6b5..9b7973e2ace5 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart @@ -17,7 +17,9 @@ class UIScrollView { UIScrollView.fromWebView(WKWebView webView); /// Point at which the origin of the content view is offset from the origin of the scroll view. - Future> get contentOffset { + /// + /// Represents [WKWebView.contentOffset](https://developer.apple.com/documentation/uikit/uiscrollview/1619404-contentoffset?language=objc). + Future> getContentOffset() { throw UnimplementedError(); } @@ -32,7 +34,9 @@ class UIScrollView { /// Set point at which the origin of the content view is offset from the origin of the scroll view. /// /// The default value is `Point(0.0, 0.0)`. - set contentOffset(FutureOr> offset) { + /// + /// Sets [WKWebView.contentOffset](https://developer.apple.com/documentation/uikit/uiscrollview/1619404-contentoffset?language=objc). + Future setContentOffset(FutureOr> offset) { throw UnimplementedError(); } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart index 94b1533fb3b2..fb714f8a05d5 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart @@ -220,7 +220,7 @@ class WKScriptMessageHandler { /// Use this method to respond to a message sent from the webpage’s /// JavaScript code. Use the [message] parameter to get the message contents and /// to determine the originating web view. - set didReceiveScriptMessage( + Future setDidReceiveScriptMessage( void Function( WKUserContentController userContentController, WKScriptMessage message, @@ -324,13 +324,17 @@ class WKWebViewConfiguration { WKWebsiteDataStore get webSiteDataStore => _websiteDataStore; /// Used to get and set the site’s cookies and to track the cached data objects. - set webSiteDataStore(WKWebsiteDataStore websiteDataStore) { + /// + /// Sets [WKWebViewConfiguration.webSiteDataStore](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/1395661-websitedatastore?language=objc). + Future setWebSiteDataStore(WKWebsiteDataStore websiteDataStore) { _websiteDataStore = websiteDataStore; throw UnimplementedError(); } /// Indicates whether HTML5 videos play inline or use the native full-screen controller. - set allowsInlineMediaPlayback(bool allow) { + /// + /// Sets [WKWebViewConfiguration.allowsInlineMediaPlayback](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/1614793-allowsinlinemediaplayback?language=objc). + Future setAllowsInlineMediaPlayback(bool allow) { throw UnimplementedError(); } @@ -338,7 +342,9 @@ class WKWebViewConfiguration { /// /// Use [WKAudiovisualMediaType.none] to indicate that no user gestures are /// required to begin playing media. - set mediaTypesRequiringUserActionForPlayback( + /// + /// Sets [WKWebViewConfiguration.mediaTypesRequiringUserActionForPlayback](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/1851524-mediatypesrequiringuseractionfor?language=objc). + Future setMediaTypesRequiringUserActionForPlayback( Set types, ) { assert(types.isNotEmpty); @@ -351,12 +357,12 @@ class WKWebViewConfiguration { /// Wraps [WKUIDelegate](https://developer.apple.com/documentation/webkit/wkuidelegate?language=objc). class WKUIDelegate { /// Indicates a new [WebView] was requested to be created with [configuration]. - set onCreateWebView( + Future setOnCreateWebView( void Function( WKWebViewConfiguration configuration, WKNavigationAction navigationAction, )? - onCreateeWebView, + onCreateWebView, ) { throw UnimplementedError(); } @@ -370,7 +376,7 @@ class WKUIDelegate { /// Wraps [WKNavigationDelegate](https://developer.apple.com/documentation/webkit/wknavigationdelegate?language=objc). class WKNavigationDelegate { /// Called when navigation from the main frame has started. - set didStartProvisionalNavigation( + Future setDidStartProvisionalNavigation( void Function( WKWebView webView, String? url, @@ -381,14 +387,14 @@ class WKNavigationDelegate { } /// Called when navigation is complete. - set didFinishNavigation( + Future setDidFinishNavigation( void Function(WKWebView webView, String? url)? didFinishNavigation, ) { throw UnimplementedError(); } /// Called when permission is needed to navigate to new content. - set decidePolicyForNavigationAction( + Future setDecidePolicyForNavigationAction( Future Function( WKWebView webView, WKNavigationAction navigationAction, @@ -398,14 +404,14 @@ class WKNavigationDelegate { } /// Called when an error occurred during navigation. - set didFailNavigation( + Future setDidFailNavigation( void Function(WKWebView webView, NSError error)? didFailNavigation, ) { throw UnimplementedError(); } /// Called when an error occurred during the early navigation process. - set didFailProvisionalNavigation( + Future setDidFailProvisionalNavigation( void Function(WKWebView webView, NSError error)? didFailProvisionalNavigation, ) { @@ -413,7 +419,7 @@ class WKNavigationDelegate { } /// Called when the web view’s content process was terminated. - set webViewWebContentProcessDidTerminate( + Future setWebViewWebContentProcessDidTerminate( void Function(WKWebView webView)? webViewWebContentProcessDidTerminate, ) { throw UnimplementedError(); @@ -455,17 +461,23 @@ class WKWebView extends NSObject { late final UIScrollView scrollView = UIScrollView.fromWebView(this); /// Used to integrate custom user interface elements into web view interactions. - set uiDelegate(WKUIDelegate? delegate) { + /// + /// Sets [WKWebView.UIDelegate](https://developer.apple.com/documentation/webkit/wkwebview/1415009-uidelegate?language=objc). + Future setUIDelegate(WKUIDelegate? delegate) { throw UnimplementedError(); } /// The object you use to manage navigation behavior for the web view. - set navigationDelegate(WKNavigationDelegate? delegate) { + /// + /// Sets [WKWebView.navigationDelegate](https://developer.apple.com/documentation/webkit/wkwebview/1414971-navigationdelegate?language=objc). + Future setNavigationDelegate(WKNavigationDelegate? delegate) { throw UnimplementedError(); } /// The URL for the current webpage. - Future get url { + /// + /// Represents [WKWebView.URL](https://developer.apple.com/documentation/webkit/wkwebview/1415005-url?language=objc). + Future getUrl() { throw UnimplementedError(); } @@ -473,8 +485,7 @@ class WKWebView extends NSObject { /// /// This value ranges from 0.0 to 1.0. /// - /// This method represents - /// [WKWebView.estimatedProgress](https://developer.apple.com/documentation/webkit/wkwebview/1415007-estimatedprogress?language=objc). + /// Represents [WKWebView.estimatedProgress](https://developer.apple.com/documentation/webkit/wkwebview/1415007-estimatedprogress?language=objc). Future getEstimatedProgress() { throw UnimplementedError(); } @@ -506,12 +517,12 @@ class WKWebView extends NSObject { } /// Indicates whether there is a valid back item in the back-forward list. - Future get canGoBack { + Future canGoBack() { throw UnimplementedError(); } /// Indicates whether there is a valid forward item in the back-forward list. - Future get canGoForward { + Future canGoForward() { throw UnimplementedError(); } @@ -531,26 +542,27 @@ class WKWebView extends NSObject { } /// The page title. - Future get title { - throw UnimplementedError(); - } - - /// An estimate of what fraction of the current navigation has been loaded. - Future get estimatedProgress { + /// + /// Represents [WKWebView.title](https://developer.apple.com/documentation/webkit/wkwebview/1415015-title?language=objc). + Future getTitle() { throw UnimplementedError(); } /// Indicates whether horizontal swipe gestures trigger page navigation. /// /// The default value is false. - set allowsBackForwardNavigationGestures(bool allow) { + /// + /// Sets [WKWebView.allowsBackForwardNavigationGestures](https://developer.apple.com/documentation/webkit/wkwebview/1414995-allowsbackforwardnavigationgestu?language=objc). + Future setAllowsBackForwardNavigationGestures(bool allow) { throw UnimplementedError(); } /// The custom user agent string. /// /// The default value of this property is null. - set customUserAgent(String? userAgent) { + /// + /// Sets [WKWebView.customUserAgent](https://developer.apple.com/documentation/webkit/wkwebview/1414950-customuseragent?language=objc). + Future setCustomUserAgent(String? userAgent) { throw UnimplementedError(); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 58c279cd56ba..67b05c1f053a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -92,15 +92,15 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { ), ); - webView.uiDelegate = uiDelegate; - uiDelegate.onCreateWebView = ( + webView.setUIDelegate(uiDelegate); + uiDelegate.setOnCreateWebView(( WKWebViewConfiguration configuration, WKNavigationAction navigationAction, ) { if (!navigationAction.targetFrame.isMainFrame) { webView.loadRequest(navigationAction.request); } - }; + }); } final Map _scriptMessageHandlers = @@ -128,19 +128,19 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { @visibleForTesting late final WKNavigationDelegate navigationDelegate = webViewProxy.createNavigationDelegate() - ..didStartProvisionalNavigation = (WKWebView webView, String? url) { + ..setDidStartProvisionalNavigation((WKWebView webView, String? url) { callbacksHandler.onPageStarted(url ?? ''); - } - ..didFinishNavigation = (WKWebView webView, String? url) { + }) + ..setDidFinishNavigation((WKWebView webView, String? url) { callbacksHandler.onPageFinished(url ?? ''); - } - ..didFailNavigation = (WKWebView webView, NSError error) { + }) + ..setDidFailNavigation((WKWebView webView, NSError error) { callbacksHandler.onWebResourceError(_toWebResourceError(error)); - } - ..didFailProvisionalNavigation = (WKWebView webView, NSError error) { + }) + ..setDidFailProvisionalNavigation((WKWebView webView, NSError error) { callbacksHandler.onWebResourceError(_toWebResourceError(error)); - } - ..webViewWebContentProcessDidTerminate = (WKWebView webView) { + }) + ..setWebViewWebContentProcessDidTerminate((WKWebView webView) { callbacksHandler.onWebResourceError(WebResourceError( errorCode: WKErrorCode.webContentProcessTerminated, // Value from https://developer.apple.com/documentation/webkit/wkerrordomain?language=objc. @@ -148,7 +148,7 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { description: '', errorType: WebResourceErrorType.webContentProcessTerminated, )); - }; + }); Future _setCreationParams( CreationParams params, { @@ -164,7 +164,7 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { await addJavascriptChannels(params.javascriptChannelNames); - webView.navigationDelegate = navigationDelegate; + webView.setNavigationDelegate(navigationDelegate); if (params.webSettings != null) { updateSettings(params.webSettings!); @@ -177,7 +177,7 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { required AutoMediaPlaybackPolicy autoMediaPlaybackPolicy, }) { if (allowsInlineMediaPlayback != null) { - configuration.allowsInlineMediaPlayback = allowsInlineMediaPlayback; + configuration.setAllowsInlineMediaPlayback(allowsInlineMediaPlayback); } late final bool requiresUserAction; @@ -190,11 +190,11 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { break; } - configuration.mediaTypesRequiringUserActionForPlayback = - { + configuration + .setMediaTypesRequiringUserActionForPlayback({ if (requiresUserAction) WKAudiovisualMediaType.all, if (!requiresUserAction) WKAudiovisualMediaType.none, - }; + }); } @override @@ -255,10 +255,10 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { } @override - Future canGoBack() => webView.canGoBack; + Future canGoBack() => webView.canGoBack(); @override - Future canGoForward() => webView.canGoForward; + Future canGoForward() => webView.canGoForward(); @override Future goBack() => webView.goBack(); @@ -311,14 +311,14 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { } @override - Future getTitle() => webView.title; + Future getTitle() => webView.getTitle(); @override Future scrollTo(int x, int y) async { - webView.scrollView.contentOffset = Point( + webView.scrollView.setContentOffset(Point( x.toDouble(), y.toDouble(), - ); + )); } @override @@ -331,13 +331,13 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { @override Future getScrollX() async { - final Point offset = await webView.scrollView.contentOffset; + final Point offset = await webView.scrollView.getContentOffset(); return offset.x.toInt(); } @override Future getScrollY() async { - final Point offset = await webView.scrollView.contentOffset; + final Point offset = await webView.scrollView.getContentOffset(); return offset.y.toInt(); } @@ -362,7 +362,7 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { (String channelName) { final WKScriptMessageHandler handler = webViewProxy.createScriptMessageHandler() - ..didReceiveScriptMessage = ( + ..setDidReceiveScriptMessage(( WKUserContentController userContentController, WKScriptMessage message, ) { @@ -370,7 +370,7 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { message.name, message.body!.toString(), ); - }; + }); _scriptMessageHandlers[channelName] = handler; final String wrapperSource = @@ -417,7 +417,7 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { void _setHasNavigationDelegate(bool hasNavigationDelegate) { if (hasNavigationDelegate) { - navigationDelegate.decidePolicyForNavigationAction = + navigationDelegate.setDecidePolicyForNavigationAction( (WKWebView webView, WKNavigationAction action) async { final bool allow = await callbacksHandler.onNavigationRequest( url: action.request.url, @@ -427,9 +427,9 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { return allow ? WKNavigationActionPolicy.allow : WKNavigationActionPolicy.cancel; - }; + }); } else { - navigationDelegate.decidePolicyForNavigationAction = null; + navigationDelegate.setDecidePolicyForNavigationAction(null); } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index 9d8c810b9edc..e8b29dccb4e4 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -115,7 +115,7 @@ void main() { await buildWidget(tester); final dynamic onCreateWebView = - verify(mockUIDelegate.onCreateWebView = captureAny).captured.single + verify(mockUIDelegate.setOnCreateWebView(captureAny)).captured.single as void Function(WKWebViewConfiguration, WKNavigationAction); const NSUrlRequest request = NSUrlRequest(url: 'https://google.com'); @@ -144,11 +144,11 @@ void main() { ), ); - verify( - mockWebViewConfiguration.mediaTypesRequiringUserActionForPlayback = - { + verify(mockWebViewConfiguration + .setMediaTypesRequiringUserActionForPlayback(< + WKAudiovisualMediaType>{ WKAudiovisualMediaType.all, - }); + })); }); testWidgets('autoMediaPlaybackPolicy false', (WidgetTester tester) async { @@ -163,11 +163,11 @@ void main() { ), ); - verify( - mockWebViewConfiguration.mediaTypesRequiringUserActionForPlayback = - { + verify(mockWebViewConfiguration + .setMediaTypesRequiringUserActionForPlayback(< + WKAudiovisualMediaType>{ WKAudiovisualMediaType.none, - }); + })); }); testWidgets('javascriptChannelNames', (WidgetTester tester) async { @@ -216,7 +216,7 @@ void main() { ), ); - verify(mockWebViewConfiguration.allowsInlineMediaPlayback = true); + verify(mockWebViewConfiguration.setAllowsInlineMediaPlayback(true)); }); }); }); @@ -352,7 +352,7 @@ void main() { testWidgets('canGoBack', (WidgetTester tester) async { await buildWidget(tester); - when(mockWebView.canGoBack).thenAnswer( + when(mockWebView.canGoBack()).thenAnswer( (_) => Future.value(false), ); expect(testController.canGoBack(), completion(false)); @@ -361,7 +361,7 @@ void main() { testWidgets('canGoForward', (WidgetTester tester) async { await buildWidget(tester); - when(mockWebView.canGoForward).thenAnswer( + when(mockWebView.canGoForward()).thenAnswer( (_) => Future.value(true), ); expect(testController.canGoForward(), completion(true)); @@ -524,7 +524,7 @@ void main() { testWidgets('getTitle', (WidgetTester tester) async { await buildWidget(tester); - when(mockWebView.title) + when(mockWebView.getTitle()) .thenAnswer((_) => Future.value('Web Title')); expect(testController.getTitle(), completion('Web Title')); }); @@ -533,7 +533,7 @@ void main() { await buildWidget(tester); await testController.scrollTo(2, 4); - verify(mockScrollView.contentOffset = const Point(2.0, 4.0)); + verify(mockScrollView.setContentOffset(const Point(2.0, 4.0))); }); testWidgets('scrollBy', (WidgetTester tester) async { @@ -546,7 +546,7 @@ void main() { testWidgets('getScrollX', (WidgetTester tester) async { await buildWidget(tester); - when(mockScrollView.contentOffset).thenAnswer( + when(mockScrollView.getContentOffset()).thenAnswer( (_) => Future>.value(const Point(8.0, 16.0))); expect(testController.getScrollX(), completion(8.0)); }); @@ -556,7 +556,7 @@ void main() { await buildWidget(tester); - when(mockScrollView.contentOffset).thenAnswer( + when(mockScrollView.getContentOffset()).thenAnswer( (_) => Future>.value(const Point(8.0, 16.0))); expect(testController.getScrollY(), completion(16.0)); }); @@ -660,8 +660,8 @@ void main() { await buildWidget(tester); final dynamic didStartProvisionalNavigation = verify( - mockNavigationDelegate.didStartProvisionalNavigation = - captureAny) + mockNavigationDelegate + .setDidStartProvisionalNavigation(captureAny)) .captured .single as void Function(WKWebView, String); didStartProvisionalNavigation(mockWebView, 'https://google.com'); @@ -673,7 +673,7 @@ void main() { await buildWidget(tester); final dynamic didFinishNavigation = - verify(mockNavigationDelegate.didFinishNavigation = captureAny) + verify(mockNavigationDelegate.setDidFinishNavigation(captureAny)) .captured .single as void Function(WKWebView, String); didFinishNavigation(mockWebView, 'https://google.com'); @@ -686,7 +686,7 @@ void main() { await buildWidget(tester); final dynamic didFailNavigation = - verify(mockNavigationDelegate.didFailNavigation = captureAny) + verify(mockNavigationDelegate.setDidFailNavigation(captureAny)) .captured .single as void Function(WKWebView, NSError); @@ -714,8 +714,8 @@ void main() { await buildWidget(tester); final dynamic didFailProvisionalNavigation = verify( - mockNavigationDelegate.didFailProvisionalNavigation = - captureAny) + mockNavigationDelegate + .setDidFailProvisionalNavigation(captureAny)) .captured .single as void Function(WKWebView, NSError); @@ -747,8 +747,8 @@ void main() { await buildWidget(tester); final dynamic webViewWebContentProcessDidTerminate = verify( - mockNavigationDelegate.webViewWebContentProcessDidTerminate = - captureAny) + mockNavigationDelegate + .setWebViewWebContentProcessDidTerminate(captureAny)) .captured .single as void Function(WKWebView); webViewWebContentProcessDidTerminate(mockWebView); @@ -771,8 +771,8 @@ void main() { await buildWidget(tester, hasNavigationDelegate: true); final dynamic decidePolicyForNavigationAction = verify( - mockNavigationDelegate.decidePolicyForNavigationAction = - captureAny) + mockNavigationDelegate + .setDecidePolicyForNavigationAction(captureAny)) .captured .single as Future Function( @@ -844,7 +844,7 @@ void main() { .single as MockWKScriptMessageHandler; final dynamic didReceiveScriptMessage = - verify(messageHandler.didReceiveScriptMessage = captureAny) + verify(messageHandler.setDidReceiveScriptMessage(captureAny)) .captured .single as void Function( WKUserContentController userContentController, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index c63aef9026a3..36b8af441e2e 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -60,19 +60,20 @@ class MockUIScrollView extends _i1.Mock implements _i4.UIScrollView { } @override - _i5.Future<_i2.Point> get contentOffset => (super.noSuchMethod( - Invocation.getter(#contentOffset), + _i5.Future<_i2.Point> getContentOffset() => (super.noSuchMethod( + Invocation.method(#getContentOffset, []), returnValue: Future<_i2.Point>.value(_FakePoint_0())) as _i5.Future<_i2.Point>); @override - set contentOffset(_i5.FutureOr<_i2.Point>? offset) => - super.noSuchMethod(Invocation.setter(#contentOffset, offset), - returnValueForMissingStub: null); - @override _i5.Future scrollBy(_i2.Point? offset) => (super.noSuchMethod(Invocation.method(#scrollBy, [offset]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future setContentOffset(_i5.FutureOr<_i2.Point>? offset) => + (super.noSuchMethod(Invocation.method(#setContentOffset, [offset]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [WKNavigationDelegate]. @@ -85,49 +86,55 @@ class MockWKNavigationDelegate extends _i1.Mock } @override - set didStartProvisionalNavigation( + _i5.Future setDidStartProvisionalNavigation( void Function(_i3.WKWebView, String?)? didStartProvisionalNavigation) => - super.noSuchMethod( - Invocation.setter( - #didStartProvisionalNavigation, didStartProvisionalNavigation), - returnValueForMissingStub: null); + (super.noSuchMethod( + Invocation.method(#setDidStartProvisionalNavigation, + [didStartProvisionalNavigation]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); @override - set didFinishNavigation( + _i5.Future setDidFinishNavigation( void Function(_i3.WKWebView, String?)? didFinishNavigation) => - super.noSuchMethod( - Invocation.setter(#didFinishNavigation, didFinishNavigation), - returnValueForMissingStub: null); + (super.noSuchMethod( + Invocation.method(#setDidFinishNavigation, [didFinishNavigation]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); @override - set decidePolicyForNavigationAction( + _i5.Future setDecidePolicyForNavigationAction( _i5.Future<_i3.WKNavigationActionPolicy> Function( _i3.WKWebView, _i3.WKNavigationAction)? decidePolicyForNavigationAction) => - super.noSuchMethod( - Invocation.setter(#decidePolicyForNavigationAction, - decidePolicyForNavigationAction), - returnValueForMissingStub: null); + (super.noSuchMethod( + Invocation.method(#setDecidePolicyForNavigationAction, + [decidePolicyForNavigationAction]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); @override - set didFailNavigation( + _i5.Future setDidFailNavigation( void Function(_i3.WKWebView, _i6.NSError)? didFailNavigation) => - super.noSuchMethod( - Invocation.setter(#didFailNavigation, didFailNavigation), - returnValueForMissingStub: null); + (super.noSuchMethod( + Invocation.method(#setDidFailNavigation, [didFailNavigation]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); @override - set didFailProvisionalNavigation( + _i5.Future setDidFailProvisionalNavigation( void Function(_i3.WKWebView, _i6.NSError)? didFailProvisionalNavigation) => - super.noSuchMethod( - Invocation.setter( - #didFailProvisionalNavigation, didFailProvisionalNavigation), - returnValueForMissingStub: null); + (super.noSuchMethod( + Invocation.method( + #setDidFailProvisionalNavigation, [didFailProvisionalNavigation]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); @override - set webViewWebContentProcessDidTerminate( + _i5.Future setWebViewWebContentProcessDidTerminate( void Function(_i3.WKWebView)? webViewWebContentProcessDidTerminate) => - super.noSuchMethod( - Invocation.setter(#webViewWebContentProcessDidTerminate, - webViewWebContentProcessDidTerminate), - returnValueForMissingStub: null); + (super.noSuchMethod( + Invocation.method(#setWebViewWebContentProcessDidTerminate, + [webViewWebContentProcessDidTerminate]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [WKScriptMessageHandler]. @@ -140,12 +147,14 @@ class MockWKScriptMessageHandler extends _i1.Mock } @override - set didReceiveScriptMessage( + _i5.Future setDidReceiveScriptMessage( void Function(_i3.WKUserContentController, _i3.WKScriptMessage)? didReceiveScriptMessage) => - super.noSuchMethod( - Invocation.setter(#didReceiveScriptMessage, didReceiveScriptMessage), - returnValueForMissingStub: null); + (super.noSuchMethod( + Invocation.method( + #setDidReceiveScriptMessage, [didReceiveScriptMessage]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [WKWebView]. @@ -166,41 +175,6 @@ class MockWKWebView extends _i1.Mock implements _i3.WKWebView { (super.noSuchMethod(Invocation.getter(#scrollView), returnValue: _FakeUIScrollView_2()) as _i4.UIScrollView); @override - set uiDelegate(_i3.WKUIDelegate? delegate) => - super.noSuchMethod(Invocation.setter(#uiDelegate, delegate), - returnValueForMissingStub: null); - @override - set navigationDelegate(_i3.WKNavigationDelegate? delegate) => - super.noSuchMethod(Invocation.setter(#navigationDelegate, delegate), - returnValueForMissingStub: null); - @override - _i5.Future get url => (super.noSuchMethod(Invocation.getter(#url), - returnValue: Future.value()) as _i5.Future); - @override - _i5.Future get canGoBack => - (super.noSuchMethod(Invocation.getter(#canGoBack), - returnValue: Future.value(false)) as _i5.Future); - @override - _i5.Future get canGoForward => - (super.noSuchMethod(Invocation.getter(#canGoForward), - returnValue: Future.value(false)) as _i5.Future); - @override - _i5.Future get title => - (super.noSuchMethod(Invocation.getter(#title), - returnValue: Future.value()) as _i5.Future); - @override - _i5.Future get estimatedProgress => - (super.noSuchMethod(Invocation.getter(#estimatedProgress), - returnValue: Future.value(0.0)) as _i5.Future); - @override - set allowsBackForwardNavigationGestures(bool? allow) => super.noSuchMethod( - Invocation.setter(#allowsBackForwardNavigationGestures, allow), - returnValueForMissingStub: null); - @override - set customUserAgent(String? userAgent) => - super.noSuchMethod(Invocation.setter(#customUserAgent, userAgent), - returnValueForMissingStub: null); - @override set observeValue( void Function( String, _i6.NSObject, Map<_i6.NSKeyValueChangeKey, Object?>)? @@ -208,6 +182,20 @@ class MockWKWebView extends _i1.Mock implements _i3.WKWebView { super.noSuchMethod(Invocation.setter(#observeValue, observeValue), returnValueForMissingStub: null); @override + _i5.Future setUIDelegate(_i3.WKUIDelegate? delegate) => + (super.noSuchMethod(Invocation.method(#setUIDelegate, [delegate]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future setNavigationDelegate(_i3.WKNavigationDelegate? delegate) => + (super.noSuchMethod(Invocation.method(#setNavigationDelegate, [delegate]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future getUrl() => + (super.noSuchMethod(Invocation.method(#getUrl, []), + returnValue: Future.value()) as _i5.Future); + @override _i5.Future getEstimatedProgress() => (super.noSuchMethod(Invocation.method(#getEstimatedProgress, []), returnValue: Future.value(0.0)) as _i5.Future); @@ -235,6 +223,14 @@ class MockWKWebView extends _i1.Mock implements _i3.WKWebView { returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i5.Future); @override + _i5.Future canGoBack() => + (super.noSuchMethod(Invocation.method(#canGoBack, []), + returnValue: Future.value(false)) as _i5.Future); + @override + _i5.Future canGoForward() => + (super.noSuchMethod(Invocation.method(#canGoForward, []), + returnValue: Future.value(false)) as _i5.Future); + @override _i5.Future goBack() => (super.noSuchMethod(Invocation.method(#goBack, []), returnValue: Future.value(), @@ -250,6 +246,21 @@ class MockWKWebView extends _i1.Mock implements _i3.WKWebView { returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i5.Future); @override + _i5.Future getTitle() => + (super.noSuchMethod(Invocation.method(#getTitle, []), + returnValue: Future.value()) as _i5.Future); + @override + _i5.Future setAllowsBackForwardNavigationGestures(bool? allow) => + (super.noSuchMethod( + Invocation.method(#setAllowsBackForwardNavigationGestures, [allow]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future setCustomUserAgent(String? userAgent) => + (super.noSuchMethod(Invocation.method(#setCustomUserAgent, [userAgent]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override _i5.Future evaluateJavaScript(String? javaScriptString) => (super .noSuchMethod(Invocation.method(#evaluateJavaScript, [javaScriptString]), returnValue: Future.value()) as _i5.Future); @@ -294,19 +305,25 @@ class MockWKWebViewConfiguration extends _i1.Mock (super.noSuchMethod(Invocation.getter(#webSiteDataStore), returnValue: _FakeWKWebsiteDataStore_4()) as _i3.WKWebsiteDataStore); @override - set webSiteDataStore(_i3.WKWebsiteDataStore? websiteDataStore) => - super.noSuchMethod(Invocation.setter(#webSiteDataStore, websiteDataStore), - returnValueForMissingStub: null); + _i5.Future setWebSiteDataStore( + _i3.WKWebsiteDataStore? websiteDataStore) => + (super.noSuchMethod( + Invocation.method(#setWebSiteDataStore, [websiteDataStore]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); @override - set allowsInlineMediaPlayback(bool? allow) => - super.noSuchMethod(Invocation.setter(#allowsInlineMediaPlayback, allow), - returnValueForMissingStub: null); + _i5.Future setAllowsInlineMediaPlayback(bool? allow) => (super + .noSuchMethod(Invocation.method(#setAllowsInlineMediaPlayback, [allow]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); @override - set mediaTypesRequiringUserActionForPlayback( + _i5.Future setMediaTypesRequiringUserActionForPlayback( Set<_i3.WKAudiovisualMediaType>? types) => - super.noSuchMethod( - Invocation.setter(#mediaTypesRequiringUserActionForPlayback, types), - returnValueForMissingStub: null); + (super.noSuchMethod( + Invocation.method( + #setMediaTypesRequiringUserActionForPlayback, [types]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [WKWebsiteDataStore]. @@ -336,11 +353,13 @@ class MockWKUIDelegate extends _i1.Mock implements _i3.WKUIDelegate { } @override - set onCreateWebView( + _i5.Future setOnCreateWebView( void Function(_i3.WKWebViewConfiguration, _i3.WKNavigationAction)? - onCreateeWebView) => - super.noSuchMethod(Invocation.setter(#onCreateWebView, onCreateeWebView), - returnValueForMissingStub: null); + onCreateWebView) => + (super.noSuchMethod( + Invocation.method(#setOnCreateWebView, [onCreateWebView]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [WKUserContentController]. From c5f34ad891cc4d47c821bf1309da734e69a98f96 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 29 Mar 2022 13:20:10 -0400 Subject: [PATCH 326/600] Roll Flutter from b4325b68a260 to 37d619d2285e (1 revision) (#5121) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index df0a28879011..df6c7e4aa309 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b4325b68a2602335263eadec484256136b059703 +37d619d2285ed719d4f4eec0df0d7d6747397082 From 2c52202ad7d8e1e0d2220e0ec50e03280ff4b97b Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 29 Mar 2022 16:35:11 -0400 Subject: [PATCH 327/600] Roll Flutter from 37d619d2285e to 43029bebc485 (4 revisions) (#5124) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index df6c7e4aa309..77e6e4b9a4ea 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -37d619d2285ed719d4f4eec0df0d7d6747397082 +43029bebc485eca7b380208f1c857e557ae875bf From 611b9572b73e3259d0550691959ee50907de5164 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 29 Mar 2022 21:30:15 -0400 Subject: [PATCH 328/600] Roll Flutter from 43029bebc485 to fb60ae53e553 (5 revisions) (#5125) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 77e6e4b9a4ea..1d99be09c720 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -43029bebc485eca7b380208f1c857e557ae875bf +fb60ae53e553486559e1ae0f400c322825808651 From b3e37559f96652fc91b14d3970cbf1c38c7a5c05 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 30 Mar 2022 09:45:17 -0400 Subject: [PATCH 329/600] Roll Flutter from fb60ae53e553 to f07f6fef9ae9 (1 revision) (#5126) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 1d99be09c720..7ea02b41af44 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -fb60ae53e553486559e1ae0f400c322825808651 +f07f6fef9ae9156c4531b62ba4a63185528f2884 From cc9d68985263ee90957b0d50acc5fb514124a486 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 31 Mar 2022 16:00:12 -0400 Subject: [PATCH 330/600] Roll Flutter from f07f6fef9ae9 to 66ed64be4fd8 (6 revisions) (#5128) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 7ea02b41af44..ef23ed91e5bb 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -f07f6fef9ae9156c4531b62ba4a63185528f2884 +66ed64be4fd8349cf47bf9b86f4d74ae3b4198f8 From 7d1923f4528e416529f0c5d1f02a706c9adad45b Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 31 Mar 2022 13:00:51 -0700 Subject: [PATCH 331/600] [webview_flutter_android] Clear local storage when `clearCache` is called (#5086) --- .../webview_flutter_test.dart | 64 ++++++++++++++++ .../webview_flutter_android/CHANGELOG.md | 4 +- .../GeneratedAndroidWebView.java | 74 +++++++++++++++++++ .../webviewflutter/WebStorageHostApiImpl.java | 53 +++++++++++++ .../webviewflutter/WebViewFlutterPlugin.java | 4 + .../WebStorageHostApiImplTest.java | 41 ++++++++++ .../webview_flutter_test.dart | 44 +++++++++++ .../lib/src/android_webview.dart | 27 +++++++ .../lib/src/android_webview.pigeon.dart | 66 +++++++++++++++++ .../lib/src/android_webview_api_impls.dart | 27 +++++++ .../lib/webview_android_widget.dart | 17 ++++- .../pigeons/android_webview.dart | 7 ++ .../webview_flutter_android/pubspec.yaml | 2 +- .../test/android_webview.pigeon.dart | 52 +++++++++++++ .../test/android_webview_test.dart | 26 +++++++ .../test/android_webview_test.mocks.dart | 19 +++++ .../test/webview_android_widget_test.dart | 5 ++ .../webview_android_widget_test.mocks.dart | 15 ++++ 18 files changed, 543 insertions(+), 4 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebStorageHostApiImpl.java create mode 100644 packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebStorageHostApiImplTest.java diff --git a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart index 02ee1976eb67..70e8179e1dd0 100644 --- a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart @@ -1217,6 +1217,52 @@ Future main() async { }, skip: _skipDueToIssue86757, ); + + testWidgets( + 'clearCache should clear local storage', + (WidgetTester tester) async { + final Completer controllerCompleter = + Completer(); + final Completer onPageFinished = Completer(); + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: WebView( + key: GlobalKey(), + initialUrl: primaryUrl, + javascriptMode: JavascriptMode.unrestricted, + onPageFinished: (_) => onPageFinished.complete(), + onWebViewCreated: (WebViewController controller) { + controllerCompleter.complete(controller); + }, + ), + ), + ); + + final WebViewController controller = await controllerCompleter.future; + await onPageFinished.future; + + await controller.runJavascript('localStorage.setItem("myCat", "Tom");'); + + expect( + controller.runJavascriptReturningResult( + 'localStorage.getItem("myCat");', + ), + completion(_webviewString('Tom')), + ); + + await controller.clearCache(); + + expect( + controller.runJavascriptReturningResult( + 'localStorage.getItem("myCat");', + ), + completion(_webviewNull()), + ); + }, + // TODO(bparrishMines): Unskip once https://github.com/flutter/plugins/pull/5086 lands and is published. + skip: Platform.isAndroid, + ); } // JavaScript booleans evaluate to different string values on Android and iOS. @@ -1228,6 +1274,24 @@ String _webviewBool(bool value) { return value ? 'true' : 'false'; } +// JavaScript `null` evaluate to different string values on Android and iOS. +// This utility method returns the string boolean value of the current platform. +String _webviewNull() { + if (defaultTargetPlatform == TargetPlatform.iOS) { + return ''; + } + return 'null'; +} + +// JavaScript String evaluate to different string values on Android and iOS. +// This utility method returns the string boolean value of the current platform. +String _webviewString(String value) { + if (defaultTargetPlatform == TargetPlatform.iOS) { + return value; + } + return '"$value"'; +} + /// Returns the value used for the HTTP User-Agent: request header in subsequent HTTP requests. Future _getUserAgent(WebViewController controller) async { return _runJavascriptReturningResult(controller, 'navigator.userAgent;'); diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 12d20b00f534..ad81b0f6e83d 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.8.4 * Fixes bug preventing `mockito` code generation for tests. +* Fixes regression where local storage wasn't cleared when `WebViewController.clearCache` was + called. ## 2.8.3 diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java index 15b78b718115..afca5ee12747 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java @@ -2199,6 +2199,80 @@ public void onProgressChanged( } } + private static class WebStorageHostApiCodec extends StandardMessageCodec { + public static final WebStorageHostApiCodec INSTANCE = new WebStorageHostApiCodec(); + + private WebStorageHostApiCodec() {} + } + + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ + public interface WebStorageHostApi { + void create(Long instanceId); + + void deleteAllData(Long instanceId); + + /** The codec used by WebStorageHostApi. */ + static MessageCodec getCodec() { + return WebStorageHostApiCodec.INSTANCE; + } + + /** + * Sets up an instance of `WebStorageHostApi` to handle messages through the `binaryMessenger`. + */ + static void setup(BinaryMessenger binaryMessenger, WebStorageHostApi api) { + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.WebStorageHostApi.create", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number instanceIdArg = (Number) args.get(0); + if (instanceIdArg == null) { + throw new NullPointerException("instanceIdArg unexpectedly null."); + } + api.create(instanceIdArg.longValue()); + wrapped.put("result", null); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.WebStorageHostApi.deleteAllData", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number instanceIdArg = (Number) args.get(0); + if (instanceIdArg == null) { + throw new NullPointerException("instanceIdArg unexpectedly null."); + } + api.deleteAllData(instanceIdArg.longValue()); + wrapped.put("result", null); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + } + } + private static Map wrapError(Throwable exception) { Map errorMap = new HashMap<>(); errorMap.put("message", exception.toString()); diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebStorageHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebStorageHostApiImpl.java new file mode 100644 index 000000000000..42e7603c0279 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebStorageHostApiImpl.java @@ -0,0 +1,53 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.webviewflutter; + +import android.webkit.WebStorage; +import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebStorageHostApi; + +/** + * Host api implementation for {@link WebStorage}. + * + *

Handles creating {@link WebStorage}s that intercommunicate with a paired Dart object. + */ +public class WebStorageHostApiImpl implements WebStorageHostApi { + private final InstanceManager instanceManager; + private final WebStorageCreator webStorageCreator; + + /** Handles creating {@link WebStorage} for a {@link WebStorageHostApiImpl}. */ + public static class WebStorageCreator { + /** + * Creates a {@link WebStorage}. + * + * @return the created {@link WebStorage}. Defaults to {@link WebStorage#getInstance} + */ + public WebStorage createWebStorage() { + return WebStorage.getInstance(); + } + } + + /** + * Creates a host API that handles creating {@link WebStorage} and invoke its methods. + * + * @param instanceManager maintains instances stored to communicate with Dart objects + * @param webStorageCreator handles creating {@link WebStorage}s + */ + public WebStorageHostApiImpl( + InstanceManager instanceManager, WebStorageCreator webStorageCreator) { + this.instanceManager = instanceManager; + this.webStorageCreator = webStorageCreator; + } + + @Override + public void create(Long instanceId) { + instanceManager.addInstance(webStorageCreator.createWebStorage(), instanceId); + } + + @Override + public void deleteAllData(Long instanceId) { + final WebStorage webStorage = (WebStorage) instanceManager.getInstance(instanceId); + webStorage.deleteAllData(); + } +} diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java index 4ef622fe47cb..67202ebef16d 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java @@ -19,6 +19,7 @@ import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.JavaScriptChannelHostApi; import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebChromeClientHostApi; import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebSettingsHostApi; +import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebStorageHostApi; import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebViewClientHostApi; import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebViewHostApi; @@ -116,6 +117,9 @@ private void setUp( FlutterAssetManagerHostApi.setup( binaryMessenger, new FlutterAssetManagerHostApiImpl(flutterAssetManager)); CookieManagerHostApi.setup(binaryMessenger, new CookieManagerHostApiImpl()); + WebStorageHostApi.setup( + binaryMessenger, + new WebStorageHostApiImpl(instanceManager, new WebStorageHostApiImpl.WebStorageCreator())); } @Override diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebStorageHostApiImplTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebStorageHostApiImplTest.java new file mode 100644 index 000000000000..e2845c2842a9 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebStorageHostApiImplTest.java @@ -0,0 +1,41 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.webviewflutter; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.webkit.WebStorage; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +public class WebStorageHostApiImplTest { + @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock public WebStorage mockWebStorage; + + @Mock WebStorageHostApiImpl.WebStorageCreator mockWebStorageCreator; + + InstanceManager testInstanceManager; + WebStorageHostApiImpl testHostApiImpl; + + @Before + public void setUp() { + testInstanceManager = new InstanceManager(); + when(mockWebStorageCreator.createWebStorage()).thenReturn(mockWebStorage); + testHostApiImpl = new WebStorageHostApiImpl(testInstanceManager, mockWebStorageCreator); + testHostApiImpl.create(0L); + } + + @Test + public void deleteAllData() { + testHostApiImpl.deleteAllData(0L); + verify(mockWebStorage).deleteAllData(); + } +} diff --git a/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart index 58f2f369bcf5..f1e95288383c 100644 --- a/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart @@ -1377,6 +1377,50 @@ Future main() async { ); }, ); + + testWidgets( + 'clearCache should clear local storage', + (WidgetTester tester) async { + final Completer controllerCompleter = + Completer(); + final Completer onPageFinished = Completer(); + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: WebView( + key: GlobalKey(), + initialUrl: primaryUrl, + javascriptMode: JavascriptMode.unrestricted, + onPageFinished: (_) => onPageFinished.complete(), + onWebViewCreated: (WebViewController controller) { + controllerCompleter.complete(controller); + }, + ), + ), + ); + + final WebViewController controller = await controllerCompleter.future; + await onPageFinished.future; + + await controller.runJavascript('localStorage.setItem("myCat", "Tom");'); + + expect( + controller.runJavascriptReturningResult( + 'localStorage.getItem("myCat");', + ), + completion('"Tom"'), + ); + + await controller.clearCache(); + + expect( + controller.runJavascriptReturningResult( + 'localStorage.getItem("myCat");', + ), + completion('null'), + ); + }, + ); } // JavaScript booleans evaluate to different string values on Android and iOS. diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart index 10989321a9bb..bd50640919f9 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart @@ -852,3 +852,30 @@ class FlutterAssetManager { Future getAssetFilePathByName(String name) => api.getAssetFilePathByName(name); } + +/// Manages the JavaScript storage APIs provided by the [WebView]. +/// +/// Wraps [WebStorage](https://developer.android.com/reference/android/webkit/WebStorage). +class WebStorage { + /// Constructs a [WebStorage]. + /// + /// This constructor is only used for testing. An instance should be obtained + /// with [WebStorage.instance]. + @visibleForTesting + WebStorage() { + AndroidWebViewFlutterApis.instance.ensureSetUp(); + api.createFromInstance(this); + } + + /// Pigeon Host Api implementation for [WebStorage]. + @visibleForTesting + static WebStorageHostApiImpl api = WebStorageHostApiImpl(); + + /// The singleton instance of this class. + static WebStorage instance = WebStorage(); + + /// Clears all storage currently being used by the JavaScript storage APIs. + Future deleteAllData() { + return api.deleteAllDataFromInstance(this); + } +} diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart index 20391c43d966..4a0965eaeac0 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart @@ -1860,3 +1860,69 @@ abstract class WebChromeClientFlutterApi { } } } + +class _WebStorageHostApiCodec extends StandardMessageCodec { + const _WebStorageHostApiCodec(); +} + +class WebStorageHostApi { + /// Constructor for [WebStorageHostApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + WebStorageHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _WebStorageHostApiCodec(); + + Future create(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WebStorageHostApi.create', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future deleteAllData(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WebStorageHostApi.deleteAllData', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart index ead60f6a2b35..9c980c80d58d 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart @@ -787,3 +787,30 @@ class WebChromeClientFlutterApiImpl extends WebChromeClientFlutterApi { instance!.onProgressChanged(webViewInstance!, progress); } } + +/// Host api implementation for [WebStorage]. +class WebStorageHostApiImpl extends WebStorageHostApi { + /// Constructs a [WebStorageHostApiImpl]. + WebStorageHostApiImpl({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : super(binaryMessenger: binaryMessenger) { + this.instanceManager = instanceManager ?? InstanceManager.instance; + } + + /// Maintains instances stored to communicate with java objects. + late final InstanceManager instanceManager; + + /// Helper method to convert instances ids to objects. + Future createFromInstance(WebStorage instance) async { + final int? instanceId = instanceManager.tryAddInstance(instance); + if (instanceId != null) { + return create(instanceId); + } + } + + /// Helper method to convert instances ids to objects. + Future deleteAllDataFromInstance(WebStorage instance) { + return deleteAllData(instanceManager.getInstanceId(instance)!); + } +} diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart index 7200aaa4a322..28d169c9cb94 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart @@ -23,6 +23,7 @@ class WebViewAndroidWidget extends StatefulWidget { @visibleForTesting this.webViewProxy = const WebViewProxy(), @visibleForTesting this.flutterAssetManager = const android_webview.FlutterAssetManager(), + @visibleForTesting this.webStorage, }); /// Initial parameters used to setup the WebView. @@ -59,6 +60,9 @@ class WebViewAndroidWidget extends StatefulWidget { final Widget Function(WebViewAndroidPlatformController controller) onBuildWidget; + /// Manages the JavaScript storage APIs. + final android_webview.WebStorage? webStorage; + @override State createState() => _WebViewAndroidWidgetState(); } @@ -76,6 +80,7 @@ class _WebViewAndroidWidgetState extends State { javascriptChannelRegistry: widget.javascriptChannelRegistry, webViewProxy: widget.webViewProxy, flutterAssetManager: widget.flutterAssetManager, + webStorage: widget.webStorage, ); } @@ -102,7 +107,9 @@ class WebViewAndroidPlatformController extends WebViewPlatformController { @visibleForTesting this.webViewProxy = const WebViewProxy(), @visibleForTesting this.flutterAssetManager = const android_webview.FlutterAssetManager(), - }) : assert(creationParams.webSettings?.hasNavigationDelegate != null), + @visibleForTesting android_webview.WebStorage? webStorage, + }) : webStorage = webStorage ?? android_webview.WebStorage.instance, + assert(creationParams.webSettings?.hasNavigationDelegate != null), super(callbacksHandler) { webView = webViewProxy.createWebView( useHybridComposition: useHybridComposition, @@ -160,6 +167,9 @@ class WebViewAndroidPlatformController extends WebViewPlatformController { late final WebViewAndroidWebChromeClient webChromeClient = WebViewAndroidWebChromeClient(); + /// Manages the JavaScript storage APIs. + final android_webview.WebStorage webStorage; + /// Receive various notifications and requests for [android_webview.WebView]. @visibleForTesting WebViewAndroidWebViewClient get webViewClient => _webViewClient; @@ -254,7 +264,10 @@ class WebViewAndroidPlatformController extends WebViewPlatformController { Future reload() => webView.reload(); @override - Future clearCache() => webView.clearCache(true); + Future clearCache() { + webView.clearCache(true); + return webStorage.deleteAllData(); + } @override Future updateSettings(WebSettings setting) async { diff --git a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart index b29835266717..d3d18f64f97d 100644 --- a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart +++ b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart @@ -222,3 +222,10 @@ abstract class WebChromeClientFlutterApi { void onProgressChanged(int instanceId, int webViewInstanceId, int progress); } + +@HostApi(dartHostTestHandler: 'TestWebStorageHostApi') +abstract class WebStorageHostApi { + void create(int instanceId); + + void deleteAllData(int instanceId); +} diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 35f0e2f2a5a0..5c3c83abc53d 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_android description: A Flutter plugin that provides a WebView widget on Android. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.8.3 +version: 2.8.4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart index 746126346750..4ee4b1ddc0b7 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart @@ -1157,3 +1157,55 @@ abstract class TestAssetManagerHostApi { } } } + +class _TestWebStorageHostApiCodec extends StandardMessageCodec { + const _TestWebStorageHostApiCodec(); +} + +abstract class TestWebStorageHostApi { + static const MessageCodec codec = _TestWebStorageHostApiCodec(); + + void create(int instanceId); + void deleteAllData(int instanceId); + static void setup(TestWebStorageHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WebStorageHostApi.create', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WebStorageHostApi.create was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WebStorageHostApi.create was null, expected non-null int.'); + api.create(arg_instanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WebStorageHostApi.deleteAllData', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WebStorageHostApi.deleteAllData was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WebStorageHostApi.deleteAllData was null, expected non-null int.'); + api.deleteAllData(arg_instanceId!); + return {}; + }); + } + } + } +} diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart index 91385ff2d364..e2e6513dd841 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart @@ -21,6 +21,7 @@ import 'android_webview_test.mocks.dart'; TestJavaScriptChannelHostApi, TestWebChromeClientHostApi, TestWebSettingsHostApi, + TestWebStorageHostApi, TestWebViewClientHostApi, TestWebViewHostApi, TestAssetManagerHostApi, @@ -677,4 +678,29 @@ void main() { verify(CookieManager.api.clearCookies()); }); }); + + group('WebStorage', () { + late MockTestWebStorageHostApi mockPlatformHostApi; + + late WebStorage webStorage; + late int webStorageInstanceId; + + setUp(() { + mockPlatformHostApi = MockTestWebStorageHostApi(); + TestWebStorageHostApi.setup(mockPlatformHostApi); + + webStorage = WebStorage(); + webStorageInstanceId = + WebStorage.api.instanceManager.getInstanceId(webStorage)!; + }); + + test('create', () { + verify(mockPlatformHostApi.create(webStorageInstanceId)); + }); + + test('deleteAllData', () { + webStorage.deleteAllData(); + verify(mockPlatformHostApi.deleteAllData(webStorageInstanceId)); + }); + }); } diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart index d6023d7de3aa..e8859c9b74c5 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart @@ -201,6 +201,25 @@ class MockTestWebSettingsHostApi extends _i1.Mock returnValueForMissingStub: null); } +/// A class which mocks [TestWebStorageHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestWebStorageHostApi extends _i1.Mock + implements _i5.TestWebStorageHostApi { + MockTestWebStorageHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void create(int? instanceId) => + super.noSuchMethod(Invocation.method(#create, [instanceId]), + returnValueForMissingStub: null); + @override + void deleteAllData(int? instanceId) => + super.noSuchMethod(Invocation.method(#deleteAllData, [instanceId]), + returnValueForMissingStub: null); +} + /// A class which mocks [TestWebViewClientHostApi]. /// /// See the documentation for Mockito's code generation for more information. diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart index af1093914047..46d6a29a1107 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart @@ -23,6 +23,7 @@ import 'webview_android_widget_test.mocks.dart'; @GenerateMocks([ android_webview.FlutterAssetManager, android_webview.WebSettings, + android_webview.WebStorage, android_webview.WebView, WebViewAndroidDownloadListener, WebViewAndroidJavaScriptChannel, @@ -39,6 +40,7 @@ void main() { late MockFlutterAssetManager mockFlutterAssetManager; late MockWebView mockWebView; late MockWebSettings mockWebSettings; + late MockWebStorage mockWebStorage; late MockWebViewProxy mockWebViewProxy; late MockWebViewPlatformCallbacksHandler mockCallbacksHandler; @@ -54,6 +56,7 @@ void main() { mockFlutterAssetManager = MockFlutterAssetManager(); mockWebView = MockWebView(); mockWebSettings = MockWebSettings(); + mockWebStorage = MockWebStorage(); when(mockWebView.settings).thenReturn(mockWebSettings); mockWebViewProxy = MockWebViewProxy(); @@ -86,6 +89,7 @@ void main() { javascriptChannelRegistry: mockJavascriptChannelRegistry, webViewProxy: mockWebViewProxy, flutterAssetManager: mockFlutterAssetManager, + webStorage: mockWebStorage, onBuildWidget: (WebViewAndroidPlatformController controller) { testController = controller; return Container(); @@ -590,6 +594,7 @@ void main() { await testController.clearCache(); verify(mockWebView.clearCache(true)); + verify(mockWebStorage.deleteAllData()); }); testWidgets('evaluateJavascript', (WidgetTester tester) async { diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart index f4d9abbd2d3c..3385e7998ba9 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart @@ -121,6 +121,21 @@ class MockWebSettings extends _i1.Mock implements _i2.WebSettings { returnValueForMissingStub: Future.value()) as _i4.Future); } +/// A class which mocks [WebStorage]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockWebStorage extends _i1.Mock implements _i2.WebStorage { + MockWebStorage() { + _i1.throwOnMissingStub(this); + } + + @override + _i4.Future deleteAllData() => + (super.noSuchMethod(Invocation.method(#deleteAllData, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); +} + /// A class which mocks [WebView]. /// /// See the documentation for Mockito's code generation for more information. From 3647ba11cbeea77eb0f6debec181efcd7748826d Mon Sep 17 00:00:00 2001 From: Kate Lovett Date: Mon, 4 Apr 2022 12:16:13 -0500 Subject: [PATCH 332/600] [camera] Migrate deprecated Scaffold methods to ScaffoldMessenger (#5151) --- packages/camera/camera/CHANGELOG.md | 4 ++++ packages/camera/camera/example/lib/main.dart | 13 +++++++------ .../camera/camera/example/test/main_test.dart | 16 ++++++++++++++++ packages/camera/camera/pubspec.yaml | 2 +- 4 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 packages/camera/camera/example/test/main_test.dart diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 03b9293b6893..cf502221efa0 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.4+19 + +* Migrate deprecated Scaffold SnackBar methods to ScaffoldMessenger. + ## 0.9.4+18 * Fixes a crash in iOS when streaming on low-performance devices. diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index d47edfed69e2..f9f1378176a3 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -10,6 +10,7 @@ import 'dart:io'; import 'package:camera/camera.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; import 'package:video_player/video_player.dart'; class CameraExampleHome extends StatefulWidget { @@ -121,12 +122,9 @@ class _CameraExampleHomeState extends State } } - final GlobalKey _scaffoldKey = GlobalKey(); - @override Widget build(BuildContext context) { return Scaffold( - key: _scaffoldKey, appBar: AppBar( title: const Text('Camera example'), ), @@ -583,7 +581,10 @@ class _CameraExampleHomeState extends State }; if (cameras.isEmpty) { - return const Text('No camera found'); + _ambiguate(SchedulerBinding.instance)?.addPostFrameCallback((_) async { + showInSnackBar('No camera found.'); + }); + return const Text('None'); } else { for (final CameraDescription cameraDescription in cameras) { toggles.add( @@ -609,8 +610,8 @@ class _CameraExampleHomeState extends State String timestamp() => DateTime.now().millisecondsSinceEpoch.toString(); void showInSnackBar(String message) { - // ignore: deprecated_member_use - _scaffoldKey.currentState?.showSnackBar(SnackBar(content: Text(message))); + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text(message))); } void onViewFinderTap(TapDownDetails details, BoxConstraints constraints) { diff --git a/packages/camera/camera/example/test/main_test.dart b/packages/camera/camera/example/test/main_test.dart new file mode 100644 index 000000000000..9a5fcdf2d5ea --- /dev/null +++ b/packages/camera/camera/example/test/main_test.dart @@ -0,0 +1,16 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:camera_example/main.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('Test snackbar', (WidgetTester tester) async { + WidgetsFlutterBinding.ensureInitialized(); + await tester.pumpWidget(CameraApp()); + await tester.pumpAndSettle(); + expect(find.byType(SnackBar), findsOneWidget); + }); +} diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 064eb919c96a..2533583d9a6e 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+18 +version: 0.9.4+19 environment: sdk: ">=2.14.0 <3.0.0" From 55669602815ea64ab04b46d6e40ac0e307622a95 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 4 Apr 2022 15:26:13 -0400 Subject: [PATCH 333/600] Add supported OS version tables to READMEs (#5106) --- .cirrus.yml | 1 + packages/espresso/CHANGELOG.md | 4 + packages/espresso/README.md | 5 +- .../file_selector/file_selector/CHANGELOG.md | 4 + .../file_selector/file_selector/README.md | 4 + .../CHANGELOG.md | 4 + .../README.md | 6 +- .../google_maps_flutter/CHANGELOG.md | 4 + .../google_maps_flutter/README.md | 6 +- .../google_sign_in/CHANGELOG.md | 4 + .../google_sign_in/google_sign_in/README.md | 4 + .../image_picker/image_picker/CHANGELOG.md | 4 + packages/image_picker/image_picker/README.md | 28 +- .../in_app_purchase/CHANGELOG.md | 4 + .../in_app_purchase/in_app_purchase/README.md | 16 +- packages/ios_platform_images/CHANGELOG.md | 4 + packages/ios_platform_images/README.md | 4 + packages/local_auth/local_auth/CHANGELOG.md | 4 + packages/local_auth/local_auth/README.md | 11 +- .../path_provider/path_provider/CHANGELOG.md | 4 + .../path_provider/path_provider/README.md | 7 +- .../quick_actions/quick_actions/CHANGELOG.md | 1 + .../quick_actions/quick_actions/README.md | 17 +- .../shared_preferences/CHANGELOG.md | 4 + .../shared_preferences/README.md | 24 +- .../url_launcher/url_launcher/CHANGELOG.md | 4 + packages/url_launcher/url_launcher/README.md | 9 +- .../video_player/video_player/CHANGELOG.md | 1 + packages/video_player/video_player/README.md | 8 +- .../webview_flutter/CHANGELOG.md | 4 + .../webview_flutter/webview_flutter/README.md | 6 +- script/tool/CHANGELOG.md | 1 + .../lib/src/common/repository_package.dart | 9 + script/tool/lib/src/main.dart | 2 + script/tool/lib/src/readme_check_command.dart | 152 ++++++++++ .../test/common/repository_package_test.dart | 4 + .../tool/test/readme_check_command_test.dart | 278 ++++++++++++++++++ 37 files changed, 609 insertions(+), 47 deletions(-) create mode 100644 script/tool/lib/src/readme_check_command.dart create mode 100644 script/tool/test/readme_check_command_test.dart diff --git a/.cirrus.yml b/.cirrus.yml index 8f6a6f7f6ebe..66e8336301d4 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -101,6 +101,7 @@ task: always: format_script: ./script/tool_runner.sh format --fail-on-change pubspec_script: ./script/tool_runner.sh pubspec-check + readme_script: ./script/tool_runner.sh readme-check license_script: dart $PLUGIN_TOOL license-check - name: federated_safety # This check is only meaningful for PRs, as it validates changes diff --git a/packages/espresso/CHANGELOG.md b/packages/espresso/CHANGELOG.md index a141b23bd4f0..b76cf2ab141b 100644 --- a/packages/espresso/CHANGELOG.md +++ b/packages/espresso/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adds OS version support information to README. + ## 0.2.0 * Updates compileSdkVersion to 31. diff --git a/packages/espresso/README.md b/packages/espresso/README.md index 7747560682e9..68c3b55089ca 100644 --- a/packages/espresso/README.md +++ b/packages/espresso/README.md @@ -2,6 +2,10 @@ Provides bindings for Espresso tests of Flutter Android apps. +| | Android | +|-------------|---------| +| **Support** | SDK 16+ | + ## Installation Add the `espresso` package as a `dev_dependency` in your app's pubspec.yaml. If you're testing the example app of a package, add it as a dev_dependency of the main package as well. @@ -99,4 +103,3 @@ gcloud firebase test android run --type instrumentation \ --results-bucket= \ --results-dir= ``` - diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index 65b3a64475e9..7eeedad37348 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adds OS version support information to README. + ## 0.8.4+1 * Adds README information about macOS entitlements. diff --git a/packages/file_selector/file_selector/README.md b/packages/file_selector/file_selector/README.md index 863164ebf286..544cde8218bd 100644 --- a/packages/file_selector/file_selector/README.md +++ b/packages/file_selector/file_selector/README.md @@ -4,6 +4,10 @@ A Flutter plugin that manages files and interactions with file dialogs. +| | macOS | Web | Windows | +|-------------|--------|-----|-------------| +| **Support** | 10.11+ | Any | Windows 10+ | + ## Usage To use this plugin, add `file_selector` as a [dependency in your pubspec.yaml file](https://flutter.dev/platform-plugins/). diff --git a/packages/flutter_plugin_android_lifecycle/CHANGELOG.md b/packages/flutter_plugin_android_lifecycle/CHANGELOG.md index 8b110b74acfc..8fdfc39f3bf9 100644 --- a/packages/flutter_plugin_android_lifecycle/CHANGELOG.md +++ b/packages/flutter_plugin_android_lifecycle/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adds OS version support information to README. + ## 2.0.5 * Updates compileSdkVersion to 31. diff --git a/packages/flutter_plugin_android_lifecycle/README.md b/packages/flutter_plugin_android_lifecycle/README.md index 3290140f4e5e..2475230d413b 100644 --- a/packages/flutter_plugin_android_lifecycle/README.md +++ b/packages/flutter_plugin_android_lifecycle/README.md @@ -9,6 +9,10 @@ The purpose of having this plugin instead of exposing an Android `Lifecycle` obj Android embedding plugins API is to force plugins to have a pub constraint that signifies the major version of the Android `Lifecycle` API they expect. +| | Android | +|-------------|---------| +| **Support** | SDK 16+ | + ## Installation Add `flutter_plugin_android_lifecycle` as a [dependency in your pubspec.yaml file](https://flutter.dev/using-packages/). @@ -32,7 +36,7 @@ public class MyPlugin implements FlutterPlugin, ActivityAware { Lifecycle lifecycle = FlutterLifecycleAdapter.getActivityLifecycle(binding); // Use lifecycle as desired. } - + //... } ``` diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index f05de47b3036..3ecea7c465e7 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adds OS version support information to README. + ## 2.1.3 * Fixes iOS crash on `EXC_BAD_ACCESS KERN_PROTECTION_FAILURE` if the map frame changes long after creation. diff --git a/packages/google_maps_flutter/google_maps_flutter/README.md b/packages/google_maps_flutter/google_maps_flutter/README.md index 038126f4fdd0..ae9a659c715f 100644 --- a/packages/google_maps_flutter/google_maps_flutter/README.md +++ b/packages/google_maps_flutter/google_maps_flutter/README.md @@ -4,6 +4,10 @@ A Flutter plugin that provides a [Google Maps](https://developers.google.com/maps/) widget. +| | Android | iOS | +|-------------|---------|--------| +| **Support** | SDK 20+ | iOS 9+ | + ## Usage To use this plugin, add `google_maps_flutter` as a [dependency in your pubspec.yaml file](https://flutter.dev/docs/development/platform-integration/platform-channels). @@ -60,7 +64,7 @@ if (defaultTargetPlatform == TargetPlatform.android) { ### iOS -This plugin requires iOS 9.0 or higher. To set up, specify your API key in the application delegate `ios/Runner/AppDelegate.m`: +To set up, specify your API key in the application delegate `ios/Runner/AppDelegate.m`: ```objectivec #include "AppDelegate.h" diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index a46023bfd788..c0a377603b34 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adds OS version support information to README. + ## 5.2.4 * Internal code cleanup for stricter analysis options. diff --git a/packages/google_sign_in/google_sign_in/README.md b/packages/google_sign_in/google_sign_in/README.md index f3787474eeef..2d6fa7cb6a12 100644 --- a/packages/google_sign_in/google_sign_in/README.md +++ b/packages/google_sign_in/google_sign_in/README.md @@ -6,6 +6,10 @@ _Note_: This plugin is still under development, and some APIs might not be available yet. [Feedback](https://github.com/flutter/flutter/issues) and [Pull Requests](https://github.com/flutter/plugins/pulls) are most welcome! +| | Android | iOS | Web | +|-------------|---------|--------|-----| +| **Support** | SDK 16+ | iOS 9+ | Any | + ## Platform integration ### Android integration diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index 2ba5a2ce4ce7..aa4a28511a8a 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adds OS version support information to README. + ## 0.8.4+11 * Fixes Activity leak. diff --git a/packages/image_picker/image_picker/README.md b/packages/image_picker/image_picker/README.md index 46a7795b748a..2fa20be34859 100755 --- a/packages/image_picker/image_picker/README.md +++ b/packages/image_picker/image_picker/README.md @@ -5,6 +5,10 @@ A Flutter plugin for iOS and Android for picking images from the image library, and taking new pictures with the camera. +| | Android | iOS | Web | +|-------------|---------|--------|----------------------------------| +| **Support** | SDK 21+ | iOS 9+ | [See `image_picker_for_web `][1] | + ## Installation First, add `image_picker` as a [dependency in your pubspec.yaml file](https://flutter.dev/docs/development/platform-integration/platform-channels). @@ -26,9 +30,9 @@ Add the following keys to your _Info.plist_ file, located in `/ios Starting with version **0.8.1** the Android implementation support to pick (multiple) images on Android 4.3 or higher. -No configuration required - the plugin should work out of the box. It is +No configuration required - the plugin should work out of the box. It is however highly recommended to prepare for Android killing the application when -low on memory. How to prepare for this is discussed in the [Handling +low on memory. How to prepare for this is discussed in the [Handling MainActivity destruction on Android](#handling-mainactivity-destruction-on-android) section. @@ -60,12 +64,12 @@ import 'package:image_picker/image_picker.dart'; ### Handling MainActivity destruction on Android When under high memory pressure the Android system may kill the MainActivity of -the application using the image_picker. On Android the image_picker makes use -of the default `Intent.ACTION_GET_CONTENT` or `MediaStore.ACTION_IMAGE_CAPTURE` -intents. This means that while the intent is executing the source application +the application using the image_picker. On Android the image_picker makes use +of the default `Intent.ACTION_GET_CONTENT` or `MediaStore.ACTION_IMAGE_CAPTURE` +intents. This means that while the intent is executing the source application is moved to the background and becomes eligable for cleanup when the system is -low on memory. When the intent finishes executing, Android will restart the -application. Since the data is never returned to the original call use the +low on memory. When the intent finishes executing, Android will restart the +application. Since the data is never returned to the original call use the `ImagePicker.retrieveLostData()` method to retrieve the lost data. For example: ```dart @@ -85,9 +89,9 @@ Future getLostData() async { } ``` -This check should always be run at startup in order to detect and handle this -case. Please refer to the -[example app](https://pub.dev/packages/image_picker/example) for a more +This check should always be run at startup in order to detect and handle this +case. Please refer to the +[example app](https://pub.dev/packages/image_picker/example) for a more complete example of handling this flow. ## Migrating to 0.8.2+ @@ -101,4 +105,6 @@ Starting with version **0.8.2** of the image_picker plugin, new methods have bee | `PickedFile image = await _picker.getImage(...)` | `XFile image = await _picker.pickImage(...)` | | `List images = await _picker.getMultiImage(...)` | `List images = await _picker.pickMultiImage(...)` | | `PickedFile video = await _picker.getVideo(...)` | `XFile video = await _picker.pickVideo(...)` | -| `LostData response = await _picker.getLostData()` | `LostDataResponse response = await _picker.retrieveLostData()` | \ No newline at end of file +| `LostData response = await _picker.getLostData()` | `LostDataResponse response = await _picker.retrieveLostData()` | + +[1]: https://pub.dev/packages/image_picker_for_web#limitations-on-the-web-platform diff --git a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md index 6253a23f48db..cfe625a0c5b4 100644 --- a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adds OS version support information to README. + ## 3.0.2 * Adds additional explanation on why it is important to complete a purchase. diff --git a/packages/in_app_purchase/in_app_purchase/README.md b/packages/in_app_purchase/in_app_purchase/README.md index dbc8e8a17bc4..258eba7f50f4 100644 --- a/packages/in_app_purchase/in_app_purchase/README.md +++ b/packages/in_app_purchase/in_app_purchase/README.md @@ -5,6 +5,10 @@ A storefront-independent API for purchases in Flutter apps. This plugin supports in-app purchases (_IAP_) through an _underlying store_, which can be the App Store (on iOS) or Google Play (on Android). +| | Android | iOS | +|-------------|---------|------| +| **Support** | SDK 16+ | 9.0+ | +

An animated image of the iOS in-app purchase UI @@ -32,7 +36,7 @@ your app with each store. Both stores have extensive guides: * [App Store documentation](https://developer.apple.com/in-app-purchase/) * [Google Play documentation](https://developer.android.com/google/play/billing/billing_overview) -> NOTE: Further in this document the App Store and Google Play will be referred +> NOTE: Further in this document the App Store and Google Play will be referred > to as "the store" or "the underlying store", except when a feature is specific > to a particular store. @@ -195,12 +199,12 @@ if (_isConsumable(productDetails)) { ### Completing a purchase The `InAppPurchase.purchaseStream` will send purchase updates after initiating -the purchase flow using `InAppPurchase.buyConsumable` or -`InAppPurchase.buyNonConsumable`. After verifying the purchase receipt and the -delivering the content to the user it is important to call +the purchase flow using `InAppPurchase.buyConsumable` or +`InAppPurchase.buyNonConsumable`. After verifying the purchase receipt and the +delivering the content to the user it is important to call `InAppPurchase.completePurchase` to tell the underlying store that the -purchase has been completed. Calling `InAppPurchase.completePurchase` will -inform the underlying store that the app verified and processed the +purchase has been completed. Calling `InAppPurchase.completePurchase` will +inform the underlying store that the app verified and processed the purchase and the store can proceed to finalize the transaction and bill the end user's payment account. diff --git a/packages/ios_platform_images/CHANGELOG.md b/packages/ios_platform_images/CHANGELOG.md index 416f93661c8d..f2fb43dec1d1 100644 --- a/packages/ios_platform_images/CHANGELOG.md +++ b/packages/ios_platform_images/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adds OS version support information to README. + ## 0.2.0+4 * Internal code cleanup for stricter analysis options. diff --git a/packages/ios_platform_images/README.md b/packages/ios_platform_images/README.md index ada89fcdffec..08dfc3e40b31 100644 --- a/packages/ios_platform_images/README.md +++ b/packages/ios_platform_images/README.md @@ -8,6 +8,10 @@ Flutter images. When loading images from Image.xcassets the device specific variant is chosen ([iOS documentation](https://developer.apple.com/design/human-interface-guidelines/ios/icons-and-images/image-size-and-resolution/)). +| | iOS | +|-------------|------| +| **Support** | 9.0+ | + ## Usage ### iOS->Flutter Example diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index 9387540d6795..a2f93cad6286 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adds OS version support information to README. + ## 1.1.11 * Adds support `localizedFallbackTitle` in authenticateWithBiometrics on iOS. diff --git a/packages/local_auth/local_auth/README.md b/packages/local_auth/local_auth/README.md index 84470c646e6b..3a60e45a57eb 100644 --- a/packages/local_auth/local_auth/README.md +++ b/packages/local_auth/local_auth/README.md @@ -6,7 +6,11 @@ the user. This means referring to biometric authentication on iOS (Touch ID or lock code) and the fingerprint APIs on Android (introduced in Android 6.0). -## Usage in Dart +| | Android | iOS | +|-------------|-----------|------| +| **Support** | SDK 16+\* | 9.0+ | + +## Usage Import the relevant file: @@ -134,6 +138,11 @@ try { } ``` +### Android + +\* The plugin will build and run on SDK 16+, but `isDeviceSupported()` will +always return false before SDK 23 (Android 6.0). + ## iOS Integration Note that this plugin works with both Touch ID and Face ID. However, to use the latter, diff --git a/packages/path_provider/path_provider/CHANGELOG.md b/packages/path_provider/path_provider/CHANGELOG.md index d71ebf70c796..4714ad9ac15d 100644 --- a/packages/path_provider/path_provider/CHANGELOG.md +++ b/packages/path_provider/path_provider/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adds OS version support information to README. + ## 2.0.9 * Updates documentation on README.md. diff --git a/packages/path_provider/path_provider/README.md b/packages/path_provider/path_provider/README.md index 20d888ff8d73..e79234ffc07c 100644 --- a/packages/path_provider/path_provider/README.md +++ b/packages/path_provider/path_provider/README.md @@ -2,10 +2,14 @@ [![pub package](https://img.shields.io/pub/v/path_provider.svg)](https://pub.dev/packages/path_provider) -A Flutter plugin for finding commonly used locations on the filesystem. +A Flutter plugin for finding commonly used locations on the filesystem. Supports Android, iOS, Linux, macOS and Windows. Not all methods are supported on all platforms. +| | Android | iOS | Linux | macOS | Windows | +|-------------|---------|------|-------|--------|-------------| +| **Support** | SDK 16+ | 9.0+ | Any | 10.11+ | Windows 10+ | + ## Usage To use this plugin, add `path_provider` as a [dependency in your pubspec.yaml file](https://flutter.dev/docs/development/platform-integration/platform-channels). @@ -40,4 +44,3 @@ Directories support by platform: With that change, tests should be updated to mock `PathProviderPlatform` rather than `PlatformChannel`. See this `path_provider` [test](https://github.com/flutter/plugins/blob/master/packages/path_provider/path_provider/test/path_provider_test.dart) for an example. - diff --git a/packages/quick_actions/quick_actions/CHANGELOG.md b/packages/quick_actions/quick_actions/CHANGELOG.md index e95d56c53e9d..2a764a57e59b 100644 --- a/packages/quick_actions/quick_actions/CHANGELOG.md +++ b/packages/quick_actions/quick_actions/CHANGELOG.md @@ -1,6 +1,7 @@ ## NEXT * Updates minimum Flutter version to 2.8. +* Adds OS version support information to README. ## 0.6.0+10 diff --git a/packages/quick_actions/quick_actions/README.md b/packages/quick_actions/quick_actions/README.md index 46e87fa0b241..10bae164d534 100644 --- a/packages/quick_actions/quick_actions/README.md +++ b/packages/quick_actions/quick_actions/README.md @@ -7,10 +7,13 @@ Quick actions refer to the [eponymous concept](https://developer.apple.com/design/human-interface-guidelines/ios/system-capabilities/home-screen-actions/) on iOS and to the [App Shortcuts](https://developer.android.com/guide/topics/ui/shortcuts.html) APIs on -Android (introduced in Android 7.1 / API level 25). It is safe to run this plugin -with earlier versions of Android as it will produce a noop. +Android. -## Usage in Dart +| | Android | iOS | +|-------------|-----------|------| +| **Support** | SDK 16+\* | 9.0+ | + +## Usage Initialize the library early in your application's lifecycle by providing a callback, which will then be called whenever the user launches the app via a @@ -40,9 +43,7 @@ Please note, that the `type` argument should be unique within your application name of the native resource (xcassets on iOS or drawable on Android) that the app will display for the quick action. -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). +### Android -For help on editing plugin code, view the [documentation](https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin). +\* The plugin will compile and run on SDK 16+, but will be a no-op below SDK 25 +(Android 7.1). diff --git a/packages/shared_preferences/shared_preferences/CHANGELOG.md b/packages/shared_preferences/shared_preferences/CHANGELOG.md index 36e60cc35763..84566e26e2c0 100644 --- a/packages/shared_preferences/shared_preferences/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adds OS version support information to README. + ## 2.0.13 * Updates documentation on README.md. diff --git a/packages/shared_preferences/shared_preferences/README.md b/packages/shared_preferences/shared_preferences/README.md index d3295ac6f6b9..03975ff021e6 100644 --- a/packages/shared_preferences/shared_preferences/README.md +++ b/packages/shared_preferences/shared_preferences/README.md @@ -3,13 +3,17 @@ [![pub package](https://img.shields.io/pub/v/shared_preferences.svg)](https://pub.dev/packages/shared_preferences) Wraps platform-specific persistent storage for simple data -(NSUserDefaults on iOS and macOS, SharedPreferences on Android, etc.). +(NSUserDefaults on iOS and macOS, SharedPreferences on Android, etc.). Data may be persisted to disk asynchronously, and there is no guarantee that writes will be persisted to disk after returning, so this plugin must not be used for storing critical data. Supported data types are `int`, `double`, `bool`, `String` and `List`. +| | Android | iOS | Linux | macOS | Web | Windows | +|-------------|---------|------|-------|--------|-----|-------------| +| **Support** | SDK 16+ | 9.0+ | Any | 10.11+ | Any | Any | + ## Usage To use this plugin, add `shared_preferences` as a [dependency in your pubspec.yaml file](https://flutter.dev/docs/development/platform-integration/platform-channels). @@ -17,24 +21,24 @@ To use this plugin, add `shared_preferences` as a [dependency in your pubspec.ya Here are small examples that show you how to use the API. #### Write data -```dart +```dart // Obtain shared preferences. final prefs = await SharedPreferences.getInstance(); -// Save an integer value to 'counter' key. +// Save an integer value to 'counter' key. await prefs.setInt('counter', 10); -// Save an boolean value to 'repeat' key. +// Save an boolean value to 'repeat' key. await prefs.setBool('repeat', true); -// Save an double value to 'decimal' key. +// Save an double value to 'decimal' key. await prefs.setDouble('decimal', 1.5); -// Save an String value to 'action' key. +// Save an String value to 'action' key. await prefs.setString('action', 'Start'); -// Save an list of strings to 'items' key. +// Save an list of strings to 'items' key. await prefs.setStringList('items', ['Earth', 'Moon', 'Sun']); ``` #### Read data -```dart +```dart // Try reading data from the 'counter' key. If it doesn't exist, returns null. final int? counter = prefs.getInt('counter'); // Try reading data from the 'repeat' key. If it doesn't exist, returns null. @@ -48,8 +52,8 @@ final List? items = prefs.getStringList('items'); ``` #### Remove an entry -```dart -// Remove data for the 'counter' key. +```dart +// Remove data for the 'counter' key. final success = await prefs.remove('counter'); ``` diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index 1634dcdab0f9..0c38f4847d9b 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adds OS version support information to README. + ## 6.0.20 * Fixes a typo in `default_package` registration for Windows, macOS, and Linux. diff --git a/packages/url_launcher/url_launcher/README.md b/packages/url_launcher/url_launcher/README.md index a4ffb6241f6c..7f8699eceba7 100644 --- a/packages/url_launcher/url_launcher/README.md +++ b/packages/url_launcher/url_launcher/README.md @@ -2,8 +2,11 @@ [![pub package](https://img.shields.io/pub/v/url_launcher.svg)](https://pub.dev/packages/url_launcher) -A Flutter plugin for launching a URL. Supports -iOS, Android, web, Windows, macOS, and Linux. +A Flutter plugin for launching a URL. + +| | Android | iOS | Linux | macOS | Web | Windows | +|-------------|---------|------|-------|--------|-----|-------------| +| **Support** | SDK 16+ | 9.0+ | Any | 10.11+ | Any | Windows 10+ | ## Usage @@ -186,5 +189,5 @@ if (await File(uri.toFilePath()).exists()) { ### macOS file access configuration -If you need to access files outside of your application's sandbox, you will need to have the necessary +If you need to access files outside of your application's sandbox, you will need to have the necessary [entitlements](https://docs.flutter.dev/desktop#entitlements-and-the-app-sandbox). diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 5d56b1585a80..d9d1983fe0ad 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,6 +1,7 @@ ## NEXT * Updates minimum Flutter version to 2.10. +* Adds OS version support information to README. ## 2.3.0 diff --git a/packages/video_player/video_player/README.md b/packages/video_player/video_player/README.md index 84b3ae7dfb54..91c1bdeb7773 100644 --- a/packages/video_player/video_player/README.md +++ b/packages/video_player/video_player/README.md @@ -4,6 +4,10 @@ A Flutter plugin for iOS, Android and Web for playing back video on a Widget surface. +| | Android | iOS | Web | +|-------------|---------|------|-------| +| **Support** | SDK 16+ | 9.0+ | Any\* | + ![The example app running in iOS](https://github.com/flutter/plugins/blob/master/packages/video_player/video_player/doc/demo_ipod.gif?raw=true) ## Installation @@ -31,7 +35,7 @@ Android Manifest file, located in `/android/app/src/main/AndroidMa > The Web platform does **not** suppport `dart:io`, so avoid using the `VideoPlayerController.file` constructor for the plugin. Using the constructor attempts to create a `VideoPlayerController.file` that will throw an `UnimplementedError`. -Different web browsers may have different video-playback capabilities (supported formats, autoplay...). Check [package:video_player_web](https://pub.dev/packages/video_player_web) for more web-specific information. +\* Different web browsers may have different video-playback capabilities (supported formats, autoplay...). Check [package:video_player_web](https://pub.dev/packages/video_player_web) for more web-specific information. The `VideoPlayerOptions.mixWithOthers` option can't be implemented in web, at least at the moment. If you use this option in web it will be silently ignored. @@ -119,7 +123,7 @@ This is not complete as of now. You can contribute to this section by [opening a You can set the playback speed on your `_controller` (instance of `VideoPlayerController`) by calling `_controller.setPlaybackSpeed`. `setPlaybackSpeed` takes a `double` speed value indicating -the rate of playback for your video. +the rate of playback for your video. For example, when given a value of `2.0`, your video will play at 2x the regular playback speed and so on. diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index 34b56d61bea8..dde4472eed57 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adds OS version support information to README. + ## 3.0.1 * Removes a duplicate Android-specific integration test. diff --git a/packages/webview_flutter/webview_flutter/README.md b/packages/webview_flutter/webview_flutter/README.md index 8387eb685b7d..ffe91441326d 100644 --- a/packages/webview_flutter/webview_flutter/README.md +++ b/packages/webview_flutter/webview_flutter/README.md @@ -7,6 +7,10 @@ A Flutter plugin that provides a WebView widget. On iOS the WebView widget is backed by a [WKWebView](https://developer.apple.com/documentation/webkit/wkwebview); On Android the WebView widget is backed by a [WebView](https://developer.android.com/reference/android/webkit/WebView). +| | Android | iOS | +|-------------|----------------|------| +| **Support** | SDK 19+ or 20+ | 9.0+ | + ## Usage Add `webview_flutter` as a [dependency in your pubspec.yaml file](https://flutter.dev/docs/development/platform-integration/platform-channels). If you are targeting Android, make sure to read the *Android Platform Views* section below to choose the platform view mode that best suits your needs. @@ -91,4 +95,4 @@ follow the steps described in the [Enabling Material Components instructions](ht ### Setting custom headers on POST requests Currently, setting custom headers when making a post request with the WebViewController's `loadRequest` method is not supported on Android. -If you require this functionality, a workaround is to make the request manually, and then load the response data using `loadHTMLString` instead. \ No newline at end of file +If you require this functionality, a workaround is to make the request manually, and then load the response data using `loadHTMLString` instead. diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 45f7f527e22c..df76e4819e63 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +- Adds a new `readme-check` command. - Updates `publish-plugin` command documentation. ## 0.8.2 diff --git a/script/tool/lib/src/common/repository_package.dart b/script/tool/lib/src/common/repository_package.dart index e0c4e4a83bfe..72e7f948fe9e 100644 --- a/script/tool/lib/src/common/repository_package.dart +++ b/script/tool/lib/src/common/repository_package.dart @@ -48,6 +48,9 @@ class RepositoryPackage { /// The package's top-level pubspec.yaml. File get pubspecFile => directory.childFile('pubspec.yaml'); + /// The package's top-level README. + File get readmeFile => directory.childFile('README.md'); + late final Pubspec _parsedPubspec = Pubspec.parse(pubspecFile.readAsStringSync()); @@ -62,6 +65,12 @@ class RepositoryPackage { directory.parent.basename != 'packages' && directory.basename.startsWith(directory.parent.basename); + /// True if this appears to be the app-facing package of a federated plugin, + /// according to repository conventions. + bool get isAppFacing => + directory.parent.basename != 'packages' && + directory.basename == directory.parent.basename; + /// True if this appears to be a platform interface package, according to /// repository conventions. bool get isPlatformInterface => diff --git a/script/tool/lib/src/main.dart b/script/tool/lib/src/main.dart index 5a71a0a8889d..aa1cf300bd2b 100644 --- a/script/tool/lib/src/main.dart +++ b/script/tool/lib/src/main.dart @@ -26,6 +26,7 @@ import 'native_test_command.dart'; import 'publish_check_command.dart'; import 'publish_plugin_command.dart'; import 'pubspec_check_command.dart'; +import 'readme_check_command.dart'; import 'test_command.dart'; import 'version_check_command.dart'; import 'xcode_analyze_command.dart'; @@ -65,6 +66,7 @@ void main(List args) { ..addCommand(PublishCheckCommand(packagesDir)) ..addCommand(PublishPluginCommand(packagesDir)) ..addCommand(PubspecCheckCommand(packagesDir)) + ..addCommand(ReadmeCheckCommand(packagesDir)) ..addCommand(TestCommand(packagesDir)) ..addCommand(VersionCheckCommand(packagesDir)) ..addCommand(XcodeAnalyzeCommand(packagesDir)); diff --git a/script/tool/lib/src/readme_check_command.dart b/script/tool/lib/src/readme_check_command.dart new file mode 100644 index 000000000000..99e271c73388 --- /dev/null +++ b/script/tool/lib/src/readme_check_command.dart @@ -0,0 +1,152 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file/file.dart'; +import 'package:git/git.dart'; +import 'package:platform/platform.dart'; +import 'package:pubspec_parse/pubspec_parse.dart'; +import 'package:yaml/yaml.dart'; + +import 'common/core.dart'; +import 'common/package_looping_command.dart'; +import 'common/process_runner.dart'; +import 'common/repository_package.dart'; + +/// A command to enforce README conventions across the repository. +class ReadmeCheckCommand extends PackageLoopingCommand { + /// Creates an instance of the README check command. + ReadmeCheckCommand( + Directory packagesDir, { + ProcessRunner processRunner = const ProcessRunner(), + Platform platform = const LocalPlatform(), + GitDir? gitDir, + }) : super( + packagesDir, + processRunner: processRunner, + platform: platform, + gitDir: gitDir, + ); + + // Standardized capitalizations for platforms that a plugin can support. + static const Map _standardPlatformNames = { + 'android': 'Android', + 'ios': 'iOS', + 'linux': 'Linux', + 'macos': 'macOS', + 'web': 'Web', + 'windows': 'Windows', + }; + + @override + final String name = 'readme-check'; + + @override + final String description = + 'Checks that READMEs follow repository conventions.'; + + @override + bool get hasLongOutput => false; + + @override + Future runForPackage(RepositoryPackage package) async { + final File readme = package.readmeFile; + + if (!readme.existsSync()) { + return PackageResult.fail(['Missing README.md']); + } + + final List errors = []; + + final Pubspec pubspec = package.parsePubspec(); + final bool isPlugin = pubspec.flutter?['plugin'] != null; + + if (isPlugin && (!package.isFederated || package.isAppFacing)) { + final String? error = _validateSupportedPlatforms(package, pubspec); + if (error != null) { + errors.add(error); + } + } + + return errors.isEmpty + ? PackageResult.success() + : PackageResult.fail(errors); + } + + /// Validates that the plugin has a supported platforms table following the + /// expected format, returning an error string if any issues are found. + String? _validateSupportedPlatforms( + RepositoryPackage package, Pubspec pubspec) { + final List contents = package.readmeFile.readAsLinesSync(); + + // Example table following expected format: + // | | Android | iOS | Web | + // |----------------|---------|----------|------------------------| + // | **Support** | SDK 21+ | iOS 10+* | [See `camera_web `][1] | + final int detailsLineNumber = + contents.indexWhere((String line) => line.startsWith('| **Support**')); + if (detailsLineNumber == -1) { + return 'No OS support table found'; + } + final int osLineNumber = detailsLineNumber - 2; + if (osLineNumber < 0 || !contents[osLineNumber].startsWith('|')) { + return 'OS support table does not have the expected header format'; + } + + // Utility method to convert an iterable of strings to a case-insensitive + // sorted, comma-separated string of its elements. + String sortedListString(Iterable entries) { + final List entryList = entries.toList(); + entryList.sort( + (String a, String b) => a.toLowerCase().compareTo(b.toLowerCase())); + return entryList.join(', '); + } + + // Validate that the supported OS lists match. + final dynamic platformsEntry = pubspec.flutter!['plugin']!['platforms']; + if (platformsEntry == null) { + logWarning('Plugin not support any platforms'); + return null; + } + final YamlMap platformSupportMaps = platformsEntry as YamlMap; + final Set actuallySupportedPlatform = + platformSupportMaps.keys.toSet().cast(); + final Iterable documentedPlatforms = contents[osLineNumber] + .split('|') + .map((String entry) => entry.trim()) + .where((String entry) => entry.isNotEmpty); + final Set documentedPlatformsLowercase = + documentedPlatforms.map((String entry) => entry.toLowerCase()).toSet(); + if (actuallySupportedPlatform.length != documentedPlatforms.length || + actuallySupportedPlatform + .intersection(documentedPlatformsLowercase) + .length != + actuallySupportedPlatform.length) { + printError(''' +${indentation}OS support table does not match supported platforms: +${indentation * 2}Actual: ${sortedListString(actuallySupportedPlatform)} +${indentation * 2}Documented: ${sortedListString(documentedPlatformsLowercase)} +'''); + return 'Incorrect OS support table'; + } + + // Enforce a standard set of capitalizations for the OS headings. + final Iterable incorrectCapitalizations = documentedPlatforms + .toSet() + .difference(_standardPlatformNames.values.toSet()); + if (incorrectCapitalizations.isNotEmpty) { + final Iterable expectedVersions = incorrectCapitalizations + .map((String name) => _standardPlatformNames[name.toLowerCase()]!); + printError(''' +${indentation}Incorrect OS capitalization: ${sortedListString(incorrectCapitalizations)} +${indentation * 2}Please use standard capitalizations: ${sortedListString(expectedVersions)} +'''); + return 'Incorrect OS support formatting'; + } + + // TODO(stuartmorgan): Add validation that the minimums in the table are + // consistent with what the current implementations require. See + // https://github.com/flutter/flutter/issues/84200 + return null; + } +} diff --git a/script/tool/test/common/repository_package_test.dart b/script/tool/test/common/repository_package_test.dart index 29e3b5832127..0a8ea36eb905 100644 --- a/script/tool/test/common/repository_package_test.dart +++ b/script/tool/test/common/repository_package_test.dart @@ -126,6 +126,7 @@ void main() { test('all return false for a simple plugin', () { final Directory plugin = createFakePlugin('a_plugin', packagesDir); expect(RepositoryPackage(plugin).isFederated, false); + expect(RepositoryPackage(plugin).isAppFacing, false); expect(RepositoryPackage(plugin).isPlatformInterface, false); expect(RepositoryPackage(plugin).isFederated, false); }); @@ -134,6 +135,7 @@ void main() { final Directory plugin = createFakePlugin('a_plugin', packagesDir.childDirectory('a_plugin')); expect(RepositoryPackage(plugin).isFederated, true); + expect(RepositoryPackage(plugin).isAppFacing, true); expect(RepositoryPackage(plugin).isPlatformInterface, false); expect(RepositoryPackage(plugin).isPlatformImplementation, false); }); @@ -142,6 +144,7 @@ void main() { final Directory plugin = createFakePlugin('a_plugin_platform_interface', packagesDir.childDirectory('a_plugin')); expect(RepositoryPackage(plugin).isFederated, true); + expect(RepositoryPackage(plugin).isAppFacing, false); expect(RepositoryPackage(plugin).isPlatformInterface, true); expect(RepositoryPackage(plugin).isPlatformImplementation, false); }); @@ -152,6 +155,7 @@ void main() { final Directory plugin = createFakePlugin( 'a_plugin_foo', packagesDir.childDirectory('a_plugin')); expect(RepositoryPackage(plugin).isFederated, true); + expect(RepositoryPackage(plugin).isAppFacing, false); expect(RepositoryPackage(plugin).isPlatformInterface, false); expect(RepositoryPackage(plugin).isPlatformImplementation, true); }); diff --git a/script/tool/test/readme_check_command_test.dart b/script/tool/test/readme_check_command_test.dart new file mode 100644 index 000000000000..aec2fa078454 --- /dev/null +++ b/script/tool/test/readme_check_command_test.dart @@ -0,0 +1,278 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:args/command_runner.dart'; +import 'package:file/file.dart'; +import 'package:file/memory.dart'; +import 'package:flutter_plugin_tools/src/common/core.dart'; +import 'package:flutter_plugin_tools/src/common/plugin_utils.dart'; +import 'package:flutter_plugin_tools/src/readme_check_command.dart'; +import 'package:test/test.dart'; + +import 'mocks.dart'; +import 'util.dart'; + +void main() { + late CommandRunner runner; + late RecordingProcessRunner processRunner; + late FileSystem fileSystem; + late MockPlatform mockPlatform; + late Directory packagesDir; + + setUp(() { + fileSystem = MemoryFileSystem(); + mockPlatform = MockPlatform(); + packagesDir = fileSystem.currentDirectory.childDirectory('packages'); + createPackagesDirectory(parentDir: packagesDir.parent); + processRunner = RecordingProcessRunner(); + final ReadmeCheckCommand command = ReadmeCheckCommand( + packagesDir, + processRunner: processRunner, + platform: mockPlatform, + ); + + runner = CommandRunner( + 'readme_check_command', 'Test for readme_check_command'); + runner.addCommand(command); + }); + + test('fails when README is missing', () async { + createFakePackage('a_package', packagesDir); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('Missing README.md'), + ]), + ); + }); + + group('plugin OS support', () { + test( + 'does not check support table for anything other than app-facing plugin packages', + () async { + const String federatedPluginName = 'a_federated_plugin'; + final Directory federatedDir = + packagesDir.childDirectory(federatedPluginName); + final List packageDirectories = [ + // A non-plugin package. + createFakePackage('a_package', packagesDir), + // Non-app-facing parts of a federated plugin. + createFakePlugin( + '${federatedPluginName}_platform_interface', federatedDir), + createFakePlugin('${federatedPluginName}_android', federatedDir), + ]; + + for (final Directory package in packageDirectories) { + package.childFile('README.md').writeAsStringSync(''' +A very useful package. +'''); + } + + final List output = await runCapturingPrint(runner, [ + 'readme-check', + ]); + + expect( + output, + containsAll([ + contains('Running for a_package...'), + contains('Running for a_federated_plugin_platform_interface...'), + contains('Running for a_federated_plugin_android...'), + contains('No issues found!'), + ]), + ); + }); + + test('fails when non-federated plugin is missing an OS support table', + () async { + final Directory pluginDir = createFakePlugin('a_plugin', packagesDir); + + pluginDir.childFile('README.md').writeAsStringSync(''' +A very useful plugin. +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('No OS support table found'), + ]), + ); + }); + + test( + 'fails when app-facing part of a federated plugin is missing an OS support table', + () async { + final Directory pluginDir = + createFakePlugin('a_plugin', packagesDir.childDirectory('a_plugin')); + + pluginDir.childFile('README.md').writeAsStringSync(''' +A very useful plugin. +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('No OS support table found'), + ]), + ); + }); + + test('fails the OS support table is missing the header', () async { + final Directory pluginDir = createFakePlugin('a_plugin', packagesDir); + + pluginDir.childFile('README.md').writeAsStringSync(''' +A very useful plugin. + +| **Support** | SDK 21+ | iOS 10+* | [See `camera_web `][1] | +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('OS support table does not have the expected header format'), + ]), + ); + }); + + test('fails if the OS support table is missing a supported OS', () async { + final Directory pluginDir = createFakePlugin( + 'a_plugin', + packagesDir, + platformSupport: { + platformAndroid: const PlatformDetails(PlatformSupport.inline), + platformIOS: const PlatformDetails(PlatformSupport.inline), + platformWeb: const PlatformDetails(PlatformSupport.inline), + }, + ); + + pluginDir.childFile('README.md').writeAsStringSync(''' +A very useful plugin. + +| | Android | iOS | +|----------------|---------|----------| +| **Support** | SDK 21+ | iOS 10+* | +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains(' OS support table does not match supported platforms:\n' + ' Actual: android, ios, web\n' + ' Documented: android, ios'), + contains('Incorrect OS support table'), + ]), + ); + }); + + test('fails if the OS support table lists an extra OS', () async { + final Directory pluginDir = createFakePlugin( + 'a_plugin', + packagesDir, + platformSupport: { + platformAndroid: const PlatformDetails(PlatformSupport.inline), + platformIOS: const PlatformDetails(PlatformSupport.inline), + }, + ); + + pluginDir.childFile('README.md').writeAsStringSync(''' +A very useful plugin. + +| | Android | iOS | Web | +|----------------|---------|----------|------------------------| +| **Support** | SDK 21+ | iOS 10+* | [See `camera_web `][1] | +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains(' OS support table does not match supported platforms:\n' + ' Actual: android, ios\n' + ' Documented: android, ios, web'), + contains('Incorrect OS support table'), + ]), + ); + }); + + test('fails if the OS support table has unexpected OS formatting', + () async { + final Directory pluginDir = createFakePlugin( + 'a_plugin', + packagesDir, + platformSupport: { + platformAndroid: const PlatformDetails(PlatformSupport.inline), + platformIOS: const PlatformDetails(PlatformSupport.inline), + platformMacOS: const PlatformDetails(PlatformSupport.inline), + platformWeb: const PlatformDetails(PlatformSupport.inline), + }, + ); + + pluginDir.childFile('README.md').writeAsStringSync(''' +A very useful plugin. + +| | android | ios | MacOS | web | +|----------------|---------|----------|-------|------------------------| +| **Support** | SDK 21+ | iOS 10+* | 10.11 | [See `camera_web `][1] | +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains(' Incorrect OS capitalization: android, ios, MacOS, web\n' + ' Please use standard capitalizations: Android, iOS, macOS, Web\n'), + contains('Incorrect OS support formatting'), + ]), + ); + }); + }); +} From df6da95528c7df4efdfe5f607074a27019f20191 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Mon, 4 Apr 2022 22:26:10 +0200 Subject: [PATCH 334/600] [in_app_purchase] Make sure unsupported userInfo doesn't crash App (#5111) --- .../in_app_purchase_storekit/CHANGELOG.md | 4 ++ .../example/ios/RunnerTests/TranslatorTests.m | 50 +++++++++++++++++++ .../ios/Classes/FIAObjectTranslator.m | 37 +++++++++++--- .../in_app_purchase_storekit/pubspec.yaml | 2 +- 4 files changed, 85 insertions(+), 8 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md index 087c6e3095db..d614baaf2305 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.0+4 + +* Ensures that `NSError` instances with an unexpected value for the `userInfo` field don't crash the app, but send an explanatory message instead. + ## 0.3.0+3 * Implements transaction caching for StoreKit ensuring transactions are delivered to the Flutter client. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/TranslatorTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/TranslatorTests.m index 0f689f602de8..c4e1ac1d059d 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/TranslatorTests.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/TranslatorTests.m @@ -158,6 +158,56 @@ - (void)testError { XCTAssertEqualObjects(map, self.errorMap); } +- (void)testErrorWithNSNumberAsUserInfo { + NSError *error = [NSError errorWithDomain:SKErrorDomain code:3 userInfo:@{@"key" : @42}]; + NSDictionary *expectedMap = + @{@"domain" : SKErrorDomain, @"code" : @3, @"userInfo" : @{@"key" : @42}}; + NSDictionary *map = [FIAObjectTranslator getMapFromNSError:error]; + XCTAssertEqualObjects(expectedMap, map); +} + +- (void)testErrorWithMultipleUnderlyingErrors { + NSError *underlyingErrorOne = [NSError errorWithDomain:SKErrorDomain code:2 userInfo:nil]; + NSError *underlyingErrorTwo = [NSError errorWithDomain:SKErrorDomain code:1 userInfo:nil]; + NSError *mainError = [NSError + errorWithDomain:SKErrorDomain + code:3 + userInfo:@{@"underlyingErrors" : @[ underlyingErrorOne, underlyingErrorTwo ]}]; + NSDictionary *expectedMap = @{ + @"domain" : SKErrorDomain, + @"code" : @3, + @"userInfo" : @{ + @"underlyingErrors" : @[ + @{@"domain" : SKErrorDomain, @"code" : @2, @"userInfo" : @{}}, + @{@"domain" : SKErrorDomain, @"code" : @1, @"userInfo" : @{}} + ] + } + }; + NSDictionary *map = [FIAObjectTranslator getMapFromNSError:mainError]; + XCTAssertEqualObjects(expectedMap, map); +} + +- (void)testErrorWithUnsupportedUserInfo { + NSError *error = [NSError errorWithDomain:SKErrorDomain + code:3 + userInfo:@{@"user_info" : [[NSObject alloc] init]}]; + NSDictionary *expectedMap = @{ + @"domain" : SKErrorDomain, + @"code" : @3, + @"userInfo" : @{ + @"user_info" : [NSString + stringWithFormat: + @"Unable to encode native userInfo object of type %@ to map. Please submit an " + @"issue at https://github.com/flutter/flutter/issues/new with the title " + @"\"[in_app_purchase_storekit] Unable to encode userInfo of type %@\" and add " + @"reproduction steps and the error details in the description field.", + [NSObject class], [NSObject class]] + } + }; + NSDictionary *map = [FIAObjectTranslator getMapFromNSError:error]; + XCTAssertEqualObjects(expectedMap, map); +} + - (void)testLocaleToMap { if (@available(iOS 10.0, *)) { NSLocale *system = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAObjectTranslator.m b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAObjectTranslator.m index 3ceb512abb10..5d87a68de67c 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAObjectTranslator.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAObjectTranslator.m @@ -167,20 +167,43 @@ + (NSDictionary *)getMapFromNSError:(NSError *)error { if (!error) { return nil; } + NSMutableDictionary *userInfo = [NSMutableDictionary new]; for (NSErrorUserInfoKey key in error.userInfo) { id value = error.userInfo[key]; - if ([value isKindOfClass:[NSError class]]) { - userInfo[key] = [FIAObjectTranslator getMapFromNSError:value]; - } else if ([value isKindOfClass:[NSURL class]]) { - userInfo[key] = [value absoluteString]; - } else { - userInfo[key] = value; - } + userInfo[key] = [FIAObjectTranslator encodeNSErrorUserInfo:value]; } return @{@"code" : @(error.code), @"domain" : error.domain ?: @"", @"userInfo" : userInfo}; } ++ (id)encodeNSErrorUserInfo:(id)value { + if ([value isKindOfClass:[NSError class]]) { + return [FIAObjectTranslator getMapFromNSError:value]; + } else if ([value isKindOfClass:[NSURL class]]) { + return [value absoluteString]; + } else if ([value isKindOfClass:[NSNumber class]]) { + return value; + } else if ([value isKindOfClass:[NSString class]]) { + return value; + } else if ([value isKindOfClass:[NSArray class]]) { + NSMutableArray *errors = [[NSMutableArray alloc] init]; + for (id error in value) { + [errors addObject:[FIAObjectTranslator encodeNSErrorUserInfo:error]]; + } + return errors; + } else { + return [NSString + stringWithFormat: + @"Unable to encode native userInfo object of type %@ to map. Please submit an issue at " + @"https://github.com/flutter/flutter/issues/new with the title " + @"\"[in_app_purchase_storekit] " + @"Unable to encode userInfo of type %@\" and add reproduction steps and the error " + @"details in " + @"the description field.", + [value class], [value class]]; + } +} + + (NSDictionary *)getMapFromSKStorefront:(SKStorefront *)storefront { if (!storefront) { return nil; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml index 51b5ce7f1614..512d2a6ba036 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_storekit description: An implementation for the iOS platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase_storekit issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.3.0+3 +version: 0.3.0+4 environment: sdk: ">=2.14.0 <3.0.0" From 7b94e2f343a8d6f2dad5a353a9586e20d82c86da Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 4 Apr 2022 17:11:09 -0400 Subject: [PATCH 335/600] [image_picker] Federate mobile implementations (#5100) --- .../image_picker/image_picker/CHANGELOG.md | 1 + .../image_picker/android/settings.gradle | 1 - .../image_picker/example/ios/Podfile | 7 - .../ios/Runner.xcodeproj/project.pbxproj | 310 +------ .../image_picker/example/pubspec.yaml | 4 +- .../image_picker/image_picker/pubspec.yaml | 14 +- .../image_picker/image_picker_android/AUTHORS | 66 ++ .../image_picker_android/CHANGELOG.md | 3 + .../image_picker/image_picker_android/LICENSE | 231 +++++ .../image_picker_android/README.md | 11 + .../android/build.gradle | 0 .../android/settings.gradle | 1 + .../android/src/main/AndroidManifest.xml | 0 .../plugins/imagepicker/ExifDataCopier.java | 0 .../plugins/imagepicker/FileUtils.java | 0 .../plugins/imagepicker/ImagePickerCache.java | 0 .../imagepicker/ImagePickerDelegate.java | 0 .../imagepicker/ImagePickerFileProvider.java | 0 .../imagepicker/ImagePickerPlugin.java | 0 .../plugins/imagepicker/ImagePickerUtils.java | 0 .../plugins/imagepicker/ImageResizer.java | 0 .../xml/flutter_image_picker_file_paths.xml | 0 .../plugins/imagepicker/FileUtilTest.java | 0 .../imagepicker/ImagePickerCacheTest.java | 0 .../imagepicker/ImagePickerDelegateTest.java | 0 .../imagepicker/ImagePickerPluginTest.java | 0 .../plugins/imagepicker/ImageResizerTest.java | 0 .../org.mockito.plugins.MockMaker | 0 .../android/src/test/resources/pngImage.png | Bin .../image_picker_android/example/README.md | 8 + .../example/android/app/build.gradle | 67 ++ .../gradle/wrapper/gradle-wrapper.properties | 5 + .../flutter/plugins/DartIntegrationTest.java | 14 + .../FlutterActivityTest.java | 19 + .../imagepickerexample/ImagePickerTest.java | 23 + .../android/app/src/debug/AndroidManifest.xml | 17 + .../android/app/src/main/AndroidManifest.xml | 19 + .../main/java/io/flutter/plugins/.gitignore | 1 + .../ImagePickerTestActivity.java | 20 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../example/android/build.gradle | 29 + .../example/android/gradle.properties | 5 + .../gradle/wrapper/gradle-wrapper.properties | 5 + .../example/android/settings.gradle | 15 + .../integration_test/image_picker_test.dart | 12 + .../example/lib/main.dart | 467 ++++++++++ .../image_picker_android/example/pubspec.yaml | 31 + .../example/test_driver/integration_test.dart | 7 + .../image_picker_android/pubspec.yaml | 28 + .../image_picker/image_picker_ios/AUTHORS | 66 ++ .../image_picker_ios/CHANGELOG.md | 3 + .../image_picker/image_picker_ios/LICENSE | 231 +++++ .../image_picker/image_picker_ios/README.md | 11 + .../image_picker_ios/example/README.md | 8 + .../integration_test/image_picker_test.dart | 12 + .../ios/Flutter/AppFrameworkInfo.plist | 30 + .../example/ios/Flutter/Debug.xcconfig | 2 + .../example/ios/Flutter/Release.xcconfig | 2 + .../image_picker_ios/example/ios/Podfile | 45 + .../ios/Runner.xcodeproj/project.pbxproj | 796 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/xcschemes/Runner.xcscheme | 117 +++ .../xcschemes/RunnerUITests.xcscheme | 52 ++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/ios/Runner/.gitignore | 2 + .../example/ios/Runner/AppDelegate.h | 10 + .../example/ios/Runner/AppDelegate.m | 16 + .../AppIcon.appiconset/Contents.json | 121 +++ .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 564 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 1588 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 1025 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 1716 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 1920 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 1895 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 3831 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 1888 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 3294 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 3612 bytes .../ios/Runner/Assets.xcassets/Contents.json | 6 + .../Runner/Base.lproj/LaunchScreen.storyboard | 27 + .../ios/Runner/Base.lproj/Main.storyboard | 26 + .../example/ios/Runner/Info.plist | 59 ++ .../example/ios/Runner/main.m | 13 + .../ios/RunnerTests/ImagePickerPluginTests.m | 4 +- .../ios/RunnerTests/ImagePickerTestImages.h | 0 .../ios/RunnerTests/ImagePickerTestImages.m | 0 .../example/ios/RunnerTests/ImageUtilTests.m | 4 +- .../example/ios/RunnerTests/Info.plist | 0 .../ios/RunnerTests/MetaDataUtilTests.m | 4 +- .../ios/RunnerTests/PhotoAssetUtilTests.m | 4 +- .../PickerSaveImageToPathOperationTests.m | 4 +- .../ImagePickerFromGalleryUITests.m | 0 .../ImagePickerFromLimitedGalleryUITests.m | 0 .../example/ios/RunnerUITests/Info.plist | 0 .../example/ios/TestImages/gifImage.gif | Bin 0 -> 843 bytes .../example/ios/TestImages/jpgImage.jpg | Bin 0 -> 3290 bytes .../example/ios/TestImages/pngImage.png | Bin 0 -> 593 bytes .../example/ios/TestImages/webpImage.webp | Bin 0 -> 2622 bytes .../ios/image_picker_exampleTests/Info.plist | 22 + .../image_picker_ios/example/lib/main.dart | 467 ++++++++++ .../image_picker_ios/example/pubspec.yaml | 29 + .../example/test_driver/integration_test.dart | 7 + .../ios/Assets/.gitkeep | 0 .../ios/Classes/FLTImagePickerImageUtil.h | 0 .../ios/Classes/FLTImagePickerImageUtil.m | 0 .../ios/Classes/FLTImagePickerMetaDataUtil.h | 0 .../ios/Classes/FLTImagePickerMetaDataUtil.m | 0 .../Classes/FLTImagePickerPhotoAssetUtil.h | 0 .../Classes/FLTImagePickerPhotoAssetUtil.m | 0 .../ios/Classes/FLTImagePickerPlugin.h | 0 .../ios/Classes/FLTImagePickerPlugin.m | 0 .../ios/Classes/FLTImagePickerPlugin_Test.h | 4 +- .../FLTPHPickerSaveImageToPathOperation.h | 0 .../FLTPHPickerSaveImageToPathOperation.m | 0 .../ios/Classes/ImagePickerPlugin.modulemap | 6 +- .../ios/Classes/image_picker_ios-umbrella.h} | 2 +- .../ios/image_picker_ios.podspec} | 6 +- .../image_picker_ios/pubspec.yaml | 26 + 127 files changed, 3367 insertions(+), 343 deletions(-) delete mode 100755 packages/image_picker/image_picker/android/settings.gradle create mode 100644 packages/image_picker/image_picker_android/AUTHORS create mode 100644 packages/image_picker/image_picker_android/CHANGELOG.md create mode 100644 packages/image_picker/image_picker_android/LICENSE create mode 100755 packages/image_picker/image_picker_android/README.md rename packages/image_picker/{image_picker => image_picker_android}/android/build.gradle (100%) create mode 100755 packages/image_picker/image_picker_android/android/settings.gradle rename packages/image_picker/{image_picker => image_picker_android}/android/src/main/AndroidManifest.xml (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/main/java/io/flutter/plugins/imagepicker/ExifDataCopier.java (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerCache.java (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerFileProvider.java (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/main/java/io/flutter/plugins/imagepicker/ImageResizer.java (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/main/res/xml/flutter_image_picker_file_paths.xml (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerCacheTest.java (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerDelegateTest.java (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/test/java/io/flutter/plugins/imagepicker/ImageResizerTest.java (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/test/resources/pngImage.png (100%) create mode 100755 packages/image_picker/image_picker_android/example/README.md create mode 100755 packages/image_picker/image_picker_android/example/android/app/build.gradle create mode 100644 packages/image_picker/image_picker_android/example/android/app/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/image_picker/image_picker_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java create mode 100644 packages/image_picker/image_picker_android/example/android/app/src/androidTest/java/io/flutter/plugins/imagepickerexample/FlutterActivityTest.java create mode 100644 packages/image_picker/image_picker_android/example/android/app/src/androidTest/java/io/flutter/plugins/imagepickerexample/ImagePickerTest.java create mode 100644 packages/image_picker/image_picker_android/example/android/app/src/debug/AndroidManifest.xml create mode 100755 packages/image_picker/image_picker_android/example/android/app/src/main/AndroidManifest.xml create mode 100755 packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/.gitignore create mode 100644 packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/ImagePickerTestActivity.java create mode 100755 packages/image_picker/image_picker_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100755 packages/image_picker/image_picker_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100755 packages/image_picker/image_picker_android/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100755 packages/image_picker/image_picker_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100755 packages/image_picker/image_picker_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100755 packages/image_picker/image_picker_android/example/android/build.gradle create mode 100755 packages/image_picker/image_picker_android/example/android/gradle.properties create mode 100644 packages/image_picker/image_picker_android/example/android/gradle/wrapper/gradle-wrapper.properties create mode 100755 packages/image_picker/image_picker_android/example/android/settings.gradle create mode 100644 packages/image_picker/image_picker_android/example/integration_test/image_picker_test.dart create mode 100755 packages/image_picker/image_picker_android/example/lib/main.dart create mode 100755 packages/image_picker/image_picker_android/example/pubspec.yaml create mode 100644 packages/image_picker/image_picker_android/example/test_driver/integration_test.dart create mode 100755 packages/image_picker/image_picker_android/pubspec.yaml create mode 100644 packages/image_picker/image_picker_ios/AUTHORS create mode 100644 packages/image_picker/image_picker_ios/CHANGELOG.md create mode 100644 packages/image_picker/image_picker_ios/LICENSE create mode 100755 packages/image_picker/image_picker_ios/README.md create mode 100755 packages/image_picker/image_picker_ios/example/README.md create mode 100644 packages/image_picker/image_picker_ios/example/integration_test/image_picker_test.dart create mode 100755 packages/image_picker/image_picker_ios/example/ios/Flutter/AppFrameworkInfo.plist create mode 100755 packages/image_picker/image_picker_ios/example/ios/Flutter/Debug.xcconfig create mode 100755 packages/image_picker/image_picker_ios/example/ios/Flutter/Release.xcconfig create mode 100644 packages/image_picker/image_picker_ios/example/ios/Podfile create mode 100644 packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/project.pbxproj create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/RunnerUITests.xcscheme create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/image_picker/image_picker_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/.gitignore create mode 100644 packages/image_picker/image_picker_ios/example/ios/Runner/AppDelegate.h create mode 100644 packages/image_picker/image_picker_ios/example/ios/Runner/AppDelegate.m create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/Contents.json create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Base.lproj/Main.storyboard create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Info.plist create mode 100644 packages/image_picker/image_picker_ios/example/ios/Runner/main.m rename packages/image_picker/{image_picker => image_picker_ios}/example/ios/RunnerTests/ImagePickerPluginTests.m (99%) rename packages/image_picker/{image_picker => image_picker_ios}/example/ios/RunnerTests/ImagePickerTestImages.h (100%) rename packages/image_picker/{image_picker => image_picker_ios}/example/ios/RunnerTests/ImagePickerTestImages.m (100%) rename packages/image_picker/{image_picker => image_picker_ios}/example/ios/RunnerTests/ImageUtilTests.m (97%) rename packages/image_picker/{image_picker => image_picker_ios}/example/ios/RunnerTests/Info.plist (100%) rename packages/image_picker/{image_picker => image_picker_ios}/example/ios/RunnerTests/MetaDataUtilTests.m (98%) rename packages/image_picker/{image_picker => image_picker_ios}/example/ios/RunnerTests/PhotoAssetUtilTests.m (99%) rename packages/image_picker/{image_picker => image_picker_ios}/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m (98%) rename packages/image_picker/{image_picker => image_picker_ios}/example/ios/RunnerUITests/ImagePickerFromGalleryUITests.m (100%) rename packages/image_picker/{image_picker => image_picker_ios}/example/ios/RunnerUITests/ImagePickerFromLimitedGalleryUITests.m (100%) rename packages/image_picker/{image_picker => image_picker_ios}/example/ios/RunnerUITests/Info.plist (100%) create mode 100644 packages/image_picker/image_picker_ios/example/ios/TestImages/gifImage.gif create mode 100644 packages/image_picker/image_picker_ios/example/ios/TestImages/jpgImage.jpg create mode 100644 packages/image_picker/image_picker_ios/example/ios/TestImages/pngImage.png create mode 100644 packages/image_picker/image_picker_ios/example/ios/TestImages/webpImage.webp create mode 100644 packages/image_picker/image_picker_ios/example/ios/image_picker_exampleTests/Info.plist create mode 100755 packages/image_picker/image_picker_ios/example/lib/main.dart create mode 100755 packages/image_picker/image_picker_ios/example/pubspec.yaml create mode 100644 packages/image_picker/image_picker_ios/example/test_driver/integration_test.dart rename packages/image_picker/{image_picker => image_picker_ios}/ios/Assets/.gitkeep (100%) rename packages/image_picker/{image_picker => image_picker_ios}/ios/Classes/FLTImagePickerImageUtil.h (100%) rename packages/image_picker/{image_picker => image_picker_ios}/ios/Classes/FLTImagePickerImageUtil.m (100%) rename packages/image_picker/{image_picker => image_picker_ios}/ios/Classes/FLTImagePickerMetaDataUtil.h (100%) rename packages/image_picker/{image_picker => image_picker_ios}/ios/Classes/FLTImagePickerMetaDataUtil.m (100%) rename packages/image_picker/{image_picker => image_picker_ios}/ios/Classes/FLTImagePickerPhotoAssetUtil.h (100%) rename packages/image_picker/{image_picker => image_picker_ios}/ios/Classes/FLTImagePickerPhotoAssetUtil.m (100%) rename packages/image_picker/{image_picker => image_picker_ios}/ios/Classes/FLTImagePickerPlugin.h (100%) rename packages/image_picker/{image_picker => image_picker_ios}/ios/Classes/FLTImagePickerPlugin.m (100%) rename packages/image_picker/{image_picker => image_picker_ios}/ios/Classes/FLTImagePickerPlugin_Test.h (95%) rename packages/image_picker/{image_picker => image_picker_ios}/ios/Classes/FLTPHPickerSaveImageToPathOperation.h (100%) rename packages/image_picker/{image_picker => image_picker_ios}/ios/Classes/FLTPHPickerSaveImageToPathOperation.m (100%) rename packages/image_picker/{image_picker => image_picker_ios}/ios/Classes/ImagePickerPlugin.modulemap (76%) rename packages/image_picker/{image_picker/ios/Classes/image_picker-umbrella.h => image_picker_ios/ios/Classes/image_picker_ios-umbrella.h} (79%) rename packages/image_picker/{image_picker/ios/image_picker.podspec => image_picker_ios/ios/image_picker_ios.podspec} (86%) create mode 100755 packages/image_picker/image_picker_ios/pubspec.yaml diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index aa4a28511a8a..d84304f0b5e9 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Moves Android and iOS implementations to federated packages. * Adds OS version support information to README. ## 0.8.4+11 diff --git a/packages/image_picker/image_picker/android/settings.gradle b/packages/image_picker/image_picker/android/settings.gradle deleted file mode 100755 index 5b9496172108..000000000000 --- a/packages/image_picker/image_picker/android/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'imagepicker' diff --git a/packages/image_picker/image_picker/example/ios/Podfile b/packages/image_picker/image_picker/example/ios/Podfile index 5bc7b7e85717..f7d6a5e68c3a 100644 --- a/packages/image_picker/image_picker/example/ios/Podfile +++ b/packages/image_picker/image_picker/example/ios/Podfile @@ -29,13 +29,6 @@ flutter_ios_podfile_setup target 'Runner' do flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) - - target 'RunnerTests' do - platform :ios, '9.0' - inherit! :search_paths - # Pods for testing - pod 'OCMock', '~> 3.8.1' - end end post_install do |installer| diff --git a/packages/image_picker/image_picker/example/ios/Runner.xcodeproj/project.pbxproj b/packages/image_picker/image_picker/example/ios/Runner.xcodeproj/project.pbxproj index 2847bfd85046..589858f39019 100644 --- a/packages/image_picker/image_picker/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/image_picker/image_picker/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,53 +3,20 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 46; objects = { /* Begin PBXBuildFile section */ - 334733FC266813EE00DCC49E /* ImageUtilTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FC8F0ED229FB90B00C8D58F /* ImageUtilTests.m */; }; - 334733FD266813F100DCC49E /* MetaDataUtilTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 680049252280D736006DD6AB /* MetaDataUtilTests.m */; }; - 334733FE266813F400DCC49E /* PhotoAssetUtilTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68F4B463228B3AB500C25614 /* PhotoAssetUtilTests.m */; }; - 334733FF266813FA00DCC49E /* ImagePickerTestImages.m in Sources */ = {isa = PBXBuildFile; fileRef = F78AF3182342D9D7008449C7 /* ImagePickerTestImages.m */; }; - 33473400266813FD00DCC49E /* ImagePickerPluginTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68B9AF71243E4B3F00927CE4 /* ImagePickerPluginTests.m */; }; - 3A72BAD3FAE6E0FA9D80826B /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35AE65F25E0B8C8214D8372B /* libPods-RunnerTests.a */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 5C9513011EC38BD300040975 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C9513001EC38BD300040975 /* GeneratedPluginRegistrant.m */; }; - 680049382280F2B9006DD6AB /* pngImage.png in Resources */ = {isa = PBXBuildFile; fileRef = 680049352280F2B8006DD6AB /* pngImage.png */; }; - 680049392280F2B9006DD6AB /* jpgImage.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 680049362280F2B8006DD6AB /* jpgImage.jpg */; }; - 6801C8392555D726009DAF8D /* ImagePickerFromGalleryUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6801C8382555D726009DAF8D /* ImagePickerFromGalleryUITests.m */; }; - 86430DF9272D71E9002D9D6C /* gifImage.gif in Resources */ = {isa = PBXBuildFile; fileRef = 9FC8F0E8229FA49E00C8D58F /* gifImage.gif */; }; - 86E9A893272754860017E6E0 /* PickerSaveImageToPathOperationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 86E9A892272754860017E6E0 /* PickerSaveImageToPathOperationTests.m */; }; - 86E9A894272754A30017E6E0 /* webpImage.webp in Resources */ = {isa = PBXBuildFile; fileRef = 86E9A88F272747B90017E6E0 /* webpImage.webp */; }; - 86E9A895272769130017E6E0 /* pngImage.png in Resources */ = {isa = PBXBuildFile; fileRef = 680049352280F2B8006DD6AB /* pngImage.png */; }; - 86E9A896272769150017E6E0 /* jpgImage.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 680049362280F2B8006DD6AB /* jpgImage.jpg */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 9FC8F0E9229FA49E00C8D58F /* gifImage.gif in Resources */ = {isa = PBXBuildFile; fileRef = 9FC8F0E8229FA49E00C8D58F /* gifImage.gif */; }; - 9FC8F0EC229FA68500C8D58F /* gifImage.gif in Resources */ = {isa = PBXBuildFile; fileRef = 9FC8F0E8229FA49E00C8D58F /* gifImage.gif */; }; - BE6173D826A958B800D0974D /* ImagePickerFromLimitedGalleryUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = BE6173D726A958B800D0974D /* ImagePickerFromLimitedGalleryUITests.m */; }; F4F7A436CCA4BF276270A3AE /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EC32F6993F4529982D9519F1 /* libPods-Runner.a */; }; /* End PBXBuildFile section */ -/* Begin PBXContainerItemProxy section */ - 334733F72668136400DCC49E /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 97C146E61CF9000F007C117D /* Project object */; - proxyType = 1; - remoteGlobalIDString = 97C146ED1CF9000F007C117D; - remoteInfo = Runner; - }; - 6801C83B2555D726009DAF8D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 97C146E61CF9000F007C117D /* Project object */; - proxyType = 1; - remoteGlobalIDString = 97C146ED1CF9000F007C117D; - remoteInfo = Runner; - }; -/* End PBXContainerItemProxy section */ - /* Begin PBXCopyFilesBuildPhase section */ 9705A1C41CF9048500538489 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; @@ -65,27 +32,18 @@ /* Begin PBXFileReference section */ 0C7B151765FD4249454C49AD /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; - 334733F22668136400DCC49E /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 334733F62668136400DCC49E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 35AE65F25E0B8C8214D8372B /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 5A9D31B91557877A0E8EF3E7 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 5C9512FF1EC38BD300040975 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 5C9513001EC38BD300040975 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 680049252280D736006DD6AB /* MetaDataUtilTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MetaDataUtilTests.m; sourceTree = ""; }; 680049352280F2B8006DD6AB /* pngImage.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = pngImage.png; sourceTree = ""; }; 680049362280F2B8006DD6AB /* jpgImage.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = jpgImage.jpg; sourceTree = ""; }; 6801632E632668F4349764C9 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - 6801C8362555D726009DAF8D /* RunnerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 6801C8382555D726009DAF8D /* ImagePickerFromGalleryUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ImagePickerFromGalleryUITests.m; sourceTree = ""; }; - 6801C83A2555D726009DAF8D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 68B9AF71243E4B3F00927CE4 /* ImagePickerPluginTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ImagePickerPluginTests.m; sourceTree = ""; }; - 68F4B463228B3AB500C25614 /* PhotoAssetUtilTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PhotoAssetUtilTests.m; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 86E9A88F272747B90017E6E0 /* webpImage.webp */ = {isa = PBXFileReference; lastKnownFileType = file; path = webpImage.webp; sourceTree = ""; }; - 86E9A892272754860017E6E0 /* PickerSaveImageToPathOperationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PickerSaveImageToPathOperationTests.m; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -95,30 +53,11 @@ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9FC8F0E8229FA49E00C8D58F /* gifImage.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = gifImage.gif; sourceTree = ""; }; - 9FC8F0ED229FB90B00C8D58F /* ImageUtilTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ImageUtilTests.m; sourceTree = ""; }; - BE6173D726A958B800D0974D /* ImagePickerFromLimitedGalleryUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ImagePickerFromLimitedGalleryUITests.m; sourceTree = ""; }; DC6FCAAD4E7580C9B3C2E21D /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; EC32F6993F4529982D9519F1 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - F78AF3172342D9D7008449C7 /* ImagePickerTestImages.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ImagePickerTestImages.h; sourceTree = ""; }; - F78AF3182342D9D7008449C7 /* ImagePickerTestImages.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ImagePickerTestImages.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 334733EF2668136400DCC49E /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 3A72BAD3FAE6E0FA9D80826B /* libPods-RunnerTests.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 6801C8332555D726009DAF8D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; 97C146EB1CF9000F007C117D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -130,21 +69,6 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 334733F32668136400DCC49E /* RunnerTests */ = { - isa = PBXGroup; - children = ( - 9FC8F0ED229FB90B00C8D58F /* ImageUtilTests.m */, - 680049252280D736006DD6AB /* MetaDataUtilTests.m */, - 68F4B463228B3AB500C25614 /* PhotoAssetUtilTests.m */, - F78AF3172342D9D7008449C7 /* ImagePickerTestImages.h */, - F78AF3182342D9D7008449C7 /* ImagePickerTestImages.m */, - 68B9AF71243E4B3F00927CE4 /* ImagePickerPluginTests.m */, - 86E9A892272754860017E6E0 /* PickerSaveImageToPathOperationTests.m */, - 334733F62668136400DCC49E /* Info.plist */, - ); - path = RunnerTests; - sourceTree = ""; - }; 680049282280E33D006DD6AB /* TestImages */ = { isa = PBXGroup; children = ( @@ -156,16 +80,6 @@ path = TestImages; sourceTree = ""; }; - 6801C8372555D726009DAF8D /* RunnerUITests */ = { - isa = PBXGroup; - children = ( - BE6173D726A958B800D0974D /* ImagePickerFromLimitedGalleryUITests.m */, - 6801C8382555D726009DAF8D /* ImagePickerFromGalleryUITests.m */, - 6801C83A2555D726009DAF8D /* Info.plist */, - ); - path = RunnerUITests; - sourceTree = ""; - }; 840012C8B5EDBCF56B0E4AC1 /* Pods */ = { isa = PBXGroup; children = ( @@ -194,8 +108,6 @@ 680049282280E33D006DD6AB /* TestImages */, 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, - 334733F32668136400DCC49E /* RunnerTests */, - 6801C8372555D726009DAF8D /* RunnerUITests */, 97C146EF1CF9000F007C117D /* Products */, 840012C8B5EDBCF56B0E4AC1 /* Pods */, CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, @@ -206,8 +118,6 @@ isa = PBXGroup; children = ( 97C146EE1CF9000F007C117D /* Runner.app */, - 6801C8362555D726009DAF8D /* RunnerUITests.xctest */, - 334733F22668136400DCC49E /* RunnerTests.xctest */, ); name = Products; sourceTree = ""; @@ -248,43 +158,6 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 334733F12668136400DCC49E /* RunnerTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 334733F92668136400DCC49E /* Build configuration list for PBXNativeTarget "RunnerTests" */; - buildPhases = ( - B8739A4353234497CF76B597 /* [CP] Check Pods Manifest.lock */, - 334733EE2668136400DCC49E /* Sources */, - 334733EF2668136400DCC49E /* Frameworks */, - 334733F02668136400DCC49E /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 334733F82668136400DCC49E /* PBXTargetDependency */, - ); - name = RunnerTests; - productName = RunnerTests; - productReference = 334733F22668136400DCC49E /* RunnerTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 6801C8352555D726009DAF8D /* RunnerUITests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 6801C83F2555D726009DAF8D /* Build configuration list for PBXNativeTarget "RunnerUITests" */; - buildPhases = ( - 6801C8322555D726009DAF8D /* Sources */, - 6801C8332555D726009DAF8D /* Frameworks */, - 6801C8342555D726009DAF8D /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 6801C83C2555D726009DAF8D /* PBXTargetDependency */, - ); - name = RunnerUITests; - productName = RunnerUITests; - productReference = 6801C8362555D726009DAF8D /* RunnerUITests.xctest */; - productType = "com.apple.product-type.bundle.ui-testing"; - }; 97C146ED1CF9000F007C117D /* Runner */ = { isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; @@ -316,16 +189,6 @@ LastUpgradeCheck = 1300; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { - 334733F12668136400DCC49E = { - CreatedOnToolsVersion = 12.5; - ProvisioningStyle = Automatic; - TestTargetID = 97C146ED1CF9000F007C117D; - }; - 6801C8352555D726009DAF8D = { - CreatedOnToolsVersion = 11.7; - ProvisioningStyle = Automatic; - TestTargetID = 97C146ED1CF9000F007C117D; - }; 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; SystemCapabilities = { @@ -350,34 +213,11 @@ projectRoot = ""; targets = ( 97C146ED1CF9000F007C117D /* Runner */, - 334733F12668136400DCC49E /* RunnerTests */, - 6801C8352555D726009DAF8D /* RunnerUITests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 334733F02668136400DCC49E /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 86430DF9272D71E9002D9D6C /* gifImage.gif in Resources */, - 86E9A894272754A30017E6E0 /* webpImage.webp in Resources */, - 86E9A895272769130017E6E0 /* pngImage.png in Resources */, - 86E9A896272769150017E6E0 /* jpgImage.jpg in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 6801C8342555D726009DAF8D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 9FC8F0EC229FA68500C8D58F /* gifImage.gif in Resources */, - 680049382280F2B9006DD6AB /* pngImage.png in Resources */, - 680049392280F2B9006DD6AB /* jpgImage.jpg in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 97C146EC1CF9000F007C117D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -438,53 +278,9 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - B8739A4353234497CF76B597 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 334733EE2668136400DCC49E /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 334733FD266813F100DCC49E /* MetaDataUtilTests.m in Sources */, - 334733FF266813FA00DCC49E /* ImagePickerTestImages.m in Sources */, - 86E9A893272754860017E6E0 /* PickerSaveImageToPathOperationTests.m in Sources */, - 334733FC266813EE00DCC49E /* ImageUtilTests.m in Sources */, - 33473400266813FD00DCC49E /* ImagePickerPluginTests.m in Sources */, - 334733FE266813F400DCC49E /* PhotoAssetUtilTests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 6801C8322555D726009DAF8D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 6801C8392555D726009DAF8D /* ImagePickerFromGalleryUITests.m in Sources */, - BE6173D826A958B800D0974D /* ImagePickerFromLimitedGalleryUITests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 97C146EA1CF9000F007C117D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -497,19 +293,6 @@ }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXTargetDependency section */ - 334733F82668136400DCC49E /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 97C146ED1CF9000F007C117D /* Runner */; - targetProxy = 334733F72668136400DCC49E /* PBXContainerItemProxy */; - }; - 6801C83C2555D726009DAF8D /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 97C146ED1CF9000F007C117D /* Runner */; - targetProxy = 6801C83B2555D726009DAF8D /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - /* Begin PBXVariantGroup section */ 97C146FA1CF9000F007C117D /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -530,79 +313,6 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ - 334733FA2668136400DCC49E /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = DC6FCAAD4E7580C9B3C2E21D /* Pods-RunnerTests.debug.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = RunnerTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; - }; - name = Debug; - }; - 334733FB2668136400DCC49E /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 0C7B151765FD4249454C49AD /* Pods-RunnerTests.release.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = RunnerTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; - }; - name = Release; - }; - 6801C83D2555D726009DAF8D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = RunnerUITests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.google.RunnerUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = Runner; - }; - name = Debug; - }; - 6801C83E2555D726009DAF8D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = RunnerUITests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.google.RunnerUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = Runner; - }; - name = Release; - }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -754,24 +464,6 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 334733F92668136400DCC49E /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 334733FA2668136400DCC49E /* Debug */, - 334733FB2668136400DCC49E /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 6801C83F2555D726009DAF8D /* Build configuration list for PBXNativeTarget "RunnerUITests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 6801C83D2555D726009DAF8D /* Debug */, - 6801C83E2555D726009DAF8D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/packages/image_picker/image_picker/example/pubspec.yaml b/packages/image_picker/image_picker/example/pubspec.yaml index 92db04501f83..4fe823587398 100755 --- a/packages/image_picker/image_picker/example/pubspec.yaml +++ b/packages/image_picker/image_picker/example/pubspec.yaml @@ -20,9 +20,7 @@ dependencies: video_player: ^2.1.4 dev_dependencies: - # TODO(bparrishMines): Change version to `0.2.0` once published. - espresso: - path: ../../../espresso + espresso: ^0.2.0 flutter_driver: sdk: flutter integration_test: diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 553599f7306f..05ac189fb05d 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -4,6 +4,9 @@ description: Flutter plugin for selecting images from the Android and iOS image repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 version: 0.8.4+11 +# Temporarily disable publishing to allow moving Android and iOS +# implementations. +publish_to: none environment: sdk: ">=2.14.0 <3.0.0" @@ -13,18 +16,21 @@ flutter: plugin: platforms: android: - package: io.flutter.plugins.imagepicker - pluginClass: ImagePickerPlugin + default_package: image_picker_android ios: - pluginClass: FLTImagePickerPlugin + default_package: image_picker_ios web: default_package: image_picker_for_web dependencies: flutter: sdk: flutter - flutter_plugin_android_lifecycle: ^2.0.1 + # Temporary path dependencies to allow moving Android and iOS implementations. + image_picker_android: + path: ../image_picker_android image_picker_for_web: ^2.1.0 + image_picker_ios: + path: ../image_picker_ios image_picker_platform_interface: ^2.3.0 dev_dependencies: diff --git a/packages/image_picker/image_picker_android/AUTHORS b/packages/image_picker/image_picker_android/AUTHORS new file mode 100644 index 000000000000..493a0b4ef9c2 --- /dev/null +++ b/packages/image_picker/image_picker_android/AUTHORS @@ -0,0 +1,66 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> diff --git a/packages/image_picker/image_picker_android/CHANGELOG.md b/packages/image_picker/image_picker_android/CHANGELOG.md new file mode 100644 index 000000000000..3472ade28d5b --- /dev/null +++ b/packages/image_picker/image_picker_android/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.8.4+11 + +* Splits from `image_picker` as a federated implementation. diff --git a/packages/image_picker/image_picker_android/LICENSE b/packages/image_picker/image_picker_android/LICENSE new file mode 100644 index 000000000000..0be8bbc3e68d --- /dev/null +++ b/packages/image_picker/image_picker_android/LICENSE @@ -0,0 +1,231 @@ +image_picker + +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------------------- +aFileChooser + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2011 - 2013 Paul Burke + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/image_picker/image_picker_android/README.md b/packages/image_picker/image_picker_android/README.md new file mode 100755 index 000000000000..43d08c2a8b3a --- /dev/null +++ b/packages/image_picker/image_picker_android/README.md @@ -0,0 +1,11 @@ +# image\_picker\_android + +The Android implementation of [`image_picker`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use `image_picker` +normally. This package will be automatically included in your app when you do. + +[1]: https://pub.dev/packages/image_picker +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin diff --git a/packages/image_picker/image_picker/android/build.gradle b/packages/image_picker/image_picker_android/android/build.gradle similarity index 100% rename from packages/image_picker/image_picker/android/build.gradle rename to packages/image_picker/image_picker_android/android/build.gradle diff --git a/packages/image_picker/image_picker_android/android/settings.gradle b/packages/image_picker/image_picker_android/android/settings.gradle new file mode 100755 index 000000000000..3c673efcd542 --- /dev/null +++ b/packages/image_picker/image_picker_android/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'image_picker_android' diff --git a/packages/image_picker/image_picker/android/src/main/AndroidManifest.xml b/packages/image_picker/image_picker_android/android/src/main/AndroidManifest.xml similarity index 100% rename from packages/image_picker/image_picker/android/src/main/AndroidManifest.xml rename to packages/image_picker/image_picker_android/android/src/main/AndroidManifest.xml diff --git a/packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ExifDataCopier.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ExifDataCopier.java similarity index 100% rename from packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ExifDataCopier.java rename to packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ExifDataCopier.java diff --git a/packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java similarity index 100% rename from packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java rename to packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java diff --git a/packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerCache.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerCache.java similarity index 100% rename from packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerCache.java rename to packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerCache.java diff --git a/packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java similarity index 100% rename from packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java rename to packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java diff --git a/packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerFileProvider.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerFileProvider.java similarity index 100% rename from packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerFileProvider.java rename to packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerFileProvider.java diff --git a/packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java similarity index 100% rename from packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java rename to packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java diff --git a/packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java similarity index 100% rename from packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java rename to packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java diff --git a/packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImageResizer.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImageResizer.java similarity index 100% rename from packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImageResizer.java rename to packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImageResizer.java diff --git a/packages/image_picker/image_picker/android/src/main/res/xml/flutter_image_picker_file_paths.xml b/packages/image_picker/image_picker_android/android/src/main/res/xml/flutter_image_picker_file_paths.xml similarity index 100% rename from packages/image_picker/image_picker/android/src/main/res/xml/flutter_image_picker_file_paths.xml rename to packages/image_picker/image_picker_android/android/src/main/res/xml/flutter_image_picker_file_paths.xml diff --git a/packages/image_picker/image_picker/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java similarity index 100% rename from packages/image_picker/image_picker/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java rename to packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java diff --git a/packages/image_picker/image_picker/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerCacheTest.java b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerCacheTest.java similarity index 100% rename from packages/image_picker/image_picker/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerCacheTest.java rename to packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerCacheTest.java diff --git a/packages/image_picker/image_picker/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerDelegateTest.java b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerDelegateTest.java similarity index 100% rename from packages/image_picker/image_picker/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerDelegateTest.java rename to packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerDelegateTest.java diff --git a/packages/image_picker/image_picker/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java similarity index 100% rename from packages/image_picker/image_picker/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java rename to packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java diff --git a/packages/image_picker/image_picker/android/src/test/java/io/flutter/plugins/imagepicker/ImageResizerTest.java b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImageResizerTest.java similarity index 100% rename from packages/image_picker/image_picker/android/src/test/java/io/flutter/plugins/imagepicker/ImageResizerTest.java rename to packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImageResizerTest.java diff --git a/packages/image_picker/image_picker/android/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/packages/image_picker/image_picker_android/android/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker similarity index 100% rename from packages/image_picker/image_picker/android/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker rename to packages/image_picker/image_picker_android/android/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker diff --git a/packages/image_picker/image_picker/android/src/test/resources/pngImage.png b/packages/image_picker/image_picker_android/android/src/test/resources/pngImage.png similarity index 100% rename from packages/image_picker/image_picker/android/src/test/resources/pngImage.png rename to packages/image_picker/image_picker_android/android/src/test/resources/pngImage.png diff --git a/packages/image_picker/image_picker_android/example/README.md b/packages/image_picker/image_picker_android/example/README.md new file mode 100755 index 000000000000..86d5c23ba209 --- /dev/null +++ b/packages/image_picker/image_picker_android/example/README.md @@ -0,0 +1,8 @@ +# image_picker_example + +Demonstrates how to use the `image_picker` plugin. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](https://flutter.dev/). diff --git a/packages/image_picker/image_picker_android/example/android/app/build.gradle b/packages/image_picker/image_picker_android/example/android/app/build.gradle new file mode 100755 index 000000000000..e73e3fe01003 --- /dev/null +++ b/packages/image_picker/image_picker_android/example/android/app/build.gradle @@ -0,0 +1,67 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 31 + testOptions.unitTests.includeAndroidResources = true + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + applicationId "io.flutter.plugins.imagepicker.example" + minSdkVersion 16 + targetSdkVersion 28 + multiDexEnabled true + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } + + testOptions { + unitTests.returnDefaultValues = true + } +} + +flutter { + source '../..' +} + +dependencies { + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + api 'androidx.test:core:1.2.0' +} diff --git a/packages/image_picker/image_picker_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/image_picker/image_picker_android/example/android/app/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..9a4163a4f5ee --- /dev/null +++ b/packages/image_picker/image_picker_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/packages/image_picker/image_picker_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java b/packages/image_picker/image_picker_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java new file mode 100644 index 000000000000..0f4298dca155 --- /dev/null +++ b/packages/image_picker/image_picker_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java @@ -0,0 +1,14 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface DartIntegrationTest {} diff --git a/packages/image_picker/image_picker_android/example/android/app/src/androidTest/java/io/flutter/plugins/imagepickerexample/FlutterActivityTest.java b/packages/image_picker/image_picker_android/example/android/app/src/androidTest/java/io/flutter/plugins/imagepickerexample/FlutterActivityTest.java new file mode 100644 index 000000000000..91e068fa8043 --- /dev/null +++ b/packages/image_picker/image_picker_android/example/android/app/src/androidTest/java/io/flutter/plugins/imagepickerexample/FlutterActivityTest.java @@ -0,0 +1,19 @@ +// 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.imagepickerexample; + +import androidx.test.rule.ActivityTestRule; +import dev.flutter.plugins.integration_test.FlutterTestRunner; +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.plugins.DartIntegrationTest; +import org.junit.Rule; +import org.junit.runner.RunWith; + +@DartIntegrationTest +@RunWith(FlutterTestRunner.class) +public class FlutterActivityTest { + @Rule + public ActivityTestRule rule = new ActivityTestRule<>(FlutterActivity.class); +} diff --git a/packages/image_picker/image_picker_android/example/android/app/src/androidTest/java/io/flutter/plugins/imagepickerexample/ImagePickerTest.java b/packages/image_picker/image_picker_android/example/android/app/src/androidTest/java/io/flutter/plugins/imagepickerexample/ImagePickerTest.java new file mode 100644 index 000000000000..c4a1532d940c --- /dev/null +++ b/packages/image_picker/image_picker_android/example/android/app/src/androidTest/java/io/flutter/plugins/imagepickerexample/ImagePickerTest.java @@ -0,0 +1,23 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.imagepickerexample; + +import static org.junit.Assert.assertTrue; + +import androidx.test.core.app.ActivityScenario; +import io.flutter.plugins.imagepicker.ImagePickerPlugin; +import org.junit.Test; + +public class ImagePickerTest { + @Test + public void imagePickerPluginIsAdded() { + final ActivityScenario scenario = + ActivityScenario.launch(ImagePickerTestActivity.class); + scenario.onActivity( + activity -> { + assertTrue(activity.engine.getPlugins().has(ImagePickerPlugin.class)); + }); + } +} diff --git a/packages/image_picker/image_picker_android/example/android/app/src/debug/AndroidManifest.xml b/packages/image_picker/image_picker_android/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 000000000000..6f85cefded34 --- /dev/null +++ b/packages/image_picker/image_picker_android/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/packages/image_picker/image_picker_android/example/android/app/src/main/AndroidManifest.xml b/packages/image_picker/image_picker_android/example/android/app/src/main/AndroidManifest.xml new file mode 100755 index 000000000000..543fca922e1b --- /dev/null +++ b/packages/image_picker/image_picker_android/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + diff --git a/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/.gitignore b/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/.gitignore new file mode 100755 index 000000000000..9eb4563d2ae1 --- /dev/null +++ b/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/.gitignore @@ -0,0 +1 @@ +GeneratedPluginRegistrant.java diff --git a/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/ImagePickerTestActivity.java b/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/ImagePickerTestActivity.java new file mode 100644 index 000000000000..827687a10e79 --- /dev/null +++ b/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/ImagePickerTestActivity.java @@ -0,0 +1,20 @@ +// 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.imagepickerexample; + +import androidx.annotation.NonNull; +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.embedding.engine.FlutterEngine; + +// Makes the FlutterEngine accessible for testing. +public class ImagePickerTestActivity extends FlutterActivity { + public FlutterEngine engine; + + @Override + public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { + super.configureFlutterEngine(flutterEngine); + engine = flutterEngine; + } +} diff --git a/packages/image_picker/image_picker_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/image_picker/image_picker_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100755 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/packages/image_picker/image_picker_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/image_picker/image_picker_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100755 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/packages/image_picker/image_picker_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/image_picker/image_picker_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100755 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/packages/image_picker/image_picker_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/image_picker/image_picker_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100755 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/packages/image_picker/image_picker_android/example/android/build.gradle b/packages/image_picker/image_picker_android/example/android/build.gradle new file mode 100755 index 000000000000..e101ac08df55 --- /dev/null +++ b/packages/image_picker/image_picker_android/example/android/build.gradle @@ -0,0 +1,29 @@ +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.3.0' + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/packages/image_picker/image_picker_android/example/android/gradle.properties b/packages/image_picker/image_picker_android/example/android/gradle.properties new file mode 100755 index 000000000000..6effed032590 --- /dev/null +++ b/packages/image_picker/image_picker_android/example/android/gradle.properties @@ -0,0 +1,5 @@ +org.gradle.jvmargs=-Xmx1536M +android.enableR8=true +android.useAndroidX=true +android.enableJetifier=true +android.enableUnitTestBinaryResources=true diff --git a/packages/image_picker/image_picker_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/image_picker/image_picker_android/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..019065d1d650 --- /dev/null +++ b/packages/image_picker/image_picker_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip diff --git a/packages/image_picker/image_picker_android/example/android/settings.gradle b/packages/image_picker/image_picker_android/example/android/settings.gradle new file mode 100755 index 000000000000..115da6cb4f4d --- /dev/null +++ b/packages/image_picker/image_picker_android/example/android/settings.gradle @@ -0,0 +1,15 @@ +include ':app' + +def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + +def plugins = new Properties() +def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') +if (pluginsFile.exists()) { + pluginsFile.withInputStream { stream -> plugins.load(stream) } +} + +plugins.each { name, path -> + def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() + include ":$name" + project(":$name").projectDir = pluginDirectory +} diff --git a/packages/image_picker/image_picker_android/example/integration_test/image_picker_test.dart b/packages/image_picker/image_picker_android/example/integration_test/image_picker_test.dart new file mode 100644 index 000000000000..2b82b4bda5e4 --- /dev/null +++ b/packages/image_picker/image_picker_android/example/integration_test/image_picker_test.dart @@ -0,0 +1,12 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('placeholder test', (WidgetTester tester) async {}); +} diff --git a/packages/image_picker/image_picker_android/example/lib/main.dart b/packages/image_picker/image_picker_android/example/lib/main.dart new file mode 100755 index 000000000000..48eee35445da --- /dev/null +++ b/packages/image_picker/image_picker_android/example/lib/main.dart @@ -0,0 +1,467 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; +import 'package:video_player/video_player.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return const MaterialApp( + title: 'Image Picker Demo', + home: MyHomePage(title: 'Image Picker Example'), + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({Key? key, this.title}) : super(key: key); + + final String? title; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + List? _imageFileList; + + set _imageFile(XFile? value) { + _imageFileList = value == null ? null : [value]; + } + + dynamic _pickImageError; + bool isVideo = false; + + VideoPlayerController? _controller; + VideoPlayerController? _toBeDisposed; + String? _retrieveDataError; + + final ImagePickerPlatform _picker = ImagePickerPlatform.instance; + final TextEditingController maxWidthController = TextEditingController(); + final TextEditingController maxHeightController = TextEditingController(); + final TextEditingController qualityController = TextEditingController(); + + Future _playVideo(XFile? file) async { + if (file != null && mounted) { + await _disposeVideoController(); + late VideoPlayerController controller; + if (kIsWeb) { + controller = VideoPlayerController.network(file.path); + } else { + controller = VideoPlayerController.file(File(file.path)); + } + _controller = controller; + // In web, most browsers won't honor a programmatic call to .play + // if the video has a sound track (and is not muted). + // Mute the video so it auto-plays in web! + // This is not needed if the call to .play is the result of user + // interaction (clicking on a "play" button, for example). + const double volume = kIsWeb ? 0.0 : 1.0; + await controller.setVolume(volume); + await controller.initialize(); + await controller.setLooping(true); + await controller.play(); + setState(() {}); + } + } + + Future _onImageButtonPressed(ImageSource source, + {BuildContext? context, bool isMultiImage = false}) async { + if (_controller != null) { + await _controller!.setVolume(0.0); + } + if (isVideo) { + final XFile? file = await _picker.getVideo( + source: source, maxDuration: const Duration(seconds: 10)); + await _playVideo(file); + } else if (isMultiImage) { + await _displayPickImageDialog(context!, + (double? maxWidth, double? maxHeight, int? quality) async { + try { + final List? pickedFileList = await _picker.getMultiImage( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: quality, + ); + setState(() { + _imageFileList = pickedFileList; + }); + } catch (e) { + setState(() { + _pickImageError = e; + }); + } + }); + } else { + await _displayPickImageDialog(context!, + (double? maxWidth, double? maxHeight, int? quality) async { + try { + final XFile? pickedFile = await _picker.getImage( + source: source, + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: quality, + ); + setState(() { + _imageFile = pickedFile; + }); + } catch (e) { + setState(() { + _pickImageError = e; + }); + } + }); + } + } + + @override + void deactivate() { + if (_controller != null) { + _controller!.setVolume(0.0); + _controller!.pause(); + } + super.deactivate(); + } + + @override + void dispose() { + _disposeVideoController(); + maxWidthController.dispose(); + maxHeightController.dispose(); + qualityController.dispose(); + super.dispose(); + } + + Future _disposeVideoController() async { + if (_toBeDisposed != null) { + await _toBeDisposed!.dispose(); + } + _toBeDisposed = _controller; + _controller = null; + } + + Widget _previewVideo() { + final Text? retrieveError = _getRetrieveErrorWidget(); + if (retrieveError != null) { + return retrieveError; + } + if (_controller == null) { + return const Text( + 'You have not yet picked a video', + textAlign: TextAlign.center, + ); + } + return Padding( + padding: const EdgeInsets.all(10.0), + child: AspectRatioVideo(_controller), + ); + } + + Widget _previewImages() { + final Text? retrieveError = _getRetrieveErrorWidget(); + if (retrieveError != null) { + return retrieveError; + } + if (_imageFileList != null) { + return Semantics( + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + // Why network for web? + // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform + return Semantics( + label: 'image_picker_example_picked_image', + child: kIsWeb + ? Image.network(_imageFileList![index].path) + : Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + label: 'image_picker_example_picked_images'); + } else if (_pickImageError != null) { + return Text( + 'Pick image error: $_pickImageError', + textAlign: TextAlign.center, + ); + } else { + return const Text( + 'You have not yet picked an image.', + textAlign: TextAlign.center, + ); + } + } + + Widget _handlePreview() { + if (isVideo) { + return _previewVideo(); + } else { + return _previewImages(); + } + } + + Future retrieveLostData() async { + final LostDataResponse response = await _picker.getLostData(); + if (response.isEmpty) { + return; + } + if (response.file != null) { + if (response.type == RetrieveType.video) { + isVideo = true; + await _playVideo(response.file); + } else { + isVideo = false; + setState(() { + _imageFile = response.file; + _imageFileList = response.files; + }); + } + } else { + _retrieveDataError = response.exception!.code; + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title!), + ), + body: Center( + child: !kIsWeb && defaultTargetPlatform == TargetPlatform.android + ? FutureBuilder( + future: retrieveLostData(), + builder: (BuildContext context, AsyncSnapshot snapshot) { + switch (snapshot.connectionState) { + case ConnectionState.none: + case ConnectionState.waiting: + return const Text( + 'You have not yet picked an image.', + textAlign: TextAlign.center, + ); + case ConnectionState.done: + return _handlePreview(); + default: + if (snapshot.hasError) { + return Text( + 'Pick image/video error: ${snapshot.error}}', + textAlign: TextAlign.center, + ); + } else { + return const Text( + 'You have not yet picked an image.', + textAlign: TextAlign.center, + ); + } + } + }, + ) + : _handlePreview(), + ), + floatingActionButton: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Semantics( + label: 'image_picker_example_from_gallery', + child: FloatingActionButton( + onPressed: () { + isVideo = false; + _onImageButtonPressed(ImageSource.gallery, context: context); + }, + heroTag: 'image0', + tooltip: 'Pick Image from gallery', + child: const Icon(Icons.photo), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + onPressed: () { + isVideo = false; + _onImageButtonPressed( + ImageSource.gallery, + context: context, + isMultiImage: true, + ); + }, + heroTag: 'image1', + tooltip: 'Pick Multiple Image from gallery', + child: const Icon(Icons.photo_library), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + onPressed: () { + isVideo = false; + _onImageButtonPressed(ImageSource.camera, context: context); + }, + heroTag: 'image2', + tooltip: 'Take a Photo', + child: const Icon(Icons.camera_alt), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + backgroundColor: Colors.red, + onPressed: () { + isVideo = true; + _onImageButtonPressed(ImageSource.gallery); + }, + heroTag: 'video0', + tooltip: 'Pick Video from gallery', + child: const Icon(Icons.video_library), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + backgroundColor: Colors.red, + onPressed: () { + isVideo = true; + _onImageButtonPressed(ImageSource.camera); + }, + heroTag: 'video1', + tooltip: 'Take a Video', + child: const Icon(Icons.videocam), + ), + ), + ], + ), + ); + } + + Text? _getRetrieveErrorWidget() { + if (_retrieveDataError != null) { + final Text result = Text(_retrieveDataError!); + _retrieveDataError = null; + return result; + } + return null; + } + + Future _displayPickImageDialog( + BuildContext context, OnPickImageCallback onPick) async { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Add optional parameters'), + content: Column( + children: [ + TextField( + controller: maxWidthController, + keyboardType: + const TextInputType.numberWithOptions(decimal: true), + decoration: const InputDecoration( + hintText: 'Enter maxWidth if desired'), + ), + TextField( + controller: maxHeightController, + keyboardType: + const TextInputType.numberWithOptions(decimal: true), + decoration: const InputDecoration( + hintText: 'Enter maxHeight if desired'), + ), + TextField( + controller: qualityController, + keyboardType: TextInputType.number, + decoration: const InputDecoration( + hintText: 'Enter quality if desired'), + ), + ], + ), + actions: [ + TextButton( + child: const Text('CANCEL'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: const Text('PICK'), + onPressed: () { + final double? width = maxWidthController.text.isNotEmpty + ? double.parse(maxWidthController.text) + : null; + final double? height = maxHeightController.text.isNotEmpty + ? double.parse(maxHeightController.text) + : null; + final int? quality = qualityController.text.isNotEmpty + ? int.parse(qualityController.text) + : null; + onPick(width, height, quality); + Navigator.of(context).pop(); + }), + ], + ); + }); + } +} + +typedef OnPickImageCallback = void Function( + double? maxWidth, double? maxHeight, int? quality); + +class AspectRatioVideo extends StatefulWidget { + const AspectRatioVideo(this.controller); + + final VideoPlayerController? controller; + + @override + AspectRatioVideoState createState() => AspectRatioVideoState(); +} + +class AspectRatioVideoState extends State { + VideoPlayerController? get controller => widget.controller; + bool initialized = false; + + void _onVideoControllerUpdate() { + if (!mounted) { + return; + } + if (initialized != controller!.value.isInitialized) { + initialized = controller!.value.isInitialized; + setState(() {}); + } + } + + @override + void initState() { + super.initState(); + controller!.addListener(_onVideoControllerUpdate); + } + + @override + void dispose() { + controller!.removeListener(_onVideoControllerUpdate); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (initialized) { + return Center( + child: AspectRatio( + aspectRatio: controller!.value.aspectRatio, + child: VideoPlayer(controller!), + ), + ); + } else { + return Container(); + } + } +} diff --git a/packages/image_picker/image_picker_android/example/pubspec.yaml b/packages/image_picker/image_picker_android/example/pubspec.yaml new file mode 100755 index 000000000000..0d88ae139c71 --- /dev/null +++ b/packages/image_picker/image_picker_android/example/pubspec.yaml @@ -0,0 +1,31 @@ +name: image_picker_example +description: Demonstrates how to use the image_picker plugin. +publish_to: none + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.5.0" + +dependencies: + flutter: + sdk: flutter + flutter_plugin_android_lifecycle: ^2.0.1 + image_picker_android: + # When depending on this package from a real application you should use: + # image_picker_android: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + image_picker_platform_interface: ^2.3.0 + video_player: ^2.1.4 + +dev_dependencies: + espresso: ^0.2.0 + flutter_driver: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/image_picker/image_picker_android/example/test_driver/integration_test.dart b/packages/image_picker/image_picker_android/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4f10f2a522f3 --- /dev/null +++ b/packages/image_picker/image_picker_android/example/test_driver/integration_test.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); diff --git a/packages/image_picker/image_picker_android/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml new file mode 100755 index 000000000000..dbeef9bed193 --- /dev/null +++ b/packages/image_picker/image_picker_android/pubspec.yaml @@ -0,0 +1,28 @@ +name: image_picker_android +description: Android implementation of the image_picker plugin. +repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_android +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 +version: 0.8.4+11 + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.5.0" + +flutter: + plugin: + implements: image_picker + platforms: + android: + package: io.flutter.plugins.imagepicker + pluginClass: ImagePickerPlugin + +dependencies: + flutter: + sdk: flutter + flutter_plugin_android_lifecycle: ^2.0.1 + image_picker_platform_interface: ^2.3.0 + +dev_dependencies: + flutter_test: + sdk: flutter + mockito: ^5.0.0 diff --git a/packages/image_picker/image_picker_ios/AUTHORS b/packages/image_picker/image_picker_ios/AUTHORS new file mode 100644 index 000000000000..493a0b4ef9c2 --- /dev/null +++ b/packages/image_picker/image_picker_ios/AUTHORS @@ -0,0 +1,66 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md new file mode 100644 index 000000000000..3472ade28d5b --- /dev/null +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.8.4+11 + +* Splits from `image_picker` as a federated implementation. diff --git a/packages/image_picker/image_picker_ios/LICENSE b/packages/image_picker/image_picker_ios/LICENSE new file mode 100644 index 000000000000..0be8bbc3e68d --- /dev/null +++ b/packages/image_picker/image_picker_ios/LICENSE @@ -0,0 +1,231 @@ +image_picker + +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------------------- +aFileChooser + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2011 - 2013 Paul Burke + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/image_picker/image_picker_ios/README.md b/packages/image_picker/image_picker_ios/README.md new file mode 100755 index 000000000000..e9fc2cfe61e7 --- /dev/null +++ b/packages/image_picker/image_picker_ios/README.md @@ -0,0 +1,11 @@ +# image\_picker\_ios + +The iOS implementation of [`image_picker`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use `image_picker` +normally. This package will be automatically included in your app when you do. + +[1]: https://pub.dev/packages/image_picker +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin diff --git a/packages/image_picker/image_picker_ios/example/README.md b/packages/image_picker/image_picker_ios/example/README.md new file mode 100755 index 000000000000..86d5c23ba209 --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/README.md @@ -0,0 +1,8 @@ +# image_picker_example + +Demonstrates how to use the `image_picker` plugin. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](https://flutter.dev/). diff --git a/packages/image_picker/image_picker_ios/example/integration_test/image_picker_test.dart b/packages/image_picker/image_picker_ios/example/integration_test/image_picker_test.dart new file mode 100644 index 000000000000..2b82b4bda5e4 --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/integration_test/image_picker_test.dart @@ -0,0 +1,12 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('placeholder test', (WidgetTester tester) async {}); +} diff --git a/packages/image_picker/image_picker_ios/example/ios/Flutter/AppFrameworkInfo.plist b/packages/image_picker/image_picker_ios/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100755 index 000000000000..3a9c234f96d4 --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + UIRequiredDeviceCapabilities + + arm64 + + MinimumOSVersion + 9.0 + + diff --git a/packages/image_picker/image_picker_ios/example/ios/Flutter/Debug.xcconfig b/packages/image_picker/image_picker_ios/example/ios/Flutter/Debug.xcconfig new file mode 100755 index 000000000000..9803018ca79d --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "Generated.xcconfig" +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" diff --git a/packages/image_picker/image_picker_ios/example/ios/Flutter/Release.xcconfig b/packages/image_picker/image_picker_ios/example/ios/Flutter/Release.xcconfig new file mode 100755 index 000000000000..a4a8c604e13d --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include "Generated.xcconfig" +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" diff --git a/packages/image_picker/image_picker_ios/example/ios/Podfile b/packages/image_picker/image_picker_ios/example/ios/Podfile new file mode 100644 index 000000000000..5bc7b7e85717 --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Podfile @@ -0,0 +1,45 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + + target 'RunnerTests' do + platform :ios, '9.0' + inherit! :search_paths + # Pods for testing + pod 'OCMock', '~> 3.8.1' + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..2847bfd85046 --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,796 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 334733FC266813EE00DCC49E /* ImageUtilTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FC8F0ED229FB90B00C8D58F /* ImageUtilTests.m */; }; + 334733FD266813F100DCC49E /* MetaDataUtilTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 680049252280D736006DD6AB /* MetaDataUtilTests.m */; }; + 334733FE266813F400DCC49E /* PhotoAssetUtilTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68F4B463228B3AB500C25614 /* PhotoAssetUtilTests.m */; }; + 334733FF266813FA00DCC49E /* ImagePickerTestImages.m in Sources */ = {isa = PBXBuildFile; fileRef = F78AF3182342D9D7008449C7 /* ImagePickerTestImages.m */; }; + 33473400266813FD00DCC49E /* ImagePickerPluginTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68B9AF71243E4B3F00927CE4 /* ImagePickerPluginTests.m */; }; + 3A72BAD3FAE6E0FA9D80826B /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35AE65F25E0B8C8214D8372B /* libPods-RunnerTests.a */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 5C9513011EC38BD300040975 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C9513001EC38BD300040975 /* GeneratedPluginRegistrant.m */; }; + 680049382280F2B9006DD6AB /* pngImage.png in Resources */ = {isa = PBXBuildFile; fileRef = 680049352280F2B8006DD6AB /* pngImage.png */; }; + 680049392280F2B9006DD6AB /* jpgImage.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 680049362280F2B8006DD6AB /* jpgImage.jpg */; }; + 6801C8392555D726009DAF8D /* ImagePickerFromGalleryUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6801C8382555D726009DAF8D /* ImagePickerFromGalleryUITests.m */; }; + 86430DF9272D71E9002D9D6C /* gifImage.gif in Resources */ = {isa = PBXBuildFile; fileRef = 9FC8F0E8229FA49E00C8D58F /* gifImage.gif */; }; + 86E9A893272754860017E6E0 /* PickerSaveImageToPathOperationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 86E9A892272754860017E6E0 /* PickerSaveImageToPathOperationTests.m */; }; + 86E9A894272754A30017E6E0 /* webpImage.webp in Resources */ = {isa = PBXBuildFile; fileRef = 86E9A88F272747B90017E6E0 /* webpImage.webp */; }; + 86E9A895272769130017E6E0 /* pngImage.png in Resources */ = {isa = PBXBuildFile; fileRef = 680049352280F2B8006DD6AB /* pngImage.png */; }; + 86E9A896272769150017E6E0 /* jpgImage.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 680049362280F2B8006DD6AB /* jpgImage.jpg */; }; + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; + 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + 9FC8F0E9229FA49E00C8D58F /* gifImage.gif in Resources */ = {isa = PBXBuildFile; fileRef = 9FC8F0E8229FA49E00C8D58F /* gifImage.gif */; }; + 9FC8F0EC229FA68500C8D58F /* gifImage.gif in Resources */ = {isa = PBXBuildFile; fileRef = 9FC8F0E8229FA49E00C8D58F /* gifImage.gif */; }; + BE6173D826A958B800D0974D /* ImagePickerFromLimitedGalleryUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = BE6173D726A958B800D0974D /* ImagePickerFromLimitedGalleryUITests.m */; }; + F4F7A436CCA4BF276270A3AE /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EC32F6993F4529982D9519F1 /* libPods-Runner.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 334733F72668136400DCC49E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; + 6801C83B2555D726009DAF8D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 0C7B151765FD4249454C49AD /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 334733F22668136400DCC49E /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 334733F62668136400DCC49E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 35AE65F25E0B8C8214D8372B /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 5A9D31B91557877A0E8EF3E7 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 5C9512FF1EC38BD300040975 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 5C9513001EC38BD300040975 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 680049252280D736006DD6AB /* MetaDataUtilTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MetaDataUtilTests.m; sourceTree = ""; }; + 680049352280F2B8006DD6AB /* pngImage.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = pngImage.png; sourceTree = ""; }; + 680049362280F2B8006DD6AB /* jpgImage.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = jpgImage.jpg; sourceTree = ""; }; + 6801632E632668F4349764C9 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 6801C8362555D726009DAF8D /* RunnerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 6801C8382555D726009DAF8D /* ImagePickerFromGalleryUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ImagePickerFromGalleryUITests.m; sourceTree = ""; }; + 6801C83A2555D726009DAF8D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 68B9AF71243E4B3F00927CE4 /* ImagePickerPluginTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ImagePickerPluginTests.m; sourceTree = ""; }; + 68F4B463228B3AB500C25614 /* PhotoAssetUtilTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PhotoAssetUtilTests.m; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 86E9A88F272747B90017E6E0 /* webpImage.webp */ = {isa = PBXFileReference; lastKnownFileType = file; path = webpImage.webp; sourceTree = ""; }; + 86E9A892272754860017E6E0 /* PickerSaveImageToPathOperationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PickerSaveImageToPathOperationTests.m; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9FC8F0E8229FA49E00C8D58F /* gifImage.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = gifImage.gif; sourceTree = ""; }; + 9FC8F0ED229FB90B00C8D58F /* ImageUtilTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ImageUtilTests.m; sourceTree = ""; }; + BE6173D726A958B800D0974D /* ImagePickerFromLimitedGalleryUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ImagePickerFromLimitedGalleryUITests.m; sourceTree = ""; }; + DC6FCAAD4E7580C9B3C2E21D /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + EC32F6993F4529982D9519F1 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + F78AF3172342D9D7008449C7 /* ImagePickerTestImages.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ImagePickerTestImages.h; sourceTree = ""; }; + F78AF3182342D9D7008449C7 /* ImagePickerTestImages.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ImagePickerTestImages.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 334733EF2668136400DCC49E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3A72BAD3FAE6E0FA9D80826B /* libPods-RunnerTests.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6801C8332555D726009DAF8D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F4F7A436CCA4BF276270A3AE /* libPods-Runner.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 334733F32668136400DCC49E /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 9FC8F0ED229FB90B00C8D58F /* ImageUtilTests.m */, + 680049252280D736006DD6AB /* MetaDataUtilTests.m */, + 68F4B463228B3AB500C25614 /* PhotoAssetUtilTests.m */, + F78AF3172342D9D7008449C7 /* ImagePickerTestImages.h */, + F78AF3182342D9D7008449C7 /* ImagePickerTestImages.m */, + 68B9AF71243E4B3F00927CE4 /* ImagePickerPluginTests.m */, + 86E9A892272754860017E6E0 /* PickerSaveImageToPathOperationTests.m */, + 334733F62668136400DCC49E /* Info.plist */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 680049282280E33D006DD6AB /* TestImages */ = { + isa = PBXGroup; + children = ( + 86E9A88F272747B90017E6E0 /* webpImage.webp */, + 9FC8F0E8229FA49E00C8D58F /* gifImage.gif */, + 680049362280F2B8006DD6AB /* jpgImage.jpg */, + 680049352280F2B8006DD6AB /* pngImage.png */, + ); + path = TestImages; + sourceTree = ""; + }; + 6801C8372555D726009DAF8D /* RunnerUITests */ = { + isa = PBXGroup; + children = ( + BE6173D726A958B800D0974D /* ImagePickerFromLimitedGalleryUITests.m */, + 6801C8382555D726009DAF8D /* ImagePickerFromGalleryUITests.m */, + 6801C83A2555D726009DAF8D /* Info.plist */, + ); + path = RunnerUITests; + sourceTree = ""; + }; + 840012C8B5EDBCF56B0E4AC1 /* Pods */ = { + isa = PBXGroup; + children = ( + 6801632E632668F4349764C9 /* Pods-Runner.debug.xcconfig */, + 5A9D31B91557877A0E8EF3E7 /* Pods-Runner.release.xcconfig */, + DC6FCAAD4E7580C9B3C2E21D /* Pods-RunnerTests.debug.xcconfig */, + 0C7B151765FD4249454C49AD /* Pods-RunnerTests.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 680049282280E33D006DD6AB /* TestImages */, + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 334733F32668136400DCC49E /* RunnerTests */, + 6801C8372555D726009DAF8D /* RunnerUITests */, + 97C146EF1CF9000F007C117D /* Products */, + 840012C8B5EDBCF56B0E4AC1 /* Pods */, + CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 6801C8362555D726009DAF8D /* RunnerUITests.xctest */, + 334733F22668136400DCC49E /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 5C9512FF1EC38BD300040975 /* GeneratedPluginRegistrant.h */, + 5C9513001EC38BD300040975 /* GeneratedPluginRegistrant.m */, + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 97C146F11CF9000F007C117D /* Supporting Files */, + ); + path = Runner; + sourceTree = ""; + }; + 97C146F11CF9000F007C117D /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 97C146F21CF9000F007C117D /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + CF3B75C9A7D2FA2A4C99F110 /* Frameworks */ = { + isa = PBXGroup; + children = ( + EC32F6993F4529982D9519F1 /* libPods-Runner.a */, + 35AE65F25E0B8C8214D8372B /* libPods-RunnerTests.a */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 334733F12668136400DCC49E /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 334733F92668136400DCC49E /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + B8739A4353234497CF76B597 /* [CP] Check Pods Manifest.lock */, + 334733EE2668136400DCC49E /* Sources */, + 334733EF2668136400DCC49E /* Frameworks */, + 334733F02668136400DCC49E /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 334733F82668136400DCC49E /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 334733F22668136400DCC49E /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 6801C8352555D726009DAF8D /* RunnerUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6801C83F2555D726009DAF8D /* Build configuration list for PBXNativeTarget "RunnerUITests" */; + buildPhases = ( + 6801C8322555D726009DAF8D /* Sources */, + 6801C8332555D726009DAF8D /* Frameworks */, + 6801C8342555D726009DAF8D /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 6801C83C2555D726009DAF8D /* PBXTargetDependency */, + ); + name = RunnerUITests; + productName = RunnerUITests; + productReference = 6801C8362555D726009DAF8D /* RunnerUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + AB1344B0443C71CD721E1BB7 /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + DefaultBuildSystemTypeForWorkspace = Original; + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = "The Flutter Authors"; + TargetAttributes = { + 334733F12668136400DCC49E = { + CreatedOnToolsVersion = 12.5; + ProvisioningStyle = Automatic; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 6801C8352555D726009DAF8D = { + CreatedOnToolsVersion = 11.7; + ProvisioningStyle = Automatic; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + SystemCapabilities = { + com.apple.BackgroundModes = { + enabled = 1; + }; + }; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 334733F12668136400DCC49E /* RunnerTests */, + 6801C8352555D726009DAF8D /* RunnerUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 334733F02668136400DCC49E /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 86430DF9272D71E9002D9D6C /* gifImage.gif in Resources */, + 86E9A894272754A30017E6E0 /* webpImage.webp in Resources */, + 86E9A895272769130017E6E0 /* pngImage.png in Resources */, + 86E9A896272769150017E6E0 /* jpgImage.jpg in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6801C8342555D726009DAF8D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9FC8F0EC229FA68500C8D58F /* gifImage.gif in Resources */, + 680049382280F2B9006DD6AB /* pngImage.png in Resources */, + 680049392280F2B9006DD6AB /* jpgImage.jpg in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 9FC8F0E9229FA49E00C8D58F /* gifImage.gif in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + AB1344B0443C71CD721E1BB7 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + B8739A4353234497CF76B597 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 334733EE2668136400DCC49E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 334733FD266813F100DCC49E /* MetaDataUtilTests.m in Sources */, + 334733FF266813FA00DCC49E /* ImagePickerTestImages.m in Sources */, + 86E9A893272754860017E6E0 /* PickerSaveImageToPathOperationTests.m in Sources */, + 334733FC266813EE00DCC49E /* ImageUtilTests.m in Sources */, + 33473400266813FD00DCC49E /* ImagePickerPluginTests.m in Sources */, + 334733FE266813F400DCC49E /* PhotoAssetUtilTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6801C8322555D726009DAF8D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6801C8392555D726009DAF8D /* ImagePickerFromGalleryUITests.m in Sources */, + BE6173D826A958B800D0974D /* ImagePickerFromLimitedGalleryUITests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, + 97C146F31CF9000F007C117D /* main.m in Sources */, + 5C9513011EC38BD300040975 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 334733F82668136400DCC49E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 334733F72668136400DCC49E /* PBXContainerItemProxy */; + }; + 6801C83C2555D726009DAF8D /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 6801C83B2555D726009DAF8D /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 334733FA2668136400DCC49E /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = DC6FCAAD4E7580C9B3C2E21D /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = RunnerTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Debug; + }; + 334733FB2668136400DCC49E /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0C7B151765FD4249454C49AD /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = RunnerTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Release; + }; + 6801C83D2555D726009DAF8D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = RunnerUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.google.RunnerUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = Runner; + }; + name = Debug; + }; + 6801C83E2555D726009DAF8D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = RunnerUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.google.RunnerUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = Runner; + }; + name = Release; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.imagePickerExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.imagePickerExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 334733F92668136400DCC49E /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 334733FA2668136400DCC49E /* Debug */, + 334733FB2668136400DCC49E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6801C83F2555D726009DAF8D /* Build configuration list for PBXNativeTarget "RunnerUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6801C83D2555D726009DAF8D /* Debug */, + 6801C83E2555D726009DAF8D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100755 index 000000000000..919434a6254f --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100755 index 000000000000..9b24f28c25cc --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/RunnerUITests.xcscheme b/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/RunnerUITests.xcscheme new file mode 100644 index 000000000000..1a97d9638346 --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/RunnerUITests.xcscheme @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/image_picker/image_picker_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100755 index 000000000000..21a3cc14c74e --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/image_picker/image_picker_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/.gitignore b/packages/image_picker/image_picker_ios/example/ios/Runner/.gitignore new file mode 100755 index 000000000000..0cab08d0bdd7 --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Runner/.gitignore @@ -0,0 +1,2 @@ +GeneratedPluginRegistrant.h +GeneratedPluginRegistrant.m diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/AppDelegate.h b/packages/image_picker/image_picker_ios/example/ios/Runner/AppDelegate.h new file mode 100644 index 000000000000..0681d288bb70 --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Runner/AppDelegate.h @@ -0,0 +1,10 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import + +@interface AppDelegate : FlutterAppDelegate + +@end diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/AppDelegate.m b/packages/image_picker/image_picker_ios/example/ios/Runner/AppDelegate.m new file mode 100644 index 000000000000..b790a0a52635 --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Runner/AppDelegate.m @@ -0,0 +1,16 @@ +// 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. + +#include "AppDelegate.h" +#include "GeneratedPluginRegistrant.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [GeneratedPluginRegistrant registerWithRegistry:self]; + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +@end diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100755 index 000000000000..d225b3c2cfe2 --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,121 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100755 index 0000000000000000000000000000000000000000..28c6bf03016f6c994b70f38d1b7346e5831b531f GIT binary patch literal 564 zcmV-40?Yl0P)Px$?ny*JR5%f>l)FnDQ543{x%ZCiu33$Wg!pQFfT_}?5Q|_VSlIbLC`dpoMXL}9 zHfd9&47Mo(7D231gb+kjFxZHS4-m~7WurTH&doVX2KI5sU4v(sJ1@T9eCIKPjsqSr z)C01LsCxk=72-vXmX}CQD#BD;Cthymh&~=f$Q8nn0J<}ZrusBy4PvRNE}+1ceuj8u z0mW5k8fmgeLnTbWHGwfKA3@PdZxhn|PypR&^p?weGftrtCbjF#+zk_5BJh7;0`#Wr zgDpM_;Ax{jO##IrT`Oz;MvfwGfV$zD#c2xckpcXC6oou4ML~ezCc2EtnsQTB4tWNg z?4bkf;hG7IMfhgNI(FV5Gs4|*GyMTIY0$B=_*mso9Ityq$m^S>15>-?0(zQ<8Qy<_TjHE33(?_M8oaM zyc;NxzRVK@DL6RJnX%U^xW0Gpg(lXp(!uK1v0YgHjs^ZXSQ|m#lV7ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 literal 0 HcmV?d00001 diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100755 index 0000000000000000000000000000000000000000..f091b6b0bca859a3f474b03065bef75ba58a9e4c GIT binary patch literal 1588 zcmV-42Fv-0P)C1SqPt}wig>|5Crh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)y zRAZ>eDe#*r`yDAVgB_R*LB*MAc)8(b{g{9McCXW!lq7r(btRoB9!8B-#AI6JMb~YFBEvdsV)`mEQO^&#eRKx@b&x- z5lZm*!WfD8oCLzfHGz#u7sT0^VLMI1MqGxF^v+`4YYnVYgk*=kU?HsSz{v({E3lb9 z>+xILjBN)t6`=g~IBOelGQ(O990@BfXf(DRI5I$qN$0Gkz-FSc$3a+2fX$AedL4u{ z4V+5Ong(9LiGcIKW?_352sR;LtDPmPJXI{YtT=O8=76o9;*n%_m|xo!i>7$IrZ-{l z-x3`7M}qzHsPV@$v#>H-TpjDh2UE$9g6sysUREDy_R(a)>=eHw-WAyfIN z*qb!_hW>G)Tu8nSw9yn#3wFMiLcfc4pY0ek1}8(NqkBR@t4{~oC>ryc-h_ByH(Cg5 z>ao-}771+xE3um9lWAY1FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zm zZQj(aA_HeBY&OC^jj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5Kh zX*|AU4QE#~SgPzOXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&g3j|zDgC+}2Q_v%YfDax z!?umcN^n}KYQ|a$Lr+51Nf9dkkYFSjZZjkma$0KOj+;aQ&721~t7QUKx61J3(P4P1 zstI~7-wOACnWP4=8oGOwz%vNDqD8w&Q`qcNGGrbbf&0s9L0De{4{mRS?o0MU+nR_! zrvshUau0G^DeMhM_v{5BuLjb#Hh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG z;P%vktHcXwTj~+IE(~px)3*MY77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC z(zYC$g|@+I+kY;dg_dE>scBf&bP1Nc@Hz<3R)V`=AGkc;8CXqdi=B4l2k|g;2%#m& z*jfX^%b!A8#bI!j9-0Fi0bOXl(-c^AB9|nQaE`*)Hw+o&jS9@7&Gov#HbD~#d{twV zXd^Tr^mWLfFh$@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)y zI9C9*oUga6=hxw6QasLPnee@3^Rr*M{CdaL5=R41nLs(AHk_=Y+A9$2&H(B7!_pURs&8aNw7?`&Z&xY_Ye z)~D5Bog^td-^QbUtkTirdyK^mTHAOuptDflut!#^lnKqU md>ggs(5nOWAqO?umG&QVYK#ibz}*4>0000U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~cTNYR~@Y9KepMPrrB8cABapAFa=`H+UGhkXUZV1GnwR1*lPyZ;*K(i~2gp|@bzp8}og7e*#% zEnr|^CWdVV!-4*Y_7rFvlww2Ze+>j*!Z!pQ?2l->4q#nqRu9`ELo6RMS5=br47g_X zRw}P9a7RRYQ%2Vsd0Me{_(EggTnuN6j=-?uFS6j^u69elMypu?t>op*wBx<=Wx8?( ztpe^(fwM6jJX7M-l*k3kEpWOl_Vk3@(_w4oc}4YF4|Rt=2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX?F&rExl^W|G8Z8;`5z-k}OGNZ0#WVb$WCpQu-_YsiqKP?BB# vzVHS-CTUF4Ozn5G+mq_~Qqto~ahA+K`|lyv3(-e}00000NkvXXu0mjfd`9t{ literal 0 HcmV?d00001 diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100755 index 0000000000000000000000000000000000000000..d0ef06e7edb86cdfe0d15b4b0d98334a86163658 GIT binary patch literal 1716 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 literal 0 HcmV?d00001 diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100755 index 0000000000000000000000000000000000000000..c8f9ed8f5cee1c98386d13b17e89f719e83555b2 GIT binary patch literal 1895 zcmV-t2blPYP)FQtfgmafE#=YDCq`qUBt#QpG%*H6QHY765~R=q zZ6iudfM}q!Pz#~9JgOi8QJ|DSu?1-*(kSi1K4#~5?#|rh?sS)(-JQqX*}ciXJ56_H zdw=^s_srbAdqxlvGyrgGet#6T7_|j;95sL%MtM;q86vOxKM$f#puR)Bjv9Zvz9-di zXOTSsZkM83)E9PYBXC<$6(|>lNLVBb&&6y{NByFCp%6+^ALR@NCTse_wqvNmSWI-m z!$%KlHFH2omF!>#%1l3LTZg(s7eof$7*xB)ZQ0h?ejh?Ta9fDv59+u#MokW+1t8Zb zgHv%K(u9G^Lv`lh#f3<6!JVTL3(dCpxHbnbA;kKqQyd1~^Xe0VIaYBSWm6nsr;dFj z4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3QU4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKubPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`Q zykI#*+rQ=z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRe zt3L_uNyQ*cE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=ky zx=~RKa4{iTm{_>_vSCm?$Ej=i6@=m%@VvAITnigVg{&@!7CDgs908761meDK5azA} z4?=NOH|PdvabgJ&fW2{Mo$Q0CcD8Qc84%{JPYt5EiG{MdLIAeX%T=D7NIP4%Hw}p9 zg)==!2Lbp#j{u_}hMiao9=!VSyx0gHbeCS`;q&vzeq|fs`y&^X-lso(Ls@-706qmA z7u*T5PMo_w3{se1t2`zWeO^hOvTsohG_;>J0wVqVe+n)AbQCx)yh9;w+J6?NF5Lmo zecS@ieAKL8%bVd@+-KT{yI|S}O>pYckUFs;ry9Ow$CD@ztz5K-*D$^{i(_1llhSh^ zEkL$}tsQt5>QA^;QgjgIfBDmcOgi5YDyu?t6vSnbp=1+@6D& z5MJ}B8q;bRlVoxasyhcUF1+)o`&3r0colr}QJ3hcSdLu;9;td>kf@Tcn<@9sIx&=m z;AD;SCh95=&p;$r{Xz3iWCO^MX83AGJ(yH&eTXgv|0=34#-&WAmw{)U7OU9!Wz^!7 zZ%jZFi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i h0WYBP*#0Ks^FNSabJA*5${_#%002ovPDHLkV1oKhTl@e3 literal 0 HcmV?d00001 diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100755 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100755 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100755 index 0000000000000000000000000000000000000000..75b2d164a5a98e212cca15ea7bf2ab5de5108680 GIT binary patch literal 3831 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 literal 0 HcmV?d00001 diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100755 index 0000000000000000000000000000000000000000..c4df70d39da7941ef3f6dcb7f06a192d8dcb308d GIT binary patch literal 1888 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 iW8 + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/Base.lproj/Main.storyboard b/packages/image_picker/image_picker_ios/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100755 index 000000000000..f3c28516fb38 --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/Info.plist b/packages/image_picker/image_picker_ios/example/ios/Runner/Info.plist new file mode 100755 index 000000000000..f9c1909383ca --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Runner/Info.plist @@ -0,0 +1,59 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + image_picker_example + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + NSCameraUsageDescription + Used to demonstrate image picker plugin + NSMicrophoneUsageDescription + Used to capture audio for image picker plugin + NSPhotoLibraryUsageDescription + Used to demonstrate image picker plugin + UIBackgroundModes + + remote-notification + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/main.m b/packages/image_picker/image_picker_ios/example/ios/Runner/main.m new file mode 100644 index 000000000000..f143297b30d6 --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Runner/main.m @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import +#import "AppDelegate.h" + +int main(int argc, char *argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerPluginTests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m similarity index 99% rename from packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerPluginTests.m rename to packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m index 5f3287400c5e..8df5299e54d9 100644 --- a/packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerPluginTests.m +++ b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m @@ -4,8 +4,8 @@ #import "ImagePickerTestImages.h" -@import image_picker; -@import image_picker.Test; +@import image_picker_ios; +@import image_picker_ios.Test; @import XCTest; #import diff --git a/packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerTestImages.h b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerTestImages.h similarity index 100% rename from packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerTestImages.h rename to packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerTestImages.h diff --git a/packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerTestImages.m b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerTestImages.m similarity index 100% rename from packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerTestImages.m rename to packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerTestImages.m diff --git a/packages/image_picker/image_picker/example/ios/RunnerTests/ImageUtilTests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImageUtilTests.m similarity index 97% rename from packages/image_picker/image_picker/example/ios/RunnerTests/ImageUtilTests.m rename to packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImageUtilTests.m index 9b9719f88116..e449a84b80bb 100644 --- a/packages/image_picker/image_picker/example/ios/RunnerTests/ImageUtilTests.m +++ b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImageUtilTests.m @@ -4,8 +4,8 @@ #import "ImagePickerTestImages.h" -@import image_picker; -@import image_picker.Test; +@import image_picker_ios; +@import image_picker_ios.Test; @import XCTest; @interface ImageUtilTests : XCTestCase diff --git a/packages/image_picker/image_picker/example/ios/RunnerTests/Info.plist b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/Info.plist similarity index 100% rename from packages/image_picker/image_picker/example/ios/RunnerTests/Info.plist rename to packages/image_picker/image_picker_ios/example/ios/RunnerTests/Info.plist diff --git a/packages/image_picker/image_picker/example/ios/RunnerTests/MetaDataUtilTests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/MetaDataUtilTests.m similarity index 98% rename from packages/image_picker/image_picker/example/ios/RunnerTests/MetaDataUtilTests.m rename to packages/image_picker/image_picker_ios/example/ios/RunnerTests/MetaDataUtilTests.m index 4160c51cc600..b684a214570b 100644 --- a/packages/image_picker/image_picker/example/ios/RunnerTests/MetaDataUtilTests.m +++ b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/MetaDataUtilTests.m @@ -4,8 +4,8 @@ #import "ImagePickerTestImages.h" -@import image_picker; -@import image_picker.Test; +@import image_picker_ios; +@import image_picker_ios.Test; @import XCTest; @interface MetaDataUtilTests : XCTestCase diff --git a/packages/image_picker/image_picker/example/ios/RunnerTests/PhotoAssetUtilTests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/PhotoAssetUtilTests.m similarity index 99% rename from packages/image_picker/image_picker/example/ios/RunnerTests/PhotoAssetUtilTests.m rename to packages/image_picker/image_picker_ios/example/ios/RunnerTests/PhotoAssetUtilTests.m index 97b4b6cd8eb3..d211ea3f91df 100644 --- a/packages/image_picker/image_picker/example/ios/RunnerTests/PhotoAssetUtilTests.m +++ b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/PhotoAssetUtilTests.m @@ -4,8 +4,8 @@ #import "ImagePickerTestImages.h" -@import image_picker; -@import image_picker.Test; +@import image_picker_ios; +@import image_picker_ios.Test; @import XCTest; @interface PhotoAssetUtilTests : XCTestCase diff --git a/packages/image_picker/image_picker/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m similarity index 98% rename from packages/image_picker/image_picker/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m rename to packages/image_picker/image_picker_ios/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m index f94db83d5696..688f5fbee032 100644 --- a/packages/image_picker/image_picker/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m +++ b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m @@ -5,8 +5,8 @@ #import #import -@import image_picker; -@import image_picker.Test; +@import image_picker_ios; +@import image_picker_ios.Test; @import XCTest; @interface PickerSaveImageToPathOperationTests : XCTestCase diff --git a/packages/image_picker/image_picker/example/ios/RunnerUITests/ImagePickerFromGalleryUITests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerUITests/ImagePickerFromGalleryUITests.m similarity index 100% rename from packages/image_picker/image_picker/example/ios/RunnerUITests/ImagePickerFromGalleryUITests.m rename to packages/image_picker/image_picker_ios/example/ios/RunnerUITests/ImagePickerFromGalleryUITests.m diff --git a/packages/image_picker/image_picker/example/ios/RunnerUITests/ImagePickerFromLimitedGalleryUITests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerUITests/ImagePickerFromLimitedGalleryUITests.m similarity index 100% rename from packages/image_picker/image_picker/example/ios/RunnerUITests/ImagePickerFromLimitedGalleryUITests.m rename to packages/image_picker/image_picker_ios/example/ios/RunnerUITests/ImagePickerFromLimitedGalleryUITests.m diff --git a/packages/image_picker/image_picker/example/ios/RunnerUITests/Info.plist b/packages/image_picker/image_picker_ios/example/ios/RunnerUITests/Info.plist similarity index 100% rename from packages/image_picker/image_picker/example/ios/RunnerUITests/Info.plist rename to packages/image_picker/image_picker_ios/example/ios/RunnerUITests/Info.plist diff --git a/packages/image_picker/image_picker_ios/example/ios/TestImages/gifImage.gif b/packages/image_picker/image_picker_ios/example/ios/TestImages/gifImage.gif new file mode 100644 index 0000000000000000000000000000000000000000..5f989fcf40c7de44fd1aa768dfec58c4d2c4bd72 GIT binary patch literal 843 zcmZ?wbhEHbHaF& zNj2h=DkPWW3a`$Q-yNp3KS+B|sK(54g;n{=JF_*PK2pk2-CeAq#NjYgL zAweavM$D*gWq;4W`G$e(Ed%E}29Ea(-0v8;-ZSvLVc>tmAn=Sq@FfHDM+Vjp4D6p6 z7{4+we`R3!&cOJcf$19q^LIv;FAS_-8Q8usuzzJ_`^L!mfq~;A12CkyJ~428W@Pxm z!0?lS;RhqbPevvn`HO+!7bC-OP%@+kR1VRQz zDF#+Bn}NZLQ5wz;V$^`DVPart&tzbMs)+*9AOJKOq#H^zFJMHNxPS?+nr8tsoXrl> z_WvPgaYji=ft9{~Ua?+cN`6wRUUGh}ennz|zM-ChK7);YML}Y6c4~=2Qfhi;o~_dR z-TRdkGE;1o!cBb*d<&dYGcrA@ic*8C{6dnevXd=SljF4@-9<|J>PIC z)FAWfZHvW%{|_(-axkzn@GvtfF)#@-G7B>PKf)jnY#6dKf&o|?kYHqDW?^Mx=iubx z1}fMpz`(@F%*@2X%*qOK1Y<2wo`FS>RY=j$kxe)-kzJ`!#HexNLJno8jR!@8E`Crk zPAY2R|V^&07y2J$~}^+4C1KUw!=a`ODXD-+%o41@afLi3{--kc9XQ&0m5*e=)JJ zFtf0O{Kd#r4)VAl3#+0bn~-B5dt#xml2Idvh||P{8xL|S8wY(5O)9#`C8lEXQ1v6o zYha%d=dmWTdeO? z6Xq8X73Ad=mJ${Px>Zt=Ur1V3T0&M#LQ>*C&=5wTRyI~PZZDSry^7odplYBQ6;?oIZfZ%QLPc&)Ua?h$ ztrA#;6_5=Q)>l#hD=EpgRf_NpP;kyKN>wn`Gto0pvg1-vP_QXVNwW%aaf50H@@$nd zN=gc>^!3Zj%k|2Q_413-^$jg8E%gnI^o@*kfhu&1EAvVcD|GXUl_7?}%yCIAPA?$S;6fmsbq+8_<{faA|!*Jp+Ag29#u` zA)Jb$7bFAG2KFJuVjH-LR?bDKDVZg9hgK@M=jW&Aq}m{CgUKNrYy&dQ%D*Tx73gJ< z$LtJk^fAPs>TUEvzD5cKNDzZXfnjXNWup&|FFUTs6>@8VQ7GZ*;usljA6n>XbQXmr}WjI}hget}722v5LZl$IPw31PjA{wzd_BFAR_$>P>n@kKy z42&HZ5K>mQ&LFiCzkq*HHY6sH5al^C?vKD{o!xR%wtz?d!#H?p%> zo|7xZc`2{1&dWKjq_n=(r3W_vHn)?&cE&={ff#ze$-aL+XVh~|cG$3VD`<(4_hc4| zgW1z(&TQ;7UADbNHxnb722-JInoK8tWF#iDT{#d2;`Ep^x+qa&lci6!WGHAud#LFQ z*`a%6^maQL7c-NMlnM;a<2csh2bv6re7)Iha=p&$^%_>xqL;q1lbRo`{!`c$krR4> z@}y5wMSCERmC5ksMN7d*DjAJrkc<0~wrEmwRDE`tp%JmU1`| qjAPF{YdVGb-*3@k6%G^*6b=*)6b=*){C^Hy%}>I|p()1c2>t?)0hB5L literal 0 HcmV?d00001 diff --git a/packages/image_picker/image_picker_ios/example/ios/image_picker_exampleTests/Info.plist b/packages/image_picker/image_picker_ios/example/ios/image_picker_exampleTests/Info.plist new file mode 100644 index 000000000000..6c40a6cd0c4a --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/image_picker_exampleTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/packages/image_picker/image_picker_ios/example/lib/main.dart b/packages/image_picker/image_picker_ios/example/lib/main.dart new file mode 100755 index 000000000000..48eee35445da --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/lib/main.dart @@ -0,0 +1,467 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; +import 'package:video_player/video_player.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return const MaterialApp( + title: 'Image Picker Demo', + home: MyHomePage(title: 'Image Picker Example'), + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({Key? key, this.title}) : super(key: key); + + final String? title; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + List? _imageFileList; + + set _imageFile(XFile? value) { + _imageFileList = value == null ? null : [value]; + } + + dynamic _pickImageError; + bool isVideo = false; + + VideoPlayerController? _controller; + VideoPlayerController? _toBeDisposed; + String? _retrieveDataError; + + final ImagePickerPlatform _picker = ImagePickerPlatform.instance; + final TextEditingController maxWidthController = TextEditingController(); + final TextEditingController maxHeightController = TextEditingController(); + final TextEditingController qualityController = TextEditingController(); + + Future _playVideo(XFile? file) async { + if (file != null && mounted) { + await _disposeVideoController(); + late VideoPlayerController controller; + if (kIsWeb) { + controller = VideoPlayerController.network(file.path); + } else { + controller = VideoPlayerController.file(File(file.path)); + } + _controller = controller; + // In web, most browsers won't honor a programmatic call to .play + // if the video has a sound track (and is not muted). + // Mute the video so it auto-plays in web! + // This is not needed if the call to .play is the result of user + // interaction (clicking on a "play" button, for example). + const double volume = kIsWeb ? 0.0 : 1.0; + await controller.setVolume(volume); + await controller.initialize(); + await controller.setLooping(true); + await controller.play(); + setState(() {}); + } + } + + Future _onImageButtonPressed(ImageSource source, + {BuildContext? context, bool isMultiImage = false}) async { + if (_controller != null) { + await _controller!.setVolume(0.0); + } + if (isVideo) { + final XFile? file = await _picker.getVideo( + source: source, maxDuration: const Duration(seconds: 10)); + await _playVideo(file); + } else if (isMultiImage) { + await _displayPickImageDialog(context!, + (double? maxWidth, double? maxHeight, int? quality) async { + try { + final List? pickedFileList = await _picker.getMultiImage( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: quality, + ); + setState(() { + _imageFileList = pickedFileList; + }); + } catch (e) { + setState(() { + _pickImageError = e; + }); + } + }); + } else { + await _displayPickImageDialog(context!, + (double? maxWidth, double? maxHeight, int? quality) async { + try { + final XFile? pickedFile = await _picker.getImage( + source: source, + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: quality, + ); + setState(() { + _imageFile = pickedFile; + }); + } catch (e) { + setState(() { + _pickImageError = e; + }); + } + }); + } + } + + @override + void deactivate() { + if (_controller != null) { + _controller!.setVolume(0.0); + _controller!.pause(); + } + super.deactivate(); + } + + @override + void dispose() { + _disposeVideoController(); + maxWidthController.dispose(); + maxHeightController.dispose(); + qualityController.dispose(); + super.dispose(); + } + + Future _disposeVideoController() async { + if (_toBeDisposed != null) { + await _toBeDisposed!.dispose(); + } + _toBeDisposed = _controller; + _controller = null; + } + + Widget _previewVideo() { + final Text? retrieveError = _getRetrieveErrorWidget(); + if (retrieveError != null) { + return retrieveError; + } + if (_controller == null) { + return const Text( + 'You have not yet picked a video', + textAlign: TextAlign.center, + ); + } + return Padding( + padding: const EdgeInsets.all(10.0), + child: AspectRatioVideo(_controller), + ); + } + + Widget _previewImages() { + final Text? retrieveError = _getRetrieveErrorWidget(); + if (retrieveError != null) { + return retrieveError; + } + if (_imageFileList != null) { + return Semantics( + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + // Why network for web? + // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform + return Semantics( + label: 'image_picker_example_picked_image', + child: kIsWeb + ? Image.network(_imageFileList![index].path) + : Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + label: 'image_picker_example_picked_images'); + } else if (_pickImageError != null) { + return Text( + 'Pick image error: $_pickImageError', + textAlign: TextAlign.center, + ); + } else { + return const Text( + 'You have not yet picked an image.', + textAlign: TextAlign.center, + ); + } + } + + Widget _handlePreview() { + if (isVideo) { + return _previewVideo(); + } else { + return _previewImages(); + } + } + + Future retrieveLostData() async { + final LostDataResponse response = await _picker.getLostData(); + if (response.isEmpty) { + return; + } + if (response.file != null) { + if (response.type == RetrieveType.video) { + isVideo = true; + await _playVideo(response.file); + } else { + isVideo = false; + setState(() { + _imageFile = response.file; + _imageFileList = response.files; + }); + } + } else { + _retrieveDataError = response.exception!.code; + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title!), + ), + body: Center( + child: !kIsWeb && defaultTargetPlatform == TargetPlatform.android + ? FutureBuilder( + future: retrieveLostData(), + builder: (BuildContext context, AsyncSnapshot snapshot) { + switch (snapshot.connectionState) { + case ConnectionState.none: + case ConnectionState.waiting: + return const Text( + 'You have not yet picked an image.', + textAlign: TextAlign.center, + ); + case ConnectionState.done: + return _handlePreview(); + default: + if (snapshot.hasError) { + return Text( + 'Pick image/video error: ${snapshot.error}}', + textAlign: TextAlign.center, + ); + } else { + return const Text( + 'You have not yet picked an image.', + textAlign: TextAlign.center, + ); + } + } + }, + ) + : _handlePreview(), + ), + floatingActionButton: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Semantics( + label: 'image_picker_example_from_gallery', + child: FloatingActionButton( + onPressed: () { + isVideo = false; + _onImageButtonPressed(ImageSource.gallery, context: context); + }, + heroTag: 'image0', + tooltip: 'Pick Image from gallery', + child: const Icon(Icons.photo), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + onPressed: () { + isVideo = false; + _onImageButtonPressed( + ImageSource.gallery, + context: context, + isMultiImage: true, + ); + }, + heroTag: 'image1', + tooltip: 'Pick Multiple Image from gallery', + child: const Icon(Icons.photo_library), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + onPressed: () { + isVideo = false; + _onImageButtonPressed(ImageSource.camera, context: context); + }, + heroTag: 'image2', + tooltip: 'Take a Photo', + child: const Icon(Icons.camera_alt), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + backgroundColor: Colors.red, + onPressed: () { + isVideo = true; + _onImageButtonPressed(ImageSource.gallery); + }, + heroTag: 'video0', + tooltip: 'Pick Video from gallery', + child: const Icon(Icons.video_library), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + backgroundColor: Colors.red, + onPressed: () { + isVideo = true; + _onImageButtonPressed(ImageSource.camera); + }, + heroTag: 'video1', + tooltip: 'Take a Video', + child: const Icon(Icons.videocam), + ), + ), + ], + ), + ); + } + + Text? _getRetrieveErrorWidget() { + if (_retrieveDataError != null) { + final Text result = Text(_retrieveDataError!); + _retrieveDataError = null; + return result; + } + return null; + } + + Future _displayPickImageDialog( + BuildContext context, OnPickImageCallback onPick) async { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Add optional parameters'), + content: Column( + children: [ + TextField( + controller: maxWidthController, + keyboardType: + const TextInputType.numberWithOptions(decimal: true), + decoration: const InputDecoration( + hintText: 'Enter maxWidth if desired'), + ), + TextField( + controller: maxHeightController, + keyboardType: + const TextInputType.numberWithOptions(decimal: true), + decoration: const InputDecoration( + hintText: 'Enter maxHeight if desired'), + ), + TextField( + controller: qualityController, + keyboardType: TextInputType.number, + decoration: const InputDecoration( + hintText: 'Enter quality if desired'), + ), + ], + ), + actions: [ + TextButton( + child: const Text('CANCEL'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: const Text('PICK'), + onPressed: () { + final double? width = maxWidthController.text.isNotEmpty + ? double.parse(maxWidthController.text) + : null; + final double? height = maxHeightController.text.isNotEmpty + ? double.parse(maxHeightController.text) + : null; + final int? quality = qualityController.text.isNotEmpty + ? int.parse(qualityController.text) + : null; + onPick(width, height, quality); + Navigator.of(context).pop(); + }), + ], + ); + }); + } +} + +typedef OnPickImageCallback = void Function( + double? maxWidth, double? maxHeight, int? quality); + +class AspectRatioVideo extends StatefulWidget { + const AspectRatioVideo(this.controller); + + final VideoPlayerController? controller; + + @override + AspectRatioVideoState createState() => AspectRatioVideoState(); +} + +class AspectRatioVideoState extends State { + VideoPlayerController? get controller => widget.controller; + bool initialized = false; + + void _onVideoControllerUpdate() { + if (!mounted) { + return; + } + if (initialized != controller!.value.isInitialized) { + initialized = controller!.value.isInitialized; + setState(() {}); + } + } + + @override + void initState() { + super.initState(); + controller!.addListener(_onVideoControllerUpdate); + } + + @override + void dispose() { + controller!.removeListener(_onVideoControllerUpdate); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (initialized) { + return Center( + child: AspectRatio( + aspectRatio: controller!.value.aspectRatio, + child: VideoPlayer(controller!), + ), + ); + } else { + return Container(); + } + } +} diff --git a/packages/image_picker/image_picker_ios/example/pubspec.yaml b/packages/image_picker/image_picker_ios/example/pubspec.yaml new file mode 100755 index 000000000000..a47893d7687f --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/pubspec.yaml @@ -0,0 +1,29 @@ +name: image_picker_example +description: Demonstrates how to use the image_picker plugin. +publish_to: none + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.5.0" + +dependencies: + flutter: + sdk: flutter + image_picker_ios: + # When depending on this package from a real application you should use: + # image_picker_ios: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + image_picker_platform_interface: ^2.3.0 + video_player: ^2.1.4 + +dev_dependencies: + flutter_driver: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/image_picker/image_picker_ios/example/test_driver/integration_test.dart b/packages/image_picker/image_picker_ios/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4f10f2a522f3 --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/test_driver/integration_test.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); diff --git a/packages/image_picker/image_picker/ios/Assets/.gitkeep b/packages/image_picker/image_picker_ios/ios/Assets/.gitkeep similarity index 100% rename from packages/image_picker/image_picker/ios/Assets/.gitkeep rename to packages/image_picker/image_picker_ios/ios/Assets/.gitkeep diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerImageUtil.h b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerImageUtil.h similarity index 100% rename from packages/image_picker/image_picker/ios/Classes/FLTImagePickerImageUtil.h rename to packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerImageUtil.h diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerImageUtil.m b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerImageUtil.m similarity index 100% rename from packages/image_picker/image_picker/ios/Classes/FLTImagePickerImageUtil.m rename to packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerImageUtil.m diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerMetaDataUtil.h b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerMetaDataUtil.h similarity index 100% rename from packages/image_picker/image_picker/ios/Classes/FLTImagePickerMetaDataUtil.h rename to packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerMetaDataUtil.h diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerMetaDataUtil.m b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerMetaDataUtil.m similarity index 100% rename from packages/image_picker/image_picker/ios/Classes/FLTImagePickerMetaDataUtil.m rename to packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerMetaDataUtil.m diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPhotoAssetUtil.h b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPhotoAssetUtil.h similarity index 100% rename from packages/image_picker/image_picker/ios/Classes/FLTImagePickerPhotoAssetUtil.h rename to packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPhotoAssetUtil.h diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPhotoAssetUtil.m b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPhotoAssetUtil.m similarity index 100% rename from packages/image_picker/image_picker/ios/Classes/FLTImagePickerPhotoAssetUtil.m rename to packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPhotoAssetUtil.m diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.h b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.h similarity index 100% rename from packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.h rename to packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.h diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m similarity index 100% rename from packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m rename to packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin_Test.h b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin_Test.h similarity index 95% rename from packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin_Test.h rename to packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin_Test.h index 5442f7d089c6..039f76de427d 100644 --- a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin_Test.h +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin_Test.h @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// This header is available in the Test module. Import via "@import image_picker.Test;" +// This header is available in the Test module. Import via "@import image_picker_ios_ios.Test;" -#import +#import /** Methods exposed for unit testing. */ @interface FLTImagePickerPlugin () diff --git a/packages/image_picker/image_picker/ios/Classes/FLTPHPickerSaveImageToPathOperation.h b/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.h similarity index 100% rename from packages/image_picker/image_picker/ios/Classes/FLTPHPickerSaveImageToPathOperation.h rename to packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.h diff --git a/packages/image_picker/image_picker/ios/Classes/FLTPHPickerSaveImageToPathOperation.m b/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.m similarity index 100% rename from packages/image_picker/image_picker/ios/Classes/FLTPHPickerSaveImageToPathOperation.m rename to packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.m diff --git a/packages/image_picker/image_picker/ios/Classes/ImagePickerPlugin.modulemap b/packages/image_picker/image_picker_ios/ios/Classes/ImagePickerPlugin.modulemap similarity index 76% rename from packages/image_picker/image_picker/ios/Classes/ImagePickerPlugin.modulemap rename to packages/image_picker/image_picker_ios/ios/Classes/ImagePickerPlugin.modulemap index dc702ea49fb1..0d60b684a256 100644 --- a/packages/image_picker/image_picker/ios/Classes/ImagePickerPlugin.modulemap +++ b/packages/image_picker/image_picker_ios/ios/Classes/ImagePickerPlugin.modulemap @@ -1,6 +1,6 @@ -framework module image_picker { - umbrella header "image_picker-umbrella.h" - +framework module image_picker_ios { + umbrella header "image_picker_ios-umbrella.h" + export * module * { export * } diff --git a/packages/image_picker/image_picker/ios/Classes/image_picker-umbrella.h b/packages/image_picker/image_picker_ios/ios/Classes/image_picker_ios-umbrella.h similarity index 79% rename from packages/image_picker/image_picker/ios/Classes/image_picker-umbrella.h rename to packages/image_picker/image_picker_ios/ios/Classes/image_picker_ios-umbrella.h index 0d89b2e1f636..0e23d6d9d60a 100644 --- a/packages/image_picker/image_picker/ios/Classes/image_picker-umbrella.h +++ b/packages/image_picker/image_picker_ios/ios/Classes/image_picker_ios-umbrella.h @@ -3,4 +3,4 @@ // found in the LICENSE file. #import -#import +#import diff --git a/packages/image_picker/image_picker/ios/image_picker.podspec b/packages/image_picker/image_picker_ios/ios/image_picker_ios.podspec similarity index 86% rename from packages/image_picker/image_picker/ios/image_picker.podspec rename to packages/image_picker/image_picker_ios/ios/image_picker_ios.podspec index 2a10b1ce01a8..549c5f09e1f8 100644 --- a/packages/image_picker/image_picker/ios/image_picker.podspec +++ b/packages/image_picker/image_picker_ios/ios/image_picker_ios.podspec @@ -2,7 +2,7 @@ # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html # Pod::Spec.new do |s| - s.name = 'image_picker' + s.name = 'image_picker_ios' s.version = '0.0.1' s.summary = 'Flutter plugin that shows an image picker.' s.description = <<-DESC @@ -12,8 +12,8 @@ Downloaded by pub (not CocoaPods). s.homepage = 'https://github.com/flutter/plugins' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/image_picker' } - s.documentation_url = 'https://pub.dev/packages/image_picker' + s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/image_picker_ios' } + s.documentation_url = 'https://pub.dev/packages/image_picker_ios' s.source_files = 'Classes/**/*.{h,m}' s.public_header_files = 'Classes/**/*.h' s.module_map = 'Classes/ImagePickerPlugin.modulemap' diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml new file mode 100755 index 000000000000..2587c9a0d15b --- /dev/null +++ b/packages/image_picker/image_picker_ios/pubspec.yaml @@ -0,0 +1,26 @@ +name: image_picker_ios +description: iOS implementation of the video_picker plugin. +repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_ios +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 +version: 0.8.4+11 + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.5.0" + +flutter: + plugin: + implements: image_picker + platforms: + ios: + pluginClass: FLTImagePickerPlugin + +dependencies: + flutter: + sdk: flutter + image_picker_platform_interface: ^2.3.0 + +dev_dependencies: + flutter_test: + sdk: flutter + mockito: ^5.0.0 From b9ea3037f1ed95c238d418a74e49b87388bbd421 Mon Sep 17 00:00:00 2001 From: Kate Lovett Date: Tue, 5 Apr 2022 13:11:10 -0500 Subject: [PATCH 336/600] [webview_flutter] Migrate deprecated Scaffold snackbars (#5150) --- .../webview_flutter/CHANGELOG.md | 3 +- .../webview_flutter/example/lib/main.dart | 129 +++++++++--------- .../example/test/main_test.dart | 39 ++++++ .../webview_flutter/pubspec.yaml | 2 +- .../webview_flutter_android/CHANGELOG.md | 4 + .../example/lib/main.dart | 79 +++++------ .../webview_flutter_android/pubspec.yaml | 2 +- .../webview_flutter_wkwebview/CHANGELOG.md | 3 +- .../example/lib/main.dart | 47 +++---- .../webview_flutter_wkwebview/pubspec.yaml | 2 +- 10 files changed, 168 insertions(+), 142 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter/example/test/main_test.dart diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index dde4472eed57..f8a6e2514d7e 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 3.0.2 +* Migrates deprecated `Scaffold.showSnackBar` to `ScaffoldMessenger` in example app. * Adds OS version support information to README. ## 3.0.1 diff --git a/packages/webview_flutter/webview_flutter/example/lib/main.dart b/packages/webview_flutter/webview_flutter/example/lib/main.dart index 88c240a7316c..51080be01df0 100644 --- a/packages/webview_flutter/webview_flutter/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter/example/lib/main.dart @@ -13,7 +13,7 @@ import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; import 'package:webview_flutter/webview_flutter.dart'; -void main() => runApp(MaterialApp(home: WebViewExample())); +void main() => runApp(const MaterialApp(home: WebViewExample())); const String kNavigationExamplePage = ''' @@ -71,6 +71,10 @@ const String kTransparentBackgroundPage = ''' '''; class WebViewExample extends StatefulWidget { + const WebViewExample({this.cookieManager}); + + final CookieManager? cookieManager; + @override _WebViewExampleState createState() => _WebViewExampleState(); } @@ -96,42 +100,38 @@ class _WebViewExampleState extends State { // This drop down menu demonstrates that Flutter widgets can be shown over the web view. actions: [ NavigationControls(_controller.future), - SampleMenu(_controller.future), + SampleMenu(_controller.future, widget.cookieManager), ], ), - // We're using a Builder here so we have a context that is below the Scaffold - // to allow calling Scaffold.of(context) so we can show a snackbar. - body: Builder(builder: (BuildContext context) { - return WebView( - initialUrl: 'https://flutter.dev', - javascriptMode: JavascriptMode.unrestricted, - onWebViewCreated: (WebViewController webViewController) { - _controller.complete(webViewController); - }, - onProgress: (int progress) { - print('WebView is loading (progress : $progress%)'); - }, - javascriptChannels: { - _toasterJavascriptChannel(context), - }, - navigationDelegate: (NavigationRequest request) { - if (request.url.startsWith('https://www.youtube.com/')) { - print('blocking navigation to $request}'); - return NavigationDecision.prevent; - } - print('allowing navigation to $request'); - return NavigationDecision.navigate; - }, - onPageStarted: (String url) { - print('Page started loading: $url'); - }, - onPageFinished: (String url) { - print('Page finished loading: $url'); - }, - gestureNavigationEnabled: true, - backgroundColor: const Color(0x00000000), - ); - }), + body: WebView( + initialUrl: 'https://flutter.dev', + javascriptMode: JavascriptMode.unrestricted, + onWebViewCreated: (WebViewController webViewController) { + _controller.complete(webViewController); + }, + onProgress: (int progress) { + print('WebView is loading (progress : $progress%)'); + }, + javascriptChannels: { + _toasterJavascriptChannel(context), + }, + navigationDelegate: (NavigationRequest request) { + if (request.url.startsWith('https://www.youtube.com/')) { + print('blocking navigation to $request}'); + return NavigationDecision.prevent; + } + print('allowing navigation to $request'); + return NavigationDecision.navigate; + }, + onPageStarted: (String url) { + print('Page started loading: $url'); + }, + onPageFinished: (String url) { + print('Page finished loading: $url'); + }, + gestureNavigationEnabled: true, + backgroundColor: const Color(0x00000000), + ), floatingActionButton: favoriteButton(), ); } @@ -140,8 +140,7 @@ class _WebViewExampleState extends State { return JavascriptChannel( name: 'Toaster', onMessageReceived: (JavascriptMessage message) { - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar( + ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(message.message)), ); }); @@ -152,19 +151,24 @@ class _WebViewExampleState extends State { future: _controller.future, builder: (BuildContext context, AsyncSnapshot controller) { - if (controller.hasData) { - return FloatingActionButton( - onPressed: () async { - final String url = (await controller.data!.currentUrl())!; - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar( - SnackBar(content: Text('Favorited $url')), - ); - }, - child: const Icon(Icons.favorite), - ); - } - return Container(); + return FloatingActionButton( + onPressed: () async { + String? url; + if (controller.hasData) { + url = (await controller.data!.currentUrl())!; + } + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + controller.hasData + ? 'Favorited $url' + : 'Unable to favorite', + ), + ), + ); + }, + child: const Icon(Icons.favorite), + ); }); } } @@ -186,10 +190,11 @@ enum MenuOptions { } class SampleMenu extends StatelessWidget { - SampleMenu(this.controller); + SampleMenu(this.controller, CookieManager? cookieManager) + : cookieManager = cookieManager ?? CookieManager(); final Future controller; - final CookieManager cookieManager = CookieManager(); + late final CookieManager cookieManager; @override Widget build(BuildContext context) { @@ -315,8 +320,7 @@ class SampleMenu extends StatelessWidget { WebViewController controller, BuildContext context) async { final String cookies = await controller.runJavascriptReturningResult('document.cookie'); - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar(SnackBar( + ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Column( mainAxisAlignment: MainAxisAlignment.end, mainAxisSize: MainAxisSize.min, @@ -332,8 +336,7 @@ class SampleMenu extends StatelessWidget { WebViewController controller, BuildContext context) async { await controller.runJavascript( 'caches.open("test_caches_entry"); localStorage["test_localStorage"] = "dummy_entry";'); - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar(const SnackBar( + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text('Added a test entry to cache.'), )); } @@ -348,8 +351,7 @@ class SampleMenu extends StatelessWidget { Future _onClearCache( WebViewController controller, BuildContext context) async { await controller.clearCache(); - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar(const SnackBar( + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text('Cache cleared.'), )); } @@ -360,8 +362,7 @@ class SampleMenu extends StatelessWidget { if (!hadCookies) { message = 'There are no cookies.'; } - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar(SnackBar( + ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text(message), )); } @@ -375,7 +376,7 @@ class SampleMenu extends StatelessWidget { Future _onSetCookie( WebViewController controller, BuildContext context) async { - await CookieManager().setCookie( + await cookieManager.setCookie( const WebViewCookie( name: 'foo', value: 'bar', domain: 'httpbin.org', path: '/anything'), ); @@ -466,8 +467,7 @@ class NavigationControls extends StatelessWidget { if (await controller!.canGoBack()) { await controller.goBack(); } else { - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar( + ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('No back history item')), ); return; @@ -482,8 +482,7 @@ class NavigationControls extends StatelessWidget { if (await controller!.canGoForward()) { await controller.goForward(); } else { - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar( + ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('No forward history item')), ); diff --git a/packages/webview_flutter/webview_flutter/example/test/main_test.dart b/packages/webview_flutter/webview_flutter/example/test/main_test.dart new file mode 100644 index 000000000000..867633366e1a --- /dev/null +++ b/packages/webview_flutter/webview_flutter/example/test/main_test.dart @@ -0,0 +1,39 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:webview_flutter/webview_flutter.dart'; +import 'package:webview_flutter_example/main.dart'; + +void main() { + testWidgets('Test snackbar from ScaffoldMessenger', + (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: WebViewExample(cookieManager: FakeCookieManager()), + ), + ); + expect(find.byIcon(Icons.favorite), findsOneWidget); + await tester.tap(find.byIcon(Icons.favorite)); + await tester.pump(); + expect(find.byType(SnackBar), findsOneWidget); + }); +} + +class FakeCookieManager implements CookieManager { + factory FakeCookieManager() { + return _instance ??= FakeCookieManager._(); + } + + FakeCookieManager._(); + + static FakeCookieManager? _instance; + + @override + Future clearCookies() => throw UnimplementedError(); + + @override + Future setCookie(WebViewCookie cookie) => throw UnimplementedError(); +} diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index 322f4ada3b56..10350984ce9a 100644 --- a/packages/webview_flutter/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter description: A Flutter plugin that provides a WebView widget on Android and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 3.0.1 +version: 3.0.2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index ad81b0f6e83d..fb5a2b85ffed 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.8.5 + +* Migrates deprecated `Scaffold.showSnackBar` to `ScaffoldMessenger` in example app. + ## 2.8.4 * Fixes bug preventing `mockito` code generation for tests. diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart index 0d0cd59af796..3dd107bd557b 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart @@ -110,37 +110,33 @@ class _WebViewExampleState extends State<_WebViewExample> { _SampleMenu(_controller.future), ], ), - // We're using a Builder here so we have a context that is below the Scaffold - // to allow calling Scaffold.of(context) so we can show a snackbar. - body: Builder(builder: (BuildContext context) { - return WebView( - initialUrl: 'https://flutter.dev', - onWebViewCreated: (WebViewController controller) { - _controller.complete(controller); - }, - onProgress: (int progress) { - print('WebView is loading (progress : $progress%)'); - }, - navigationDelegate: (NavigationRequest request) { - if (request.url.startsWith('https://www.youtube.com/')) { - print('blocking navigation to $request}'); - return NavigationDecision.prevent; - } - print('allowing navigation to $request'); - return NavigationDecision.navigate; - }, - onPageStarted: (String url) { - print('Page started loading: $url'); - }, - onPageFinished: (String url) { - print('Page finished loading: $url'); - }, - javascriptChannels: _createJavascriptChannels(context), - javascriptMode: JavascriptMode.unrestricted, - userAgent: 'Custom_User_Agent', - backgroundColor: const Color(0x80000000), - ); - }), + body: WebView( + initialUrl: 'https://flutter.dev', + onWebViewCreated: (WebViewController controller) { + _controller.complete(controller); + }, + onProgress: (int progress) { + print('WebView is loading (progress : $progress%)'); + }, + navigationDelegate: (NavigationRequest request) { + if (request.url.startsWith('https://www.youtube.com/')) { + print('blocking navigation to $request}'); + return NavigationDecision.prevent; + } + print('allowing navigation to $request'); + return NavigationDecision.navigate; + }, + onPageStarted: (String url) { + print('Page started loading: $url'); + }, + onPageFinished: (String url) { + print('Page finished loading: $url'); + }, + javascriptChannels: _createJavascriptChannels(context), + javascriptMode: JavascriptMode.unrestricted, + userAgent: 'Custom_User_Agent', + backgroundColor: const Color(0x80000000), + ), floatingActionButton: favoriteButton(), ); } @@ -154,8 +150,7 @@ class _WebViewExampleState extends State<_WebViewExample> { return FloatingActionButton( onPressed: () async { final String url = (await controller.data!.currentUrl())!; - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar( + ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Favorited $url')), ); }, @@ -323,8 +318,7 @@ class _SampleMenu extends StatelessWidget { WebViewController controller, BuildContext context) async { final String cookies = await controller.runJavascriptReturningResult('document.cookie'); - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar(SnackBar( + ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Column( mainAxisAlignment: MainAxisAlignment.end, mainAxisSize: MainAxisSize.min, @@ -340,8 +334,7 @@ class _SampleMenu extends StatelessWidget { WebViewController controller, BuildContext context) async { await controller.runJavascript( 'caches.open("test_caches_entry"); localStorage["test_localStorage"] = "dummy_entry";'); - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar(const SnackBar( + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text('Added a test entry to cache.'), )); } @@ -356,8 +349,7 @@ class _SampleMenu extends StatelessWidget { Future _onClearCache( WebViewController controller, BuildContext context) async { await controller.clearCache(); - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar(const SnackBar( + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text('Cache cleared.'), )); } @@ -369,8 +361,7 @@ class _SampleMenu extends StatelessWidget { if (!hadCookies) { message = 'There are no cookies.'; } - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar(SnackBar( + ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text(message), )); } @@ -474,8 +465,7 @@ class _NavigationControls extends StatelessWidget { if (await controller!.canGoBack()) { await controller.goBack(); } else { - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar( + ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('No back history item')), ); return; @@ -490,8 +480,7 @@ class _NavigationControls extends StatelessWidget { if (await controller!.canGoForward()) { await controller.goForward(); } else { - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar( + ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('No forward history item')), ); diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 5c3c83abc53d..3fae698d1481 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_android description: A Flutter plugin that provides a WebView widget on Android. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.8.4 +version: 2.8.5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index c3dd8726273e..bad900de46b4 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 2.7.2 * Fixes an integration test race condition. +* Migrates deprecated `Scaffold.showSnackBar` to `ScaffoldMessenger` in example app. ## 2.7.1 diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart index d4e0ec4aba0c..e126de8491e5 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart @@ -105,27 +105,23 @@ class _WebViewExampleState extends State<_WebViewExample> { _SampleMenu(_controller.future), ], ), - // We're using a Builder here so we have a context that is below the Scaffold - // to allow calling Scaffold.of(context) so we can show a snackbar. - body: Builder(builder: (BuildContext context) { - return WebView( - initialUrl: 'https://flutter.dev/', - onWebViewCreated: (WebViewController controller) { - _controller.complete(controller); - }, - javascriptChannels: _createJavascriptChannels(context), - javascriptMode: JavascriptMode.unrestricted, - navigationDelegate: (NavigationRequest request) { - if (request.url.startsWith('https://www.youtube.com/')) { - print('blocking navigation to $request}'); - return NavigationDecision.prevent; - } - print('allowing navigation to $request'); - return NavigationDecision.navigate; - }, - backgroundColor: const Color(0x80000000), - ); - }), + body: WebView( + initialUrl: 'https://flutter.dev/', + onWebViewCreated: (WebViewController controller) { + _controller.complete(controller); + }, + javascriptChannels: _createJavascriptChannels(context), + javascriptMode: JavascriptMode.unrestricted, + navigationDelegate: (NavigationRequest request) { + if (request.url.startsWith('https://www.youtube.com/')) { + print('blocking navigation to $request}'); + return NavigationDecision.prevent; + } + print('allowing navigation to $request'); + return NavigationDecision.navigate; + }, + backgroundColor: const Color(0x80000000), + ), floatingActionButton: favoriteButton(), ); } @@ -139,8 +135,7 @@ class _WebViewExampleState extends State<_WebViewExample> { return FloatingActionButton( onPressed: () async { final String url = (await controller.data!.currentUrl())!; - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar( + ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Favorited $url')), ); }, @@ -456,8 +451,7 @@ class _NavigationControls extends StatelessWidget { if (await controller!.canGoBack()) { await controller.goBack(); } else { - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar( + ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('No back history item')), ); return; @@ -472,8 +466,7 @@ class _NavigationControls extends StatelessWidget { if (await controller!.canGoForward()) { await controller.goForward(); } else { - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar( + ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('No forward history item')), ); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 219d6ce49f4f..75e7ccaaf74f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_wkwebview description: A Flutter plugin that provides a WebView widget based on Apple's WKWebView control. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_wkwebview issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.7.1 +version: 2.7.2 environment: sdk: ">=2.14.0 <3.0.0" From d2ddbf470f9ec5a7c8dad220ff854568c0ed1b9b Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 5 Apr 2022 15:56:11 -0400 Subject: [PATCH 337/600] [image_picker] Publish federated version (#5174) --- packages/image_picker/image_picker/CHANGELOG.md | 2 +- packages/image_picker/image_picker/pubspec.yaml | 12 +++--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index d84304f0b5e9..f1bf54c5cd35 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,4 +1,4 @@ -## NEXT +## 0.8.5 * Moves Android and iOS implementations to federated packages. * Adds OS version support information to README. diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 05ac189fb05d..77a50916283e 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -3,10 +3,7 @@ description: Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.4+11 -# Temporarily disable publishing to allow moving Android and iOS -# implementations. -publish_to: none +version: 0.8.5 environment: sdk: ">=2.14.0 <3.0.0" @@ -25,12 +22,9 @@ flutter: dependencies: flutter: sdk: flutter - # Temporary path dependencies to allow moving Android and iOS implementations. - image_picker_android: - path: ../image_picker_android + image_picker_android: ^0.8.4+11 image_picker_for_web: ^2.1.0 - image_picker_ios: - path: ../image_picker_ios + image_picker_ios: ^0.8.4+11 image_picker_platform_interface: ^2.3.0 dev_dependencies: From 029c9998b8546eaa5e2c95bcb5a69b9c5ea7716d Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 5 Apr 2022 19:31:08 -0400 Subject: [PATCH 338/600] [espresso] Updates androidx.test.ext dependencies (#5178) --- .ci/flutter_master.version | 2 +- packages/espresso/CHANGELOG.md | 4 +++- packages/espresso/android/build.gradle | 4 ++-- packages/espresso/pubspec.yaml | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index ef23ed91e5bb..ac353e2ed1d7 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -66ed64be4fd8349cf47bf9b86f4d74ae3b4198f8 +5c6918933e15672e216675fc2e0e06bf8307a55a diff --git a/packages/espresso/CHANGELOG.md b/packages/espresso/CHANGELOG.md index b76cf2ab141b..eb1f267ca1d3 100644 --- a/packages/espresso/CHANGELOG.md +++ b/packages/espresso/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.2.0+1 * Adds OS version support information to README. +* Updates `androidx.test.ext:junit` and `androidx.test.ext:truth` for + compatibilty with updated Flutter template. ## 0.2.0 diff --git a/packages/espresso/android/build.gradle b/packages/espresso/android/build.gradle index 097e53f1c90b..62baba0ac31b 100644 --- a/packages/espresso/android/build.gradle +++ b/packages/espresso/android/build.gradle @@ -67,8 +67,8 @@ dependencies { api 'androidx.test:rules:1.1.0' // Assertions - api 'androidx.test.ext:junit:1.0.0' - api 'androidx.test.ext:truth:1.0.0' + api 'androidx.test.ext:junit:1.1.3' + api 'androidx.test.ext:truth:1.4.0' api 'com.google.truth:truth:0.42' // Espresso dependencies diff --git a/packages/espresso/pubspec.yaml b/packages/espresso/pubspec.yaml index 0bbf3c01158e..7737fc46d4b6 100644 --- a/packages/espresso/pubspec.yaml +++ b/packages/espresso/pubspec.yaml @@ -3,7 +3,7 @@ description: Java classes for testing Flutter apps using Espresso. Allows driving Flutter widgets from a native Espresso test. repository: https://github.com/flutter/plugins/tree/main/packages/espresso issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+espresso%22 -version: 0.2.0 +version: 0.2.0+1 environment: sdk: ">=2.12.0 <3.0.0" From 1b9df5c05572cbabe7bc432b8bab6de9bab3dbb4 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 5 Apr 2022 20:56:12 -0400 Subject: [PATCH 339/600] Roll Flutter from 5c6918933e15 to 5788f5ef07c2 (61 revisions) (#5179) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index ac353e2ed1d7..e543fb794641 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -5c6918933e15672e216675fc2e0e06bf8307a55a +5788f5ef07c202c252de7d8050e6ab8d4c7652bd From c489d243f2ce46fe562e87863af06b61ca4be712 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 6 Apr 2022 01:01:07 -0400 Subject: [PATCH 340/600] Roll Flutter from 5788f5ef07c2 to 08c46bcb58cf (1 revision) (#5180) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index e543fb794641..75cc696990b1 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -5788f5ef07c202c252de7d8050e6ab8d4c7652bd +08c46bcb58cf10cd101a351950e05eecaeb2b701 From 828a56a501bc8a72aed476e6e9fd47a9826a43d7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 6 Apr 2022 11:36:11 -0400 Subject: [PATCH 341/600] Roll Flutter from 08c46bcb58cf to 120b3deb182c (1 revision) (#5181) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 75cc696990b1..1998e56b9aff 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -08c46bcb58cf10cd101a351950e05eecaeb2b701 +120b3deb182c04cf09b509aea7952a74eb2d716d From 90fef1b9fefa1374cf0329fa14097e4807193131 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 6 Apr 2022 12:41:17 -0400 Subject: [PATCH 342/600] Roll Flutter from 120b3deb182c to 94fefaa49d86 (1 revision) (#5182) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 1998e56b9aff..9a17191a84be 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -120b3deb182c04cf09b509aea7952a74eb2d716d +94fefaa49d86fde856146c05d6817382a2fe7b3c From 17843017baa9b4967497cdc17c2095b0867fea6b Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 6 Apr 2022 13:46:11 -0400 Subject: [PATCH 343/600] Roll Flutter from 94fefaa49d86 to 676edd4c828f (6 revisions) (#5183) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 9a17191a84be..aec0c3b24f26 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -94fefaa49d86fde856146c05d6817382a2fe7b3c +676edd4c828f2c4d1fe69d610b3a0290be69ebd6 From eaef38109cc6a3c1272f0a861cfb0b7b756feb0a Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 6 Apr 2022 14:51:11 -0400 Subject: [PATCH 344/600] Roll Flutter from 676edd4c828f to f8e528a4ef80 (8 revisions) (#5185) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index aec0c3b24f26..259d5dd8ef87 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -676edd4c828f2c4d1fe69d610b3a0290be69ebd6 +f8e528a4ef8069fa964bf41b38c40c2da55b15a3 From af6b227474e79710c9242dafcc799956f1affbb9 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 6 Apr 2022 15:56:11 -0400 Subject: [PATCH 345/600] Roll Flutter from f8e528a4ef80 to e382f1376a9b (2 revisions) (#5186) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 259d5dd8ef87..2158e53edb2f 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -f8e528a4ef8069fa964bf41b38c40c2da55b15a3 +e382f1376a9b428f12036098c119d570e3f62651 From 00164cb3e5c82b1f0f0f64fd43edf9cb21d718d5 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 6 Apr 2022 17:01:20 -0400 Subject: [PATCH 346/600] Roll Flutter from e382f1376a9b to d08f7ab9c949 (3 revisions) (#5187) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 2158e53edb2f..ac2143591260 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -e382f1376a9b428f12036098c119d570e3f62651 +d08f7ab9c949425845074809f42086523c236330 From 8ca3aa40254b007fd9bb7e52eb84ce54763e67cf Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Wed, 6 Apr 2022 15:16:40 -0700 Subject: [PATCH 347/600] [webview_flutter_wkwebview] Added support for the rest of the creation params (#5130) --- .../lib/src/foundation/foundation.dart | 2 +- .../lib/src/ui_kit/ui_kit.dart | 26 +- .../lib/src/web_kit/web_kit.dart | 49 +++- .../lib/src/web_kit_webview_widget.dart | 154 ++++++++--- .../test/src/web_kit_webview_widget_test.dart | 246 +++++++++++++++++- .../web_kit_webview_widget_test.mocks.dart | 129 +++++---- 6 files changed, 506 insertions(+), 100 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart index 2d80ea20b636..466c03a6c8e9 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart @@ -156,7 +156,7 @@ class NSObject { } /// Informs the observing object when the value at the specified key path has changed. - set observeValue( + Future setObserveValue( void Function( String keyPath, NSObject object, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart index 9b7973e2ace5..08dd7541a9f4 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart @@ -5,12 +5,15 @@ import 'dart:async'; import 'dart:math'; +import 'package:flutter/painting.dart'; + +import '../foundation/foundation.dart'; import '../web_kit/web_kit.dart'; /// A view that allows the scrolling and zooming of its contained views. /// /// Wraps [UIScrollView](https://developer.apple.com/documentation/uikit/uiscrollview?language=objc). -class UIScrollView { +class UIScrollView extends UIView { /// Constructs a [UIScrollView] that is owned by [webView]. // TODO(bparrishMines): Remove ignore once constructor is implemented. // ignore: avoid_unused_constructor_parameters @@ -40,3 +43,24 @@ class UIScrollView { throw UnimplementedError(); } } + +/// Manages the content for a rectangular area on the screen. +/// +/// Wraps [UIView](https://developer.apple.com/documentation/uikit/uiview?language=objc). +class UIView extends NSObject { + /// The view’s background color. + /// + /// The default value is null, which results in a transparent background color. + /// + /// Sets [UIView.backgroundColor](https://developer.apple.com/documentation/uikit/uiview/1622591-backgroundcolor?language=objc). + Future setBackgroundColor(Color? color) { + throw UnimplementedError(); + } + + /// Determines whether the view is opaque. + /// + /// Sets [UIView.opaque](https://developer.apple.com/documentation/uikit/uiview?language=objc). + Future setOpaque(bool opaque) { + throw UnimplementedError(); + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart index fb714f8a05d5..fb315cc847fb 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart @@ -192,6 +192,29 @@ class WKScriptMessage { final Object? body; } +/// Encapsulates the standard behaviors to apply to websites. +/// +/// Wraps [WKPreferences](https://developer.apple.com/documentation/webkit/wkpreferences?language=objc). +class WKPreferences { + /// Constructs a [WKPreferences]. + WKPreferences(); + + // A WKPreferences that is owned by configuration. + WKPreferences._fromWebViewConfiguration( + // TODO(bparrishMines): Remove ignore once constructor is implemented. + // ignore: avoid_unused_constructor_parameters + WKWebViewConfiguration configuration, + ); + + // TODO(bparrishMines): Deprecated for iOS 14.0+. Add support for alternative. + /// Sets whether JavaScript is enabled. + /// + /// The default value is true. + Future setJavaScriptEnabled(bool enabled) { + throw UnimplementedError(); + } +} + /// Manages cookies, disk and memory caches, and other types of data for a web view. /// /// Wraps [WKWebsiteDataStore](https://developer.apple.com/documentation/webkit/wkwebsitedatastore?language=objc). @@ -245,7 +268,7 @@ class WKUserContentController { WKUserContentController(); // A WKUserContentController that is owned by configuration. - WKUserContentController._fromWebViewConfiguretion( + WKUserContentController._fromWebViewConfiguration( // TODO(bparrishMines): Remove ignore once constructor is implemented. // ignore: avoid_unused_constructor_parameters WKWebViewConfiguration configuration, @@ -304,25 +327,31 @@ class WKUserContentController { /// Wraps [WKWebViewConfiguration](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration?language=objc). class WKWebViewConfiguration { /// Constructs a [WKWebViewConfiguration]. - WKWebViewConfiguration({required this.userContentController}); + WKWebViewConfiguration(); // A WKWebViewConfiguration that is owned by webView. // TODO(bparrishMines): Remove ignore once constructor is implemented. // ignore: avoid_unused_constructor_parameters - WKWebViewConfiguration._fromWebView(WKWebView webView) { - userContentController = - WKUserContentController._fromWebViewConfiguretion(this); - } - - /// Coordinates interactions between your app’s code and the webpage’s scripts and other content. - late final WKUserContentController userContentController; + WKWebViewConfiguration._fromWebView(WKWebView webView); late WKWebsiteDataStore _websiteDataStore = WKWebsiteDataStore._fromWebViewConfiguration(this); + late final WKUserContentController _userContentController = + WKUserContentController._fromWebViewConfiguration(this); + + late final WKPreferences _preferences = + WKPreferences._fromWebViewConfiguration(this); + /// Used to get and set the site’s cookies and to track the cached data objects. WKWebsiteDataStore get webSiteDataStore => _websiteDataStore; + /// Coordinates interactions between your app’s code and the webpage’s scripts and other content. + WKUserContentController get userContentController => _userContentController; + + /// Manages the preference-related settings for the web view. + WKPreferences get preferences => _preferences; + /// Used to get and set the site’s cookies and to track the cached data objects. /// /// Sets [WKWebViewConfiguration.webSiteDataStore](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/1395661-websitedatastore?language=objc). @@ -429,7 +458,7 @@ class WKNavigationDelegate { /// Object that displays interactive web content, such as for an in-app browser. /// /// Wraps [WKWebView](https://developer.apple.com/documentation/webkit/wkwebview?language=objc). -class WKWebView extends NSObject { +class WKWebView extends UIView { /// Constructs a [WKWebView]. /// /// [configuration] contains the configuration details for the web view. This diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 67b05c1f053a..d7cbfb4510b2 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:math'; import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:path/path.dart' as path; @@ -86,23 +87,12 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { }) : super(callbacksHandler) { _setCreationParams( creationParams, - configuration: configuration ?? - WKWebViewConfiguration( - userContentController: WKUserContentController(), - ), + configuration: configuration ?? WKWebViewConfiguration(), ); - - webView.setUIDelegate(uiDelegate); - uiDelegate.setOnCreateWebView(( - WKWebViewConfiguration configuration, - WKNavigationAction navigationAction, - ) { - if (!navigationAction.targetFrame.isMainFrame) { - webView.loadRequest(navigationAction.request); - } - }); } + bool _zoomEnabled = true; + final Map _scriptMessageHandlers = {}; @@ -162,13 +152,37 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { webView = webViewProxy.createWebView(configuration); + webView.setUIDelegate(uiDelegate); + uiDelegate.setOnCreateWebView(( + WKWebViewConfiguration configuration, + WKNavigationAction navigationAction, + ) { + if (!navigationAction.targetFrame.isMainFrame) { + webView.loadRequest(navigationAction.request); + } + }); + await addJavascriptChannels(params.javascriptChannelNames); webView.setNavigationDelegate(navigationDelegate); + if (params.userAgent != null) { + webView.setCustomUserAgent(params.userAgent!); + } + if (params.webSettings != null) { updateSettings(params.webSettings!); } + + if (params.backgroundColor != null) { + webView.setOpaque(false); + webView.setBackgroundColor(Colors.transparent); + webView.scrollView.setBackgroundColor(params.backgroundColor!); + } + + if (params.initialUrl != null) { + await loadUrl(params.initialUrl!, null); + } } void _setWebViewConfiguration( @@ -343,12 +357,20 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { @override Future updateSettings(WebSettings setting) async { - if (setting.hasNavigationDelegate != null) { - _setHasNavigationDelegate(setting.hasNavigationDelegate!); - } - if (setting.hasProgressTracking != null) { - _setHasProgressTracking(setting.hasProgressTracking!); - } + await Future.wait(>[ + _setUserAgent(setting.userAgent), + if (setting.hasNavigationDelegate != null) + _setHasNavigationDelegate(setting.hasNavigationDelegate!), + if (setting.hasProgressTracking != null) + _setHasProgressTracking(setting.hasProgressTracking!), + if (setting.javascriptMode != null) + _setJavaScriptMode(setting.javascriptMode!), + if (setting.zoomEnabled != null) _setZoomEnabled(setting.zoomEnabled!), + if (setting.gestureNavigationEnabled != null) + webView.setAllowsBackForwardNavigationGestures( + setting.gestureNavigationEnabled!, + ), + ]); } @override @@ -400,24 +422,12 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { return; } - // WKWebView does not support removing a single user script, so this removes - // all user scripts and all message handlers and re-registers channels that - // shouldn't be removed. Note that this workaround could interfere with - // exposing support for custom scripts from applications. - webView.configuration.userContentController.removeAllUserScripts(); - webView.configuration.userContentController - .removeAllScriptMessageHandlers(); - - javascriptChannelNames.forEach(_scriptMessageHandlers.remove); - final Set remainingNames = _scriptMessageHandlers.keys.toSet(); - _scriptMessageHandlers.clear(); - - await addJavascriptChannels(remainingNames); + await _resetUserScripts(removedJavaScriptChannels: javascriptChannelNames); } - void _setHasNavigationDelegate(bool hasNavigationDelegate) { + Future _setHasNavigationDelegate(bool hasNavigationDelegate) { if (hasNavigationDelegate) { - navigationDelegate.setDecidePolicyForNavigationAction( + return navigationDelegate.setDecidePolicyForNavigationAction( (WKWebView webView, WKNavigationAction action) async { final bool allow = await callbacksHandler.onNavigationRequest( url: action.request.url, @@ -429,20 +439,20 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { : WKNavigationActionPolicy.cancel; }); } else { - navigationDelegate.setDecidePolicyForNavigationAction(null); + return navigationDelegate.setDecidePolicyForNavigationAction(null); } } Future _setHasProgressTracking(bool hasProgressTracking) { if (hasProgressTracking) { - webView.observeValue = ( + webView.setObserveValue(( String keyPath, NSObject object, Map change, ) { final double progress = change[NSKeyValueChangeKey.newValue]! as double; callbacksHandler.onProgress((progress * 100).round()); - }; + }); return webView.addObserver( webView, keyPath: 'estimatedProgress', @@ -451,11 +461,77 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { }, ); } else { - webView.observeValue = null; + webView.setObserveValue(null); return webView.removeObserver(webView, keyPath: 'estimatedProgress'); } } + Future _setJavaScriptMode(JavascriptMode mode) { + switch (mode) { + case JavascriptMode.disabled: + return webView.configuration.preferences.setJavaScriptEnabled(false); + case JavascriptMode.unrestricted: + return webView.configuration.preferences.setJavaScriptEnabled(true); + } + } + + Future _setUserAgent(WebSetting userAgent) async { + if (userAgent.isPresent) { + await webView.setCustomUserAgent(userAgent.value); + } + } + + Future _setZoomEnabled(bool zoomEnabled) async { + if (_zoomEnabled == zoomEnabled) { + return; + } + + _zoomEnabled = zoomEnabled; + if (!zoomEnabled) { + return _disableZoom(); + } + + return _resetUserScripts(); + } + + Future _disableZoom() { + const WKUserScript userScript = WKUserScript( + "var meta = document.createElement('meta');" + "meta.name = 'viewport';" + "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0," + "user-scalable=no';" + "var head = document.getElementsByTagName('head')[0];head.appendChild(meta);", + WKUserScriptInjectionTime.atDocumentEnd, + isMainFrameOnly: true, + ); + return webView.configuration.userContentController + .addUserScript(userScript); + } + + // WkWebView does not support removing a single user script, so all user + // scripts and all message handlers are removed instead. And the JavaScript + // channels that shouldn't be removed are re-registered. Note that this + // workaround could interfere with exposing support for custom scripts from + // applications. + Future _resetUserScripts({ + Set removedJavaScriptChannels = const {}, + }) async { + webView.configuration.userContentController.removeAllUserScripts(); + webView.configuration.userContentController + .removeAllScriptMessageHandlers(); + + removedJavaScriptChannels.forEach(_scriptMessageHandlers.remove); + final Set remainingNames = _scriptMessageHandlers.keys.toSet(); + _scriptMessageHandlers.clear(); + + await Future.wait(>[ + addJavascriptChannels(remainingNames), + // Zoom is disabled with a WKUserScript, so this adds it back if it was + // removed above. + if (!_zoomEnabled) _disableZoom(), + ]); + } + static WebResourceError _toWebResourceError(NSError error) { WebResourceErrorType? errorType; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index e8b29dccb4e4..5d36e593fadb 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:math'; import 'dart:typed_data'; +import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -22,6 +23,7 @@ import 'web_kit_webview_widget_test.mocks.dart'; @GenerateMocks([ UIScrollView, WKNavigationDelegate, + WKPreferences, WKScriptMessageHandler, WKWebView, WKWebViewConfiguration, @@ -39,6 +41,7 @@ void main() { late MockWKWebView mockWebView; late MockWebViewWidgetProxy mockWebViewWidgetProxy; late MockWKUserContentController mockUserContentController; + late MockWKPreferences mockPreferences; late MockWKWebViewConfiguration mockWebViewConfiguration; late MockWKUIDelegate mockUIDelegate; late MockUIScrollView mockScrollView; @@ -54,6 +57,7 @@ void main() { mockWebView = MockWKWebView(); mockWebViewConfiguration = MockWKWebViewConfiguration(); mockUserContentController = MockWKUserContentController(); + mockPreferences = MockWKPreferences(); mockUIDelegate = MockWKUIDelegate(); mockScrollView = MockUIScrollView(); mockWebsiteDataStore = MockWKWebsiteDataStore(); @@ -68,6 +72,7 @@ void main() { when(mockWebViewConfiguration.userContentController).thenReturn( mockUserContentController, ); + when(mockWebViewConfiguration.preferences).thenReturn(mockPreferences); when(mockWebView.scrollView).thenReturn(mockScrollView); @@ -131,6 +136,55 @@ void main() { }); group('$CreationParams', () { + testWidgets('initialUrl', (WidgetTester tester) async { + await buildWidget( + tester, + creationParams: CreationParams( + initialUrl: 'https://www.google.com', + webSettings: WebSettings( + userAgent: const WebSetting.absent(), + hasNavigationDelegate: false, + ), + ), + ); + final NSUrlRequest request = verify(mockWebView.loadRequest(captureAny)) + .captured + .single as NSUrlRequest; + expect(request.url, 'https://www.google.com'); + }); + + testWidgets('backgroundColor', (WidgetTester tester) async { + await buildWidget( + tester, + creationParams: CreationParams( + backgroundColor: Colors.red, + webSettings: WebSettings( + userAgent: const WebSetting.absent(), + hasNavigationDelegate: false, + ), + ), + ); + + verify(mockWebView.setOpaque(false)); + verify(mockWebView.setBackgroundColor(Colors.transparent)); + verify(mockScrollView.setBackgroundColor(Colors.red)); + }); + + testWidgets('userAgent', (WidgetTester tester) async { + await buildWidget( + tester, + creationParams: CreationParams( + userAgent: 'MyUserAgent', + webSettings: WebSettings( + userAgent: const WebSetting.absent(), + hasNavigationDelegate: false, + ), + ), + ); + + verify(mockWebView.setCustomUserAgent('MyUserAgent')); + }); + testWidgets('autoMediaPlaybackPolicy true', (WidgetTester tester) async { await buildWidget( tester, @@ -205,6 +259,158 @@ void main() { }); group('$WebSettings', () { + testWidgets('javascriptMode', (WidgetTester tester) async { + await buildWidget( + tester, + creationParams: CreationParams( + webSettings: WebSettings( + userAgent: const WebSetting.absent(), + javascriptMode: JavascriptMode.unrestricted, + hasNavigationDelegate: false, + ), + ), + ); + + verify(mockPreferences.setJavaScriptEnabled(true)); + }); + + testWidgets('hasNavigationDelegate', (WidgetTester tester) async { + await buildWidget( + tester, + creationParams: CreationParams( + webSettings: WebSettings( + userAgent: const WebSetting.absent(), + hasNavigationDelegate: true, + ), + ), + ); + + verify(mockNavigationDelegate + .setDecidePolicyForNavigationAction(argThat(isNotNull))); + }); + + testWidgets('userAgent', (WidgetTester tester) async { + await buildWidget( + tester, + creationParams: CreationParams( + webSettings: WebSettings( + userAgent: const WebSetting.of('myUserAgent'), + hasNavigationDelegate: false, + ), + ), + ); + + verify(mockWebView.setCustomUserAgent('myUserAgent')); + }); + + testWidgets( + 'enabling zoom re-adds JavaScript channels', + (WidgetTester tester) async { + when(mockWebViewWidgetProxy.createScriptMessageHandler()) + .thenReturn( + MockWKScriptMessageHandler(), + ); + + await buildWidget( + tester, + creationParams: CreationParams( + webSettings: WebSettings( + userAgent: const WebSetting.absent(), + zoomEnabled: false, + hasNavigationDelegate: false, + ), + javascriptChannelNames: {'myChannel'}, + ), + ); + + clearInteractions(mockUserContentController); + + await testController.updateSettings(WebSettings( + userAgent: const WebSetting.absent(), + zoomEnabled: true, + )); + + final List javaScriptChannels = verifyInOrder([ + mockUserContentController.removeAllUserScripts(), + mockUserContentController.removeAllScriptMessageHandlers(), + mockUserContentController.addScriptMessageHandler( + captureAny, + captureAny, + ), + ]).captured[2]; + + expect( + javaScriptChannels[0], + isA(), + ); + expect(javaScriptChannels[1], 'myChannel'); + }, + ); + + testWidgets( + 'enabling zoom removes script', + (WidgetTester tester) async { + when(mockWebViewWidgetProxy.createScriptMessageHandler()) + .thenReturn( + MockWKScriptMessageHandler(), + ); + + await buildWidget( + tester, + creationParams: CreationParams( + webSettings: WebSettings( + userAgent: const WebSetting.absent(), + zoomEnabled: false, + hasNavigationDelegate: false, + ), + ), + ); + + clearInteractions(mockUserContentController); + + await testController.updateSettings(WebSettings( + userAgent: const WebSetting.absent(), + zoomEnabled: true, + )); + + verify(mockUserContentController.removeAllUserScripts()); + verify(mockUserContentController.removeAllScriptMessageHandlers()); + verifyNever(mockUserContentController.addScriptMessageHandler( + any, + any, + )); + }, + ); + + testWidgets('zoomEnabled is false', (WidgetTester tester) async { + await buildWidget( + tester, + creationParams: CreationParams( + webSettings: WebSettings( + userAgent: const WebSetting.absent(), + zoomEnabled: false, + hasNavigationDelegate: false, + ), + ), + ); + + final WKUserScript zoomScript = + verify(mockUserContentController.addUserScript(captureAny)) + .captured + .first as WKUserScript; + expect(zoomScript.isMainFrameOnly, isTrue); + expect(zoomScript.injectionTime, + WKUserScriptInjectionTime.atDocumentEnd); + expect( + zoomScript.source, + "var meta = document.createElement('meta');" + "meta.name = 'viewport';" + "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0," + "user-scalable=no';" + "var head = document.getElementsByTagName('head')[0];head.appendChild(meta);", + ); + }); + testWidgets('allowsInlineMediaPlayback', (WidgetTester tester) async { await buildWidget( tester, @@ -653,6 +859,44 @@ void main() { ); expect(userScripts[0].isMainFrameOnly, false); }); + + testWidgets('removeJavascriptChannels with zoom disabled', + (WidgetTester tester) async { + when(mockWebViewWidgetProxy.createScriptMessageHandler()).thenReturn( + MockWKScriptMessageHandler(), + ); + + await buildWidget( + tester, + creationParams: CreationParams( + webSettings: WebSettings( + userAgent: const WebSetting.absent(), + zoomEnabled: false, + hasNavigationDelegate: false, + ), + ), + ); + + await testController.addJavascriptChannels({'c'}); + clearInteractions(mockUserContentController); + await testController.removeJavascriptChannels({'c'}); + + final WKUserScript zoomScript = + verify(mockUserContentController.addUserScript(captureAny)) + .captured + .first as WKUserScript; + expect(zoomScript.isMainFrameOnly, isTrue); + expect( + zoomScript.injectionTime, WKUserScriptInjectionTime.atDocumentEnd); + expect( + zoomScript.source, + "var meta = document.createElement('meta');" + "meta.name = 'viewport';" + "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0," + "user-scalable=no';" + "var head = document.getElementsByTagName('head')[0];head.appendChild(meta);", + ); + }); }); group('$WebViewPlatformCallbacksHandler', () { @@ -803,7 +1047,7 @@ void main() { testWidgets('onProgress', (WidgetTester tester) async { await buildWidget(tester, hasProgressTracking: true); final dynamic observeValue = - verify(mockWebView.observeValue = captureAny).captured.single + verify(mockWebView.setObserveValue(captureAny)).captured.single as void Function( String keyPath, NSObject object, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index 36b8af441e2e..8c8758d5139c 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -1,22 +1,24 @@ // Mocks generated by Mockito 5.1.0 from annotations -// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. +// in webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. // Do not manually edit this file. import 'dart:async' as _i5; import 'dart:math' as _i2; +import 'dart:ui' as _i6; import 'package:mockito/mockito.dart' as _i1; import 'package:webview_flutter_platform_interface/src/types/javascript_channel.dart' - as _i8; -import 'package:webview_flutter_platform_interface/src/types/types.dart' as _i9; + as _i9; +import 'package:webview_flutter_platform_interface/src/types/types.dart' + as _i10; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart' - as _i7; + as _i8; import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart' - as _i6; + as _i7; import 'package:webview_flutter_wkwebview/src/ui_kit/ui_kit.dart' as _i4; import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart' as _i3; import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart' - as _i10; + as _i11; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -35,20 +37,22 @@ class _FakeWKWebViewConfiguration_1 extends _i1.Fake class _FakeUIScrollView_2 extends _i1.Fake implements _i4.UIScrollView {} -class _FakeWKUserContentController_3 extends _i1.Fake +class _FakeWKWebsiteDataStore_3 extends _i1.Fake + implements _i3.WKWebsiteDataStore {} + +class _FakeWKUserContentController_4 extends _i1.Fake implements _i3.WKUserContentController {} -class _FakeWKWebsiteDataStore_4 extends _i1.Fake - implements _i3.WKWebsiteDataStore {} +class _FakeWKPreferences_5 extends _i1.Fake implements _i3.WKPreferences {} -class _FakeWKWebView_5 extends _i1.Fake implements _i3.WKWebView {} +class _FakeWKWebView_6 extends _i1.Fake implements _i3.WKWebView {} -class _FakeWKScriptMessageHandler_6 extends _i1.Fake +class _FakeWKScriptMessageHandler_7 extends _i1.Fake implements _i3.WKScriptMessageHandler {} -class _FakeWKUIDelegate_7 extends _i1.Fake implements _i3.WKUIDelegate {} +class _FakeWKUIDelegate_8 extends _i1.Fake implements _i3.WKUIDelegate {} -class _FakeWKNavigationDelegate_8 extends _i1.Fake +class _FakeWKNavigationDelegate_9 extends _i1.Fake implements _i3.WKNavigationDelegate {} /// A class which mocks [UIScrollView]. @@ -65,6 +69,11 @@ class MockUIScrollView extends _i1.Mock implements _i4.UIScrollView { returnValue: Future<_i2.Point>.value(_FakePoint_0())) as _i5.Future<_i2.Point>); @override + _i5.Future setBackgroundColor(_i6.Color? color) => + (super.noSuchMethod(Invocation.method(#setBackgroundColor, [color]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override _i5.Future scrollBy(_i2.Point? offset) => (super.noSuchMethod(Invocation.method(#scrollBy, [offset]), returnValue: Future.value(), @@ -113,14 +122,14 @@ class MockWKNavigationDelegate extends _i1.Mock returnValueForMissingStub: Future.value()) as _i5.Future); @override _i5.Future setDidFailNavigation( - void Function(_i3.WKWebView, _i6.NSError)? didFailNavigation) => + void Function(_i3.WKWebView, _i7.NSError)? didFailNavigation) => (super.noSuchMethod( Invocation.method(#setDidFailNavigation, [didFailNavigation]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i5.Future); @override _i5.Future setDidFailProvisionalNavigation( - void Function(_i3.WKWebView, _i6.NSError)? + void Function(_i3.WKWebView, _i7.NSError)? didFailProvisionalNavigation) => (super.noSuchMethod( Invocation.method( @@ -137,6 +146,21 @@ class MockWKNavigationDelegate extends _i1.Mock returnValueForMissingStub: Future.value()) as _i5.Future); } +/// A class which mocks [WKPreferences]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockWKPreferences extends _i1.Mock implements _i3.WKPreferences { + MockWKPreferences() { + _i1.throwOnMissingStub(this); + } + + @override + _i5.Future setJavaScriptEnabled(bool? enabled) => + (super.noSuchMethod(Invocation.method(#setJavaScriptEnabled, [enabled]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); +} + /// A class which mocks [WKScriptMessageHandler]. /// /// See the documentation for Mockito's code generation for more information. @@ -175,13 +199,6 @@ class MockWKWebView extends _i1.Mock implements _i3.WKWebView { (super.noSuchMethod(Invocation.getter(#scrollView), returnValue: _FakeUIScrollView_2()) as _i4.UIScrollView); @override - set observeValue( - void Function( - String, _i6.NSObject, Map<_i6.NSKeyValueChangeKey, Object?>)? - observeValue) => - super.noSuchMethod(Invocation.setter(#observeValue, observeValue), - returnValueForMissingStub: null); - @override _i5.Future setUIDelegate(_i3.WKUIDelegate? delegate) => (super.noSuchMethod(Invocation.method(#setUIDelegate, [delegate]), returnValue: Future.value(), @@ -200,7 +217,7 @@ class MockWKWebView extends _i1.Mock implements _i3.WKWebView { (super.noSuchMethod(Invocation.method(#getEstimatedProgress, []), returnValue: Future.value(0.0)) as _i5.Future); @override - _i5.Future loadRequest(_i6.NSUrlRequest? request) => + _i5.Future loadRequest(_i7.NSUrlRequest? request) => (super.noSuchMethod(Invocation.method(#loadRequest, [request]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i5.Future); @@ -265,19 +282,37 @@ class MockWKWebView extends _i1.Mock implements _i3.WKWebView { .noSuchMethod(Invocation.method(#evaluateJavaScript, [javaScriptString]), returnValue: Future.value()) as _i5.Future); @override - _i5.Future addObserver(_i6.NSObject? observer, - {String? keyPath, Set<_i6.NSKeyValueObservingOptions>? options}) => + _i5.Future setBackgroundColor(_i6.Color? color) => + (super.noSuchMethod(Invocation.method(#setBackgroundColor, [color]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future setOpaque(bool? opaque) => + (super.noSuchMethod(Invocation.method(#setOpaque, [opaque]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future addObserver(_i7.NSObject? observer, + {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) => (super.noSuchMethod( Invocation.method( #addObserver, [observer], {#keyPath: keyPath, #options: options}), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i5.Future removeObserver(_i6.NSObject? observer, {String? keyPath}) => + _i5.Future removeObserver(_i7.NSObject? observer, {String? keyPath}) => (super.noSuchMethod( Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future setObserveValue( + void Function( + String, _i7.NSObject, Map<_i7.NSKeyValueChangeKey, Object?>)? + observeValue) => + (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [WKWebViewConfiguration]. @@ -289,21 +324,19 @@ class MockWKWebViewConfiguration extends _i1.Mock _i1.throwOnMissingStub(this); } + @override + _i3.WKWebsiteDataStore get webSiteDataStore => + (super.noSuchMethod(Invocation.getter(#webSiteDataStore), + returnValue: _FakeWKWebsiteDataStore_3()) as _i3.WKWebsiteDataStore); @override _i3.WKUserContentController get userContentController => (super.noSuchMethod(Invocation.getter(#userContentController), - returnValue: _FakeWKUserContentController_3()) + returnValue: _FakeWKUserContentController_4()) as _i3.WKUserContentController); @override - set userContentController( - _i3.WKUserContentController? _userContentController) => - super.noSuchMethod( - Invocation.setter(#userContentController, _userContentController), - returnValueForMissingStub: null); - @override - _i3.WKWebsiteDataStore get webSiteDataStore => - (super.noSuchMethod(Invocation.getter(#webSiteDataStore), - returnValue: _FakeWKWebsiteDataStore_4()) as _i3.WKWebsiteDataStore); + _i3.WKPreferences get preferences => + (super.noSuchMethod(Invocation.getter(#preferences), + returnValue: _FakeWKPreferences_5()) as _i3.WKPreferences); @override _i5.Future setWebSiteDataStore( _i3.WKWebsiteDataStore? websiteDataStore) => @@ -404,23 +437,23 @@ class MockWKUserContentController extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockJavascriptChannelRegistry extends _i1.Mock - implements _i7.JavascriptChannelRegistry { + implements _i8.JavascriptChannelRegistry { MockJavascriptChannelRegistry() { _i1.throwOnMissingStub(this); } @override - Map get channels => + Map get channels => (super.noSuchMethod(Invocation.getter(#channels), - returnValue: {}) - as Map); + returnValue: {}) + as Map); @override void onJavascriptChannelMessage(String? channel, String? message) => super.noSuchMethod( Invocation.method(#onJavascriptChannelMessage, [channel, message]), returnValueForMissingStub: null); @override - void updateJavascriptChannelsFromSet(Set<_i8.JavascriptChannel>? channels) => + void updateJavascriptChannelsFromSet(Set<_i9.JavascriptChannel>? channels) => super.noSuchMethod( Invocation.method(#updateJavascriptChannelsFromSet, [channels]), returnValueForMissingStub: null); @@ -430,7 +463,7 @@ class MockJavascriptChannelRegistry extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockWebViewPlatformCallbacksHandler extends _i1.Mock - implements _i7.WebViewPlatformCallbacksHandler { + implements _i8.WebViewPlatformCallbacksHandler { MockWebViewPlatformCallbacksHandler() { _i1.throwOnMissingStub(this); } @@ -454,7 +487,7 @@ class MockWebViewPlatformCallbacksHandler extends _i1.Mock super.noSuchMethod(Invocation.method(#onProgress, [progress]), returnValueForMissingStub: null); @override - void onWebResourceError(_i9.WebResourceError? error) => + void onWebResourceError(_i10.WebResourceError? error) => super.noSuchMethod(Invocation.method(#onWebResourceError, [error]), returnValueForMissingStub: null); } @@ -463,7 +496,7 @@ class MockWebViewPlatformCallbacksHandler extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockWebViewWidgetProxy extends _i1.Mock - implements _i10.WebViewWidgetProxy { + implements _i11.WebViewWidgetProxy { MockWebViewWidgetProxy() { _i1.throwOnMissingStub(this); } @@ -471,18 +504,18 @@ class MockWebViewWidgetProxy extends _i1.Mock @override _i3.WKWebView createWebView(_i3.WKWebViewConfiguration? configuration) => (super.noSuchMethod(Invocation.method(#createWebView, [configuration]), - returnValue: _FakeWKWebView_5()) as _i3.WKWebView); + returnValue: _FakeWKWebView_6()) as _i3.WKWebView); @override _i3.WKScriptMessageHandler createScriptMessageHandler() => (super.noSuchMethod(Invocation.method(#createScriptMessageHandler, []), - returnValue: _FakeWKScriptMessageHandler_6()) + returnValue: _FakeWKScriptMessageHandler_7()) as _i3.WKScriptMessageHandler); @override _i3.WKUIDelegate createUIDelgate() => (super.noSuchMethod(Invocation.method(#createUIDelgate, []), - returnValue: _FakeWKUIDelegate_7()) as _i3.WKUIDelegate); + returnValue: _FakeWKUIDelegate_8()) as _i3.WKUIDelegate); @override _i3.WKNavigationDelegate createNavigationDelegate() => (super.noSuchMethod( Invocation.method(#createNavigationDelegate, []), - returnValue: _FakeWKNavigationDelegate_8()) as _i3.WKNavigationDelegate); + returnValue: _FakeWKNavigationDelegate_9()) as _i3.WKNavigationDelegate); } From b5a1d7ecf74d6587caf305658dbda668e5027458 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 7 Apr 2022 08:51:08 -0400 Subject: [PATCH 348/600] Roll Flutter from d08f7ab9c949 to 2999e8531569 (6 revisions) (#5190) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index ac2143591260..7f64f4658e1e 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -d08f7ab9c949425845074809f42086523c236330 +2999e8531569bf57c1eb8c771e503adaeeffea8a From 9e874339eab4a985302e783599bed5b53e4fb0c6 Mon Sep 17 00:00:00 2001 From: Viren Khatri <44755140+werainkhatri@users.noreply.github.com> Date: Thu, 7 Apr 2022 19:56:12 +0530 Subject: [PATCH 349/600] migrate from ui.hash* to Object.hash* (#4994) --- packages/camera/camera_web/CHANGELOG.md | 3 ++- .../camera_web/lib/src/types/camera_metadata.dart | 4 +--- .../camera_web/lib/src/types/camera_options.dart | 10 ++++------ .../lib/src/types/zoom_level_capability.dart | 3 +-- packages/camera/camera_web/pubspec.yaml | 2 +- .../CHANGELOG.md | 5 +++++ .../lib/src/types/camera.dart | 4 ++-- .../lib/src/types/location.dart | 6 ++---- .../lib/src/types/maps_object_updates.dart | 6 ++---- .../lib/src/types/marker.dart | 4 ++-- .../lib/src/types/screen_coordinate.dart | 4 +--- .../lib/src/types/tile_overlay.dart | 4 +--- .../lib/src/types/ui.dart | 4 +--- .../pubspec.yaml | 4 ++-- .../test/types/maps_object_updates_test.dart | 11 ++++------- .../test/types/test_maps_object.dart | 5 +---- .../test/types/tile_overlay_test.dart | 4 +--- .../test/types/tile_overlay_updates_test.dart | 10 ++++------ packages/google_sign_in/google_sign_in/CHANGELOG.md | 3 ++- .../google_sign_in/lib/google_sign_in.dart | 3 +-- packages/google_sign_in/google_sign_in/pubspec.yaml | 2 +- .../in_app_purchase_android/CHANGELOG.md | 5 +++++ .../src/billing_client_wrappers/purchase_wrapper.dart | 10 ++++------ .../billing_client_wrappers/sku_details_wrapper.dart | 8 +++----- .../in_app_purchase_android/pubspec.yaml | 4 ++-- .../in_app_purchase_storekit/CHANGELOG.md | 4 ++++ .../store_kit_wrappers/sk_payment_queue_wrapper.dart | 7 +++---- .../sk_payment_transaction_wrappers.dart | 6 ++---- .../src/store_kit_wrappers/sk_product_wrapper.dart | 11 +++++------ .../src/store_kit_wrappers/sk_storefront_wrapper.dart | 4 +--- .../in_app_purchase_storekit/pubspec.yaml | 2 +- packages/ios_platform_images/CHANGELOG.md | 3 ++- .../ios_platform_images/lib/ios_platform_images.dart | 2 +- packages/ios_platform_images/pubspec.yaml | 2 +- .../webview_flutter_platform_interface/CHANGELOG.md | 5 +++++ .../lib/src/types/web_settings.dart | 2 +- .../webview_flutter_platform_interface/pubspec.yaml | 4 ++-- 37 files changed, 83 insertions(+), 97 deletions(-) diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index 9e486667c103..852e4a03fd43 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.2.1+4 +* Migrates from `ui.hash*` to `Object.hash*`. * Updates minimum Flutter version for changes in 0.2.1+3. ## 0.2.1+3 diff --git a/packages/camera/camera_web/lib/src/types/camera_metadata.dart b/packages/camera/camera_web/lib/src/types/camera_metadata.dart index c42dd3ad8b75..e5c6b3875b6a 100644 --- a/packages/camera/camera_web/lib/src/types/camera_metadata.dart +++ b/packages/camera/camera_web/lib/src/types/camera_metadata.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues; - import 'package:flutter/foundation.dart'; /// Metadata used along the camera description @@ -38,5 +36,5 @@ class CameraMetadata { } @override - int get hashCode => hashValues(deviceId.hashCode, facingMode.hashCode); + int get hashCode => Object.hash(deviceId.hashCode, facingMode.hashCode); } diff --git a/packages/camera/camera_web/lib/src/types/camera_options.dart b/packages/camera/camera_web/lib/src/types/camera_options.dart index 8fa40bdc1bb8..08491b56081b 100644 --- a/packages/camera/camera_web/lib/src/types/camera_options.dart +++ b/packages/camera/camera_web/lib/src/types/camera_options.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues; - import 'package:flutter/foundation.dart'; /// Options used to create a camera with the given @@ -50,7 +48,7 @@ class CameraOptions { } @override - int get hashCode => hashValues(audio, video); + int get hashCode => Object.hash(audio, video); } /// Indicates whether the audio track is requested. @@ -140,7 +138,7 @@ class VideoConstraints { } @override - int get hashCode => hashValues(facingMode, width, height, deviceId); + int get hashCode => Object.hash(facingMode, width, height, deviceId); } /// The camera type used in [FacingModeConstraint]. @@ -213,7 +211,7 @@ class FacingModeConstraint { } @override - int get hashCode => hashValues(ideal, exact); + int get hashCode => Object.hash(ideal, exact); } /// The size of the requested video track used in @@ -272,5 +270,5 @@ class VideoSizeConstraint { } @override - int get hashCode => hashValues(minimum, ideal, maximum); + int get hashCode => Object.hash(minimum, ideal, maximum); } diff --git a/packages/camera/camera_web/lib/src/types/zoom_level_capability.dart b/packages/camera/camera_web/lib/src/types/zoom_level_capability.dart index 9a868d2bc0dc..d20bd25108bb 100644 --- a/packages/camera/camera_web/lib/src/types/zoom_level_capability.dart +++ b/packages/camera/camera_web/lib/src/types/zoom_level_capability.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:html' as html; -import 'dart:ui' show hashValues; import 'package:flutter/foundation.dart'; @@ -46,5 +45,5 @@ class ZoomLevelCapability { } @override - int get hashCode => hashValues(minimum, maximum, videoTrack); + int get hashCode => Object.hash(minimum, maximum, videoTrack); } diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index cb6aa19c49f1..2d1a4508eb73 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_web description: A Flutter plugin for getting information about and controlling the camera on Web. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.2.1+3 +version: 0.2.1+4 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md index d1b4ef8c18cf..b955bc033210 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.1.6 + +* Migrates from `ui.hash*` to `Object.hash*`. +* Updates minimum Flutter version to 2.5.0. + ## 2.1.5 Removes dependency on `meta`. diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/camera.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/camera.dart index 7cb6369e7f59..89006eba6214 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/camera.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/camera.dart @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues, Offset; +import 'dart:ui' show Offset; import 'types.dart'; @@ -99,7 +99,7 @@ class CameraPosition { } @override - int get hashCode => hashValues(bearing, target, tilt, zoom); + int get hashCode => Object.hash(bearing, target, tilt, zoom); @override String toString() => diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/location.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/location.dart index e2ca635be75e..5bc4ca608f1a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/location.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/location.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues; - import 'package:flutter/foundation.dart' show visibleForTesting; /// A pair of latitude and longitude coordinates, stored as degrees. @@ -55,7 +53,7 @@ class LatLng { } @override - int get hashCode => hashValues(latitude, longitude); + int get hashCode => Object.hash(latitude, longitude); } /// A latitude/longitude aligned rectangle. @@ -132,5 +130,5 @@ class LatLngBounds { } @override - int get hashCode => hashValues(southwest, northeast); + int get hashCode => Object.hash(southwest, northeast); } diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/maps_object_updates.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/maps_object_updates.dart index 2e2eefa3d32e..3267cad8d8a2 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/maps_object_updates.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/maps_object_updates.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues, hashList; - import 'package:flutter/foundation.dart' show objectRuntimeType, setEquals; import 'maps_object.dart'; @@ -114,8 +112,8 @@ class MapsObjectUpdates { } @override - int get hashCode => hashValues(hashList(_objectsToAdd), - hashList(_objectIdsToRemove), hashList(_objectsToChange)); + int get hashCode => Object.hash(Object.hashAll(_objectsToAdd), + Object.hashAll(_objectIdsToRemove), Object.hashAll(_objectsToChange)); @override String toString() { diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/marker.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/marker.dart index 5e62f7d9ffba..8057d2962e9e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/marker.dart @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues, Offset; +import 'dart:ui' show Offset; import 'package:flutter/foundation.dart' show immutable, ValueChanged, VoidCallback; @@ -90,7 +90,7 @@ class InfoWindow { } @override - int get hashCode => hashValues(title.hashCode, snippet, anchor); + int get hashCode => Object.hash(title.hashCode, snippet, anchor); @override String toString() { diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/screen_coordinate.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/screen_coordinate.dart index 46bc6c12e509..b424aa5c00e4 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/screen_coordinate.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/screen_coordinate.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues; - import 'package:flutter/foundation.dart' show immutable; /// Represents a point coordinate in the [GoogleMap]'s view. @@ -42,5 +40,5 @@ class ScreenCoordinate { } @override - int get hashCode => hashValues(x, y); + int get hashCode => Object.hash(x, y); } diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/tile_overlay.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/tile_overlay.dart index db2b53d63512..80b05272e21d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/tile_overlay.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/tile_overlay.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues; - import 'package:flutter/foundation.dart' show immutable; import 'types.dart'; @@ -146,6 +144,6 @@ class TileOverlay implements MapsObject { } @override - int get hashCode => hashValues(tileOverlayId, fadeIn, tileProvider, + int get hashCode => Object.hash(tileOverlayId, fadeIn, tileProvider, transparency, zIndex, visible, tileSize); } diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/ui.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/ui.dart index 38c34fcfd27f..18f88b910b48 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/ui.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/ui.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues; - import 'types.dart'; /// Type of map tiles to display. @@ -97,7 +95,7 @@ class MinMaxZoomPreference { } @override - int get hashCode => hashValues(minZoom, maxZoom); + int get hashCode => Object.hash(minZoom, maxZoom); @override String toString() { diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml index 6125dd43d9f6..8df31fcf626b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml @@ -4,11 +4,11 @@ repository: https://github.com/flutter/plugins/tree/main/packages/google_maps_fl issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.1.5 +version: 2.1.6 environment: sdk: '>=2.12.0 <3.0.0' - flutter: ">=2.0.0" + flutter: ">=2.5.0" dependencies: collection: ^1.15.0 diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/maps_object_updates_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/maps_object_updates_test.dart index f09f70fd769e..73ed1d9d1a90 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/maps_object_updates_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/maps_object_updates_test.dart @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues, hashList; - -import 'package:flutter/rendering.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_flutter_platform_interface/src/types/maps_object.dart'; import 'package:google_maps_flutter_platform_interface/src/types/maps_object_updates.dart'; @@ -129,10 +126,10 @@ void main() { TestMapsObjectUpdate.from(previous, current); expect( updates.hashCode, - hashValues( - hashList(updates.objectsToAdd), - hashList(updates.objectIdsToRemove), - hashList(updates.objectsToChange))); + Object.hash( + Object.hashAll(updates.objectsToAdd), + Object.hashAll(updates.objectIdsToRemove), + Object.hashAll(updates.objectsToChange))); }); test('toString', () async { diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/test_maps_object.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/test_maps_object.dart index b95ae50a8f08..e28da7ab79ad 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/test_maps_object.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/test_maps_object.dart @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues; - -import 'package:flutter/rendering.dart'; import 'package:google_maps_flutter_platform_interface/src/types/maps_object.dart'; import 'package:google_maps_flutter_platform_interface/src/types/maps_object_updates.dart'; @@ -37,7 +34,7 @@ class TestMapsObject implements MapsObject { } @override - int get hashCode => hashValues(mapsId, data); + int get hashCode => Object.hash(mapsId, data); } class TestMapsObjectUpdate extends MapsObjectUpdates { diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_overlay_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_overlay_test.dart index 3a4c34764ef7..c3ccf695032b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_overlay_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_overlay_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues; - import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; @@ -130,7 +128,7 @@ void main() { tileSize: 128); expect( tileOverlay.hashCode, - hashValues( + Object.hash( tileOverlay.tileOverlayId, tileOverlay.fadeIn, tileOverlay.tileProvider, diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_overlay_updates_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_overlay_updates_test.dart index 05be14e1ba0b..fbb345c50563 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_overlay_updates_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_overlay_updates_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues, hashList; - import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_flutter_platform_interface/src/types/tile_overlay.dart'; import 'package:google_maps_flutter_platform_interface/src/types/tile_overlay_updates.dart'; @@ -98,10 +96,10 @@ void main() { TileOverlayUpdates.from(previous, current); expect( updates.hashCode, - hashValues( - hashList(updates.tileOverlaysToAdd), - hashList(updates.tileOverlayIdsToRemove), - hashList(updates.tileOverlaysToChange))); + Object.hash( + Object.hashAll(updates.tileOverlaysToAdd), + Object.hashAll(updates.tileOverlayIdsToRemove), + Object.hashAll(updates.tileOverlaysToChange))); }); test('toString', () async { diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index c0a377603b34..24b729c4d78a 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 5.2.5 +* Migrates from `ui.hash*` to `Object.hash*`. * Adds OS version support information to README. ## 5.2.4 diff --git a/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart b/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart index 6afd409807fa..228f34b651c5 100644 --- a/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart +++ b/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:ui' show hashValues; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart' show PlatformException; @@ -148,7 +147,7 @@ class GoogleSignInAccount implements GoogleIdentity { @override int get hashCode => - hashValues(displayName, email, id, photoUrl, _idToken, serverAuthCode); + Object.hash(displayName, email, id, photoUrl, _idToken, serverAuthCode); @override String toString() { diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index de15d0bb0740..2c5de81f8e98 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Google Sign-In, a secure authentication system for signing in with a Google account on Android and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.2.4 +version: 5.2.5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md index a871cfafb4a2..626dba704ad7 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.2.2+3 + +* Migrates from `ui.hash*` to `Object.hash*`. +* Updates minimum Flutter version to 2.5.0. + ## 0.2.2+2 * Internal code cleanup for stricter analysis options. diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/purchase_wrapper.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/purchase_wrapper.dart index 653e5147f9b0..efaf07984df7 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/purchase_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/purchase_wrapper.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues; - import 'package:flutter/foundation.dart'; import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -71,7 +69,7 @@ class PurchaseWrapper { } @override - int get hashCode => hashValues( + int get hashCode => Object.hash( orderId, packageName, purchaseTime, @@ -238,7 +236,7 @@ class PurchaseHistoryRecordWrapper { } @override - int get hashCode => hashValues(purchaseTime, purchaseToken, signature, sku, + int get hashCode => Object.hash(purchaseTime, purchaseToken, signature, sku, originalJson, developerPayload); } @@ -278,7 +276,7 @@ class PurchasesResultWrapper { } @override - int get hashCode => hashValues(billingResult, responseCode, purchasesList); + int get hashCode => Object.hash(billingResult, responseCode, purchasesList); /// The detailed description of the status of the operation. final BillingResultWrapper billingResult; @@ -326,7 +324,7 @@ class PurchasesHistoryResult { } @override - int get hashCode => hashValues(billingResult, purchaseHistoryRecordList); + int get hashCode => Object.hash(billingResult, purchaseHistoryRecordList); /// The detailed description of the status of the [BillingClient.queryPurchaseHistory]. final BillingResultWrapper billingResult; diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/sku_details_wrapper.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/sku_details_wrapper.dart index 53595c572901..07f9d8f29abf 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/sku_details_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/sku_details_wrapper.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues; - import 'package:flutter/foundation.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -161,7 +159,7 @@ class SkuDetailsWrapper { @override int get hashCode { - return hashValues( + return Object.hash( description.hashCode, freeTrialPeriod.hashCode, introductoryPrice.hashCode, @@ -216,7 +214,7 @@ class SkuDetailsResponseWrapper { } @override - int get hashCode => hashValues(billingResult, skuDetailsList); + int get hashCode => Object.hash(billingResult, skuDetailsList); } /// Params containing the response code and the debug message from the Play Billing API response. @@ -261,5 +259,5 @@ class BillingResultWrapper { } @override - int get hashCode => hashValues(responseCode, debugMessage); + int get hashCode => Object.hash(responseCode, debugMessage); } diff --git a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml index 0a667c672945..62888e6dfb73 100644 --- a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml @@ -2,11 +2,11 @@ name: in_app_purchase_android description: An implementation for the Android platform of the Flutter `in_app_purchase` plugin. This uses the Android BillingClient APIs. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.2.2+2 +version: 0.2.2+3 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.5.0" flutter: plugin: diff --git a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md index d614baaf2305..4af33f177799 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.0+5 + +* Migrates from `ui.hash*` to `Object.hash*`. + ## 0.3.0+4 * Ensures that `NSError` instances with an unexpected value for the `userInfo` field don't crash the app, but send an explanatory message instead. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart index 022848281327..819c291a1441 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:ui' show hashValues; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; @@ -365,7 +364,7 @@ class SKError { } @override - int get hashCode => hashValues( + int get hashCode => Object.hash( code, domain, userInfo, @@ -482,7 +481,7 @@ class SKPaymentWrapper { } @override - int get hashCode => hashValues(productIdentifier, applicationUsername, + int get hashCode => Object.hash(productIdentifier, applicationUsername, quantity, simulatesAskToBuyInSandbox, requestData); @override @@ -585,5 +584,5 @@ class SKPaymentDiscountWrapper { @override int get hashCode => - hashValues(identifier, keyIdentifier, nonce, signature, timestamp); + Object.hash(identifier, keyIdentifier, nonce, signature, timestamp); } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_transaction_wrappers.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_transaction_wrappers.dart index 4c4c91257d9d..3894721a1f80 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_transaction_wrappers.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_transaction_wrappers.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues; - import 'package:flutter/foundation.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -190,8 +188,8 @@ class SKPaymentTransactionWrapper { } @override - int get hashCode => hashValues(payment, transactionState, originalTransaction, - transactionTimeStamp, transactionIdentifier, error); + int get hashCode => Object.hash(payment, transactionState, + originalTransaction, transactionTimeStamp, transactionIdentifier, error); @override String toString() => _$SKPaymentTransactionWrapperToJson(this).toString(); diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart index 105d999d8a69..2354563261fc 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues; import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -64,7 +63,7 @@ class SkProductResponseWrapper { } @override - int get hashCode => hashValues(products, invalidProductIdentifiers); + int get hashCode => Object.hash(products, invalidProductIdentifiers); } /// Dart wrapper around StoreKit's [SKProductPeriodUnit](https://developer.apple.com/documentation/storekit/skproductperiodunit?language=objc). @@ -142,7 +141,7 @@ class SKProductSubscriptionPeriodWrapper { } @override - int get hashCode => hashValues(numberOfUnits, unit); + int get hashCode => Object.hash(numberOfUnits, unit); } /// Dart wrapper around StoreKit's [SKProductDiscountPaymentMode](https://developer.apple.com/documentation/storekit/skproductdiscountpaymentmode?language=objc). @@ -232,7 +231,7 @@ class SKProductDiscountWrapper { } @override - int get hashCode => hashValues( + int get hashCode => Object.hash( price, priceLocale, numberOfPeriods, paymentMode, subscriptionPeriod); } @@ -342,7 +341,7 @@ class SKProductWrapper { } @override - int get hashCode => hashValues( + int get hashCode => Object.hash( productIdentifier, localizedTitle, localizedDescription, @@ -410,5 +409,5 @@ class SKPriceLocaleWrapper { } @override - int get hashCode => hashValues(currencySymbol, currencyCode); + int get hashCode => Object.hash(currencySymbol, currencyCode); } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_storefront_wrapper.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_storefront_wrapper.dart index 0bf1103a5abd..ff9e9b7db746 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_storefront_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_storefront_wrapper.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues; - import 'package:flutter/foundation.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -56,7 +54,7 @@ class SKStorefrontWrapper { } @override - int get hashCode => hashValues( + int get hashCode => Object.hash( countryCode, identifier, ); diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml index 512d2a6ba036..9693c186119c 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_storekit description: An implementation for the iOS platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase_storekit issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.3.0+4 +version: 0.3.0+5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/ios_platform_images/CHANGELOG.md b/packages/ios_platform_images/CHANGELOG.md index f2fb43dec1d1..5e7d997cc711 100644 --- a/packages/ios_platform_images/CHANGELOG.md +++ b/packages/ios_platform_images/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.2.0+5 +* Migrates from `ui.hash*` to `Object.hash*`. * Adds OS version support information to README. ## 0.2.0+4 diff --git a/packages/ios_platform_images/lib/ios_platform_images.dart b/packages/ios_platform_images/lib/ios_platform_images.dart index 23a437d775ef..9632b3b2c05d 100644 --- a/packages/ios_platform_images/lib/ios_platform_images.dart +++ b/packages/ios_platform_images/lib/ios_platform_images.dart @@ -95,7 +95,7 @@ class _FutureMemoryImage extends ImageProvider<_FutureMemoryImage> { /// See [ImageProvider.hashCode]. @override - int get hashCode => hashValues(_futureBytes.hashCode, _futureScale); + int get hashCode => Object.hash(_futureBytes.hashCode, _futureScale); /// See [ImageProvider.toString]. @override diff --git a/packages/ios_platform_images/pubspec.yaml b/packages/ios_platform_images/pubspec.yaml index fe7952abbc3b..7f80714e4c1c 100644 --- a/packages/ios_platform_images/pubspec.yaml +++ b/packages/ios_platform_images/pubspec.yaml @@ -2,7 +2,7 @@ name: ios_platform_images description: A plugin to share images between Flutter and iOS in add-to-app setups. repository: https://github.com/flutter/plugins/tree/main/packages/ios_platform_images issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+ios_platform_images%22 -version: 0.2.0+4 +version: 0.2.0+5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md index 500bee7d2622..f3f612adac35 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.8.2 + +* Migrates from `ui.hash*` to `Object.hash*`. +* Updates minimum Flutter version to 2.5.0. + ## 1.8.1 * Update to use the `verify` method introduced in platform_plugin_interface 2.1.0. diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/web_settings.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/web_settings.dart index 57c0a482089c..102ab10ccea7 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/web_settings.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/web_settings.dart @@ -62,7 +62,7 @@ class WebSetting { } @override - int get hashCode => hashValues(_value, isPresent); + int get hashCode => Object.hash(_value, isPresent); } /// Settings for configuring a WebViewPlatform. diff --git a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml index 50b816d51d5d..f9e754931bb8 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml @@ -4,11 +4,11 @@ repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutte issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview_flutter%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 1.8.1 +version: 1.8.2 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.5.0" dependencies: flutter: From 68e600e54aa0da732ca72dc459a109266a2ac4a7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 7 Apr 2022 11:11:06 -0400 Subject: [PATCH 350/600] Roll Flutter from 2999e8531569 to f5f9ad915e55 (7 revisions) (#5193) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 7f64f4658e1e..0001767b4015 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2999e8531569bf57c1eb8c771e503adaeeffea8a +f5f9ad915e554283ccff109fed969d9ef255fb91 From ab07a4ec3ebf6579f9d5422a8bfc166b2eedbe66 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 7 Apr 2022 09:52:51 -0700 Subject: [PATCH 351/600] [webview_flutter_wkwebview] Implement Dart side of the Pigeon HostApis (#5129) --- .../lib/src/common/instance_manager.dart | 52 + .../lib/src/common/web_kit.pigeon.dart | 1683 +++++++++++++++++ .../lib/src/foundation/foundation.dart | 27 +- .../src/foundation/foundation_api_impls.dart | 85 + .../lib/src/ui_kit/ui_kit.dart | 42 +- .../lib/src/ui_kit/ui_kit_api_impls.dart | 107 ++ .../lib/src/web_kit/web_kit.dart | 319 +++- .../lib/src/web_kit/web_kit_api_impls.dart | 549 ++++++ .../lib/src/web_kit_webview_widget.dart | 2 +- .../pigeons/web_kit.dart | 352 ++++ .../webview_flutter_wkwebview/pubspec.yaml | 1 + .../src/common/instance_manager_test.dart | 35 + .../test/src/common/test_web_kit.pigeon.dart | 1308 +++++++++++++ .../test/src/foundation/foundation_test.dart | 100 + .../src/foundation/foundation_test.mocks.dart | 48 + .../test/src/ui_kit/ui_kit_test.dart | 122 ++ .../test/src/ui_kit/ui_kit_test.mocks.dart | 200 ++ .../test/src/web_kit/web_kit_test.dart | 530 ++++++ .../test/src/web_kit/web_kit_test.mocks.dart | 287 +++ .../test/src/web_kit_webview_widget_test.dart | 2 +- .../web_kit_webview_widget_test.mocks.dart | 74 +- 21 files changed, 5806 insertions(+), 119 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/instance_manager.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/src/common/instance_manager_test.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.mocks.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.mocks.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/instance_manager.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/instance_manager.dart new file mode 100644 index 000000000000..830ba2e94935 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/instance_manager.dart @@ -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. + +/// Maintains instances stored to communicate with Objective-C objects. +class InstanceManager { + final Map _instanceIdsToInstances = {}; + final Map _instancesToInstanceIds = {}; + + int _nextInstanceId = 0; + + /// Global instance of [InstanceManager]. + static final InstanceManager instance = InstanceManager(); + + /// Attempt to add a new instance. + /// + /// Returns new if [instance] has already been added. Otherwise, it is added + /// with a new instance id. + int? tryAddInstance(Object instance) { + if (_instancesToInstanceIds.containsKey(instance)) { + return null; + } + + final int instanceId = _nextInstanceId++; + _instancesToInstanceIds[instance] = instanceId; + _instanceIdsToInstances[instanceId] = instance; + return instanceId; + } + + /// Remove the instance from the manager. + /// + /// Returns null if the instance is removed. Otherwise, return the instanceId + /// of the removed instance. + int? removeInstance(T instance) { + final int? instanceId = _instancesToInstanceIds[instance]; + if (instanceId != null) { + _instancesToInstanceIds.remove(instance); + _instanceIdsToInstances.remove(instanceId); + } + return instanceId; + } + + /// Retrieve the Object paired with instanceId. + T? getInstance(int instanceId) { + return _instanceIdsToInstances[instanceId] as T?; + } + + /// Retrieve the instanceId paired with instance. + int? getInstanceId(Object instance) { + return _instancesToInstanceIds[instance]; + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart new file mode 100644 index 000000000000..22013d2ff0cc --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart @@ -0,0 +1,1683 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v2.0.3), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name +// @dart = 2.12 +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; + +import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; +import 'package:flutter/services.dart'; + +enum NSKeyValueObservingOptionsEnum { + newValue, + oldValue, + initialValue, + priorNotification, +} + +enum NSKeyValueChangeEnum { + setting, + insertion, + removal, + replacement, +} + +enum NSKeyValueChangeKeyEnum { + indexes, + kind, + newValue, + notificationIsPrior, + oldValue, +} + +enum WKUserScriptInjectionTimeEnum { + atDocumentStart, + atDocumentEnd, +} + +enum WKAudiovisualMediaTypeEnum { + none, + audio, + video, + all, +} + +enum WKWebsiteDataTypesEnum { + cookies, + memoryCache, + diskCache, + offlineWebApplicationCache, + localStroage, + sessionStorage, + sqlDatabases, + indexedDBDatabases, +} + +enum WKNavigationActionPolicyEnum { + allow, + cancel, +} + +class NSKeyValueObservingOptionsEnumData { + NSKeyValueObservingOptionsEnumData({ + this.value, + }); + + NSKeyValueObservingOptionsEnum? value; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['value'] = value == null ? null : value!.index; + return pigeonMap; + } + + static NSKeyValueObservingOptionsEnumData decode(Object message) { + final Map pigeonMap = message as Map; + return NSKeyValueObservingOptionsEnumData( + value: pigeonMap['value'] != null + ? NSKeyValueObservingOptionsEnum.values[pigeonMap['value']! as int] + : null, + ); + } +} + +class WKUserScriptInjectionTimeEnumData { + WKUserScriptInjectionTimeEnumData({ + this.value, + }); + + WKUserScriptInjectionTimeEnum? value; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['value'] = value == null ? null : value!.index; + return pigeonMap; + } + + static WKUserScriptInjectionTimeEnumData decode(Object message) { + final Map pigeonMap = message as Map; + return WKUserScriptInjectionTimeEnumData( + value: pigeonMap['value'] != null + ? WKUserScriptInjectionTimeEnum.values[pigeonMap['value']! as int] + : null, + ); + } +} + +class WKAudiovisualMediaTypeEnumData { + WKAudiovisualMediaTypeEnumData({ + this.value, + }); + + WKAudiovisualMediaTypeEnum? value; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['value'] = value == null ? null : value!.index; + return pigeonMap; + } + + static WKAudiovisualMediaTypeEnumData decode(Object message) { + final Map pigeonMap = message as Map; + return WKAudiovisualMediaTypeEnumData( + value: pigeonMap['value'] != null + ? WKAudiovisualMediaTypeEnum.values[pigeonMap['value']! as int] + : null, + ); + } +} + +class WKWebsiteDataTypesEnumData { + WKWebsiteDataTypesEnumData({ + this.value, + }); + + WKWebsiteDataTypesEnum? value; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['value'] = value == null ? null : value!.index; + return pigeonMap; + } + + static WKWebsiteDataTypesEnumData decode(Object message) { + final Map pigeonMap = message as Map; + return WKWebsiteDataTypesEnumData( + value: pigeonMap['value'] != null + ? WKWebsiteDataTypesEnum.values[pigeonMap['value']! as int] + : null, + ); + } +} + +class NSUrlRequestData { + NSUrlRequestData({ + required this.url, + this.httpMethod, + this.httpBody, + required this.allHttpHeaderFields, + }); + + String url; + String? httpMethod; + Uint8List? httpBody; + Map allHttpHeaderFields; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['url'] = url; + pigeonMap['httpMethod'] = httpMethod; + pigeonMap['httpBody'] = httpBody; + pigeonMap['allHttpHeaderFields'] = allHttpHeaderFields; + return pigeonMap; + } + + static NSUrlRequestData decode(Object message) { + final Map pigeonMap = message as Map; + return NSUrlRequestData( + url: pigeonMap['url']! as String, + httpMethod: pigeonMap['httpMethod'] as String?, + httpBody: pigeonMap['httpBody'] as Uint8List?, + allHttpHeaderFields: + (pigeonMap['allHttpHeaderFields'] as Map?)! + .cast(), + ); + } +} + +class WKUserScriptData { + WKUserScriptData({ + required this.source, + this.injectionTime, + required this.isMainFrameOnly, + }); + + String source; + WKUserScriptInjectionTimeEnumData? injectionTime; + bool isMainFrameOnly; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['source'] = source; + pigeonMap['injectionTime'] = + injectionTime == null ? null : injectionTime!.encode(); + pigeonMap['isMainFrameOnly'] = isMainFrameOnly; + return pigeonMap; + } + + static WKUserScriptData decode(Object message) { + final Map pigeonMap = message as Map; + return WKUserScriptData( + source: pigeonMap['source']! as String, + injectionTime: pigeonMap['injectionTime'] != null + ? WKUserScriptInjectionTimeEnumData.decode( + pigeonMap['injectionTime']!) + : null, + isMainFrameOnly: pigeonMap['isMainFrameOnly']! as bool, + ); + } +} + +class _WKWebsiteDataStoreHostApiCodec extends StandardMessageCodec { + const _WKWebsiteDataStoreHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is WKWebsiteDataTypesEnumData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return WKWebsiteDataTypesEnumData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +class WKWebsiteDataStoreHostApi { + /// Constructor for [WKWebsiteDataStoreHostApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + WKWebsiteDataStoreHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _WKWebsiteDataStoreHostApiCodec(); + + Future createFromWebViewConfiguration( + int arg_instanceId, int arg_configurationInstanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebsiteDataStoreHostApi.createFromWebViewConfiguration', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_configurationInstanceId]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future removeDataOfTypes( + int arg_instanceId, + List arg_dataTypes, + double arg_secondsModifiedSinceEpoch) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel.send([ + arg_instanceId, + arg_dataTypes, + arg_secondsModifiedSinceEpoch + ]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} + +class _UIViewHostApiCodec extends StandardMessageCodec { + const _UIViewHostApiCodec(); +} + +class UIViewHostApi { + /// Constructor for [UIViewHostApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + UIViewHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _UIViewHostApiCodec(); + + Future> getContentOffset(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UIViewHostApi.getContentOffset', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as List?)!.cast(); + } + } + + Future setBackgroundColor(int arg_instanceId, int? arg_value) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UIViewHostApi.setBackgroundColor', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_value]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setOpaque(int arg_instanceId, bool arg_opaque) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UIViewHostApi.setOpaque', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_opaque]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} + +class _UIScrollViewHostApiCodec extends StandardMessageCodec { + const _UIScrollViewHostApiCodec(); +} + +class UIScrollViewHostApi { + /// Constructor for [UIScrollViewHostApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + UIScrollViewHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _UIScrollViewHostApiCodec(); + + Future createFromWebView( + int arg_instanceId, int arg_webViewInstanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UIScrollViewHostApi.createFromWebView', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_webViewInstanceId]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future> getContentOffset(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UIScrollViewHostApi.getContentOffset', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as List?)!.cast(); + } + } + + Future scrollBy(int arg_instanceId, double arg_x, double arg_y) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UIScrollViewHostApi.scrollBy', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_x, arg_y]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setContentOffset( + int arg_instanceId, double arg_x, double arg_y) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UIScrollViewHostApi.setContentOffset', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_x, arg_y]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} + +class _WKWebViewConfigurationHostApiCodec extends StandardMessageCodec { + const _WKWebViewConfigurationHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is WKAudiovisualMediaTypeEnumData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return WKAudiovisualMediaTypeEnumData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +class WKWebViewConfigurationHostApi { + /// Constructor for [WKWebViewConfigurationHostApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + WKWebViewConfigurationHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = + _WKWebViewConfigurationHostApiCodec(); + + Future create(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewConfigurationHostApi.create', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future createFromWebView( + int arg_instanceId, int arg_webViewInstanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewConfigurationHostApi.createFromWebView', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_webViewInstanceId]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setAllowsInlineMediaPlayback( + int arg_instanceId, bool arg_allow) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewConfigurationHostApi.setAllowsInlineMediaPlayback', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_allow]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setMediaTypesRequiringUserActionForPlayback(int arg_instanceId, + List arg_types) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewConfigurationHostApi.setMediaTypesRequiringUserActionForPlayback', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_types]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} + +class _WKUserContentControllerHostApiCodec extends StandardMessageCodec { + const _WKUserContentControllerHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is WKUserScriptData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is WKUserScriptInjectionTimeEnumData) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return WKUserScriptData.decode(readValue(buffer)!); + + case 129: + return WKUserScriptInjectionTimeEnumData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +class WKUserContentControllerHostApi { + /// Constructor for [WKUserContentControllerHostApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + WKUserContentControllerHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = + _WKUserContentControllerHostApiCodec(); + + Future createFromWebViewConfiguration( + int arg_instanceId, int arg_configurationInstanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUserContentControllerHostApi.createFromWebViewConfiguration', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_configurationInstanceId]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future addScriptMessageHandler( + int arg_instanceId, int arg_handlerInstanceid, String arg_name) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUserContentControllerHostApi.addScriptMessageHandler', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_handlerInstanceid, arg_name]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future removeScriptMessageHandler( + int arg_instanceId, String arg_name) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUserContentControllerHostApi.removeScriptMessageHandler', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_name]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future removeAllScriptMessageHandlers(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUserContentControllerHostApi.removeAllScriptMessageHandlers', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future addUserScript( + int arg_instanceId, WKUserScriptData arg_userScript) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUserContentControllerHostApi.addUserScript', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_userScript]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future removeAllUserScripts(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUserContentControllerHostApi.removeAllUserScripts', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} + +class _WKPreferencesHostApiCodec extends StandardMessageCodec { + const _WKPreferencesHostApiCodec(); +} + +class WKPreferencesHostApi { + /// Constructor for [WKPreferencesHostApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + WKPreferencesHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _WKPreferencesHostApiCodec(); + + Future createFromWebViewConfiguration( + int arg_instanceId, int arg_configurationInstanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKPreferencesHostApi.createFromWebViewConfiguration', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_configurationInstanceId]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setJavaScriptEnabled( + int arg_instanceId, bool arg_enabled) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKPreferencesHostApi.setJavaScriptEnabled', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_enabled]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} + +class _WKScriptMessageHandlerHostApiCodec extends StandardMessageCodec { + const _WKScriptMessageHandlerHostApiCodec(); +} + +class WKScriptMessageHandlerHostApi { + /// Constructor for [WKScriptMessageHandlerHostApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + WKScriptMessageHandlerHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = + _WKScriptMessageHandlerHostApiCodec(); + + Future create(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKScriptMessageHandlerHostApi.create', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} + +class _WKNavigationDelegateHostApiCodec extends StandardMessageCodec { + const _WKNavigationDelegateHostApiCodec(); +} + +class WKNavigationDelegateHostApi { + /// Constructor for [WKNavigationDelegateHostApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + WKNavigationDelegateHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = + _WKNavigationDelegateHostApiCodec(); + + Future create(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKNavigationDelegateHostApi.create', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} + +class _NSObjectHostApiCodec extends StandardMessageCodec { + const _NSObjectHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is NSKeyValueObservingOptionsEnumData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return NSKeyValueObservingOptionsEnumData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +class NSObjectHostApi { + /// Constructor for [NSObjectHostApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + NSObjectHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _NSObjectHostApiCodec(); + + Future dispose(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NSObjectHostApi.dispose', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future addObserver( + int arg_instanceId, + int arg_observerInstanceId, + String arg_keyPath, + List arg_options) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NSObjectHostApi.addObserver', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel.send([ + arg_instanceId, + arg_observerInstanceId, + arg_keyPath, + arg_options + ]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future removeObserver(int arg_instanceId, int arg_observerInstanceId, + String arg_keyPath) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NSObjectHostApi.removeObserver', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel.send( + [arg_instanceId, arg_observerInstanceId, arg_keyPath]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} + +class _WKWebViewHostApiCodec extends StandardMessageCodec { + const _WKWebViewHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is NSUrlRequestData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return NSUrlRequestData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +class WKWebViewHostApi { + /// Constructor for [WKWebViewHostApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + WKWebViewHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _WKWebViewHostApiCodec(); + + Future create( + int arg_instanceId, int arg_configurationInstanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.create', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_configurationInstanceId]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setUIDelegate( + int arg_instanceId, int? arg_uiDelegateInstanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.setUIDelegate', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_uiDelegateInstanceId]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setNavigationDelegate( + int arg_instanceId, int? arg_navigationDelegateInstanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.setNavigationDelegate', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_navigationDelegateInstanceId]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future getUrl(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.getUrl', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return (replyMap['result'] as String?); + } + } + + Future getEstimatedProgress(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.getEstimatedProgress', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as double?)!; + } + } + + Future loadRequest( + int arg_instanceId, NSUrlRequestData arg_request) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.loadRequest', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_request]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future loadHtmlString( + int arg_instanceId, String arg_string, String? arg_baseUrl) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.loadHtmlString', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_string, arg_baseUrl]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future loadFileUrl( + int arg_instanceId, String arg_url, String arg_readAccessUrl) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.loadFileUrl', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_url, arg_readAccessUrl]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future loadFlutterAsset(int arg_instanceId, String arg_key) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.loadFlutterAsset', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_key]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future canGoBack(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.canGoBack', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as bool?)!; + } + } + + Future canGoForward(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.canGoForward', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as bool?)!; + } + } + + Future goBack(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.goBack', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future goForward(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.goForward', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future reload(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.reload', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future getTitle(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.getTitle', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return (replyMap['result'] as String?); + } + } + + Future setAllowsBackForwardNavigationGestures( + int arg_instanceId, bool arg_allow) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.setAllowsBackForwardNavigationGestures', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_allow]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setCustomUserAgent( + int arg_instanceId, String? arg_userAgent) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.setCustomUserAgent', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_userAgent]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future evaluateJavaScript( + int arg_instanceId, String arg_javascriptString) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.evaluateJavaScript', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_javascriptString]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as String?)!; + } + } +} + +class _WKUIDelegateHostApiCodec extends StandardMessageCodec { + const _WKUIDelegateHostApiCodec(); +} + +class WKUIDelegateHostApi { + /// Constructor for [WKUIDelegateHostApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + WKUIDelegateHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _WKUIDelegateHostApiCodec(); + + Future create(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUIDelegateHostApi.create', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart index 466c03a6c8e9..8aa315aa10ee 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart @@ -5,6 +5,10 @@ import 'dart:typed_data'; import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +import '../common/instance_manager.dart'; +import 'foundation_api_impls.dart'; /// The values that can be returned in a change map. /// @@ -140,6 +144,15 @@ class NSError { /// The root class of most Objective-C class hierarchies. class NSObject { + /// Constructs an [NSObject]. + NSObject({BinaryMessenger? binaryMessenger, InstanceManager? instanceManager}) + : _api = NSObjectHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ); + + final NSObjectHostApiImpl _api; + /// Registers the observer object to receive KVO notifications. Future addObserver( NSObject observer, { @@ -147,12 +160,22 @@ class NSObject { required Set options, }) { assert(options.isNotEmpty); - throw UnimplementedError(); + return _api.addObserverForInstances( + this, + observer, + keyPath, + options, + ); } /// Stops the observer object from receiving change notifications for the property. Future removeObserver(NSObject observer, {required String keyPath}) { - throw UnimplementedError(); + return _api.removeObserverForInstances(this, observer, keyPath); + } + + /// Release the reference to the Objective-C object. + Future dispose() { + return _api.disposeForInstances(this); } /// Informs the observing object when the value at the specified key path has changed. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart new file mode 100644 index 000000000000..ec0d60cc748e --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart @@ -0,0 +1,85 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; + +import '../common/instance_manager.dart'; +import '../common/web_kit.pigeon.dart'; +import 'foundation.dart'; + +Iterable + _toNSKeyValueObservingOptionsEnumData( + Iterable options, +) { + return options.map(( + NSKeyValueObservingOptions option, + ) { + late final NSKeyValueObservingOptionsEnum? value; + switch (option) { + case NSKeyValueObservingOptions.newValue: + value = NSKeyValueObservingOptionsEnum.newValue; + break; + case NSKeyValueObservingOptions.oldValue: + value = NSKeyValueObservingOptionsEnum.oldValue; + break; + case NSKeyValueObservingOptions.initialValue: + value = NSKeyValueObservingOptionsEnum.initialValue; + break; + case NSKeyValueObservingOptions.priorNotification: + value = NSKeyValueObservingOptionsEnum.priorNotification; + break; + } + + return NSKeyValueObservingOptionsEnumData(value: value); + }); +} + +/// Host api implementation for [NSObject]. +class NSObjectHostApiImpl extends NSObjectHostApi { + /// Constructs an [NSObjectHostApiImpl]. + NSObjectHostApiImpl({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? InstanceManager.instance, + super(binaryMessenger: binaryMessenger); + + /// Maintains instances stored to communicate with Objective-C objects. + final InstanceManager instanceManager; + + /// Calls [addObserver] with the ids of the provided object instances. + Future addObserverForInstances( + NSObject instance, + NSObject observer, + String keyPath, + Set options, + ) { + return addObserver( + instanceManager.getInstanceId(instance)!, + instanceManager.getInstanceId(observer)!, + keyPath, + _toNSKeyValueObservingOptionsEnumData(options).toList(), + ); + } + + /// Calls [removeObserver] with the ids of the provided object instances. + Future removeObserverForInstances( + NSObject instance, + NSObject observer, + String keyPath, + ) { + return removeObserver( + instanceManager.getInstanceId(instance)!, + instanceManager.getInstanceId(observer)!, + keyPath, + ); + } + + /// Calls [dispose] with the ids of the provided object instances. + Future disposeForInstances(NSObject instance) async { + final int? instanceId = instanceManager.removeInstance(instance); + if (instanceId != null) { + await dispose(instanceId); + } + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart index 08dd7541a9f4..7c1bdd01724c 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart @@ -2,28 +2,39 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; import 'dart:math'; -import 'package:flutter/painting.dart'; +import 'package:flutter/painting.dart' show Color; +import 'package:flutter/services.dart'; +import '../common/instance_manager.dart'; import '../foundation/foundation.dart'; import '../web_kit/web_kit.dart'; +import 'ui_kit_api_impls.dart'; /// A view that allows the scrolling and zooming of its contained views. /// /// Wraps [UIScrollView](https://developer.apple.com/documentation/uikit/uiscrollview?language=objc). class UIScrollView extends UIView { /// Constructs a [UIScrollView] that is owned by [webView]. - // TODO(bparrishMines): Remove ignore once constructor is implemented. - // ignore: avoid_unused_constructor_parameters - UIScrollView.fromWebView(WKWebView webView); + UIScrollView.fromWebView( + WKWebView webView, { + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _scrollViewApi = UIScrollViewHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ) { + _scrollViewApi.createFromWebViewForInstances(this, webView); + } + + final UIScrollViewHostApiImpl _scrollViewApi; /// Point at which the origin of the content view is offset from the origin of the scroll view. /// /// Represents [WKWebView.contentOffset](https://developer.apple.com/documentation/uikit/uiscrollview/1619404-contentoffset?language=objc). Future> getContentOffset() { - throw UnimplementedError(); + return _scrollViewApi.getContentOffsetForInstances(this); } /// Move the scrolled position of this view. @@ -31,7 +42,7 @@ class UIScrollView extends UIView { /// This method is not a part of UIKit and is only a helper method to make /// scrollBy atomic. Future scrollBy(Point offset) { - throw UnimplementedError(); + return _scrollViewApi.scrollByForInstances(this, offset); } /// Set point at which the origin of the content view is offset from the origin of the scroll view. @@ -39,8 +50,8 @@ class UIScrollView extends UIView { /// The default value is `Point(0.0, 0.0)`. /// /// Sets [WKWebView.contentOffset](https://developer.apple.com/documentation/uikit/uiscrollview/1619404-contentoffset?language=objc). - Future setContentOffset(FutureOr> offset) { - throw UnimplementedError(); + Future setContentOffset(Point offset) { + return _scrollViewApi.setContentOffsetForInstances(this, offset); } } @@ -48,19 +59,28 @@ class UIScrollView extends UIView { /// /// Wraps [UIView](https://developer.apple.com/documentation/uikit/uiview?language=objc). class UIView extends NSObject { + /// Constructs an [NSObject]. + UIView({BinaryMessenger? binaryMessenger, InstanceManager? instanceManager}) + : _viewApi = UIViewHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ); + + final UIViewHostApiImpl _viewApi; + /// The view’s background color. /// /// The default value is null, which results in a transparent background color. /// /// Sets [UIView.backgroundColor](https://developer.apple.com/documentation/uikit/uiview/1622591-backgroundcolor?language=objc). Future setBackgroundColor(Color? color) { - throw UnimplementedError(); + return _viewApi.setBackgroundColorForInstances(this, color); } /// Determines whether the view is opaque. /// /// Sets [UIView.opaque](https://developer.apple.com/documentation/uikit/uiview?language=objc). Future setOpaque(bool opaque) { - throw UnimplementedError(); + return _viewApi.setOpaqueForInstances(this, opaque); } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart new file mode 100644 index 000000000000..b2ca5672f8e2 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart @@ -0,0 +1,107 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:math'; + +import 'package:flutter/painting.dart' show Color; +import 'package:flutter/services.dart'; + +import '../common/instance_manager.dart'; +import '../common/web_kit.pigeon.dart'; +import '../web_kit/web_kit.dart'; +import 'ui_kit.dart'; + +/// Host api implementation for [UIScrollView]. +class UIScrollViewHostApiImpl extends UIScrollViewHostApi { + /// Constructs a [UIScrollViewHostApiImpl]. + UIScrollViewHostApiImpl({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? InstanceManager.instance, + super(binaryMessenger: binaryMessenger); + + /// Maintains instances stored to communicate with Objective-C objects. + final InstanceManager instanceManager; + + /// Calls [createFromWebView] with the ids of the provided object instances. + Future createFromWebViewForInstances( + UIScrollView instance, + WKWebView webView, + ) async { + final int? instanceId = instanceManager.tryAddInstance(instance); + if (instanceId != null) { + await createFromWebView( + instanceId, + instanceManager.getInstanceId(webView)!, + ); + } + } + + /// Calls [getContentOffset] with the ids of the provided object instances. + Future> getContentOffsetForInstances( + UIScrollView instance, + ) async { + final List point = await getContentOffset( + instanceManager.getInstanceId(instance)!, + ); + return Point(point[0]!, point[1]!); + } + + /// Calls [scrollBy] with the ids of the provided object instances. + Future scrollByForInstances( + UIScrollView instance, + Point offset, + ) { + return scrollBy( + instanceManager.getInstanceId(instance)!, + offset.x, + offset.y, + ); + } + + /// Calls [setContentOffset] with the ids of the provided object instances. + Future setContentOffsetForInstances( + UIScrollView instance, + Point offset, + ) async { + return setContentOffset( + instanceManager.getInstanceId(instance)!, + offset.x, + offset.y, + ); + } +} + +/// Host api implementation for [UIView]. +class UIViewHostApiImpl extends UIViewHostApi { + /// Constructs a [UIViewHostApiImpl]. + UIViewHostApiImpl({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? InstanceManager.instance, + super(binaryMessenger: binaryMessenger); + + /// Maintains instances stored to communicate with Objective-C objects. + final InstanceManager instanceManager; + + /// Calls [setBackgroundColor] with the ids of the provided object instances. + Future setBackgroundColorForInstances( + UIView instance, + Color? color, + ) async { + return setBackgroundColor( + instanceManager.getInstanceId(instance)!, + color?.value, + ); + } + + /// Calls [setOpaque] with the ids of the provided object instances. + Future setOpaqueForInstances( + UIView instance, + bool opaque, + ) async { + return setOpaque(instanceManager.getInstanceId(instance)!, opaque); + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart index fb315cc847fb..0703ce1267d9 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart @@ -3,9 +3,12 @@ // found in the LICENSE file. import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import '../common/instance_manager.dart'; import '../foundation/foundation.dart'; import '../ui_kit/ui_kit.dart'; +import 'web_kit_api_impls.dart'; /// Times at which to inject script content into a webpage. /// @@ -196,22 +199,30 @@ class WKScriptMessage { /// /// Wraps [WKPreferences](https://developer.apple.com/documentation/webkit/wkpreferences?language=objc). class WKPreferences { - /// Constructs a [WKPreferences]. - WKPreferences(); - - // A WKPreferences that is owned by configuration. - WKPreferences._fromWebViewConfiguration( - // TODO(bparrishMines): Remove ignore once constructor is implemented. - // ignore: avoid_unused_constructor_parameters - WKWebViewConfiguration configuration, - ); + /// Constructs a [WKPreferences] that is owned by [configuration]. + @visibleForTesting + WKPreferences.fromWebViewConfiguration( + WKWebViewConfiguration configuration, { + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _preferencesApi = WKPreferencesHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ) { + _preferencesApi.createFromWebViewConfigurationForInstances( + this, + configuration, + ); + } + + final WKPreferencesHostApiImpl _preferencesApi; // TODO(bparrishMines): Deprecated for iOS 14.0+. Add support for alternative. /// Sets whether JavaScript is enabled. /// /// The default value is true. Future setJavaScriptEnabled(bool enabled) { - throw UnimplementedError(); + return _preferencesApi.setJavaScriptEnabledForInstances(this, enabled); } } @@ -219,18 +230,34 @@ class WKPreferences { /// /// Wraps [WKWebsiteDataStore](https://developer.apple.com/documentation/webkit/wkwebsitedatastore?language=objc). class WKWebsiteDataStore { - WKWebsiteDataStore._fromWebViewConfiguration( - // TODO(bparrishMines): Remove ignore once constructor is implemented. - // ignore: avoid_unused_constructor_parameters - WKWebViewConfiguration configuration, - ); + /// Constructs a [WKWebsiteDataStore] that is owned by [configuration]. + @visibleForTesting + WKWebsiteDataStore.fromWebViewConfiguration( + WKWebViewConfiguration configuration, { + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _websiteDataStoreApi = WKWebsiteDataStoreHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ) { + _websiteDataStoreApi.createFromWebViewConfigurationForInstances( + this, + configuration, + ); + } + + final WKWebsiteDataStoreHostApiImpl _websiteDataStoreApi; /// Removes website data that changed after the specified date. Future removeDataOfTypes( Set dataTypes, DateTime since, ) { - throw UnimplementedError(); + return _websiteDataStoreApi.removeDataOfTypesForInstances( + this, + dataTypes, + secondsModifiedSinceEpoch: since.millisecondsSinceEpoch / 1000, + ); } } @@ -238,6 +265,19 @@ class WKWebsiteDataStore { /// /// Wraps [WKScriptMessageHandler](https://developer.apple.com/documentation/webkit/wkscriptmessagehandler?language=objc) class WKScriptMessageHandler { + /// Constructs a [WKScriptMessageHandler]. + WKScriptMessageHandler({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _scriptMessageHandlerApi = WKScriptMessageHandlerHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ) { + _scriptMessageHandlerApi.createForInstances(this); + } + + final WKScriptMessageHandlerHostApiImpl _scriptMessageHandlerApi; + /// Tells the handler that a webpage sent a script message. /// /// Use this method to respond to a message sent from the webpage’s @@ -264,15 +304,23 @@ class WKScriptMessageHandler { /// /// Wraps [WKUserContentController](https://developer.apple.com/documentation/webkit/wkusercontentcontroller?language=objc). class WKUserContentController { - /// Constructs a [WKUserContentController]. - WKUserContentController(); - - // A WKUserContentController that is owned by configuration. - WKUserContentController._fromWebViewConfiguration( - // TODO(bparrishMines): Remove ignore once constructor is implemented. - // ignore: avoid_unused_constructor_parameters - WKWebViewConfiguration configuration, - ); + /// Constructs a [WKUserContentController] that is owned by [configuration]. + @visibleForTesting + WKUserContentController.fromWebViewConfiguration( + WKWebViewConfiguration configuration, { + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _userContentControllerApi = WKUserContentControllerHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ) { + _userContentControllerApi.createFromWebViewConfigurationForInstances( + this, + configuration, + ); + } + + final WKUserContentControllerHostApiImpl _userContentControllerApi; /// Installs a message handler that you can call from your JavaScript code. /// @@ -290,7 +338,11 @@ class WKUserContentController { String name, ) { assert(name.isNotEmpty); - throw UnimplementedError(); + return _userContentControllerApi.addScriptMessageHandlerForInstances( + this, + handler, + name, + ); } /// Uninstalls the custom message handler with the specified name from your JavaScript code. @@ -303,22 +355,28 @@ class WKUserContentController { /// message handler from the page content world. If you installed the message /// handler in a different content world, this method doesn’t remove it. Future removeScriptMessageHandler(String name) { - throw UnimplementedError(); + return _userContentControllerApi.removeScriptMessageHandlerForInstances( + this, + name, + ); } /// Uninstalls all custom message handlers associated with the user content controller. Future removeAllScriptMessageHandlers() { - throw UnimplementedError(); + return _userContentControllerApi.removeAllScriptMessageHandlersForInstances( + this, + ); } /// Injects the specified script into the webpage’s content. Future addUserScript(WKUserScript userScript) { - throw UnimplementedError(); + return _userContentControllerApi.addUserScriptForInstances( + this, userScript); } /// Removes all user scripts from the web view. Future removeAllUserScripts() { - throw UnimplementedError(); + return _userContentControllerApi.removeAllUserScriptsForInstances(this); } } @@ -327,44 +385,81 @@ class WKUserContentController { /// Wraps [WKWebViewConfiguration](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration?language=objc). class WKWebViewConfiguration { /// Constructs a [WKWebViewConfiguration]. - WKWebViewConfiguration(); - - // A WKWebViewConfiguration that is owned by webView. - // TODO(bparrishMines): Remove ignore once constructor is implemented. - // ignore: avoid_unused_constructor_parameters - WKWebViewConfiguration._fromWebView(WKWebView webView); - - late WKWebsiteDataStore _websiteDataStore = - WKWebsiteDataStore._fromWebViewConfiguration(this); - - late final WKUserContentController _userContentController = - WKUserContentController._fromWebViewConfiguration(this); - - late final WKPreferences _preferences = - WKPreferences._fromWebViewConfiguration(this); - - /// Used to get and set the site’s cookies and to track the cached data objects. - WKWebsiteDataStore get webSiteDataStore => _websiteDataStore; + factory WKWebViewConfiguration({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) { + final WKWebViewConfiguration configuration = WKWebViewConfiguration._( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ); + configuration._webViewConfigurationApi.createForInstances(configuration); + return configuration; + } + + /// A WKWebViewConfiguration that is owned by webView. + @visibleForTesting + factory WKWebViewConfiguration.fromWebView( + WKWebView webView, { + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) { + final WKWebViewConfiguration configuration = WKWebViewConfiguration._( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ); + configuration._webViewConfigurationApi.createFromWebViewForInstances( + configuration, + webView, + ); + return configuration; + } + + WKWebViewConfiguration._({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _binaryMessenger = binaryMessenger, + _instanceManager = instanceManager, + _webViewConfigurationApi = WKWebViewConfigurationHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ); + + final BinaryMessenger? _binaryMessenger; + final InstanceManager? _instanceManager; + + late final WKWebViewConfigurationHostApiImpl _webViewConfigurationApi; /// Coordinates interactions between your app’s code and the webpage’s scripts and other content. - WKUserContentController get userContentController => _userContentController; + late final WKUserContentController userContentController = + WKUserContentController.fromWebViewConfiguration( + this, + binaryMessenger: _binaryMessenger, + instanceManager: _instanceManager, + ); /// Manages the preference-related settings for the web view. - WKPreferences get preferences => _preferences; + late final WKPreferences preferences = + WKPreferences.fromWebViewConfiguration(this); /// Used to get and set the site’s cookies and to track the cached data objects. /// - /// Sets [WKWebViewConfiguration.webSiteDataStore](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/1395661-websitedatastore?language=objc). - Future setWebSiteDataStore(WKWebsiteDataStore websiteDataStore) { - _websiteDataStore = websiteDataStore; - throw UnimplementedError(); - } + /// Represents [WKWebViewConfiguration.webSiteDataStore](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/1395661-websitedatastore?language=objc). + late final WKWebsiteDataStore websiteDataStore = + WKWebsiteDataStore.fromWebViewConfiguration( + this, + binaryMessenger: _binaryMessenger, + instanceManager: _instanceManager, + ); /// Indicates whether HTML5 videos play inline or use the native full-screen controller. /// /// Sets [WKWebViewConfiguration.allowsInlineMediaPlayback](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/1614793-allowsinlinemediaplayback?language=objc). Future setAllowsInlineMediaPlayback(bool allow) { - throw UnimplementedError(); + return _webViewConfigurationApi.setAllowsInlineMediaPlaybackForInstances( + this, + allow, + ); } /// The media types that require a user gesture to begin playing. @@ -377,7 +472,11 @@ class WKWebViewConfiguration { Set types, ) { assert(types.isNotEmpty); - throw UnimplementedError(); + return _webViewConfigurationApi + .setMediaTypesRequiringUserActionForPlaybackForInstances( + this, + types, + ); } } @@ -385,7 +484,20 @@ class WKWebViewConfiguration { /// /// Wraps [WKUIDelegate](https://developer.apple.com/documentation/webkit/wkuidelegate?language=objc). class WKUIDelegate { - /// Indicates a new [WebView] was requested to be created with [configuration]. + /// Constructs a [WKUIDelegate]. + WKUIDelegate({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _uiDelegateApi = WKUIDelegateHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ) { + _uiDelegateApi.createForInstances(this); + } + + final WKUIDelegateHostApiImpl _uiDelegateApi; + + /// Indicates a new [WKWebView] was requested to be created with [configuration]. Future setOnCreateWebView( void Function( WKWebViewConfiguration configuration, @@ -404,6 +516,19 @@ class WKUIDelegate { /// /// Wraps [WKNavigationDelegate](https://developer.apple.com/documentation/webkit/wknavigationdelegate?language=objc). class WKNavigationDelegate { + /// Constructs a [WKNavigationDelegate]. + WKNavigationDelegate({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _navigationDelegateApi = WKNavigationDelegateHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ) { + _navigationDelegateApi.createForInstances(this); + } + + final WKNavigationDelegateHostApiImpl _navigationDelegateApi; + /// Called when navigation from the main frame has started. Future setDidStartProvisionalNavigation( void Function( @@ -468,11 +593,27 @@ class WKWebView extends UIView { /// values, see [WKWebViewConfiguration]. If you didn’t create your web view /// using the `configuration` parameter, this value uses a default /// configuration object. - // TODO(bparrishMines): Remove ignore once constructor is implemented. - // ignore: avoid_unused_constructor_parameters - WKWebView([WKWebViewConfiguration? configuration]) { - throw UnimplementedError(); - } + WKWebView( + WKWebViewConfiguration configuration, { + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _binaryMessenger = binaryMessenger, + _instanceManager = instanceManager, + _webViewApi = WKWebViewHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ), + super( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ) { + _webViewApi.createForInstances(this, configuration); + } + + final BinaryMessenger? _binaryMessenger; + final InstanceManager? _instanceManager; + + final WKWebViewHostApiImpl _webViewApi; /// Contains the configuration details for the web view. /// @@ -484,30 +625,38 @@ class WKWebView extends UIView { /// If you didn’t create your web view with a [WKWebViewConfiguration] this /// property contains a default configuration object. late final WKWebViewConfiguration configuration = - WKWebViewConfiguration._fromWebView(this); + WKWebViewConfiguration.fromWebView( + this, + binaryMessenger: _binaryMessenger, + instanceManager: _instanceManager, + ); /// The scrollable view associated with the web view. - late final UIScrollView scrollView = UIScrollView.fromWebView(this); + late final UIScrollView scrollView = UIScrollView.fromWebView( + this, + binaryMessenger: _binaryMessenger, + instanceManager: _instanceManager, + ); /// Used to integrate custom user interface elements into web view interactions. /// /// Sets [WKWebView.UIDelegate](https://developer.apple.com/documentation/webkit/wkwebview/1415009-uidelegate?language=objc). Future setUIDelegate(WKUIDelegate? delegate) { - throw UnimplementedError(); + return _webViewApi.setUIDelegateForInstances(this, delegate); } /// The object you use to manage navigation behavior for the web view. /// /// Sets [WKWebView.navigationDelegate](https://developer.apple.com/documentation/webkit/wkwebview/1414971-navigationdelegate?language=objc). Future setNavigationDelegate(WKNavigationDelegate? delegate) { - throw UnimplementedError(); + return _webViewApi.setNavigationDelegateForInstances(this, delegate); } /// The URL for the current webpage. /// /// Represents [WKWebView.URL](https://developer.apple.com/documentation/webkit/wkwebview/1415005-url?language=objc). Future getUrl() { - throw UnimplementedError(); + return _webViewApi.getUrlForInstances(this); } /// An estimate of what fraction of the current navigation has been loaded. @@ -516,7 +665,7 @@ class WKWebView extends UIView { /// /// Represents [WKWebView.estimatedProgress](https://developer.apple.com/documentation/webkit/wkwebview/1415007-estimatedprogress?language=objc). Future getEstimatedProgress() { - throw UnimplementedError(); + return _webViewApi.getEstimatedProgressForInstances(this); } /// Loads the web content referenced by the specified URL request object and navigates to it. @@ -524,17 +673,17 @@ class WKWebView extends UIView { /// Use this method to load a page from a local or network-based URL. For /// example, you might use it to navigate to a network-based webpage. Future loadRequest(NSUrlRequest request) { - throw UnimplementedError(); + return _webViewApi.loadRequestForInstances(this, request); } /// Loads the contents of the specified HTML string and navigates to it. Future loadHtmlString(String string, {String? baseUrl}) { - throw UnimplementedError(); + return _webViewApi.loadHtmlStringForInstances(this, string, baseUrl); } /// Loads the web content from the specified file and navigates to it. Future loadFileUrl(String url, {required String readAccessUrl}) { - throw UnimplementedError(); + return _webViewApi.loadFileUrlForInstances(this, url, readAccessUrl); } /// Loads the Flutter asset specified in the pubspec.yaml file. @@ -542,39 +691,39 @@ class WKWebView extends UIView { /// This method is not a part of WebKit and is only a Flutter specific helper /// method. Future loadFlutterAsset(String key) { - throw UnimplementedError(); + return _webViewApi.loadFlutterAssetForInstances(this, key); } /// Indicates whether there is a valid back item in the back-forward list. Future canGoBack() { - throw UnimplementedError(); + return _webViewApi.canGoBackForInstances(this); } /// Indicates whether there is a valid forward item in the back-forward list. Future canGoForward() { - throw UnimplementedError(); + return _webViewApi.canGoForwardForInstances(this); } /// Navigates to the back item in the back-forward list. Future goBack() { - throw UnimplementedError(); + return _webViewApi.goBackForInstances(this); } /// Navigates to the forward item in the back-forward list. Future goForward() { - throw UnimplementedError(); + return _webViewApi.goForwardForInstances(this); } /// Reloads the current webpage. Future reload() { - throw UnimplementedError(); + return _webViewApi.reloadForInstances(this); } /// The page title. /// /// Represents [WKWebView.title](https://developer.apple.com/documentation/webkit/wkwebview/1415015-title?language=objc). Future getTitle() { - throw UnimplementedError(); + return _webViewApi.getTitleForInstances(this); } /// Indicates whether horizontal swipe gestures trigger page navigation. @@ -583,7 +732,10 @@ class WKWebView extends UIView { /// /// Sets [WKWebView.allowsBackForwardNavigationGestures](https://developer.apple.com/documentation/webkit/wkwebview/1414995-allowsbackforwardnavigationgestu?language=objc). Future setAllowsBackForwardNavigationGestures(bool allow) { - throw UnimplementedError(); + return _webViewApi.setAllowsBackForwardNavigationGesturesForInstances( + this, + allow, + ); } /// The custom user agent string. @@ -592,7 +744,7 @@ class WKWebView extends UIView { /// /// Sets [WKWebView.customUserAgent](https://developer.apple.com/documentation/webkit/wkwebview/1414950-customuseragent?language=objc). Future setCustomUserAgent(String? userAgent) { - throw UnimplementedError(); + return _webViewApi.setCustomUserAgentForInstances(this, userAgent); } /// Evaluates the specified JavaScript string. @@ -600,6 +752,9 @@ class WKWebView extends UIView { /// Throws a `PlatformException` if an error occurs or return value is not /// supported. Future evaluateJavaScript(String javaScriptString) { - throw UnimplementedError(); + return _webViewApi.evaluateJavaScriptForInstances( + this, + javaScriptString, + ); } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart new file mode 100644 index 000000000000..188f65cbe177 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart @@ -0,0 +1,549 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; + +import '../common/instance_manager.dart'; +import '../common/web_kit.pigeon.dart'; +import '../foundation/foundation.dart'; +import 'web_kit.dart'; + +Iterable _toWKWebsiteDataTypesEnumData( + Iterable types) { + return types.map((WKWebsiteDataTypes type) { + late final WKWebsiteDataTypesEnum value; + switch (type) { + case WKWebsiteDataTypes.cookies: + value = WKWebsiteDataTypesEnum.cookies; + break; + case WKWebsiteDataTypes.memoryCache: + value = WKWebsiteDataTypesEnum.memoryCache; + break; + case WKWebsiteDataTypes.diskCache: + value = WKWebsiteDataTypesEnum.diskCache; + break; + case WKWebsiteDataTypes.offlineWebApplicationCache: + value = WKWebsiteDataTypesEnum.offlineWebApplicationCache; + break; + case WKWebsiteDataTypes.localStroage: + value = WKWebsiteDataTypesEnum.localStroage; + break; + case WKWebsiteDataTypes.sessionStorage: + value = WKWebsiteDataTypesEnum.sessionStorage; + break; + case WKWebsiteDataTypes.sqlDatabases: + value = WKWebsiteDataTypesEnum.sqlDatabases; + break; + case WKWebsiteDataTypes.indexedDBDatabases: + value = WKWebsiteDataTypesEnum.indexedDBDatabases; + break; + } + + return WKWebsiteDataTypesEnumData(value: value); + }); +} + +extension _WKUserScriptInjectionTimeConverter on WKUserScriptInjectionTime { + WKUserScriptInjectionTimeEnumData toWKUserScriptInjectionTimeEnumData() { + late final WKUserScriptInjectionTimeEnum value; + switch (this) { + case WKUserScriptInjectionTime.atDocumentStart: + value = WKUserScriptInjectionTimeEnum.atDocumentStart; + break; + case WKUserScriptInjectionTime.atDocumentEnd: + value = WKUserScriptInjectionTimeEnum.atDocumentEnd; + break; + } + + return WKUserScriptInjectionTimeEnumData(value: value); + } +} + +Iterable _toWKAudiovisualMediaTypeEnumData( + Iterable types, +) { + return types + .map((WKAudiovisualMediaType type) { + late final WKAudiovisualMediaTypeEnum value; + switch (type) { + case WKAudiovisualMediaType.none: + value = WKAudiovisualMediaTypeEnum.none; + break; + case WKAudiovisualMediaType.audio: + value = WKAudiovisualMediaTypeEnum.audio; + break; + case WKAudiovisualMediaType.video: + value = WKAudiovisualMediaTypeEnum.video; + break; + case WKAudiovisualMediaType.all: + value = WKAudiovisualMediaTypeEnum.all; + break; + } + + return WKAudiovisualMediaTypeEnumData(value: value); + }); +} + +extension _WKUserScriptConverter on WKUserScript { + WKUserScriptData toWKUserScriptData() { + return WKUserScriptData( + source: source, + injectionTime: injectionTime.toWKUserScriptInjectionTimeEnumData(), + isMainFrameOnly: isMainFrameOnly, + ); + } +} + +extension _NSUrlRequestConverter on NSUrlRequest { + NSUrlRequestData toNSUrlRequestData() { + return NSUrlRequestData( + url: url, + httpMethod: httpMethod, + httpBody: httpBody, + allHttpHeaderFields: allHttpHeaderFields, + ); + } +} + +/// Host api implementation for [WKWebSiteDataStore]. +class WKWebsiteDataStoreHostApiImpl extends WKWebsiteDataStoreHostApi { + /// Constructs a [WebsiteDataStoreHostApiImpl]. + WKWebsiteDataStoreHostApiImpl({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? InstanceManager.instance, + super(binaryMessenger: binaryMessenger); + + /// Maintains instances stored to communicate with Objective-C objects. + final InstanceManager instanceManager; + + /// Calls [createFromWebViewConfiguration] with the ids of the provided object instances. + Future createFromWebViewConfigurationForInstances( + WKWebsiteDataStore instance, + WKWebViewConfiguration configuration, + ) async { + final int? instanceId = instanceManager.tryAddInstance(instance); + if (instanceId != null) { + await createFromWebViewConfiguration( + instanceId, + instanceManager.getInstanceId(configuration)!, + ); + } + } + + /// Calls [removeDataOfTypes] with the ids of the provided object instances. + Future removeDataOfTypesForInstances( + WKWebsiteDataStore instance, + Set dataTypes, { + required double secondsModifiedSinceEpoch, + }) { + return removeDataOfTypes( + instanceManager.getInstanceId(instance)!, + _toWKWebsiteDataTypesEnumData(dataTypes).toList(), + secondsModifiedSinceEpoch, + ); + } +} + +/// Host api implementation for [WKScriptMessageHandler]. +class WKScriptMessageHandlerHostApiImpl extends WKScriptMessageHandlerHostApi { + /// Constructs a [WKScriptMessageHandlerHostApiImpl]. + WKScriptMessageHandlerHostApiImpl({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? InstanceManager.instance, + super(binaryMessenger: binaryMessenger); + + /// Maintains instances stored to communicate with Objective-C objects. + final InstanceManager instanceManager; + + /// Calls [create] with the ids of the provided object instances. + Future createForInstances(WKScriptMessageHandler instance) async { + final int? instanceId = instanceManager.tryAddInstance(instance); + if (instanceId != null) { + await create(instanceId); + } + } +} + +/// Host api implementation for [WKPreferences]. +class WKPreferencesHostApiImpl extends WKPreferencesHostApi { + /// Constructs a [WKPreferencesHostApiImpl]. + WKPreferencesHostApiImpl({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? InstanceManager.instance, + super(binaryMessenger: binaryMessenger); + + /// Maintains instances stored to communicate with Objective-C objects. + final InstanceManager instanceManager; + + /// Calls [createFromWebViewConfiguration] with the ids of the provided object instances. + Future createFromWebViewConfigurationForInstances( + WKPreferences instance, + WKWebViewConfiguration configuration, + ) async { + final int? instanceId = instanceManager.tryAddInstance(instance); + if (instanceId != null) { + await createFromWebViewConfiguration( + instanceId, + instanceManager.getInstanceId(configuration)!, + ); + } + } + + /// Calls [setJavaScriptEnabled] with the ids of the provided object instances. + Future setJavaScriptEnabledForInstances( + WKPreferences instance, + bool enabled, + ) { + return setJavaScriptEnabled( + instanceManager.getInstanceId(instance)!, + enabled, + ); + } +} + +/// Host api implementation for [WKUserContentController]. +class WKUserContentControllerHostApiImpl + extends WKUserContentControllerHostApi { + /// Constructs a [WKUserContentControllerHostApiImpl]. + WKUserContentControllerHostApiImpl({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? InstanceManager.instance, + super(binaryMessenger: binaryMessenger); + + /// Maintains instances stored to communicate with Objective-C objects. + final InstanceManager instanceManager; + + /// Calls [createFromWebViewConfiguration] with the ids of the provided object instances. + Future createFromWebViewConfigurationForInstances( + WKUserContentController instance, + WKWebViewConfiguration configuration, + ) async { + final int? instanceId = instanceManager.tryAddInstance(instance); + if (instanceId != null) { + await createFromWebViewConfiguration( + instanceId, + instanceManager.getInstanceId(configuration)!, + ); + } + } + + /// Calls [addScriptMessageHandler] with the ids of the provided object instances. + Future addScriptMessageHandlerForInstances( + WKUserContentController instance, + WKScriptMessageHandler handler, + String name, + ) { + return addScriptMessageHandler( + instanceManager.getInstanceId(instance)!, + instanceManager.getInstanceId(handler)!, + name, + ); + } + + /// Calls [removeScriptMessageHandler] with the ids of the provided object instances. + Future removeScriptMessageHandlerForInstances( + WKUserContentController instance, + String name, + ) { + return removeScriptMessageHandler( + instanceManager.getInstanceId(instance)!, + name, + ); + } + + /// Calls [removeAllScriptMessageHandlers] with the ids of the provided object instances. + Future removeAllScriptMessageHandlersForInstances( + WKUserContentController instance, + ) { + return removeAllScriptMessageHandlers( + instanceManager.getInstanceId(instance)!, + ); + } + + /// Calls [addUserScript] with the ids of the provided object instances. + Future addUserScriptForInstances( + WKUserContentController instance, + WKUserScript userScript, + ) { + return addUserScript( + instanceManager.getInstanceId(instance)!, + userScript.toWKUserScriptData(), + ); + } + + /// Calls [removeAllUserScripts] with the ids of the provided object instances. + Future removeAllUserScriptsForInstances( + WKUserContentController instance, + ) { + return removeAllUserScripts(instanceManager.getInstanceId(instance)!); + } +} + +/// Host api implementation for [WKWebViewConfiguration]. +class WKWebViewConfigurationHostApiImpl extends WKWebViewConfigurationHostApi { + /// Constructs a [WKWebViewConfigurationHostApiImpl]. + WKWebViewConfigurationHostApiImpl({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? InstanceManager.instance, + super(binaryMessenger: binaryMessenger); + + /// Maintains instances stored to communicate with Objective-C objects. + final InstanceManager instanceManager; + + /// Calls [create] with the ids of the provided object instances. + Future createForInstances(WKWebViewConfiguration instance) async { + final int? instanceId = instanceManager.tryAddInstance(instance); + if (instanceId != null) { + await create(instanceId); + } + } + + /// Calls [createFromWebView] with the ids of the provided object instances. + Future createFromWebViewForInstances( + WKWebViewConfiguration instance, + WKWebView webView, + ) async { + final int? instanceId = instanceManager.tryAddInstance(instance); + if (instanceId != null) { + await createFromWebView( + instanceId, + instanceManager.getInstanceId(webView)!, + ); + } + } + + /// Calls [setAllowsInlineMediaPlayback] with the ids of the provided object instances. + Future setAllowsInlineMediaPlaybackForInstances( + WKWebViewConfiguration instance, + bool allow, + ) { + return setAllowsInlineMediaPlayback( + instanceManager.getInstanceId(instance)!, + allow, + ); + } + + /// Calls [setMediaTypesRequiringUserActionForPlayback] with the ids of the provided object instances. + Future setMediaTypesRequiringUserActionForPlaybackForInstances( + WKWebViewConfiguration instance, + Set types, + ) { + return setMediaTypesRequiringUserActionForPlayback( + instanceManager.getInstanceId(instance)!, + _toWKAudiovisualMediaTypeEnumData(types).toList(), + ); + } +} + +/// Host api implementation for [WKUIDelegate]. +class WKUIDelegateHostApiImpl extends WKUIDelegateHostApi { + /// Constructs a [WKUIDelegateHostApiImpl]. + WKUIDelegateHostApiImpl({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? InstanceManager.instance, + super(binaryMessenger: binaryMessenger); + + /// Maintains instances stored to communicate with Objective-C objects. + final InstanceManager instanceManager; + + /// Calls [create] with the ids of the provided object instances. + Future createForInstances(WKUIDelegate instance) async { + final int? instanceId = instanceManager.tryAddInstance(instance); + if (instanceId != null) { + await create(instanceId); + } + } +} + +/// Host api implementation for [WKNavigationDelegate]. +class WKNavigationDelegateHostApiImpl extends WKNavigationDelegateHostApi { + /// Constructs a [WKNavigationDelegateHostApiImpl]. + WKNavigationDelegateHostApiImpl({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? InstanceManager.instance, + super(binaryMessenger: binaryMessenger); + + /// Maintains instances stored to communicate with Objective-C objects. + final InstanceManager instanceManager; + + /// Calls [create] with the ids of the provided object instances. + Future createForInstances(WKNavigationDelegate instance) async { + final int? instanceId = instanceManager.tryAddInstance(instance); + if (instanceId != null) { + await create(instanceId); + } + } +} + +/// Host api implementation for [WKWebView]. +class WKWebViewHostApiImpl extends WKWebViewHostApi { + /// Constructs a [WKWebViewHostApiImpl]. + WKWebViewHostApiImpl({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? InstanceManager.instance, + super(binaryMessenger: binaryMessenger); + + /// Maintains instances stored to communicate with Objective-C objects. + final InstanceManager instanceManager; + + /// Calls [create] with the ids of the provided object instances. + Future createForInstances( + WKWebView instance, + WKWebViewConfiguration configuration, + ) async { + final int? instanceId = instanceManager.tryAddInstance(instance); + if (instanceId != null) { + await create( + instanceId, + instanceManager.getInstanceId(configuration)!, + ); + } + } + + /// Calls [loadRequest] with the ids of the provided object instances. + Future loadRequestForInstances( + WKWebView webView, NSUrlRequest request) { + return loadRequest( + instanceManager.getInstanceId(webView)!, + request.toNSUrlRequestData(), + ); + } + + /// Calls [loadHtmlString] with the ids of the provided object instances. + Future loadHtmlStringForInstances( + WKWebView instance, + String string, + String? baseUrl, + ) { + return loadHtmlString( + instanceManager.getInstanceId(instance)!, + string, + baseUrl, + ); + } + + /// Calls [loadFileUrl] with the ids of the provided object instances. + Future loadFileUrlForInstances( + WKWebView instance, + String url, + String readAccessUrl, + ) { + return loadFileUrl( + instanceManager.getInstanceId(instance)!, + url, + readAccessUrl, + ); + } + + /// Calls [loadFlutterAsset] with the ids of the provided object instances. + Future loadFlutterAssetForInstances(WKWebView instance, String key) { + return loadFlutterAsset( + instanceManager.getInstanceId(instance)!, + key, + ); + } + + /// Calls [canGoBack] with the ids of the provided object instances. + Future canGoBackForInstances(WKWebView instance) { + return canGoBack(instanceManager.getInstanceId(instance)!); + } + + /// Calls [canGoForward] with the ids of the provided object instances. + Future canGoForwardForInstances(WKWebView instance) { + return canGoForward(instanceManager.getInstanceId(instance)!); + } + + /// Calls [goBack] with the ids of the provided object instances. + Future goBackForInstances(WKWebView instance) { + return goBack(instanceManager.getInstanceId(instance)!); + } + + /// Calls [goForward] with the ids of the provided object instances. + Future goForwardForInstances(WKWebView instance) { + return goForward(instanceManager.getInstanceId(instance)!); + } + + /// Calls [reload] with the ids of the provided object instances. + Future reloadForInstances(WKWebView instance) { + return reload(instanceManager.getInstanceId(instance)!); + } + + /// Calls [getUrl] with the ids of the provided object instances. + Future getUrlForInstances(WKWebView instance) { + return getUrl(instanceManager.getInstanceId(instance)!); + } + + /// Calls [getTitle] with the ids of the provided object instances. + Future getTitleForInstances(WKWebView instance) { + return getTitle(instanceManager.getInstanceId(instance)!); + } + + /// Calls [getEstimatedProgress] with the ids of the provided object instances. + Future getEstimatedProgressForInstances(WKWebView instance) { + return getEstimatedProgress(instanceManager.getInstanceId(instance)!); + } + + /// Calls [setAllowsBackForwardNavigationGestures] with the ids of the provided object instances. + Future setAllowsBackForwardNavigationGesturesForInstances( + WKWebView instance, + bool allow, + ) { + return setAllowsBackForwardNavigationGestures( + instanceManager.getInstanceId(instance)!, + allow, + ); + } + + /// Calls [setCustomUserAgent] with the ids of the provided object instances. + Future setCustomUserAgentForInstances( + WKWebView instance, + String? userAgent, + ) { + return setCustomUserAgent( + instanceManager.getInstanceId(instance)!, + userAgent, + ); + } + + /// Calls [evaluateJavaScript] with the ids of the provided object instances. + Future evaluateJavaScriptForInstances( + WKWebView instance, + String javaScriptString, + ) { + return evaluateJavaScript( + instanceManager.getInstanceId(instance)!, + javaScriptString, + ); + } + + /// Calls [setNavigationDelegate] with the ids of the provided object instances. + Future setNavigationDelegateForInstances( + WKWebView instance, + WKNavigationDelegate? delegate, + ) { + return setNavigationDelegate( + instanceManager.getInstanceId(instance)!, + delegate != null ? instanceManager.getInstanceId(delegate)! : null, + ); + } + + /// Calls [setUIDelegate] with the ids of the provided object instances. + Future setUIDelegateForInstances( + WKWebView instance, + WKUIDelegate? delegate, + ) { + return setUIDelegate( + instanceManager.getInstanceId(instance)!, + delegate != null ? instanceManager.getInstanceId(delegate)! : null, + ); + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index d7cbfb4510b2..d63cbef71f9f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -226,7 +226,7 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { @override Future clearCache() { - return webView.configuration.webSiteDataStore.removeDataOfTypes( + return webView.configuration.websiteDataStore.removeDataOfTypes( { WKWebsiteDataTypes.memoryCache, WKWebsiteDataTypes.diskCache, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart new file mode 100644 index 000000000000..0d5328dda8d6 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart @@ -0,0 +1,352 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:pigeon/pigeon.dart'; + +@ConfigurePigeon( + PigeonOptions( + dartOut: 'lib/src/common/web_kit.pigeon.dart', + dartTestOut: 'test/src/common/test_web_kit.pigeon.dart', + dartOptions: DartOptions(isNullSafe: true, copyrightHeader: [ + 'Copyright 2013 The Flutter Authors. All rights reserved.', + 'Use of this source code is governed by a BSD-style license that can be', + 'found in the LICENSE file.', + ]), + ), +) + +/// Mirror of NSKeyValueObservingOptions. +/// +/// See https://developer.apple.com/documentation/foundation/nskeyvalueobservingoptions?language=objc. +enum NSKeyValueObservingOptionsEnum { + newValue, + oldValue, + initialValue, + priorNotification, +} + +class NSKeyValueObservingOptionsEnumData { + // TODO(bparrishMines): Generated code fails when enums are marked as nonnull. + // Change to nonnull once this is fixed: https://github.com/flutter/flutter/issues/100594 + late NSKeyValueObservingOptionsEnum? value; +} + +/// Mirror of NSKeyValueChange. +/// +/// See https://developer.apple.com/documentation/foundation/nskeyvaluechange?language=objc. +enum NSKeyValueChangeEnum { + setting, + insertion, + removal, + replacement, +} + +class NSKeyValueChangeEnumData { + late NSKeyValueChangeEnum? value; +} + +/// Mirror of NSKeyValueChangeKey. +/// +/// See https://developer.apple.com/documentation/foundation/nskeyvaluechangekey?language=objc. +enum NSKeyValueChangeKeyEnum { + indexes, + kind, + newValue, + notificationIsPrior, + oldValue, +} + +class NSKeyValueChangeKeyEnumData { + late NSKeyValueChangeKeyEnum? value; +} + +/// Mirror of WKUserScriptInjectionTime. +/// +/// See https://developer.apple.com/documentation/webkit/wkuserscriptinjectiontime?language=objc. +enum WKUserScriptInjectionTimeEnum { + atDocumentStart, + atDocumentEnd, +} + +class WKUserScriptInjectionTimeEnumData { + late WKUserScriptInjectionTimeEnum? value; +} + +/// Mirror of WKAudiovisualMediaTypes. +/// +/// See [WKAudiovisualMediaTypes](https://developer.apple.com/documentation/webkit/wkaudiovisualmediatypes?language=objc). +enum WKAudiovisualMediaTypeEnum { + none, + audio, + video, + all, +} + +class WKAudiovisualMediaTypeEnumData { + late WKAudiovisualMediaTypeEnum? value; +} + +/// Mirror of WKWebsiteDataTypes. +/// +/// See https://developer.apple.com/documentation/webkit/wkwebsitedatarecord/data_store_record_types?language=objc. +enum WKWebsiteDataTypesEnum { + cookies, + memoryCache, + diskCache, + offlineWebApplicationCache, + localStroage, + sessionStorage, + sqlDatabases, + indexedDBDatabases, +} + +class WKWebsiteDataTypesEnumData { + late WKWebsiteDataTypesEnum? value; +} + +/// Mirror of WKNavigationActionPolicy. +/// +/// See https://developer.apple.com/documentation/webkit/wknavigationactionpolicy?language=objc. +enum WKNavigationActionPolicyEnum { + allow, + cancel, +} + +class WKNavigationActionPolicyEnumData { + late WKNavigationActionPolicyEnum? value; +} + +/// Mirror of NSURLRequest. +/// +/// See https://developer.apple.com/documentation/foundation/nsurlrequest?language=objc. +class NSUrlRequestData { + late String url; + late String? httpMethod; + late Uint8List? httpBody; + late Map allHttpHeaderFields; +} + +/// Mirror of WKUserScript. +/// +/// See https://developer.apple.com/documentation/webkit/wkuserscript?language=objc. +class WKUserScriptData { + late String source; + late WKUserScriptInjectionTimeEnumData? injectionTime; + late bool isMainFrameOnly; +} + +/// Mirror of WKNavigationAction. +/// +/// See https://developer.apple.com/documentation/webkit/wknavigationaction. +class WKNavigationActionData { + late NSUrlRequestData request; + late WKFrameInfoData targetFrame; +} + +/// Mirror of WKFrameInfo. +/// +/// See https://developer.apple.com/documentation/webkit/wkframeinfo?language=objc. +class WKFrameInfoData { + late bool isMainFrame; +} + +/// Mirror of NSError. +/// +/// See https://developer.apple.com/documentation/foundation/nserror?language=objc. +class NSErrorData { + late int code; + late String domain; + late String localiziedDescription; +} + +/// Mirror of WKScriptMessage. +/// +/// See https://developer.apple.com/documentation/webkit/wkscriptmessage?language=objc. +class WKScriptMessageData { + late String name; + late Object? body; +} + +/// Mirror of WKWebsiteDataStore. +/// +/// See https://developer.apple.com/documentation/webkit/wkwebsitedatastore?language=objc. +@HostApi(dartHostTestHandler: 'TestWKWebsiteDataStoreHostApi') +abstract class WKWebsiteDataStoreHostApi { + void createFromWebViewConfiguration( + int instanceId, + int configurationInstanceId, + ); + + @async + void removeDataOfTypes( + int instanceId, + List dataTypes, + double secondsModifiedSinceEpoch, + ); +} + +/// Mirror of UIView. +/// +/// See https://developer.apple.com/documentation/uikit/uiview?language=objc. +@HostApi(dartHostTestHandler: 'TestUIViewHostApi') +abstract class UIViewHostApi { + List getContentOffset(int instanceId); + + void setBackgroundColor(int instanceId, int? value); + + void setOpaque(int instanceId, bool opaque); +} + +/// Mirror of UIScrollView. +/// +/// See https://developer.apple.com/documentation/uikit/uiscrollview?language=objc. +@HostApi(dartHostTestHandler: 'TestUIScrollViewHostApi') +abstract class UIScrollViewHostApi { + void createFromWebView(int instanceId, int webViewInstanceId); + + List getContentOffset(int instanceId); + + void scrollBy(int instanceId, double x, double y); + + void setContentOffset(int instanceId, double x, double y); +} + +/// Mirror of WKWebViewConfiguration. +/// +/// See https://developer.apple.com/documentation/webkit/wkwebviewconfiguration?language=objc. +@HostApi(dartHostTestHandler: 'TestWKWebViewConfigurationHostApi') +abstract class WKWebViewConfigurationHostApi { + void create(int instanceId); + + void createFromWebView(int instanceId, int webViewInstanceId); + + void setAllowsInlineMediaPlayback(int instanceId, bool allow); + + void setMediaTypesRequiringUserActionForPlayback( + int instanceId, + List types, + ); +} + +/// Mirror of WKUserContentController. +/// +/// See https://developer.apple.com/documentation/webkit/wkusercontentcontroller?language=objc. +@HostApi(dartHostTestHandler: 'TestWKUserContentControllerHostApi') +abstract class WKUserContentControllerHostApi { + void createFromWebViewConfiguration( + int instanceId, + int configurationInstanceId, + ); + + void addScriptMessageHandler( + int instanceId, + int handlerInstanceid, + String name, + ); + + void removeScriptMessageHandler(int instanceId, String name); + + void removeAllScriptMessageHandlers(int instanceId); + + void addUserScript(int instanceId, WKUserScriptData userScript); + + void removeAllUserScripts(int instanceId); +} + +/// Mirror of WKUserPreferences. +/// +/// See https://developer.apple.com/documentation/webkit/wkpreferences?language=objc. +@HostApi(dartHostTestHandler: 'TestWKPreferencesHostApi') +abstract class WKPreferencesHostApi { + void createFromWebViewConfiguration( + int instanceId, + int configurationInstanceId, + ); + + void setJavaScriptEnabled(int instanceId, bool enabled); +} + +/// Mirror of WKScriptMessageHandler. +/// +/// See https://developer.apple.com/documentation/webkit/wkscriptmessagehandler?language=objc. +@HostApi(dartHostTestHandler: 'TestWKScriptMessageHandlerHostApi') +abstract class WKScriptMessageHandlerHostApi { + void create(int instanceId); +} + +/// Mirror of WKNavigationDelegate. +/// +/// See https://developer.apple.com/documentation/webkit/wknavigationdelegate?language=objc. +@HostApi(dartHostTestHandler: 'TestWKNavigationDelegateHostApi') +abstract class WKNavigationDelegateHostApi { + void create(int instanceId); +} + +/// Mirror of NSObject. +/// +/// See https://developer.apple.com/documentation/objectivec/nsobject. +@HostApi(dartHostTestHandler: 'TestNSObjectHostApi') +abstract class NSObjectHostApi { + void dispose(int instanceId); + + void addObserver( + int instanceId, + int observerInstanceId, + String keyPath, + List options, + ); + + void removeObserver(int instanceId, int observerInstanceId, String keyPath); +} + +/// Mirror of WKWebView. +/// +/// See https://developer.apple.com/documentation/webkit/wkwebview?language=objc. +@HostApi(dartHostTestHandler: 'TestWKWebViewHostApi') +abstract class WKWebViewHostApi { + void create(int instanceId, int configurationInstanceId); + + void setUIDelegate(int instanceId, int? uiDelegateInstanceId); + + void setNavigationDelegate(int instanceId, int? navigationDelegateInstanceId); + + String? getUrl(int instanceId); + + double getEstimatedProgress(int instanceId); + + void loadRequest(int instanceId, NSUrlRequestData request); + + void loadHtmlString(int instanceId, String string, String? baseUrl); + + void loadFileUrl(int instanceId, String url, String readAccessUrl); + + void loadFlutterAsset(int instanceId, String key); + + bool canGoBack(int instanceId); + + bool canGoForward(int instanceId); + + void goBack(int instanceId); + + void goForward(int instanceId); + + void reload(int instanceId); + + String? getTitle(int instanceId); + + void setAllowsBackForwardNavigationGestures(int instanceId, bool allow); + + void setCustomUserAgent(int instanceId, String? userAgent); + + @async + String evaluateJavaScript(int instanceId, String javascriptString); +} + +/// Mirror of WKUIDelegate. +/// +/// See https://developer.apple.com/documentation/webkit/wkuidelegate?language=objc. +@HostApi(dartHostTestHandler: 'TestWKUIDelegateHostApi') +abstract class WKUIDelegateHostApi { + void create(int instanceId); +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 75e7ccaaf74f..2a4a7a4f674d 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -29,3 +29,4 @@ dev_dependencies: sdk: flutter mockito: ^5.1.0 pedantic: ^1.10.0 + pigeon: ^2.0.3 diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/instance_manager_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/instance_manager_test.dart new file mode 100644 index 000000000000..10956c0a4aba --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/instance_manager_test.dart @@ -0,0 +1,35 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:webview_flutter_wkwebview/src/common/instance_manager.dart'; + +void main() { + group('InstanceManager', () { + late InstanceManager testInstanceManager; + + setUp(() { + testInstanceManager = InstanceManager(); + }); + + test('tryAddInstance', () { + final Object object = Object(); + + expect(testInstanceManager.tryAddInstance(object), 0); + expect(testInstanceManager.getInstanceId(object), 0); + expect(testInstanceManager.getInstance(0), object); + expect(testInstanceManager.tryAddInstance(object), null); + }); + + test('removeInstance', () { + final Object object = Object(); + testInstanceManager.tryAddInstance(object); + + expect(testInstanceManager.removeInstance(object), 0); + expect(testInstanceManager.getInstanceId(object), null); + expect(testInstanceManager.getInstance(0), null); + expect(testInstanceManager.removeInstance(object), null); + }); + }); +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart new file mode 100644 index 000000000000..78e4b9dcfd3d --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart @@ -0,0 +1,1308 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v2.0.3), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis +// ignore_for_file: avoid_relative_lib_imports +// @dart = 2.12 +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; +import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:webview_flutter_wkwebview/src/common/web_kit.pigeon.dart'; + +class _TestWKWebsiteDataStoreHostApiCodec extends StandardMessageCodec { + const _TestWKWebsiteDataStoreHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is WKWebsiteDataTypesEnumData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return WKWebsiteDataTypesEnumData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class TestWKWebsiteDataStoreHostApi { + static const MessageCodec codec = + _TestWKWebsiteDataStoreHostApiCodec(); + + void createFromWebViewConfiguration( + int instanceId, int configurationInstanceId); + Future removeDataOfTypes( + int instanceId, + List dataTypes, + double secondsModifiedSinceEpoch); + static void setup(TestWKWebsiteDataStoreHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebsiteDataStoreHostApi.createFromWebViewConfiguration', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.createFromWebViewConfiguration was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.createFromWebViewConfiguration was null, expected non-null int.'); + final int? arg_configurationInstanceId = (args[1] as int?); + assert(arg_configurationInstanceId != null, + 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.createFromWebViewConfiguration was null, expected non-null int.'); + api.createFromWebViewConfiguration( + arg_instanceId!, arg_configurationInstanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes was null, expected non-null int.'); + final List? arg_dataTypes = + (args[1] as List?)?.cast(); + assert(arg_dataTypes != null, + 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes was null, expected non-null List.'); + final double? arg_secondsModifiedSinceEpoch = (args[2] as double?); + assert(arg_secondsModifiedSinceEpoch != null, + 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes was null, expected non-null double.'); + await api.removeDataOfTypes( + arg_instanceId!, arg_dataTypes!, arg_secondsModifiedSinceEpoch!); + return {}; + }); + } + } + } +} + +class _TestUIViewHostApiCodec extends StandardMessageCodec { + const _TestUIViewHostApiCodec(); +} + +abstract class TestUIViewHostApi { + static const MessageCodec codec = _TestUIViewHostApiCodec(); + + List getContentOffset(int instanceId); + void setBackgroundColor(int instanceId, int? value); + void setOpaque(int instanceId, bool opaque); + static void setup(TestUIViewHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UIViewHostApi.getContentOffset', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.UIViewHostApi.getContentOffset was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.UIViewHostApi.getContentOffset was null, expected non-null int.'); + final List output = api.getContentOffset(arg_instanceId!); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UIViewHostApi.setBackgroundColor', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.UIViewHostApi.setBackgroundColor was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.UIViewHostApi.setBackgroundColor was null, expected non-null int.'); + final int? arg_value = (args[1] as int?); + assert(arg_value != null, + 'Argument for dev.flutter.pigeon.UIViewHostApi.setBackgroundColor was null, expected non-null int.'); + api.setBackgroundColor(arg_instanceId!, arg_value!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UIViewHostApi.setOpaque', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.UIViewHostApi.setOpaque was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.UIViewHostApi.setOpaque was null, expected non-null int.'); + final bool? arg_opaque = (args[1] as bool?); + assert(arg_opaque != null, + 'Argument for dev.flutter.pigeon.UIViewHostApi.setOpaque was null, expected non-null bool.'); + api.setOpaque(arg_instanceId!, arg_opaque!); + return {}; + }); + } + } + } +} + +class _TestUIScrollViewHostApiCodec extends StandardMessageCodec { + const _TestUIScrollViewHostApiCodec(); +} + +abstract class TestUIScrollViewHostApi { + static const MessageCodec codec = _TestUIScrollViewHostApiCodec(); + + void createFromWebView(int instanceId, int webViewInstanceId); + List getContentOffset(int instanceId); + void scrollBy(int instanceId, double x, double y); + void setContentOffset(int instanceId, double x, double y); + static void setup(TestUIScrollViewHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UIScrollViewHostApi.createFromWebView', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.createFromWebView was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.createFromWebView was null, expected non-null int.'); + final int? arg_webViewInstanceId = (args[1] as int?); + assert(arg_webViewInstanceId != null, + 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.createFromWebView was null, expected non-null int.'); + api.createFromWebView(arg_instanceId!, arg_webViewInstanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UIScrollViewHostApi.getContentOffset', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.getContentOffset was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.getContentOffset was null, expected non-null int.'); + final List output = api.getContentOffset(arg_instanceId!); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UIScrollViewHostApi.scrollBy', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.scrollBy was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.scrollBy was null, expected non-null int.'); + final double? arg_x = (args[1] as double?); + assert(arg_x != null, + 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.scrollBy was null, expected non-null double.'); + final double? arg_y = (args[2] as double?); + assert(arg_y != null, + 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.scrollBy was null, expected non-null double.'); + api.scrollBy(arg_instanceId!, arg_x!, arg_y!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UIScrollViewHostApi.setContentOffset', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.setContentOffset was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.setContentOffset was null, expected non-null int.'); + final double? arg_x = (args[1] as double?); + assert(arg_x != null, + 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.setContentOffset was null, expected non-null double.'); + final double? arg_y = (args[2] as double?); + assert(arg_y != null, + 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.setContentOffset was null, expected non-null double.'); + api.setContentOffset(arg_instanceId!, arg_x!, arg_y!); + return {}; + }); + } + } + } +} + +class _TestWKWebViewConfigurationHostApiCodec extends StandardMessageCodec { + const _TestWKWebViewConfigurationHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is WKAudiovisualMediaTypeEnumData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return WKAudiovisualMediaTypeEnumData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class TestWKWebViewConfigurationHostApi { + static const MessageCodec codec = + _TestWKWebViewConfigurationHostApiCodec(); + + void create(int instanceId); + void createFromWebView(int instanceId, int webViewInstanceId); + void setAllowsInlineMediaPlayback(int instanceId, bool allow); + void setMediaTypesRequiringUserActionForPlayback( + int instanceId, List types); + static void setup(TestWKWebViewConfigurationHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewConfigurationHostApi.create', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.create was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.create was null, expected non-null int.'); + api.create(arg_instanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewConfigurationHostApi.createFromWebView', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.createFromWebView was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.createFromWebView was null, expected non-null int.'); + final int? arg_webViewInstanceId = (args[1] as int?); + assert(arg_webViewInstanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.createFromWebView was null, expected non-null int.'); + api.createFromWebView(arg_instanceId!, arg_webViewInstanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewConfigurationHostApi.setAllowsInlineMediaPlayback', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.setAllowsInlineMediaPlayback was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.setAllowsInlineMediaPlayback was null, expected non-null int.'); + final bool? arg_allow = (args[1] as bool?); + assert(arg_allow != null, + 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.setAllowsInlineMediaPlayback was null, expected non-null bool.'); + api.setAllowsInlineMediaPlayback(arg_instanceId!, arg_allow!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewConfigurationHostApi.setMediaTypesRequiringUserActionForPlayback', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.setMediaTypesRequiringUserActionForPlayback was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.setMediaTypesRequiringUserActionForPlayback was null, expected non-null int.'); + final List? arg_types = + (args[1] as List?) + ?.cast(); + assert(arg_types != null, + 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.setMediaTypesRequiringUserActionForPlayback was null, expected non-null List.'); + api.setMediaTypesRequiringUserActionForPlayback( + arg_instanceId!, arg_types!); + return {}; + }); + } + } + } +} + +class _TestWKUserContentControllerHostApiCodec extends StandardMessageCodec { + const _TestWKUserContentControllerHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is WKUserScriptData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is WKUserScriptInjectionTimeEnumData) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return WKUserScriptData.decode(readValue(buffer)!); + + case 129: + return WKUserScriptInjectionTimeEnumData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class TestWKUserContentControllerHostApi { + static const MessageCodec codec = + _TestWKUserContentControllerHostApiCodec(); + + void createFromWebViewConfiguration( + int instanceId, int configurationInstanceId); + void addScriptMessageHandler( + int instanceId, int handlerInstanceid, String name); + void removeScriptMessageHandler(int instanceId, String name); + void removeAllScriptMessageHandlers(int instanceId); + void addUserScript(int instanceId, WKUserScriptData userScript); + void removeAllUserScripts(int instanceId); + static void setup(TestWKUserContentControllerHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUserContentControllerHostApi.createFromWebViewConfiguration', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.createFromWebViewConfiguration was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.createFromWebViewConfiguration was null, expected non-null int.'); + final int? arg_configurationInstanceId = (args[1] as int?); + assert(arg_configurationInstanceId != null, + 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.createFromWebViewConfiguration was null, expected non-null int.'); + api.createFromWebViewConfiguration( + arg_instanceId!, arg_configurationInstanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUserContentControllerHostApi.addScriptMessageHandler', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.addScriptMessageHandler was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.addScriptMessageHandler was null, expected non-null int.'); + final int? arg_handlerInstanceid = (args[1] as int?); + assert(arg_handlerInstanceid != null, + 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.addScriptMessageHandler was null, expected non-null int.'); + final String? arg_name = (args[2] as String?); + assert(arg_name != null, + 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.addScriptMessageHandler was null, expected non-null String.'); + api.addScriptMessageHandler( + arg_instanceId!, arg_handlerInstanceid!, arg_name!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUserContentControllerHostApi.removeScriptMessageHandler', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.removeScriptMessageHandler was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.removeScriptMessageHandler was null, expected non-null int.'); + final String? arg_name = (args[1] as String?); + assert(arg_name != null, + 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.removeScriptMessageHandler was null, expected non-null String.'); + api.removeScriptMessageHandler(arg_instanceId!, arg_name!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUserContentControllerHostApi.removeAllScriptMessageHandlers', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.removeAllScriptMessageHandlers was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.removeAllScriptMessageHandlers was null, expected non-null int.'); + api.removeAllScriptMessageHandlers(arg_instanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUserContentControllerHostApi.addUserScript', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.addUserScript was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.addUserScript was null, expected non-null int.'); + final WKUserScriptData? arg_userScript = + (args[1] as WKUserScriptData?); + assert(arg_userScript != null, + 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.addUserScript was null, expected non-null WKUserScriptData.'); + api.addUserScript(arg_instanceId!, arg_userScript!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUserContentControllerHostApi.removeAllUserScripts', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.removeAllUserScripts was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.removeAllUserScripts was null, expected non-null int.'); + api.removeAllUserScripts(arg_instanceId!); + return {}; + }); + } + } + } +} + +class _TestWKPreferencesHostApiCodec extends StandardMessageCodec { + const _TestWKPreferencesHostApiCodec(); +} + +abstract class TestWKPreferencesHostApi { + static const MessageCodec codec = _TestWKPreferencesHostApiCodec(); + + void createFromWebViewConfiguration( + int instanceId, int configurationInstanceId); + void setJavaScriptEnabled(int instanceId, bool enabled); + static void setup(TestWKPreferencesHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKPreferencesHostApi.createFromWebViewConfiguration', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKPreferencesHostApi.createFromWebViewConfiguration was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKPreferencesHostApi.createFromWebViewConfiguration was null, expected non-null int.'); + final int? arg_configurationInstanceId = (args[1] as int?); + assert(arg_configurationInstanceId != null, + 'Argument for dev.flutter.pigeon.WKPreferencesHostApi.createFromWebViewConfiguration was null, expected non-null int.'); + api.createFromWebViewConfiguration( + arg_instanceId!, arg_configurationInstanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKPreferencesHostApi.setJavaScriptEnabled', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKPreferencesHostApi.setJavaScriptEnabled was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKPreferencesHostApi.setJavaScriptEnabled was null, expected non-null int.'); + final bool? arg_enabled = (args[1] as bool?); + assert(arg_enabled != null, + 'Argument for dev.flutter.pigeon.WKPreferencesHostApi.setJavaScriptEnabled was null, expected non-null bool.'); + api.setJavaScriptEnabled(arg_instanceId!, arg_enabled!); + return {}; + }); + } + } + } +} + +class _TestWKScriptMessageHandlerHostApiCodec extends StandardMessageCodec { + const _TestWKScriptMessageHandlerHostApiCodec(); +} + +abstract class TestWKScriptMessageHandlerHostApi { + static const MessageCodec codec = + _TestWKScriptMessageHandlerHostApiCodec(); + + void create(int instanceId); + static void setup(TestWKScriptMessageHandlerHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKScriptMessageHandlerHostApi.create', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKScriptMessageHandlerHostApi.create was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKScriptMessageHandlerHostApi.create was null, expected non-null int.'); + api.create(arg_instanceId!); + return {}; + }); + } + } + } +} + +class _TestWKNavigationDelegateHostApiCodec extends StandardMessageCodec { + const _TestWKNavigationDelegateHostApiCodec(); +} + +abstract class TestWKNavigationDelegateHostApi { + static const MessageCodec codec = + _TestWKNavigationDelegateHostApiCodec(); + + void create(int instanceId); + static void setup(TestWKNavigationDelegateHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKNavigationDelegateHostApi.create', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateHostApi.create was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateHostApi.create was null, expected non-null int.'); + api.create(arg_instanceId!); + return {}; + }); + } + } + } +} + +class _TestNSObjectHostApiCodec extends StandardMessageCodec { + const _TestNSObjectHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is NSKeyValueObservingOptionsEnumData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return NSKeyValueObservingOptionsEnumData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class TestNSObjectHostApi { + static const MessageCodec codec = _TestNSObjectHostApiCodec(); + + void dispose(int instanceId); + void addObserver(int instanceId, int observerInstanceId, String keyPath, + List options); + void removeObserver(int instanceId, int observerInstanceId, String keyPath); + static void setup(TestNSObjectHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NSObjectHostApi.dispose', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.NSObjectHostApi.dispose was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.NSObjectHostApi.dispose was null, expected non-null int.'); + api.dispose(arg_instanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NSObjectHostApi.addObserver', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.NSObjectHostApi.addObserver was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.NSObjectHostApi.addObserver was null, expected non-null int.'); + final int? arg_observerInstanceId = (args[1] as int?); + assert(arg_observerInstanceId != null, + 'Argument for dev.flutter.pigeon.NSObjectHostApi.addObserver was null, expected non-null int.'); + final String? arg_keyPath = (args[2] as String?); + assert(arg_keyPath != null, + 'Argument for dev.flutter.pigeon.NSObjectHostApi.addObserver was null, expected non-null String.'); + final List? arg_options = + (args[3] as List?) + ?.cast(); + assert(arg_options != null, + 'Argument for dev.flutter.pigeon.NSObjectHostApi.addObserver was null, expected non-null List.'); + api.addObserver(arg_instanceId!, arg_observerInstanceId!, + arg_keyPath!, arg_options!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NSObjectHostApi.removeObserver', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.NSObjectHostApi.removeObserver was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.NSObjectHostApi.removeObserver was null, expected non-null int.'); + final int? arg_observerInstanceId = (args[1] as int?); + assert(arg_observerInstanceId != null, + 'Argument for dev.flutter.pigeon.NSObjectHostApi.removeObserver was null, expected non-null int.'); + final String? arg_keyPath = (args[2] as String?); + assert(arg_keyPath != null, + 'Argument for dev.flutter.pigeon.NSObjectHostApi.removeObserver was null, expected non-null String.'); + api.removeObserver( + arg_instanceId!, arg_observerInstanceId!, arg_keyPath!); + return {}; + }); + } + } + } +} + +class _TestWKWebViewHostApiCodec extends StandardMessageCodec { + const _TestWKWebViewHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is NSUrlRequestData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return NSUrlRequestData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class TestWKWebViewHostApi { + static const MessageCodec codec = _TestWKWebViewHostApiCodec(); + + void create(int instanceId, int configurationInstanceId); + void setUIDelegate(int instanceId, int? uiDelegateInstanceId); + void setNavigationDelegate(int instanceId, int? navigationDelegateInstanceId); + String? getUrl(int instanceId); + double getEstimatedProgress(int instanceId); + void loadRequest(int instanceId, NSUrlRequestData request); + void loadHtmlString(int instanceId, String string, String? baseUrl); + void loadFileUrl(int instanceId, String url, String readAccessUrl); + void loadFlutterAsset(int instanceId, String key); + bool canGoBack(int instanceId); + bool canGoForward(int instanceId); + void goBack(int instanceId); + void goForward(int instanceId); + void reload(int instanceId); + String? getTitle(int instanceId); + void setAllowsBackForwardNavigationGestures(int instanceId, bool allow); + void setCustomUserAgent(int instanceId, String? userAgent); + Future evaluateJavaScript(int instanceId, String javascriptString); + static void setup(TestWKWebViewHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.create', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.create was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.create was null, expected non-null int.'); + final int? arg_configurationInstanceId = (args[1] as int?); + assert(arg_configurationInstanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.create was null, expected non-null int.'); + api.create(arg_instanceId!, arg_configurationInstanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.setUIDelegate', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setUIDelegate was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setUIDelegate was null, expected non-null int.'); + final int? arg_uiDelegateInstanceId = (args[1] as int?); + assert(arg_uiDelegateInstanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setUIDelegate was null, expected non-null int.'); + api.setUIDelegate(arg_instanceId!, arg_uiDelegateInstanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.setNavigationDelegate', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setNavigationDelegate was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setNavigationDelegate was null, expected non-null int.'); + final int? arg_navigationDelegateInstanceId = (args[1] as int?); + assert(arg_navigationDelegateInstanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setNavigationDelegate was null, expected non-null int.'); + api.setNavigationDelegate( + arg_instanceId!, arg_navigationDelegateInstanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.getUrl', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.getUrl was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.getUrl was null, expected non-null int.'); + final String? output = api.getUrl(arg_instanceId!); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.getEstimatedProgress', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.getEstimatedProgress was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.getEstimatedProgress was null, expected non-null int.'); + final double output = api.getEstimatedProgress(arg_instanceId!); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.loadRequest', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadRequest was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadRequest was null, expected non-null int.'); + final NSUrlRequestData? arg_request = (args[1] as NSUrlRequestData?); + assert(arg_request != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadRequest was null, expected non-null NSUrlRequestData.'); + api.loadRequest(arg_instanceId!, arg_request!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.loadHtmlString', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadHtmlString was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadHtmlString was null, expected non-null int.'); + final String? arg_string = (args[1] as String?); + assert(arg_string != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadHtmlString was null, expected non-null String.'); + final String? arg_baseUrl = (args[2] as String?); + assert(arg_baseUrl != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadHtmlString was null, expected non-null String.'); + api.loadHtmlString(arg_instanceId!, arg_string!, arg_baseUrl!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.loadFileUrl', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadFileUrl was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadFileUrl was null, expected non-null int.'); + final String? arg_url = (args[1] as String?); + assert(arg_url != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadFileUrl was null, expected non-null String.'); + final String? arg_readAccessUrl = (args[2] as String?); + assert(arg_readAccessUrl != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadFileUrl was null, expected non-null String.'); + api.loadFileUrl(arg_instanceId!, arg_url!, arg_readAccessUrl!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.loadFlutterAsset', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadFlutterAsset was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadFlutterAsset was null, expected non-null int.'); + final String? arg_key = (args[1] as String?); + assert(arg_key != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadFlutterAsset was null, expected non-null String.'); + api.loadFlutterAsset(arg_instanceId!, arg_key!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.canGoBack', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.canGoBack was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.canGoBack was null, expected non-null int.'); + final bool output = api.canGoBack(arg_instanceId!); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.canGoForward', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.canGoForward was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.canGoForward was null, expected non-null int.'); + final bool output = api.canGoForward(arg_instanceId!); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.goBack', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.goBack was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.goBack was null, expected non-null int.'); + api.goBack(arg_instanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.goForward', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.goForward was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.goForward was null, expected non-null int.'); + api.goForward(arg_instanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.reload', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.reload was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.reload was null, expected non-null int.'); + api.reload(arg_instanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.getTitle', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.getTitle was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.getTitle was null, expected non-null int.'); + final String? output = api.getTitle(arg_instanceId!); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.setAllowsBackForwardNavigationGestures', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setAllowsBackForwardNavigationGestures was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setAllowsBackForwardNavigationGestures was null, expected non-null int.'); + final bool? arg_allow = (args[1] as bool?); + assert(arg_allow != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setAllowsBackForwardNavigationGestures was null, expected non-null bool.'); + api.setAllowsBackForwardNavigationGestures( + arg_instanceId!, arg_allow!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.setCustomUserAgent', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setCustomUserAgent was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setCustomUserAgent was null, expected non-null int.'); + final String? arg_userAgent = (args[1] as String?); + assert(arg_userAgent != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setCustomUserAgent was null, expected non-null String.'); + api.setCustomUserAgent(arg_instanceId!, arg_userAgent!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.evaluateJavaScript', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.evaluateJavaScript was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.evaluateJavaScript was null, expected non-null int.'); + final String? arg_javascriptString = (args[1] as String?); + assert(arg_javascriptString != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.evaluateJavaScript was null, expected non-null String.'); + final String output = await api.evaluateJavaScript( + arg_instanceId!, arg_javascriptString!); + return {'result': output}; + }); + } + } + } +} + +class _TestWKUIDelegateHostApiCodec extends StandardMessageCodec { + const _TestWKUIDelegateHostApiCodec(); +} + +abstract class TestWKUIDelegateHostApi { + static const MessageCodec codec = _TestWKUIDelegateHostApiCodec(); + + void create(int instanceId); + static void setup(TestWKUIDelegateHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUIDelegateHostApi.create', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKUIDelegateHostApi.create was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKUIDelegateHostApi.create was null, expected non-null int.'); + api.create(arg_instanceId!); + return {}; + }); + } + } + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart new file mode 100644 index 000000000000..a08680940bcc --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart @@ -0,0 +1,100 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:webview_flutter_wkwebview/src/common/instance_manager.dart'; +import 'package:webview_flutter_wkwebview/src/common/web_kit.pigeon.dart'; +import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; + +import '../common/test_web_kit.pigeon.dart'; +import 'foundation_test.mocks.dart'; + +@GenerateMocks([ + TestNSObjectHostApi, +]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('Foundation', () { + late InstanceManager instanceManager; + + setUp(() { + instanceManager = InstanceManager(); + }); + + group('$NSObject', () { + late MockTestNSObjectHostApi mockPlatformHostApi; + + late NSObject object; + + setUp(() { + mockPlatformHostApi = MockTestNSObjectHostApi(); + TestNSObjectHostApi.setup(mockPlatformHostApi); + + object = NSObject(instanceManager: instanceManager); + instanceManager.tryAddInstance(object); + }); + + tearDown(() { + TestNSObjectHostApi.setup(null); + }); + + test('addObserver', () async { + final NSObject observer = NSObject(instanceManager: instanceManager); + instanceManager.tryAddInstance(observer); + + await object.addObserver( + observer, + keyPath: 'aKeyPath', + options: { + NSKeyValueObservingOptions.initialValue, + NSKeyValueObservingOptions.priorNotification, + }, + ); + + final List optionsData = + verify(mockPlatformHostApi.addObserver( + instanceManager.getInstanceId(object), + instanceManager.getInstanceId(observer), + 'aKeyPath', + captureAny, + )).captured.single as List; + + expect(optionsData, hasLength(2)); + expect( + optionsData[0]!.value, + NSKeyValueObservingOptionsEnum.initialValue, + ); + expect( + optionsData[1]!.value, + NSKeyValueObservingOptionsEnum.priorNotification, + ); + }); + + test('removeObserver', () async { + final NSObject observer = NSObject(instanceManager: instanceManager); + instanceManager.tryAddInstance(observer); + + await object.removeObserver(observer, keyPath: 'aKeyPath'); + + verify(mockPlatformHostApi.removeObserver( + instanceManager.getInstanceId(object), + instanceManager.getInstanceId(observer), + 'aKeyPath', + )); + }); + + test('dispose', () async { + final int instanceId = instanceManager.getInstanceId(object)!; + + await object.dispose(); + verify( + mockPlatformHostApi.dispose(instanceId), + ); + }); + }); + }); +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.mocks.dart new file mode 100644 index 000000000000..7bd208eeac05 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.mocks.dart @@ -0,0 +1,48 @@ +// Mocks generated by Mockito 5.1.0 from annotations +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart. +// Do not manually edit this file. + +import 'package:mockito/mockito.dart' as _i1; +import 'package:webview_flutter_wkwebview/src/common/web_kit.pigeon.dart' + as _i3; + +import '../common/test_web_kit.pigeon.dart' as _i2; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types + +/// A class which mocks [TestNSObjectHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestNSObjectHostApi extends _i1.Mock + implements _i2.TestNSObjectHostApi { + MockTestNSObjectHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void dispose(int? instanceId) => + super.noSuchMethod(Invocation.method(#dispose, [instanceId]), + returnValueForMissingStub: null); + @override + void addObserver(int? instanceId, int? observerInstanceId, String? keyPath, + List<_i3.NSKeyValueObservingOptionsEnumData?>? options) => + super.noSuchMethod( + Invocation.method( + #addObserver, [instanceId, observerInstanceId, keyPath, options]), + returnValueForMissingStub: null); + @override + void removeObserver( + int? instanceId, int? observerInstanceId, String? keyPath) => + super.noSuchMethod( + Invocation.method( + #removeObserver, [instanceId, observerInstanceId, keyPath]), + returnValueForMissingStub: null); +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart new file mode 100644 index 000000000000..b6c50609552f --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart @@ -0,0 +1,122 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:webview_flutter_wkwebview/src/common/instance_manager.dart'; +import 'package:webview_flutter_wkwebview/src/ui_kit/ui_kit.dart'; +import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart'; + +import '../common/test_web_kit.pigeon.dart'; +import 'ui_kit_test.mocks.dart'; + +@GenerateMocks([ + TestWKWebViewConfigurationHostApi, + TestWKWebViewHostApi, + TestUIScrollViewHostApi, + TestUIViewHostApi, +]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('UIKit', () { + late InstanceManager instanceManager; + + setUp(() { + instanceManager = InstanceManager(); + }); + + group('$UIScrollView', () { + late MockTestUIScrollViewHostApi mockPlatformHostApi; + + late UIScrollView scrollView; + late int scrollViewInstanceId; + + setUp(() { + mockPlatformHostApi = MockTestUIScrollViewHostApi(); + TestUIScrollViewHostApi.setup(mockPlatformHostApi); + + TestWKWebViewConfigurationHostApi.setup( + MockTestWKWebViewConfigurationHostApi(), + ); + TestWKWebViewHostApi.setup(MockTestWKWebViewHostApi()); + final WKWebView webView = WKWebView( + WKWebViewConfiguration(instanceManager: instanceManager), + instanceManager: instanceManager, + ); + + scrollView = UIScrollView.fromWebView( + webView, + instanceManager: instanceManager, + ); + scrollViewInstanceId = instanceManager.getInstanceId(scrollView)!; + }); + + tearDown(() { + TestUIScrollViewHostApi.setup(null); + TestWKWebViewConfigurationHostApi.setup(null); + TestWKWebViewHostApi.setup(null); + }); + + test('getContentOffset', () async { + when(mockPlatformHostApi.getContentOffset(scrollViewInstanceId)) + .thenReturn([4.0, 10.0]); + expect( + scrollView.getContentOffset(), + completion(const Point(4.0, 10.0)), + ); + }); + + test('scrollBy', () async { + await scrollView.scrollBy(const Point(4.0, 10.0)); + verify(mockPlatformHostApi.scrollBy(scrollViewInstanceId, 4.0, 10.0)); + }); + + test('setContentOffset', () async { + await scrollView.setContentOffset(const Point(4.0, 10.0)); + verify(mockPlatformHostApi.setContentOffset( + scrollViewInstanceId, + 4.0, + 10.0, + )); + }); + }); + + group('$UIView', () { + late MockTestUIViewHostApi mockPlatformHostApi; + + late UIView view; + late int viewInstanceId; + + setUp(() { + mockPlatformHostApi = MockTestUIViewHostApi(); + TestUIViewHostApi.setup(mockPlatformHostApi); + + view = UIView(instanceManager: instanceManager); + viewInstanceId = instanceManager.tryAddInstance(view)!; + }); + + tearDown(() { + TestUIViewHostApi.setup(null); + }); + + test('setBackgroundColor', () async { + await view.setBackgroundColor(Colors.red); + verify(mockPlatformHostApi.setBackgroundColor( + viewInstanceId, + Colors.red.value, + )); + }); + + test('setOpaque', () async { + await view.setOpaque(false); + verify(mockPlatformHostApi.setOpaque(viewInstanceId, false)); + }); + }); + }); +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.mocks.dart new file mode 100644 index 000000000000..e876aa63cd29 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.mocks.dart @@ -0,0 +1,200 @@ +// Mocks generated by Mockito 5.1.0 from annotations +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart. +// Do not manually edit this file. + +import 'dart:async' as _i4; + +import 'package:mockito/mockito.dart' as _i1; +import 'package:webview_flutter_wkwebview/src/common/web_kit.pigeon.dart' + as _i3; + +import '../common/test_web_kit.pigeon.dart' as _i2; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types + +/// A class which mocks [TestWKWebViewConfigurationHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestWKWebViewConfigurationHostApi extends _i1.Mock + implements _i2.TestWKWebViewConfigurationHostApi { + MockTestWKWebViewConfigurationHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void create(int? instanceId) => + super.noSuchMethod(Invocation.method(#create, [instanceId]), + returnValueForMissingStub: null); + @override + void createFromWebView(int? instanceId, int? webViewInstanceId) => + super.noSuchMethod( + Invocation.method( + #createFromWebView, [instanceId, webViewInstanceId]), + returnValueForMissingStub: null); + @override + void setAllowsInlineMediaPlayback(int? instanceId, bool? allow) => + super.noSuchMethod( + Invocation.method(#setAllowsInlineMediaPlayback, [instanceId, allow]), + returnValueForMissingStub: null); + @override + void setMediaTypesRequiringUserActionForPlayback( + int? instanceId, List<_i3.WKAudiovisualMediaTypeEnumData?>? types) => + super.noSuchMethod( + Invocation.method(#setMediaTypesRequiringUserActionForPlayback, + [instanceId, types]), + returnValueForMissingStub: null); +} + +/// A class which mocks [TestWKWebViewHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestWKWebViewHostApi extends _i1.Mock + implements _i2.TestWKWebViewHostApi { + MockTestWKWebViewHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void create(int? instanceId, int? configurationInstanceId) => + super.noSuchMethod( + Invocation.method(#create, [instanceId, configurationInstanceId]), + returnValueForMissingStub: null); + @override + void setUIDelegate(int? instanceId, int? uiDelegateInstanceId) => + super.noSuchMethod( + Invocation.method(#setUIDelegate, [instanceId, uiDelegateInstanceId]), + returnValueForMissingStub: null); + @override + void setNavigationDelegate( + int? instanceId, int? navigationDelegateInstanceId) => + super.noSuchMethod( + Invocation.method(#setNavigationDelegate, + [instanceId, navigationDelegateInstanceId]), + returnValueForMissingStub: null); + @override + String? getUrl(int? instanceId) => + (super.noSuchMethod(Invocation.method(#getUrl, [instanceId])) as String?); + @override + double getEstimatedProgress(int? instanceId) => (super.noSuchMethod( + Invocation.method(#getEstimatedProgress, [instanceId]), + returnValue: 0.0) as double); + @override + void loadRequest(int? instanceId, _i3.NSUrlRequestData? request) => + super.noSuchMethod(Invocation.method(#loadRequest, [instanceId, request]), + returnValueForMissingStub: null); + @override + void loadHtmlString(int? instanceId, String? string, String? baseUrl) => + super.noSuchMethod( + Invocation.method(#loadHtmlString, [instanceId, string, baseUrl]), + returnValueForMissingStub: null); + @override + void loadFileUrl(int? instanceId, String? url, String? readAccessUrl) => + super.noSuchMethod( + Invocation.method(#loadFileUrl, [instanceId, url, readAccessUrl]), + returnValueForMissingStub: null); + @override + void loadFlutterAsset(int? instanceId, String? key) => super.noSuchMethod( + Invocation.method(#loadFlutterAsset, [instanceId, key]), + returnValueForMissingStub: null); + @override + bool canGoBack(int? instanceId) => + (super.noSuchMethod(Invocation.method(#canGoBack, [instanceId]), + returnValue: false) as bool); + @override + bool canGoForward(int? instanceId) => + (super.noSuchMethod(Invocation.method(#canGoForward, [instanceId]), + returnValue: false) as bool); + @override + void goBack(int? instanceId) => + super.noSuchMethod(Invocation.method(#goBack, [instanceId]), + returnValueForMissingStub: null); + @override + void goForward(int? instanceId) => + super.noSuchMethod(Invocation.method(#goForward, [instanceId]), + returnValueForMissingStub: null); + @override + void reload(int? instanceId) => + super.noSuchMethod(Invocation.method(#reload, [instanceId]), + returnValueForMissingStub: null); + @override + String? getTitle(int? instanceId) => + (super.noSuchMethod(Invocation.method(#getTitle, [instanceId])) + as String?); + @override + void setAllowsBackForwardNavigationGestures(int? instanceId, bool? allow) => + super.noSuchMethod( + Invocation.method( + #setAllowsBackForwardNavigationGestures, [instanceId, allow]), + returnValueForMissingStub: null); + @override + void setCustomUserAgent(int? instanceId, String? userAgent) => + super.noSuchMethod( + Invocation.method(#setCustomUserAgent, [instanceId, userAgent]), + returnValueForMissingStub: null); + @override + _i4.Future evaluateJavaScript( + int? instanceId, String? javascriptString) => + (super.noSuchMethod( + Invocation.method( + #evaluateJavaScript, [instanceId, javascriptString]), + returnValue: Future.value('')) as _i4.Future); +} + +/// A class which mocks [TestUIScrollViewHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestUIScrollViewHostApi extends _i1.Mock + implements _i2.TestUIScrollViewHostApi { + MockTestUIScrollViewHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void createFromWebView(int? instanceId, int? webViewInstanceId) => + super.noSuchMethod( + Invocation.method( + #createFromWebView, [instanceId, webViewInstanceId]), + returnValueForMissingStub: null); + @override + List getContentOffset(int? instanceId) => + (super.noSuchMethod(Invocation.method(#getContentOffset, [instanceId]), + returnValue: []) as List); + @override + void scrollBy(int? instanceId, double? x, double? y) => + super.noSuchMethod(Invocation.method(#scrollBy, [instanceId, x, y]), + returnValueForMissingStub: null); + @override + void setContentOffset(int? instanceId, double? x, double? y) => super + .noSuchMethod(Invocation.method(#setContentOffset, [instanceId, x, y]), + returnValueForMissingStub: null); +} + +/// A class which mocks [TestUIViewHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestUIViewHostApi extends _i1.Mock implements _i2.TestUIViewHostApi { + MockTestUIViewHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + List getContentOffset(int? instanceId) => + (super.noSuchMethod(Invocation.method(#getContentOffset, [instanceId]), + returnValue: []) as List); + @override + void setBackgroundColor(int? instanceId, int? value) => super.noSuchMethod( + Invocation.method(#setBackgroundColor, [instanceId, value]), + returnValueForMissingStub: null); + @override + void setOpaque(int? instanceId, bool? opaque) => + super.noSuchMethod(Invocation.method(#setOpaque, [instanceId, opaque]), + returnValueForMissingStub: null); +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart new file mode 100644 index 000000000000..d51fa9793dd4 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart @@ -0,0 +1,530 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:webview_flutter_wkwebview/src/common/instance_manager.dart'; +import 'package:webview_flutter_wkwebview/src/common/web_kit.pigeon.dart'; +import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; +import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart'; + +import '../common/test_web_kit.pigeon.dart'; +import 'web_kit_test.mocks.dart'; + +@GenerateMocks([ + TestWKNavigationDelegateHostApi, + TestWKPreferencesHostApi, + TestWKScriptMessageHandlerHostApi, + TestWKUIDelegateHostApi, + TestWKUserContentControllerHostApi, + TestWKWebViewConfigurationHostApi, + TestWKWebViewHostApi, + TestWKWebsiteDataStoreHostApi, +]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('WebKit', () { + late InstanceManager instanceManager; + + setUp(() { + instanceManager = InstanceManager(); + }); + + group('$WKWebsiteDataStore', () { + late MockTestWKWebsiteDataStoreHostApi mockPlatformHostApi; + + late WKWebsiteDataStore websiteDataStore; + + late WKWebViewConfiguration webViewConfiguration; + + setUp(() { + mockPlatformHostApi = MockTestWKWebsiteDataStoreHostApi(); + TestWKWebsiteDataStoreHostApi.setup(mockPlatformHostApi); + + TestWKWebViewConfigurationHostApi.setup( + MockTestWKWebViewConfigurationHostApi(), + ); + webViewConfiguration = WKWebViewConfiguration( + instanceManager: instanceManager, + ); + + websiteDataStore = WKWebsiteDataStore.fromWebViewConfiguration( + webViewConfiguration, + instanceManager: instanceManager, + ); + }); + + tearDown(() { + TestWKWebsiteDataStoreHostApi.setup(null); + TestWKWebViewConfigurationHostApi.setup(null); + }); + + test('createFromWebViewConfiguration', () { + verify(mockPlatformHostApi.createFromWebViewConfiguration( + instanceManager.getInstanceId(websiteDataStore), + instanceManager.getInstanceId(webViewConfiguration), + )); + }); + + test('removeDataOfTypes', () { + websiteDataStore.removeDataOfTypes( + {WKWebsiteDataTypes.cookies}, + DateTime.fromMillisecondsSinceEpoch(5000), + ); + + final WKWebsiteDataTypesEnumData typeData = + verify(mockPlatformHostApi.removeDataOfTypes( + instanceManager.getInstanceId(websiteDataStore), + captureAny, + 5.0, + )).captured.single.single as WKWebsiteDataTypesEnumData; + + expect(typeData.value, WKWebsiteDataTypesEnum.cookies); + }); + }); + + group('$WKScriptMessageHandler', () { + late MockTestWKScriptMessageHandlerHostApi mockPlatformHostApi; + + late WKScriptMessageHandler scriptMessageHandler; + + setUp(() async { + mockPlatformHostApi = MockTestWKScriptMessageHandlerHostApi(); + TestWKScriptMessageHandlerHostApi.setup(mockPlatformHostApi); + + scriptMessageHandler = WKScriptMessageHandler( + instanceManager: instanceManager, + ); + }); + + tearDown(() { + TestWKScriptMessageHandlerHostApi.setup(null); + }); + + test('create', () async { + verify(mockPlatformHostApi.create( + instanceManager.getInstanceId(scriptMessageHandler), + )); + }); + }); + + group('$WKPreferences', () { + late MockTestWKPreferencesHostApi mockPlatformHostApi; + + late WKPreferences preferences; + + late WKWebViewConfiguration webViewConfiguration; + + setUp(() { + mockPlatformHostApi = MockTestWKPreferencesHostApi(); + TestWKPreferencesHostApi.setup(mockPlatformHostApi); + + TestWKWebViewConfigurationHostApi.setup( + MockTestWKWebViewConfigurationHostApi(), + ); + webViewConfiguration = WKWebViewConfiguration( + instanceManager: instanceManager, + ); + + preferences = WKPreferences.fromWebViewConfiguration( + webViewConfiguration, + instanceManager: instanceManager, + ); + }); + + tearDown(() { + TestWKPreferencesHostApi.setup(null); + TestWKWebViewConfigurationHostApi.setup(null); + }); + + test('createFromWebViewConfiguration', () async { + verify(mockPlatformHostApi.createFromWebViewConfiguration( + instanceManager.getInstanceId(preferences), + instanceManager.getInstanceId(webViewConfiguration), + )); + }); + + test('setJavaScriptEnabled', () async { + await preferences.setJavaScriptEnabled(true); + verify(mockPlatformHostApi.setJavaScriptEnabled( + instanceManager.getInstanceId(preferences), + true, + )); + }); + }); + + group('$WKUserContentController', () { + late MockTestWKUserContentControllerHostApi mockPlatformHostApi; + + late WKUserContentController userContentController; + + late WKWebViewConfiguration webViewConfiguration; + + setUp(() { + mockPlatformHostApi = MockTestWKUserContentControllerHostApi(); + TestWKUserContentControllerHostApi.setup(mockPlatformHostApi); + + TestWKWebViewConfigurationHostApi.setup( + MockTestWKWebViewConfigurationHostApi(), + ); + webViewConfiguration = WKWebViewConfiguration( + instanceManager: instanceManager, + ); + + userContentController = + WKUserContentController.fromWebViewConfiguration( + webViewConfiguration, + instanceManager: instanceManager, + ); + }); + + tearDown(() { + TestWKUserContentControllerHostApi.setup(null); + TestWKWebViewConfigurationHostApi.setup(null); + }); + + test('createFromWebViewConfiguration', () async { + verify(mockPlatformHostApi.createFromWebViewConfiguration( + instanceManager.getInstanceId(userContentController), + instanceManager.getInstanceId(webViewConfiguration), + )); + }); + + test('addScriptMessageHandler', () async { + TestWKScriptMessageHandlerHostApi.setup( + MockTestWKScriptMessageHandlerHostApi(), + ); + final WKScriptMessageHandler handler = WKScriptMessageHandler( + instanceManager: instanceManager, + ); + + userContentController.addScriptMessageHandler(handler, 'handlerName'); + verify(mockPlatformHostApi.addScriptMessageHandler( + instanceManager.getInstanceId(userContentController), + instanceManager.getInstanceId(handler), + 'handlerName', + )); + }); + + test('removeScriptMessageHandler', () async { + userContentController.removeScriptMessageHandler('handlerName'); + verify(mockPlatformHostApi.removeScriptMessageHandler( + instanceManager.getInstanceId(userContentController), + 'handlerName', + )); + }); + + test('removeAllScriptMessageHandlers', () async { + userContentController.removeAllScriptMessageHandlers(); + verify(mockPlatformHostApi.removeAllScriptMessageHandlers( + instanceManager.getInstanceId(userContentController), + )); + }); + + test('addUserScript', () { + userContentController.addUserScript(const WKUserScript( + 'aScript', + WKUserScriptInjectionTime.atDocumentEnd, + isMainFrameOnly: false, + )); + verify(mockPlatformHostApi.addUserScript( + instanceManager.getInstanceId(userContentController), + argThat(isA()), + )); + }); + + test('removeAllUserScripts', () { + userContentController.removeAllUserScripts(); + verify(mockPlatformHostApi.removeAllUserScripts( + instanceManager.getInstanceId(userContentController), + )); + }); + }); + + group('$WKWebViewConfiguration', () { + late MockTestWKWebViewConfigurationHostApi mockPlatformHostApi; + + late WKWebViewConfiguration webViewConfiguration; + + setUp(() async { + mockPlatformHostApi = MockTestWKWebViewConfigurationHostApi(); + TestWKWebViewConfigurationHostApi.setup(mockPlatformHostApi); + + webViewConfiguration = WKWebViewConfiguration( + instanceManager: instanceManager, + ); + }); + + tearDown(() { + TestWKWebViewConfigurationHostApi.setup(null); + }); + + test('create', () async { + verify( + mockPlatformHostApi.create(instanceManager.getInstanceId( + webViewConfiguration, + )), + ); + }); + + test('createFromWebView', () async { + TestWKWebViewHostApi.setup(MockTestWKWebViewHostApi()); + final WKWebView webView = WKWebView( + webViewConfiguration, + instanceManager: instanceManager, + ); + + final WKWebViewConfiguration configurationFromWebView = + WKWebViewConfiguration.fromWebView( + webView, + instanceManager: instanceManager, + ); + verify(mockPlatformHostApi.createFromWebView( + instanceManager.getInstanceId(configurationFromWebView)!, + instanceManager.getInstanceId(webView)!, + )); + }); + + test('allowsInlineMediaPlayback', () { + webViewConfiguration.setAllowsInlineMediaPlayback(true); + verify(mockPlatformHostApi.setAllowsInlineMediaPlayback( + instanceManager.getInstanceId(webViewConfiguration), + true, + )); + }); + + test('mediaTypesRequiringUserActionForPlayback', () { + webViewConfiguration.setMediaTypesRequiringUserActionForPlayback( + { + WKAudiovisualMediaType.audio, + WKAudiovisualMediaType.video, + }, + ); + + final List typeData = verify( + mockPlatformHostApi.setMediaTypesRequiringUserActionForPlayback( + instanceManager.getInstanceId(webViewConfiguration), + captureAny, + )).captured.single as List; + + expect(typeData, hasLength(2)); + expect(typeData[0]!.value, WKAudiovisualMediaTypeEnum.audio); + expect(typeData[1]!.value, WKAudiovisualMediaTypeEnum.video); + }); + }); + + group('$WKNavigationDelegate', () { + late MockTestWKNavigationDelegateHostApi mockPlatformHostApi; + + late WKNavigationDelegate navigationDelegate; + + setUp(() async { + mockPlatformHostApi = MockTestWKNavigationDelegateHostApi(); + TestWKNavigationDelegateHostApi.setup(mockPlatformHostApi); + + navigationDelegate = WKNavigationDelegate( + instanceManager: instanceManager, + ); + }); + + tearDown(() { + TestWKNavigationDelegateHostApi.setup(null); + }); + + test('create', () async { + verify(mockPlatformHostApi.create( + instanceManager.getInstanceId(navigationDelegate), + )); + }); + }); + + group('$WKWebView', () { + late MockTestWKWebViewHostApi mockPlatformHostApi; + + late WKWebViewConfiguration webViewConfiguration; + + late WKWebView webView; + late int webViewInstanceId; + + setUp(() { + mockPlatformHostApi = MockTestWKWebViewHostApi(); + TestWKWebViewHostApi.setup(mockPlatformHostApi); + + TestWKWebViewConfigurationHostApi.setup( + MockTestWKWebViewConfigurationHostApi()); + webViewConfiguration = WKWebViewConfiguration( + instanceManager: instanceManager, + ); + + webView = WKWebView( + webViewConfiguration, + instanceManager: instanceManager, + ); + webViewInstanceId = instanceManager.getInstanceId(webView)!; + }); + + tearDown(() { + TestWKWebViewHostApi.setup(null); + TestWKWebViewConfigurationHostApi.setup(null); + }); + + test('create', () async { + verify(mockPlatformHostApi.create( + instanceManager.getInstanceId(webView), + instanceManager.getInstanceId( + webViewConfiguration, + ), + )); + }); + + test('setUIDelegate', () async { + TestWKUIDelegateHostApi.setup(MockTestWKUIDelegateHostApi()); + final WKUIDelegate uiDelegate = WKUIDelegate( + instanceManager: instanceManager, + ); + + await webView.setUIDelegate(uiDelegate); + verify(mockPlatformHostApi.setUIDelegate( + webViewInstanceId, + instanceManager.getInstanceId(uiDelegate), + )); + + TestWKUIDelegateHostApi.setup(null); + }); + + test('setNavigationDelegate', () async { + TestWKNavigationDelegateHostApi.setup( + MockTestWKNavigationDelegateHostApi(), + ); + final WKNavigationDelegate navigationDelegate = WKNavigationDelegate( + instanceManager: instanceManager, + ); + + await webView.setNavigationDelegate(navigationDelegate); + verify(mockPlatformHostApi.setNavigationDelegate( + webViewInstanceId, + instanceManager.getInstanceId(navigationDelegate), + )); + + TestWKNavigationDelegateHostApi.setup(null); + }); + + test('getUrl', () { + when( + mockPlatformHostApi.getUrl(webViewInstanceId), + ).thenReturn('www.flutter.dev'); + expect(webView.getUrl(), completion('www.flutter.dev')); + }); + + test('getEstimatedProgress', () { + when( + mockPlatformHostApi.getEstimatedProgress(webViewInstanceId), + ).thenReturn(54.5); + expect(webView.getEstimatedProgress(), completion(54.5)); + }); + + test('loadRequest', () { + webView.loadRequest(const NSUrlRequest(url: 'www.flutter.dev')); + verify(mockPlatformHostApi.loadRequest( + webViewInstanceId, + argThat(isA()), + )); + }); + + test('loadHtmlString', () { + webView.loadHtmlString('a', baseUrl: 'b'); + verify(mockPlatformHostApi.loadHtmlString(webViewInstanceId, 'a', 'b')); + }); + + test('loadFileUrl', () { + webView.loadFileUrl('a', readAccessUrl: 'b'); + verify(mockPlatformHostApi.loadFileUrl(webViewInstanceId, 'a', 'b')); + }); + + test('loadFlutterAsset', () { + webView.loadFlutterAsset('a'); + verify(mockPlatformHostApi.loadFlutterAsset(webViewInstanceId, 'a')); + }); + + test('canGoBack', () { + when(mockPlatformHostApi.canGoBack(webViewInstanceId)).thenReturn(true); + expect(webView.canGoBack(), completion(isTrue)); + }); + + test('canGoForward', () { + when(mockPlatformHostApi.canGoForward(webViewInstanceId)) + .thenReturn(false); + expect(webView.canGoForward(), completion(isFalse)); + }); + + test('goBack', () { + webView.goBack(); + verify(mockPlatformHostApi.goBack(webViewInstanceId)); + }); + + test('goForward', () { + webView.goForward(); + verify(mockPlatformHostApi.goForward(webViewInstanceId)); + }); + + test('reload', () { + webView.reload(); + verify(mockPlatformHostApi.reload(webViewInstanceId)); + }); + + test('getTitle', () { + when(mockPlatformHostApi.getTitle(webViewInstanceId)) + .thenReturn('MyTitle'); + expect(webView.getTitle(), completion('MyTitle')); + }); + + test('setAllowsBackForwardNavigationGestures', () { + webView.setAllowsBackForwardNavigationGestures(false); + verify(mockPlatformHostApi.setAllowsBackForwardNavigationGestures( + webViewInstanceId, + false, + )); + }); + + test('customUserAgent', () { + webView.setCustomUserAgent('hello'); + verify(mockPlatformHostApi.setCustomUserAgent( + webViewInstanceId, + 'hello', + )); + }); + + test('evaluateJavaScript', () { + when(mockPlatformHostApi.evaluateJavaScript(webViewInstanceId, 'gogo')) + .thenAnswer((_) => Future.value('stopstop')); + expect(webView.evaluateJavaScript('gogo'), completion('stopstop')); + }); + }); + + group('$WKUIDelegate', () { + late MockTestWKUIDelegateHostApi mockPlatformHostApi; + + late WKUIDelegate uiDelegate; + + setUp(() async { + mockPlatformHostApi = MockTestWKUIDelegateHostApi(); + TestWKUIDelegateHostApi.setup(mockPlatformHostApi); + + uiDelegate = WKUIDelegate(instanceManager: instanceManager); + }); + + tearDown(() { + TestWKUIDelegateHostApi.setup(null); + }); + + test('create', () async { + verify(mockPlatformHostApi.create( + instanceManager.getInstanceId(uiDelegate), + )); + }); + }); + }); +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart new file mode 100644 index 000000000000..a2f16317f1fe --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart @@ -0,0 +1,287 @@ +// Mocks generated by Mockito 5.1.0 from annotations +// in webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart. +// Do not manually edit this file. + +import 'dart:async' as _i4; + +import 'package:mockito/mockito.dart' as _i1; +import 'package:webview_flutter_wkwebview/src/common/web_kit.pigeon.dart' + as _i3; + +import '../common/test_web_kit.pigeon.dart' as _i2; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types + +/// A class which mocks [TestWKNavigationDelegateHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestWKNavigationDelegateHostApi extends _i1.Mock + implements _i2.TestWKNavigationDelegateHostApi { + MockTestWKNavigationDelegateHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void create(int? instanceId) => + super.noSuchMethod(Invocation.method(#create, [instanceId]), + returnValueForMissingStub: null); +} + +/// A class which mocks [TestWKPreferencesHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestWKPreferencesHostApi extends _i1.Mock + implements _i2.TestWKPreferencesHostApi { + MockTestWKPreferencesHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void createFromWebViewConfiguration( + int? instanceId, int? configurationInstanceId) => + super.noSuchMethod( + Invocation.method(#createFromWebViewConfiguration, + [instanceId, configurationInstanceId]), + returnValueForMissingStub: null); + @override + void setJavaScriptEnabled(int? instanceId, bool? enabled) => + super.noSuchMethod( + Invocation.method(#setJavaScriptEnabled, [instanceId, enabled]), + returnValueForMissingStub: null); +} + +/// A class which mocks [TestWKScriptMessageHandlerHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestWKScriptMessageHandlerHostApi extends _i1.Mock + implements _i2.TestWKScriptMessageHandlerHostApi { + MockTestWKScriptMessageHandlerHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void create(int? instanceId) => + super.noSuchMethod(Invocation.method(#create, [instanceId]), + returnValueForMissingStub: null); +} + +/// A class which mocks [TestWKUIDelegateHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestWKUIDelegateHostApi extends _i1.Mock + implements _i2.TestWKUIDelegateHostApi { + MockTestWKUIDelegateHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void create(int? instanceId) => + super.noSuchMethod(Invocation.method(#create, [instanceId]), + returnValueForMissingStub: null); +} + +/// A class which mocks [TestWKUserContentControllerHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestWKUserContentControllerHostApi extends _i1.Mock + implements _i2.TestWKUserContentControllerHostApi { + MockTestWKUserContentControllerHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void createFromWebViewConfiguration( + int? instanceId, int? configurationInstanceId) => + super.noSuchMethod( + Invocation.method(#createFromWebViewConfiguration, + [instanceId, configurationInstanceId]), + returnValueForMissingStub: null); + @override + void addScriptMessageHandler( + int? instanceId, int? handlerInstanceid, String? name) => + super.noSuchMethod( + Invocation.method( + #addScriptMessageHandler, [instanceId, handlerInstanceid, name]), + returnValueForMissingStub: null); + @override + void removeScriptMessageHandler(int? instanceId, String? name) => + super.noSuchMethod( + Invocation.method(#removeScriptMessageHandler, [instanceId, name]), + returnValueForMissingStub: null); + @override + void removeAllScriptMessageHandlers(int? instanceId) => super.noSuchMethod( + Invocation.method(#removeAllScriptMessageHandlers, [instanceId]), + returnValueForMissingStub: null); + @override + void addUserScript(int? instanceId, _i3.WKUserScriptData? userScript) => super + .noSuchMethod(Invocation.method(#addUserScript, [instanceId, userScript]), + returnValueForMissingStub: null); + @override + void removeAllUserScripts(int? instanceId) => + super.noSuchMethod(Invocation.method(#removeAllUserScripts, [instanceId]), + returnValueForMissingStub: null); +} + +/// A class which mocks [TestWKWebViewConfigurationHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestWKWebViewConfigurationHostApi extends _i1.Mock + implements _i2.TestWKWebViewConfigurationHostApi { + MockTestWKWebViewConfigurationHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void create(int? instanceId) => + super.noSuchMethod(Invocation.method(#create, [instanceId]), + returnValueForMissingStub: null); + @override + void createFromWebView(int? instanceId, int? webViewInstanceId) => + super.noSuchMethod( + Invocation.method( + #createFromWebView, [instanceId, webViewInstanceId]), + returnValueForMissingStub: null); + @override + void setAllowsInlineMediaPlayback(int? instanceId, bool? allow) => + super.noSuchMethod( + Invocation.method(#setAllowsInlineMediaPlayback, [instanceId, allow]), + returnValueForMissingStub: null); + @override + void setMediaTypesRequiringUserActionForPlayback( + int? instanceId, List<_i3.WKAudiovisualMediaTypeEnumData?>? types) => + super.noSuchMethod( + Invocation.method(#setMediaTypesRequiringUserActionForPlayback, + [instanceId, types]), + returnValueForMissingStub: null); +} + +/// A class which mocks [TestWKWebViewHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestWKWebViewHostApi extends _i1.Mock + implements _i2.TestWKWebViewHostApi { + MockTestWKWebViewHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void create(int? instanceId, int? configurationInstanceId) => + super.noSuchMethod( + Invocation.method(#create, [instanceId, configurationInstanceId]), + returnValueForMissingStub: null); + @override + void setUIDelegate(int? instanceId, int? uiDelegateInstanceId) => + super.noSuchMethod( + Invocation.method(#setUIDelegate, [instanceId, uiDelegateInstanceId]), + returnValueForMissingStub: null); + @override + void setNavigationDelegate( + int? instanceId, int? navigationDelegateInstanceId) => + super.noSuchMethod( + Invocation.method(#setNavigationDelegate, + [instanceId, navigationDelegateInstanceId]), + returnValueForMissingStub: null); + @override + String? getUrl(int? instanceId) => + (super.noSuchMethod(Invocation.method(#getUrl, [instanceId])) as String?); + @override + double getEstimatedProgress(int? instanceId) => (super.noSuchMethod( + Invocation.method(#getEstimatedProgress, [instanceId]), + returnValue: 0.0) as double); + @override + void loadRequest(int? instanceId, _i3.NSUrlRequestData? request) => + super.noSuchMethod(Invocation.method(#loadRequest, [instanceId, request]), + returnValueForMissingStub: null); + @override + void loadHtmlString(int? instanceId, String? string, String? baseUrl) => + super.noSuchMethod( + Invocation.method(#loadHtmlString, [instanceId, string, baseUrl]), + returnValueForMissingStub: null); + @override + void loadFileUrl(int? instanceId, String? url, String? readAccessUrl) => + super.noSuchMethod( + Invocation.method(#loadFileUrl, [instanceId, url, readAccessUrl]), + returnValueForMissingStub: null); + @override + void loadFlutterAsset(int? instanceId, String? key) => super.noSuchMethod( + Invocation.method(#loadFlutterAsset, [instanceId, key]), + returnValueForMissingStub: null); + @override + bool canGoBack(int? instanceId) => + (super.noSuchMethod(Invocation.method(#canGoBack, [instanceId]), + returnValue: false) as bool); + @override + bool canGoForward(int? instanceId) => + (super.noSuchMethod(Invocation.method(#canGoForward, [instanceId]), + returnValue: false) as bool); + @override + void goBack(int? instanceId) => + super.noSuchMethod(Invocation.method(#goBack, [instanceId]), + returnValueForMissingStub: null); + @override + void goForward(int? instanceId) => + super.noSuchMethod(Invocation.method(#goForward, [instanceId]), + returnValueForMissingStub: null); + @override + void reload(int? instanceId) => + super.noSuchMethod(Invocation.method(#reload, [instanceId]), + returnValueForMissingStub: null); + @override + String? getTitle(int? instanceId) => + (super.noSuchMethod(Invocation.method(#getTitle, [instanceId])) + as String?); + @override + void setAllowsBackForwardNavigationGestures(int? instanceId, bool? allow) => + super.noSuchMethod( + Invocation.method( + #setAllowsBackForwardNavigationGestures, [instanceId, allow]), + returnValueForMissingStub: null); + @override + void setCustomUserAgent(int? instanceId, String? userAgent) => + super.noSuchMethod( + Invocation.method(#setCustomUserAgent, [instanceId, userAgent]), + returnValueForMissingStub: null); + @override + _i4.Future evaluateJavaScript( + int? instanceId, String? javascriptString) => + (super.noSuchMethod( + Invocation.method( + #evaluateJavaScript, [instanceId, javascriptString]), + returnValue: Future.value('')) as _i4.Future); +} + +/// A class which mocks [TestWKWebsiteDataStoreHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestWKWebsiteDataStoreHostApi extends _i1.Mock + implements _i2.TestWKWebsiteDataStoreHostApi { + MockTestWKWebsiteDataStoreHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void createFromWebViewConfiguration( + int? instanceId, int? configurationInstanceId) => + super.noSuchMethod( + Invocation.method(#createFromWebViewConfiguration, + [instanceId, configurationInstanceId]), + returnValueForMissingStub: null); + @override + _i4.Future removeDataOfTypes( + int? instanceId, + List<_i3.WKWebsiteDataTypesEnumData?>? dataTypes, + double? secondsModifiedSinceEpoch) => + (super.noSuchMethod( + Invocation.method(#removeDataOfTypes, + [instanceId, dataTypes, secondsModifiedSinceEpoch]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index 5d36e593fadb..0c36f6b7de5a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -76,7 +76,7 @@ void main() { when(mockWebView.scrollView).thenReturn(mockScrollView); - when(mockWebViewConfiguration.webSiteDataStore).thenReturn( + when(mockWebViewConfiguration.websiteDataStore).thenReturn( mockWebsiteDataStore, ); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index 8c8758d5139c..6c34e0c72004 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.1.0 from annotations -// in webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. // Do not manually edit this file. import 'dart:async' as _i5; @@ -37,13 +37,13 @@ class _FakeWKWebViewConfiguration_1 extends _i1.Fake class _FakeUIScrollView_2 extends _i1.Fake implements _i4.UIScrollView {} -class _FakeWKWebsiteDataStore_3 extends _i1.Fake - implements _i3.WKWebsiteDataStore {} - -class _FakeWKUserContentController_4 extends _i1.Fake +class _FakeWKUserContentController_3 extends _i1.Fake implements _i3.WKUserContentController {} -class _FakeWKPreferences_5 extends _i1.Fake implements _i3.WKPreferences {} +class _FakeWKPreferences_4 extends _i1.Fake implements _i3.WKPreferences {} + +class _FakeWKWebsiteDataStore_5 extends _i1.Fake + implements _i3.WKWebsiteDataStore {} class _FakeWKWebView_6 extends _i1.Fake implements _i3.WKWebView {} @@ -69,18 +69,50 @@ class MockUIScrollView extends _i1.Mock implements _i4.UIScrollView { returnValue: Future<_i2.Point>.value(_FakePoint_0())) as _i5.Future<_i2.Point>); @override + _i5.Future scrollBy(_i2.Point? offset) => + (super.noSuchMethod(Invocation.method(#scrollBy, [offset]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future setContentOffset(_i2.Point? offset) => + (super.noSuchMethod(Invocation.method(#setContentOffset, [offset]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override _i5.Future setBackgroundColor(_i6.Color? color) => (super.noSuchMethod(Invocation.method(#setBackgroundColor, [color]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i5.Future scrollBy(_i2.Point? offset) => - (super.noSuchMethod(Invocation.method(#scrollBy, [offset]), + _i5.Future setOpaque(bool? opaque) => + (super.noSuchMethod(Invocation.method(#setOpaque, [opaque]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i5.Future setContentOffset(_i5.FutureOr<_i2.Point>? offset) => - (super.noSuchMethod(Invocation.method(#setContentOffset, [offset]), + _i5.Future addObserver(_i7.NSObject? observer, + {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) => + (super.noSuchMethod( + Invocation.method( + #addObserver, [observer], {#keyPath: keyPath, #options: options}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future removeObserver(_i7.NSObject? observer, {String? keyPath}) => + (super.noSuchMethod( + Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future dispose() => + (super.noSuchMethod(Invocation.method(#dispose, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future setObserveValue( + void Function( + String, _i7.NSObject, Map<_i7.NSKeyValueChangeKey, Object?>)? + observeValue) => + (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i5.Future); } @@ -306,6 +338,11 @@ class MockWKWebView extends _i1.Mock implements _i3.WKWebView { returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i5.Future); @override + _i5.Future dispose() => + (super.noSuchMethod(Invocation.method(#dispose, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override _i5.Future setObserveValue( void Function( String, _i7.NSObject, Map<_i7.NSKeyValueChangeKey, Object?>)? @@ -324,26 +361,19 @@ class MockWKWebViewConfiguration extends _i1.Mock _i1.throwOnMissingStub(this); } - @override - _i3.WKWebsiteDataStore get webSiteDataStore => - (super.noSuchMethod(Invocation.getter(#webSiteDataStore), - returnValue: _FakeWKWebsiteDataStore_3()) as _i3.WKWebsiteDataStore); @override _i3.WKUserContentController get userContentController => (super.noSuchMethod(Invocation.getter(#userContentController), - returnValue: _FakeWKUserContentController_4()) + returnValue: _FakeWKUserContentController_3()) as _i3.WKUserContentController); @override _i3.WKPreferences get preferences => (super.noSuchMethod(Invocation.getter(#preferences), - returnValue: _FakeWKPreferences_5()) as _i3.WKPreferences); + returnValue: _FakeWKPreferences_4()) as _i3.WKPreferences); @override - _i5.Future setWebSiteDataStore( - _i3.WKWebsiteDataStore? websiteDataStore) => - (super.noSuchMethod( - Invocation.method(#setWebSiteDataStore, [websiteDataStore]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + _i3.WKWebsiteDataStore get websiteDataStore => + (super.noSuchMethod(Invocation.getter(#websiteDataStore), + returnValue: _FakeWKWebsiteDataStore_5()) as _i3.WKWebsiteDataStore); @override _i5.Future setAllowsInlineMediaPlayback(bool? allow) => (super .noSuchMethod(Invocation.method(#setAllowsInlineMediaPlayback, [allow]), From 3e44799597d7cd36cbf4d65435454faf7a2bdd62 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 7 Apr 2022 14:06:10 -0400 Subject: [PATCH 352/600] Roll Flutter from f5f9ad915e55 to c7d2935077fb (1 revision) (#5197) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 0001767b4015..c23046b108f7 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -f5f9ad915e554283ccff109fed969d9ef255fb91 +c7d2935077fb46d18fe7986f472c5347044256f7 From 7013dbd45efaee6e194211f429a90a57b1778bb2 Mon Sep 17 00:00:00 2001 From: BeMacized Date: Thu, 7 Apr 2022 21:51:10 +0200 Subject: [PATCH 353/600] [local_auth_platform_interface] Export externally used types from local_auth_platform_interface.dart directly. (#5196) --- .../local_auth/local_auth_platform_interface/CHANGELOG.md | 4 ++++ .../lib/local_auth_platform_interface.dart | 6 +++--- .../local_auth_platform_interface/lib/types/types.dart | 7 +++++++ .../local_auth/local_auth_platform_interface/pubspec.yaml | 2 +- 4 files changed, 15 insertions(+), 4 deletions(-) create mode 100644 packages/local_auth/local_auth_platform_interface/lib/types/types.dart diff --git a/packages/local_auth/local_auth_platform_interface/CHANGELOG.md b/packages/local_auth/local_auth_platform_interface/CHANGELOG.md index 0d8803f93540..d16a91ee259a 100644 --- a/packages/local_auth/local_auth_platform_interface/CHANGELOG.md +++ b/packages/local_auth/local_auth_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.1 + +* Export externally used types from local_auth_platform_interface.dart directly. + ## 1.0.0 * Initial release. diff --git a/packages/local_auth/local_auth_platform_interface/lib/local_auth_platform_interface.dart b/packages/local_auth/local_auth_platform_interface/lib/local_auth_platform_interface.dart index b909ee90d12b..de652b20f462 100644 --- a/packages/local_auth/local_auth_platform_interface/lib/local_auth_platform_interface.dart +++ b/packages/local_auth/local_auth_platform_interface/lib/local_auth_platform_interface.dart @@ -3,11 +3,11 @@ // found in the LICENSE file. import 'package:local_auth_platform_interface/default_method_channel_platform.dart'; -import 'package:local_auth_platform_interface/types/auth_messages.dart'; -import 'package:local_auth_platform_interface/types/auth_options.dart'; -import 'package:local_auth_platform_interface/types/biometric_type.dart'; +import 'package:local_auth_platform_interface/types/types.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +export 'package:local_auth_platform_interface/types/types.dart'; + /// The interface that implementations of local_auth must implement. /// /// Platform implementations should extend this class rather than implement it as `local_auth` diff --git a/packages/local_auth/local_auth_platform_interface/lib/types/types.dart b/packages/local_auth/local_auth_platform_interface/lib/types/types.dart new file mode 100644 index 000000000000..ea43b942cffd --- /dev/null +++ b/packages/local_auth/local_auth_platform_interface/lib/types/types.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +export 'auth_messages.dart'; +export 'auth_options.dart'; +export 'biometric_type.dart'; diff --git a/packages/local_auth/local_auth_platform_interface/pubspec.yaml b/packages/local_auth/local_auth_platform_interface/pubspec.yaml index f04268926ebd..cd12a8a2dcab 100644 --- a/packages/local_auth/local_auth_platform_interface/pubspec.yaml +++ b/packages/local_auth/local_auth_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/l issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 1.0.0 +version: 1.0.1 environment: sdk: ">=2.14.0 <3.0.0" From 6db6c925525b23647268bf4b88b6e0737d8c8ec5 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 7 Apr 2022 16:31:12 -0400 Subject: [PATCH 354/600] Roll Flutter from c7d2935077fb to 64f7bf7e2ffd (1 revision) (#5198) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index c23046b108f7..67e6e123190a 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c7d2935077fb46d18fe7986f472c5347044256f7 +64f7bf7e2ffd1f56c3185f344ee8bc07aad0e094 From 1647add69e489831d673f8554f2df5285105129e Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 7 Apr 2022 17:36:12 -0400 Subject: [PATCH 355/600] Roll Flutter from 64f7bf7e2ffd to b528310f589b (2 revisions) (#5200) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 67e6e123190a..40f762f991f1 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -64f7bf7e2ffd1f56c3185f344ee8bc07aad0e094 +b528310f589b162ec9760903ea30943ea8f38171 From 21f2e8301f64dd1d9c12bbbd1efb4b9aa9a993e2 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 7 Apr 2022 19:56:09 -0400 Subject: [PATCH 356/600] Roll Flutter from b528310f589b to b0de14ce07c0 (4 revisions) (#5202) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 40f762f991f1..3fa2f83271a4 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b528310f589b162ec9760903ea30943ea8f38171 +b0de14ce07c00760c74d20bbe23c69a5d4a09b6b From 07de4d02343678303ba429ada58af7d147df6fc6 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 7 Apr 2022 22:06:09 -0400 Subject: [PATCH 357/600] Roll Flutter from b0de14ce07c0 to e3f8b50fd523 (5 revisions) (#5205) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 3fa2f83271a4..4af947ec5f05 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b0de14ce07c00760c74d20bbe23c69a5d4a09b6b +e3f8b50fd52342adb14db30a246211779677e70f From 9434dd4ef3dd246aaa702b6837b3aaf216f566f4 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 8 Apr 2022 08:56:05 -0400 Subject: [PATCH 358/600] Roll Flutter from e3f8b50fd523 to 8e259c0fa32b (1 revision) (#5206) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 4af947ec5f05..527d7ec008f1 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -e3f8b50fd52342adb14db30a246211779677e70f +8e259c0fa32b67c681c3ba167978cfcf3054dcd9 From 45327fbf3e920ae2825217fc5ce93d7a753eab1e Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 8 Apr 2022 10:01:08 -0400 Subject: [PATCH 359/600] Roll Flutter from 8e259c0fa32b to 221235c7ed1a (5 revisions) (#5208) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 527d7ec008f1..12c984204c82 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -8e259c0fa32b67c681c3ba167978cfcf3054dcd9 +221235c7ed1aad465b4c4a6c2d6277c954df5ccc From 4aa77f977cab42514d56a01d52004921c30e3601 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 8 Apr 2022 12:26:11 -0400 Subject: [PATCH 360/600] Roll Flutter from 221235c7ed1a to b63c4a6db091 (1 revision) (#5210) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 12c984204c82..17261e759419 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -221235c7ed1aad465b4c4a6c2d6277c954df5ccc +b63c4a6db091a6bb47d5e0e0b32fa5b5b410b28e From f5083a817fc8357b07c0c86690eac2d2997176dd Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 8 Apr 2022 13:31:09 -0400 Subject: [PATCH 361/600] Roll Flutter from b63c4a6db091 to 2979ff6f1342 (1 revision) (#5211) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 17261e759419..61a5368c1a4f 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b63c4a6db091a6bb47d5e0e0b32fa5b5b410b28e +2979ff6f13429a37d3c7b2083029b71b0d413738 From ad146f15b77837271b0aff28a5e283328a3d5999 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 8 Apr 2022 14:36:09 -0400 Subject: [PATCH 362/600] Roll Flutter from 2979ff6f1342 to 3109073fd9f9 (1 revision) (#5212) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 61a5368c1a4f..6da5863d9f74 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2979ff6f13429a37d3c7b2083029b71b0d413738 +3109073fd9f9219d81ba20d5cf77ec12e708b53f From 3376571d03846a76ff503767b016a1c4e27e9153 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 8 Apr 2022 20:12:10 -0400 Subject: [PATCH 363/600] Roll Flutter from 3109073fd9f9 to cae740b9032f (1 revision) (#5213) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 6da5863d9f74..9ae85077f980 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -3109073fd9f9219d81ba20d5cf77ec12e708b53f +cae740b9032f528dddc7978671b918210070daae From a733da89f1a82eb3da39d44fcffabf1506918a3a Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 8 Apr 2022 21:19:11 -0400 Subject: [PATCH 364/600] Roll Flutter from cae740b9032f to c14ca6d32112 (8 revisions) (#5216) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 9ae85077f980..2b0996dde1ba 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -cae740b9032f528dddc7978671b918210070daae +c14ca6d32112cfd7801347ac25ebc1b6863ff118 From d2cbf8ae82a8155b1b8a1be84d365005cabaad82 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 8 Apr 2022 22:24:12 -0400 Subject: [PATCH 365/600] Roll Flutter from c14ca6d32112 to c1227319ab95 (2 revisions) (#5217) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 2b0996dde1ba..9f6b26b73348 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c14ca6d32112cfd7801347ac25ebc1b6863ff118 +c1227319ab95a45743bc2bc4af9546f3c1d9636c From eab13bb6107ce654ee07b113cc62f77e76f84a69 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 9 Apr 2022 02:34:07 -0400 Subject: [PATCH 366/600] Roll Flutter from c1227319ab95 to 8ff3640c9f04 (3 revisions) (#5220) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 9f6b26b73348..37994874d17d 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c1227319ab95a45743bc2bc4af9546f3c1d9636c +8ff3640c9f047ec87a86f3ab4dc7081c788979a4 From 1600a9002417484e495dccf30959ed0d5c8bae24 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 9 Apr 2022 05:49:10 -0400 Subject: [PATCH 367/600] Roll Flutter from 8ff3640c9f04 to 074ee4b2f446 (1 revision) (#5221) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 37994874d17d..baae0a825831 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -8ff3640c9f047ec87a86f3ab4dc7081c788979a4 +074ee4b2f44621840d4a3ba9a68b7093246cdf0d From 700fba5b1bc74d98924f2a41e4eaa7dfb44fe96c Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 9 Apr 2022 06:54:07 -0400 Subject: [PATCH 368/600] Roll Flutter from 074ee4b2f446 to dd73a32c0ce8 (1 revision) (#5222) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index baae0a825831..0dfcd9c461d1 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -074ee4b2f44621840d4a3ba9a68b7093246cdf0d +dd73a32c0ce81cdbcfa80a53ed9baa0f624645e6 From dd2cd964a136b2191ec7887004ac0ddf0657ee89 Mon Sep 17 00:00:00 2001 From: BeMacized Date: Mon, 11 Apr 2022 19:49:09 +0200 Subject: [PATCH 369/600] [local_auth] Update app facing package to use default method channel implementation in new platform interface package. (#5195) --- packages/local_auth/local_auth/CHANGELOG.md | 1 + .../local_auth/lib/auth_strings.dart | 7 +- .../local_auth/local_auth/lib/local_auth.dart | 95 ++--- packages/local_auth/local_auth/pubspec.yaml | 9 +- .../local_auth/test/local_auth_test.dart | 360 ++++++------------ 5 files changed, 168 insertions(+), 304 deletions(-) diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index a2f93cad6286..1ca9fe146c7b 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -1,6 +1,7 @@ ## NEXT * Adds OS version support information to README. +* Switches over to default method implementation in new platform interface. ## 1.1.11 diff --git a/packages/local_auth/local_auth/lib/auth_strings.dart b/packages/local_auth/local_auth/lib/auth_strings.dart index 3e34659b8dad..585742ac68c2 100644 --- a/packages/local_auth/local_auth/lib/auth_strings.dart +++ b/packages/local_auth/local_auth/lib/auth_strings.dart @@ -9,11 +9,12 @@ // ignore_for_file: public_member_api_docs import 'package:intl/intl.dart'; +import 'package:local_auth_platform_interface/types/auth_messages.dart'; /// Android side authentication messages. /// /// Provides default values for all messages. -class AndroidAuthMessages { +class AndroidAuthMessages extends AuthMessages { const AndroidAuthMessages({ this.biometricHint, this.biometricNotRecognized, @@ -38,6 +39,7 @@ class AndroidAuthMessages { final String? goToSettingsDescription; final String? signInTitle; + @override Map get args { return { 'biometricHint': biometricHint ?? androidBiometricHint, @@ -62,7 +64,7 @@ class AndroidAuthMessages { /// iOS side authentication messages. /// /// Provides default values for all messages. -class IOSAuthMessages { +class IOSAuthMessages extends AuthMessages { const IOSAuthMessages({ this.lockOut, this.goToSettingsButton, @@ -77,6 +79,7 @@ class IOSAuthMessages { final String? cancelButton; final String? localizedFallbackTitle; + @override Map get args { return { 'lockOut': lockOut ?? iOSLockOut, diff --git a/packages/local_auth/local_auth/lib/local_auth.dart b/packages/local_auth/local_auth/lib/local_auth.dart index 3e925c00e5ae..32818b31783d 100644 --- a/packages/local_auth/local_auth/lib/local_auth.dart +++ b/packages/local_auth/local_auth/lib/local_auth.dart @@ -12,14 +12,11 @@ import 'dart:async'; import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter/services.dart'; +import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; import 'package:platform/platform.dart'; - import 'auth_strings.dart'; -import 'error_codes.dart'; - -enum BiometricType { face, fingerprint, iris } -const MethodChannel _channel = MethodChannel('plugins.flutter.io/local_auth'); +export 'package:local_auth_platform_interface/types/biometric_type.dart'; Platform _platform = const LocalPlatform(); @@ -31,7 +28,7 @@ void setMockPathProviderPlatform(Platform platform) { /// A Flutter plugin for authenticating the user identity locally. class LocalAuthentication { /// The `authenticateWithBiometrics` method has been deprecated. - /// Use `authenticate` with `biometricOnly: true` instead + /// Use `authenticate` with `biometricOnly: true` instead. @Deprecated('Use `authenticate` with `biometricOnly: true` instead') Future authenticateWithBiometrics({ required String localizedReason, @@ -41,21 +38,21 @@ class LocalAuthentication { IOSAuthMessages iOSAuthStrings = const IOSAuthMessages(), bool sensitiveTransaction = true, }) => - authenticate( + LocalAuthPlatform.instance.authenticate( localizedReason: localizedReason, - useErrorDialogs: useErrorDialogs, - stickyAuth: stickyAuth, - androidAuthStrings: androidAuthStrings, - iOSAuthStrings: iOSAuthStrings, - sensitiveTransaction: sensitiveTransaction, - biometricOnly: true, + authMessages: [iOSAuthStrings, androidAuthStrings], + options: AuthenticationOptions( + useErrorDialogs: useErrorDialogs, + stickyAuth: stickyAuth, + sensitiveTransaction: sensitiveTransaction, + biometricOnly: true, + ), ); /// Authenticates the user with biometrics available on the device while also /// allowing the user to use device authentication - pin, pattern, passcode. /// - /// Returns a [Future] holding true, if the user successfully authenticated, - /// false otherwise. + /// Returns true, if the user successfully authenticated, false otherwise. /// /// [localizedReason] is the message to show to user while prompting them /// for authentication. This is typically along the lines of: 'Please scan @@ -99,29 +96,17 @@ class LocalAuthentication { IOSAuthMessages iOSAuthStrings = const IOSAuthMessages(), bool sensitiveTransaction = true, bool biometricOnly = false, - }) async { - assert(localizedReason.isNotEmpty); - - final Map args = { - 'localizedReason': localizedReason, - 'useErrorDialogs': useErrorDialogs, - 'stickyAuth': stickyAuth, - 'sensitiveTransaction': sensitiveTransaction, - 'biometricOnly': biometricOnly, - }; - if (_platform.isIOS) { - args.addAll(iOSAuthStrings.args); - } else if (_platform.isAndroid) { - args.addAll(androidAuthStrings.args); - } else { - throw PlatformException( - code: otherOperatingSystem, - message: 'Local authentication does not support non-Android/iOS ' - 'operating systems.', - details: 'Your operating system is ${_platform.operatingSystem}', - ); - } - return (await _channel.invokeMethod('authenticate', args)) ?? false; + }) { + return LocalAuthPlatform.instance.authenticate( + localizedReason: localizedReason, + authMessages: [iOSAuthStrings, androidAuthStrings], + options: AuthenticationOptions( + useErrorDialogs: useErrorDialogs, + stickyAuth: stickyAuth, + sensitiveTransaction: sensitiveTransaction, + biometricOnly: biometricOnly, + ), + ); } /// Returns true if auth was cancelled successfully. @@ -131,7 +116,7 @@ class LocalAuthentication { /// Returns [Future] bool true or false: Future stopAuthentication() async { if (_platform.isAndroid) { - return await _channel.invokeMethod('stopAuthentication') ?? false; + return LocalAuthPlatform.instance.stopAuthentication(); } return true; } @@ -139,16 +124,15 @@ class LocalAuthentication { /// Returns true if device is capable of checking biometrics /// /// Returns a [Future] bool true or false: - Future get canCheckBiometrics async => - (await _channel.invokeListMethod('getAvailableBiometrics'))! - .isNotEmpty; + Future get canCheckBiometrics => + LocalAuthPlatform.instance.deviceSupportsBiometrics(); /// Returns true if device is capable of checking biometrics or is able to /// fail over to device credentials. /// /// Returns a [Future] bool true or false: Future isDeviceSupported() async => - (await _channel.invokeMethod('isDeviceSupported')) ?? false; + LocalAuthPlatform.instance.isDeviceSupported(); /// Returns a list of enrolled biometrics /// @@ -156,27 +140,6 @@ class LocalAuthentication { /// - BiometricType.face /// - BiometricType.fingerprint /// - BiometricType.iris (not yet implemented) - Future> getAvailableBiometrics() async { - final List result = (await _channel.invokeListMethod( - 'getAvailableBiometrics', - )) ?? - []; - final List biometrics = []; - for (final String value in result) { - switch (value) { - case 'face': - biometrics.add(BiometricType.face); - break; - case 'fingerprint': - biometrics.add(BiometricType.fingerprint); - break; - case 'iris': - biometrics.add(BiometricType.iris); - break; - case 'undefined': - break; - } - } - return biometrics; - } + Future> getAvailableBiometrics() => + LocalAuthPlatform.instance.getEnrolledBiometrics(); } diff --git a/packages/local_auth/local_auth/pubspec.yaml b/packages/local_auth/local_auth/pubspec.yaml index 78c79f4abce4..5716eae9546b 100644 --- a/packages/local_auth/local_auth/pubspec.yaml +++ b/packages/local_auth/local_auth/pubspec.yaml @@ -5,9 +5,13 @@ repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/loc issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 version: 1.1.11 +# Temporarily disable publishing to allow moving Android and iOS +# implementations. +publish_to: none + environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: @@ -23,6 +27,7 @@ dependencies: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.1 intl: ^0.17.0 + local_auth_platform_interface: ^1.0.1 platform: ^3.0.0 dev_dependencies: @@ -32,4 +37,4 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.10.0 + mockito: ^5.1.0 diff --git a/packages/local_auth/local_auth/test/local_auth_test.dart b/packages/local_auth/local_auth/test/local_auth_test.dart index 3de9758f9d0c..b92297d90231 100644 --- a/packages/local_auth/local_auth/test/local_auth_test.dart +++ b/packages/local_auth/local_auth/test/local_auth_test.dart @@ -2,255 +2,147 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; - -import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:local_auth/auth_strings.dart'; import 'package:local_auth/local_auth.dart'; +import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; +import 'package:local_auth_platform_interface/types/auth_messages.dart'; +import 'package:local_auth_platform_interface/types/auth_options.dart'; +import 'package:mockito/mockito.dart'; import 'package:platform/platform.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; void main() { - TestWidgetsFlutterBinding.ensureInitialized(); + WidgetsFlutterBinding.ensureInitialized(); + late LocalAuthentication localAuthentication; + late MockLocalAuthPlatform mockLocalAuthPlatform; + + setUp(() { + localAuthentication = LocalAuthentication(); + mockLocalAuthPlatform = MockLocalAuthPlatform(); + LocalAuthPlatform.instance = mockLocalAuthPlatform; + }); + + test('authenticateWithBiometrics calls platform implementation', () { + when(mockLocalAuthPlatform.authenticate( + localizedReason: anyNamed('localizedReason'), + authMessages: anyNamed('authMessages'), + options: anyNamed('options'), + )).thenAnswer((_) async => true); + localAuthentication.authenticateWithBiometrics( + localizedReason: 'Test Reason'); + verify(mockLocalAuthPlatform.authenticate( + localizedReason: 'Test Reason', + authMessages: [ + const IOSAuthMessages(), + const AndroidAuthMessages(), + ], + options: const AuthenticationOptions(biometricOnly: true), + )).called(1); + }); + + test('authenticate calls platform implementation', () { + when(mockLocalAuthPlatform.authenticate( + localizedReason: anyNamed('localizedReason'), + authMessages: anyNamed('authMessages'), + options: anyNamed('options'), + )).thenAnswer((_) async => true); + localAuthentication.authenticate(localizedReason: 'Test Reason'); + verify(mockLocalAuthPlatform.authenticate( + localizedReason: 'Test Reason', + authMessages: [ + const IOSAuthMessages(), + const AndroidAuthMessages(), + ], + options: const AuthenticationOptions(), + )).called(1); + }); - group('LocalAuth', () { - const MethodChannel channel = MethodChannel( - 'plugins.flutter.io/local_auth', - ); + test('isDeviceSupported calls platform implementation', () { + when(mockLocalAuthPlatform.isDeviceSupported()) + .thenAnswer((_) async => true); + localAuthentication.isDeviceSupported(); + verify(mockLocalAuthPlatform.isDeviceSupported()).called(1); + }); - final List log = []; - late LocalAuthentication localAuthentication; + test('getEnrolledBiometrics calls platform implementation', () { + when(mockLocalAuthPlatform.getEnrolledBiometrics()) + .thenAnswer((_) async => []); + localAuthentication.getAvailableBiometrics(); + verify(mockLocalAuthPlatform.getEnrolledBiometrics()).called(1); + }); - setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) { - log.add(methodCall); - return Future.value(true); - }); - localAuthentication = LocalAuthentication(); - log.clear(); - }); + test('stopAuthentication calls platform implementation on Android', () { + when(mockLocalAuthPlatform.stopAuthentication()) + .thenAnswer((_) async => true); + setMockPathProviderPlatform(FakePlatform(operatingSystem: 'android')); + localAuthentication.stopAuthentication(); + verify(mockLocalAuthPlatform.stopAuthentication()).called(1); + }); - group('With device auth fail over', () { - test('authenticate with no args on Android.', () async { - setMockPathProviderPlatform(FakePlatform(operatingSystem: 'android')); - await localAuthentication.authenticate( - localizedReason: 'Needs secure', - biometricOnly: true, - ); - expect( - log, - [ - isMethodCall( - 'authenticate', - arguments: { - 'localizedReason': 'Needs secure', - 'useErrorDialogs': true, - 'stickyAuth': false, - 'sensitiveTransaction': true, - 'biometricOnly': true, - 'biometricHint': androidBiometricHint, - 'biometricNotRecognized': androidBiometricNotRecognized, - 'biometricSuccess': androidBiometricSuccess, - 'biometricRequired': androidBiometricRequiredTitle, - 'cancelButton': androidCancelButton, - 'deviceCredentialsRequired': - androidDeviceCredentialsRequiredTitle, - 'deviceCredentialsSetupDescription': - androidDeviceCredentialsSetupDescription, - 'goToSetting': goToSettings, - 'goToSettingDescription': androidGoToSettingsDescription, - 'signInTitle': androidSignInTitle, - }, - ), - ], - ); - }); + test('stopAuthentication does not call platform implementation on iOS', () { + setMockPathProviderPlatform(FakePlatform(operatingSystem: 'ios')); + localAuthentication.stopAuthentication(); + verifyNever(mockLocalAuthPlatform.stopAuthentication()); + }); - test('authenticate with no args on iOS.', () async { - setMockPathProviderPlatform(FakePlatform(operatingSystem: 'ios')); - await localAuthentication.authenticate( - localizedReason: 'Needs secure', - biometricOnly: true, - ); - expect( - log, - [ - isMethodCall('authenticate', arguments: { - 'localizedReason': 'Needs secure', - 'useErrorDialogs': true, - 'stickyAuth': false, - 'sensitiveTransaction': true, - 'biometricOnly': true, - 'lockOut': iOSLockOut, - 'goToSetting': goToSettings, - 'goToSettingDescriptionIOS': iOSGoToSettingsDescription, - 'okButton': iOSOkButton, - }), - ], - ); - }); + test('canCheckBiometrics returns correct result', () async { + when(mockLocalAuthPlatform.deviceSupportsBiometrics()) + .thenAnswer((_) async => false); + bool? result; + result = await localAuthentication.canCheckBiometrics; + expect(result, false); + when(mockLocalAuthPlatform.deviceSupportsBiometrics()) + .thenAnswer((_) async => true); + result = await localAuthentication.canCheckBiometrics; + expect(result, true); + verify(mockLocalAuthPlatform.deviceSupportsBiometrics()).called(2); + }); +} - test('authenticate with `localizedFallbackTitle` on iOS.', () async { - const IOSAuthMessages iosAuthMessages = - IOSAuthMessages(localizedFallbackTitle: 'Enter PIN'); - setMockPathProviderPlatform(FakePlatform(operatingSystem: 'ios')); - await localAuthentication.authenticate( - localizedReason: 'Needs secure', - biometricOnly: true, - iOSAuthStrings: iosAuthMessages, - ); - expect( - log, - [ - isMethodCall('authenticate', arguments: { - 'localizedReason': 'Needs secure', - 'useErrorDialogs': true, - 'stickyAuth': false, - 'sensitiveTransaction': true, - 'biometricOnly': true, - 'lockOut': iOSLockOut, - 'goToSetting': goToSettings, - 'goToSettingDescriptionIOS': iOSGoToSettingsDescription, - 'okButton': iOSOkButton, - 'localizedFallbackTitle': 'Enter PIN', - }), - ], - ); - }); +class MockLocalAuthPlatform extends Mock + with MockPlatformInterfaceMixin + implements LocalAuthPlatform { + MockLocalAuthPlatform() { + throwOnMissingStub(this); + } - test('authenticate with no localizedReason on iOS.', () async { - setMockPathProviderPlatform(FakePlatform(operatingSystem: 'ios')); - await expectLater( - localAuthentication.authenticate( - localizedReason: '', - biometricOnly: true, - ), - throwsAssertionError, - ); - }); + @override + Future authenticate({ + String? localizedReason, + Iterable? authMessages = const [ + IOSAuthMessages(), + AndroidAuthMessages() + ], + AuthenticationOptions? options = const AuthenticationOptions(), + }) => + super.noSuchMethod( + Invocation.method(#authenticate, [], { + #localizedReason: localizedReason, + #authMessages: authMessages, + #options: options, + }), + returnValue: Future.value(false)) as Future; - test('authenticate with no sensitive transaction.', () async { - setMockPathProviderPlatform(FakePlatform(operatingSystem: 'android')); - await localAuthentication.authenticate( - localizedReason: 'Insecure', - sensitiveTransaction: false, - useErrorDialogs: false, - biometricOnly: true, - ); - expect( - log, - [ - isMethodCall('authenticate', arguments: { - 'localizedReason': 'Insecure', - 'useErrorDialogs': false, - 'stickyAuth': false, - 'sensitiveTransaction': false, - 'biometricOnly': true, - 'biometricHint': androidBiometricHint, - 'biometricNotRecognized': androidBiometricNotRecognized, - 'biometricSuccess': androidBiometricSuccess, - 'biometricRequired': androidBiometricRequiredTitle, - 'cancelButton': androidCancelButton, - 'deviceCredentialsRequired': - androidDeviceCredentialsRequiredTitle, - 'deviceCredentialsSetupDescription': - androidDeviceCredentialsSetupDescription, - 'goToSetting': goToSettings, - 'goToSettingDescription': androidGoToSettingsDescription, - 'signInTitle': androidSignInTitle, - }), - ], - ); - }); - }); + @override + Future> getEnrolledBiometrics() => + super.noSuchMethod(Invocation.method(#getEnrolledBiometrics, []), + returnValue: Future>.value([])) + as Future>; - group('With biometrics only', () { - test('authenticate with no args on Android.', () async { - setMockPathProviderPlatform(FakePlatform(operatingSystem: 'android')); - await localAuthentication.authenticate( - localizedReason: 'Needs secure', - ); - expect( - log, - [ - isMethodCall('authenticate', arguments: { - 'localizedReason': 'Needs secure', - 'useErrorDialogs': true, - 'stickyAuth': false, - 'sensitiveTransaction': true, - 'biometricOnly': false, - 'biometricHint': androidBiometricHint, - 'biometricNotRecognized': androidBiometricNotRecognized, - 'biometricSuccess': androidBiometricSuccess, - 'biometricRequired': androidBiometricRequiredTitle, - 'cancelButton': androidCancelButton, - 'deviceCredentialsRequired': - androidDeviceCredentialsRequiredTitle, - 'deviceCredentialsSetupDescription': - androidDeviceCredentialsSetupDescription, - 'goToSetting': goToSettings, - 'goToSettingDescription': androidGoToSettingsDescription, - 'signInTitle': androidSignInTitle, - }), - ], - ); - }); + @override + Future isDeviceSupported() => + super.noSuchMethod(Invocation.method(#isDeviceSupported, []), + returnValue: Future.value(false)) as Future; - test('authenticate with no args on iOS.', () async { - setMockPathProviderPlatform(FakePlatform(operatingSystem: 'ios')); - await localAuthentication.authenticate( - localizedReason: 'Needs secure', - ); - expect( - log, - [ - isMethodCall('authenticate', arguments: { - 'localizedReason': 'Needs secure', - 'useErrorDialogs': true, - 'stickyAuth': false, - 'sensitiveTransaction': true, - 'biometricOnly': false, - 'lockOut': iOSLockOut, - 'goToSetting': goToSettings, - 'goToSettingDescriptionIOS': iOSGoToSettingsDescription, - 'okButton': iOSOkButton, - }), - ], - ); - }); + @override + Future stopAuthentication() => + super.noSuchMethod(Invocation.method(#stopAuthentication, []), + returnValue: Future.value(false)) as Future; - test('authenticate with no sensitive transaction.', () async { - setMockPathProviderPlatform(FakePlatform(operatingSystem: 'android')); - await localAuthentication.authenticate( - localizedReason: 'Insecure', - sensitiveTransaction: false, - useErrorDialogs: false, - ); - expect( - log, - [ - isMethodCall('authenticate', arguments: { - 'localizedReason': 'Insecure', - 'useErrorDialogs': false, - 'stickyAuth': false, - 'sensitiveTransaction': false, - 'biometricOnly': false, - 'biometricHint': androidBiometricHint, - 'biometricNotRecognized': androidBiometricNotRecognized, - 'biometricSuccess': androidBiometricSuccess, - 'biometricRequired': androidBiometricRequiredTitle, - 'cancelButton': androidCancelButton, - 'deviceCredentialsRequired': - androidDeviceCredentialsRequiredTitle, - 'deviceCredentialsSetupDescription': - androidDeviceCredentialsSetupDescription, - 'goToSetting': goToSettings, - 'goToSettingDescription': androidGoToSettingsDescription, - 'signInTitle': androidSignInTitle, - }), - ], - ); - }); - }); - }); + @override + Future deviceSupportsBiometrics() => super.noSuchMethod( + Invocation.method(#deviceSupportsBiometrics, []), + returnValue: Future.value(false)) as Future; } From e187f9d5ec7adab8a1c59cc164b5b801abe8a4cc Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 11 Apr 2022 14:29:11 -0400 Subject: [PATCH 370/600] Roll Flutter from dd73a32c0ce8 to 61a86b5bee38 (2 revisions) (#5224) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 0dfcd9c461d1..da189c4029e3 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -dd73a32c0ce81cdbcfa80a53ed9baa0f624645e6 +61a86b5bee381bb001dd7f3c9baab30a97c50c15 From fdcf76c8278df1e3ccd37e1ba9b9102039bd1de8 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 11 Apr 2022 16:39:12 -0400 Subject: [PATCH 371/600] Roll Flutter from 61a86b5bee38 to b08647376976 (18 revisions) (#5227) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index da189c4029e3..1da60dd836a4 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -61a86b5bee381bb001dd7f3c9baab30a97c50c15 +b0864737697694dacfcee7e506f13667c00889d6 From ceed798f297d2f0b7ad9ee742b0968837274a75e Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 11 Apr 2022 18:09:10 -0400 Subject: [PATCH 372/600] Roll Flutter from b08647376976 to 6b461de0129e (3 revisions) (#5228) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 1da60dd836a4..1eec7021c4be 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b0864737697694dacfcee7e506f13667c00889d6 +6b461de0129ef7215118ef9d76e77bfa4c5f01df From 5d8a3efa910d002680726f77e69a3f6848bd2e40 Mon Sep 17 00:00:00 2001 From: BeMacized Date: Tue, 12 Apr 2022 00:32:29 +0200 Subject: [PATCH 373/600] [local_auth] Add native iOS implementation of platform interface [2] (#5207) * Switch over app facing package to use default method channel implementation in platform interface. * Remove accidental deprecation * Export externally used types from local_auth_platform_interface.dart directly. * Add missing license header * Update imports of types from platform interface package. * Move changes from local_auth/federation_ios to local_auth/federation_ios_2 * Update dependency on platform interface package * Fix analysis issues --- .../local_auth/local_auth/example/ios/Podfile | 6 - .../ios/Runner.xcodeproj/project.pbxproj | 161 +---- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- packages/local_auth/local_auth/pubspec.yaml | 5 +- packages/local_auth/local_auth_ios/AUTHORS | 67 ++ .../local_auth/local_auth_ios/CHANGELOG.md | 3 + packages/local_auth/local_auth_ios/LICENSE | 25 + packages/local_auth/local_auth_ios/README.md | 11 + .../local_auth_ios/example/README.md | 8 + .../integration_test/local_auth_test.dart | 19 + .../ios/Flutter/AppFrameworkInfo.plist | 30 + .../example/ios/Flutter/Debug.xcconfig | 2 + .../example/ios/Flutter/Release.xcconfig | 2 + .../local_auth_ios/example/ios/Podfile | 43 ++ .../ios/Runner.xcodeproj/project.pbxproj | 630 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/xcschemes/Runner.xcscheme | 97 +++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/ios/Runner/AppDelegate.h | 10 + .../example/ios/Runner/AppDelegate.m | 17 + .../AppIcon.appiconset/Contents.json | 116 ++++ .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 564 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 1588 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 1025 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 1716 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 1920 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 1895 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 3831 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 1888 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 3294 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 3612 bytes .../Runner/Base.lproj/LaunchScreen.storyboard | 27 + .../ios/Runner/Base.lproj/Main.storyboard | 26 + .../example/ios/Runner/Info.plist | 51 ++ .../local_auth_ios/example/ios/Runner/main.m | 13 + .../ios/RunnerTests/FLTLocalAuthPluginTests.m | 2 +- .../example/ios/RunnerTests/Info.plist | 0 .../local_auth_ios/example/lib/main.dart | 240 +++++++ .../local_auth_ios/example/pubspec.yaml | 28 + .../example/test_driver/integration_test.dart | 7 + .../ios/Assets/.gitkeep | 0 .../ios/Classes/FLTLocalAuthPlugin.h | 0 .../ios/Classes/FLTLocalAuthPlugin.m | 3 +- .../ios/local_auth_ios.podspec} | 4 +- .../local_auth_ios/lib/local_auth_ios.dart | 88 +++ .../lib/types/auth_messages_ios.dart | 96 +++ .../local_auth/local_auth_ios/pubspec.yaml | 27 + .../local_auth_ios/test/local_auth_test.dart | 163 +++++ 53 files changed, 1881 insertions(+), 173 deletions(-) create mode 100644 packages/local_auth/local_auth_ios/AUTHORS create mode 100644 packages/local_auth/local_auth_ios/CHANGELOG.md create mode 100644 packages/local_auth/local_auth_ios/LICENSE create mode 100644 packages/local_auth/local_auth_ios/README.md create mode 100644 packages/local_auth/local_auth_ios/example/README.md create mode 100644 packages/local_auth/local_auth_ios/example/integration_test/local_auth_test.dart create mode 100644 packages/local_auth/local_auth_ios/example/ios/Flutter/AppFrameworkInfo.plist create mode 100644 packages/local_auth/local_auth_ios/example/ios/Flutter/Debug.xcconfig create mode 100644 packages/local_auth/local_auth_ios/example/ios/Flutter/Release.xcconfig create mode 100644 packages/local_auth/local_auth_ios/example/ios/Podfile create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/project.pbxproj create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/AppDelegate.h create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/AppDelegate.m create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Base.lproj/Main.storyboard create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Info.plist create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/main.m rename packages/local_auth/{local_auth => local_auth_ios}/example/ios/RunnerTests/FLTLocalAuthPluginTests.m (99%) rename packages/local_auth/{local_auth => local_auth_ios}/example/ios/RunnerTests/Info.plist (100%) create mode 100644 packages/local_auth/local_auth_ios/example/lib/main.dart create mode 100644 packages/local_auth/local_auth_ios/example/pubspec.yaml create mode 100644 packages/local_auth/local_auth_ios/example/test_driver/integration_test.dart rename packages/local_auth/{local_auth => local_auth_ios}/ios/Assets/.gitkeep (100%) rename packages/local_auth/{local_auth => local_auth_ios}/ios/Classes/FLTLocalAuthPlugin.h (100%) rename packages/local_auth/{local_auth => local_auth_ios}/ios/Classes/FLTLocalAuthPlugin.m (99%) rename packages/local_auth/{local_auth/ios/local_auth.podspec => local_auth_ios/ios/local_auth_ios.podspec} (89%) create mode 100644 packages/local_auth/local_auth_ios/lib/local_auth_ios.dart create mode 100644 packages/local_auth/local_auth_ios/lib/types/auth_messages_ios.dart create mode 100644 packages/local_auth/local_auth_ios/pubspec.yaml create mode 100644 packages/local_auth/local_auth_ios/test/local_auth_test.dart diff --git a/packages/local_auth/local_auth/example/ios/Podfile b/packages/local_auth/local_auth/example/ios/Podfile index ef20d8e3c010..f7d6a5e68c3a 100644 --- a/packages/local_auth/local_auth/example/ios/Podfile +++ b/packages/local_auth/local_auth/example/ios/Podfile @@ -29,12 +29,6 @@ flutter_ios_podfile_setup target 'Runner' do flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) - - target 'RunnerTests' do - inherit! :search_paths - - pod 'OCMock', '3.5' - end end post_install do |installer| diff --git a/packages/local_auth/local_auth/example/ios/Runner.xcodeproj/project.pbxproj b/packages/local_auth/local_auth/example/ios/Runner.xcodeproj/project.pbxproj index 3de4b94f9d5c..b40fbca4cf66 100644 --- a/packages/local_auth/local_auth/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/local_auth/local_auth/example/ios/Runner.xcodeproj/project.pbxproj @@ -9,25 +9,14 @@ /* Begin PBXBuildFile section */ 0CCCD07A2CE24E13C9C1EEA4 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9D274A3F79473B1549B2BBD5 /* libPods-Runner.a */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 3398D2E426164AD8005A052F /* FLTLocalAuthPluginTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3398D2E326164AD8005A052F /* FLTLocalAuthPluginTests.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - B726772E092FC537C9618264 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 719FE2C7EAF8D9A045E09C29 /* libPods-RunnerTests.a */; }; /* End PBXBuildFile section */ -/* Begin PBXContainerItemProxy section */ - 3398D2D226163948005A052F /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 97C146E61CF9000F007C117D /* Project object */; - proxyType = 1; - remoteGlobalIDString = 97C146ED1CF9000F007C117D; - remoteInfo = Runner; - }; -/* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ 9705A1C41CF9048500538489 /* Embed Frameworks */ = { @@ -45,11 +34,8 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 3398D2CD26163948005A052F /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 3398D2D126163948005A052F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 3398D2DC261649CD005A052F /* liblocal_auth.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = liblocal_auth.a; sourceTree = BUILT_PRODUCTS_DIR; }; 3398D2DF26164A03005A052F /* liblocal_auth.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = liblocal_auth.a; sourceTree = BUILT_PRODUCTS_DIR; }; - 3398D2E326164AD8005A052F /* FLTLocalAuthPluginTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTLocalAuthPluginTests.m; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 658CDD04B21E4EA92F8EF229 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 719FE2C7EAF8D9A045E09C29 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -71,14 +57,6 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 3398D2CA26163948005A052F /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - B726772E092FC537C9618264 /* libPods-RunnerTests.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 97C146EB1CF9000F007C117D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -90,15 +68,6 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 33BF11D226680B2E002967F3 /* RunnerTests */ = { - isa = PBXGroup; - children = ( - 3398D2E326164AD8005A052F /* FLTLocalAuthPluginTests.m */, - 3398D2D126163948005A052F /* Info.plist */, - ); - path = RunnerTests; - sourceTree = ""; - }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -113,7 +82,6 @@ 97C146E51CF9000F007C117D = { isa = PBXGroup; children = ( - 33BF11D226680B2E002967F3 /* RunnerTests */, 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, @@ -126,7 +94,6 @@ isa = PBXGroup; children = ( 97C146EE1CF9000F007C117D /* Runner.app */, - 3398D2CD26163948005A052F /* RunnerTests.xctest */, ); name = Products; sourceTree = ""; @@ -180,25 +147,6 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 3398D2CC26163948005A052F /* RunnerTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 3398D2D426163948005A052F /* Build configuration list for PBXNativeTarget "RunnerTests" */; - buildPhases = ( - B5AF6C7A6759E6F38749E537 /* [CP] Check Pods Manifest.lock */, - 3398D2C926163948005A052F /* Sources */, - 3398D2CA26163948005A052F /* Frameworks */, - 3398D2CB26163948005A052F /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 3398D2D326163948005A052F /* PBXTargetDependency */, - ); - name = RunnerTests; - productName = RunnerTests; - productReference = 3398D2CD26163948005A052F /* RunnerTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; 97C146ED1CF9000F007C117D /* Runner */ = { isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; @@ -226,14 +174,9 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1100; + LastUpgradeCheck = 1300; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { - 3398D2CC26163948005A052F = { - CreatedOnToolsVersion = 12.4; - ProvisioningStyle = Automatic; - TestTargetID = 97C146ED1CF9000F007C117D; - }; 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; }; @@ -253,19 +196,11 @@ projectRoot = ""; targets = ( 97C146ED1CF9000F007C117D /* Runner */, - 3398D2CC26163948005A052F /* RunnerTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 3398D2CB26163948005A052F /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; 97C146EC1CF9000F007C117D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -326,39 +261,9 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - B5AF6C7A6759E6F38749E537 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 3398D2C926163948005A052F /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 3398D2E426164AD8005A052F /* FLTLocalAuthPluginTests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 97C146EA1CF9000F007C117D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -371,14 +276,6 @@ }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXTargetDependency section */ - 3398D2D326163948005A052F /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 97C146ED1CF9000F007C117D /* Runner */; - targetProxy = 3398D2D226163948005A052F /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - /* Begin PBXVariantGroup section */ 97C146FA1CF9000F007C117D /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -399,53 +296,6 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ - 3398D2D526163948005A052F /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 99302E79EC77497F2F274D12 /* Pods-RunnerTests.debug.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = RunnerTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.google.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; - }; - name = Debug; - }; - 3398D2D626163948005A052F /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = FEA527BB0A821430FEAA1566 /* Pods-RunnerTests.release.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = RunnerTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.google.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; - }; - name = Release; - }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -597,15 +447,6 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 3398D2D426163948005A052F /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 3398D2D526163948005A052F /* Debug */, - 3398D2D626163948005A052F /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/packages/local_auth/local_auth/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/local_auth/local_auth/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 58a5d07a15c8..b2af55dd6d37 100644 --- a/packages/local_auth/local_auth/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/local_auth/local_auth/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ + +Google Inc. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> +Bodhi Mulders diff --git a/packages/local_auth/local_auth_ios/CHANGELOG.md b/packages/local_auth/local_auth_ios/CHANGELOG.md new file mode 100644 index 000000000000..7f198f2d66c0 --- /dev/null +++ b/packages/local_auth/local_auth_ios/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +* Initial release from migration to federated architecture. diff --git a/packages/local_auth/local_auth_ios/LICENSE b/packages/local_auth/local_auth_ios/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/local_auth/local_auth_ios/LICENSE @@ -0,0 +1,25 @@ +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/local_auth/local_auth_ios/README.md b/packages/local_auth/local_auth_ios/README.md new file mode 100644 index 000000000000..d9f40436b617 --- /dev/null +++ b/packages/local_auth/local_auth_ios/README.md @@ -0,0 +1,11 @@ +# local\_auth\_ios + +The iOS implementation of [`local_auth`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use `local_auth` +normally. This package will be automatically included in your app when you do. + +[1]: https://pub.dev/packages/local_auth +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin diff --git a/packages/local_auth/local_auth_ios/example/README.md b/packages/local_auth/local_auth_ios/example/README.md new file mode 100644 index 000000000000..a4a6091c9ba6 --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/README.md @@ -0,0 +1,8 @@ +# local_auth_example + +Demonstrates how to use the local_auth plugin. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](https://flutter.dev/). diff --git a/packages/local_auth/local_auth_ios/example/integration_test/local_auth_test.dart b/packages/local_auth/local_auth_ios/example/integration_test/local_auth_test.dart new file mode 100644 index 000000000000..d73cfd6aa625 --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/integration_test/local_auth_test.dart @@ -0,0 +1,19 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + +import 'package:local_auth_ios/local_auth_ios.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('canCheckBiometrics', (WidgetTester tester) async { + expect( + LocalAuthIOS().getEnrolledBiometrics(), + completion(isList), + ); + }); +} diff --git a/packages/local_auth/local_auth_ios/example/ios/Flutter/AppFrameworkInfo.plist b/packages/local_auth/local_auth_ios/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 000000000000..3a9c234f96d4 --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + UIRequiredDeviceCapabilities + + arm64 + + MinimumOSVersion + 9.0 + + diff --git a/packages/local_auth/local_auth_ios/example/ios/Flutter/Debug.xcconfig b/packages/local_auth/local_auth_ios/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 000000000000..e8efba114687 --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/packages/local_auth/local_auth_ios/example/ios/Flutter/Release.xcconfig b/packages/local_auth/local_auth_ios/example/ios/Flutter/Release.xcconfig new file mode 100644 index 000000000000..399e9340e6f6 --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/packages/local_auth/local_auth_ios/example/ios/Podfile b/packages/local_auth/local_auth_ios/example/ios/Podfile new file mode 100644 index 000000000000..ee8f1d9ec3ef --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/ios/Podfile @@ -0,0 +1,43 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + + pod 'OCMock','3.5' + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..f8b4056d135d --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,630 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 0CCCD07A2CE24E13C9C1EEA4 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9D274A3F79473B1549B2BBD5 /* libPods-Runner.a */; }; + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3398D2E426164AD8005A052F /* FLTLocalAuthPluginTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3398D2E326164AD8005A052F /* FLTLocalAuthPluginTests.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 691CB38B382734AF80FBCA4C /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ADBFA21B380E07A3A585383D /* libPods-RunnerTests.a */; }; + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; + 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 3398D2D226163948005A052F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 3398D2CD26163948005A052F /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3398D2D126163948005A052F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 3398D2DC261649CD005A052F /* liblocal_auth.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = liblocal_auth.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 3398D2DF26164A03005A052F /* liblocal_auth.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = liblocal_auth.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 3398D2E326164AD8005A052F /* FLTLocalAuthPluginTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTLocalAuthPluginTests.m; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 658CDD04B21E4EA92F8EF229 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 8D6545CD14E27D6F8299FFD5 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9D274A3F79473B1549B2BBD5 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + ADBFA21B380E07A3A585383D /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + D9B3BCBC68F8928E2907FB87 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + EB36DF6C3F25E00DF4175422 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 3398D2CA26163948005A052F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 691CB38B382734AF80FBCA4C /* libPods-RunnerTests.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0CCCD07A2CE24E13C9C1EEA4 /* libPods-Runner.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 33BF11D226680B2E002967F3 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 3398D2E326164AD8005A052F /* FLTLocalAuthPluginTests.m */, + 3398D2D126163948005A052F /* Info.plist */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 33BF11D226680B2E002967F3 /* RunnerTests */, + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + F8CC53B854B121315C7319D2 /* Pods */, + E2D5FA899A019BD3E0DB0917 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 3398D2CD26163948005A052F /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 97C146F11CF9000F007C117D /* Supporting Files */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + ); + path = Runner; + sourceTree = ""; + }; + 97C146F11CF9000F007C117D /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 97C146F21CF9000F007C117D /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + E2D5FA899A019BD3E0DB0917 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 3398D2DF26164A03005A052F /* liblocal_auth.a */, + 3398D2DC261649CD005A052F /* liblocal_auth.a */, + 9D274A3F79473B1549B2BBD5 /* libPods-Runner.a */, + ADBFA21B380E07A3A585383D /* libPods-RunnerTests.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + F8CC53B854B121315C7319D2 /* Pods */ = { + isa = PBXGroup; + children = ( + EB36DF6C3F25E00DF4175422 /* Pods-Runner.debug.xcconfig */, + 658CDD04B21E4EA92F8EF229 /* Pods-Runner.release.xcconfig */, + 8D6545CD14E27D6F8299FFD5 /* Pods-RunnerTests.debug.xcconfig */, + D9B3BCBC68F8928E2907FB87 /* Pods-RunnerTests.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 3398D2CC26163948005A052F /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3398D2D426163948005A052F /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 9C21D3AD392EA849AEB09231 /* [CP] Check Pods Manifest.lock */, + 3398D2C926163948005A052F /* Sources */, + 3398D2CA26163948005A052F /* Frameworks */, + 3398D2CB26163948005A052F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 3398D2D326163948005A052F /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 3398D2CD26163948005A052F /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 98D96A2D1A74AF66E3DD2DBC /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = "The Flutter Authors"; + TargetAttributes = { + 3398D2CC26163948005A052F = { + CreatedOnToolsVersion = 12.4; + ProvisioningStyle = Automatic; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 3398D2CC26163948005A052F /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 3398D2CB26163948005A052F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + 98D96A2D1A74AF66E3DD2DBC /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 9C21D3AD392EA849AEB09231 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 3398D2C926163948005A052F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3398D2E426164AD8005A052F /* FLTLocalAuthPluginTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, + 97C146F31CF9000F007C117D /* main.m in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 3398D2D326163948005A052F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 3398D2D226163948005A052F /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 3398D2D526163948005A052F /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8D6545CD14E27D6F8299FFD5 /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = RunnerTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.google.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Debug; + }; + 3398D2D626163948005A052F /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D9B3BCBC68F8928E2907FB87 /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = RunnerTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.google.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Release; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.localAuthExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.localAuthExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 3398D2D426163948005A052F /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3398D2D526163948005A052F /* Debug */, + 3398D2D626163948005A052F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..919434a6254f --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000000..b2af55dd6d37 --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/local_auth/local_auth_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..21a3cc14c74e --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/local_auth/local_auth_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner/AppDelegate.h b/packages/local_auth/local_auth_ios/example/ios/Runner/AppDelegate.h new file mode 100644 index 000000000000..0681d288bb70 --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/ios/Runner/AppDelegate.h @@ -0,0 +1,10 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import + +@interface AppDelegate : FlutterAppDelegate + +@end diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner/AppDelegate.m b/packages/local_auth/local_auth_ios/example/ios/Runner/AppDelegate.m new file mode 100644 index 000000000000..30b87969f44a --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/ios/Runner/AppDelegate.m @@ -0,0 +1,17 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "AppDelegate.h" +#include "GeneratedPluginRegistrant.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [GeneratedPluginRegistrant registerWithRegistry:self]; + // Override point for customization after application launch. + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +@end diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000000..d22f10b2ab63 --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..28c6bf03016f6c994b70f38d1b7346e5831b531f GIT binary patch literal 564 zcmV-40?Yl0P)Px$?ny*JR5%f>l)FnDQ543{x%ZCiu33$Wg!pQFfT_}?5Q|_VSlIbLC`dpoMXL}9 zHfd9&47Mo(7D231gb+kjFxZHS4-m~7WurTH&doVX2KI5sU4v(sJ1@T9eCIKPjsqSr z)C01LsCxk=72-vXmX}CQD#BD;Cthymh&~=f$Q8nn0J<}ZrusBy4PvRNE}+1ceuj8u z0mW5k8fmgeLnTbWHGwfKA3@PdZxhn|PypR&^p?weGftrtCbjF#+zk_5BJh7;0`#Wr zgDpM_;Ax{jO##IrT`Oz;MvfwGfV$zD#c2xckpcXC6oou4ML~ezCc2EtnsQTB4tWNg z?4bkf;hG7IMfhgNI(FV5Gs4|*GyMTIY0$B=_*mso9Ityq$m^S>15>-?0(zQ<8Qy<_TjHE33(?_M8oaM zyc;NxzRVK@DL6RJnX%U^xW0Gpg(lXp(!uK1v0YgHjs^ZXSQ|m#lV7ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 literal 0 HcmV?d00001 diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..f091b6b0bca859a3f474b03065bef75ba58a9e4c GIT binary patch literal 1588 zcmV-42Fv-0P)C1SqPt}wig>|5Crh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)y zRAZ>eDe#*r`yDAVgB_R*LB*MAc)8(b{g{9McCXW!lq7r(btRoB9!8B-#AI6JMb~YFBEvdsV)`mEQO^&#eRKx@b&x- z5lZm*!WfD8oCLzfHGz#u7sT0^VLMI1MqGxF^v+`4YYnVYgk*=kU?HsSz{v({E3lb9 z>+xILjBN)t6`=g~IBOelGQ(O990@BfXf(DRI5I$qN$0Gkz-FSc$3a+2fX$AedL4u{ z4V+5Ong(9LiGcIKW?_352sR;LtDPmPJXI{YtT=O8=76o9;*n%_m|xo!i>7$IrZ-{l z-x3`7M}qzHsPV@$v#>H-TpjDh2UE$9g6sysUREDy_R(a)>=eHw-WAyfIN z*qb!_hW>G)Tu8nSw9yn#3wFMiLcfc4pY0ek1}8(NqkBR@t4{~oC>ryc-h_ByH(Cg5 z>ao-}771+xE3um9lWAY1FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zm zZQj(aA_HeBY&OC^jj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5Kh zX*|AU4QE#~SgPzOXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&g3j|zDgC+}2Q_v%YfDax z!?umcN^n}KYQ|a$Lr+51Nf9dkkYFSjZZjkma$0KOj+;aQ&721~t7QUKx61J3(P4P1 zstI~7-wOACnWP4=8oGOwz%vNDqD8w&Q`qcNGGrbbf&0s9L0De{4{mRS?o0MU+nR_! zrvshUau0G^DeMhM_v{5BuLjb#Hh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG z;P%vktHcXwTj~+IE(~px)3*MY77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC z(zYC$g|@+I+kY;dg_dE>scBf&bP1Nc@Hz<3R)V`=AGkc;8CXqdi=B4l2k|g;2%#m& z*jfX^%b!A8#bI!j9-0Fi0bOXl(-c^AB9|nQaE`*)Hw+o&jS9@7&Gov#HbD~#d{twV zXd^Tr^mWLfFh$@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)y zI9C9*oUga6=hxw6QasLPnee@3^Rr*M{CdaL5=R41nLs(AHk_=Y+A9$2&H(B7!_pURs&8aNw7?`&Z&xY_Ye z)~D5Bog^td-^QbUtkTirdyK^mTHAOuptDflut!#^lnKqU md>ggs(5nOWAqO?umG&QVYK#ibz}*4>0000U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~cTNYR~@Y9KepMPrrB8cABapAFa=`H+UGhkXUZV1GnwR1*lPyZ;*K(i~2gp|@bzp8}og7e*#% zEnr|^CWdVV!-4*Y_7rFvlww2Ze+>j*!Z!pQ?2l->4q#nqRu9`ELo6RMS5=br47g_X zRw}P9a7RRYQ%2Vsd0Me{_(EggTnuN6j=-?uFS6j^u69elMypu?t>op*wBx<=Wx8?( ztpe^(fwM6jJX7M-l*k3kEpWOl_Vk3@(_w4oc}4YF4|Rt=2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX?F&rExl^W|G8Z8;`5z-k}OGNZ0#WVb$WCpQu-_YsiqKP?BB# vzVHS-CTUF4Ozn5G+mq_~Qqto~ahA+K`|lyv3(-e}00000NkvXXu0mjfd`9t{ literal 0 HcmV?d00001 diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d0ef06e7edb86cdfe0d15b4b0d98334a86163658 GIT binary patch literal 1716 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 literal 0 HcmV?d00001 diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c8f9ed8f5cee1c98386d13b17e89f719e83555b2 GIT binary patch literal 1895 zcmV-t2blPYP)FQtfgmafE#=YDCq`qUBt#QpG%*H6QHY765~R=q zZ6iudfM}q!Pz#~9JgOi8QJ|DSu?1-*(kSi1K4#~5?#|rh?sS)(-JQqX*}ciXJ56_H zdw=^s_srbAdqxlvGyrgGet#6T7_|j;95sL%MtM;q86vOxKM$f#puR)Bjv9Zvz9-di zXOTSsZkM83)E9PYBXC<$6(|>lNLVBb&&6y{NByFCp%6+^ALR@NCTse_wqvNmSWI-m z!$%KlHFH2omF!>#%1l3LTZg(s7eof$7*xB)ZQ0h?ejh?Ta9fDv59+u#MokW+1t8Zb zgHv%K(u9G^Lv`lh#f3<6!JVTL3(dCpxHbnbA;kKqQyd1~^Xe0VIaYBSWm6nsr;dFj z4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3QU4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKubPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`Q zykI#*+rQ=z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRe zt3L_uNyQ*cE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=ky zx=~RKa4{iTm{_>_vSCm?$Ej=i6@=m%@VvAITnigVg{&@!7CDgs908761meDK5azA} z4?=NOH|PdvabgJ&fW2{Mo$Q0CcD8Qc84%{JPYt5EiG{MdLIAeX%T=D7NIP4%Hw}p9 zg)==!2Lbp#j{u_}hMiao9=!VSyx0gHbeCS`;q&vzeq|fs`y&^X-lso(Ls@-706qmA z7u*T5PMo_w3{se1t2`zWeO^hOvTsohG_;>J0wVqVe+n)AbQCx)yh9;w+J6?NF5Lmo zecS@ieAKL8%bVd@+-KT{yI|S}O>pYckUFs;ry9Ow$CD@ztz5K-*D$^{i(_1llhSh^ zEkL$}tsQt5>QA^;QgjgIfBDmcOgi5YDyu?t6vSnbp=1+@6D& z5MJ}B8q;bRlVoxasyhcUF1+)o`&3r0colr}QJ3hcSdLu;9;td>kf@Tcn<@9sIx&=m z;AD;SCh95=&p;$r{Xz3iWCO^MX83AGJ(yH&eTXgv|0=34#-&WAmw{)U7OU9!Wz^!7 zZ%jZFi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i h0WYBP*#0Ks^FNSabJA*5${_#%002ovPDHLkV1oKhTl@e3 literal 0 HcmV?d00001 diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..75b2d164a5a98e212cca15ea7bf2ab5de5108680 GIT binary patch literal 3831 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 literal 0 HcmV?d00001 diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..c4df70d39da7941ef3f6dcb7f06a192d8dcb308d GIT binary patch literal 1888 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 iW8 + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner/Base.lproj/Main.storyboard b/packages/local_auth/local_auth_ios/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 000000000000..f3c28516fb38 --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner/Info.plist b/packages/local_auth/local_auth_ios/example/ios/Runner/Info.plist new file mode 100644 index 000000000000..f8e0356d0a68 --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/ios/Runner/Info.plist @@ -0,0 +1,51 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + local_auth_example + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + NSFaceIDUsageDescription + App needs to authenticate using faces. + + diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner/main.m b/packages/local_auth/local_auth_ios/example/ios/Runner/main.m new file mode 100644 index 000000000000..f143297b30d6 --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/ios/Runner/main.m @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import +#import "AppDelegate.h" + +int main(int argc, char *argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/packages/local_auth/local_auth/example/ios/RunnerTests/FLTLocalAuthPluginTests.m b/packages/local_auth/local_auth_ios/example/ios/RunnerTests/FLTLocalAuthPluginTests.m similarity index 99% rename from packages/local_auth/local_auth/example/ios/RunnerTests/FLTLocalAuthPluginTests.m rename to packages/local_auth/local_auth_ios/example/ios/RunnerTests/FLTLocalAuthPluginTests.m index 3572524d8991..744d9124f7b1 100644 --- a/packages/local_auth/local_auth/example/ios/RunnerTests/FLTLocalAuthPluginTests.m +++ b/packages/local_auth/local_auth_ios/example/ios/RunnerTests/FLTLocalAuthPluginTests.m @@ -10,7 +10,7 @@ #if __has_include() #import #else -@import local_auth; +@import local_auth_ios; #endif // Private API needed for tests. diff --git a/packages/local_auth/local_auth/example/ios/RunnerTests/Info.plist b/packages/local_auth/local_auth_ios/example/ios/RunnerTests/Info.plist similarity index 100% rename from packages/local_auth/local_auth/example/ios/RunnerTests/Info.plist rename to packages/local_auth/local_auth_ios/example/ios/RunnerTests/Info.plist diff --git a/packages/local_auth/local_auth_ios/example/lib/main.dart b/packages/local_auth/local_auth_ios/example/lib/main.dart new file mode 100644 index 000000000000..9346be10fd78 --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/lib/main.dart @@ -0,0 +1,240 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:local_auth_ios/local_auth_ios.dart'; +import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; +import 'package:local_auth_platform_interface/types/auth_messages.dart'; +import 'package:local_auth_platform_interface/types/biometric_type.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatefulWidget { + @override + _MyAppState createState() => _MyAppState(); +} + +class _MyAppState extends State { + _SupportState _supportState = _SupportState.unknown; + bool? _canCheckBiometrics; + List? _availableBiometrics; + String _authorized = 'Not Authorized'; + bool _isAuthenticating = false; + + @override + void initState() { + super.initState(); + LocalAuthPlatform.instance.isDeviceSupported().then( + (bool isSupported) => setState(() => _supportState = isSupported + ? _SupportState.supported + : _SupportState.unsupported), + ); + } + + Future _checkBiometrics() async { + late bool canCheckBiometrics; + try { + canCheckBiometrics = + (await LocalAuthPlatform.instance.getEnrolledBiometrics()).isNotEmpty; + } on PlatformException catch (e) { + canCheckBiometrics = false; + print(e); + } + if (!mounted) { + return; + } + + setState(() { + _canCheckBiometrics = canCheckBiometrics; + }); + } + + Future _getEnrolledBiometrics() async { + late List availableBiometrics; + try { + availableBiometrics = + await LocalAuthPlatform.instance.getEnrolledBiometrics(); + } on PlatformException catch (e) { + availableBiometrics = []; + print(e); + } + if (!mounted) { + return; + } + + setState(() { + _availableBiometrics = availableBiometrics; + }); + } + + Future _authenticate() async { + bool authenticated = false; + try { + setState(() { + _isAuthenticating = true; + _authorized = 'Authenticating'; + }); + authenticated = await LocalAuthPlatform.instance.authenticate( + localizedReason: 'Let OS determine authentication method', + authMessages: [const IOSAuthMessages()], + options: const AuthenticationOptions( + useErrorDialogs: true, + stickyAuth: true, + ), + ); + setState(() { + _isAuthenticating = false; + }); + } on PlatformException catch (e) { + print(e); + setState(() { + _isAuthenticating = false; + _authorized = 'Error - ${e.message}'; + }); + return; + } + if (!mounted) { + return; + } + + setState( + () => _authorized = authenticated ? 'Authorized' : 'Not Authorized'); + } + + Future _authenticateWithBiometrics() async { + bool authenticated = false; + try { + setState(() { + _isAuthenticating = true; + _authorized = 'Authenticating'; + }); + authenticated = await LocalAuthPlatform.instance.authenticate( + localizedReason: + 'Scan your fingerprint (or face or whatever) to authenticate', + authMessages: [const IOSAuthMessages()], + options: const AuthenticationOptions( + useErrorDialogs: true, + stickyAuth: true, + biometricOnly: true, + ), + ); + setState(() { + _isAuthenticating = false; + _authorized = 'Authenticating'; + }); + } on PlatformException catch (e) { + print(e); + setState(() { + _isAuthenticating = false; + _authorized = 'Error - ${e.message}'; + }); + return; + } + if (!mounted) { + return; + } + + final String message = authenticated ? 'Authorized' : 'Not Authorized'; + setState(() { + _authorized = message; + }); + } + + Future _cancelAuthentication() async { + await LocalAuthPlatform.instance.stopAuthentication(); + setState(() => _isAuthenticating = false); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Plugin example app'), + ), + body: ListView( + padding: const EdgeInsets.only(top: 30), + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (_supportState == _SupportState.unknown) + const CircularProgressIndicator() + else if (_supportState == _SupportState.supported) + const Text('This device is supported') + else + const Text('This device is not supported'), + const Divider(height: 100), + Text('Can check biometrics: $_canCheckBiometrics\n'), + ElevatedButton( + child: const Text('Check biometrics'), + onPressed: _checkBiometrics, + ), + const Divider(height: 100), + Text('Available biometrics: $_availableBiometrics\n'), + ElevatedButton( + child: const Text('Get available biometrics'), + onPressed: _getEnrolledBiometrics, + ), + const Divider(height: 100), + Text('Current State: $_authorized\n'), + if (_isAuthenticating) + ElevatedButton( + onPressed: _cancelAuthentication, + child: Row( + mainAxisSize: MainAxisSize.min, + children: const [ + Text('Cancel Authentication'), + Icon(Icons.cancel), + ], + ), + ) + else + Column( + children: [ + ElevatedButton( + child: Row( + mainAxisSize: MainAxisSize.min, + children: const [ + Text('Authenticate'), + Icon(Icons.perm_device_information), + ], + ), + onPressed: _authenticate, + ), + ElevatedButton( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text(_isAuthenticating + ? 'Cancel' + : 'Authenticate: biometrics only'), + const Icon(Icons.fingerprint), + ], + ), + onPressed: _authenticateWithBiometrics, + ), + ], + ), + ], + ), + ], + ), + ), + ); + } +} + +enum _SupportState { + unknown, + supported, + unsupported, +} diff --git a/packages/local_auth/local_auth_ios/example/pubspec.yaml b/packages/local_auth/local_auth_ios/example/pubspec.yaml new file mode 100644 index 000000000000..f83806b9d08e --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/pubspec.yaml @@ -0,0 +1,28 @@ +name: local_auth_ios_example +description: Demonstrates how to use the local_auth_ios plugin. +publish_to: none + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.8.0" + +dependencies: + flutter: + sdk: flutter + local_auth_ios: + # When depending on this package from a real application you should use: + # local_auth: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + local_auth_platform_interface: ^1.0.0 + +dev_dependencies: + flutter_driver: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/local_auth/local_auth_ios/example/test_driver/integration_test.dart b/packages/local_auth/local_auth_ios/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4f10f2a522f3 --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/test_driver/integration_test.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); diff --git a/packages/local_auth/local_auth/ios/Assets/.gitkeep b/packages/local_auth/local_auth_ios/ios/Assets/.gitkeep similarity index 100% rename from packages/local_auth/local_auth/ios/Assets/.gitkeep rename to packages/local_auth/local_auth_ios/ios/Assets/.gitkeep diff --git a/packages/local_auth/local_auth/ios/Classes/FLTLocalAuthPlugin.h b/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.h similarity index 100% rename from packages/local_auth/local_auth/ios/Classes/FLTLocalAuthPlugin.h rename to packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.h diff --git a/packages/local_auth/local_auth/ios/Classes/FLTLocalAuthPlugin.m b/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m similarity index 99% rename from packages/local_auth/local_auth/ios/Classes/FLTLocalAuthPlugin.m rename to packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m index 70113efa00a0..d4d0e0d0f79f 100644 --- a/packages/local_auth/local_auth/ios/Classes/FLTLocalAuthPlugin.m +++ b/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m @@ -20,7 +20,7 @@ @implementation FLTLocalAuthPlugin { + (void)registerWithRegistrar:(NSObject *)registrar { FlutterMethodChannel *channel = - [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/local_auth" + [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/local_auth_ios" binaryMessenger:[registrar messenger]]; FLTLocalAuthPlugin *instance = [[FLTLocalAuthPlugin alloc] init]; [registrar addMethodCallDelegate:instance channel:channel]; @@ -180,7 +180,6 @@ - (void)handleAuthReplyWithSuccess:(BOOL)success case LAErrorPasscodeNotSet: case LAErrorTouchIDNotAvailable: case LAErrorTouchIDNotEnrolled: - case LAErrorUserFallback: case LAErrorTouchIDLockout: [self handleErrors:error flutterArguments:arguments withFlutterResult:result]; return; diff --git a/packages/local_auth/local_auth/ios/local_auth.podspec b/packages/local_auth/local_auth_ios/ios/local_auth_ios.podspec similarity index 89% rename from packages/local_auth/local_auth/ios/local_auth.podspec rename to packages/local_auth/local_auth_ios/ios/local_auth_ios.podspec index 4ab779ad50cd..0828c6085ea2 100644 --- a/packages/local_auth/local_auth/ios/local_auth.podspec +++ b/packages/local_auth/local_auth_ios/ios/local_auth_ios.podspec @@ -2,7 +2,7 @@ # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html # Pod::Spec.new do |s| - s.name = 'local_auth' + s.name = 'local_auth_ios' s.version = '0.0.1' s.summary = 'Flutter Local Auth' s.description = <<-DESC @@ -13,7 +13,7 @@ Downloaded by pub (not CocoaPods). s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/local_auth' } - s.documentation_url = 'https://pub.dev/packages/local_auth' + s.documentation_url = 'https://pub.dev/packages/local_auth_ios' s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' s.dependency 'Flutter' diff --git a/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart b/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart new file mode 100644 index 000000000000..7d085ccf14d9 --- /dev/null +++ b/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart @@ -0,0 +1,88 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; +import 'package:local_auth_ios/types/auth_messages_ios.dart'; +import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; +import 'package:local_auth_platform_interface/types/auth_messages.dart'; +import 'package:local_auth_platform_interface/types/auth_options.dart'; +import 'package:local_auth_platform_interface/types/biometric_type.dart'; + +export 'package:local_auth_ios/types/auth_messages_ios.dart'; +export 'package:local_auth_platform_interface/types/auth_messages.dart'; +export 'package:local_auth_platform_interface/types/auth_options.dart'; +export 'package:local_auth_platform_interface/types/biometric_type.dart'; + +const MethodChannel _channel = + MethodChannel('plugins.flutter.io/local_auth_ios'); + +/// The implementation of [LocalAuthPlatform] for iOS. +class LocalAuthIOS extends LocalAuthPlatform { + /// Registers this class as the default instance of [LocalAuthPlatform]. + static void registerWith() { + LocalAuthPlatform.instance = LocalAuthIOS(); + } + + @override + Future authenticate({ + required String localizedReason, + required Iterable authMessages, + AuthenticationOptions options = const AuthenticationOptions(), + }) async { + assert(localizedReason.isNotEmpty); + final Map args = { + 'localizedReason': localizedReason, + 'useErrorDialogs': options.useErrorDialogs, + 'stickyAuth': options.stickyAuth, + 'sensitiveTransaction': options.sensitiveTransaction, + 'biometricOnly': options.biometricOnly, + }; + args.addAll(const IOSAuthMessages().args); + for (final AuthMessages messages in authMessages) { + if (messages is IOSAuthMessages) { + args.addAll(messages.args); + } + } + return (await _channel.invokeMethod('authenticate', args)) ?? false; + } + + @override + Future deviceSupportsBiometrics() async { + return (await getEnrolledBiometrics()).isNotEmpty; + } + + @override + Future> getEnrolledBiometrics() async { + final List result = (await _channel.invokeListMethod( + 'getAvailableBiometrics', + )) ?? + []; + final List biometrics = []; + for (final String value in result) { + switch (value) { + case 'face': + biometrics.add(BiometricType.face); + break; + case 'fingerprint': + biometrics.add(BiometricType.fingerprint); + break; + case 'iris': + biometrics.add(BiometricType.iris); + break; + case 'undefined': + break; + } + } + return biometrics; + } + + @override + Future isDeviceSupported() async => + (await _channel.invokeMethod('isDeviceSupported')) ?? false; + + @override + Future stopAuthentication() async { + throw UnimplementedError('stopAuthentication() is not supported on iOS.'); + } +} diff --git a/packages/local_auth/local_auth_ios/lib/types/auth_messages_ios.dart b/packages/local_auth/local_auth_ios/lib/types/auth_messages_ios.dart new file mode 100644 index 000000000000..8a776243d242 --- /dev/null +++ b/packages/local_auth/local_auth_ios/lib/types/auth_messages_ios.dart @@ -0,0 +1,96 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; +import 'package:intl/intl.dart'; +import 'package:local_auth_platform_interface/types/auth_messages.dart'; + +/// Class wrapping all authentication messages needed on iOS. +/// Provides default values for all messages. +@immutable +class IOSAuthMessages extends AuthMessages { + /// Constructs a new instance. + const IOSAuthMessages({ + this.lockOut, + this.goToSettingsButton, + this.goToSettingsDescription, + this.cancelButton, + }); + + /// Message advising the user to re-enable biometrics on their device. + final String? lockOut; + + /// Message shown on a button that the user can click to go to settings pages + /// from the current dialog. + /// Maximum 30 characters. + final String? goToSettingsButton; + + /// Message advising the user to go to the settings and configure Biometrics + /// for their device. + final String? goToSettingsDescription; + + /// Message shown on a button that the user can click to leave the current + /// dialog. + /// Maximum 30 characters. + final String? cancelButton; + + @override + Map get args { + return { + 'lockOut': lockOut ?? iOSLockOut, + 'goToSetting': goToSettingsButton ?? goToSettings, + 'goToSettingDescriptionIOS': + goToSettingsDescription ?? iOSGoToSettingsDescription, + 'okButton': cancelButton ?? iOSOkButton, + }; + } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is IOSAuthMessages && + runtimeType == other.runtimeType && + lockOut == other.lockOut && + goToSettingsButton == other.goToSettingsButton && + goToSettingsDescription == other.goToSettingsDescription && + cancelButton == other.cancelButton; + + @override + int get hashCode => + lockOut.hashCode ^ + goToSettingsButton.hashCode ^ + goToSettingsDescription.hashCode ^ + cancelButton.hashCode; +} + +// Default Strings for IOSAuthMessages plugin. Currently supports English. +// Intl.message must be string literals. + +/// Message shown on a button that the user can click to go to settings pages +/// from the current dialog. +String get goToSettings => Intl.message('Go to settings', + desc: 'Message shown on a button that the user can click to go to ' + 'settings pages from the current dialog. Maximum 30 characters.'); + +/// Message advising the user to re-enable biometrics on their device. +/// It shows in a dialog on iOS. +String get iOSLockOut => Intl.message( + 'Biometric authentication is disabled. Please lock and unlock your screen to ' + 'enable it.', + desc: 'Message advising the user to re-enable biometrics on their device.'); + +/// Message advising the user to go to the settings and configure Biometrics +/// for their device. +String get iOSGoToSettingsDescription => Intl.message( + 'Biometric authentication is not set up on your device. Please either enable ' + 'Touch ID or Face ID on your phone.', + desc: + 'Message advising the user to go to the settings and configure Biometrics ' + 'for their device.'); + +/// Message shown on a button that the user can click to leave the current +/// dialog. +String get iOSOkButton => Intl.message('OK', + desc: 'Message showed on a button that the user can click to leave the ' + 'current dialog. Maximum 30 characters.'); diff --git a/packages/local_auth/local_auth_ios/pubspec.yaml b/packages/local_auth/local_auth_ios/pubspec.yaml new file mode 100644 index 000000000000..a9126fdea676 --- /dev/null +++ b/packages/local_auth/local_auth_ios/pubspec.yaml @@ -0,0 +1,27 @@ +name: local_auth_ios +description: iOS implementation of the local_auth plugin. +repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_ios +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 +version: 1.0.0 + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.8.0" + +flutter: + plugin: + implements: local_auth + platforms: + ios: + pluginClass: FLTLocalAuthPlugin + dartPluginClass: LocalAuthIOS + +dependencies: + flutter: + sdk: flutter + intl: ^0.17.0 + local_auth_platform_interface: ^1.0.0 + +dev_dependencies: + flutter_test: + sdk: flutter \ No newline at end of file diff --git a/packages/local_auth/local_auth_ios/test/local_auth_test.dart b/packages/local_auth/local_auth_ios/test/local_auth_test.dart new file mode 100644 index 000000000000..b529764adbd6 --- /dev/null +++ b/packages/local_auth/local_auth_ios/test/local_auth_test.dart @@ -0,0 +1,163 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:local_auth_ios/local_auth_ios.dart'; +import 'package:local_auth_platform_interface/types/auth_options.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('LocalAuth', () { + const MethodChannel channel = MethodChannel( + 'plugins.flutter.io/local_auth_ios', + ); + + final List log = []; + late LocalAuthIOS localAuthentication; + + setUp(() { + channel.setMockMethodCallHandler((MethodCall methodCall) { + log.add(methodCall); + switch (methodCall.method) { + case 'getAvailableBiometrics': + return Future>.value( + ['face', 'fingerprint', 'iris', 'undefined']); + default: + return Future.value(true); + } + }); + localAuthentication = LocalAuthIOS(); + log.clear(); + }); + + test('deviceSupportsBiometrics calls getEnrolledBiometrics', () async { + final bool result = await localAuthentication.deviceSupportsBiometrics(); + + expect( + log, + [ + isMethodCall('getAvailableBiometrics', arguments: null), + ], + ); + expect(result, true); + }); + + test('getEnrolledBiometrics calls platform', () async { + final List result = + await localAuthentication.getEnrolledBiometrics(); + + expect( + log, + [ + isMethodCall('getAvailableBiometrics', arguments: null), + ], + ); + expect(result, [ + BiometricType.face, + BiometricType.fingerprint, + BiometricType.iris + ]); + }); + + test('isDeviceSupported calls platform', () async { + await localAuthentication.isDeviceSupported(); + + expect( + log, + [ + isMethodCall('isDeviceSupported', arguments: null), + ], + ); + }); + + test('stopAuthentication throws UnimplementedError', () async { + expect(() async => await localAuthentication.stopAuthentication(), + throwsUnimplementedError); + }); + + group('With device auth fail over', () { + test('authenticate with no args.', () async { + await localAuthentication.authenticate( + authMessages: [const IOSAuthMessages()], + localizedReason: 'Needs secure', + options: const AuthenticationOptions(biometricOnly: true), + ); + expect( + log, + [ + isMethodCall('authenticate', + arguments: { + 'localizedReason': 'Needs secure', + 'useErrorDialogs': true, + 'stickyAuth': false, + 'sensitiveTransaction': true, + 'biometricOnly': true, + }..addAll(const IOSAuthMessages().args)), + ], + ); + }); + + test('authenticate with no localizedReason.', () async { + await expectLater( + localAuthentication.authenticate( + authMessages: [const IOSAuthMessages()], + localizedReason: '', + options: const AuthenticationOptions(biometricOnly: true), + ), + throwsAssertionError, + ); + }); + }); + + group('With biometrics only', () { + test('authenticate with no args.', () async { + await localAuthentication.authenticate( + authMessages: [const IOSAuthMessages()], + localizedReason: 'Needs secure', + ); + expect( + log, + [ + isMethodCall('authenticate', + arguments: { + 'localizedReason': 'Needs secure', + 'useErrorDialogs': true, + 'stickyAuth': false, + 'sensitiveTransaction': true, + 'biometricOnly': false, + }..addAll(const IOSAuthMessages().args)), + ], + ); + }); + + test('authenticate with no sensitive transaction.', () async { + await localAuthentication.authenticate( + authMessages: [const IOSAuthMessages()], + localizedReason: 'Insecure', + options: const AuthenticationOptions( + sensitiveTransaction: false, + useErrorDialogs: false, + ), + ); + expect( + log, + [ + isMethodCall('authenticate', + arguments: { + 'localizedReason': 'Insecure', + 'useErrorDialogs': false, + 'stickyAuth': false, + 'sensitiveTransaction': false, + 'biometricOnly': false, + }..addAll(const IOSAuthMessages().args)), + ], + ); + }); + }); + }); +} From d05ec56c6ddaa6870632e9a7b73071df1ea6495b Mon Sep 17 00:00:00 2001 From: BeMacized Date: Tue, 12 Apr 2022 01:39:10 +0200 Subject: [PATCH 374/600] [local_auth] Add native Android implementation of platform interface (#4700) --- packages/local_auth/local_auth/pubspec.yaml | 7 +- .../local_auth/local_auth_android/AUTHORS | 67 +++++ .../local_auth_android/CHANGELOG.md | 3 + .../local_auth/local_auth_android/LICENSE | 25 ++ .../local_auth/local_auth_android/README.md | 11 + .../android/build.gradle | 0 .../android/lint-baseline.xml | 0 .../android/settings.gradle | 0 .../android/src/main/AndroidManifest.xml | 0 .../localauth/AuthenticationHelper.java | 0 .../plugins/localauth/LocalAuthPlugin.java | 2 +- .../res/drawable/fingerprint_initial_icon.xml | 0 .../res/drawable/fingerprint_success_icon.xml | 0 .../res/drawable/fingerprint_warning_icon.xml | 0 .../main/res/drawable/ic_done_white_24dp.xml | 0 .../drawable/ic_fingerprint_white_24dp.xml | 0 .../drawable/ic_priority_high_white_24dp.xml | 0 .../src/main/res/layout/go_to_setting.xml | 0 .../android/src/main/res/layout/scan_fp.xml | 0 .../android/src/main/res/values/colors.xml | 0 .../android/src/main/res/values/dimens.xml | 0 .../android/src/main/res/values/styles.xml | 0 .../plugins/localauth/LocalAuthTest.java | 0 .../local_auth_android/example/README.md | 8 + .../example/android/app/build.gradle | 58 +++++ .../gradle/wrapper/gradle-wrapper.properties | 5 + .../flutter/plugins/DartIntegrationTest.java | 14 ++ .../FlutterFragmentActivityTest.java | 20 ++ .../android/app/src/main/AndroidManifest.xml | 21 ++ .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../example/android/build.gradle | 29 +++ .../example/android/gradle.properties | 4 + .../gradle/wrapper/gradle-wrapper.properties | 6 + .../example/android/settings.gradle | 15 ++ .../example/android/settings_aar.gradle | 1 + .../integration_test/local_auth_test.dart | 19 ++ .../local_auth_android/example/lib/main.dart | 238 ++++++++++++++++++ .../local_auth_android/example/pubspec.yaml | 28 +++ .../example/test_driver/integration_test.dart | 7 + .../lib/local_auth_android.dart | 87 +++++++ .../lib/types/auth_messages_android.dart | 191 ++++++++++++++ .../local_auth_android/pubspec.yaml | 29 +++ .../test/local_auth_test.dart | 181 +++++++++++++ 47 files changed, 1072 insertions(+), 4 deletions(-) create mode 100644 packages/local_auth/local_auth_android/AUTHORS create mode 100644 packages/local_auth/local_auth_android/CHANGELOG.md create mode 100644 packages/local_auth/local_auth_android/LICENSE create mode 100644 packages/local_auth/local_auth_android/README.md rename packages/local_auth/{local_auth => local_auth_android}/android/build.gradle (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/lint-baseline.xml (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/settings.gradle (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/main/AndroidManifest.xml (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java (99%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/main/res/drawable/fingerprint_initial_icon.xml (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/main/res/drawable/fingerprint_success_icon.xml (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/main/res/drawable/fingerprint_warning_icon.xml (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/main/res/drawable/ic_done_white_24dp.xml (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/main/res/drawable/ic_fingerprint_white_24dp.xml (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/main/res/drawable/ic_priority_high_white_24dp.xml (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/main/res/layout/go_to_setting.xml (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/main/res/layout/scan_fp.xml (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/main/res/values/colors.xml (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/main/res/values/dimens.xml (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/main/res/values/styles.xml (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java (100%) create mode 100644 packages/local_auth/local_auth_android/example/README.md create mode 100644 packages/local_auth/local_auth_android/example/android/app/build.gradle create mode 100644 packages/local_auth/local_auth_android/example/android/app/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/local_auth/local_auth_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java create mode 100644 packages/local_auth/local_auth_android/example/android/app/src/androidTest/java/io/flutter/plugins/localauth/FlutterFragmentActivityTest.java create mode 100644 packages/local_auth/local_auth_android/example/android/app/src/main/AndroidManifest.xml create mode 100644 packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 packages/local_auth/local_auth_android/example/android/build.gradle create mode 100644 packages/local_auth/local_auth_android/example/android/gradle.properties create mode 100644 packages/local_auth/local_auth_android/example/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/local_auth/local_auth_android/example/android/settings.gradle create mode 100644 packages/local_auth/local_auth_android/example/android/settings_aar.gradle create mode 100644 packages/local_auth/local_auth_android/example/integration_test/local_auth_test.dart create mode 100644 packages/local_auth/local_auth_android/example/lib/main.dart create mode 100644 packages/local_auth/local_auth_android/example/pubspec.yaml create mode 100644 packages/local_auth/local_auth_android/example/test_driver/integration_test.dart create mode 100644 packages/local_auth/local_auth_android/lib/local_auth_android.dart create mode 100644 packages/local_auth/local_auth_android/lib/types/auth_messages_android.dart create mode 100644 packages/local_auth/local_auth_android/pubspec.yaml create mode 100644 packages/local_auth/local_auth_android/test/local_auth_test.dart diff --git a/packages/local_auth/local_auth/pubspec.yaml b/packages/local_auth/local_auth/pubspec.yaml index dbffb4cda879..baf5d46b7b00 100644 --- a/packages/local_auth/local_auth/pubspec.yaml +++ b/packages/local_auth/local_auth/pubspec.yaml @@ -17,8 +17,7 @@ flutter: plugin: platforms: android: - package: io.flutter.plugins.localauth - pluginClass: LocalAuthPlugin + default_package: local_auth_android ios: default_package: local_auth_ios @@ -27,7 +26,9 @@ dependencies: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.1 intl: ^0.17.0 - # Temporary path dependencies to allow moving Android and iOS implementations. +# Temporary path dependencies to allow moving Android and iOS implementations. + local_auth_android: + path: ../local_auth_android local_auth_ios: path: ../local_auth_ios local_auth_platform_interface: ^1.0.1 diff --git a/packages/local_auth/local_auth_android/AUTHORS b/packages/local_auth/local_auth_android/AUTHORS new file mode 100644 index 000000000000..d5694690c247 --- /dev/null +++ b/packages/local_auth/local_auth_android/AUTHORS @@ -0,0 +1,67 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> +Bodhi Mulders diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md new file mode 100644 index 000000000000..7f198f2d66c0 --- /dev/null +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +* Initial release from migration to federated architecture. diff --git a/packages/local_auth/local_auth_android/LICENSE b/packages/local_auth/local_auth_android/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/local_auth/local_auth_android/LICENSE @@ -0,0 +1,25 @@ +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/local_auth/local_auth_android/README.md b/packages/local_auth/local_auth_android/README.md new file mode 100644 index 000000000000..07244912f231 --- /dev/null +++ b/packages/local_auth/local_auth_android/README.md @@ -0,0 +1,11 @@ +# local\_auth\_android + +The Android implementation of [`local_auth`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use `local_auth` +normally. This package will be automatically included in your app when you do. + +[1]: https://pub.dev/packages/local_auth +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin \ No newline at end of file diff --git a/packages/local_auth/local_auth/android/build.gradle b/packages/local_auth/local_auth_android/android/build.gradle similarity index 100% rename from packages/local_auth/local_auth/android/build.gradle rename to packages/local_auth/local_auth_android/android/build.gradle diff --git a/packages/local_auth/local_auth/android/lint-baseline.xml b/packages/local_auth/local_auth_android/android/lint-baseline.xml similarity index 100% rename from packages/local_auth/local_auth/android/lint-baseline.xml rename to packages/local_auth/local_auth_android/android/lint-baseline.xml diff --git a/packages/local_auth/local_auth/android/settings.gradle b/packages/local_auth/local_auth_android/android/settings.gradle similarity index 100% rename from packages/local_auth/local_auth/android/settings.gradle rename to packages/local_auth/local_auth_android/android/settings.gradle diff --git a/packages/local_auth/local_auth/android/src/main/AndroidManifest.xml b/packages/local_auth/local_auth_android/android/src/main/AndroidManifest.xml similarity index 100% rename from packages/local_auth/local_auth/android/src/main/AndroidManifest.xml rename to packages/local_auth/local_auth_android/android/src/main/AndroidManifest.xml diff --git a/packages/local_auth/local_auth/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java b/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java similarity index 100% rename from packages/local_auth/local_auth/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java rename to packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java diff --git a/packages/local_auth/local_auth/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java b/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java similarity index 99% rename from packages/local_auth/local_auth/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java rename to packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java index a63e22a512d0..49a6b788fe46 100644 --- a/packages/local_auth/local_auth/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java +++ b/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java @@ -40,7 +40,7 @@ */ @SuppressWarnings("deprecation") public class LocalAuthPlugin implements MethodCallHandler, FlutterPlugin, ActivityAware { - private static final String CHANNEL_NAME = "plugins.flutter.io/local_auth"; + private static final String CHANNEL_NAME = "plugins.flutter.io/local_auth_android"; private static final int LOCK_REQUEST_CODE = 221; private Activity activity; private final AtomicBoolean authInProgress = new AtomicBoolean(false); diff --git a/packages/local_auth/local_auth/android/src/main/res/drawable/fingerprint_initial_icon.xml b/packages/local_auth/local_auth_android/android/src/main/res/drawable/fingerprint_initial_icon.xml similarity index 100% rename from packages/local_auth/local_auth/android/src/main/res/drawable/fingerprint_initial_icon.xml rename to packages/local_auth/local_auth_android/android/src/main/res/drawable/fingerprint_initial_icon.xml diff --git a/packages/local_auth/local_auth/android/src/main/res/drawable/fingerprint_success_icon.xml b/packages/local_auth/local_auth_android/android/src/main/res/drawable/fingerprint_success_icon.xml similarity index 100% rename from packages/local_auth/local_auth/android/src/main/res/drawable/fingerprint_success_icon.xml rename to packages/local_auth/local_auth_android/android/src/main/res/drawable/fingerprint_success_icon.xml diff --git a/packages/local_auth/local_auth/android/src/main/res/drawable/fingerprint_warning_icon.xml b/packages/local_auth/local_auth_android/android/src/main/res/drawable/fingerprint_warning_icon.xml similarity index 100% rename from packages/local_auth/local_auth/android/src/main/res/drawable/fingerprint_warning_icon.xml rename to packages/local_auth/local_auth_android/android/src/main/res/drawable/fingerprint_warning_icon.xml diff --git a/packages/local_auth/local_auth/android/src/main/res/drawable/ic_done_white_24dp.xml b/packages/local_auth/local_auth_android/android/src/main/res/drawable/ic_done_white_24dp.xml similarity index 100% rename from packages/local_auth/local_auth/android/src/main/res/drawable/ic_done_white_24dp.xml rename to packages/local_auth/local_auth_android/android/src/main/res/drawable/ic_done_white_24dp.xml diff --git a/packages/local_auth/local_auth/android/src/main/res/drawable/ic_fingerprint_white_24dp.xml b/packages/local_auth/local_auth_android/android/src/main/res/drawable/ic_fingerprint_white_24dp.xml similarity index 100% rename from packages/local_auth/local_auth/android/src/main/res/drawable/ic_fingerprint_white_24dp.xml rename to packages/local_auth/local_auth_android/android/src/main/res/drawable/ic_fingerprint_white_24dp.xml diff --git a/packages/local_auth/local_auth/android/src/main/res/drawable/ic_priority_high_white_24dp.xml b/packages/local_auth/local_auth_android/android/src/main/res/drawable/ic_priority_high_white_24dp.xml similarity index 100% rename from packages/local_auth/local_auth/android/src/main/res/drawable/ic_priority_high_white_24dp.xml rename to packages/local_auth/local_auth_android/android/src/main/res/drawable/ic_priority_high_white_24dp.xml diff --git a/packages/local_auth/local_auth/android/src/main/res/layout/go_to_setting.xml b/packages/local_auth/local_auth_android/android/src/main/res/layout/go_to_setting.xml similarity index 100% rename from packages/local_auth/local_auth/android/src/main/res/layout/go_to_setting.xml rename to packages/local_auth/local_auth_android/android/src/main/res/layout/go_to_setting.xml diff --git a/packages/local_auth/local_auth/android/src/main/res/layout/scan_fp.xml b/packages/local_auth/local_auth_android/android/src/main/res/layout/scan_fp.xml similarity index 100% rename from packages/local_auth/local_auth/android/src/main/res/layout/scan_fp.xml rename to packages/local_auth/local_auth_android/android/src/main/res/layout/scan_fp.xml diff --git a/packages/local_auth/local_auth/android/src/main/res/values/colors.xml b/packages/local_auth/local_auth_android/android/src/main/res/values/colors.xml similarity index 100% rename from packages/local_auth/local_auth/android/src/main/res/values/colors.xml rename to packages/local_auth/local_auth_android/android/src/main/res/values/colors.xml diff --git a/packages/local_auth/local_auth/android/src/main/res/values/dimens.xml b/packages/local_auth/local_auth_android/android/src/main/res/values/dimens.xml similarity index 100% rename from packages/local_auth/local_auth/android/src/main/res/values/dimens.xml rename to packages/local_auth/local_auth_android/android/src/main/res/values/dimens.xml diff --git a/packages/local_auth/local_auth/android/src/main/res/values/styles.xml b/packages/local_auth/local_auth_android/android/src/main/res/values/styles.xml similarity index 100% rename from packages/local_auth/local_auth/android/src/main/res/values/styles.xml rename to packages/local_auth/local_auth_android/android/src/main/res/values/styles.xml diff --git a/packages/local_auth/local_auth/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java b/packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java similarity index 100% rename from packages/local_auth/local_auth/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java rename to packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java diff --git a/packages/local_auth/local_auth_android/example/README.md b/packages/local_auth/local_auth_android/example/README.md new file mode 100644 index 000000000000..a4a6091c9ba6 --- /dev/null +++ b/packages/local_auth/local_auth_android/example/README.md @@ -0,0 +1,8 @@ +# local_auth_example + +Demonstrates how to use the local_auth plugin. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](https://flutter.dev/). diff --git a/packages/local_auth/local_auth_android/example/android/app/build.gradle b/packages/local_auth/local_auth_android/example/android/app/build.gradle new file mode 100644 index 000000000000..d1cef4bf53a9 --- /dev/null +++ b/packages/local_auth/local_auth_android/example/android/app/build.gradle @@ -0,0 +1,58 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 31 + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + applicationId "io.flutter.plugins.localauthexample" + minSdkVersion 16 + targetSdkVersion 28 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test:runner:1.1.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' +} diff --git a/packages/local_auth/local_auth_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/local_auth/local_auth_android/example/android/app/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..186b71557c50 --- /dev/null +++ b/packages/local_auth/local_auth_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/packages/local_auth/local_auth_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java b/packages/local_auth/local_auth_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java new file mode 100644 index 000000000000..0f4298dca155 --- /dev/null +++ b/packages/local_auth/local_auth_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java @@ -0,0 +1,14 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface DartIntegrationTest {} diff --git a/packages/local_auth/local_auth_android/example/android/app/src/androidTest/java/io/flutter/plugins/localauth/FlutterFragmentActivityTest.java b/packages/local_auth/local_auth_android/example/android/app/src/androidTest/java/io/flutter/plugins/localauth/FlutterFragmentActivityTest.java new file mode 100644 index 000000000000..68c22371d7dd --- /dev/null +++ b/packages/local_auth/local_auth_android/example/android/app/src/androidTest/java/io/flutter/plugins/localauth/FlutterFragmentActivityTest.java @@ -0,0 +1,20 @@ +// 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.localauth; + +import androidx.test.rule.ActivityTestRule; +import dev.flutter.plugins.integration_test.FlutterTestRunner; +import io.flutter.embedding.android.FlutterFragmentActivity; +import io.flutter.plugins.DartIntegrationTest; +import org.junit.Rule; +import org.junit.runner.RunWith; + +@DartIntegrationTest +@RunWith(FlutterTestRunner.class) +public class FlutterFragmentActivityTest { + @Rule + public ActivityTestRule rule = + new ActivityTestRule<>(FlutterFragmentActivity.class); +} diff --git a/packages/local_auth/local_auth_android/example/android/app/src/main/AndroidManifest.xml b/packages/local_auth/local_auth_android/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..8c091772107a --- /dev/null +++ b/packages/local_auth/local_auth_android/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + diff --git a/packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/packages/local_auth/local_auth_android/example/android/build.gradle b/packages/local_auth/local_auth_android/example/android/build.gradle new file mode 100644 index 000000000000..54c943621de5 --- /dev/null +++ b/packages/local_auth/local_auth_android/example/android/build.gradle @@ -0,0 +1,29 @@ +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:4.1.1' + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/packages/local_auth/local_auth_android/example/android/gradle.properties b/packages/local_auth/local_auth_android/example/android/gradle.properties new file mode 100644 index 000000000000..7fe61a74cee0 --- /dev/null +++ b/packages/local_auth/local_auth_android/example/android/gradle.properties @@ -0,0 +1,4 @@ +org.gradle.jvmargs=-Xmx1024m +android.useAndroidX=true +android.enableJetifier=true +android.enableR8=true diff --git a/packages/local_auth/local_auth_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/local_auth/local_auth_android/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..cd9fe1c68282 --- /dev/null +++ b/packages/local_auth/local_auth_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sun Jan 03 14:07:08 CST 2021 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip diff --git a/packages/local_auth/local_auth_android/example/android/settings.gradle b/packages/local_auth/local_auth_android/example/android/settings.gradle new file mode 100644 index 000000000000..115da6cb4f4d --- /dev/null +++ b/packages/local_auth/local_auth_android/example/android/settings.gradle @@ -0,0 +1,15 @@ +include ':app' + +def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + +def plugins = new Properties() +def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') +if (pluginsFile.exists()) { + pluginsFile.withInputStream { stream -> plugins.load(stream) } +} + +plugins.each { name, path -> + def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() + include ":$name" + project(":$name").projectDir = pluginDirectory +} diff --git a/packages/local_auth/local_auth_android/example/android/settings_aar.gradle b/packages/local_auth/local_auth_android/example/android/settings_aar.gradle new file mode 100644 index 000000000000..e7b4def49cb5 --- /dev/null +++ b/packages/local_auth/local_auth_android/example/android/settings_aar.gradle @@ -0,0 +1 @@ +include ':app' diff --git a/packages/local_auth/local_auth_android/example/integration_test/local_auth_test.dart b/packages/local_auth/local_auth_android/example/integration_test/local_auth_test.dart new file mode 100644 index 000000000000..1dfc0ae7a6d6 --- /dev/null +++ b/packages/local_auth/local_auth_android/example/integration_test/local_auth_test.dart @@ -0,0 +1,19 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + +import 'package:local_auth_android/local_auth_android.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('canCheckBiometrics', (WidgetTester tester) async { + expect( + LocalAuthAndroid().getEnrolledBiometrics(), + completion(isList), + ); + }); +} diff --git a/packages/local_auth/local_auth_android/example/lib/main.dart b/packages/local_auth/local_auth_android/example/lib/main.dart new file mode 100644 index 000000000000..4c045214734d --- /dev/null +++ b/packages/local_auth/local_auth_android/example/lib/main.dart @@ -0,0 +1,238 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:local_auth_android/local_auth_android.dart'; +import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatefulWidget { + @override + _MyAppState createState() => _MyAppState(); +} + +class _MyAppState extends State { + _SupportState _supportState = _SupportState.unknown; + bool? _canCheckBiometrics; + List? _availableBiometrics; + String _authorized = 'Not Authorized'; + bool _isAuthenticating = false; + + @override + void initState() { + super.initState(); + LocalAuthPlatform.instance.isDeviceSupported().then( + (bool isSupported) => setState(() => _supportState = isSupported + ? _SupportState.supported + : _SupportState.unsupported), + ); + } + + Future _checkBiometrics() async { + late bool canCheckBiometrics; + try { + canCheckBiometrics = + (await LocalAuthPlatform.instance.getEnrolledBiometrics()).isNotEmpty; + } on PlatformException catch (e) { + canCheckBiometrics = false; + print(e); + } + if (!mounted) { + return; + } + + setState(() { + _canCheckBiometrics = canCheckBiometrics; + }); + } + + Future _getEnrolledBiometrics() async { + late List availableBiometrics; + try { + availableBiometrics = + await LocalAuthPlatform.instance.getEnrolledBiometrics(); + } on PlatformException catch (e) { + availableBiometrics = []; + print(e); + } + if (!mounted) { + return; + } + + setState(() { + _availableBiometrics = availableBiometrics; + }); + } + + Future _authenticate() async { + bool authenticated = false; + try { + setState(() { + _isAuthenticating = true; + _authorized = 'Authenticating'; + }); + authenticated = await LocalAuthPlatform.instance.authenticate( + localizedReason: 'Let OS determine authentication method', + authMessages: [const AndroidAuthMessages()], + options: const AuthenticationOptions( + useErrorDialogs: true, + stickyAuth: true, + ), + ); + setState(() { + _isAuthenticating = false; + }); + } on PlatformException catch (e) { + print(e); + setState(() { + _isAuthenticating = false; + _authorized = 'Error - ${e.message}'; + }); + return; + } + if (!mounted) { + return; + } + + setState( + () => _authorized = authenticated ? 'Authorized' : 'Not Authorized'); + } + + Future _authenticateWithBiometrics() async { + bool authenticated = false; + try { + setState(() { + _isAuthenticating = true; + _authorized = 'Authenticating'; + }); + authenticated = await LocalAuthPlatform.instance.authenticate( + localizedReason: + 'Scan your fingerprint (or face or whatever) to authenticate', + authMessages: [const AndroidAuthMessages()], + options: const AuthenticationOptions( + useErrorDialogs: true, + stickyAuth: true, + biometricOnly: true, + ), + ); + setState(() { + _isAuthenticating = false; + _authorized = 'Authenticating'; + }); + } on PlatformException catch (e) { + print(e); + setState(() { + _isAuthenticating = false; + _authorized = 'Error - ${e.message}'; + }); + return; + } + if (!mounted) { + return; + } + + final String message = authenticated ? 'Authorized' : 'Not Authorized'; + setState(() { + _authorized = message; + }); + } + + Future _cancelAuthentication() async { + await LocalAuthPlatform.instance.stopAuthentication(); + setState(() => _isAuthenticating = false); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Plugin example app'), + ), + body: ListView( + padding: const EdgeInsets.only(top: 30), + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (_supportState == _SupportState.unknown) + const CircularProgressIndicator() + else if (_supportState == _SupportState.supported) + const Text('This device is supported') + else + const Text('This device is not supported'), + const Divider(height: 100), + Text('Can check biometrics: $_canCheckBiometrics\n'), + ElevatedButton( + child: const Text('Check biometrics'), + onPressed: _checkBiometrics, + ), + const Divider(height: 100), + Text('Available biometrics: $_availableBiometrics\n'), + ElevatedButton( + child: const Text('Get available biometrics'), + onPressed: _getEnrolledBiometrics, + ), + const Divider(height: 100), + Text('Current State: $_authorized\n'), + if (_isAuthenticating) + ElevatedButton( + onPressed: _cancelAuthentication, + child: Row( + mainAxisSize: MainAxisSize.min, + children: const [ + Text('Cancel Authentication'), + Icon(Icons.cancel), + ], + ), + ) + else + Column( + children: [ + ElevatedButton( + child: Row( + mainAxisSize: MainAxisSize.min, + children: const [ + Text('Authenticate'), + Icon(Icons.perm_device_information), + ], + ), + onPressed: _authenticate, + ), + ElevatedButton( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text(_isAuthenticating + ? 'Cancel' + : 'Authenticate: biometrics only'), + const Icon(Icons.fingerprint), + ], + ), + onPressed: _authenticateWithBiometrics, + ), + ], + ), + ], + ), + ], + ), + ), + ); + } +} + +enum _SupportState { + unknown, + supported, + unsupported, +} diff --git a/packages/local_auth/local_auth_android/example/pubspec.yaml b/packages/local_auth/local_auth_android/example/pubspec.yaml new file mode 100644 index 000000000000..c07a81d2be3b --- /dev/null +++ b/packages/local_auth/local_auth_android/example/pubspec.yaml @@ -0,0 +1,28 @@ +name: local_auth_android_example +description: Demonstrates how to use the local_auth_android plugin. +publish_to: none + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.8.0" + +dependencies: + flutter: + sdk: flutter + local_auth_android: + # When depending on this package from a real application you should use: + # local_auth_android: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + local_auth_platform_interface: ^1.0.0 + +dev_dependencies: + flutter_driver: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/local_auth/local_auth_android/example/test_driver/integration_test.dart b/packages/local_auth/local_auth_android/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4f10f2a522f3 --- /dev/null +++ b/packages/local_auth/local_auth_android/example/test_driver/integration_test.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); diff --git a/packages/local_auth/local_auth_android/lib/local_auth_android.dart b/packages/local_auth/local_auth_android/lib/local_auth_android.dart new file mode 100644 index 000000000000..a3f314e3347b --- /dev/null +++ b/packages/local_auth/local_auth_android/lib/local_auth_android.dart @@ -0,0 +1,87 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; +import 'package:local_auth_android/types/auth_messages_android.dart'; +import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; +import 'package:local_auth_platform_interface/types/auth_messages.dart'; +import 'package:local_auth_platform_interface/types/auth_options.dart'; +import 'package:local_auth_platform_interface/types/biometric_type.dart'; + +export 'package:local_auth_android/types/auth_messages_android.dart'; +export 'package:local_auth_platform_interface/types/auth_messages.dart'; +export 'package:local_auth_platform_interface/types/auth_options.dart'; +export 'package:local_auth_platform_interface/types/biometric_type.dart'; + +const MethodChannel _channel = + MethodChannel('plugins.flutter.io/local_auth_android'); + +/// The implementation of [LocalAuthPlatform] for Android. +class LocalAuthAndroid extends LocalAuthPlatform { + /// Registers this class as the default instance of [LocalAuthPlatform]. + static void registerWith() { + LocalAuthPlatform.instance = LocalAuthAndroid(); + } + + @override + Future authenticate({ + required String localizedReason, + required Iterable authMessages, + AuthenticationOptions options = const AuthenticationOptions(), + }) async { + assert(localizedReason.isNotEmpty); + final Map args = { + 'localizedReason': localizedReason, + 'useErrorDialogs': options.useErrorDialogs, + 'stickyAuth': options.stickyAuth, + 'sensitiveTransaction': options.sensitiveTransaction, + 'biometricOnly': options.biometricOnly, + }; + args.addAll(const AndroidAuthMessages().args); + for (final AuthMessages messages in authMessages) { + if (messages is AndroidAuthMessages) { + args.addAll(messages.args); + } + } + return (await _channel.invokeMethod('authenticate', args)) ?? false; + } + + @override + Future deviceSupportsBiometrics() async { + return (await getEnrolledBiometrics()).isNotEmpty; + } + + @override + Future> getEnrolledBiometrics() async { + final List result = (await _channel.invokeListMethod( + 'getAvailableBiometrics', + )) ?? + []; + final List biometrics = []; + for (final String value in result) { + switch (value) { + case 'face': + biometrics.add(BiometricType.face); + break; + case 'fingerprint': + biometrics.add(BiometricType.fingerprint); + break; + case 'iris': + biometrics.add(BiometricType.iris); + break; + case 'undefined': + break; + } + } + return biometrics; + } + + @override + Future isDeviceSupported() async => + (await _channel.invokeMethod('isDeviceSupported')) ?? false; + + @override + Future stopAuthentication() async => + await _channel.invokeMethod('stopAuthentication') ?? false; +} diff --git a/packages/local_auth/local_auth_android/lib/types/auth_messages_android.dart b/packages/local_auth/local_auth_android/lib/types/auth_messages_android.dart new file mode 100644 index 000000000000..ea61a4b06d4e --- /dev/null +++ b/packages/local_auth/local_auth_android/lib/types/auth_messages_android.dart @@ -0,0 +1,191 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; +import 'package:intl/intl.dart'; +import 'package:local_auth_platform_interface/types/auth_messages.dart'; + +/// Android side authentication messages. +/// +/// Provides default values for all messages. +@immutable +class AndroidAuthMessages extends AuthMessages { + /// Constructs a new instance. + const AndroidAuthMessages({ + this.biometricHint, + this.biometricNotRecognized, + this.biometricRequiredTitle, + this.biometricSuccess, + this.cancelButton, + this.deviceCredentialsRequiredTitle, + this.deviceCredentialsSetupDescription, + this.goToSettingsButton, + this.goToSettingsDescription, + this.signInTitle, + }); + + /// Hint message advising the user how to authenticate with biometrics. + /// Maximum 60 characters. + final String? biometricHint; + + /// Message to let the user know that authentication was failed. + /// Maximum 60 characters. + final String? biometricNotRecognized; + + /// Message shown as a title in a dialog which indicates the user + /// has not set up biometric authentication on their device. + /// Maximum 60 characters. + final String? biometricRequiredTitle; + + /// Message to let the user know that authentication was successful. + /// Maximum 60 characters + final String? biometricSuccess; + + /// Message shown on a button that the user can click to leave the + /// current dialog. + /// Maximum 30 characters. + final String? cancelButton; + + /// Message shown as a title in a dialog which indicates the user + /// has not set up credentials authentication on their device. + /// Maximum 60 characters. + final String? deviceCredentialsRequiredTitle; + + /// Message advising the user to go to the settings and configure + /// device credentials on their device. + final String? deviceCredentialsSetupDescription; + + /// Message shown on a button that the user can click to go to settings pages + /// from the current dialog. + /// Maximum 30 characters. + final String? goToSettingsButton; + + /// Message advising the user to go to the settings and configure + /// biometric on their device. + final String? goToSettingsDescription; + + /// Message shown as a title in a dialog which indicates the user + /// that they need to scan biometric to continue. + /// Maximum 60 characters. + final String? signInTitle; + + @override + Map get args { + return { + 'biometricHint': biometricHint ?? androidBiometricHint, + 'biometricNotRecognized': + biometricNotRecognized ?? androidBiometricNotRecognized, + 'biometricSuccess': biometricSuccess ?? androidBiometricSuccess, + 'biometricRequired': + biometricRequiredTitle ?? androidBiometricRequiredTitle, + 'cancelButton': cancelButton ?? androidCancelButton, + 'deviceCredentialsRequired': deviceCredentialsRequiredTitle ?? + androidDeviceCredentialsRequiredTitle, + 'deviceCredentialsSetupDescription': deviceCredentialsSetupDescription ?? + androidDeviceCredentialsSetupDescription, + 'goToSetting': goToSettingsButton ?? goToSettings, + 'goToSettingDescription': + goToSettingsDescription ?? androidGoToSettingsDescription, + 'signInTitle': signInTitle ?? androidSignInTitle, + }; + } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is AndroidAuthMessages && + runtimeType == other.runtimeType && + biometricHint == other.biometricHint && + biometricNotRecognized == other.biometricNotRecognized && + biometricRequiredTitle == other.biometricRequiredTitle && + biometricSuccess == other.biometricSuccess && + cancelButton == other.cancelButton && + deviceCredentialsRequiredTitle == + other.deviceCredentialsRequiredTitle && + deviceCredentialsSetupDescription == + other.deviceCredentialsSetupDescription && + goToSettingsButton == other.goToSettingsButton && + goToSettingsDescription == other.goToSettingsDescription && + signInTitle == other.signInTitle; + + @override + int get hashCode => + biometricHint.hashCode ^ + biometricNotRecognized.hashCode ^ + biometricRequiredTitle.hashCode ^ + biometricSuccess.hashCode ^ + cancelButton.hashCode ^ + deviceCredentialsRequiredTitle.hashCode ^ + deviceCredentialsSetupDescription.hashCode ^ + goToSettingsButton.hashCode ^ + goToSettingsDescription.hashCode ^ + signInTitle.hashCode; +} + +// Default strings for AndroidAuthMessages. Currently supports English. +// Intl.message must be string literals. + +/// Message shown on a button that the user can click to go to settings pages +/// from the current dialog. +String get goToSettings => Intl.message('Go to settings', + desc: 'Message shown on a button that the user can click to go to ' + 'settings pages from the current dialog. Maximum 30 characters.'); + +/// Hint message advising the user how to authenticate with biometrics. +String get androidBiometricHint => Intl.message('Verify identity', + desc: 'Hint message advising the user how to authenticate with biometrics. ' + 'Maximum 60 characters.'); + +/// Message to let the user know that authentication was failed. +String get androidBiometricNotRecognized => + Intl.message('Not recognized. Try again.', + desc: 'Message to let the user know that authentication was failed. ' + 'Maximum 60 characters.'); + +/// Message to let the user know that authentication was successful. It +String get androidBiometricSuccess => Intl.message('Success', + desc: 'Message to let the user know that authentication was successful. ' + 'Maximum 60 characters.'); + +/// Message shown on a button that the user can click to leave the +/// current dialog. +String get androidCancelButton => Intl.message('Cancel', + desc: 'Message shown on a button that the user can click to leave the ' + 'current dialog. Maximum 30 characters.'); + +/// Message shown as a title in a dialog which indicates the user +/// that they need to scan biometric to continue. +String get androidSignInTitle => Intl.message('Authentication required', + desc: 'Message shown as a title in a dialog which indicates the user ' + 'that they need to scan biometric to continue. Maximum 60 characters.'); + +/// Message shown as a title in a dialog which indicates the user +/// has not set up biometric authentication on their device. +String get androidBiometricRequiredTitle => Intl.message('Biometric required', + desc: 'Message shown as a title in a dialog which indicates the user ' + 'has not set up biometric authentication on their device. ' + 'Maximum 60 characters.'); + +/// Message shown as a title in a dialog which indicates the user +/// has not set up credentials authentication on their device. +String get androidDeviceCredentialsRequiredTitle => + Intl.message('Device credentials required', + desc: 'Message shown as a title in a dialog which indicates the user ' + 'has not set up credentials authentication on their device. ' + 'Maximum 60 characters.'); + +/// Message advising the user to go to the settings and configure +/// device credentials on their device. +String get androidDeviceCredentialsSetupDescription => + Intl.message('Device credentials required', + desc: 'Message advising the user to go to the settings and configure ' + 'device credentials on their device.'); + +/// Message advising the user to go to the settings and configure +/// biometric on their device. +String get androidGoToSettingsDescription => Intl.message( + 'Biometric authentication is not set up on your device. Go to ' + '\'Settings > Security\' to add biometric authentication.', + desc: 'Message advising the user to go to the settings and configure ' + 'biometric on their device.'); diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml new file mode 100644 index 000000000000..ec2991db6a90 --- /dev/null +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -0,0 +1,29 @@ +name: local_auth_android +description: Android implementation of the local_auth plugin. +repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_android +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 +version: 1.0.0 + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.8.0" + +flutter: + plugin: + implements: local_auth + platforms: + android: + package: io.flutter.plugins.localauth + pluginClass: LocalAuthPlugin + dartPluginClass: LocalAuthAndroid + +dependencies: + flutter: + sdk: flutter + flutter_plugin_android_lifecycle: ^2.0.1 + intl: ^0.17.0 + local_auth_platform_interface: ^1.0.0 + +dev_dependencies: + flutter_test: + sdk: flutter \ No newline at end of file diff --git a/packages/local_auth/local_auth_android/test/local_auth_test.dart b/packages/local_auth/local_auth_android/test/local_auth_test.dart new file mode 100644 index 000000000000..31f5e5796445 --- /dev/null +++ b/packages/local_auth/local_auth_android/test/local_auth_test.dart @@ -0,0 +1,181 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:local_auth_android/local_auth_android.dart'; +import 'package:local_auth_platform_interface/types/auth_messages.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('LocalAuth', () { + const MethodChannel channel = MethodChannel( + 'plugins.flutter.io/local_auth_android', + ); + + final List log = []; + late LocalAuthAndroid localAuthentication; + + setUp(() { + channel.setMockMethodCallHandler((MethodCall methodCall) { + log.add(methodCall); + switch (methodCall.method) { + case 'getAvailableBiometrics': + return Future>.value( + ['face', 'fingerprint', 'iris', 'undefined']); + default: + return Future.value(true); + } + }); + localAuthentication = LocalAuthAndroid(); + log.clear(); + }); + + test('deviceSupportsBiometrics calls getEnrolledBiometrics', () async { + final bool result = await localAuthentication.deviceSupportsBiometrics(); + + expect( + log, + [ + isMethodCall('getAvailableBiometrics', arguments: null), + ], + ); + expect(result, true); + }); + + test('getEnrolledBiometrics calls platform', () async { + final List result = + await localAuthentication.getEnrolledBiometrics(); + + expect( + log, + [ + isMethodCall('getAvailableBiometrics', arguments: null), + ], + ); + expect(result, [ + BiometricType.face, + BiometricType.fingerprint, + BiometricType.iris + ]); + }); + + test('isDeviceSupported calls platform', () async { + await localAuthentication.isDeviceSupported(); + expect( + log, + [ + isMethodCall('isDeviceSupported', arguments: null), + ], + ); + }); + + test('stopAuthentication calls platform', () async { + await localAuthentication.stopAuthentication(); + expect( + log, + [ + isMethodCall('stopAuthentication', arguments: null), + ], + ); + }); + + group('With device auth fail over', () { + test('authenticate with no args.', () async { + await localAuthentication.authenticate( + authMessages: [const AndroidAuthMessages()], + localizedReason: 'Needs secure', + options: const AuthenticationOptions(biometricOnly: true), + ); + expect( + log, + [ + isMethodCall('authenticate', + arguments: { + 'localizedReason': 'Needs secure', + 'useErrorDialogs': true, + 'stickyAuth': false, + 'sensitiveTransaction': true, + 'biometricOnly': true, + }..addAll(const AndroidAuthMessages().args)), + ], + ); + }); + + test('authenticate with no sensitive transaction.', () async { + await localAuthentication.authenticate( + authMessages: [const AndroidAuthMessages()], + localizedReason: 'Insecure', + options: const AuthenticationOptions( + sensitiveTransaction: false, + useErrorDialogs: false, + biometricOnly: true, + ), + ); + expect( + log, + [ + isMethodCall('authenticate', + arguments: { + 'localizedReason': 'Insecure', + 'useErrorDialogs': false, + 'stickyAuth': false, + 'sensitiveTransaction': false, + 'biometricOnly': true, + }..addAll(const AndroidAuthMessages().args)), + ], + ); + }); + }); + + group('With biometrics only', () { + test('authenticate with no args.', () async { + await localAuthentication.authenticate( + authMessages: [const AndroidAuthMessages()], + localizedReason: 'Needs secure', + ); + expect( + log, + [ + isMethodCall('authenticate', + arguments: { + 'localizedReason': 'Needs secure', + 'useErrorDialogs': true, + 'stickyAuth': false, + 'sensitiveTransaction': true, + 'biometricOnly': false, + }..addAll(const AndroidAuthMessages().args)), + ], + ); + }); + + test('authenticate with no sensitive transaction.', () async { + await localAuthentication.authenticate( + authMessages: [const AndroidAuthMessages()], + localizedReason: 'Insecure', + options: const AuthenticationOptions( + sensitiveTransaction: false, + useErrorDialogs: false, + ), + ); + expect( + log, + [ + isMethodCall('authenticate', + arguments: { + 'localizedReason': 'Insecure', + 'useErrorDialogs': false, + 'stickyAuth': false, + 'sensitiveTransaction': false, + 'biometricOnly': false, + }..addAll(const AndroidAuthMessages().args)), + ], + ); + }); + }); + }); +} From 4ee959fc7f95cf3f57464c2db8432987c6065a19 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 11 Apr 2022 21:29:16 -0400 Subject: [PATCH 375/600] Roll Flutter from 6b461de0129e to 355fd23a799d (4 revisions) (#5230) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 1eec7021c4be..ab6577828f8d 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -6b461de0129ef7215118ef9d76e77bfa4c5f01df +355fd23a799dd437dd8d7067d75a04000d13c889 From 737e6282d971e802e64f4791827dbaaee116a411 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 11 Apr 2022 23:39:12 -0400 Subject: [PATCH 376/600] Roll Flutter from 355fd23a799d to 8a899d4a74ff (4 revisions) (#5232) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index ab6577828f8d..5366d5a15553 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -355fd23a799dd437dd8d7067d75a04000d13c889 +8a899d4a74ff131648e9e9633db5151c275f1f08 From c3307440476bae722735cac63808886cc692da5a Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 12 Apr 2022 10:24:14 -0400 Subject: [PATCH 377/600] Roll Flutter from 8a899d4a74ff to 9e0f0fe9d7b8 (2 revisions) (#5233) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 5366d5a15553..25c6ddc070f4 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -8a899d4a74ff131648e9e9633db5151c275f1f08 +9e0f0fe9d7b8fc42195c84c521bf5809c9dbbfe4 From e39b3effb09434de7071190e70323d55dfd806d7 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 12 Apr 2022 14:19:12 -0400 Subject: [PATCH 378/600] [google_sign_in] Federate mobile implementations (#5214) --- .../google_sign_in/CHANGELOG.md | 4 + .../google_sign_in/android/settings.gradle | 1 - .../google_sign_in/example/ios/Podfile | 5 - .../ios/Runner.xcodeproj/project.pbxproj | 249 ------ .../google_sign_in/pubspec.yaml | 14 +- .../google_sign_in_android/AUTHORS | 66 ++ .../google_sign_in_android/CHANGELOG.md | 3 + .../google_sign_in_android/LICENSE | 25 + .../google_sign_in_android/README.md | 11 + .../android/build.gradle | 0 .../android/settings.gradle | 1 + .../android/src/main/AndroidManifest.xml | 0 .../googlesignin/BackgroundTaskRunner.java | 0 .../plugins/googlesignin/Executors.java | 0 .../googlesignin/GoogleSignInPlugin.java | 0 .../googlesignin/GoogleSignInWrapper.java | 0 .../googlesignin/GoogleSignInTest.java | 0 .../google_sign_in_android/example/README.md | 8 + .../example/android/app/build.gradle | 67 ++ .../example/android/app/google-services.json | 246 ++++++ .../gradle/wrapper/gradle-wrapper.properties | 5 + .../flutter/plugins/DartIntegrationTest.java | 14 + .../FlutterActivityTest.java | 19 + .../googlesigninexample/GoogleSignInTest.java | 0 .../android/app/src/debug/AndroidManifest.xml | 17 + .../android/app/src/main/AndroidManifest.xml | 19 + .../main/java/io/flutter/plugins/.gitignore | 1 + .../GoogleSignInTestActivity.java | 20 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values/strings.xml | 4 + .../example/android/build.gradle | 29 + .../example/android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + .../example/android/settings.gradle | 15 + .../integration_test/google_sign_in_test.dart | 16 + .../example/lib/main.dart | 179 +++++ .../example/pubspec.yaml | 30 + .../example/test_driver/integration_test.dart | 7 + .../google_sign_in_android/pubspec.yaml | 35 + .../google_sign_in/google_sign_in_ios/AUTHORS | 66 ++ .../google_sign_in_ios/CHANGELOG.md | 3 + .../google_sign_in/google_sign_in_ios/LICENSE | 25 + .../google_sign_in_ios/README.md | 11 + .../google_sign_in_ios/example/README.md | 8 + .../integration_test/google_sign_in_test.dart | 16 + .../ios/Flutter/AppFrameworkInfo.plist | 30 + .../example/ios/Flutter/Debug.xcconfig | 2 + .../example/ios/Flutter/Release.xcconfig | 2 + .../ios/GoogleSignInPluginTest}/Info.plist | 0 .../google_sign_in_ios/example/ios/Podfile | 47 ++ .../ios/Runner.xcodeproj/project.pbxproj | 740 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/xcschemes/Runner.xcscheme | 107 +++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/ios/Runner/AppDelegate.h | 10 + .../example/ios/Runner/AppDelegate.m | 17 + .../AppIcon.appiconset/Contents.json | 116 +++ .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 564 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 1588 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 1025 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 1716 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 1920 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 1895 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 3831 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 1888 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 3294 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 3612 bytes .../Runner/Base.lproj/LaunchScreen.storyboard | 27 + .../ios/Runner/Base.lproj/Main.storyboard | 26 + .../ios/Runner/GoogleService-Info.plist | 44 ++ .../example/ios/Runner/Info.plist | 64 ++ .../example/ios/Runner/main.m | 13 + .../ios/RunnerTests/GoogleSignInTests.m | 4 +- .../example/ios/RunnerTests}/Info.plist | 0 .../ios/RunnerUITests/GoogleSignInUITests.m | 0 .../example/ios/RunnerUITests/Info.plist | 22 + .../google_sign_in_ios/example/lib/main.dart | 179 +++++ .../google_sign_in_ios/example/pubspec.yaml | 29 + .../example/test_driver/integration_test.dart | 7 + .../ios/Assets/.gitkeep | 0 .../ios/Classes/FLTGoogleSignInPlugin.h | 0 .../ios/Classes/FLTGoogleSignInPlugin.m | 0 .../Classes/FLTGoogleSignInPlugin.modulemap | 4 +- .../ios/Classes/FLTGoogleSignInPlugin_Test.h | 2 +- .../Classes/google_sign_in_ios-umbrella.h} | 2 +- .../ios/google_sign_in_ios.podspec} | 4 +- .../google_sign_in_ios/pubspec.yaml | 35 + 96 files changed, 2539 insertions(+), 266 deletions(-) delete mode 100644 packages/google_sign_in/google_sign_in/android/settings.gradle create mode 100644 packages/google_sign_in/google_sign_in_android/AUTHORS create mode 100644 packages/google_sign_in/google_sign_in_android/CHANGELOG.md create mode 100644 packages/google_sign_in/google_sign_in_android/LICENSE create mode 100644 packages/google_sign_in/google_sign_in_android/README.md rename packages/google_sign_in/{google_sign_in => google_sign_in_android}/android/build.gradle (100%) create mode 100644 packages/google_sign_in/google_sign_in_android/android/settings.gradle rename packages/google_sign_in/{google_sign_in => google_sign_in_android}/android/src/main/AndroidManifest.xml (100%) rename packages/google_sign_in/{google_sign_in => google_sign_in_android}/android/src/main/java/io/flutter/plugins/googlesignin/BackgroundTaskRunner.java (100%) rename packages/google_sign_in/{google_sign_in => google_sign_in_android}/android/src/main/java/io/flutter/plugins/googlesignin/Executors.java (100%) rename packages/google_sign_in/{google_sign_in => google_sign_in_android}/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java (100%) rename packages/google_sign_in/{google_sign_in => google_sign_in_android}/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInWrapper.java (100%) rename packages/google_sign_in/{google_sign_in => google_sign_in_android}/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java (100%) create mode 100644 packages/google_sign_in/google_sign_in_android/example/README.md create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/build.gradle create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/google-services.json create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/src/androidTest/java/io/flutter/plugins/googlesigninexample/FlutterActivityTest.java rename packages/google_sign_in/{google_sign_in => google_sign_in_android}/example/android/app/src/androidTest/java/io/flutter/plugins/googlesigninexample/GoogleSignInTest.java (100%) create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/src/debug/AndroidManifest.xml create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/src/main/AndroidManifest.xml create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/src/main/java/io/flutter/plugins/.gitignore create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/GoogleSignInTestActivity.java create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/src/main/res/values/strings.xml create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/build.gradle create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/gradle.properties create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/settings.gradle create mode 100644 packages/google_sign_in/google_sign_in_android/example/integration_test/google_sign_in_test.dart create mode 100644 packages/google_sign_in/google_sign_in_android/example/lib/main.dart create mode 100644 packages/google_sign_in/google_sign_in_android/example/pubspec.yaml create mode 100644 packages/google_sign_in/google_sign_in_android/example/test_driver/integration_test.dart create mode 100644 packages/google_sign_in/google_sign_in_android/pubspec.yaml create mode 100644 packages/google_sign_in/google_sign_in_ios/AUTHORS create mode 100644 packages/google_sign_in/google_sign_in_ios/CHANGELOG.md create mode 100644 packages/google_sign_in/google_sign_in_ios/LICENSE create mode 100644 packages/google_sign_in/google_sign_in_ios/README.md create mode 100644 packages/google_sign_in/google_sign_in_ios/example/README.md create mode 100644 packages/google_sign_in/google_sign_in_ios/example/integration_test/google_sign_in_test.dart create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Flutter/AppFrameworkInfo.plist create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Flutter/Debug.xcconfig create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Flutter/Release.xcconfig rename packages/google_sign_in/{google_sign_in/example/ios/RunnerTests => google_sign_in_ios/example/ios/GoogleSignInPluginTest}/Info.plist (100%) create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Podfile create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/AppDelegate.h create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/AppDelegate.m create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Base.lproj/Main.storyboard create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/GoogleService-Info.plist create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Info.plist create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/main.m rename packages/google_sign_in/{google_sign_in => google_sign_in_ios}/example/ios/RunnerTests/GoogleSignInTests.m (99%) rename packages/google_sign_in/{google_sign_in/example/ios/RunnerUITests => google_sign_in_ios/example/ios/RunnerTests}/Info.plist (100%) rename packages/google_sign_in/{google_sign_in => google_sign_in_ios}/example/ios/RunnerUITests/GoogleSignInUITests.m (100%) create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/RunnerUITests/Info.plist create mode 100644 packages/google_sign_in/google_sign_in_ios/example/lib/main.dart create mode 100644 packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml create mode 100644 packages/google_sign_in/google_sign_in_ios/example/test_driver/integration_test.dart rename packages/google_sign_in/{google_sign_in => google_sign_in_ios}/ios/Assets/.gitkeep (100%) rename packages/google_sign_in/{google_sign_in => google_sign_in_ios}/ios/Classes/FLTGoogleSignInPlugin.h (100%) rename packages/google_sign_in/{google_sign_in => google_sign_in_ios}/ios/Classes/FLTGoogleSignInPlugin.m (100%) rename packages/google_sign_in/{google_sign_in => google_sign_in_ios}/ios/Classes/FLTGoogleSignInPlugin.modulemap (55%) rename packages/google_sign_in/{google_sign_in => google_sign_in_ios}/ios/Classes/FLTGoogleSignInPlugin_Test.h (89%) rename packages/google_sign_in/{google_sign_in/ios/Classes/google_sign_in-umbrella.h => google_sign_in_ios/ios/Classes/google_sign_in_ios-umbrella.h} (85%) rename packages/google_sign_in/{google_sign_in/ios/google_sign_in.podspec => google_sign_in_ios/ios/google_sign_in_ios.podspec} (90%) create mode 100644 packages/google_sign_in/google_sign_in_ios/pubspec.yaml diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index 24b729c4d78a..bee4dafaf16f 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Moves Android and iOS implementations to federated packages. + ## 5.2.5 * Migrates from `ui.hash*` to `Object.hash*`. diff --git a/packages/google_sign_in/google_sign_in/android/settings.gradle b/packages/google_sign_in/google_sign_in/android/settings.gradle deleted file mode 100644 index d943fae5ece0..000000000000 --- a/packages/google_sign_in/google_sign_in/android/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'googlesignin' diff --git a/packages/google_sign_in/google_sign_in/example/ios/Podfile b/packages/google_sign_in/google_sign_in/example/ios/Podfile index e577a3081fe8..56085c312df7 100644 --- a/packages/google_sign_in/google_sign_in/example/ios/Podfile +++ b/packages/google_sign_in/google_sign_in/example/ios/Podfile @@ -29,11 +29,6 @@ flutter_ios_podfile_setup target 'Runner' do flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) - target 'RunnerTests' do - inherit! :search_paths - - pod 'OCMock','3.5' - end end post_install do |installer| diff --git a/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/project.pbxproj b/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/project.pbxproj index 06857ed2bd59..8909bb9b31c6 100644 --- a/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/project.pbxproj @@ -16,28 +16,8 @@ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; C2FB9CBA01DB0A2DE5F31E12 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0263E28FA425D1CE928BDE15 /* libPods-Runner.a */; }; - C56D3B06A42F3B35C1F47A43 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18AD6475292B9C45B529DDC9 /* libPods-RunnerTests.a */; }; - F76AC1A52666D0540040C8BC /* GoogleSignInTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F76AC1A42666D0540040C8BC /* GoogleSignInTests.m */; }; - F76AC1B32666D0610040C8BC /* GoogleSignInUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = F76AC1B22666D0610040C8BC /* GoogleSignInUITests.m */; }; /* End PBXBuildFile section */ -/* Begin PBXContainerItemProxy section */ - F76AC1A72666D0540040C8BC /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 97C146E61CF9000F007C117D /* Project object */; - proxyType = 1; - remoteGlobalIDString = 97C146ED1CF9000F007C117D; - remoteInfo = Runner; - }; - F76AC1B52666D0610040C8BC /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 97C146E61CF9000F007C117D /* Project object */; - proxyType = 1; - remoteGlobalIDString = 97C146ED1CF9000F007C117D; - remoteInfo = Runner; - }; -/* End PBXContainerItemProxy section */ - /* Begin PBXCopyFilesBuildPhase section */ 9705A1C41CF9048500538489 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; @@ -73,12 +53,6 @@ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; F582639B44581540871D9BB0 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - F76AC1A22666D0540040C8BC /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - F76AC1A42666D0540040C8BC /* GoogleSignInTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoogleSignInTests.m; sourceTree = ""; }; - F76AC1A62666D0540040C8BC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - F76AC1B02666D0610040C8BC /* RunnerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - F76AC1B22666D0610040C8BC /* GoogleSignInUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoogleSignInUITests.m; sourceTree = ""; }; - F76AC1B42666D0610040C8BC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -90,21 +64,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - F76AC19F2666D0540040C8BC /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - C56D3B06A42F3B35C1F47A43 /* libPods-RunnerTests.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F76AC1AD2666D0610040C8BC /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -135,8 +94,6 @@ children = ( 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, - F76AC1A32666D0540040C8BC /* RunnerTests */, - F76AC1B12666D0610040C8BC /* RunnerUITests */, 97C146EF1CF9000F007C117D /* Products */, 840012C8B5EDBCF56B0E4AC1 /* Pods */, CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, @@ -147,8 +104,6 @@ isa = PBXGroup; children = ( 97C146EE1CF9000F007C117D /* Runner.app */, - F76AC1A22666D0540040C8BC /* RunnerTests.xctest */, - F76AC1B02666D0610040C8BC /* RunnerUITests.xctest */, ); name = Products; sourceTree = ""; @@ -187,24 +142,6 @@ name = Frameworks; sourceTree = ""; }; - F76AC1A32666D0540040C8BC /* RunnerTests */ = { - isa = PBXGroup; - children = ( - F76AC1A42666D0540040C8BC /* GoogleSignInTests.m */, - F76AC1A62666D0540040C8BC /* Info.plist */, - ); - path = RunnerTests; - sourceTree = ""; - }; - F76AC1B12666D0610040C8BC /* RunnerUITests */ = { - isa = PBXGroup; - children = ( - F76AC1B22666D0610040C8BC /* GoogleSignInUITests.m */, - F76AC1B42666D0610040C8BC /* Info.plist */, - ); - path = RunnerUITests; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -230,43 +167,6 @@ productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; }; - F76AC1A12666D0540040C8BC /* RunnerTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = F76AC1AB2666D0540040C8BC /* Build configuration list for PBXNativeTarget "RunnerTests" */; - buildPhases = ( - 27975964E48117AA65B1D6C7 /* [CP] Check Pods Manifest.lock */, - F76AC19E2666D0540040C8BC /* Sources */, - F76AC19F2666D0540040C8BC /* Frameworks */, - F76AC1A02666D0540040C8BC /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - F76AC1A82666D0540040C8BC /* PBXTargetDependency */, - ); - name = RunnerTests; - productName = RunnerTests; - productReference = F76AC1A22666D0540040C8BC /* RunnerTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - F76AC1AF2666D0610040C8BC /* RunnerUITests */ = { - isa = PBXNativeTarget; - buildConfigurationList = F76AC1B72666D0610040C8BC /* Build configuration list for PBXNativeTarget "RunnerUITests" */; - buildPhases = ( - F76AC1AC2666D0610040C8BC /* Sources */, - F76AC1AD2666D0610040C8BC /* Frameworks */, - F76AC1AE2666D0610040C8BC /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - F76AC1B62666D0610040C8BC /* PBXTargetDependency */, - ); - name = RunnerUITests; - productName = RunnerUITests; - productReference = F76AC1B02666D0610040C8BC /* RunnerUITests.xctest */; - productType = "com.apple.product-type.bundle.ui-testing"; - }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -279,16 +179,6 @@ 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; }; - F76AC1A12666D0540040C8BC = { - CreatedOnToolsVersion = 12.5; - ProvisioningStyle = Automatic; - TestTargetID = 97C146ED1CF9000F007C117D; - }; - F76AC1AF2666D0610040C8BC = { - CreatedOnToolsVersion = 12.5; - ProvisioningStyle = Automatic; - TestTargetID = 97C146ED1CF9000F007C117D; - }; }; }; buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; @@ -305,8 +195,6 @@ projectRoot = ""; targets = ( 97C146ED1CF9000F007C117D /* Runner */, - F76AC1A12666D0540040C8BC /* RunnerTests */, - F76AC1AF2666D0610040C8BC /* RunnerUITests */, ); }; /* End PBXProject section */ @@ -324,45 +212,9 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - F76AC1A02666D0540040C8BC /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F76AC1AE2666D0610040C8BC /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 27975964E48117AA65B1D6C7 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -440,37 +292,8 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - F76AC19E2666D0540040C8BC /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - F76AC1A52666D0540040C8BC /* GoogleSignInTests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F76AC1AC2666D0610040C8BC /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - F76AC1B32666D0610040C8BC /* GoogleSignInUITests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXTargetDependency section */ - F76AC1A82666D0540040C8BC /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 97C146ED1CF9000F007C117D /* Runner */; - targetProxy = F76AC1A72666D0540040C8BC /* PBXContainerItemProxy */; - }; - F76AC1B62666D0610040C8BC /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 97C146ED1CF9000F007C117D /* Runner */; - targetProxy = F76AC1B52666D0610040C8BC /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - /* Begin PBXVariantGroup section */ 97C146FA1CF9000F007C117D /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -641,60 +464,6 @@ }; name = Release; }; - F76AC1A92666D0540040C8BC /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 37E582FF620A90D0EB2C0851 /* Pods-RunnerTests.debug.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; - INFOPLIST_FILE = RunnerTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; - }; - name = Debug; - }; - F76AC1AA2666D0540040C8BC /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 45D93D4513839BFEA2AA74FE /* Pods-RunnerTests.release.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; - INFOPLIST_FILE = RunnerTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; - }; - name = Release; - }; - F76AC1B82666D0610040C8BC /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = RunnerUITests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_TARGET_NAME = Runner; - }; - name = Debug; - }; - F76AC1B92666D0610040C8BC /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = RunnerUITests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_TARGET_NAME = Runner; - }; - name = Release; - }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -716,24 +485,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - F76AC1AB2666D0540040C8BC /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F76AC1A92666D0540040C8BC /* Debug */, - F76AC1AA2666D0540040C8BC /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - F76AC1B72666D0610040C8BC /* Build configuration list for PBXNativeTarget "RunnerUITests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F76AC1B82666D0610040C8BC /* Debug */, - F76AC1B92666D0610040C8BC /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; /* End XCConfigurationList section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index 2c5de81f8e98..43904af23121 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/pubspec.yaml @@ -4,6 +4,10 @@ description: Flutter plugin for Google Sign-In, a secure authentication system repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 version: 5.2.5 +# Temporarily disable publishing to allow moving Android and iOS +# implementations. +publish_to: none + environment: sdk: ">=2.14.0 <3.0.0" @@ -13,16 +17,20 @@ flutter: plugin: platforms: android: - package: io.flutter.plugins.googlesignin - pluginClass: GoogleSignInPlugin + default_package: google_sign_in_android ios: - pluginClass: FLTGoogleSignInPlugin + default_package: google_sign_in_ios web: default_package: google_sign_in_web dependencies: flutter: sdk: flutter + # Temporary path dependencies to allow moving Android and iOS implementations. + google_sign_in_android: + path: ../google_sign_in_android + google_sign_in_ios: + path: ../google_sign_in_ios google_sign_in_platform_interface: ^2.1.0 google_sign_in_web: ^0.10.0 diff --git a/packages/google_sign_in/google_sign_in_android/AUTHORS b/packages/google_sign_in/google_sign_in_android/AUTHORS new file mode 100644 index 000000000000..493a0b4ef9c2 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/AUTHORS @@ -0,0 +1,66 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> diff --git a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md new file mode 100644 index 000000000000..e7c6847e7750 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md @@ -0,0 +1,3 @@ +## 5.2.5 + +* Splits from `video_player` as a federated implementation. diff --git a/packages/google_sign_in/google_sign_in_android/LICENSE b/packages/google_sign_in/google_sign_in_android/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/LICENSE @@ -0,0 +1,25 @@ +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/google_sign_in/google_sign_in_android/README.md b/packages/google_sign_in/google_sign_in_android/README.md new file mode 100644 index 000000000000..5c7c70ede917 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/README.md @@ -0,0 +1,11 @@ +# google\_sign\_in\_android + +The Android implementation of [`google_sign_in`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use `google_sign_in` +normally. This package will be automatically included in your app when you do. + +[1]: https://pub.dev/packages/google_sign_in +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin diff --git a/packages/google_sign_in/google_sign_in/android/build.gradle b/packages/google_sign_in/google_sign_in_android/android/build.gradle similarity index 100% rename from packages/google_sign_in/google_sign_in/android/build.gradle rename to packages/google_sign_in/google_sign_in_android/android/build.gradle diff --git a/packages/google_sign_in/google_sign_in_android/android/settings.gradle b/packages/google_sign_in/google_sign_in_android/android/settings.gradle new file mode 100644 index 000000000000..35ebd0e2428a --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'google_sign_in_android' diff --git a/packages/google_sign_in/google_sign_in/android/src/main/AndroidManifest.xml b/packages/google_sign_in/google_sign_in_android/android/src/main/AndroidManifest.xml similarity index 100% rename from packages/google_sign_in/google_sign_in/android/src/main/AndroidManifest.xml rename to packages/google_sign_in/google_sign_in_android/android/src/main/AndroidManifest.xml diff --git a/packages/google_sign_in/google_sign_in/android/src/main/java/io/flutter/plugins/googlesignin/BackgroundTaskRunner.java b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/BackgroundTaskRunner.java similarity index 100% rename from packages/google_sign_in/google_sign_in/android/src/main/java/io/flutter/plugins/googlesignin/BackgroundTaskRunner.java rename to packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/BackgroundTaskRunner.java diff --git a/packages/google_sign_in/google_sign_in/android/src/main/java/io/flutter/plugins/googlesignin/Executors.java b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/Executors.java similarity index 100% rename from packages/google_sign_in/google_sign_in/android/src/main/java/io/flutter/plugins/googlesignin/Executors.java rename to packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/Executors.java diff --git a/packages/google_sign_in/google_sign_in/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java similarity index 100% rename from packages/google_sign_in/google_sign_in/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java rename to packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java diff --git a/packages/google_sign_in/google_sign_in/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInWrapper.java b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInWrapper.java similarity index 100% rename from packages/google_sign_in/google_sign_in/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInWrapper.java rename to packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInWrapper.java diff --git a/packages/google_sign_in/google_sign_in/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java b/packages/google_sign_in/google_sign_in_android/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java similarity index 100% rename from packages/google_sign_in/google_sign_in/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java rename to packages/google_sign_in/google_sign_in_android/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java diff --git a/packages/google_sign_in/google_sign_in_android/example/README.md b/packages/google_sign_in/google_sign_in_android/example/README.md new file mode 100644 index 000000000000..79d99dc72982 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/README.md @@ -0,0 +1,8 @@ +# google_sign_in_android example + +Exercises the Android implementation of `GoogleSignInPlatform`. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](https://flutter.dev/). diff --git a/packages/google_sign_in/google_sign_in_android/example/android/app/build.gradle b/packages/google_sign_in/google_sign_in_android/example/android/app/build.gradle new file mode 100644 index 000000000000..8ac99fe56f3a --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/android/app/build.gradle @@ -0,0 +1,67 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 31 + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + applicationId "io.flutter.plugins.googlesigninexample" + minSdkVersion 16 + targetSdkVersion 28 + multiDexEnabled true + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } + + testOptions { + unitTests.returnDefaultValues = true + } +} + +flutter { + source '../..' +} + +dependencies { + implementation 'com.google.android.gms:play-services-auth:16.0.1' + testImplementation'junit:junit:4.12' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + api 'androidx.test:core:1.2.0' +} diff --git a/packages/google_sign_in/google_sign_in_android/example/android/app/google-services.json b/packages/google_sign_in/google_sign_in_android/example/android/app/google-services.json new file mode 100644 index 000000000000..efa524535553 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/android/app/google-services.json @@ -0,0 +1,246 @@ +{ + "project_info": { + "project_number": "479882132969", + "firebase_url": "https://my-flutter-proj.firebaseio.com", + "project_id": "my-flutter-proj", + "storage_bucket": "my-flutter-proj.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:479882132969:android:c73fd19ff7e2c0be", + "android_client_info": { + "package_name": "io.flutter.plugins.cameraexample" + } + }, + "oauth_client": [ + { + "client_id": "479882132969-0d20fkjtr1p8evfomfkf3vmi50uajml2.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyCrZz9T0Pg0rDnpxfNuPBrOxGhXskfebXs" + } + ], + "services": { + "analytics_service": { + "status": 1 + }, + "appinvite_service": { + "status": 1, + "other_platform_oauth_client": [] + }, + "ads_service": { + "status": 2 + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:479882132969:android:632cdf3fc0a17139", + "android_client_info": { + "package_name": "io.flutter.plugins.firebasedynamiclinksexample" + } + }, + "oauth_client": [ + { + "client_id": "479882132969-32qusitiag53931ck80h121ajhlc5a7e.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "io.flutter.plugins.firebasedynamiclinksexample", + "certificate_hash": "e733b7a303250b63e06de6f7c9767c517d69cfa0" + } + }, + { + "client_id": "479882132969-0d20fkjtr1p8evfomfkf3vmi50uajml2.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyCrZz9T0Pg0rDnpxfNuPBrOxGhXskfebXs" + } + ], + "services": { + "analytics_service": { + "status": 1 + }, + "appinvite_service": { + "status": 2, + "other_platform_oauth_client": [ + { + "client_id": "479882132969-0d20fkjtr1p8evfomfkf3vmi50uajml2.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "479882132969-gjp4e63ogu2h6guttj2ie6t3f10ic7i8.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "io.flutter.plugins.firebaseMlVisionExample" + } + } + ] + }, + "ads_service": { + "status": 2 + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:479882132969:android:ae50362b4bc06086", + "android_client_info": { + "package_name": "io.flutter.plugins.firebasemlvisionexample" + } + }, + "oauth_client": [ + { + "client_id": "479882132969-9pp74fkgmtvt47t9rikc1p861v7n85tn.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "io.flutter.plugins.firebasemlvisionexample", + "certificate_hash": "e733b7a303250b63e06de6f7c9767c517d69cfa0" + } + }, + { + "client_id": "479882132969-0d20fkjtr1p8evfomfkf3vmi50uajml2.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyCrZz9T0Pg0rDnpxfNuPBrOxGhXskfebXs" + } + ], + "services": { + "analytics_service": { + "status": 1 + }, + "appinvite_service": { + "status": 2, + "other_platform_oauth_client": [ + { + "client_id": "479882132969-0d20fkjtr1p8evfomfkf3vmi50uajml2.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "479882132969-gjp4e63ogu2h6guttj2ie6t3f10ic7i8.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "io.flutter.plugins.firebaseMlVisionExample" + } + } + ] + }, + "ads_service": { + "status": 2 + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:479882132969:android:215a22700e1b466b", + "android_client_info": { + "package_name": "io.flutter.plugins.firebaseperformanceexample" + } + }, + "oauth_client": [ + { + "client_id": "479882132969-8h4kiv8m7ho4tvn6uuujsfcrf69unuf7.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "io.flutter.plugins.firebaseperformanceexample", + "certificate_hash": "e733b7a303250b63e06de6f7c9767c517d69cfa0" + } + }, + { + "client_id": "479882132969-0d20fkjtr1p8evfomfkf3vmi50uajml2.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyCrZz9T0Pg0rDnpxfNuPBrOxGhXskfebXs" + } + ], + "services": { + "analytics_service": { + "status": 1 + }, + "appinvite_service": { + "status": 2, + "other_platform_oauth_client": [ + { + "client_id": "479882132969-0d20fkjtr1p8evfomfkf3vmi50uajml2.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "479882132969-gjp4e63ogu2h6guttj2ie6t3f10ic7i8.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "io.flutter.plugins.firebaseMlVisionExample" + } + } + ] + }, + "ads_service": { + "status": 2 + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:479882132969:android:5e9f1f89e134dc86", + "android_client_info": { + "package_name": "io.flutter.plugins.googlesigninexample" + } + }, + "oauth_client": [ + { + "client_id": "479882132969-90ml692hkonp587sl0v0rurmnvkekgrg.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "io.flutter.plugins.googlesigninexample", + "certificate_hash": "e733b7a303250b63e06de6f7c9767c517d69cfa0" + } + }, + { + "client_id": "479882132969-0d20fkjtr1p8evfomfkf3vmi50uajml2.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyCrZz9T0Pg0rDnpxfNuPBrOxGhXskfebXs" + } + ], + "services": { + "analytics_service": { + "status": 1 + }, + "appinvite_service": { + "status": 2, + "other_platform_oauth_client": [ + { + "client_id": "479882132969-0d20fkjtr1p8evfomfkf3vmi50uajml2.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "479882132969-gjp4e63ogu2h6guttj2ie6t3f10ic7i8.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "io.flutter.plugins.firebaseMlVisionExample" + } + } + ] + }, + "ads_service": { + "status": 2 + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/packages/google_sign_in/google_sign_in_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/google_sign_in/google_sign_in_android/example/android/app/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..9a4163a4f5ee --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/packages/google_sign_in/google_sign_in_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java b/packages/google_sign_in/google_sign_in_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java new file mode 100644 index 000000000000..0f4298dca155 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java @@ -0,0 +1,14 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface DartIntegrationTest {} diff --git a/packages/google_sign_in/google_sign_in_android/example/android/app/src/androidTest/java/io/flutter/plugins/googlesigninexample/FlutterActivityTest.java b/packages/google_sign_in/google_sign_in_android/example/android/app/src/androidTest/java/io/flutter/plugins/googlesigninexample/FlutterActivityTest.java new file mode 100644 index 000000000000..edc01de491af --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/android/app/src/androidTest/java/io/flutter/plugins/googlesigninexample/FlutterActivityTest.java @@ -0,0 +1,19 @@ +// 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.googlesigninexample; + +import androidx.test.rule.ActivityTestRule; +import dev.flutter.plugins.integration_test.FlutterTestRunner; +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.plugins.DartIntegrationTest; +import org.junit.Rule; +import org.junit.runner.RunWith; + +@DartIntegrationTest +@RunWith(FlutterTestRunner.class) +public class FlutterActivityTest { + @Rule + public ActivityTestRule rule = new ActivityTestRule<>(FlutterActivity.class); +} diff --git a/packages/google_sign_in/google_sign_in/example/android/app/src/androidTest/java/io/flutter/plugins/googlesigninexample/GoogleSignInTest.java b/packages/google_sign_in/google_sign_in_android/example/android/app/src/androidTest/java/io/flutter/plugins/googlesigninexample/GoogleSignInTest.java similarity index 100% rename from packages/google_sign_in/google_sign_in/example/android/app/src/androidTest/java/io/flutter/plugins/googlesigninexample/GoogleSignInTest.java rename to packages/google_sign_in/google_sign_in_android/example/android/app/src/androidTest/java/io/flutter/plugins/googlesigninexample/GoogleSignInTest.java diff --git a/packages/google_sign_in/google_sign_in_android/example/android/app/src/debug/AndroidManifest.xml b/packages/google_sign_in/google_sign_in_android/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 000000000000..4d764900a530 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/AndroidManifest.xml b/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..22a34d7218f7 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + diff --git a/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/java/io/flutter/plugins/.gitignore b/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/java/io/flutter/plugins/.gitignore new file mode 100644 index 000000000000..9eb4563d2ae1 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/java/io/flutter/plugins/.gitignore @@ -0,0 +1 @@ +GeneratedPluginRegistrant.java diff --git a/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/GoogleSignInTestActivity.java b/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/GoogleSignInTestActivity.java new file mode 100644 index 000000000000..09506a2632df --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/GoogleSignInTestActivity.java @@ -0,0 +1,20 @@ +// 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.googlesigninexample; + +import androidx.annotation.NonNull; +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.embedding.engine.FlutterEngine; + +// Makes the FlutterEngine accessible for testing. +public class GoogleSignInTestActivity extends FlutterActivity { + public FlutterEngine engine; + + @Override + public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { + super.configureFlutterEngine(flutterEngine); + engine = flutterEngine; + } +} diff --git a/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/res/values/strings.xml b/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/res/values/strings.xml new file mode 100644 index 000000000000..c7e28ffcedd1 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + + YOUR_WEB_CLIENT_ID + diff --git a/packages/google_sign_in/google_sign_in_android/example/android/build.gradle b/packages/google_sign_in/google_sign_in_android/example/android/build.gradle new file mode 100644 index 000000000000..e101ac08df55 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/android/build.gradle @@ -0,0 +1,29 @@ +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.3.0' + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/packages/google_sign_in/google_sign_in_android/example/android/gradle.properties b/packages/google_sign_in/google_sign_in_android/example/android/gradle.properties new file mode 100644 index 000000000000..d12b9a8297e5 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.enableR8=true +android.useAndroidX=true diff --git a/packages/google_sign_in/google_sign_in_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/google_sign_in/google_sign_in_android/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..019065d1d650 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip diff --git a/packages/google_sign_in/google_sign_in_android/example/android/settings.gradle b/packages/google_sign_in/google_sign_in_android/example/android/settings.gradle new file mode 100644 index 000000000000..115da6cb4f4d --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/android/settings.gradle @@ -0,0 +1,15 @@ +include ':app' + +def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + +def plugins = new Properties() +def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') +if (pluginsFile.exists()) { + pluginsFile.withInputStream { stream -> plugins.load(stream) } +} + +plugins.each { name, path -> + def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() + include ":$name" + project(":$name").projectDir = pluginDirectory +} diff --git a/packages/google_sign_in/google_sign_in_android/example/integration_test/google_sign_in_test.dart b/packages/google_sign_in/google_sign_in_android/example/integration_test/google_sign_in_test.dart new file mode 100644 index 000000000000..d4631f6a6fd3 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/integration_test/google_sign_in_test.dart @@ -0,0 +1,16 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; +import 'package:integration_test/integration_test.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('Can initialize the plugin', (WidgetTester tester) async { + final GoogleSignInPlatform signIn = GoogleSignInPlatform.instance; + expect(signIn, isNotNull); + }); +} diff --git a/packages/google_sign_in/google_sign_in_android/example/lib/main.dart b/packages/google_sign_in/google_sign_in_android/example/lib/main.dart new file mode 100644 index 000000000000..a750c330001d --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/lib/main.dart @@ -0,0 +1,179 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; +import 'dart:convert' show json; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; +import 'package:http/http.dart' as http; + +void main() { + runApp( + MaterialApp( + title: 'Google Sign In', + home: SignInDemo(), + ), + ); +} + +class SignInDemo extends StatefulWidget { + @override + State createState() => SignInDemoState(); +} + +class SignInDemoState extends State { + GoogleSignInUserData? _currentUser; + String _contactText = ''; + // Future that completes when `init` has completed on the sign in instance. + Future? _initialization; + + @override + void initState() { + super.initState(); + _signIn(); + } + + Future _ensureInitialized() { + return _initialization ??= GoogleSignInPlatform.instance.init( + scopes: [ + 'email', + 'https://www.googleapis.com/auth/contacts.readonly', + ], + )..catchError((dynamic _) { + _initialization = null; + }); + } + + void _setUser(GoogleSignInUserData? user) { + setState(() { + _currentUser = user; + if (user != null) { + _handleGetContact(user); + } + }); + } + + Future _signIn() async { + await _ensureInitialized(); + final GoogleSignInUserData? newUser = + await GoogleSignInPlatform.instance.signInSilently(); + _setUser(newUser); + } + + Future> _getAuthHeaders() async { + final GoogleSignInUserData? user = _currentUser; + if (user == null) { + throw StateError('No user signed in'); + } + + final GoogleSignInTokenData response = + await GoogleSignInPlatform.instance.getTokens( + email: user.email, + shouldRecoverAuth: true, + ); + + return { + 'Authorization': 'Bearer ${response.accessToken}', + // TODO(kevmoo): Use the correct value once it's available. + // See https://github.com/flutter/flutter/issues/80905 + 'X-Goog-AuthUser': '0', + }; + } + + Future _handleGetContact(GoogleSignInUserData user) async { + setState(() { + _contactText = 'Loading contact info...'; + }); + final http.Response response = await http.get( + Uri.parse('https://people.googleapis.com/v1/people/me/connections' + '?requestMask.includeField=person.names'), + headers: await _getAuthHeaders(), + ); + if (response.statusCode != 200) { + setState(() { + _contactText = 'People API gave a ${response.statusCode} ' + 'response. Check logs for details.'; + }); + print('People API ${response.statusCode} response: ${response.body}'); + return; + } + final Map data = + json.decode(response.body) as Map; + final int contactCount = + (data['connections'] as List?)?.length ?? 0; + setState(() { + _contactText = '$contactCount contacts found'; + }); + } + + Future _handleSignIn() async { + try { + await _ensureInitialized(); + _setUser(await GoogleSignInPlatform.instance.signIn()); + } catch (error) { + final bool canceled = + error is PlatformException && error.code == 'sign_in_canceled'; + if (!canceled) { + print(error); + } + } + } + + Future _handleSignOut() async { + await _ensureInitialized(); + await GoogleSignInPlatform.instance.disconnect(); + } + + Widget _buildBody() { + final GoogleSignInUserData? user = _currentUser; + if (user != null) { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + ListTile( + title: Text(user.displayName ?? ''), + subtitle: Text(user.email), + ), + const Text('Signed in successfully.'), + Text(_contactText), + ElevatedButton( + child: const Text('SIGN OUT'), + onPressed: _handleSignOut, + ), + ElevatedButton( + child: const Text('REFRESH'), + onPressed: () => _handleGetContact(user), + ), + ], + ); + } else { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + const Text('You are not currently signed in.'), + ElevatedButton( + child: const Text('SIGN IN'), + onPressed: _handleSignIn, + ), + ], + ); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Google Sign In'), + ), + body: ConstrainedBox( + constraints: const BoxConstraints.expand(), + child: _buildBody(), + )); + } +} diff --git a/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml b/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml new file mode 100644 index 000000000000..316cdd893a2c --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml @@ -0,0 +1,30 @@ +name: google_sign_in_example +description: Example of Google Sign-In plugin. +publish_to: none + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.5.0" + +dependencies: + flutter: + sdk: flutter + google_sign_in_android: + # When depending on this package from a real application you should use: + # google_sign_in_android: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + google_sign_in_platform_interface: ^2.1.0 + http: ^0.13.0 + +dev_dependencies: + espresso: ^0.1.0+2 + flutter_driver: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/google_sign_in/google_sign_in_android/example/test_driver/integration_test.dart b/packages/google_sign_in/google_sign_in_android/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4f10f2a522f3 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/test_driver/integration_test.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); diff --git a/packages/google_sign_in/google_sign_in_android/pubspec.yaml b/packages/google_sign_in/google_sign_in_android/pubspec.yaml new file mode 100644 index 000000000000..efd679ca399b --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/pubspec.yaml @@ -0,0 +1,35 @@ +name: google_sign_in_android +description: Android implementation of the google_sign_in plugin. +repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in_android +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 +version: 5.2.5 + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.5.0" + +flutter: + plugin: + implements: google_sign_in + platforms: + android: + package: io.flutter.plugins.googlesignin + pluginClass: GoogleSignInPlugin + +dependencies: + flutter: + sdk: flutter + google_sign_in_platform_interface: ^2.1.0 + +dev_dependencies: + flutter_driver: + sdk: flutter + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + +# The example deliberately includes limited-use secrets. +false_secrets: + - /example/android/app/google-services.json + - /example/lib/main.dart diff --git a/packages/google_sign_in/google_sign_in_ios/AUTHORS b/packages/google_sign_in/google_sign_in_ios/AUTHORS new file mode 100644 index 000000000000..493a0b4ef9c2 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/AUTHORS @@ -0,0 +1,66 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> diff --git a/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md b/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md new file mode 100644 index 000000000000..e7c6847e7750 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md @@ -0,0 +1,3 @@ +## 5.2.5 + +* Splits from `video_player` as a federated implementation. diff --git a/packages/google_sign_in/google_sign_in_ios/LICENSE b/packages/google_sign_in/google_sign_in_ios/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/LICENSE @@ -0,0 +1,25 @@ +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/google_sign_in/google_sign_in_ios/README.md b/packages/google_sign_in/google_sign_in_ios/README.md new file mode 100644 index 000000000000..25e08fdb4040 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/README.md @@ -0,0 +1,11 @@ +# google\_sign\_in\_ios + +The iOS implementation of [`google_sign_in`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use `google_sign_in` +normally. This package will be automatically included in your app when you do. + +[1]: https://pub.dev/packages/google_sign_in +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin diff --git a/packages/google_sign_in/google_sign_in_ios/example/README.md b/packages/google_sign_in/google_sign_in_ios/example/README.md new file mode 100644 index 000000000000..ca3dc7023fc9 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/README.md @@ -0,0 +1,8 @@ +# google_sign_in_ios example + +Exercises the iOS implementation of `GoogleSignInPlatform`. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](https://flutter.dev/). diff --git a/packages/google_sign_in/google_sign_in_ios/example/integration_test/google_sign_in_test.dart b/packages/google_sign_in/google_sign_in_ios/example/integration_test/google_sign_in_test.dart new file mode 100644 index 000000000000..d4631f6a6fd3 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/integration_test/google_sign_in_test.dart @@ -0,0 +1,16 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; +import 'package:integration_test/integration_test.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('Can initialize the plugin', (WidgetTester tester) async { + final GoogleSignInPlatform signIn = GoogleSignInPlatform.instance; + expect(signIn, isNotNull); + }); +} diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Flutter/AppFrameworkInfo.plist b/packages/google_sign_in/google_sign_in_ios/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 000000000000..3a9c234f96d4 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + UIRequiredDeviceCapabilities + + arm64 + + MinimumOSVersion + 9.0 + + diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Flutter/Debug.xcconfig b/packages/google_sign_in/google_sign_in_ios/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 000000000000..9803018ca79d --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "Generated.xcconfig" +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Flutter/Release.xcconfig b/packages/google_sign_in/google_sign_in_ios/example/ios/Flutter/Release.xcconfig new file mode 100644 index 000000000000..a4a8c604e13d --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include "Generated.xcconfig" +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" diff --git a/packages/google_sign_in/google_sign_in/example/ios/RunnerTests/Info.plist b/packages/google_sign_in/google_sign_in_ios/example/ios/GoogleSignInPluginTest/Info.plist similarity index 100% rename from packages/google_sign_in/google_sign_in/example/ios/RunnerTests/Info.plist rename to packages/google_sign_in/google_sign_in_ios/example/ios/GoogleSignInPluginTest/Info.plist diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile b/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile new file mode 100644 index 000000000000..e577a3081fe8 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile @@ -0,0 +1,47 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + + pod 'OCMock','3.5' + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + target.build_configurations.each do |build_configuration| + # GoogleSignIn does not support arm64 simulators. + build_configuration.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64 i386' + end + end +end diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..f2bf4ebc514e --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,740 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 5C6F5A6E1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C6F5A6D1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.m */; }; + 7A303C2E1E89D76400B1F19E /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7A303C2D1E89D76400B1F19E /* GoogleService-Info.plist */; }; + 7ACDFB0E1E8944C400BE2D00 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7ACDFB0D1E8944C400BE2D00 /* AppFrameworkInfo.plist */; }; + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; + 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + C2FB9CBA01DB0A2DE5F31E12 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0263E28FA425D1CE928BDE15 /* libPods-Runner.a */; }; + C56D3B06A42F3B35C1F47A43 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18AD6475292B9C45B529DDC9 /* libPods-RunnerTests.a */; }; + F76AC1A52666D0540040C8BC /* GoogleSignInTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F76AC1A42666D0540040C8BC /* GoogleSignInTests.m */; }; + F76AC1B32666D0610040C8BC /* GoogleSignInUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = F76AC1B22666D0610040C8BC /* GoogleSignInUITests.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + F76AC1A72666D0540040C8BC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; + F76AC1B52666D0610040C8BC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 0263E28FA425D1CE928BDE15 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 18AD6475292B9C45B529DDC9 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 37E582FF620A90D0EB2C0851 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + 45D93D4513839BFEA2AA74FE /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 5A76713E622F06379AEDEBFA /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 5C6F5A6C1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 5C6F5A6D1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 7A303C2D1E89D76400B1F19E /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 7ACDFB0D1E8944C400BE2D00 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + F582639B44581540871D9BB0 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + F76AC1A22666D0540040C8BC /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + F76AC1A42666D0540040C8BC /* GoogleSignInTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoogleSignInTests.m; sourceTree = ""; }; + F76AC1A62666D0540040C8BC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + F76AC1B02666D0610040C8BC /* RunnerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + F76AC1B22666D0610040C8BC /* GoogleSignInUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoogleSignInUITests.m; sourceTree = ""; }; + F76AC1B42666D0610040C8BC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C2FB9CBA01DB0A2DE5F31E12 /* libPods-Runner.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F76AC19F2666D0540040C8BC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C56D3B06A42F3B35C1F47A43 /* libPods-RunnerTests.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F76AC1AD2666D0610040C8BC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 840012C8B5EDBCF56B0E4AC1 /* Pods */ = { + isa = PBXGroup; + children = ( + 5A76713E622F06379AEDEBFA /* Pods-Runner.debug.xcconfig */, + F582639B44581540871D9BB0 /* Pods-Runner.release.xcconfig */, + 37E582FF620A90D0EB2C0851 /* Pods-RunnerTests.debug.xcconfig */, + 45D93D4513839BFEA2AA74FE /* Pods-RunnerTests.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 7ACDFB0D1E8944C400BE2D00 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + F76AC1A32666D0540040C8BC /* RunnerTests */, + F76AC1B12666D0610040C8BC /* RunnerUITests */, + 97C146EF1CF9000F007C117D /* Products */, + 840012C8B5EDBCF56B0E4AC1 /* Pods */, + CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + F76AC1A22666D0540040C8BC /* RunnerTests.xctest */, + F76AC1B02666D0610040C8BC /* RunnerUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 5C6F5A6C1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.h */, + 5C6F5A6D1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.m */, + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 7A303C2D1E89D76400B1F19E /* GoogleService-Info.plist */, + 97C147021CF9000F007C117D /* Info.plist */, + 97C146F11CF9000F007C117D /* Supporting Files */, + ); + path = Runner; + sourceTree = ""; + }; + 97C146F11CF9000F007C117D /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 97C146F21CF9000F007C117D /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + CF3B75C9A7D2FA2A4C99F110 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 0263E28FA425D1CE928BDE15 /* libPods-Runner.a */, + 18AD6475292B9C45B529DDC9 /* libPods-RunnerTests.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + F76AC1A32666D0540040C8BC /* RunnerTests */ = { + isa = PBXGroup; + children = ( + F76AC1A42666D0540040C8BC /* GoogleSignInTests.m */, + F76AC1A62666D0540040C8BC /* Info.plist */, + ); + path = RunnerTests; + sourceTree = ""; + }; + F76AC1B12666D0610040C8BC /* RunnerUITests */ = { + isa = PBXGroup; + children = ( + F76AC1B22666D0610040C8BC /* GoogleSignInUITests.m */, + F76AC1B42666D0610040C8BC /* Info.plist */, + ); + path = RunnerUITests; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + AB1344B0443C71CD721E1BB7 /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 532EA9D341340B1DCD08293D /* [CP] Copy Pods Resources */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; + F76AC1A12666D0540040C8BC /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = F76AC1AB2666D0540040C8BC /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 27975964E48117AA65B1D6C7 /* [CP] Check Pods Manifest.lock */, + F76AC19E2666D0540040C8BC /* Sources */, + F76AC19F2666D0540040C8BC /* Frameworks */, + F76AC1A02666D0540040C8BC /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + F76AC1A82666D0540040C8BC /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = F76AC1A22666D0540040C8BC /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + F76AC1AF2666D0610040C8BC /* RunnerUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = F76AC1B72666D0610040C8BC /* Build configuration list for PBXNativeTarget "RunnerUITests" */; + buildPhases = ( + F76AC1AC2666D0610040C8BC /* Sources */, + F76AC1AD2666D0610040C8BC /* Frameworks */, + F76AC1AE2666D0610040C8BC /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + F76AC1B62666D0610040C8BC /* PBXTargetDependency */, + ); + name = RunnerUITests; + productName = RunnerUITests; + productReference = F76AC1B02666D0610040C8BC /* RunnerUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = "The Flutter Authors"; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + }; + F76AC1A12666D0540040C8BC = { + CreatedOnToolsVersion = 12.5; + ProvisioningStyle = Automatic; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + F76AC1AF2666D0610040C8BC = { + CreatedOnToolsVersion = 12.5; + ProvisioningStyle = Automatic; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + F76AC1A12666D0540040C8BC /* RunnerTests */, + F76AC1AF2666D0610040C8BC /* RunnerUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7A303C2E1E89D76400B1F19E /* GoogleService-Info.plist in Resources */, + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 7ACDFB0E1E8944C400BE2D00 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F76AC1A02666D0540040C8BC /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F76AC1AE2666D0610040C8BC /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 27975964E48117AA65B1D6C7 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed\n/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin\n"; + }; + 532EA9D341340B1DCD08293D /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", + "${PODS_ROOT}/GoogleSignIn/Resources/GoogleSignIn.bundle", + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleSignIn.bundle", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + AB1344B0443C71CD721E1BB7 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, + 97C146F31CF9000F007C117D /* main.m in Sources */, + 5C6F5A6E1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F76AC19E2666D0540040C8BC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F76AC1A52666D0540040C8BC /* GoogleSignInTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F76AC1AC2666D0610040C8BC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F76AC1B32666D0610040C8BC /* GoogleSignInUITests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + F76AC1A82666D0540040C8BC /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = F76AC1A72666D0540040C8BC /* PBXContainerItemProxy */; + }; + F76AC1B62666D0610040C8BC /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = F76AC1B52666D0610040C8BC /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ENABLE_BITCODE = NO; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.googleSignInExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ENABLE_BITCODE = NO; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.googleSignInExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + F76AC1A92666D0540040C8BC /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 37E582FF620A90D0EB2C0851 /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; + INFOPLIST_FILE = RunnerTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Debug; + }; + F76AC1AA2666D0540040C8BC /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 45D93D4513839BFEA2AA74FE /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; + INFOPLIST_FILE = RunnerTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Release; + }; + F76AC1B82666D0610040C8BC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = RunnerUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_TARGET_NAME = Runner; + }; + name = Debug; + }; + F76AC1B92666D0610040C8BC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = RunnerUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_TARGET_NAME = Runner; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F76AC1AB2666D0540040C8BC /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F76AC1A92666D0540040C8BC /* Debug */, + F76AC1AA2666D0540040C8BC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F76AC1B72666D0610040C8BC /* Build configuration list for PBXNativeTarget "RunnerUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F76AC1B82666D0610040C8BC /* Debug */, + F76AC1B92666D0610040C8BC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..919434a6254f --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000000..f4569c48ce10 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..21a3cc14c74e --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/AppDelegate.h b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/AppDelegate.h new file mode 100644 index 000000000000..0681d288bb70 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/AppDelegate.h @@ -0,0 +1,10 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import + +@interface AppDelegate : FlutterAppDelegate + +@end diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/AppDelegate.m b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/AppDelegate.m new file mode 100644 index 000000000000..30b87969f44a --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/AppDelegate.m @@ -0,0 +1,17 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "AppDelegate.h" +#include "GeneratedPluginRegistrant.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [GeneratedPluginRegistrant registerWithRegistry:self]; + // Override point for customization after application launch. + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +@end diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000000..d22f10b2ab63 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..28c6bf03016f6c994b70f38d1b7346e5831b531f GIT binary patch literal 564 zcmV-40?Yl0P)Px$?ny*JR5%f>l)FnDQ543{x%ZCiu33$Wg!pQFfT_}?5Q|_VSlIbLC`dpoMXL}9 zHfd9&47Mo(7D231gb+kjFxZHS4-m~7WurTH&doVX2KI5sU4v(sJ1@T9eCIKPjsqSr z)C01LsCxk=72-vXmX}CQD#BD;Cthymh&~=f$Q8nn0J<}ZrusBy4PvRNE}+1ceuj8u z0mW5k8fmgeLnTbWHGwfKA3@PdZxhn|PypR&^p?weGftrtCbjF#+zk_5BJh7;0`#Wr zgDpM_;Ax{jO##IrT`Oz;MvfwGfV$zD#c2xckpcXC6oou4ML~ezCc2EtnsQTB4tWNg z?4bkf;hG7IMfhgNI(FV5Gs4|*GyMTIY0$B=_*mso9Ityq$m^S>15>-?0(zQ<8Qy<_TjHE33(?_M8oaM zyc;NxzRVK@DL6RJnX%U^xW0Gpg(lXp(!uK1v0YgHjs^ZXSQ|m#lV7ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..f091b6b0bca859a3f474b03065bef75ba58a9e4c GIT binary patch literal 1588 zcmV-42Fv-0P)C1SqPt}wig>|5Crh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)y zRAZ>eDe#*r`yDAVgB_R*LB*MAc)8(b{g{9McCXW!lq7r(btRoB9!8B-#AI6JMb~YFBEvdsV)`mEQO^&#eRKx@b&x- z5lZm*!WfD8oCLzfHGz#u7sT0^VLMI1MqGxF^v+`4YYnVYgk*=kU?HsSz{v({E3lb9 z>+xILjBN)t6`=g~IBOelGQ(O990@BfXf(DRI5I$qN$0Gkz-FSc$3a+2fX$AedL4u{ z4V+5Ong(9LiGcIKW?_352sR;LtDPmPJXI{YtT=O8=76o9;*n%_m|xo!i>7$IrZ-{l z-x3`7M}qzHsPV@$v#>H-TpjDh2UE$9g6sysUREDy_R(a)>=eHw-WAyfIN z*qb!_hW>G)Tu8nSw9yn#3wFMiLcfc4pY0ek1}8(NqkBR@t4{~oC>ryc-h_ByH(Cg5 z>ao-}771+xE3um9lWAY1FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zm zZQj(aA_HeBY&OC^jj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5Kh zX*|AU4QE#~SgPzOXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&g3j|zDgC+}2Q_v%YfDax z!?umcN^n}KYQ|a$Lr+51Nf9dkkYFSjZZjkma$0KOj+;aQ&721~t7QUKx61J3(P4P1 zstI~7-wOACnWP4=8oGOwz%vNDqD8w&Q`qcNGGrbbf&0s9L0De{4{mRS?o0MU+nR_! zrvshUau0G^DeMhM_v{5BuLjb#Hh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG z;P%vktHcXwTj~+IE(~px)3*MY77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC z(zYC$g|@+I+kY;dg_dE>scBf&bP1Nc@Hz<3R)V`=AGkc;8CXqdi=B4l2k|g;2%#m& z*jfX^%b!A8#bI!j9-0Fi0bOXl(-c^AB9|nQaE`*)Hw+o&jS9@7&Gov#HbD~#d{twV zXd^Tr^mWLfFh$@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)y zI9C9*oUga6=hxw6QasLPnee@3^Rr*M{CdaL5=R41nLs(AHk_=Y+A9$2&H(B7!_pURs&8aNw7?`&Z&xY_Ye z)~D5Bog^td-^QbUtkTirdyK^mTHAOuptDflut!#^lnKqU md>ggs(5nOWAqO?umG&QVYK#ibz}*4>0000U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~cTNYR~@Y9KepMPrrB8cABapAFa=`H+UGhkXUZV1GnwR1*lPyZ;*K(i~2gp|@bzp8}og7e*#% zEnr|^CWdVV!-4*Y_7rFvlww2Ze+>j*!Z!pQ?2l->4q#nqRu9`ELo6RMS5=br47g_X zRw}P9a7RRYQ%2Vsd0Me{_(EggTnuN6j=-?uFS6j^u69elMypu?t>op*wBx<=Wx8?( ztpe^(fwM6jJX7M-l*k3kEpWOl_Vk3@(_w4oc}4YF4|Rt=2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX?F&rExl^W|G8Z8;`5z-k}OGNZ0#WVb$WCpQu-_YsiqKP?BB# vzVHS-CTUF4Ozn5G+mq_~Qqto~ahA+K`|lyv3(-e}00000NkvXXu0mjfd`9t{ literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d0ef06e7edb86cdfe0d15b4b0d98334a86163658 GIT binary patch literal 1716 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 literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c8f9ed8f5cee1c98386d13b17e89f719e83555b2 GIT binary patch literal 1895 zcmV-t2blPYP)FQtfgmafE#=YDCq`qUBt#QpG%*H6QHY765~R=q zZ6iudfM}q!Pz#~9JgOi8QJ|DSu?1-*(kSi1K4#~5?#|rh?sS)(-JQqX*}ciXJ56_H zdw=^s_srbAdqxlvGyrgGet#6T7_|j;95sL%MtM;q86vOxKM$f#puR)Bjv9Zvz9-di zXOTSsZkM83)E9PYBXC<$6(|>lNLVBb&&6y{NByFCp%6+^ALR@NCTse_wqvNmSWI-m z!$%KlHFH2omF!>#%1l3LTZg(s7eof$7*xB)ZQ0h?ejh?Ta9fDv59+u#MokW+1t8Zb zgHv%K(u9G^Lv`lh#f3<6!JVTL3(dCpxHbnbA;kKqQyd1~^Xe0VIaYBSWm6nsr;dFj z4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3QU4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKubPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`Q zykI#*+rQ=z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRe zt3L_uNyQ*cE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=ky zx=~RKa4{iTm{_>_vSCm?$Ej=i6@=m%@VvAITnigVg{&@!7CDgs908761meDK5azA} z4?=NOH|PdvabgJ&fW2{Mo$Q0CcD8Qc84%{JPYt5EiG{MdLIAeX%T=D7NIP4%Hw}p9 zg)==!2Lbp#j{u_}hMiao9=!VSyx0gHbeCS`;q&vzeq|fs`y&^X-lso(Ls@-706qmA z7u*T5PMo_w3{se1t2`zWeO^hOvTsohG_;>J0wVqVe+n)AbQCx)yh9;w+J6?NF5Lmo zecS@ieAKL8%bVd@+-KT{yI|S}O>pYckUFs;ry9Ow$CD@ztz5K-*D$^{i(_1llhSh^ zEkL$}tsQt5>QA^;QgjgIfBDmcOgi5YDyu?t6vSnbp=1+@6D& z5MJ}B8q;bRlVoxasyhcUF1+)o`&3r0colr}QJ3hcSdLu;9;td>kf@Tcn<@9sIx&=m z;AD;SCh95=&p;$r{Xz3iWCO^MX83AGJ(yH&eTXgv|0=34#-&WAmw{)U7OU9!Wz^!7 zZ%jZFi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i h0WYBP*#0Ks^FNSabJA*5${_#%002ovPDHLkV1oKhTl@e3 literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..75b2d164a5a98e212cca15ea7bf2ab5de5108680 GIT binary patch literal 3831 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 literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..c4df70d39da7941ef3f6dcb7f06a192d8dcb308d GIT binary patch literal 1888 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 iW8 + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Base.lproj/Main.storyboard b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 000000000000..f3c28516fb38 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/GoogleService-Info.plist b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/GoogleService-Info.plist new file mode 100644 index 000000000000..6042aab908af --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/GoogleService-Info.plist @@ -0,0 +1,44 @@ + + + + + AD_UNIT_ID_FOR_BANNER_TEST + ca-app-pub-3940256099942544/2934735716 + AD_UNIT_ID_FOR_INTERSTITIAL_TEST + ca-app-pub-3940256099942544/4411468910 + CLIENT_ID + 479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u + ANDROID_CLIENT_ID + 479882132969-jie8r1me6dsra60pal6ejaj8dgme3tg0.apps.googleusercontent.com + API_KEY + AIzaSyBECOwLTAN6PU4Aet1b2QLGIb3kRK8Xjew + GCM_SENDER_ID + 479882132969 + PLIST_VERSION + 1 + BUNDLE_ID + io.flutter.plugins.googleSignInExample + PROJECT_ID + my-flutter-proj + STORAGE_BUCKET + my-flutter-proj.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:479882132969:ios:2643f950e0a0da08 + DATABASE_URL + https://my-flutter-proj.firebaseio.com + SERVER_CLIENT_ID + YOUR_SERVER_CLIENT_ID + + \ No newline at end of file diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Info.plist b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Info.plist new file mode 100644 index 000000000000..187584d1cfd9 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Info.plist @@ -0,0 +1,64 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + Google Sign-In Example + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + GoogleSignInExample + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLSchemes + + com.googleusercontent.apps.479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u + + + + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + CADisableMinimumFrameDurationOnPhone + + + diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/main.m b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/main.m new file mode 100644 index 000000000000..f143297b30d6 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/main.m @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import +#import "AppDelegate.h" + +int main(int argc, char *argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/packages/google_sign_in/google_sign_in/example/ios/RunnerTests/GoogleSignInTests.m b/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m similarity index 99% rename from packages/google_sign_in/google_sign_in/example/ios/RunnerTests/GoogleSignInTests.m rename to packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m index 6f8b821a5299..dbbd65397ac5 100644 --- a/packages/google_sign_in/google_sign_in/example/ios/RunnerTests/GoogleSignInTests.m +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m @@ -5,8 +5,8 @@ @import Flutter; @import XCTest; -@import google_sign_in; -@import google_sign_in.Test; +@import google_sign_in_ios; +@import google_sign_in_ios.Test; @import GoogleSignIn; // OCMock library doesn't generate a valid modulemap. diff --git a/packages/google_sign_in/google_sign_in/example/ios/RunnerUITests/Info.plist b/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/Info.plist similarity index 100% rename from packages/google_sign_in/google_sign_in/example/ios/RunnerUITests/Info.plist rename to packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/Info.plist diff --git a/packages/google_sign_in/google_sign_in/example/ios/RunnerUITests/GoogleSignInUITests.m b/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerUITests/GoogleSignInUITests.m similarity index 100% rename from packages/google_sign_in/google_sign_in/example/ios/RunnerUITests/GoogleSignInUITests.m rename to packages/google_sign_in/google_sign_in_ios/example/ios/RunnerUITests/GoogleSignInUITests.m diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerUITests/Info.plist b/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerUITests/Info.plist new file mode 100644 index 000000000000..64d65ca49577 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerUITests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart b/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart new file mode 100644 index 000000000000..a750c330001d --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart @@ -0,0 +1,179 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; +import 'dart:convert' show json; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; +import 'package:http/http.dart' as http; + +void main() { + runApp( + MaterialApp( + title: 'Google Sign In', + home: SignInDemo(), + ), + ); +} + +class SignInDemo extends StatefulWidget { + @override + State createState() => SignInDemoState(); +} + +class SignInDemoState extends State { + GoogleSignInUserData? _currentUser; + String _contactText = ''; + // Future that completes when `init` has completed on the sign in instance. + Future? _initialization; + + @override + void initState() { + super.initState(); + _signIn(); + } + + Future _ensureInitialized() { + return _initialization ??= GoogleSignInPlatform.instance.init( + scopes: [ + 'email', + 'https://www.googleapis.com/auth/contacts.readonly', + ], + )..catchError((dynamic _) { + _initialization = null; + }); + } + + void _setUser(GoogleSignInUserData? user) { + setState(() { + _currentUser = user; + if (user != null) { + _handleGetContact(user); + } + }); + } + + Future _signIn() async { + await _ensureInitialized(); + final GoogleSignInUserData? newUser = + await GoogleSignInPlatform.instance.signInSilently(); + _setUser(newUser); + } + + Future> _getAuthHeaders() async { + final GoogleSignInUserData? user = _currentUser; + if (user == null) { + throw StateError('No user signed in'); + } + + final GoogleSignInTokenData response = + await GoogleSignInPlatform.instance.getTokens( + email: user.email, + shouldRecoverAuth: true, + ); + + return { + 'Authorization': 'Bearer ${response.accessToken}', + // TODO(kevmoo): Use the correct value once it's available. + // See https://github.com/flutter/flutter/issues/80905 + 'X-Goog-AuthUser': '0', + }; + } + + Future _handleGetContact(GoogleSignInUserData user) async { + setState(() { + _contactText = 'Loading contact info...'; + }); + final http.Response response = await http.get( + Uri.parse('https://people.googleapis.com/v1/people/me/connections' + '?requestMask.includeField=person.names'), + headers: await _getAuthHeaders(), + ); + if (response.statusCode != 200) { + setState(() { + _contactText = 'People API gave a ${response.statusCode} ' + 'response. Check logs for details.'; + }); + print('People API ${response.statusCode} response: ${response.body}'); + return; + } + final Map data = + json.decode(response.body) as Map; + final int contactCount = + (data['connections'] as List?)?.length ?? 0; + setState(() { + _contactText = '$contactCount contacts found'; + }); + } + + Future _handleSignIn() async { + try { + await _ensureInitialized(); + _setUser(await GoogleSignInPlatform.instance.signIn()); + } catch (error) { + final bool canceled = + error is PlatformException && error.code == 'sign_in_canceled'; + if (!canceled) { + print(error); + } + } + } + + Future _handleSignOut() async { + await _ensureInitialized(); + await GoogleSignInPlatform.instance.disconnect(); + } + + Widget _buildBody() { + final GoogleSignInUserData? user = _currentUser; + if (user != null) { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + ListTile( + title: Text(user.displayName ?? ''), + subtitle: Text(user.email), + ), + const Text('Signed in successfully.'), + Text(_contactText), + ElevatedButton( + child: const Text('SIGN OUT'), + onPressed: _handleSignOut, + ), + ElevatedButton( + child: const Text('REFRESH'), + onPressed: () => _handleGetContact(user), + ), + ], + ); + } else { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + const Text('You are not currently signed in.'), + ElevatedButton( + child: const Text('SIGN IN'), + onPressed: _handleSignIn, + ), + ], + ); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Google Sign In'), + ), + body: ConstrainedBox( + constraints: const BoxConstraints.expand(), + child: _buildBody(), + )); + } +} diff --git a/packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml b/packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml new file mode 100644 index 000000000000..f2d32c521c1a --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml @@ -0,0 +1,29 @@ +name: google_sign_in_example +description: Example of Google Sign-In plugin. +publish_to: none + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.5.0" + +dependencies: + flutter: + sdk: flutter + google_sign_in_ios: + # When depending on this package from a real application you should use: + # google_sign_in_ios: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + google_sign_in_platform_interface: ^2.1.0 + http: ^0.13.0 + +dev_dependencies: + flutter_driver: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/google_sign_in/google_sign_in_ios/example/test_driver/integration_test.dart b/packages/google_sign_in/google_sign_in_ios/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4f10f2a522f3 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/test_driver/integration_test.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); diff --git a/packages/google_sign_in/google_sign_in/ios/Assets/.gitkeep b/packages/google_sign_in/google_sign_in_ios/ios/Assets/.gitkeep similarity index 100% rename from packages/google_sign_in/google_sign_in/ios/Assets/.gitkeep rename to packages/google_sign_in/google_sign_in_ios/ios/Assets/.gitkeep diff --git a/packages/google_sign_in/google_sign_in/ios/Classes/FLTGoogleSignInPlugin.h b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.h similarity index 100% rename from packages/google_sign_in/google_sign_in/ios/Classes/FLTGoogleSignInPlugin.h rename to packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.h diff --git a/packages/google_sign_in/google_sign_in/ios/Classes/FLTGoogleSignInPlugin.m b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m similarity index 100% rename from packages/google_sign_in/google_sign_in/ios/Classes/FLTGoogleSignInPlugin.m rename to packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m diff --git a/packages/google_sign_in/google_sign_in/ios/Classes/FLTGoogleSignInPlugin.modulemap b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.modulemap similarity index 55% rename from packages/google_sign_in/google_sign_in/ios/Classes/FLTGoogleSignInPlugin.modulemap rename to packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.modulemap index 271f509e7fd7..31e30d93c582 100644 --- a/packages/google_sign_in/google_sign_in/ios/Classes/FLTGoogleSignInPlugin.modulemap +++ b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.modulemap @@ -1,5 +1,5 @@ -framework module google_sign_in { - umbrella header "google_sign_in-umbrella.h" +framework module google_sign_in_ios { + umbrella header "google_sign_in_ios-umbrella.h" export * module * { export * } diff --git a/packages/google_sign_in/google_sign_in/ios/Classes/FLTGoogleSignInPlugin_Test.h b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin_Test.h similarity index 89% rename from packages/google_sign_in/google_sign_in/ios/Classes/FLTGoogleSignInPlugin_Test.h rename to packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin_Test.h index 8fa6cf348018..f8d5be6f8522 100644 --- a/packages/google_sign_in/google_sign_in/ios/Classes/FLTGoogleSignInPlugin_Test.h +++ b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin_Test.h @@ -4,7 +4,7 @@ // This header is available in the Test module. Import via "@import google_sign_in.Test;" -#import +#import @class GIDSignIn; diff --git a/packages/google_sign_in/google_sign_in/ios/Classes/google_sign_in-umbrella.h b/packages/google_sign_in/google_sign_in_ios/ios/Classes/google_sign_in_ios-umbrella.h similarity index 85% rename from packages/google_sign_in/google_sign_in/ios/Classes/google_sign_in-umbrella.h rename to packages/google_sign_in/google_sign_in_ios/ios/Classes/google_sign_in_ios-umbrella.h index 343c390f1782..23b7e992a5cd 100644 --- a/packages/google_sign_in/google_sign_in/ios/Classes/google_sign_in-umbrella.h +++ b/packages/google_sign_in/google_sign_in_ios/ios/Classes/google_sign_in_ios-umbrella.h @@ -3,7 +3,7 @@ // found in the LICENSE file. #import -#import +#import FOUNDATION_EXPORT double google_sign_inVersionNumber; FOUNDATION_EXPORT const unsigned char google_sign_inVersionString[]; diff --git a/packages/google_sign_in/google_sign_in/ios/google_sign_in.podspec b/packages/google_sign_in/google_sign_in_ios/ios/google_sign_in_ios.podspec similarity index 90% rename from packages/google_sign_in/google_sign_in/ios/google_sign_in.podspec rename to packages/google_sign_in/google_sign_in_ios/ios/google_sign_in_ios.podspec index 19ea753520a7..f583f6cffbf0 100644 --- a/packages/google_sign_in/google_sign_in/ios/google_sign_in.podspec +++ b/packages/google_sign_in/google_sign_in_ios/ios/google_sign_in_ios.podspec @@ -2,7 +2,7 @@ # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html # Pod::Spec.new do |s| - s.name = 'google_sign_in' + s.name = 'google_sign_in_ios' s.version = '0.0.1' s.summary = 'Google Sign-In plugin for Flutter' s.description = <<-DESC @@ -11,7 +11,7 @@ Enables Google Sign-In in Flutter apps. s.homepage = 'https://github.com/flutter/plugins/tree/main/packages/google_sign_in' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/google_sign_in' } + s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/google_sign_in/google_sign_in_ios' } s.source_files = 'Classes/**/*.{h,m}' s.public_header_files = 'Classes/**/*.h' s.module_map = 'Classes/FLTGoogleSignInPlugin.modulemap' diff --git a/packages/google_sign_in/google_sign_in_ios/pubspec.yaml b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml new file mode 100644 index 000000000000..52c4f77feb4b --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml @@ -0,0 +1,35 @@ +name: google_sign_in_ios +description: Android implementation of the google_sign_in plugin. +repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in_ios +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 +version: 5.2.5 + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.5.0" + +flutter: + plugin: + implements: google_sign_in + platforms: + ios: + pluginClass: FLTGoogleSignInPlugin + +dependencies: + flutter: + sdk: flutter + google_sign_in_platform_interface: ^2.1.0 + +dev_dependencies: + flutter_driver: + sdk: flutter + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + +# The example deliberately includes limited-use secrets. +false_secrets: + - /example/ios/Runner/GoogleService-Info.plist + - /example/ios/RunnerTests/GoogleSignInTests.m + - /example/lib/main.dart From f6229363f4e6e67eb5329bdb4a918d14fa8d5883 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 12 Apr 2022 15:04:12 -0400 Subject: [PATCH 379/600] Roll Flutter from 9e0f0fe9d7b8 to 0f2b1a3baf20 (4 revisions) (#5235) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 25c6ddc070f4..b3b760fb2b46 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -9e0f0fe9d7b8fc42195c84c521bf5809c9dbbfe4 +0f2b1a3baf203586ad0580e9c74920cc2db4e2f7 From 52bd662fd4e8a59a3d2015b6ef928f22330ba09f Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Tue, 12 Apr 2022 12:51:56 -0700 Subject: [PATCH 380/600] [webview_flutter_wkwebview] Add support for cookie manager (#5203) --- .../lib/src/common/web_kit.pigeon.dart | 9 +- .../lib/src/foundation/foundation.dart | 89 ++++++++++++++++++- .../lib/src/web_kit/web_kit.dart | 74 +++++++++++++-- .../lib/src/web_kit/web_kit_api_impls.dart | 2 +- .../lib/src/web_kit_cookie_manager.dart | 54 +++++++++++ .../pigeons/web_kit.dart | 2 +- .../test/src/common/test_web_kit.pigeon.dart | 6 +- .../src/foundation/foundation_test.mocks.dart | 2 +- .../test/src/web_kit/web_kit_test.dart | 22 +++-- .../test/src/web_kit/web_kit_test.mocks.dart | 7 +- .../test/src/web_kit_cookie_manager_test.dart | 86 ++++++++++++++++++ .../web_kit_cookie_manager_test.mocks.dart | 59 ++++++++++++ .../test/src/web_kit_webview_widget_test.dart | 22 ++--- .../web_kit_webview_widget_test.mocks.dart | 30 ++++--- 14 files changed, 414 insertions(+), 50 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_cookie_manager.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart index 22013d2ff0cc..5bf79a675be0 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart @@ -283,7 +283,7 @@ class WKWebsiteDataStoreHostApi { } } - Future removeDataOfTypes( + Future removeDataOfTypes( int arg_instanceId, List arg_dataTypes, double arg_secondsModifiedSinceEpoch) async { @@ -308,8 +308,13 @@ class WKWebsiteDataStoreHostApi { message: error['message'] as String?, details: error['details'], ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { - return; + return (replyMap['result'] as bool?)!; } } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart index 8aa315aa10ee..1fef81d9dbaa 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart @@ -86,10 +86,85 @@ enum NSKeyValueChangeKey { /// Indicates the value of this key is the value before the attribute was changed. /// - /// https://developer.apple.com/documentation/foundation/nskeyvaluechangeoldkey?language=objc. + /// See https://developer.apple.com/documentation/foundation/nskeyvaluechangeoldkey?language=objc. oldValue, } +/// The supported keys in a cookie attributes dictionary. +/// +/// Wraps [NSHTTPCookiePropertyKey](https://developer.apple.com/documentation/foundation/nshttpcookiepropertykey). +enum NSHttpCookiePropertyKey { + /// A String object containing the comment for the cookie. + /// + /// See https://developer.apple.com/documentation/foundation/nshttpcookiecomment. + comment, + + /// A String object containing the comment URL for the cookie. + /// + /// See https://developer.apple.com/documentation/foundation/nshttpcookiecommenturl. + commentUrl, + + /// A String object stating whether the cookie should be discarded at the end of the session. + /// + /// See https://developer.apple.com/documentation/foundation/nshttpcookiediscard. + discard, + + /// A String object specifying the expiration date for the cookie. + /// + /// See https://developer.apple.com/documentation/foundation/nshttpcookiedomain. + domain, + + /// A String object specifying the expiration date for the cookie. + /// + /// See https://developer.apple.com/documentation/foundation/nshttpcookieexpires. + expires, + + /// A String object containing an integer value stating how long in seconds the cookie should be kept, at most. + /// + /// See https://developer.apple.com/documentation/foundation/nshttpcookiemaximumage. + maximumAge, + + /// A String object containing the name of the cookie (required). + /// + /// See https://developer.apple.com/documentation/foundation/nshttpcookiename. + name, + + /// A String object containing the URL that set this cookie. + /// + /// See https://developer.apple.com/documentation/foundation/nshttpcookieoriginurl. + originUrl, + + /// A String object containing the path for the cookie. + /// + /// See https://developer.apple.com/documentation/foundation/nshttpcookiepath. + path, + + /// A String object containing comma-separated integer values specifying the ports for the cookie. + /// + /// See https://developer.apple.com/documentation/foundation/nshttpcookieport. + port, + + /// A String indicating the same-site policy for the cookie. + /// + /// See https://developer.apple.com/documentation/foundation/nshttpcookiesamesitepolicy. + sameSitePolicy, + + /// A String object indicating that the cookie should be transmitted only over secure channels. + /// + /// See https://developer.apple.com/documentation/foundation/nshttpcookiesecure. + secure, + + /// A String object containing the value of the cookie. + /// + /// See https://developer.apple.com/documentation/foundation/nshttpcookievalue. + value, + + /// A String object that specifies the version of the cookie. + /// + /// See https://developer.apple.com/documentation/foundation/nshttpcookieversion. + version, +} + /// A URL load request that is independent of protocol or URL scheme. /// /// Wraps [NSUrlRequest](https://developer.apple.com/documentation/foundation/nsurlrequest?language=objc). @@ -142,6 +217,18 @@ class NSError { final String localizedDescription; } +/// A representation of an HTTP cookie. +/// +/// Wraps [NSHTTPCookie](https://developer.apple.com/documentation/foundation/nshttpcookie). +@immutable +class NSHttpCookie { + /// Initializes an HTTP cookie object using the provided properties. + const NSHttpCookie.withProperties(this.properties); + + /// Properties of the new cookie object. + final Map properties; +} + /// The root class of most Objective-C class hierarchies. class NSObject { /// Constructs an [NSObject]. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart index 0703ce1267d9..416e2626fc4a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart @@ -126,6 +126,18 @@ class WKErrorCode { static const int javaScriptResultTypeIsUnsupported = 5; } +/// A record of the data that a particular website stores persistently. +/// +/// Wraps [WKWebsiteDataRecord](https://developer.apple.com/documentation/webkit/wkwebsitedatarecord?language=objc). +@immutable +class WKWebsiteDataRecord { + /// Constructs a [WKWebsiteDataRecord]. + const WKWebsiteDataRecord({required this.displayName}); + + /// Identifying information that you display to users. + final String displayName; +} + /// An object that contains information about an action that causes navigation to occur. /// /// Wraps [WKNavigationAction](https://developer.apple.com/documentation/webkit/wknavigationaction?language=objc). @@ -230,26 +242,51 @@ class WKPreferences { /// /// Wraps [WKWebsiteDataStore](https://developer.apple.com/documentation/webkit/wkwebsitedatastore?language=objc). class WKWebsiteDataStore { - /// Constructs a [WKWebsiteDataStore] that is owned by [configuration]. - @visibleForTesting - WKWebsiteDataStore.fromWebViewConfiguration( - WKWebViewConfiguration configuration, { + WKWebsiteDataStore._({ BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, }) : _websiteDataStoreApi = WKWebsiteDataStoreHostApiImpl( binaryMessenger: binaryMessenger, instanceManager: instanceManager, - ) { - _websiteDataStoreApi.createFromWebViewConfigurationForInstances( - this, + ); + + factory WKWebsiteDataStore._defaultDataStore() { + throw UnimplementedError(); + } + + /// Constructs a [WKWebsiteDataStore] that is owned by [configuration]. + @visibleForTesting + factory WKWebsiteDataStore.fromWebViewConfiguration( + WKWebViewConfiguration configuration, { + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) { + final WKWebsiteDataStore websiteDataStore = WKWebsiteDataStore._( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ); + websiteDataStore._websiteDataStoreApi + .createFromWebViewConfigurationForInstances( + websiteDataStore, configuration, ); + return websiteDataStore; } + /// Default data store that stores data persistently to disk. + static final WKWebsiteDataStore defaultDataStore = + WKWebsiteDataStore._defaultDataStore(); + final WKWebsiteDataStoreHostApiImpl _websiteDataStoreApi; + /// Manages the HTTP cookies associated with a particular web view. + late final WKHttpCookieStore httpCookieStore = + WKHttpCookieStore.fromWebsiteDataStore(this); + /// Removes website data that changed after the specified date. - Future removeDataOfTypes( + /// + /// Returns whether any data was removed. + Future removeDataOfTypes( Set dataTypes, DateTime since, ) { @@ -261,6 +298,26 @@ class WKWebsiteDataStore { } } +/// An object that manages the HTTP cookies associated with a particular web view. +/// +/// Wraps [WKHTTPCookieStore](https://developer.apple.com/documentation/webkit/wkhttpcookiestore?language=objc). +class WKHttpCookieStore { + /// Constructs a [WKHttpCookieStore] that is owned by [dataStore]. + @visibleForTesting + WKHttpCookieStore.fromWebsiteDataStore( + // TODO(bparrishMines): Remove ignore on implementation. + // ignore: avoid_unused_constructor_parameters + WKWebsiteDataStore dataStore, + ) { + throw UnimplementedError(); + } + + /// Adds a cookie to the cookie store. + Future setCookie(NSHttpCookie cookie) { + throw UnimplementedError(); + } +} + /// An interface for receiving messages from JavaScript code running in a webpage. /// /// Wraps [WKScriptMessageHandler](https://developer.apple.com/documentation/webkit/wkscriptmessagehandler?language=objc) @@ -471,7 +528,6 @@ class WKWebViewConfiguration { Future setMediaTypesRequiringUserActionForPlayback( Set types, ) { - assert(types.isNotEmpty); return _webViewConfigurationApi .setMediaTypesRequiringUserActionForPlaybackForInstances( this, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart index 188f65cbe177..1cda9d4b94c2 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart @@ -133,7 +133,7 @@ class WKWebsiteDataStoreHostApiImpl extends WKWebsiteDataStoreHostApi { } /// Calls [removeDataOfTypes] with the ids of the provided object instances. - Future removeDataOfTypesForInstances( + Future removeDataOfTypesForInstances( WKWebsiteDataStore instance, Set dataTypes, { required double secondsModifiedSinceEpoch, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_cookie_manager.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_cookie_manager.dart new file mode 100644 index 000000000000..c73111d73fd0 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_cookie_manager.dart @@ -0,0 +1,54 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; +import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; +import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart'; + +/// Handles all cookie operations for the WebView platform. +class WebKitCookieManager extends WebViewCookieManagerPlatform { + /// Constructs a [WebKitCookieManager]. + WebKitCookieManager({WKWebsiteDataStore? websiteDataStore}) + : websiteDataStore = + websiteDataStore ?? WKWebsiteDataStore.defaultDataStore; + + /// Manages stored data for [WKWebView]s. + final WKWebsiteDataStore websiteDataStore; + + @override + Future clearCookies() async { + return websiteDataStore.removeDataOfTypes( + {WKWebsiteDataTypes.cookies}, + DateTime.fromMillisecondsSinceEpoch(0), + ); + } + + @override + Future setCookie(WebViewCookie cookie) { + if (!_isValidPath(cookie.path)) { + throw ArgumentError( + 'The path property for the provided cookie was not given a legal value.'); + } + + return websiteDataStore.httpCookieStore.setCookie( + NSHttpCookie.withProperties( + { + NSHttpCookiePropertyKey.name: cookie.name, + NSHttpCookiePropertyKey.value: cookie.value, + NSHttpCookiePropertyKey.domain: cookie.domain, + NSHttpCookiePropertyKey.path: cookie.path, + }, + ), + ); + } + + bool _isValidPath(String path) { + // Permitted ranges based on RFC6265bis: https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-02#section-4.1.1 + return !path.codeUnits.any( + (int char) { + return (char < 0x20 || char > 0x3A) && (char < 0x3C || char > 0x7E); + }, + ); + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart index 0d5328dda8d6..370149e638ff 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart @@ -179,7 +179,7 @@ abstract class WKWebsiteDataStoreHostApi { ); @async - void removeDataOfTypes( + bool removeDataOfTypes( int instanceId, List dataTypes, double secondsModifiedSinceEpoch, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart index 78e4b9dcfd3d..0e369a351c75 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart @@ -44,7 +44,7 @@ abstract class TestWKWebsiteDataStoreHostApi { void createFromWebViewConfiguration( int instanceId, int configurationInstanceId); - Future removeDataOfTypes( + Future removeDataOfTypes( int instanceId, List dataTypes, double secondsModifiedSinceEpoch); @@ -96,9 +96,9 @@ abstract class TestWKWebsiteDataStoreHostApi { final double? arg_secondsModifiedSinceEpoch = (args[2] as double?); assert(arg_secondsModifiedSinceEpoch != null, 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes was null, expected non-null double.'); - await api.removeDataOfTypes( + final bool output = await api.removeDataOfTypes( arg_instanceId!, arg_dataTypes!, arg_secondsModifiedSinceEpoch!); - return {}; + return {'result': output}; }); } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.mocks.dart index 7bd208eeac05..b8552c5d8157 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.mocks.dart @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.1.0 from annotations -// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart. +// in webview_flutter_wkwebview/test/src/foundation/foundation_test.dart. // Do not manually edit this file. import 'package:mockito/mockito.dart' as _i1; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart index d51fa9793dd4..aa77157efcf7 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart @@ -70,19 +70,29 @@ void main() { }); test('removeDataOfTypes', () { - websiteDataStore.removeDataOfTypes( - {WKWebsiteDataTypes.cookies}, - DateTime.fromMillisecondsSinceEpoch(5000), + when(mockPlatformHostApi.removeDataOfTypes( + any, + any, + any, + )).thenAnswer((_) => Future.value(true)); + + expect( + websiteDataStore.removeDataOfTypes( + {WKWebsiteDataTypes.cookies}, + DateTime.fromMillisecondsSinceEpoch(5000), + ), + completion(true), ); - final WKWebsiteDataTypesEnumData typeData = + final List typeData = verify(mockPlatformHostApi.removeDataOfTypes( instanceManager.getInstanceId(websiteDataStore), captureAny, 5.0, - )).captured.single.single as WKWebsiteDataTypesEnumData; + )).captured.single.cast() + as List; - expect(typeData.value, WKWebsiteDataTypesEnum.cookies); + expect(typeData.single.value, WKWebsiteDataTypesEnum.cookies); }); }); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart index a2f16317f1fe..9629e2e62636 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.1.0 from annotations -// in webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart. +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart. // Do not manually edit this file. import 'dart:async' as _i4; @@ -275,13 +275,12 @@ class MockTestWKWebsiteDataStoreHostApi extends _i1.Mock [instanceId, configurationInstanceId]), returnValueForMissingStub: null); @override - _i4.Future removeDataOfTypes( + _i4.Future removeDataOfTypes( int? instanceId, List<_i3.WKWebsiteDataTypesEnumData?>? dataTypes, double? secondsModifiedSinceEpoch) => (super.noSuchMethod( Invocation.method(#removeDataOfTypes, [instanceId, dataTypes, secondsModifiedSinceEpoch]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValue: Future.value(false)) as _i4.Future); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart new file mode 100644 index 000000000000..4529316d3159 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart @@ -0,0 +1,86 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; +import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; +import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart'; +import 'package:webview_flutter_wkwebview/src/web_kit_cookie_manager.dart'; +import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart'; + +import 'web_kit_cookie_manager_test.mocks.dart'; + +@GenerateMocks([ + WKHttpCookieStore, + WKWebsiteDataStore, +]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('$WebKitWebViewWidget', () { + late MockWKWebsiteDataStore mockWebsiteDataStore; + late MockWKHttpCookieStore mockWKHttpCookieStore; + + late WebKitCookieManager cookieManager; + + setUp(() { + mockWebsiteDataStore = MockWKWebsiteDataStore(); + mockWKHttpCookieStore = MockWKHttpCookieStore(); + when(mockWebsiteDataStore.httpCookieStore) + .thenReturn(mockWKHttpCookieStore); + + cookieManager = + WebKitCookieManager(websiteDataStore: mockWebsiteDataStore); + }); + + test('clearCookies', () async { + when(mockWebsiteDataStore.removeDataOfTypes( + {WKWebsiteDataTypes.cookies}, any)) + .thenAnswer((_) => Future.value(true)); + expect(cookieManager.clearCookies(), completion(true)); + + when(mockWebsiteDataStore.removeDataOfTypes( + {WKWebsiteDataTypes.cookies}, any)) + .thenAnswer((_) => Future.value(false)); + expect(cookieManager.clearCookies(), completion(false)); + }); + + test('setCookie', () async { + await cookieManager.setCookie( + const WebViewCookie(name: 'a', value: 'b', domain: 'c', path: 'd'), + ); + + final NSHttpCookie cookie = + verify(mockWKHttpCookieStore.setCookie(captureAny)).captured.single + as NSHttpCookie; + expect( + cookie.properties, + { + NSHttpCookiePropertyKey.name: 'a', + NSHttpCookiePropertyKey.value: 'b', + NSHttpCookiePropertyKey.domain: 'c', + NSHttpCookiePropertyKey.path: 'd', + }, + ); + }); + + test('setCookie throws argument error with invalid path', () async { + expect( + () => cookieManager.setCookie( + WebViewCookie( + name: 'a', + value: 'b', + domain: 'c', + path: String.fromCharCode(0x1F), + ), + ), + throwsArgumentError, + ); + }); + }); +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart new file mode 100644 index 000000000000..88025d3d9465 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart @@ -0,0 +1,59 @@ +// Mocks generated by Mockito 5.1.0 from annotations +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart. +// Do not manually edit this file. + +import 'dart:async' as _i3; + +import 'package:mockito/mockito.dart' as _i1; +import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart' + as _i4; +import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart' as _i2; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types + +class _FakeWKHttpCookieStore_0 extends _i1.Fake + implements _i2.WKHttpCookieStore {} + +/// A class which mocks [WKHttpCookieStore]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockWKHttpCookieStore extends _i1.Mock implements _i2.WKHttpCookieStore { + MockWKHttpCookieStore() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.Future setCookie(_i4.NSHttpCookie? cookie) => + (super.noSuchMethod(Invocation.method(#setCookie, [cookie]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i3.Future); +} + +/// A class which mocks [WKWebsiteDataStore]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockWKWebsiteDataStore extends _i1.Mock + implements _i2.WKWebsiteDataStore { + MockWKWebsiteDataStore() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.WKHttpCookieStore get httpCookieStore => + (super.noSuchMethod(Invocation.getter(#httpCookieStore), + returnValue: _FakeWKHttpCookieStore_0()) as _i2.WKHttpCookieStore); + @override + _i3.Future removeDataOfTypes( + Set<_i2.WKWebsiteDataTypes>? dataTypes, DateTime? since) => + (super.noSuchMethod( + Invocation.method(#removeDataOfTypes, [dataTypes, since]), + returnValue: Future.value(false)) as _i3.Future); +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index 0c36f6b7de5a..86ee1a99b949 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -769,17 +769,19 @@ void main() { testWidgets('clearCache', (WidgetTester tester) async { await buildWidget(tester); + when( + mockWebsiteDataStore.removeDataOfTypes( + { + WKWebsiteDataTypes.memoryCache, + WKWebsiteDataTypes.diskCache, + WKWebsiteDataTypes.offlineWebApplicationCache, + WKWebsiteDataTypes.localStroage, + }, + DateTime.fromMillisecondsSinceEpoch(0), + ), + ).thenAnswer((_) => Future.value(false)); - await testController.clearCache(); - verify(mockWebsiteDataStore.removeDataOfTypes( - { - WKWebsiteDataTypes.memoryCache, - WKWebsiteDataTypes.diskCache, - WKWebsiteDataTypes.offlineWebApplicationCache, - WKWebsiteDataTypes.localStroage, - }, - DateTime.fromMillisecondsSinceEpoch(0), - )); + expect(testController.clearCache(), completes); }); testWidgets('addJavascriptChannels', (WidgetTester tester) async { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index 6c34e0c72004..bc79f656b6e3 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.1.0 from annotations -// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. +// in webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. // Do not manually edit this file. import 'dart:async' as _i5; @@ -45,14 +45,17 @@ class _FakeWKPreferences_4 extends _i1.Fake implements _i3.WKPreferences {} class _FakeWKWebsiteDataStore_5 extends _i1.Fake implements _i3.WKWebsiteDataStore {} -class _FakeWKWebView_6 extends _i1.Fake implements _i3.WKWebView {} +class _FakeWKHttpCookieStore_6 extends _i1.Fake + implements _i3.WKHttpCookieStore {} -class _FakeWKScriptMessageHandler_7 extends _i1.Fake +class _FakeWKWebView_7 extends _i1.Fake implements _i3.WKWebView {} + +class _FakeWKScriptMessageHandler_8 extends _i1.Fake implements _i3.WKScriptMessageHandler {} -class _FakeWKUIDelegate_8 extends _i1.Fake implements _i3.WKUIDelegate {} +class _FakeWKUIDelegate_9 extends _i1.Fake implements _i3.WKUIDelegate {} -class _FakeWKNavigationDelegate_9 extends _i1.Fake +class _FakeWKNavigationDelegate_10 extends _i1.Fake implements _i3.WKNavigationDelegate {} /// A class which mocks [UIScrollView]. @@ -399,12 +402,15 @@ class MockWKWebsiteDataStore extends _i1.Mock } @override - _i5.Future removeDataOfTypes( + _i3.WKHttpCookieStore get httpCookieStore => + (super.noSuchMethod(Invocation.getter(#httpCookieStore), + returnValue: _FakeWKHttpCookieStore_6()) as _i3.WKHttpCookieStore); + @override + _i5.Future removeDataOfTypes( Set<_i3.WKWebsiteDataTypes>? dataTypes, DateTime? since) => (super.noSuchMethod( Invocation.method(#removeDataOfTypes, [dataTypes, since]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValue: Future.value(false)) as _i5.Future); } /// A class which mocks [WKUIDelegate]. @@ -534,18 +540,18 @@ class MockWebViewWidgetProxy extends _i1.Mock @override _i3.WKWebView createWebView(_i3.WKWebViewConfiguration? configuration) => (super.noSuchMethod(Invocation.method(#createWebView, [configuration]), - returnValue: _FakeWKWebView_6()) as _i3.WKWebView); + returnValue: _FakeWKWebView_7()) as _i3.WKWebView); @override _i3.WKScriptMessageHandler createScriptMessageHandler() => (super.noSuchMethod(Invocation.method(#createScriptMessageHandler, []), - returnValue: _FakeWKScriptMessageHandler_7()) + returnValue: _FakeWKScriptMessageHandler_8()) as _i3.WKScriptMessageHandler); @override _i3.WKUIDelegate createUIDelgate() => (super.noSuchMethod(Invocation.method(#createUIDelgate, []), - returnValue: _FakeWKUIDelegate_8()) as _i3.WKUIDelegate); + returnValue: _FakeWKUIDelegate_9()) as _i3.WKUIDelegate); @override _i3.WKNavigationDelegate createNavigationDelegate() => (super.noSuchMethod( Invocation.method(#createNavigationDelegate, []), - returnValue: _FakeWKNavigationDelegate_9()) as _i3.WKNavigationDelegate); + returnValue: _FakeWKNavigationDelegate_10()) as _i3.WKNavigationDelegate); } From 463bf985b12f150f67a5fe93dffee145c3279adb Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 12 Apr 2022 16:39:11 -0400 Subject: [PATCH 381/600] Roll Flutter from 0f2b1a3baf20 to 33e540a92e39 (6 revisions) (#5237) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index b3b760fb2b46..77cdca8dff59 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -0f2b1a3baf203586ad0580e9c74920cc2db4e2f7 +33e540a92e3916fa8c44f095a032f44bb6110664 From 818fed95281f9ca3a93920b34e17aaf23a0f24c8 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 12 Apr 2022 17:44:14 -0400 Subject: [PATCH 382/600] Roll Flutter from 33e540a92e39 to 17be6d73e6cb (2 revisions) (#5239) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 77cdca8dff59..bdf93dcf4489 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -33e540a92e3916fa8c44f095a032f44bb6110664 +17be6d73e6cb5f21f601569f11fea60597330d84 From 0b7e457131c15d1a2fed1025f1fb4cd8f9e37f36 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 12 Apr 2022 20:24:13 -0400 Subject: [PATCH 383/600] [google_sign_in] Publish federation (#5238) --- packages/google_sign_in/google_sign_in/CHANGELOG.md | 2 +- packages/google_sign_in/google_sign_in/pubspec.yaml | 12 +++--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index bee4dafaf16f..caab46de7c5b 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,4 +1,4 @@ -## NEXT +## 5.3.0 * Moves Android and iOS implementations to federated packages. diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index 43904af23121..760706f2e7bc 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/pubspec.yaml @@ -3,10 +3,7 @@ description: Flutter plugin for Google Sign-In, a secure authentication system for signing in with a Google account on Android and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.2.5 -# Temporarily disable publishing to allow moving Android and iOS -# implementations. -publish_to: none +version: 5.3.0 environment: @@ -26,11 +23,8 @@ flutter: dependencies: flutter: sdk: flutter - # Temporary path dependencies to allow moving Android and iOS implementations. - google_sign_in_android: - path: ../google_sign_in_android - google_sign_in_ios: - path: ../google_sign_in_ios + google_sign_in_android: ^5.2.5 + google_sign_in_ios: ^5.2.5 google_sign_in_platform_interface: ^2.1.0 google_sign_in_web: ^0.10.0 From a7e3bfc5c4256c2a2e71403fed384949fa5922fb Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 12 Apr 2022 21:04:13 -0400 Subject: [PATCH 384/600] Roll Flutter from 17be6d73e6cb to 888208c1f4e0 (4 revisions) (#5240) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index bdf93dcf4489..0e23be9dad2b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -17be6d73e6cb5f21f601569f11fea60597330d84 +888208c1f4e0d779e8863997a12725e5ef32e0c0 From 07bf81700b9aeef29f9007bc1f232e345d3094db Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 13 Apr 2022 00:19:06 -0400 Subject: [PATCH 385/600] Roll Flutter from 888208c1f4e0 to fd73f2730c78 (10 revisions) (#5246) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 0e23be9dad2b..83cc089a8050 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -888208c1f4e0d779e8863997a12725e5ef32e0c0 +fd73f2730c7876a2d52d71d88669cc48256da709 From d9450806d2f887830f18bea5f72c2dc3cee06d17 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 13 Apr 2022 01:24:11 -0400 Subject: [PATCH 386/600] Roll Flutter from fd73f2730c78 to 009688a99eb9 (1 revision) (#5247) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 83cc089a8050..b0e5d8c17c1b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -fd73f2730c7876a2d52d71d88669cc48256da709 +009688a99eb9f2c0a2db06ceb90022461705f572 From b6a9d6ccf0d13be817d29b36ece660079d606896 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 13 Apr 2022 10:59:09 -0400 Subject: [PATCH 387/600] Roll Flutter from 009688a99eb9 to 2b0255f0d9f3 (2 revisions) (#5248) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index b0e5d8c17c1b..146bc5e5f6b2 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -009688a99eb9f2c0a2db06ceb90022461705f572 +2b0255f0d9f38f5671eaffe2438240ed048670cc From ace57091e0c7901f28a6d36f056169c4e254a6bd Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 13 Apr 2022 12:04:10 -0400 Subject: [PATCH 388/600] Roll Flutter from 2b0255f0d9f3 to 5fb74db1a9cb (1 revision) (#5251) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 146bc5e5f6b2..1efe14550c21 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2b0255f0d9f38f5671eaffe2438240ed048670cc +5fb74db1a9cb8c57dbf58c1611159e4c31d2d43d From 980cf40ea600198f3b89a9f20ac3aee81313d8ca Mon Sep 17 00:00:00 2001 From: BeMacized Date: Wed, 13 Apr 2022 18:49:10 +0200 Subject: [PATCH 389/600] [local_auth] Change stopAuthentication to return false instead of throwing on iOS. (#5249) --- packages/local_auth/local_auth_ios/CHANGELOG.md | 4 ++++ packages/local_auth/local_auth_ios/lib/local_auth_ios.dart | 3 ++- packages/local_auth/local_auth_ios/pubspec.yaml | 2 +- .../local_auth/local_auth_ios/test/local_auth_test.dart | 6 +++--- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/local_auth/local_auth_ios/CHANGELOG.md b/packages/local_auth/local_auth_ios/CHANGELOG.md index 7f198f2d66c0..d826cc8032e5 100644 --- a/packages/local_auth/local_auth_ios/CHANGELOG.md +++ b/packages/local_auth/local_auth_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.1 + +* BREAKING CHANGE: Changes `stopAuthentication` to always return false instead of throwing an error. + ## 1.0.0 * Initial release from migration to federated architecture. diff --git a/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart b/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart index 7d085ccf14d9..d92d076d79fb 100644 --- a/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart +++ b/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart @@ -81,8 +81,9 @@ class LocalAuthIOS extends LocalAuthPlatform { Future isDeviceSupported() async => (await _channel.invokeMethod('isDeviceSupported')) ?? false; + /// Always returns false as this method is not supported on iOS. @override Future stopAuthentication() async { - throw UnimplementedError('stopAuthentication() is not supported on iOS.'); + return false; } } diff --git a/packages/local_auth/local_auth_ios/pubspec.yaml b/packages/local_auth/local_auth_ios/pubspec.yaml index a9126fdea676..537b1a64d003 100644 --- a/packages/local_auth/local_auth_ios/pubspec.yaml +++ b/packages/local_auth/local_auth_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_ios description: iOS implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.0 +version: 1.0.1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_ios/test/local_auth_test.dart b/packages/local_auth/local_auth_ios/test/local_auth_test.dart index b529764adbd6..180383bc258c 100644 --- a/packages/local_auth/local_auth_ios/test/local_auth_test.dart +++ b/packages/local_auth/local_auth_ios/test/local_auth_test.dart @@ -75,9 +75,9 @@ void main() { ); }); - test('stopAuthentication throws UnimplementedError', () async { - expect(() async => await localAuthentication.stopAuthentication(), - throwsUnimplementedError); + test('stopAuthentication returns false', () async { + final bool result = await localAuthentication.stopAuthentication(); + expect(result, false); }); group('With device auth fail over', () { From f487761bf965273e2d9a6bd1c09a7eb145efbd13 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 13 Apr 2022 13:29:12 -0400 Subject: [PATCH 390/600] Roll Flutter from 5fb74db1a9cb to 86d43e428aaa (2 revisions) (#5252) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 1efe14550c21..ac8097884e27 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -5fb74db1a9cb8c57dbf58c1611159e4c31d2d43d +86d43e428aaa049c7d83242c6b8f0b386cf39934 From 787060564a5143fa06e98e473740b3c4ab1786ef Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 13 Apr 2022 14:34:12 -0400 Subject: [PATCH 391/600] Roll Flutter from 86d43e428aaa to a20cd33d6734 (3 revisions) (#5253) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index ac8097884e27..3bad460e6f61 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -86d43e428aaa049c7d83242c6b8f0b386cf39934 +a20cd33d6734769b0d186ef91d653326972ed784 From af665aee67d86a6202bbd50afa67dec7a465a162 Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Wed, 13 Apr 2022 12:34:09 -0700 Subject: [PATCH 392/600] [google_sign_in_web] Add `plugin_name` to `init` call. (#5242) --- .../google_sign_in_web/CHANGELOG.md | 4 ++- .../example/integration_test/auth2_test.dart | 21 +++++++++++++++ .../gapi_mocks/src/auth2_init.dart | 2 ++ .../lib/google_sign_in_web.dart | 1 + .../lib/src/generated/gapiauth2.dart | 26 ++++++++++++------- .../google_sign_in_web/pubspec.yaml | 2 +- 6 files changed, 45 insertions(+), 11 deletions(-) diff --git a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md index e35caf75ea1f..aab4acae0376 100644 --- a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.10.1 * Updates minimum Flutter version to 2.8. +* Passes `plugin_name` to Google Sign-In's `init` method so new applications can + continue using this plugin after April 30th 2022. Issue [#88084](https://github.com/flutter/flutter/issues/88084). ## 0.10.0+5 diff --git a/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_test.dart b/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_test.dart index 2af9476dbb33..9db024361580 100644 --- a/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_test.dart +++ b/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:html' as html; + import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; @@ -126,6 +128,25 @@ void main() { throwsAssertionError); }); + // See: https://github.com/flutter/flutter/issues/88084 + testWidgets('Init passes plugin_name parameter with the expected value', + (WidgetTester tester) async { + await plugin.init( + hostedDomain: 'foo', + scopes: ['some', 'scope'], + clientId: '1234', + ); + + final Object? initParameters = + js_util.getProperty(html.window, 'gapi2.init.parameters'); + expect(initParameters, isNotNull); + + final Object? pluginNameParameter = + js_util.getProperty(initParameters!, 'plugin_name'); + expect(pluginNameParameter, isA()); + expect(pluginNameParameter, 'dart-google_sign_in_web'); + }); + group('Successful .init, then', () { setUp(() async { await plugin.init( diff --git a/packages/google_sign_in/google_sign_in_web/example/integration_test/gapi_mocks/src/auth2_init.dart b/packages/google_sign_in/google_sign_in_web/example/integration_test/gapi_mocks/src/auth2_init.dart index cc49b2759e7f..84f4e6ee8ba8 100644 --- a/packages/google_sign_in/google_sign_in_web/example/integration_test/gapi_mocks/src/auth2_init.dart +++ b/packages/google_sign_in/google_sign_in_web/example/integration_test/gapi_mocks/src/auth2_init.dart @@ -12,6 +12,8 @@ var mockUser = ${googleUser(userData)}; function GapiAuth2() {} GapiAuth2.prototype.init = function (initOptions) { + /*Leak the initOptions so we can look at them later.*/ + window['gapi2.init.parameters'] = initOptions; return { then: (onSuccess, onError) => { window.setTimeout(() => { diff --git a/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart b/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart index 731ced5ddbbe..533c353df310 100644 --- a/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart +++ b/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart @@ -92,6 +92,7 @@ class GoogleSignInPlugin extends GoogleSignInPlatform { // The js lib wants a space-separated list of values scope: scopes.join(' '), client_id: appClientId!, + plugin_name: 'dart-google_sign_in_web', )); final Completer isAuthInitialized = Completer(); diff --git a/packages/google_sign_in/google_sign_in_web/lib/src/generated/gapiauth2.dart b/packages/google_sign_in/google_sign_in_web/lib/src/generated/gapiauth2.dart index 88d196bad007..8e23713c90e9 100644 --- a/packages/google_sign_in/google_sign_in_web/lib/src/generated/gapiauth2.dart +++ b/packages/google_sign_in/google_sign_in_web/lib/src/generated/gapiauth2.dart @@ -233,15 +233,23 @@ abstract class ClientConfig { /// The default redirect_uri is the current URL stripped of query parameters and hash fragment. external String? get redirect_uri; external set redirect_uri(String? v); - external factory ClientConfig( - {String client_id, - String cookie_policy, - String scope, - bool fetch_basic_profile, - String? hosted_domain, - String openid_realm, - String /*'popup'|'redirect'*/ ux_mode, - String redirect_uri}); + + /// Allows newly created Client IDs to use the Google Platform Library from now until the March 30th, 2023 deprecation date. + /// See: https://github.com/flutter/flutter/issues/88084 + external String? get plugin_name; + external set plugin_name(String? v); + + external factory ClientConfig({ + String client_id, + String cookie_policy, + String scope, + bool fetch_basic_profile, + String? hosted_domain, + String openid_realm, + String /*'popup'|'redirect'*/ ux_mode, + String redirect_uri, + String plugin_name, + }); } @JS('gapi.auth2.SigninOptionsBuilder') diff --git a/packages/google_sign_in/google_sign_in_web/pubspec.yaml b/packages/google_sign_in/google_sign_in_web/pubspec.yaml index d97a7c4da6f1..5a09453b8e86 100644 --- a/packages/google_sign_in/google_sign_in_web/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_web/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Google Sign-In, a secure authentication system for signing in with a Google account on Android, iOS and Web. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 0.10.0+5 +version: 0.10.1 environment: sdk: ">=2.12.0 <3.0.0" From 4fa853d5c5feeb05f7e98a4fe236dc0373d186a7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 13 Apr 2022 16:44:07 -0400 Subject: [PATCH 393/600] Roll Flutter from a20cd33d6734 to 9526c84b2e48 (7 revisions) (#5255) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 3bad460e6f61..5c830b3b1a37 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -a20cd33d6734769b0d186ef91d653326972ed784 +9526c84b2e48331260f119687498f32424635730 From c622c1d1b0fa719b47d9bebb5836f16e51df6fd2 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 13 Apr 2022 17:49:06 -0400 Subject: [PATCH 394/600] Roll Flutter from 9526c84b2e48 to 52d17b667250 (2 revisions) (#5257) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 5c830b3b1a37..6c29c6d510c9 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -9526c84b2e48331260f119687498f32424635730 +52d17b667250c6f126f0a03cec04b6a8c72dee04 From 97fbd1fb42025b748bf1499399a2ba813943c94f Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 14 Apr 2022 00:14:09 -0400 Subject: [PATCH 395/600] Roll Flutter from 52d17b667250 to adda8e93881d (3 revisions) (#5258) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 6c29c6d510c9..9ed8fdcadfa8 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -52d17b667250c6f126f0a03cec04b6a8c72dee04 +adda8e93881d681680de2f7f33bfc021dac20c9d From c188b1854ac6e30597ffb3e3f935d5ec7218aae6 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 14 Apr 2022 02:34:09 -0400 Subject: [PATCH 396/600] Roll Flutter from adda8e93881d to 61be9229b7f3 (1 revision) (#5259) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 9ed8fdcadfa8..fbce84b0d935 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -adda8e93881d681680de2f7f33bfc021dac20c9d +61be9229b7f35557fceed203b572dc96384ade42 From 6f57118e69cc5c3a9bb6c19968142ebb6561ac99 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 14 Apr 2022 03:39:08 -0400 Subject: [PATCH 397/600] Roll Flutter from 61be9229b7f3 to c2dc92ca881d (3 revisions) (#5260) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index fbce84b0d935..449ed38ee12e 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -61be9229b7f35557fceed203b572dc96384ade42 +c2dc92ca881d3f71e8db872798362dcaa4eecf4b From d14ba3689576cd712150f719fb2ff75c7f294ff1 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 14 Apr 2022 04:44:06 -0400 Subject: [PATCH 398/600] Roll Flutter from c2dc92ca881d to 3aba137efd31 (1 revision) (#5261) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 449ed38ee12e..636ff4aecae0 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c2dc92ca881d3f71e8db872798362dcaa4eecf4b +3aba137efd3109c0a4735f814cac5ac864ceb592 From 84cec3dda5fd2376e057426104769a0f0045f4d3 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 14 Apr 2022 05:49:08 -0400 Subject: [PATCH 399/600] Roll Flutter from 3aba137efd31 to cb968c5f3291 (1 revision) (#5262) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 636ff4aecae0..7da40e375dd3 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -3aba137efd3109c0a4735f814cac5ac864ceb592 +cb968c5f3291b53e6f380b757babfca8b9ac2b86 From b76f3f51bdc685fd514daf927121413dbfb8d92f Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 14 Apr 2022 10:24:09 -0400 Subject: [PATCH 400/600] Roll Flutter from cb968c5f3291 to 05759325bad1 (1 revision) (#5263) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 7da40e375dd3..6817202325c6 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -cb968c5f3291b53e6f380b757babfca8b9ac2b86 +05759325bad1493757c02ad31ad7cc9e332e06ad From b2f5465c087730e32e657ac865c13de501033045 Mon Sep 17 00:00:00 2001 From: BeMacized Date: Thu, 14 Apr 2022 17:59:08 +0200 Subject: [PATCH 401/600] [local_auth] Refactor package to make use of new platform interface and native implementations (#4701) --- packages/local_auth/local_auth/AUTHORS | 1 + packages/local_auth/local_auth/CHANGELOG.md | 39 ++++- .../local_auth/example/lib/main.dart | 16 +- .../local_auth/lib/auth_strings.dart | 164 ------------------ .../local_auth/local_auth/lib/local_auth.dart | 146 +--------------- .../local_auth/lib/src/local_auth.dart | 77 ++++++++ .../lib/{ => src/types}/error_codes.dart | 2 +- packages/local_auth/local_auth/pubspec.yaml | 16 +- .../local_auth/test/local_auth_test.dart | 39 +---- 9 files changed, 142 insertions(+), 358 deletions(-) delete mode 100644 packages/local_auth/local_auth/lib/auth_strings.dart create mode 100644 packages/local_auth/local_auth/lib/src/local_auth.dart rename packages/local_auth/local_auth/lib/{ => src/types}/error_codes.dart (93%) diff --git a/packages/local_auth/local_auth/AUTHORS b/packages/local_auth/local_auth/AUTHORS index 493a0b4ef9c2..d5694690c247 100644 --- a/packages/local_auth/local_auth/AUTHORS +++ b/packages/local_auth/local_auth/AUTHORS @@ -64,3 +64,4 @@ Aleksandr Yurkovskiy Anton Borries Alex Li Rahul Raj <64.rahulraj@gmail.com> +Bodhi Mulders diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index 1ca9fe146c7b..deac871d935f 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -1,7 +1,42 @@ -## NEXT +## 2.0.0 +* Migrates plugin to federated architecture. * Adds OS version support information to README. -* Switches over to default method implementation in new platform interface. +* BREAKING CHANGE: Deprecated method `authenticateWithBiometrics` has been removed. + Use `authenticate` instead. +* BREAKING CHANGE: Enum `BiometricType` has been expanded with options for `strong` and `weak`, + and applications should be updated to handle these accordingly. +* BREAKING CHANGE: Parameters of `authenticate` have been changed. + + Example: + ```dart + // Old way of calling `authenticate`. + Future authenticate( + localizedReason: 'localized reason', + useErrorDialogs: true, + stickyAuth: false, + androidAuthStrings: const AndroidAuthMessages(), + iOSAuthStrings: const IOSAuthMessages(), + sensitiveTransaction: true, + biometricOnly: false, + ); + // New way of calling `authenticate`. + Future authenticate( + localizedReason: 'localized reason', + authMessages: const [ + IOSAuthMessages(), + AndroidAuthMessages() + ], + options: const AuthenticationOptions( + useErrorDialogs: true, + stickyAuth: false, + sensitiveTransaction: true, + biometricOnly: false, + ), + ); + ``` + + ## 1.1.11 diff --git a/packages/local_auth/local_auth/example/lib/main.dart b/packages/local_auth/local_auth/example/lib/main.dart index a9604b3411b7..92ad7cf4fb3f 100644 --- a/packages/local_auth/local_auth/example/lib/main.dart +++ b/packages/local_auth/local_auth/example/lib/main.dart @@ -79,9 +79,12 @@ class _MyAppState extends State { _authorized = 'Authenticating'; }); authenticated = await auth.authenticate( - localizedReason: 'Let OS determine authentication method', + localizedReason: 'Let OS determine authentication method', + options: const AuthenticationOptions( useErrorDialogs: true, - stickyAuth: true); + stickyAuth: true, + ), + ); setState(() { _isAuthenticating = false; }); @@ -109,11 +112,14 @@ class _MyAppState extends State { _authorized = 'Authenticating'; }); authenticated = await auth.authenticate( - localizedReason: - 'Scan your fingerprint (or face or whatever) to authenticate', + localizedReason: + 'Scan your fingerprint (or face or whatever) to authenticate', + options: const AuthenticationOptions( useErrorDialogs: true, stickyAuth: true, - biometricOnly: true); + biometricOnly: true, + ), + ); setState(() { _isAuthenticating = false; _authorized = 'Authenticating'; diff --git a/packages/local_auth/local_auth/lib/auth_strings.dart b/packages/local_auth/local_auth/lib/auth_strings.dart deleted file mode 100644 index 585742ac68c2..000000000000 --- a/packages/local_auth/local_auth/lib/auth_strings.dart +++ /dev/null @@ -1,164 +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. - -// This is a temporary ignore to allow us to land a new set of linter rules in a -// series of manageable patches instead of one gigantic PR. It disables some of -// the new lints that are already failing on this plugin, for this plugin. It -// should be deleted and the failing lints addressed as soon as possible. -// ignore_for_file: public_member_api_docs - -import 'package:intl/intl.dart'; -import 'package:local_auth_platform_interface/types/auth_messages.dart'; - -/// Android side authentication messages. -/// -/// Provides default values for all messages. -class AndroidAuthMessages extends AuthMessages { - const AndroidAuthMessages({ - this.biometricHint, - this.biometricNotRecognized, - this.biometricRequiredTitle, - this.biometricSuccess, - this.cancelButton, - this.deviceCredentialsRequiredTitle, - this.deviceCredentialsSetupDescription, - this.goToSettingsButton, - this.goToSettingsDescription, - this.signInTitle, - }); - - final String? biometricHint; - final String? biometricNotRecognized; - final String? biometricRequiredTitle; - final String? biometricSuccess; - final String? cancelButton; - final String? deviceCredentialsRequiredTitle; - final String? deviceCredentialsSetupDescription; - final String? goToSettingsButton; - final String? goToSettingsDescription; - final String? signInTitle; - - @override - Map get args { - return { - 'biometricHint': biometricHint ?? androidBiometricHint, - 'biometricNotRecognized': - biometricNotRecognized ?? androidBiometricNotRecognized, - 'biometricSuccess': biometricSuccess ?? androidBiometricSuccess, - 'biometricRequired': - biometricRequiredTitle ?? androidBiometricRequiredTitle, - 'cancelButton': cancelButton ?? androidCancelButton, - 'deviceCredentialsRequired': deviceCredentialsRequiredTitle ?? - androidDeviceCredentialsRequiredTitle, - 'deviceCredentialsSetupDescription': deviceCredentialsSetupDescription ?? - androidDeviceCredentialsSetupDescription, - 'goToSetting': goToSettingsButton ?? goToSettings, - 'goToSettingDescription': - goToSettingsDescription ?? androidGoToSettingsDescription, - 'signInTitle': signInTitle ?? androidSignInTitle, - }; - } -} - -/// iOS side authentication messages. -/// -/// Provides default values for all messages. -class IOSAuthMessages extends AuthMessages { - const IOSAuthMessages({ - this.lockOut, - this.goToSettingsButton, - this.goToSettingsDescription, - this.cancelButton, - this.localizedFallbackTitle, - }); - - final String? lockOut; - final String? goToSettingsButton; - final String? goToSettingsDescription; - final String? cancelButton; - final String? localizedFallbackTitle; - - @override - Map get args { - return { - 'lockOut': lockOut ?? iOSLockOut, - 'goToSetting': goToSettingsButton ?? goToSettings, - 'goToSettingDescriptionIOS': - goToSettingsDescription ?? iOSGoToSettingsDescription, - 'okButton': cancelButton ?? iOSOkButton, - if (localizedFallbackTitle != null) - 'localizedFallbackTitle': localizedFallbackTitle!, - }; - } -} - -// Strings for local_authentication plugin. Currently supports English. -// Intl.message must be string literals. -String get androidBiometricHint => Intl.message('Verify identity', - desc: - 'Hint message advising the user how to authenticate with biometrics. It is ' - 'used on Android side. Maximum 60 characters.'); - -String get androidBiometricNotRecognized => - Intl.message('Not recognized. Try again.', - desc: 'Message to let the user know that authentication was failed. It ' - 'is used on Android side. Maximum 60 characters.'); - -String get androidBiometricSuccess => Intl.message('Success', - desc: 'Message to let the user know that authentication was successful. It ' - 'is used on Android side. Maximum 60 characters.'); - -String get androidCancelButton => Intl.message('Cancel', - desc: 'Message showed on a button that the user can click to leave the ' - 'current dialog. It is used on Android side. Maximum 30 characters.'); - -String get androidSignInTitle => Intl.message('Authentication required', - desc: 'Message showed as a title in a dialog which indicates the user ' - 'that they need to scan biometric to continue. It is used on ' - 'Android side. Maximum 60 characters.'); - -String get androidBiometricRequiredTitle => Intl.message('Biometric required', - desc: 'Message showed as a title in a dialog which indicates the user ' - 'has not set up biometric authentication on their device. It is used on Android' - ' side. Maximum 60 characters.'); - -String get androidDeviceCredentialsRequiredTitle => Intl.message( - 'Device credentials required', - desc: 'Message showed as a title in a dialog which indicates the user ' - 'has not set up credentials authentication on their device. It is used on Android' - ' side. Maximum 60 characters.'); - -String get androidDeviceCredentialsSetupDescription => Intl.message( - 'Device credentials required', - desc: 'Message advising the user to go to the settings and configure ' - 'device credentials on their device. It shows in a dialog on Android side.'); - -String get goToSettings => Intl.message('Go to settings', - desc: 'Message showed on a button that the user can click to go to ' - 'settings pages from the current dialog. It is used on both Android ' - 'and iOS side. Maximum 30 characters.'); - -String get androidGoToSettingsDescription => Intl.message( - 'Biometric authentication is not set up on your device. Go to ' - '\'Settings > Security\' to add biometric authentication.', - desc: 'Message advising the user to go to the settings and configure ' - 'biometric on their device. It shows in a dialog on Android side.'); - -String get iOSLockOut => Intl.message( - 'Biometric authentication is disabled. Please lock and unlock your screen to ' - 'enable it.', - desc: - 'Message advising the user to re-enable biometrics on their device. It ' - 'shows in a dialog on iOS side.'); - -String get iOSGoToSettingsDescription => Intl.message( - 'Biometric authentication is not set up on your device. Please either enable ' - 'Touch ID or Face ID on your phone.', - desc: - 'Message advising the user to go to the settings and configure Biometrics ' - 'for their device. It shows in a dialog on iOS side.'); - -String get iOSOkButton => Intl.message('OK', - desc: 'Message showed on a button that the user can click to leave the ' - 'current dialog. It is used on iOS side. Maximum 30 characters.'); diff --git a/packages/local_auth/local_auth/lib/local_auth.dart b/packages/local_auth/local_auth/lib/local_auth.dart index 32818b31783d..7c42fedc7755 100644 --- a/packages/local_auth/local_auth/lib/local_auth.dart +++ b/packages/local_auth/local_auth/lib/local_auth.dart @@ -2,144 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// This is a temporary ignore to allow us to land a new set of linter rules in a -// series of manageable patches instead of one gigantic PR. It disables some of -// the new lints that are already failing on this plugin, for this plugin. It -// should be deleted and the failing lints addressed as soon as possible. -// ignore_for_file: public_member_api_docs - -import 'dart:async'; - -import 'package:flutter/foundation.dart' show visibleForTesting; -import 'package:flutter/services.dart'; -import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; -import 'package:platform/platform.dart'; -import 'auth_strings.dart'; - -export 'package:local_auth_platform_interface/types/biometric_type.dart'; - -Platform _platform = const LocalPlatform(); - -@visibleForTesting -void setMockPathProviderPlatform(Platform platform) { - _platform = platform; -} - -/// A Flutter plugin for authenticating the user identity locally. -class LocalAuthentication { - /// The `authenticateWithBiometrics` method has been deprecated. - /// Use `authenticate` with `biometricOnly: true` instead. - @Deprecated('Use `authenticate` with `biometricOnly: true` instead') - Future authenticateWithBiometrics({ - required String localizedReason, - bool useErrorDialogs = true, - bool stickyAuth = false, - AndroidAuthMessages androidAuthStrings = const AndroidAuthMessages(), - IOSAuthMessages iOSAuthStrings = const IOSAuthMessages(), - bool sensitiveTransaction = true, - }) => - LocalAuthPlatform.instance.authenticate( - localizedReason: localizedReason, - authMessages: [iOSAuthStrings, androidAuthStrings], - options: AuthenticationOptions( - useErrorDialogs: useErrorDialogs, - stickyAuth: stickyAuth, - sensitiveTransaction: sensitiveTransaction, - biometricOnly: true, - ), - ); - - /// Authenticates the user with biometrics available on the device while also - /// allowing the user to use device authentication - pin, pattern, passcode. - /// - /// Returns true, if the user successfully authenticated, false otherwise. - /// - /// [localizedReason] is the message to show to user while prompting them - /// for authentication. This is typically along the lines of: 'Please scan - /// your finger to access MyApp.'. This must not be empty. - /// - /// [useErrorDialogs] = true means the system will attempt to handle user - /// fixable issues encountered while authenticating. For instance, if - /// fingerprint reader exists on the phone but there's no fingerprint - /// registered, the plugin will attempt to take the user to settings to add - /// one. Anything that is not user fixable, such as no biometric sensor on - /// device, will be returned as a [PlatformException]. - /// - /// [stickyAuth] is used when the application goes into background for any - /// reason while the authentication is in progress. Due to security reasons, - /// the authentication has to be stopped at that time. If stickyAuth is set - /// to true, authentication resumes when the app is resumed. If it is set to - /// false (default), then as soon as app is paused a failure message is sent - /// back to Dart and it is up to the client app to restart authentication or - /// do something else. - /// - /// Construct [AndroidAuthStrings] and [IOSAuthStrings] if you want to - /// customize messages in the dialogs. - /// - /// Setting [sensitiveTransaction] to true enables platform specific - /// precautions. For instance, on face unlock, Android opens a confirmation - /// dialog after the face is recognized to make sure the user meant to unlock - /// their phone. - /// - /// Setting [biometricOnly] to true prevents authenticates from using non-biometric - /// local authentication such as pin, passcode, and passcode. - /// - /// Throws an [PlatformException] if there were technical problems with local - /// authentication (e.g. lack of relevant hardware). This might throw - /// [PlatformException] with error code [otherOperatingSystem] on the iOS - /// simulator. - Future authenticate({ - required String localizedReason, - bool useErrorDialogs = true, - bool stickyAuth = false, - AndroidAuthMessages androidAuthStrings = const AndroidAuthMessages(), - IOSAuthMessages iOSAuthStrings = const IOSAuthMessages(), - bool sensitiveTransaction = true, - bool biometricOnly = false, - }) { - return LocalAuthPlatform.instance.authenticate( - localizedReason: localizedReason, - authMessages: [iOSAuthStrings, androidAuthStrings], - options: AuthenticationOptions( - useErrorDialogs: useErrorDialogs, - stickyAuth: stickyAuth, - sensitiveTransaction: sensitiveTransaction, - biometricOnly: biometricOnly, - ), - ); - } - - /// Returns true if auth was cancelled successfully. - /// This api only works for Android. - /// Returns false if there was some error or no auth in progress. - /// - /// Returns [Future] bool true or false: - Future stopAuthentication() async { - if (_platform.isAndroid) { - return LocalAuthPlatform.instance.stopAuthentication(); - } - return true; - } - - /// Returns true if device is capable of checking biometrics - /// - /// Returns a [Future] bool true or false: - Future get canCheckBiometrics => - LocalAuthPlatform.instance.deviceSupportsBiometrics(); - - /// Returns true if device is capable of checking biometrics or is able to - /// fail over to device credentials. - /// - /// Returns a [Future] bool true or false: - Future isDeviceSupported() async => - LocalAuthPlatform.instance.isDeviceSupported(); - - /// Returns a list of enrolled biometrics - /// - /// Returns a [Future] List with the following possibilities: - /// - BiometricType.face - /// - BiometricType.fingerprint - /// - BiometricType.iris (not yet implemented) - Future> getAvailableBiometrics() => - LocalAuthPlatform.instance.getEnrolledBiometrics(); -} +export 'package:local_auth/src/local_auth.dart' show LocalAuthentication; +export 'package:local_auth_platform_interface/types/auth_options.dart' + show AuthenticationOptions; +export 'package:local_auth_platform_interface/types/biometric_type.dart' + show BiometricType; diff --git a/packages/local_auth/local_auth/lib/src/local_auth.dart b/packages/local_auth/local_auth/lib/src/local_auth.dart new file mode 100644 index 000000000000..508e2b14e129 --- /dev/null +++ b/packages/local_auth/local_auth/lib/src/local_auth.dart @@ -0,0 +1,77 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This is a temporary ignore to allow us to land a new set of linter rules in a +// series of manageable patches instead of one gigantic PR. It disables some of +// the new lints that are already failing on this plugin, for this plugin. It +// should be deleted and the failing lints addressed as soon as possible. +// ignore_for_file: public_member_api_docs + +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:local_auth/src/types/error_codes.dart'; +import 'package:local_auth_android/local_auth_android.dart'; +import 'package:local_auth_ios/local_auth_ios.dart'; +import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; +import 'package:local_auth_platform_interface/types/auth_messages.dart'; +import 'package:local_auth_platform_interface/types/biometric_type.dart'; + +/// A Flutter plugin for authenticating the user identity locally. +class LocalAuthentication { + /// Authenticates the user with biometrics available on the device while also + /// allowing the user to use device authentication - pin, pattern, passcode. + /// + /// Returns true if the user successfully authenticated, false otherwise. + /// + /// [localizedReason] is the message to show to user while prompting them + /// for authentication. This is typically along the lines of: 'Authenticate + /// to access MyApp.'. This must not be empty. + /// + /// Provide [authMessages] if you want to + /// customize messages in the dialogs. + /// + /// Provide [options] for configuring further authentication related options. + /// + /// Throws a [PlatformException] if there were technical problems with local + /// authentication (e.g. lack of relevant hardware). This might throw + /// [PlatformException] with error code [otherOperatingSystem] on the iOS + /// simulator. + Future authenticate( + {required String localizedReason, + Iterable authMessages = const [ + IOSAuthMessages(), + AndroidAuthMessages() + ], + AuthenticationOptions options = const AuthenticationOptions()}) { + return LocalAuthPlatform.instance.authenticate( + localizedReason: localizedReason, + authMessages: authMessages, + options: options, + ); + } + + /// Cancels any in-progress authentication, returning true if auth was + /// cancelled successfully. + /// + /// This API is not supported by all platforms. + /// Returns false if there was some error, no authentication in progress, + /// or the current platform lacks support. + Future stopAuthentication() async { + return LocalAuthPlatform.instance.stopAuthentication(); + } + + /// Returns true if device is capable of checking biometrics. + Future get canCheckBiometrics => + LocalAuthPlatform.instance.deviceSupportsBiometrics(); + + /// Returns true if device is capable of checking biometrics or is able to + /// fail over to device credentials. + Future isDeviceSupported() async => + LocalAuthPlatform.instance.isDeviceSupported(); + + /// Returns a list of enrolled biometrics. + Future> getAvailableBiometrics() => + LocalAuthPlatform.instance.getEnrolledBiometrics(); +} diff --git a/packages/local_auth/local_auth/lib/error_codes.dart b/packages/local_auth/local_auth/lib/src/types/error_codes.dart similarity index 93% rename from packages/local_auth/local_auth/lib/error_codes.dart rename to packages/local_auth/local_auth/lib/src/types/error_codes.dart index bcf15b7b2154..3426099bacbd 100644 --- a/packages/local_auth/local_auth/lib/error_codes.dart +++ b/packages/local_auth/local_auth/lib/src/types/error_codes.dart @@ -15,7 +15,7 @@ const String notEnrolled = 'NotEnrolled'; /// Indicates the device does not have a Touch ID/fingerprint scanner. const String notAvailable = 'NotAvailable'; -/// Indicates the device operating system is not iOS or Android. +/// Indicates the device operating system is unsupported. const String otherOperatingSystem = 'OtherOperatingSystem'; /// Indicates the API lock out due to too many attempts. diff --git a/packages/local_auth/local_auth/pubspec.yaml b/packages/local_auth/local_auth/pubspec.yaml index baf5d46b7b00..fa055fab17f8 100644 --- a/packages/local_auth/local_auth/pubspec.yaml +++ b/packages/local_auth/local_auth/pubspec.yaml @@ -3,11 +3,7 @@ description: Flutter plugin for Android and iOS devices to allow local authentication via fingerprint, touch ID, face ID, passcode, pin, or pattern. repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.1.11 - -# Temporarily disable publishing to allow moving Android and iOS -# implementations. -publish_to: none +version: 2.0.0 environment: sdk: ">=2.14.0 <3.0.0" @@ -24,15 +20,10 @@ flutter: dependencies: flutter: sdk: flutter - flutter_plugin_android_lifecycle: ^2.0.1 intl: ^0.17.0 -# Temporary path dependencies to allow moving Android and iOS implementations. - local_auth_android: - path: ../local_auth_android - local_auth_ios: - path: ../local_auth_ios + local_auth_android: ^1.0.0 + local_auth_ios: ^1.0.1 local_auth_platform_interface: ^1.0.1 - platform: ^3.0.0 dev_dependencies: flutter_driver: @@ -42,3 +33,4 @@ dev_dependencies: integration_test: sdk: flutter mockito: ^5.1.0 + plugin_platform_interface: ^2.1.2 diff --git a/packages/local_auth/local_auth/test/local_auth_test.dart b/packages/local_auth/local_auth/test/local_auth_test.dart index b92297d90231..d3f92dfe95db 100644 --- a/packages/local_auth/local_auth/test/local_auth_test.dart +++ b/packages/local_auth/local_auth/test/local_auth_test.dart @@ -4,13 +4,14 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:local_auth/auth_strings.dart'; import 'package:local_auth/local_auth.dart'; +import 'package:local_auth/src/local_auth.dart'; +import 'package:local_auth_android/local_auth_android.dart'; +import 'package:local_auth_ios/local_auth_ios.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; import 'package:local_auth_platform_interface/types/auth_messages.dart'; import 'package:local_auth_platform_interface/types/auth_options.dart'; import 'package:mockito/mockito.dart'; -import 'package:platform/platform.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; void main() { @@ -24,24 +25,6 @@ void main() { LocalAuthPlatform.instance = mockLocalAuthPlatform; }); - test('authenticateWithBiometrics calls platform implementation', () { - when(mockLocalAuthPlatform.authenticate( - localizedReason: anyNamed('localizedReason'), - authMessages: anyNamed('authMessages'), - options: anyNamed('options'), - )).thenAnswer((_) async => true); - localAuthentication.authenticateWithBiometrics( - localizedReason: 'Test Reason'); - verify(mockLocalAuthPlatform.authenticate( - localizedReason: 'Test Reason', - authMessages: [ - const IOSAuthMessages(), - const AndroidAuthMessages(), - ], - options: const AuthenticationOptions(biometricOnly: true), - )).called(1); - }); - test('authenticate calls platform implementation', () { when(mockLocalAuthPlatform.authenticate( localizedReason: anyNamed('localizedReason'), @@ -73,20 +56,13 @@ void main() { verify(mockLocalAuthPlatform.getEnrolledBiometrics()).called(1); }); - test('stopAuthentication calls platform implementation on Android', () { + test('stopAuthentication calls platform implementation', () { when(mockLocalAuthPlatform.stopAuthentication()) .thenAnswer((_) async => true); - setMockPathProviderPlatform(FakePlatform(operatingSystem: 'android')); localAuthentication.stopAuthentication(); verify(mockLocalAuthPlatform.stopAuthentication()).called(1); }); - test('stopAuthentication does not call platform implementation on iOS', () { - setMockPathProviderPlatform(FakePlatform(operatingSystem: 'ios')); - localAuthentication.stopAuthentication(); - verifyNever(mockLocalAuthPlatform.stopAuthentication()); - }); - test('canCheckBiometrics returns correct result', () async { when(mockLocalAuthPlatform.deviceSupportsBiometrics()) .thenAnswer((_) async => false); @@ -110,11 +86,8 @@ class MockLocalAuthPlatform extends Mock @override Future authenticate({ - String? localizedReason, - Iterable? authMessages = const [ - IOSAuthMessages(), - AndroidAuthMessages() - ], + required String? localizedReason, + required Iterable? authMessages, AuthenticationOptions? options = const AuthenticationOptions(), }) => super.noSuchMethod( From ce66b3b386de03cc1bdd226ba6010b60e0257a70 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 14 Apr 2022 12:54:10 -0400 Subject: [PATCH 402/600] Roll Flutter from 05759325bad1 to ff9d6e5e339f (1 revision) (#5265) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 6817202325c6..be9c1d0f99a9 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -05759325bad1493757c02ad31ad7cc9e332e06ad +ff9d6e5e339fa3f8af64199f95bbfe5ecb2ddd4e From 08e4e02ef2a104e591fcaa92f8ff1f57775f75af Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 14 Apr 2022 13:59:12 -0400 Subject: [PATCH 403/600] Roll Flutter from ff9d6e5e339f to e2d12060a2bc (2 revisions) (#5266) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index be9c1d0f99a9..b71dfa7411fb 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ff9d6e5e339fa3f8af64199f95bbfe5ecb2ddd4e +e2d12060a2bc574a3e1209e206d5cf031cdbc335 From 98b79b6f016bca66ad7022bb2aba76983b15b099 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 14 Apr 2022 15:04:10 -0400 Subject: [PATCH 404/600] Roll Flutter from e2d12060a2bc to 08e467dde7a1 (1 revision) (#5268) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index b71dfa7411fb..c9a269c2cc44 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -e2d12060a2bc574a3e1209e206d5cf031cdbc335 +08e467dde7a1c3906ce6f4231d9f1d1b36b64022 From 2614d857174199312d6cf756f07e7df737e607b6 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 14 Apr 2022 17:14:09 -0400 Subject: [PATCH 405/600] Roll Flutter from 08e467dde7a1 to 2b8333240d38 (5 revisions) (#5270) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index c9a269c2cc44..103b05611de2 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -08e467dde7a1c3906ce6f4231d9f1d1b36b64022 +2b8333240d38cf72b8e13580e24d01f5a188e26c From e3a184f30f1758642d7679369b2942a4ea44bd54 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 14 Apr 2022 18:25:46 -0700 Subject: [PATCH 406/600] [webview_flutter_wkwebview] Implement `WKNavigationDelegate.didFinishNavigation` as a proof of concept for callback methods (#5199) --- .../common/function_flutter_api_impls.dart | 25 +++++ .../lib/src/common/web_kit.pigeon.dart | 101 ++++++++++++++++++ .../lib/src/foundation/foundation.dart | 6 +- .../src/foundation/foundation_api_impls.dart | 50 +++++++++ .../lib/src/web_kit/web_kit.dart | 19 ++-- .../lib/src/web_kit/web_kit_api_impls.dart | 87 +++++++++++++++ .../pigeons/web_kit.dart | 20 ++++ .../src/common/function_flutter_api_test.dart | 33 ++++++ .../test/src/common/test_web_kit.pigeon.dart | 24 +++++ .../test/src/web_kit/web_kit_test.dart | 44 ++++++++ .../test/src/web_kit/web_kit_test.mocks.dart | 6 ++ 11 files changed, 407 insertions(+), 8 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/function_flutter_api_impls.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/src/common/function_flutter_api_test.dart diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/function_flutter_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/function_flutter_api_impls.dart new file mode 100644 index 000000000000..c6eb711513d2 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/function_flutter_api_impls.dart @@ -0,0 +1,25 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'instance_manager.dart'; +import 'web_kit.pigeon.dart'; + +/// Flutter api to dispose functions. +class FunctionFlutterApiImpl extends FunctionFlutterApi { + /// Constructs a [FunctionFlutterApiImpl]. + FunctionFlutterApiImpl({InstanceManager? instanceManager}) { + this.instanceManager = instanceManager ?? InstanceManager.instance; + } + + /// Maintains instances stored to communicate with native language objects. + late final InstanceManager instanceManager; + + @override + void dispose(int instanceId) { + final Function? function = instanceManager.getInstance(instanceId); + if (function != null) { + instanceManager.removeInstance(function); + } + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart index 5bf79a675be0..39f5663c1838 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart @@ -1022,6 +1022,75 @@ class WKNavigationDelegateHostApi { return; } } + + Future setDidFinishNavigation( + int arg_instanceId, int? arg_functionInstanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKNavigationDelegateHostApi.setDidFinishNavigation', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_functionInstanceId]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} + +class _WKNavigationDelegateFlutterApiCodec extends StandardMessageCodec { + const _WKNavigationDelegateFlutterApiCodec(); +} + +abstract class WKNavigationDelegateFlutterApi { + static const MessageCodec codec = + _WKNavigationDelegateFlutterApiCodec(); + + void didFinishNavigation( + int functionInstanceId, int webViewInstanceId, String? url); + static void setup(WKNavigationDelegateFlutterApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation was null.'); + final List args = (message as List?)!; + final int? arg_functionInstanceId = (args[0] as int?); + assert(arg_functionInstanceId != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation was null, expected non-null int.'); + final int? arg_webViewInstanceId = (args[1] as int?); + assert(arg_webViewInstanceId != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation was null, expected non-null int.'); + final String? arg_url = (args[2] as String?); + assert(arg_url != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation was null, expected non-null String.'); + api.didFinishNavigation( + arg_functionInstanceId!, arg_webViewInstanceId!, arg_url!); + return; + }); + } + } + } } class _NSObjectHostApiCodec extends StandardMessageCodec { @@ -1142,6 +1211,38 @@ class NSObjectHostApi { } } +class _FunctionFlutterApiCodec extends StandardMessageCodec { + const _FunctionFlutterApiCodec(); +} + +abstract class FunctionFlutterApi { + static const MessageCodec codec = _FunctionFlutterApiCodec(); + + void dispose(int instanceId); + static void setup(FunctionFlutterApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.FunctionFlutterApi.dispose', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.FunctionFlutterApi.dispose was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.FunctionFlutterApi.dispose was null, expected non-null int.'); + api.dispose(arg_instanceId!); + return; + }); + } + } + } +} + class _WKWebViewHostApiCodec extends StandardMessageCodec { const _WKWebViewHostApiCodec(); @override diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart index 1fef81d9dbaa..ec7bb5377de0 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart @@ -236,7 +236,11 @@ class NSObject { : _api = NSObjectHostApiImpl( binaryMessenger: binaryMessenger, instanceManager: instanceManager, - ); + ) { + // Ensures FlutterApis for the Foundation library and FunctionFlutterApi are + // set up. + FoundationFlutterApis.instance.ensureSetUp(); + } final NSObjectHostApiImpl _api; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart index ec0d60cc748e..b007d1a8312c 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart @@ -2,8 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; +import '../common/function_flutter_api_impls.dart'; import '../common/instance_manager.dart'; import '../common/web_kit.pigeon.dart'; import 'foundation.dart'; @@ -35,6 +37,54 @@ Iterable }); } +/// Handles initialization of Flutter APIs for the Foundation library. +class FoundationFlutterApis { + /// Constructs a [FoundationFlutterApis]. + @visibleForTesting + FoundationFlutterApis({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _binaryMessenger = binaryMessenger { + functionFlutterApi = + FunctionFlutterApiImpl(instanceManager: instanceManager); + } + + static FoundationFlutterApis _instance = FoundationFlutterApis(); + + /// Sets the global instance containing the Flutter Apis for the Foundation library. + @visibleForTesting + static set instance(FoundationFlutterApis instance) { + _instance = instance; + } + + /// Global instance containing the Flutter Apis for the Foundation library. + static FoundationFlutterApis get instance { + return _instance; + } + + final BinaryMessenger? _binaryMessenger; + bool _hasBeenSetUp = false; + + /// Flutter Api for disposing functions. + /// + /// This FlutterApi is placed here because [FoundationFlutterApis.ensureSetUp] + /// is called inside [NSObject] and [NSObject] is the parent class of all + /// objects. + @visibleForTesting + late final FunctionFlutterApiImpl functionFlutterApi; + + /// Ensures all the Flutter APIs have been set up to receive calls from native code. + void ensureSetUp() { + if (!_hasBeenSetUp) { + FunctionFlutterApi.setup( + functionFlutterApi, + binaryMessenger: _binaryMessenger, + ); + _hasBeenSetUp = true; + } + } +} + /// Host api implementation for [NSObject]. class NSObjectHostApiImpl extends NSObjectHostApi { /// Constructs an [NSObjectHostApiImpl]. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart index 416e2626fc4a..64a45ada265f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart @@ -571,15 +571,20 @@ class WKUIDelegate { /// coordinate changes in your web view’s main frame. /// /// Wraps [WKNavigationDelegate](https://developer.apple.com/documentation/webkit/wknavigationdelegate?language=objc). -class WKNavigationDelegate { +class WKNavigationDelegate extends NSObject { /// Constructs a [WKNavigationDelegate]. WKNavigationDelegate({ BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, - }) : _navigationDelegateApi = WKNavigationDelegateHostApiImpl( + }) : _navigationDelegateApi = WKNavigationDelegateHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ), + super( binaryMessenger: binaryMessenger, instanceManager: instanceManager, ) { + WebKitFlutterApis.instance.ensureSetUp(); _navigationDelegateApi.createForInstances(this); } @@ -587,10 +592,7 @@ class WKNavigationDelegate { /// Called when navigation from the main frame has started. Future setDidStartProvisionalNavigation( - void Function( - WKWebView webView, - String? url, - )? + void Function(WKWebView webView, String? url)? didStartProvisionalNavigation, ) { throw UnimplementedError(); @@ -600,7 +602,10 @@ class WKNavigationDelegate { Future setDidFinishNavigation( void Function(WKWebView webView, String? url)? didFinishNavigation, ) { - throw UnimplementedError(); + return _navigationDelegateApi.setDidFinishNavigationFromInstance( + this, + didFinishNavigation, + ); } /// Called when permission is needed to navigate to new content. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart index 1cda9d4b94c2..d33407905375 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import '../common/instance_manager.dart'; @@ -106,6 +107,51 @@ extension _NSUrlRequestConverter on NSUrlRequest { } } +/// Handles initialization of Flutter APIs for WebKit. +class WebKitFlutterApis { + /// Constructs a [WebKitFlutterApis]. + @visibleForTesting + WebKitFlutterApis({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _binaryMessenger = binaryMessenger { + navigationDelegateFlutterApi = WKNavigationDelegateFlutterApiImpl( + instanceManager: instanceManager, + ); + } + + static WebKitFlutterApis _instance = WebKitFlutterApis(); + + /// Sets the global instance containing the Flutter Apis for the WebKit library. + @visibleForTesting + static set instance(WebKitFlutterApis instance) { + _instance = instance; + } + + /// Global instance containing the Flutter Apis for the WebKit library. + static WebKitFlutterApis get instance { + return _instance; + } + + final BinaryMessenger? _binaryMessenger; + bool _hasBeenSetUp = false; + + /// Flutter Api for [WKNavigationDelegate]. + @visibleForTesting + late final WKNavigationDelegateFlutterApiImpl navigationDelegateFlutterApi; + + /// Ensures all the Flutter APIs have been set up to receive calls from native code. + void ensureSetUp() { + if (!_hasBeenSetUp) { + WKNavigationDelegateFlutterApi.setup( + navigationDelegateFlutterApi, + binaryMessenger: _binaryMessenger, + ); + _hasBeenSetUp = true; + } + } +} + /// Host api implementation for [WKWebSiteDataStore]. class WKWebsiteDataStoreHostApiImpl extends WKWebsiteDataStoreHostApi { /// Constructs a [WebsiteDataStoreHostApiImpl]. @@ -381,6 +427,47 @@ class WKNavigationDelegateHostApiImpl extends WKNavigationDelegateHostApi { await create(instanceId); } } + + /// Calls [setDidFinishNavigation] with the ids of the provided object instances. + Future setDidFinishNavigationFromInstance( + WKNavigationDelegate instance, + void Function(WKWebView, String?)? didFinishNavigation, + ) { + int? functionInstanceId; + if (didFinishNavigation != null) { + functionInstanceId = instanceManager.getInstanceId(didFinishNavigation) ?? + instanceManager.tryAddInstance(didFinishNavigation)!; + } + return setDidFinishNavigation( + instanceManager.getInstanceId(instance)!, + functionInstanceId, + ); + } +} + +/// Flutter api implementation for [WKNavigationDelegate]. +class WKNavigationDelegateFlutterApiImpl + extends WKNavigationDelegateFlutterApi { + /// Constructs a [WKNavigationDelegateFlutterApiImpl]. + WKNavigationDelegateFlutterApiImpl({InstanceManager? instanceManager}) { + this.instanceManager = instanceManager ?? InstanceManager.instance; + } + + /// Maintains instances stored to communicate with native language objects. + late final InstanceManager instanceManager; + + @override + void didFinishNavigation( + int functionInstanceId, + int webViewInstanceId, + String? url, + ) { + final void Function( + WKWebView webView, + String? url, + ) function = instanceManager.getInstance(functionInstanceId)!; + function(instanceManager.getInstance(webViewInstanceId)!, url); + } } /// Host api implementation for [WKWebView]. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart index 370149e638ff..b0a239fcd146 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart @@ -281,6 +281,20 @@ abstract class WKScriptMessageHandlerHostApi { @HostApi(dartHostTestHandler: 'TestWKNavigationDelegateHostApi') abstract class WKNavigationDelegateHostApi { void create(int instanceId); + + void setDidFinishNavigation(int instanceId, int? functionInstanceId); +} + +/// Mirror of WKNavigationDelegate. +/// +/// See https://developer.apple.com/documentation/webkit/wknavigationdelegate?language=objc. +@FlutterApi() +abstract class WKNavigationDelegateFlutterApi { + void didFinishNavigation( + int functionInstanceId, + int webViewInstanceId, + String? url, + ); } /// Mirror of NSObject. @@ -300,6 +314,12 @@ abstract class NSObjectHostApi { void removeObserver(int instanceId, int observerInstanceId, String keyPath); } +/// Disposes references to functions. +@FlutterApi() +abstract class FunctionFlutterApi { + void dispose(int instanceId); +} + /// Mirror of WKWebView. /// /// See https://developer.apple.com/documentation/webkit/wkwebview?language=objc. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/function_flutter_api_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/function_flutter_api_test.dart new file mode 100644 index 000000000000..63e59386ceaf --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/function_flutter_api_test.dart @@ -0,0 +1,33 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:webview_flutter_wkwebview/src/common/instance_manager.dart'; +import 'package:webview_flutter_wkwebview/src/common/web_kit.pigeon.dart'; +import 'package:webview_flutter_wkwebview/src/foundation/foundation_api_impls.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('$FunctionFlutterApi', () { + late InstanceManager instanceManager; + + setUp(() { + instanceManager = InstanceManager(); + }); + + test('dispose', () { + final Function function = () {}; + final int functionInstanceId = instanceManager.tryAddInstance(function)!; + + FoundationFlutterApis.instance = FoundationFlutterApis( + instanceManager: instanceManager, + )..ensureSetUp(); + + FoundationFlutterApis.instance.functionFlutterApi + .dispose(functionInstanceId); + expect(instanceManager.getInstanceId(function), isNull); + }); + }); +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart index 0e369a351c75..06a7b7a0a4c0 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart @@ -707,6 +707,7 @@ abstract class TestWKNavigationDelegateHostApi { _TestWKNavigationDelegateHostApiCodec(); void create(int instanceId); + void setDidFinishNavigation(int instanceId, int? functionInstanceId); static void setup(TestWKNavigationDelegateHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -728,6 +729,29 @@ abstract class TestWKNavigationDelegateHostApi { }); } } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKNavigationDelegateHostApi.setDidFinishNavigation', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateHostApi.setDidFinishNavigation was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateHostApi.setDidFinishNavigation was null, expected non-null int.'); + final int? arg_functionInstanceId = (args[1] as int?); + assert(arg_functionInstanceId != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateHostApi.setDidFinishNavigation was null, expected non-null int.'); + api.setDidFinishNavigation(arg_instanceId!, arg_functionInstanceId!); + return {}; + }); + } + } } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart index aa77157efcf7..00a3a31a6957 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:async'; + import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; @@ -9,6 +11,7 @@ import 'package:webview_flutter_wkwebview/src/common/instance_manager.dart'; import 'package:webview_flutter_wkwebview/src/common/web_kit.pigeon.dart'; import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart'; +import 'package:webview_flutter_wkwebview/src/web_kit/web_kit_api_impls.dart'; import '../common/test_web_kit.pigeon.dart'; import 'web_kit_test.mocks.dart'; @@ -28,9 +31,12 @@ void main() { group('WebKit', () { late InstanceManager instanceManager; + late WebKitFlutterApis flutterApis; setUp(() { instanceManager = InstanceManager(); + flutterApis = WebKitFlutterApis(instanceManager: instanceManager); + WebKitFlutterApis.instance = flutterApis; }); group('$WKWebsiteDataStore', () { @@ -329,12 +335,23 @@ void main() { group('$WKNavigationDelegate', () { late MockTestWKNavigationDelegateHostApi mockPlatformHostApi; + late WKWebView webView; + late WKNavigationDelegate navigationDelegate; setUp(() async { mockPlatformHostApi = MockTestWKNavigationDelegateHostApi(); TestWKNavigationDelegateHostApi.setup(mockPlatformHostApi); + TestWKWebViewConfigurationHostApi.setup( + MockTestWKWebViewConfigurationHostApi(), + ); + TestWKWebViewHostApi.setup(MockTestWKWebViewHostApi()); + webView = WKWebView( + WKWebViewConfiguration(instanceManager: instanceManager), + instanceManager: instanceManager, + ); + navigationDelegate = WKNavigationDelegate( instanceManager: instanceManager, ); @@ -342,6 +359,8 @@ void main() { tearDown(() { TestWKNavigationDelegateHostApi.setup(null); + TestWKWebViewConfigurationHostApi.setup(null); + TestWKWebViewHostApi.setup(null); }); test('create', () async { @@ -349,6 +368,31 @@ void main() { instanceManager.getInstanceId(navigationDelegate), )); }); + + test('setDidFinishNavigation', () async { + final Completer> argsCompleter = + Completer>(); + + navigationDelegate.setDidFinishNavigation( + (WKWebView webView, String? url) { + argsCompleter.complete([webView, url]); + }, + ); + + final int functionInstanceId = + verify(mockPlatformHostApi.setDidFinishNavigation( + instanceManager.getInstanceId(navigationDelegate), + captureAny, + )).captured.single as int; + + flutterApis.navigationDelegateFlutterApi.didFinishNavigation( + functionInstanceId, + instanceManager.getInstanceId(webView)!, + 'url', + ); + + expect(argsCompleter.future, completion([webView, 'url'])); + }); }); group('$WKWebView', () { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart index 9629e2e62636..3758c9e573a4 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart @@ -33,6 +33,12 @@ class MockTestWKNavigationDelegateHostApi extends _i1.Mock void create(int? instanceId) => super.noSuchMethod(Invocation.method(#create, [instanceId]), returnValueForMissingStub: null); + @override + void setDidFinishNavigation(int? instanceId, int? functionInstanceId) => + super.noSuchMethod( + Invocation.method( + #setDidFinishNavigation, [instanceId, functionInstanceId]), + returnValueForMissingStub: null); } /// A class which mocks [TestWKPreferencesHostApi]. From d07eaffb0d76ec128fa3e9746b1b624eda3483e2 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 14 Apr 2022 20:07:17 -0700 Subject: [PATCH 407/600] [webview_flutter_wkwebview] Implements the `HostApis` and methods for the `CookieManager`. (#5244) --- .../lib/src/common/web_kit.pigeon.dart | 181 ++++++++++++++++++ .../lib/src/web_kit/web_kit.dart | 40 +++- .../lib/src/web_kit/web_kit_api_impls.dart | 115 +++++++++++ .../pigeons/web_kit.dart | 46 +++++ .../test/src/common/test_web_kit.pigeon.dart | 109 +++++++++++ .../test/src/foundation/foundation_test.dart | 2 +- .../src/foundation/foundation_test.mocks.dart | 2 +- .../test/src/ui_kit/ui_kit_test.dart | 4 +- .../test/src/web_kit/web_kit_test.dart | 90 ++++++++- .../test/src/web_kit/web_kit_test.mocks.dart | 28 ++- .../test/src/web_kit_cookie_manager_test.dart | 3 +- .../web_kit_cookie_manager_test.mocks.dart | 27 +++ .../test/src/web_kit_webview_widget_test.dart | 12 +- 13 files changed, 629 insertions(+), 30 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart index 39f5663c1838..6395f4171f45 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart @@ -61,6 +61,23 @@ enum WKNavigationActionPolicyEnum { cancel, } +enum NSHttpCookiePropertyKeyEnum { + comment, + commentUrl, + discard, + domain, + expires, + maximumAge, + name, + originUrl, + path, + port, + sameSitePolicy, + secure, + value, + version, +} + class NSKeyValueObservingOptionsEnumData { NSKeyValueObservingOptionsEnumData({ this.value, @@ -153,6 +170,29 @@ class WKWebsiteDataTypesEnumData { } } +class NSHttpCookiePropertyKeyEnumData { + NSHttpCookiePropertyKeyEnumData({ + this.value, + }); + + NSHttpCookiePropertyKeyEnum? value; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['value'] = value == null ? null : value!.index; + return pigeonMap; + } + + static NSHttpCookiePropertyKeyEnumData decode(Object message) { + final Map pigeonMap = message as Map; + return NSHttpCookiePropertyKeyEnumData( + value: pigeonMap['value'] != null + ? NSHttpCookiePropertyKeyEnum.values[pigeonMap['value']! as int] + : null, + ); + } +} + class NSUrlRequestData { NSUrlRequestData({ required this.url, @@ -221,6 +261,28 @@ class WKUserScriptData { } } +class NSHttpCookieData { + NSHttpCookieData({ + required this.properties, + }); + + Map properties; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['properties'] = properties; + return pigeonMap; + } + + static NSHttpCookieData decode(Object message) { + final Map pigeonMap = message as Map; + return NSHttpCookieData( + properties: (pigeonMap['properties'] as Map?)! + .cast(), + ); + } +} + class _WKWebsiteDataStoreHostApiCodec extends StandardMessageCodec { const _WKWebsiteDataStoreHostApiCodec(); @override @@ -283,6 +345,31 @@ class WKWebsiteDataStoreHostApi { } } + Future createDefaultDataStore(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebsiteDataStoreHostApi.createDefaultDataStore', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + Future removeDataOfTypes( int arg_instanceId, List arg_dataTypes, @@ -1787,3 +1874,97 @@ class WKUIDelegateHostApi { } } } + +class _WKHttpCookieStoreHostApiCodec extends StandardMessageCodec { + const _WKHttpCookieStoreHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is NSHttpCookieData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is NSHttpCookiePropertyKeyEnumData) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return NSHttpCookieData.decode(readValue(buffer)!); + + case 129: + return NSHttpCookiePropertyKeyEnumData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +class WKHttpCookieStoreHostApi { + /// Constructor for [WKHttpCookieStoreHostApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + WKHttpCookieStoreHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _WKHttpCookieStoreHostApiCodec(); + + Future createFromWebsiteDataStore( + int arg_instanceId, int arg_websiteDataStoreInstanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKHttpCookieStoreHostApi.createFromWebsiteDataStore', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_websiteDataStoreInstanceId]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setCookie( + int arg_instanceId, NSHttpCookieData arg_cookie) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKHttpCookieStoreHostApi.setCookie', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_cookie]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart index 64a45ada265f..ab63db67bba1 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart @@ -251,7 +251,11 @@ class WKWebsiteDataStore { ); factory WKWebsiteDataStore._defaultDataStore() { - throw UnimplementedError(); + final WKWebsiteDataStore websiteDataStore = WKWebsiteDataStore._(); + websiteDataStore._websiteDataStoreApi.createDefaultDataStoreForInstances( + websiteDataStore, + ); + return websiteDataStore; } /// Constructs a [WKWebsiteDataStore] that is owned by [configuration]. @@ -301,20 +305,38 @@ class WKWebsiteDataStore { /// An object that manages the HTTP cookies associated with a particular web view. /// /// Wraps [WKHTTPCookieStore](https://developer.apple.com/documentation/webkit/wkhttpcookiestore?language=objc). -class WKHttpCookieStore { +class WKHttpCookieStore extends NSObject { + WKHttpCookieStore._({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _httpCookieStoreApi = WKHttpCookieStoreHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ); + /// Constructs a [WKHttpCookieStore] that is owned by [dataStore]. @visibleForTesting - WKHttpCookieStore.fromWebsiteDataStore( - // TODO(bparrishMines): Remove ignore on implementation. - // ignore: avoid_unused_constructor_parameters - WKWebsiteDataStore dataStore, - ) { - throw UnimplementedError(); + factory WKHttpCookieStore.fromWebsiteDataStore( + WKWebsiteDataStore dataStore, { + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) { + final WKHttpCookieStore cookieStore = WKHttpCookieStore._( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ); + cookieStore._httpCookieStoreApi.createFromWebsiteDataStoreForInstances( + cookieStore, + dataStore, + ); + return cookieStore; } + final WKHttpCookieStoreHostApiImpl _httpCookieStoreApi; + /// Adds a cookie to the cookie store. Future setCookie(NSHttpCookie cookie) { - throw UnimplementedError(); + return _httpCookieStoreApi.setCookieForInsances(this, cookie); } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart index d33407905375..b970ab26bcc7 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart @@ -45,6 +45,73 @@ Iterable _toWKWebsiteDataTypesEnumData( }); } +extension _NSHttpCookieConverter on NSHttpCookie { + NSHttpCookieData toNSHttpCookieData() { + return NSHttpCookieData( + properties: properties.map( + (NSHttpCookiePropertyKey key, Object value) { + return MapEntry( + key.toNSHttpCookiePropertyKeyEnumData(), + value.toString(), + ); + }, + ), + ); + } +} + +extension _NSHttpCookiePropertyKeyConverter on NSHttpCookiePropertyKey { + NSHttpCookiePropertyKeyEnumData toNSHttpCookiePropertyKeyEnumData() { + late final NSHttpCookiePropertyKeyEnum value; + switch (this) { + case NSHttpCookiePropertyKey.comment: + value = NSHttpCookiePropertyKeyEnum.comment; + break; + case NSHttpCookiePropertyKey.commentUrl: + value = NSHttpCookiePropertyKeyEnum.commentUrl; + break; + case NSHttpCookiePropertyKey.discard: + value = NSHttpCookiePropertyKeyEnum.discard; + break; + case NSHttpCookiePropertyKey.domain: + value = NSHttpCookiePropertyKeyEnum.domain; + break; + case NSHttpCookiePropertyKey.expires: + value = NSHttpCookiePropertyKeyEnum.expires; + break; + case NSHttpCookiePropertyKey.maximumAge: + value = NSHttpCookiePropertyKeyEnum.maximumAge; + break; + case NSHttpCookiePropertyKey.name: + value = NSHttpCookiePropertyKeyEnum.name; + break; + case NSHttpCookiePropertyKey.originUrl: + value = NSHttpCookiePropertyKeyEnum.originUrl; + break; + case NSHttpCookiePropertyKey.path: + value = NSHttpCookiePropertyKeyEnum.path; + break; + case NSHttpCookiePropertyKey.port: + value = NSHttpCookiePropertyKeyEnum.port; + break; + case NSHttpCookiePropertyKey.sameSitePolicy: + value = NSHttpCookiePropertyKeyEnum.sameSitePolicy; + break; + case NSHttpCookiePropertyKey.secure: + value = NSHttpCookiePropertyKeyEnum.secure; + break; + case NSHttpCookiePropertyKey.value: + value = NSHttpCookiePropertyKeyEnum.value; + break; + case NSHttpCookiePropertyKey.version: + value = NSHttpCookiePropertyKeyEnum.version; + break; + } + + return NSHttpCookiePropertyKeyEnumData(value: value); + } +} + extension _WKUserScriptInjectionTimeConverter on WKUserScriptInjectionTime { WKUserScriptInjectionTimeEnumData toWKUserScriptInjectionTimeEnumData() { late final WKUserScriptInjectionTimeEnum value; @@ -178,6 +245,16 @@ class WKWebsiteDataStoreHostApiImpl extends WKWebsiteDataStoreHostApi { } } + /// Calls [createDefaultDataStore] with the ids of the provided object instances. + Future createDefaultDataStoreForInstances( + WKWebsiteDataStore instance, + ) async { + final int? instanceId = instanceManager.tryAddInstance(instance); + if (instanceId != null) { + await createDefaultDataStore(instanceId); + } + } + /// Calls [removeDataOfTypes] with the ids of the provided object instances. Future removeDataOfTypesForInstances( WKWebsiteDataStore instance, @@ -251,6 +328,44 @@ class WKPreferencesHostApiImpl extends WKPreferencesHostApi { } } +/// Host api implementation for [WKHttpCookieStore]. +class WKHttpCookieStoreHostApiImpl extends WKHttpCookieStoreHostApi { + /// Constructs a [WKHttpCookieStoreHostApiImpl]. + WKHttpCookieStoreHostApiImpl({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? InstanceManager.instance, + super(binaryMessenger: binaryMessenger); + + /// Maintains instances stored to communicate with Objective-C objects. + final InstanceManager instanceManager; + + /// Calls [createFromWebsiteDataStore] with the ids of the provided object instances. + Future createFromWebsiteDataStoreForInstances( + WKHttpCookieStore instance, + WKWebsiteDataStore dataStore, + ) async { + final int? instanceId = instanceManager.tryAddInstance(instance); + if (instanceId != null) { + await createFromWebsiteDataStore( + instanceId, + instanceManager.getInstanceId(dataStore)!, + ); + } + } + + /// Calls [setCookie] with the ids of the provided object instances. + Future setCookieForInsances( + WKHttpCookieStore instance, + NSHttpCookie cookie, + ) { + return setCookie( + instanceManager.getInstanceId(instance)!, + cookie.toNSHttpCookieData(), + ); + } +} + /// Host api implementation for [WKUserContentController]. class WKUserContentControllerHostApiImpl extends WKUserContentControllerHostApi { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart index b0a239fcd146..23d8712b0486 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart @@ -117,6 +117,30 @@ class WKNavigationActionPolicyEnumData { late WKNavigationActionPolicyEnum? value; } +/// Mirror of NSHTTPCookiePropertyKey. +/// +/// See https://developer.apple.com/documentation/foundation/nshttpcookiepropertykey. +enum NSHttpCookiePropertyKeyEnum { + comment, + commentUrl, + discard, + domain, + expires, + maximumAge, + name, + originUrl, + path, + port, + sameSitePolicy, + secure, + value, + version, +} + +class NSHttpCookiePropertyKeyEnumData { + late NSHttpCookiePropertyKeyEnum? value; +} + /// Mirror of NSURLRequest. /// /// See https://developer.apple.com/documentation/foundation/nsurlrequest?language=objc. @@ -168,6 +192,13 @@ class WKScriptMessageData { late Object? body; } +/// Mirror of NSHttpCookieData. +/// +/// See https://developer.apple.com/documentation/foundation/nshttpcookie?language=objc. +class NSHttpCookieData { + late Map properties; +} + /// Mirror of WKWebsiteDataStore. /// /// See https://developer.apple.com/documentation/webkit/wkwebsitedatastore?language=objc. @@ -178,6 +209,8 @@ abstract class WKWebsiteDataStoreHostApi { int configurationInstanceId, ); + void createDefaultDataStore(int instanceId); + @async bool removeDataOfTypes( int instanceId, @@ -370,3 +403,16 @@ abstract class WKWebViewHostApi { abstract class WKUIDelegateHostApi { void create(int instanceId); } + +/// Mirror of WKHttpCookieStore. +/// +/// See https://developer.apple.com/documentation/webkit/wkhttpcookiestore?language=objc. +@HostApi(dartHostTestHandler: 'TestWKHttpCookieStoreHostApi') +abstract class WKHttpCookieStoreHostApi { + void createFromWebsiteDataStore( + int instanceId, + int websiteDataStoreInstanceId, + ); + + void setCookie(int instanceId, NSHttpCookieData cookie); +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart index 06a7b7a0a4c0..67ae8882cbd3 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart @@ -44,6 +44,7 @@ abstract class TestWKWebsiteDataStoreHostApi { void createFromWebViewConfiguration( int instanceId, int configurationInstanceId); + void createDefaultDataStore(int instanceId); Future removeDataOfTypes( int instanceId, List dataTypes, @@ -74,6 +75,26 @@ abstract class TestWKWebsiteDataStoreHostApi { }); } } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebsiteDataStoreHostApi.createDefaultDataStore', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.createDefaultDataStore was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.createDefaultDataStore was null, expected non-null int.'); + api.createDefaultDataStore(arg_instanceId!); + return {}; + }); + } + } { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes', @@ -1330,3 +1351,91 @@ abstract class TestWKUIDelegateHostApi { } } } + +class _TestWKHttpCookieStoreHostApiCodec extends StandardMessageCodec { + const _TestWKHttpCookieStoreHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is NSHttpCookieData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is NSHttpCookiePropertyKeyEnumData) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return NSHttpCookieData.decode(readValue(buffer)!); + + case 129: + return NSHttpCookiePropertyKeyEnumData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class TestWKHttpCookieStoreHostApi { + static const MessageCodec codec = + _TestWKHttpCookieStoreHostApiCodec(); + + void createFromWebsiteDataStore( + int instanceId, int websiteDataStoreInstanceId); + void setCookie(int instanceId, NSHttpCookieData cookie); + static void setup(TestWKHttpCookieStoreHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKHttpCookieStoreHostApi.createFromWebsiteDataStore', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKHttpCookieStoreHostApi.createFromWebsiteDataStore was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKHttpCookieStoreHostApi.createFromWebsiteDataStore was null, expected non-null int.'); + final int? arg_websiteDataStoreInstanceId = (args[1] as int?); + assert(arg_websiteDataStoreInstanceId != null, + 'Argument for dev.flutter.pigeon.WKHttpCookieStoreHostApi.createFromWebsiteDataStore was null, expected non-null int.'); + api.createFromWebsiteDataStore( + arg_instanceId!, arg_websiteDataStoreInstanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKHttpCookieStoreHostApi.setCookie', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKHttpCookieStoreHostApi.setCookie was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WKHttpCookieStoreHostApi.setCookie was null, expected non-null int.'); + final NSHttpCookieData? arg_cookie = (args[1] as NSHttpCookieData?); + assert(arg_cookie != null, + 'Argument for dev.flutter.pigeon.WKHttpCookieStoreHostApi.setCookie was null, expected non-null NSHttpCookieData.'); + api.setCookie(arg_instanceId!, arg_cookie!); + return {}; + }); + } + } + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart index a08680940bcc..007c2bc32252 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart @@ -25,7 +25,7 @@ void main() { instanceManager = InstanceManager(); }); - group('$NSObject', () { + group('NSObject', () { late MockTestNSObjectHostApi mockPlatformHostApi; late NSObject object; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.mocks.dart index b8552c5d8157..7bd208eeac05 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.mocks.dart @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.1.0 from annotations -// in webview_flutter_wkwebview/test/src/foundation/foundation_test.dart. +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart. // Do not manually edit this file. import 'package:mockito/mockito.dart' as _i1; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart index b6c50609552f..7db190f8192c 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart @@ -31,7 +31,7 @@ void main() { instanceManager = InstanceManager(); }); - group('$UIScrollView', () { + group('UIScrollView', () { late MockTestUIScrollViewHostApi mockPlatformHostApi; late UIScrollView scrollView; @@ -87,7 +87,7 @@ void main() { }); }); - group('$UIView', () { + group('UIView', () { late MockTestUIViewHostApi mockPlatformHostApi; late UIView view; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart index 00a3a31a6957..8564bf889c2f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart @@ -17,6 +17,7 @@ import '../common/test_web_kit.pigeon.dart'; import 'web_kit_test.mocks.dart'; @GenerateMocks([ + TestWKHttpCookieStoreHostApi, TestWKNavigationDelegateHostApi, TestWKPreferencesHostApi, TestWKScriptMessageHandlerHostApi, @@ -39,7 +40,7 @@ void main() { WebKitFlutterApis.instance = flutterApis; }); - group('$WKWebsiteDataStore', () { + group('WKWebsiteDataStore', () { late MockTestWKWebsiteDataStoreHostApi mockPlatformHostApi; late WKWebsiteDataStore websiteDataStore; @@ -75,6 +76,16 @@ void main() { )); }); + test('createDefaultDataStore', () { + final WKWebsiteDataStore defaultDataStore = + WKWebsiteDataStore.defaultDataStore; + verify( + mockPlatformHostApi.createDefaultDataStore( + InstanceManager.instance.getInstanceId(defaultDataStore), + ), + ); + }); + test('removeDataOfTypes', () { when(mockPlatformHostApi.removeDataOfTypes( any, @@ -102,7 +113,70 @@ void main() { }); }); - group('$WKScriptMessageHandler', () { + group('WKHttpCookieStore', () { + late MockTestWKHttpCookieStoreHostApi mockPlatformHostApi; + + late WKHttpCookieStore httpCookieStore; + + late WKWebsiteDataStore websiteDataStore; + + setUp(() { + mockPlatformHostApi = MockTestWKHttpCookieStoreHostApi(); + TestWKHttpCookieStoreHostApi.setup(mockPlatformHostApi); + + TestWKWebViewConfigurationHostApi.setup( + MockTestWKWebViewConfigurationHostApi(), + ); + TestWKWebsiteDataStoreHostApi.setup( + MockTestWKWebsiteDataStoreHostApi(), + ); + + websiteDataStore = WKWebsiteDataStore.fromWebViewConfiguration( + WKWebViewConfiguration(instanceManager: instanceManager), + instanceManager: instanceManager, + ); + + httpCookieStore = WKHttpCookieStore.fromWebsiteDataStore( + websiteDataStore, + instanceManager: instanceManager, + ); + }); + + tearDown(() { + TestWKHttpCookieStoreHostApi.setup(null); + TestWKWebsiteDataStoreHostApi.setup(null); + TestWKWebViewConfigurationHostApi.setup(null); + }); + + test('createFromWebsiteDataStore', () { + verify(mockPlatformHostApi.createFromWebsiteDataStore( + instanceManager.getInstanceId(httpCookieStore), + instanceManager.getInstanceId(websiteDataStore), + )); + }); + + test('setCookie', () async { + await httpCookieStore.setCookie( + const NSHttpCookie.withProperties({ + NSHttpCookiePropertyKey.comment: 'aComment', + })); + + final NSHttpCookieData cookie = verify( + mockPlatformHostApi.setCookie( + instanceManager.getInstanceId(httpCookieStore)!, + captureAny, + ), + ).captured.single as NSHttpCookieData; + + expect( + cookie.properties.entries.single.key!.value, + NSHttpCookiePropertyKeyEnum.comment, + ); + expect(cookie.properties.entries.single.value, 'aComment'); + }); + }); + + group('WKScriptMessageHandler', () { late MockTestWKScriptMessageHandlerHostApi mockPlatformHostApi; late WKScriptMessageHandler scriptMessageHandler; @@ -127,7 +201,7 @@ void main() { }); }); - group('$WKPreferences', () { + group('WKPreferences', () { late MockTestWKPreferencesHostApi mockPlatformHostApi; late WKPreferences preferences; @@ -172,7 +246,7 @@ void main() { }); }); - group('$WKUserContentController', () { + group('WKUserContentController', () { late MockTestWKUserContentControllerHostApi mockPlatformHostApi; late WKUserContentController userContentController; @@ -260,7 +334,7 @@ void main() { }); }); - group('$WKWebViewConfiguration', () { + group('WKWebViewConfiguration', () { late MockTestWKWebViewConfigurationHostApi mockPlatformHostApi; late WKWebViewConfiguration webViewConfiguration; @@ -332,7 +406,7 @@ void main() { }); }); - group('$WKNavigationDelegate', () { + group('WKNavigationDelegate', () { late MockTestWKNavigationDelegateHostApi mockPlatformHostApi; late WKWebView webView; @@ -395,7 +469,7 @@ void main() { }); }); - group('$WKWebView', () { + group('WKWebView', () { late MockTestWKWebViewHostApi mockPlatformHostApi; late WKWebViewConfiguration webViewConfiguration; @@ -558,7 +632,7 @@ void main() { }); }); - group('$WKUIDelegate', () { + group('WKUIDelegate', () { late MockTestWKUIDelegateHostApi mockPlatformHostApi; late WKUIDelegate uiDelegate; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart index 3758c9e573a4..67c06286185c 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.1.0 from annotations -// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart. +// in webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart. // Do not manually edit this file. import 'dart:async' as _i4; @@ -20,6 +20,28 @@ import '../common/test_web_kit.pigeon.dart' as _i2; // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types +/// A class which mocks [TestWKHttpCookieStoreHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestWKHttpCookieStoreHostApi extends _i1.Mock + implements _i2.TestWKHttpCookieStoreHostApi { + MockTestWKHttpCookieStoreHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void createFromWebsiteDataStore( + int? instanceId, int? websiteDataStoreInstanceId) => + super.noSuchMethod( + Invocation.method(#createFromWebsiteDataStore, + [instanceId, websiteDataStoreInstanceId]), + returnValueForMissingStub: null); + @override + void setCookie(int? instanceId, _i3.NSHttpCookieData? cookie) => + super.noSuchMethod(Invocation.method(#setCookie, [instanceId, cookie]), + returnValueForMissingStub: null); +} + /// A class which mocks [TestWKNavigationDelegateHostApi]. /// /// See the documentation for Mockito's code generation for more information. @@ -281,6 +303,10 @@ class MockTestWKWebsiteDataStoreHostApi extends _i1.Mock [instanceId, configurationInstanceId]), returnValueForMissingStub: null); @override + void createDefaultDataStore(int? instanceId) => super.noSuchMethod( + Invocation.method(#createDefaultDataStore, [instanceId]), + returnValueForMissingStub: null); + @override _i4.Future removeDataOfTypes( int? instanceId, List<_i3.WKWebsiteDataTypesEnumData?>? dataTypes, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart index 4529316d3159..5238c0bb2c56 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart @@ -11,7 +11,6 @@ import 'package:webview_flutter_platform_interface/webview_flutter_platform_inte import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart'; import 'package:webview_flutter_wkwebview/src/web_kit_cookie_manager.dart'; -import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart'; import 'web_kit_cookie_manager_test.mocks.dart'; @@ -22,7 +21,7 @@ import 'web_kit_cookie_manager_test.mocks.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - group('$WebKitWebViewWidget', () { + group('WebKitWebViewWidget', () { late MockWKWebsiteDataStore mockWebsiteDataStore; late MockWKHttpCookieStore mockWKHttpCookieStore; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart index 88025d3d9465..a85c57f7bdb3 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart @@ -35,6 +35,33 @@ class MockWKHttpCookieStore extends _i1.Mock implements _i2.WKHttpCookieStore { (super.noSuchMethod(Invocation.method(#setCookie, [cookie]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i3.Future); + @override + _i3.Future addObserver(_i4.NSObject? observer, + {String? keyPath, Set<_i4.NSKeyValueObservingOptions>? options}) => + (super.noSuchMethod( + Invocation.method( + #addObserver, [observer], {#keyPath: keyPath, #options: options}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i3.Future); + @override + _i3.Future removeObserver(_i4.NSObject? observer, {String? keyPath}) => + (super.noSuchMethod( + Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i3.Future); + @override + _i3.Future dispose() => + (super.noSuchMethod(Invocation.method(#dispose, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i3.Future); + @override + _i3.Future setObserveValue( + void Function( + String, _i4.NSObject, Map<_i4.NSKeyValueChangeKey, Object?>)? + observeValue) => + (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i3.Future); } /// A class which mocks [WKWebsiteDataStore]. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index 86ee1a99b949..8c104bdb3ee2 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -37,7 +37,7 @@ import 'web_kit_webview_widget_test.mocks.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - group('$WebKitWebViewWidget', () { + group('WebKitWebViewWidget', () { late MockWKWebView mockWebView; late MockWebViewWidgetProxy mockWebViewWidgetProxy; late MockWKUserContentController mockUserContentController; @@ -135,7 +135,7 @@ void main() { verify(mockWebView.loadRequest(request)); }); - group('$CreationParams', () { + group('CreationParams', () { testWidgets('initialUrl', (WidgetTester tester) async { await buildWidget( tester, @@ -258,7 +258,7 @@ void main() { expect(javaScriptChannels[3], 'b'); }); - group('$WebSettings', () { + group('WebSettings', () { testWidgets('javascriptMode', (WidgetTester tester) async { await buildWidget( tester, @@ -427,7 +427,7 @@ void main() { }); }); - group('$WebKitWebViewPlatformController', () { + group('WebKitWebViewPlatformController', () { testWidgets('loadFile', (WidgetTester tester) async { await buildWidget(tester); @@ -901,7 +901,7 @@ void main() { }); }); - group('$WebViewPlatformCallbacksHandler', () { + group('WebViewPlatformCallbacksHandler', () { testWidgets('onPageStarted', (WidgetTester tester) async { await buildWidget(tester); @@ -1074,7 +1074,7 @@ void main() { }); }); - group('$JavascriptChannelRegistry', () { + group('JavascriptChannelRegistry', () { testWidgets('onJavascriptChannelMessage', (WidgetTester tester) async { when(mockWebViewWidgetProxy.createScriptMessageHandler()).thenReturn( MockWKScriptMessageHandler(), From c0bc7ccaf7142db4b181782f1cea3a58f0f8eea4 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 15 Apr 2022 14:01:46 -0400 Subject: [PATCH 408/600] [flutter_plugin_tools] Preserve Dart SDK version in all-plugins-app (#5281) Fixes `all-plugins-app` to preserve the original application's Dart SDK version to avoid changing language feature opt-ins that the template may rely on. --- .ci/flutter_master.version | 2 +- script/tool/CHANGELOG.md | 3 ++ .../src/create_all_plugins_app_command.dart | 27 +++++++++++++---- .../create_all_plugins_app_command_test.dart | 30 ++++++++++++++++--- 4 files changed, 51 insertions(+), 11 deletions(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 103b05611de2..d6feb755f3e2 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2b8333240d38cf72b8e13580e24d01f5a188e26c +329ceaef666ff8cebd4dc36325ab6bfebdc7c8ef diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index df76e4819e63..6e632c67684d 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -2,6 +2,9 @@ - Adds a new `readme-check` command. - Updates `publish-plugin` command documentation. +- Fixes `all-plugins-app` to preserve the original application's Dart SDK + version to avoid changing language feature opt-ins that the template may + rely on. ## 0.8.2 diff --git a/script/tool/lib/src/create_all_plugins_app_command.dart b/script/tool/lib/src/create_all_plugins_app_command.dart index 82f29bd501f3..6b44ab788786 100644 --- a/script/tool/lib/src/create_all_plugins_app_command.dart +++ b/script/tool/lib/src/create_all_plugins_app_command.dart @@ -147,6 +147,19 @@ class CreateAllPluginsAppCommand extends PluginCommand { } Future _genPubspecWithAllPlugins() async { + final RepositoryPackage buildAllApp = RepositoryPackage(appDirectory); + // Read the old pubspec file's Dart SDK version, in order to preserve it + // in the new file. The template sometimes relies on having opted in to + // specific language features via SDK version, so using a different one + // can cause compilation failures. + final Pubspec originalPubspec = buildAllApp.parsePubspec(); + const String dartSdkKey = 'sdk'; + final VersionConstraint dartSdkConstraint = + originalPubspec.environment?[dartSdkKey] ?? + VersionConstraint.compatibleWith( + Version.parse('2.12.0'), + ); + final Map pluginDeps = await _getValidPathDependencies(); final Pubspec pubspec = Pubspec( @@ -154,9 +167,7 @@ class CreateAllPluginsAppCommand extends PluginCommand { description: 'Flutter app containing all 1st party plugins.', version: Version.parse('1.0.0+1'), environment: { - 'sdk': VersionConstraint.compatibleWith( - Version.parse('2.12.0'), - ), + dartSdkKey: dartSdkConstraint, }, dependencies: { 'flutter': SdkDependency('flutter'), @@ -166,8 +177,7 @@ class CreateAllPluginsAppCommand extends PluginCommand { }, dependencyOverrides: pluginDeps, ); - final File pubspecFile = appDirectory.childFile('pubspec.yaml'); - pubspecFile.writeAsStringSync(_pubspecToString(pubspec)); + buildAllApp.pubspecFile.writeAsStringSync(_pubspecToString(pubspec)); } Future> _getValidPathDependencies() async { @@ -212,7 +222,12 @@ dev_dependencies:${_pubspecMapString(pubspec.devDependencies)} for (final MapEntry entry in values.entries) { buffer.writeln(); if (entry.value is VersionConstraint) { - buffer.write(' ${entry.key}: ${entry.value}'); + String value = entry.value.toString(); + // Range constraints require quoting. + if (value.startsWith('>') || value.startsWith('<')) { + value = "'$value'"; + } + buffer.write(' ${entry.key}: $value'); } else if (entry.value is SdkDependency) { final SdkDependency dep = entry.value as SdkDependency; buffer.write(' ${entry.key}: \n sdk: ${dep.sdk}'); diff --git a/script/tool/test/create_all_plugins_app_command_test.dart b/script/tool/test/create_all_plugins_app_command_test.dart index 0066cc53f61a..917adca020d4 100644 --- a/script/tool/test/create_all_plugins_app_command_test.dart +++ b/script/tool/test/create_all_plugins_app_command_test.dart @@ -2,10 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io' as io; + import 'package:args/command_runner.dart'; import 'package:file/file.dart'; import 'package:file/local.dart'; +import 'package:flutter_plugin_tools/src/common/repository_package.dart'; import 'package:flutter_plugin_tools/src/create_all_plugins_app_command.dart'; +import 'package:platform/platform.dart'; +import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:test/test.dart'; import 'util.dart'; @@ -76,14 +81,31 @@ void main() { ])); }); - test('pubspec is compatible with null-safe app code', () async { + test('pubspec preserves existing Dart SDK version', () async { + const String baselineProjectName = 'baseline'; + final Directory baselineProjectDirectory = + testRoot.childDirectory(baselineProjectName); + io.Process.runSync( + getFlutterCommand(const LocalPlatform()), + [ + 'create', + '--template=app', + '--project-name=$baselineProjectName', + baselineProjectDirectory.path, + ], + ); + final Pubspec baselinePubspec = + RepositoryPackage(baselineProjectDirectory).parsePubspec(); + createFakePlugin('plugina', packagesDir); await runCapturingPrint(runner, ['all-plugins-app']); - final String pubspec = - command.appDirectory.childFile('pubspec.yaml').readAsStringSync(); + final Pubspec generatedPubspec = + RepositoryPackage(command.appDirectory).parsePubspec(); - expect(pubspec, contains(RegExp('sdk:\\s*(?:["\']>=|[^])2\\.12\\.'))); + const String dartSdkKey = 'sdk'; + expect(generatedPubspec.environment?[dartSdkKey], + baselinePubspec.environment?[dartSdkKey]); }); test('handles --output-dir', () async { From 02ac515c174b72de810d2c38b603c6b95117ceb7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 15 Apr 2022 16:19:09 -0400 Subject: [PATCH 409/600] Roll Flutter from 329ceaef666f to ec8289c3f90c (26 revisions) (#5283) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index d6feb755f3e2..1234b94e1803 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -329ceaef666ff8cebd4dc36325ab6bfebdc7c8ef +ec8289c3f90c283a8d4ae6a6e2f817baded1776c From 78bf77f604f26967c8851f840ea41bea5ecd7e06 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 16 Apr 2022 13:09:10 -0400 Subject: [PATCH 410/600] Roll Flutter from ec8289c3f90c to 44be0b84ba54 (2 revisions) (#5284) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 1234b94e1803..05c73f193285 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ec8289c3f90c283a8d4ae6a6e2f817baded1776c +44be0b84ba54a1a0a99304a4793bb30b340b7c15 From 8ffa1bf606c151d853797ca952e69032d934efa8 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 16 Apr 2022 14:14:06 -0400 Subject: [PATCH 411/600] Roll Flutter from 44be0b84ba54 to aa5d7b6972ee (12 revisions) (#5286) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 05c73f193285..4dbcb70f91c4 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -44be0b84ba54a1a0a99304a4793bb30b340b7c15 +aa5d7b6972ee5be596c15b83d39f7d3dde6ad939 From c9addb447354ce45c977b28acb758a9df6897a63 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 17 Apr 2022 16:24:09 -0400 Subject: [PATCH 412/600] Roll Flutter from aa5d7b6972ee to f4875ae865e9 (1 revision) (#5289) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 4dbcb70f91c4..048f3562ae90 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -aa5d7b6972ee5be596c15b83d39f7d3dde6ad939 +f4875ae865e9d42644a93bbab2164fc7baac4ff7 From c581be43333c4d5f649ad7ab928437e88e269c85 Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Mon, 18 Apr 2022 12:24:08 -0700 Subject: [PATCH 413/600] [video_player_web] Stop buffering when browser canPlayThrough. (#5068) --- .../video_player_web/CHANGELOG.md | 8 + .../example/integration_test/utils.dart | 16 ++ .../integration_test/video_player_test.dart | 195 ++++++++++++++ .../video_player_web_test.dart | 59 +++- .../lib/src/video_player.dart | 254 ++++++++++++++++++ .../lib/video_player_web.dart | 247 +++-------------- .../video_player_web/pubspec.yaml | 2 +- 7 files changed, 557 insertions(+), 224 deletions(-) create mode 100644 packages/video_player/video_player_web/example/integration_test/utils.dart create mode 100644 packages/video_player/video_player_web/example/integration_test/video_player_test.dart create mode 100644 packages/video_player/video_player_web/lib/src/video_player.dart diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md index 1cd428c4deea..3310660137a2 100644 --- a/packages/video_player/video_player_web/CHANGELOG.md +++ b/packages/video_player/video_player_web/CHANGELOG.md @@ -1,3 +1,11 @@ +## 2.0.8 + +* Ensures `buffering` state is only removed when the browser reports enough data + has been buffered so that the video can likely play through without stopping + (`onCanPlayThrough`). Issue [#94630](https://github.com/flutter/flutter/issues/94630). +* Improves testability of the `_VideoPlayer` private class. +* Ensures that tests that listen to a Stream fail "fast" (1 second max timeout). + ## 2.0.7 * Internal code cleanup for stricter analysis options. diff --git a/packages/video_player/video_player_web/example/integration_test/utils.dart b/packages/video_player/video_player_web/example/integration_test/utils.dart new file mode 100644 index 000000000000..b0118514053a --- /dev/null +++ b/packages/video_player/video_player_web/example/integration_test/utils.dart @@ -0,0 +1,16 @@ +// 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. + +// Returns the URL to load an asset from this example app as a network source. +// +// TODO(stuartmorgan): Convert this to a local `HttpServer` that vends the +// assets directly, https://github.com/flutter/flutter/issues/95420 +String getUrlForAssetAsNetworkSource(String assetKey) { + return 'https://github.com/flutter/plugins/blob/' + // This hash can be rolled forward to pick up newly-added assets. + 'cb381ced070d356799dddf24aca38ce0579d3d7b' + '/packages/video_player/video_player/example/' + '$assetKey' + '?raw=true'; +} diff --git a/packages/video_player/video_player_web/example/integration_test/video_player_test.dart b/packages/video_player/video_player_web/example/integration_test/video_player_test.dart new file mode 100644 index 000000000000..41aba9792e23 --- /dev/null +++ b/packages/video_player/video_player_web/example/integration_test/video_player_test.dart @@ -0,0 +1,195 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:html' as html; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:video_player_platform_interface/video_player_platform_interface.dart'; +import 'package:video_player_web/src/video_player.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group('VideoPlayer', () { + late html.VideoElement video; + + setUp(() { + // Never set "src" on the video, so this test doesn't hit the network! + video = html.VideoElement() + ..controls = true + ..setAttribute('playsinline', 'false'); + }); + + testWidgets('fixes critical video element config', (WidgetTester _) async { + VideoPlayer(videoElement: video).initialize(); + + expect(video.controls, isFalse, + reason: 'Video is controlled through code'); + expect(video.getAttribute('autoplay'), 'false', + reason: 'Cannot autoplay on the web'); + expect(video.getAttribute('playsinline'), 'true', + reason: 'Needed by safari iOS'); + }); + + testWidgets('setVolume', (WidgetTester tester) async { + final VideoPlayer player = VideoPlayer(videoElement: video)..initialize(); + + player.setVolume(0); + + expect(video.volume, isZero, reason: 'Volume should be zero'); + expect(video.muted, isTrue, reason: 'muted attribute should be true'); + + expect(() { + player.setVolume(-0.0001); + }, throwsAssertionError, reason: 'Volume cannot be < 0'); + + expect(() { + player.setVolume(1.0001); + }, throwsAssertionError, reason: 'Volume cannot be > 1'); + }); + + testWidgets('setPlaybackSpeed', (WidgetTester tester) async { + final VideoPlayer player = VideoPlayer(videoElement: video)..initialize(); + + expect(() { + player.setPlaybackSpeed(-1); + }, throwsAssertionError, reason: 'Playback speed cannot be < 0'); + + expect(() { + player.setPlaybackSpeed(0); + }, throwsAssertionError, reason: 'Playback speed cannot be == 0'); + }); + + testWidgets('seekTo', (WidgetTester tester) async { + final VideoPlayer player = VideoPlayer(videoElement: video)..initialize(); + + expect(() { + player.seekTo(const Duration(seconds: -1)); + }, throwsAssertionError, reason: 'Cannot seek into negative numbers'); + }); + + // The events tested in this group do *not* represent the actual sequence + // of events from a real "video" element. They're crafted to test the + // behavior of the VideoPlayer in different states with different events. + group('events', () { + late StreamController streamController; + late VideoPlayer player; + late Stream timedStream; + + final Set bufferingEvents = { + VideoEventType.bufferingStart, + VideoEventType.bufferingEnd, + }; + + setUp(() { + streamController = StreamController(); + player = + VideoPlayer(videoElement: video, eventController: streamController) + ..initialize(); + + // This stream will automatically close after 100 ms without seeing any events + timedStream = streamController.stream.timeout( + const Duration(milliseconds: 100), + onTimeout: (EventSink sink) { + sink.close(); + }, + ); + }); + + testWidgets('buffering dispatches only when it changes', + (WidgetTester tester) async { + // Take all the "buffering" events that we see during the next few seconds + final Future> stream = timedStream + .where( + (VideoEvent event) => bufferingEvents.contains(event.eventType)) + .map((VideoEvent event) => + event.eventType == VideoEventType.bufferingStart) + .toList(); + + // Simulate some events coming from the player... + player.setBuffering(true); + player.setBuffering(true); + player.setBuffering(true); + player.setBuffering(false); + player.setBuffering(false); + player.setBuffering(true); + player.setBuffering(false); + player.setBuffering(true); + player.setBuffering(false); + + final List events = await stream; + + expect(events, hasLength(6)); + expect(events, [true, false, true, false, true, false]); + }); + + testWidgets('canplay event does not change buffering state', + (WidgetTester tester) async { + // Take all the "buffering" events that we see during the next few seconds + final Future> stream = timedStream + .where( + (VideoEvent event) => bufferingEvents.contains(event.eventType)) + .map((VideoEvent event) => + event.eventType == VideoEventType.bufferingStart) + .toList(); + + player.setBuffering(true); + + // Simulate "canplay" event... + video.dispatchEvent(html.Event('canplay')); + + final List events = await stream; + + expect(events, hasLength(1)); + expect(events, [true]); + }); + + testWidgets('canplaythrough event does change buffering state', + (WidgetTester tester) async { + // Take all the "buffering" events that we see during the next few seconds + final Future> stream = timedStream + .where( + (VideoEvent event) => bufferingEvents.contains(event.eventType)) + .map((VideoEvent event) => + event.eventType == VideoEventType.bufferingStart) + .toList(); + + player.setBuffering(true); + + // Simulate "canplaythrough" event... + video.dispatchEvent(html.Event('canplaythrough')); + + final List events = await stream; + + expect(events, hasLength(2)); + expect(events, [true, false]); + }); + + testWidgets('initialized dispatches only once', + (WidgetTester tester) async { + // Dispatch some bogus "canplay" events from the video object + video.dispatchEvent(html.Event('canplay')); + video.dispatchEvent(html.Event('canplay')); + video.dispatchEvent(html.Event('canplay')); + + // Take all the "initialized" events that we see during the next few seconds + final Future> stream = timedStream + .where((VideoEvent event) => + event.eventType == VideoEventType.initialized) + .toList(); + + video.dispatchEvent(html.Event('canplay')); + video.dispatchEvent(html.Event('canplay')); + video.dispatchEvent(html.Event('canplay')); + + final List events = await stream; + + expect(events, hasLength(1)); + expect(events[0].eventType, VideoEventType.initialized); + }); + }); + }); +} diff --git a/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart b/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart index 97b03642cd07..5053ea6e5b04 100644 --- a/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart +++ b/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart @@ -11,10 +11,15 @@ import 'package:integration_test/integration_test.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; import 'package:video_player_web/video_player_web.dart'; +import 'utils.dart'; + +// Use WebM to allow CI to run tests in Chromium. +const String _videoAssetKey = 'assets/Butterfly-209.webm'; + void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - group('VideoPlayer for Web', () { + group('VideoPlayerWeb plugin (hits network)', () { late Future textureId; setUp(() { @@ -23,8 +28,7 @@ void main() { .create( DataSource( sourceType: DataSourceType.network, - uri: - 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4', + uri: getUrlForAssetAsNetworkSource(_videoAssetKey), ), ) .then((int? textureId) => textureId!); @@ -38,9 +42,9 @@ void main() { expect( VideoPlayerPlatform.instance.create( DataSource( - sourceType: DataSourceType.network, - uri: - 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4'), + sourceType: DataSourceType.network, + uri: getUrlForAssetAsNetworkSource(_videoAssetKey), + ), ), completion(isNonZero)); }); @@ -100,9 +104,9 @@ void main() { (WidgetTester tester) async { final int videoPlayerId = (await VideoPlayerPlatform.instance.create( DataSource( - sourceType: DataSourceType.network, - uri: - 'https://flutter.github.io/assets-for-api-docs/assets/videos/_non_existent_video.mp4'), + sourceType: DataSourceType.network, + uri: getUrlForAssetAsNetworkSource('assets/__non_existent.webm'), + ), ))!; final Stream eventStream = @@ -113,7 +117,7 @@ void main() { await VideoPlayerPlatform.instance.play(videoPlayerId); expect(() async { - await eventStream.last; + await eventStream.timeout(const Duration(seconds: 5)).last; }, throwsA(isA())); }); @@ -164,5 +168,40 @@ void main() { expect(VideoPlayerPlatform.instance.setMixWithOthers(true), completes); expect(VideoPlayerPlatform.instance.setMixWithOthers(false), completes); }); + + testWidgets('video playback lifecycle', (WidgetTester tester) async { + final int videoPlayerId = await textureId; + final Stream eventStream = + VideoPlayerPlatform.instance.videoEventsFor(videoPlayerId); + + final Future> stream = eventStream.timeout( + const Duration(seconds: 1), + onTimeout: (EventSink sink) { + sink.close(); + }, + ).toList(); + + await VideoPlayerPlatform.instance.setVolume(videoPlayerId, 0); + await VideoPlayerPlatform.instance.play(videoPlayerId); + + // Let the video play, until we stop seeing events for a second + final List events = await stream; + + await VideoPlayerPlatform.instance.pause(videoPlayerId); + + // The expected list of event types should look like this: + // 1. bufferingStart, + // 2. bufferingUpdate (videoElement.onWaiting), + // 3. initialized (videoElement.onCanPlay), + // 4. bufferingEnd (videoElement.onCanPlayThrough), + expect( + events.map((VideoEvent e) => e.eventType), + equals([ + VideoEventType.bufferingStart, + VideoEventType.bufferingUpdate, + VideoEventType.initialized, + VideoEventType.bufferingEnd + ])); + }); }); } diff --git a/packages/video_player/video_player_web/lib/src/video_player.dart b/packages/video_player/video_player_web/lib/src/video_player.dart new file mode 100644 index 000000000000..eda188cb1b9f --- /dev/null +++ b/packages/video_player/video_player_web/lib/src/video_player.dart @@ -0,0 +1,254 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:html' as html; + +import 'package:flutter/foundation.dart' show visibleForTesting; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:video_player_platform_interface/video_player_platform_interface.dart'; + +// An error code value to error name Map. +// See: https://developer.mozilla.org/en-US/docs/Web/API/MediaError/code +const Map _kErrorValueToErrorName = { + 1: 'MEDIA_ERR_ABORTED', + 2: 'MEDIA_ERR_NETWORK', + 3: 'MEDIA_ERR_DECODE', + 4: 'MEDIA_ERR_SRC_NOT_SUPPORTED', +}; + +// An error code value to description Map. +// See: https://developer.mozilla.org/en-US/docs/Web/API/MediaError/code +const Map _kErrorValueToErrorDescription = { + 1: 'The user canceled the fetching of the video.', + 2: 'A network error occurred while fetching the video, despite having previously been available.', + 3: 'An error occurred while trying to decode the video, despite having previously been determined to be usable.', + 4: 'The video has been found to be unsuitable (missing or in a format not supported by your browser).', +}; + +// The default error message, when the error is an empty string +// See: https://developer.mozilla.org/en-US/docs/Web/API/MediaError/message +const String _kDefaultErrorMessage = + 'No further diagnostic information can be determined or provided.'; + +/// Wraps a [html.VideoElement] so its API complies with what is expected by the plugin. +class VideoPlayer { + /// Create a [VideoPlayer] from a [html.VideoElement] instance. + VideoPlayer({ + required html.VideoElement videoElement, + @visibleForTesting StreamController? eventController, + }) : _videoElement = videoElement, + _eventController = eventController ?? StreamController(); + + final StreamController _eventController; + final html.VideoElement _videoElement; + + bool _isInitialized = false; + bool _isBuffering = false; + + /// Returns the [Stream] of [VideoEvent]s from the inner [html.VideoElement]. + Stream get events => _eventController.stream; + + /// Initializes the wrapped [html.VideoElement]. + /// + /// This method sets the required DOM attributes so videos can [play] programmatically, + /// and attaches listeners to the internal events from the [html.VideoElement] + /// to react to them / expose them through the [VideoPlayer.events] stream. + void initialize() { + _videoElement + ..autoplay = false + ..controls = false; + + // Allows Safari iOS to play the video inline + _videoElement.setAttribute('playsinline', 'true'); + + // Set autoplay to false since most browsers won't autoplay a video unless it is muted + _videoElement.setAttribute('autoplay', 'false'); + + _videoElement.onCanPlay.listen((dynamic _) { + if (!_isInitialized) { + _isInitialized = true; + _sendInitialized(); + } + }); + + _videoElement.onCanPlayThrough.listen((dynamic _) { + setBuffering(false); + }); + + _videoElement.onPlaying.listen((dynamic _) { + setBuffering(false); + }); + + _videoElement.onWaiting.listen((dynamic _) { + setBuffering(true); + _sendBufferingRangesUpdate(); + }); + + // The error event fires when some form of error occurs while attempting to load or perform the media. + _videoElement.onError.listen((html.Event _) { + setBuffering(false); + // The Event itself (_) doesn't contain info about the actual error. + // We need to look at the HTMLMediaElement.error. + // See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/error + final html.MediaError error = _videoElement.error!; + _eventController.addError(PlatformException( + code: _kErrorValueToErrorName[error.code]!, + message: error.message != '' ? error.message : _kDefaultErrorMessage, + details: _kErrorValueToErrorDescription[error.code], + )); + }); + + _videoElement.onEnded.listen((dynamic _) { + setBuffering(false); + _eventController.add(VideoEvent(eventType: VideoEventType.completed)); + }); + } + + /// Attempts to play the video. + /// + /// If this method is called programmatically (without user interaction), it + /// might fail unless the video is completely muted (or it has no Audio tracks). + /// + /// When called from some user interaction (a tap on a button), the above + /// limitation should disappear. + Future play() { + return _videoElement.play().catchError((Object e) { + // play() attempts to begin playback of the media. It returns + // a Promise which can get rejected in case of failure to begin + // playback for any reason, such as permission issues. + // The rejection handler is called with a DomException. + // See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play + final html.DomException exception = e as html.DomException; + _eventController.addError(PlatformException( + code: exception.name, + message: exception.message, + )); + }, test: (Object e) => e is html.DomException); + } + + /// Pauses the video in the current position. + void pause() { + _videoElement.pause(); + } + + /// Controls whether the video should start again after it finishes. + void setLooping(bool value) { + _videoElement.loop = value; + } + + /// Sets the volume at which the media will be played. + /// + /// Values must fall between 0 and 1, where 0 is muted and 1 is the loudest. + /// + /// When volume is set to 0, the `muted` property is also applied to the + /// [html.VideoElement]. This is required for auto-play on the web. + void setVolume(double volume) { + assert(volume >= 0 && volume <= 1); + + // TODO(ditman): Do we need to expose a "muted" API? + // https://github.com/flutter/flutter/issues/60721 + _videoElement.muted = !(volume > 0.0); + _videoElement.volume = volume; + } + + /// Sets the playback `speed`. + /// + /// A `speed` of 1.0 is "normal speed," values lower than 1.0 make the media + /// play slower than normal, higher values make it play faster. + /// + /// `speed` cannot be negative. + /// + /// The audio is muted when the fast forward or slow motion is outside a useful + /// range (for example, Gecko mutes the sound outside the range 0.25 to 4.0). + /// + /// The pitch of the audio is corrected by default. + void setPlaybackSpeed(double speed) { + assert(speed > 0); + + _videoElement.playbackRate = speed; + } + + /// Moves the playback head to a new `position`. + /// + /// `position` cannot be negative. + void seekTo(Duration position) { + assert(!position.isNegative); + + _videoElement.currentTime = position.inMilliseconds.toDouble() / 1000; + } + + /// Returns the current playback head position as a [Duration]. + Duration getPosition() { + _sendBufferingRangesUpdate(); + return Duration(milliseconds: (_videoElement.currentTime * 1000).round()); + } + + /// Disposes of the current [html.VideoElement]. + void dispose() { + _videoElement.removeAttribute('src'); + _videoElement.load(); + } + + // Sends an [VideoEventType.initialized] [VideoEvent] with info about the wrapped video. + void _sendInitialized() { + final Duration? duration = !_videoElement.duration.isNaN + ? Duration( + milliseconds: (_videoElement.duration * 1000).round(), + ) + : null; + + final Size? size = !_videoElement.videoHeight.isNaN + ? Size( + _videoElement.videoWidth.toDouble(), + _videoElement.videoHeight.toDouble(), + ) + : null; + + _eventController.add( + VideoEvent( + eventType: VideoEventType.initialized, + duration: duration, + size: size, + ), + ); + } + + /// Caches the current "buffering" state of the video. + /// + /// If the current buffering state is different from the previous one + /// ([_isBuffering]), this dispatches a [VideoEvent]. + @visibleForTesting + void setBuffering(bool buffering) { + if (_isBuffering != buffering) { + _isBuffering = buffering; + _eventController.add(VideoEvent( + eventType: _isBuffering + ? VideoEventType.bufferingStart + : VideoEventType.bufferingEnd, + )); + } + } + + // Broadcasts the [html.VideoElement.buffered] status through the [events] stream. + void _sendBufferingRangesUpdate() { + _eventController.add(VideoEvent( + buffered: _toDurationRange(_videoElement.buffered), + eventType: VideoEventType.bufferingUpdate, + )); + } + + // Converts from [html.TimeRanges] to our own List. + List _toDurationRange(html.TimeRanges buffered) { + final List durationRange = []; + for (int i = 0; i < buffered.length; i++) { + durationRange.add(DurationRange( + Duration(milliseconds: (buffered.start(i) * 1000).round()), + Duration(milliseconds: (buffered.end(i) * 1000).round()), + )); + } + return durationRange; + } +} diff --git a/packages/video_player/video_player_web/lib/video_player_web.dart b/packages/video_player/video_player_web/lib/video_player_web.dart index a676850f3488..e52fd83de79e 100644 --- a/packages/video_player/video_player_web/lib/video_player_web.dart +++ b/packages/video_player/video_player_web/lib/video_player_web.dart @@ -6,34 +6,11 @@ import 'dart:async'; import 'dart:html'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; import 'src/shims/dart_ui.dart' as ui; - -// An error code value to error name Map. -// See: https://developer.mozilla.org/en-US/docs/Web/API/MediaError/code -const Map _kErrorValueToErrorName = { - 1: 'MEDIA_ERR_ABORTED', - 2: 'MEDIA_ERR_NETWORK', - 3: 'MEDIA_ERR_DECODE', - 4: 'MEDIA_ERR_SRC_NOT_SUPPORTED', -}; - -// An error code value to description Map. -// See: https://developer.mozilla.org/en-US/docs/Web/API/MediaError/code -const Map _kErrorValueToErrorDescription = { - 1: 'The user canceled the fetching of the video.', - 2: 'A network error occurred while fetching the video, despite having previously been available.', - 3: 'An error occurred while trying to decode the video, despite having previously been determined to be usable.', - 4: 'The video has been found to be unsuitable (missing or in a format not supported by your browser).', -}; - -// The default error message, when the error is an empty string -// See: https://developer.mozilla.org/en-US/docs/Web/API/MediaError/message -const String _kDefaultErrorMessage = - 'No further diagnostic information can be determined or provided.'; +import 'src/video_player.dart'; /// The web implementation of [VideoPlayerPlatform]. /// @@ -44,8 +21,10 @@ class VideoPlayerPlugin extends VideoPlayerPlatform { VideoPlayerPlatform.instance = VideoPlayerPlugin(); } - final Map _videoPlayers = {}; + // Map of textureId -> VideoPlayer instances + final Map _videoPlayers = {}; + // Simulate the native "textureId". int _textureCounter = 1; @override @@ -55,13 +34,13 @@ class VideoPlayerPlugin extends VideoPlayerPlatform { @override Future dispose(int textureId) async { - _videoPlayers[textureId]!.dispose(); + _player(textureId).dispose(); _videoPlayers.remove(textureId); return; } void _disposeAllPlayers() { - for (final _VideoPlayer videoPlayer in _videoPlayers.values) { + for (final VideoPlayer videoPlayer in _videoPlayers.values) { videoPlayer.dispose(); } _videoPlayers.clear(); @@ -69,8 +48,7 @@ class VideoPlayerPlugin extends VideoPlayerPlatform { @override Future create(DataSource dataSource) async { - final int textureId = _textureCounter; - _textureCounter++; + final int textureId = _textureCounter++; late String uri; switch (dataSource.sourceType) { @@ -95,58 +73,69 @@ class VideoPlayerPlugin extends VideoPlayerPlatform { 'web implementation of video_player cannot play content uri')); } - final _VideoPlayer player = _VideoPlayer( - uri: uri, - textureId: textureId, - ); + final VideoElement videoElement = VideoElement() + ..id = 'videoElement-$textureId' + ..src = uri + ..style.border = 'none' + ..style.height = '100%' + ..style.width = '100%'; - player.initialize(); + // TODO(hterkelsen): Use initialization parameters once they are available + ui.platformViewRegistry.registerViewFactory( + 'videoPlayer-$textureId', (int viewId) => videoElement); + + final VideoPlayer player = VideoPlayer(videoElement: videoElement) + ..initialize(); _videoPlayers[textureId] = player; + return textureId; } @override Future setLooping(int textureId, bool looping) async { - return _videoPlayers[textureId]!.setLooping(looping); + return _player(textureId).setLooping(looping); } @override Future play(int textureId) async { - return _videoPlayers[textureId]!.play(); + return _player(textureId).play(); } @override Future pause(int textureId) async { - return _videoPlayers[textureId]!.pause(); + return _player(textureId).pause(); } @override Future setVolume(int textureId, double volume) async { - return _videoPlayers[textureId]!.setVolume(volume); + return _player(textureId).setVolume(volume); } @override Future setPlaybackSpeed(int textureId, double speed) async { - assert(speed > 0); - - return _videoPlayers[textureId]!.setPlaybackSpeed(speed); + return _player(textureId).setPlaybackSpeed(speed); } @override Future seekTo(int textureId, Duration position) async { - return _videoPlayers[textureId]!.seekTo(position); + return _player(textureId).seekTo(position); } @override Future getPosition(int textureId) async { - _videoPlayers[textureId]!.sendBufferingUpdate(); - return _videoPlayers[textureId]!.getPosition(); + return _player(textureId).getPosition(); } @override Stream videoEventsFor(int textureId) { - return _videoPlayers[textureId]!.eventController.stream; + return _player(textureId).events; + } + + // Retrieves a [VideoPlayer] by its internal `id`. + // It must have been created earlier from the [create] method. + VideoPlayer _player(int id) { + return _videoPlayers[id]!; } @override @@ -158,171 +147,3 @@ class VideoPlayerPlugin extends VideoPlayerPlatform { @override Future setMixWithOthers(bool mixWithOthers) => Future.value(); } - -class _VideoPlayer { - _VideoPlayer({required this.uri, required this.textureId}); - - final StreamController eventController = - StreamController(); - - final String uri; - final int textureId; - late VideoElement videoElement; - bool isInitialized = false; - bool isBuffering = false; - - void setBuffering(bool buffering) { - if (isBuffering != buffering) { - isBuffering = buffering; - eventController.add(VideoEvent( - eventType: isBuffering - ? VideoEventType.bufferingStart - : VideoEventType.bufferingEnd)); - } - } - - void initialize() { - videoElement = VideoElement() - ..src = uri - ..autoplay = false - ..controls = false - ..style.border = 'none' - ..style.height = '100%' - ..style.width = '100%'; - - // Allows Safari iOS to play the video inline - videoElement.setAttribute('playsinline', 'true'); - - // Set autoplay to false since most browsers won't autoplay a video unless it is muted - videoElement.setAttribute('autoplay', 'false'); - - // TODO(hterkelsen): Use initialization parameters once they are available - ui.platformViewRegistry.registerViewFactory( - 'videoPlayer-$textureId', (int viewId) => videoElement); - - videoElement.onCanPlay.listen((dynamic _) { - if (!isInitialized) { - isInitialized = true; - sendInitialized(); - } - setBuffering(false); - }); - - videoElement.onCanPlayThrough.listen((dynamic _) { - setBuffering(false); - }); - - videoElement.onPlaying.listen((dynamic _) { - setBuffering(false); - }); - - videoElement.onWaiting.listen((dynamic _) { - setBuffering(true); - sendBufferingUpdate(); - }); - - // The error event fires when some form of error occurs while attempting to load or perform the media. - videoElement.onError.listen((Event _) { - setBuffering(false); - // The Event itself (_) doesn't contain info about the actual error. - // We need to look at the HTMLMediaElement.error. - // See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/error - final MediaError error = videoElement.error!; - eventController.addError(PlatformException( - code: _kErrorValueToErrorName[error.code]!, - message: error.message != '' ? error.message : _kDefaultErrorMessage, - details: _kErrorValueToErrorDescription[error.code], - )); - }); - - videoElement.onEnded.listen((dynamic _) { - setBuffering(false); - eventController.add(VideoEvent(eventType: VideoEventType.completed)); - }); - } - - void sendBufferingUpdate() { - eventController.add(VideoEvent( - buffered: _toDurationRange(videoElement.buffered), - eventType: VideoEventType.bufferingUpdate, - )); - } - - Future play() { - return videoElement.play().catchError((Object e) { - // play() attempts to begin playback of the media. It returns - // a Promise which can get rejected in case of failure to begin - // playback for any reason, such as permission issues. - // The rejection handler is called with a DomException. - // See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play - final DomException exception = e as DomException; - eventController.addError(PlatformException( - code: exception.name, - message: exception.message, - )); - }, test: (Object e) => e is DomException); - } - - void pause() { - videoElement.pause(); - } - - void setLooping(bool value) { - videoElement.loop = value; - } - - void setVolume(double value) { - // TODO(ditman): Do we need to expose a "muted" API? https://github.com/flutter/flutter/issues/60721 - if (value > 0.0) { - videoElement.muted = false; - } else { - videoElement.muted = true; - } - videoElement.volume = value; - } - - void setPlaybackSpeed(double speed) { - assert(speed > 0); - - videoElement.playbackRate = speed; - } - - void seekTo(Duration position) { - videoElement.currentTime = position.inMilliseconds.toDouble() / 1000; - } - - Duration getPosition() { - return Duration(milliseconds: (videoElement.currentTime * 1000).round()); - } - - void sendInitialized() { - eventController.add( - VideoEvent( - eventType: VideoEventType.initialized, - duration: Duration( - milliseconds: (videoElement.duration * 1000).round(), - ), - size: Size( - videoElement.videoWidth.toDouble(), - videoElement.videoHeight.toDouble(), - ), - ), - ); - } - - void dispose() { - videoElement.removeAttribute('src'); - videoElement.load(); - } - - List _toDurationRange(TimeRanges buffered) { - final List durationRange = []; - for (int i = 0; i < buffered.length; i++) { - durationRange.add(DurationRange( - Duration(milliseconds: (buffered.start(i) * 1000).round()), - Duration(milliseconds: (buffered.end(i) * 1000).round()), - )); - } - return durationRange; - } -} diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml index 69a2df4e99e4..064517e1f264 100644 --- a/packages/video_player/video_player_web/pubspec.yaml +++ b/packages/video_player/video_player_web/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_web description: Web platform implementation of video_player. repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.0.7 +version: 2.0.8 environment: sdk: ">=2.12.0 <3.0.0" From 788d7e299193af8418d250806dc8c337f01706ab Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 18 Apr 2022 16:09:09 -0400 Subject: [PATCH 414/600] Roll Flutter from f4875ae865e9 to 3c4d7a1aed65 (2 revisions) (#5292) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 048f3562ae90..c91967b89b46 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -f4875ae865e9d42644a93bbab2164fc7baac4ff7 +3c4d7a1aed650f75ea0900a466bc2e10e362fb32 From 26e1d673de594bfcce4a4f54aae36bca920526fe Mon Sep 17 00:00:00 2001 From: Emmanuel Garcia Date: Mon, 18 Apr 2022 16:29:09 -0700 Subject: [PATCH 415/600] Add owners for Android implementations (#5293) --- CODEOWNERS | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index 88ba1f575a4c..0ce86cc085c4 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -9,4 +9,18 @@ packages/webview_flutter/** @bparrishMines # Sub-package-level rules. These should stay last, since the last matching # entry takes precedence. -packages/**/*_web/** @ditman + +# - Web +packages/**/*_web/** @ditman + +# - Android +packages/camera/android/** @camsim99 +packages/google_maps_flutter/android/** @GaryQian +packages/google_sign_in/google_sign_in_android/** @camsim99 +packages/image_picker/image_picker_android/** @GaryQian +packages/in_app_purchase/in_app_purchase_android/** @blasten +packages/local_auth/local_auth_android/** @blasten +packages/path_provider/path_provider_android/** @camsim99 +packages/quick_actions/quick_actions_android/** @camsim99 +packages/url_launcher/url_launcher_android/** @GaryQian +packages/video_player/video_player_android/** @blasten From 357d05ad154bc4d4e240cced1bfbda123d3ac436 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 18 Apr 2022 21:44:10 -0400 Subject: [PATCH 416/600] Roll Flutter from 3c4d7a1aed65 to 3752fb7bd15e (11 revisions) (#5294) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index c91967b89b46..d475c20f7e3a 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -3c4d7a1aed650f75ea0900a466bc2e10e362fb32 +3752fb7bd15e72215cb9dc40086816e6ff7d5e7d From 4a59768fc17d697ea9fc2060d9383da367564c86 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 19 Apr 2022 00:29:08 -0400 Subject: [PATCH 417/600] Roll Flutter from 3752fb7bd15e to fd360c4a1d24 (2 revisions) (#5295) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index d475c20f7e3a..27faac63c443 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -3752fb7bd15e72215cb9dc40086816e6ff7d5e7d +fd360c4a1d2418ad2d1ad83f7b5cc125696837f9 From 06257ddbf67e902fdc4a42155ce10100c9e9f676 Mon Sep 17 00:00:00 2001 From: Alex Li Date: Tue, 19 Apr 2022 13:09:05 +0800 Subject: [PATCH 418/600] [video_player_avfoundation] Applies the standardized transform for videos with different orientations (#5069) --- .../video_player_avfoundation/CHANGELOG.md | 4 + .../ios/RunnerTests/VideoPlayerTests.m | 93 +++++++++++++++++++ .../ios/Classes/AVAssetTrackUtils.h | 14 +++ .../ios/Classes/AVAssetTrackUtils.m | 46 +++++++++ .../ios/Classes/FLTVideoPlayerPlugin.m | 28 +----- .../video_player_avfoundation/pubspec.yaml | 2 +- 6 files changed, 162 insertions(+), 25 deletions(-) create mode 100644 packages/video_player/video_player_avfoundation/ios/Classes/AVAssetTrackUtils.h create mode 100644 packages/video_player/video_player_avfoundation/ios/Classes/AVAssetTrackUtils.m diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index c36c9878d1a1..a1f23eb670fc 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.2 + +* Applies the standardized transform for videos with different orientations. + ## 2.3.1 * Renames internal method channels to avoid potential confusion with the diff --git a/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m b/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m index cbf2866aa071..7decd04bd168 100644 --- a/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m +++ b/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m @@ -7,6 +7,7 @@ @import XCTest; #import +#import @interface FLTVideoPlayer : NSObject @property(readonly, nonatomic) AVPlayer *player; @@ -17,6 +18,44 @@ @interface FLTVideoPlayerPlugin (Test) NSMutableDictionary *playersByTextureId; @end +@interface FakeAVAssetTrack : AVAssetTrack +@property(readonly, nonatomic) CGAffineTransform preferredTransform; +@property(readonly, nonatomic) CGSize naturalSize; +@property(readonly, nonatomic) UIImageOrientation orientation; +- (instancetype)initWithOrientation:(UIImageOrientation)orientation; +@end + +@implementation FakeAVAssetTrack + +- (instancetype)initWithOrientation:(UIImageOrientation)orientation { + _orientation = orientation; + _naturalSize = CGSizeMake(800, 600); + return self; +} + +- (CGAffineTransform)preferredTransform { + switch (_orientation) { + case UIImageOrientationUp: + return CGAffineTransformMake(1, 0, 0, 1, 0, 0); + case UIImageOrientationDown: + return CGAffineTransformMake(-1, 0, 0, -1, 0, 0); + case UIImageOrientationLeft: + return CGAffineTransformMake(0, -1, 1, 0, 0, 0); + case UIImageOrientationRight: + return CGAffineTransformMake(0, 1, -1, 0, 0, 0); + case UIImageOrientationUpMirrored: + return CGAffineTransformMake(-1, 0, 0, 1, 0, 0); + case UIImageOrientationDownMirrored: + return CGAffineTransformMake(1, 0, 0, -1, 0, 0); + case UIImageOrientationLeftMirrored: + return CGAffineTransformMake(0, -1, -1, 0, 0, 0); + case UIImageOrientationRightMirrored: + return CGAffineTransformMake(0, 1, 1, 0, 0, 0); + } +} + +@end + @interface VideoPlayerTests : XCTestCase @end @@ -121,6 +160,17 @@ - (void)testHLSControls { XCTAssertEqualWithAccuracy([videoInitialization[@"duration"] intValue], 4000, 200); } +- (void)testTransformFix { + [self validateTransformFixForOrientation:UIImageOrientationUp]; + [self validateTransformFixForOrientation:UIImageOrientationDown]; + [self validateTransformFixForOrientation:UIImageOrientationLeft]; + [self validateTransformFixForOrientation:UIImageOrientationRight]; + [self validateTransformFixForOrientation:UIImageOrientationUpMirrored]; + [self validateTransformFixForOrientation:UIImageOrientationDownMirrored]; + [self validateTransformFixForOrientation:UIImageOrientationLeftMirrored]; + [self validateTransformFixForOrientation:UIImageOrientationRightMirrored]; +} + - (NSDictionary *)testPlugin:(FLTVideoPlayerPlugin *)videoPlayerPlugin uri:(NSString *)uri { FlutterError *error; @@ -175,4 +225,47 @@ - (void)testHLSControls { return initializationEvent; } +- (void)validateTransformFixForOrientation:(UIImageOrientation)orientation { + AVAssetTrack *track = [[FakeAVAssetTrack alloc] initWithOrientation:orientation]; + CGAffineTransform t = FLTGetStandardizedTransformForTrack(track); + CGSize size = track.naturalSize; + CGFloat expectX, expectY; + switch (orientation) { + case UIImageOrientationUp: + expectX = 0; + expectY = 0; + break; + case UIImageOrientationDown: + expectX = size.width; + expectY = size.height; + break; + case UIImageOrientationLeft: + expectX = 0; + expectY = size.width; + break; + case UIImageOrientationRight: + expectX = size.height; + expectY = 0; + break; + case UIImageOrientationUpMirrored: + expectX = size.width; + expectY = 0; + break; + case UIImageOrientationDownMirrored: + expectX = 0; + expectY = size.height; + break; + case UIImageOrientationLeftMirrored: + expectX = size.height; + expectY = size.width; + break; + case UIImageOrientationRightMirrored: + expectX = 0; + expectY = 0; + break; + } + XCTAssertEqual(t.tx, expectX); + XCTAssertEqual(t.ty, expectY); +} + @end diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/AVAssetTrackUtils.h b/packages/video_player/video_player_avfoundation/ios/Classes/AVAssetTrackUtils.h new file mode 100644 index 000000000000..9d736bc21afe --- /dev/null +++ b/packages/video_player/video_player_avfoundation/ios/Classes/AVAssetTrackUtils.h @@ -0,0 +1,14 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +/** + * Returns a standardized transform + * according to the orientation of the track. + * + * Note: https://stackoverflow.com/questions/64161544 + * `AVAssetTrack.preferredTransform` can have wrong `tx` and `ty`. + */ +CGAffineTransform FLTGetStandardizedTransformForTrack(AVAssetTrack* track); diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/AVAssetTrackUtils.m b/packages/video_player/video_player_avfoundation/ios/Classes/AVAssetTrackUtils.m new file mode 100644 index 000000000000..de75859a94a4 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/ios/Classes/AVAssetTrackUtils.m @@ -0,0 +1,46 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +CGAffineTransform FLTGetStandardizedTransformForTrack(AVAssetTrack *track) { + CGAffineTransform t = track.preferredTransform; + CGSize size = track.naturalSize; + // Each case of control flows corresponds to a specific + // `UIImageOrientation`, with 8 cases in total. + if (t.a == 1 && t.b == 0 && t.c == 0 && t.d == 1) { + // UIImageOrientationUp + t.tx = 0; + t.ty = 0; + } else if (t.a == -1 && t.b == 0 && t.c == 0 && t.d == -1) { + // UIImageOrientationDown + t.tx = size.width; + t.ty = size.height; + } else if (t.a == 0 && t.b == -1 && t.c == 1 && t.d == 0) { + // UIImageOrientationLeft + t.tx = 0; + t.ty = size.width; + } else if (t.a == 0 && t.b == 1 && t.c == -1 && t.d == 0) { + // UIImageOrientationRight + t.tx = size.height; + t.ty = 0; + } else if (t.a == -1 && t.b == 0 && t.c == 0 && t.d == 1) { + // UIImageOrientationUpMirrored + t.tx = size.width; + t.ty = 0; + } else if (t.a == 1 && t.b == 0 && t.c == 0 && t.d == -1) { + // UIImageOrientationDownMirrored + t.tx = 0; + t.ty = size.height; + } else if (t.a == 0 && t.b == -1 && t.c == -1 && t.d == 0) { + // UIImageOrientationLeftMirrored + t.tx = size.height; + t.ty = size.width; + } else if (t.a == 0 && t.b == 1 && t.c == 1 && t.d == 0) { + // UIImageOrientationRightMirrored + t.tx = 0; + t.ty = 0; + } + return t; +} diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m index 2fb9f7f55600..a95779b1cbab 100644 --- a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m +++ b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m @@ -3,8 +3,11 @@ // found in the LICENSE file. #import "FLTVideoPlayerPlugin.h" + #import #import + +#import "AVAssetTrackUtils.h" #import "messages.g.h" #if !__has_feature(objc_arc) @@ -187,29 +190,6 @@ - (instancetype)initWithURL:(NSURL *)url return [self initWithPlayerItem:item frameUpdater:frameUpdater]; } -- (CGAffineTransform)fixTransform:(AVAssetTrack *)videoTrack { - CGAffineTransform transform = videoTrack.preferredTransform; - // TODO(@recastrodiaz): why do we need to do this? Why is the preferredTransform incorrect? - // At least 2 user videos show a black screen when in portrait mode if we directly use the - // videoTrack.preferredTransform Setting tx to the height of the video instead of 0, properly - // displays the video https://github.com/flutter/flutter/issues/17606#issuecomment-413473181 - if (transform.tx == 0 && transform.ty == 0) { - NSInteger rotationDegrees = (NSInteger)round(radiansToDegrees(atan2(transform.b, transform.a))); - NSLog(@"TX and TY are 0. Rotation: %ld. Natural width,height: %f, %f", (long)rotationDegrees, - videoTrack.naturalSize.width, videoTrack.naturalSize.height); - if (rotationDegrees == 90) { - NSLog(@"Setting transform tx"); - transform.tx = videoTrack.naturalSize.height; - transform.ty = 0; - } else if (rotationDegrees == 270) { - NSLog(@"Setting transform ty"); - transform.tx = 0; - transform.ty = videoTrack.naturalSize.width; - } - } - return transform; -} - - (instancetype)initWithPlayerItem:(AVPlayerItem *)item frameUpdater:(FLTFrameUpdater *)frameUpdater { self = [super init]; @@ -226,7 +206,7 @@ - (instancetype)initWithPlayerItem:(AVPlayerItem *)item if ([videoTrack statusOfValueForKey:@"preferredTransform" error:nil] == AVKeyValueStatusLoaded) { // Rotate the video by using a videoComposition and the preferredTransform - self->_preferredTransform = [self fixTransform:videoTrack]; + self->_preferredTransform = FLTGetStandardizedTransformForTrack(videoTrack); // Note: // https://developer.apple.com/documentation/avfoundation/avplayeritem/1388818-videocomposition // Video composition can only be used with file-based media and is not supported for diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index 0e42c26de829..5874b52cba6f 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_avfoundation description: iOS implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.1 +version: 2.3.2 environment: sdk: ">=2.14.0 <3.0.0" From 8084ac1f363aa910ed0af64b42610df909d83ee3 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 19 Apr 2022 08:24:09 -0400 Subject: [PATCH 419/600] Roll Flutter from fd360c4a1d24 to ef5a6da35a72 (1 revision) (#5298) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 27faac63c443..668e05f55a1d 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -fd360c4a1d2418ad2d1ad83f7b5cc125696837f9 +ef5a6da35a72dcd89cabb06f4e075801ea260224 From 3e1f7acf2c0b29cdc630b3702457ae169e157128 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 19 Apr 2022 11:44:12 -0400 Subject: [PATCH 420/600] Roll Flutter from ef5a6da35a72 to eb54cefb97fa (1 revision) (#5299) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 668e05f55a1d..35799ab67c50 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ef5a6da35a72dcd89cabb06f4e075801ea260224 +eb54cefb97fa5f712f80e3a2bdeb0baa6542c4eb From a9d2381908cf7adf899d0fcec727f9968fbac01e Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 19 Apr 2022 12:49:11 -0400 Subject: [PATCH 421/600] Roll Flutter from eb54cefb97fa to c2ec2426ac39 (1 revision) (#5300) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 35799ab67c50..c714466df2d2 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -eb54cefb97fa5f712f80e3a2bdeb0baa6542c4eb +c2ec2426ac39f89e0d09f7f2b549033557eb398f From 659ee36c8ff8894f33b3c9c8368fddf994df1f23 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 19 Apr 2022 13:54:18 -0400 Subject: [PATCH 422/600] Roll Flutter from c2ec2426ac39 to 220ffc77f375 (1 revision) (#5302) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index c714466df2d2..61e76583b2f6 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c2ec2426ac39f89e0d09f7f2b549033557eb398f +220ffc77f3752ed9d59e66babddbe33527422e1b From 65d1f52cef095764967ee93daf22eadfe3dac9cd Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 19 Apr 2022 16:04:10 -0400 Subject: [PATCH 423/600] Roll Flutter from 220ffc77f375 to ce6188bef15c (5 revisions) (#5304) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 61e76583b2f6..8d4ed0152920 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -220ffc77f3752ed9d59e66babddbe33527422e1b +ce6188bef15c468cfd87e183b0ee14eed1a21c2d From fb531ede7f05ba53391e049f3b812caa8eb67d5a Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 19 Apr 2022 17:14:11 -0400 Subject: [PATCH 424/600] Roll Flutter from ce6188bef15c to fecc8904ff8c (1 revision) (#5305) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 8d4ed0152920..550dc4c4a068 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ce6188bef15c468cfd87e183b0ee14eed1a21c2d +fecc8904ff8cbe6d59a91eb85a9957884ea89b96 From 6e18c7195678f5f2ff12e79a5fe738280a66fe26 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 19 Apr 2022 18:19:11 -0400 Subject: [PATCH 425/600] Roll Flutter from fecc8904ff8c to 9e4f2650dbf9 (2 revisions) (#5307) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 550dc4c4a068..ee5ede6ecf5b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -fecc8904ff8cbe6d59a91eb85a9957884ea89b96 +9e4f2650dbf93295ee3a6cc8409375fc3ebb8bcc From 1d9f3d35a533ded6ebf05a3f36ab43c76d535326 Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Wed, 20 Apr 2022 08:39:10 -0700 Subject: [PATCH 426/600] Add iOS path CODEOWNERS (#5306) --- CODEOWNERS | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 0ce86cc085c4..fdf0e30550f7 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -14,8 +14,8 @@ packages/webview_flutter/** @bparrishMines packages/**/*_web/** @ditman # - Android -packages/camera/android/** @camsim99 -packages/google_maps_flutter/android/** @GaryQian +packages/camera/camera/android/** @camsim99 +packages/google_maps_flutter/google_maps_flutter/android/** @GaryQian packages/google_sign_in/google_sign_in_android/** @camsim99 packages/image_picker/image_picker_android/** @GaryQian packages/in_app_purchase/in_app_purchase_android/** @blasten @@ -24,3 +24,18 @@ packages/path_provider/path_provider_android/** @camsim99 packages/quick_actions/quick_actions_android/** @camsim99 packages/url_launcher/url_launcher_android/** @GaryQian packages/video_player/video_player_android/** @blasten + +# - iOS +packages/camera/camera/ios/** @hellohuanlin +packages/google_maps_flutter/google_maps_flutter/ios/** @cyanglaz +packages/google_sign_in/google_sign_in_ios/** @jmagman +packages/image_picker/image_picker_ios/** @cyanglaz +packages/in_app_purchase/in_app_purchase_storekit/** @cyanglaz +packages/ios_platform_images/ios/** @jmagman +packages/local_auth/local_auth_ios/** @hellohuanlin +packages/path_provider/path_provider_ios/** @jmagman +packages/quick_actions/quick_actions_ios/** @hellohuanlin +packages/shared_preferences/shared_preferences_ios/** @cyanglaz +packages/url_launcher/url_launcher_ios/** @jmagman +packages/video_player/video_player_avfoundation/** @hellohuanlin +packages/webview_flutter/webview_flutter_wkwebview/** @cyanglaz From 0829ecf67722f2724a6cf950ea06a7fbf9f77650 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 20 Apr 2022 12:29:11 -0400 Subject: [PATCH 427/600] Roll Flutter from 9e4f2650dbf9 to b9f1a907b1aa (2 revisions) (#5308) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index ee5ede6ecf5b..bd294fffd991 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -9e4f2650dbf93295ee3a6cc8409375fc3ebb8bcc +b9f1a907b1aa5f13d0443cecb7e9f851b153c11f From 102cf1bb6308e7560a04aebf854a5ceb546639ea Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 20 Apr 2022 15:29:09 -0400 Subject: [PATCH 428/600] Roll Flutter from b9f1a907b1aa to e526f859ef80 (10 revisions) (#5311) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index bd294fffd991..ade3095c4732 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b9f1a907b1aa5f13d0443cecb7e9f851b153c11f +e526f859ef8061690828762cb779ea29d6cd4981 From f29c36c952312123c4d52af5b1e955f096b11c57 Mon Sep 17 00:00:00 2001 From: Alex Furaiev <33546696+furaiev@users.noreply.github.com> Date: Wed, 20 Apr 2022 23:14:09 +0300 Subject: [PATCH 429/600] [local_auth] support localizedFallbackTitle in IOSAuthMessages (#5297) --- .../local_auth/local_auth_ios/CHANGELOG.md | 4 ++++ .../ios/Classes/FLTLocalAuthPlugin.m | 1 + .../lib/types/auth_messages_ios.dart | 13 +++++++++-- .../local_auth/local_auth_ios/pubspec.yaml | 2 +- .../local_auth_ios/test/local_auth_test.dart | 23 +++++++++++++++++++ 5 files changed, 40 insertions(+), 3 deletions(-) diff --git a/packages/local_auth/local_auth_ios/CHANGELOG.md b/packages/local_auth/local_auth_ios/CHANGELOG.md index d826cc8032e5..ccacae986d51 100644 --- a/packages/local_auth/local_auth_ios/CHANGELOG.md +++ b/packages/local_auth/local_auth_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.2 + +* Adds support `localizedFallbackTitle` in authenticateWithBiometrics on iOS. + ## 1.0.1 * BREAKING CHANGE: Changes `stopAuthentication` to always return false instead of throwing an error. diff --git a/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m b/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m index d4d0e0d0f79f..ffd1dd2a869d 100644 --- a/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m +++ b/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m @@ -181,6 +181,7 @@ - (void)handleAuthReplyWithSuccess:(BOOL)success case LAErrorTouchIDNotAvailable: case LAErrorTouchIDNotEnrolled: case LAErrorTouchIDLockout: + case LAErrorUserFallback: [self handleErrors:error flutterArguments:arguments withFlutterResult:result]; return; case LAErrorSystemCancel: diff --git a/packages/local_auth/local_auth_ios/lib/types/auth_messages_ios.dart b/packages/local_auth/local_auth_ios/lib/types/auth_messages_ios.dart index 8a776243d242..b3236a7ff66e 100644 --- a/packages/local_auth/local_auth_ios/lib/types/auth_messages_ios.dart +++ b/packages/local_auth/local_auth_ios/lib/types/auth_messages_ios.dart @@ -16,6 +16,7 @@ class IOSAuthMessages extends AuthMessages { this.goToSettingsButton, this.goToSettingsDescription, this.cancelButton, + this.localizedFallbackTitle, }); /// Message advising the user to re-enable biometrics on their device. @@ -35,6 +36,10 @@ class IOSAuthMessages extends AuthMessages { /// Maximum 30 characters. final String? cancelButton; + /// The localized title for the fallback button in the dialog presented to + /// the user during authentication. + final String? localizedFallbackTitle; + @override Map get args { return { @@ -43,6 +48,8 @@ class IOSAuthMessages extends AuthMessages { 'goToSettingDescriptionIOS': goToSettingsDescription ?? iOSGoToSettingsDescription, 'okButton': cancelButton ?? iOSOkButton, + if (localizedFallbackTitle != null) + 'localizedFallbackTitle': localizedFallbackTitle!, }; } @@ -54,14 +61,16 @@ class IOSAuthMessages extends AuthMessages { lockOut == other.lockOut && goToSettingsButton == other.goToSettingsButton && goToSettingsDescription == other.goToSettingsDescription && - cancelButton == other.cancelButton; + cancelButton == other.cancelButton && + localizedFallbackTitle == other.localizedFallbackTitle; @override int get hashCode => lockOut.hashCode ^ goToSettingsButton.hashCode ^ goToSettingsDescription.hashCode ^ - cancelButton.hashCode; + cancelButton.hashCode ^ + localizedFallbackTitle.hashCode; } // Default Strings for IOSAuthMessages plugin. Currently supports English. diff --git a/packages/local_auth/local_auth_ios/pubspec.yaml b/packages/local_auth/local_auth_ios/pubspec.yaml index 537b1a64d003..9e69dc7a6617 100644 --- a/packages/local_auth/local_auth_ios/pubspec.yaml +++ b/packages/local_auth/local_auth_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_ios description: iOS implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.1 +version: 1.0.2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_ios/test/local_auth_test.dart b/packages/local_auth/local_auth_ios/test/local_auth_test.dart index 180383bc258c..aa94b8aa48b8 100644 --- a/packages/local_auth/local_auth_ios/test/local_auth_test.dart +++ b/packages/local_auth/local_auth_ios/test/local_auth_test.dart @@ -135,6 +135,29 @@ void main() { ); }); + test('authenticate with `localizedFallbackTitle`', () async { + await localAuthentication.authenticate( + authMessages: [ + const IOSAuthMessages(localizedFallbackTitle: 'Enter PIN'), + ], + localizedReason: 'Needs secure', + ); + expect( + log, + [ + isMethodCall('authenticate', + arguments: { + 'localizedReason': 'Needs secure', + 'useErrorDialogs': true, + 'stickyAuth': false, + 'sensitiveTransaction': true, + 'biometricOnly': false, + 'localizedFallbackTitle': 'Enter PIN', + }..addAll(const IOSAuthMessages().args)), + ], + ); + }); + test('authenticate with no sensitive transaction.', () async { await localAuthentication.authenticate( authMessages: [const IOSAuthMessages()], From d4615a85a7454d158577cf2a2e64c8ee165d7bcf Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 21 Apr 2022 00:44:15 -0400 Subject: [PATCH 430/600] Roll Flutter from e526f859ef80 to fce3cee7763c (4 revisions) (#5313) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index ade3095c4732..c6d41cf2d8dd 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -e526f859ef8061690828762cb779ea29d6cd4981 +fce3cee7763c601648e1d98027d0a4aa85f22ebd From e67d151b37b880472daac99ee0ceb7621c858125 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 21 Apr 2022 02:54:09 -0400 Subject: [PATCH 431/600] Roll Flutter from fce3cee7763c to 3fefb2d5eb5f (8 revisions) (#5317) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index c6d41cf2d8dd..8e38ba79e6c4 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -fce3cee7763c601648e1d98027d0a4aa85f22ebd +3fefb2d5eb5f807ad2046267054f1d3a4d6bea74 From 5c3c1b8aac065aa1988f114d0c66ecec758e9cbb Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 21 Apr 2022 03:59:12 -0400 Subject: [PATCH 432/600] Roll Flutter from 3fefb2d5eb5f to 0dccb58a73dd (1 revision) (#5318) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 8e38ba79e6c4..627a0ce88e85 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -3fefb2d5eb5f807ad2046267054f1d3a4d6bea74 +0dccb58a73dd0448b3cc4b24396b61ddcce4f861 From 8ff316f7d49c8a6a512c98a6c95f480767e3218e Mon Sep 17 00:00:00 2001 From: Navaron Bracke Date: Thu, 21 Apr 2022 19:49:13 +0200 Subject: [PATCH 433/600] [camera] Fix the orientation of videos recorded in landscape on Android (#4946) --- packages/camera/camera/CHANGELOG.md | 4 +++ .../DeviceOrientationManager.java | 22 ++++++++++------ .../DeviceOrientationManagerTest.java | 26 ++++++++++++++----- packages/camera/camera/pubspec.yaml | 2 +- 4 files changed, 38 insertions(+), 16 deletions(-) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index cf502221efa0..b86443249dad 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.4+20 + +* Fixes an issue with the orientation of videos recorded in landscape on Android. + ## 0.9.4+19 * Migrate deprecated Scaffold SnackBar methods to ScaffoldMessenger. diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManager.java b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManager.java index dd1e489e6225..ec6fa13dbd1d 100644 --- a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManager.java +++ b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManager.java @@ -142,26 +142,32 @@ public int getPhotoOrientation(PlatformChannel.DeviceOrientation orientation) { } /** - * Returns the device's video orientation in degrees based on the sensor orientation and the last - * known UI orientation. + * Returns the device's video orientation in clockwise degrees based on the sensor orientation and + * the last known UI orientation. * *

Returns one of 0, 90, 180 or 270. * - * @return The device's video orientation in degrees. + * @return The device's video orientation in clockwise degrees. */ public int getVideoOrientation() { return this.getVideoOrientation(this.lastOrientation); } /** - * Returns the device's video orientation in degrees based on the sensor orientation and the - * supplied {@link PlatformChannel.DeviceOrientation} value. + * Returns the device's video orientation in clockwise degrees based on the sensor orientation and + * the supplied {@link PlatformChannel.DeviceOrientation} value. * *

Returns one of 0, 90, 180 or 270. * + *

More details can be found in the official Android documentation: + * https://developer.android.com/reference/android/media/MediaRecorder#setOrientationHint(int) + * + *

See also: + * https://developer.android.com/training/camera2/camera-preview-large-screens#orientation_calculation + * * @param orientation The {@link PlatformChannel.DeviceOrientation} value that is to be converted * into degrees. - * @return The device's video orientation in degrees. + * @return The device's video orientation in clockwise degrees. */ public int getVideoOrientation(PlatformChannel.DeviceOrientation orientation) { int angle = 0; @@ -179,10 +185,10 @@ public int getVideoOrientation(PlatformChannel.DeviceOrientation orientation) { angle = 180; break; case LANDSCAPE_LEFT: - angle = 90; + angle = 270; break; case LANDSCAPE_RIGHT: - angle = 270; + angle = 90; break; } diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManagerTest.java b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManagerTest.java index 82449a10188a..3762006f46d4 100644 --- a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManagerTest.java +++ b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManagerTest.java @@ -62,9 +62,9 @@ public void getVideoOrientation_whenNaturalScreenOrientationEqualsPortraitUp() { deviceOrientationManager.getVideoOrientation(DeviceOrientation.LANDSCAPE_RIGHT); assertEquals(0, degreesPortraitUp); - assertEquals(90, degreesLandscapeLeft); + assertEquals(270, degreesLandscapeLeft); assertEquals(180, degreesPortraitDown); - assertEquals(270, degreesLandscapeRight); + assertEquals(90, degreesLandscapeRight); } @Test @@ -81,18 +81,30 @@ public void getVideoOrientation_whenNaturalScreenOrientationEqualsLandscapeLeft( orientationManager.getVideoOrientation(DeviceOrientation.LANDSCAPE_RIGHT); assertEquals(90, degreesPortraitUp); - assertEquals(180, degreesLandscapeLeft); + assertEquals(0, degreesLandscapeLeft); assertEquals(270, degreesPortraitDown); - assertEquals(0, degreesLandscapeRight); + assertEquals(180, degreesLandscapeRight); } @Test - public void getVideoOrientation_shouldFallbackToSensorOrientationWhenOrientationIsNull() { - setUpUIOrientationMocks(Configuration.ORIENTATION_LANDSCAPE, Surface.ROTATION_0); + public void getVideoOrientation_fallbackToPortraitSensorOrientationWhenOrientationIsNull() { + setUpUIOrientationMocks(Configuration.ORIENTATION_PORTRAIT, Surface.ROTATION_0); int degrees = deviceOrientationManager.getVideoOrientation(null); - assertEquals(90, degrees); + assertEquals(0, degrees); + } + + @Test + public void getVideoOrientation_fallbackToLandscapeSensorOrientationWhenOrientationIsNull() { + setUpUIOrientationMocks(Configuration.ORIENTATION_LANDSCAPE, Surface.ROTATION_0); + + DeviceOrientationManager orientationManager = + DeviceOrientationManager.create(mockActivity, mockDartMessenger, false, 90); + + int degrees = orientationManager.getVideoOrientation(null); + + assertEquals(0, degrees); } @Test diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 2533583d9a6e..feb83f95cd8d 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+19 +version: 0.9.4+20 environment: sdk: ">=2.14.0 <3.0.0" From 92a1ffeda02e6a3cfbe1f588f43007706daf330c Mon Sep 17 00:00:00 2001 From: Alexandre Zollinger Chohfi Date: Thu, 21 Apr 2022 11:39:10 -0700 Subject: [PATCH 434/600] [image_picker] Added image_picker_for_windows. (#4863) --- .../image_picker/image_picker_windows/AUTHORS | 7 + .../image_picker_windows/CHANGELOG.md | 3 + .../image_picker/image_picker_windows/LICENSE | 25 ++ .../image_picker_windows/README.md | 16 + .../image_picker_windows/example/README.md | 8 + .../example/lib/main.dart | 414 ++++++++++++++++++ .../image_picker_windows/example/pubspec.yaml | 27 ++ .../example/windows/.gitignore | 17 + .../example/windows/CMakeLists.txt | 95 ++++ .../example/windows/flutter/CMakeLists.txt | 103 +++++ .../windows/flutter/generated_plugins.cmake | 16 + .../example/windows/runner/CMakeLists.txt | 17 + .../example/windows/runner/Runner.rc | 121 +++++ .../example/windows/runner/flutter_window.cpp | 65 +++ .../example/windows/runner/flutter_window.h | 37 ++ .../example/windows/runner/main.cpp | 46 ++ .../example/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../windows/runner/runner.exe.manifest | 20 + .../example/windows/runner/utils.cpp | 67 +++ .../example/windows/runner/utils.h | 23 + .../example/windows/runner/win32_window.cpp | 241 ++++++++++ .../example/windows/runner/win32_window.h | 99 +++++ .../lib/image_picker_windows.dart | 167 +++++++ .../image_picker_windows/pubspec.yaml | 29 ++ .../test/image_picker_windows_test.dart | 128 ++++++ .../test/image_picker_windows_test.mocks.dart | 78 ++++ script/configs/exclude_integration_win32.yaml | 1 + 28 files changed, 1886 insertions(+) create mode 100644 packages/image_picker/image_picker_windows/AUTHORS create mode 100644 packages/image_picker/image_picker_windows/CHANGELOG.md create mode 100644 packages/image_picker/image_picker_windows/LICENSE create mode 100644 packages/image_picker/image_picker_windows/README.md create mode 100644 packages/image_picker/image_picker_windows/example/README.md create mode 100644 packages/image_picker/image_picker_windows/example/lib/main.dart create mode 100644 packages/image_picker/image_picker_windows/example/pubspec.yaml create mode 100644 packages/image_picker/image_picker_windows/example/windows/.gitignore create mode 100644 packages/image_picker/image_picker_windows/example/windows/CMakeLists.txt create mode 100644 packages/image_picker/image_picker_windows/example/windows/flutter/CMakeLists.txt create mode 100644 packages/image_picker/image_picker_windows/example/windows/flutter/generated_plugins.cmake create mode 100644 packages/image_picker/image_picker_windows/example/windows/runner/CMakeLists.txt create mode 100644 packages/image_picker/image_picker_windows/example/windows/runner/Runner.rc create mode 100644 packages/image_picker/image_picker_windows/example/windows/runner/flutter_window.cpp create mode 100644 packages/image_picker/image_picker_windows/example/windows/runner/flutter_window.h create mode 100644 packages/image_picker/image_picker_windows/example/windows/runner/main.cpp create mode 100644 packages/image_picker/image_picker_windows/example/windows/runner/resource.h create mode 100644 packages/image_picker/image_picker_windows/example/windows/runner/resources/app_icon.ico create mode 100644 packages/image_picker/image_picker_windows/example/windows/runner/runner.exe.manifest create mode 100644 packages/image_picker/image_picker_windows/example/windows/runner/utils.cpp create mode 100644 packages/image_picker/image_picker_windows/example/windows/runner/utils.h create mode 100644 packages/image_picker/image_picker_windows/example/windows/runner/win32_window.cpp create mode 100644 packages/image_picker/image_picker_windows/example/windows/runner/win32_window.h create mode 100644 packages/image_picker/image_picker_windows/lib/image_picker_windows.dart create mode 100644 packages/image_picker/image_picker_windows/pubspec.yaml create mode 100644 packages/image_picker/image_picker_windows/test/image_picker_windows_test.dart create mode 100644 packages/image_picker/image_picker_windows/test/image_picker_windows_test.mocks.dart diff --git a/packages/image_picker/image_picker_windows/AUTHORS b/packages/image_picker/image_picker_windows/AUTHORS new file mode 100644 index 000000000000..5db3d584e6bc --- /dev/null +++ b/packages/image_picker/image_picker_windows/AUTHORS @@ -0,0 +1,7 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. +Alexandre Zollinger Chohfi \ No newline at end of file diff --git a/packages/image_picker/image_picker_windows/CHANGELOG.md b/packages/image_picker/image_picker_windows/CHANGELOG.md new file mode 100644 index 000000000000..c4a71a0176b5 --- /dev/null +++ b/packages/image_picker/image_picker_windows/CHANGELOG.md @@ -0,0 +1,3 @@ +# 0.1.0 + +* Initial Windows support. diff --git a/packages/image_picker/image_picker_windows/LICENSE b/packages/image_picker/image_picker_windows/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/image_picker/image_picker_windows/LICENSE @@ -0,0 +1,25 @@ +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/image_picker/image_picker_windows/README.md b/packages/image_picker/image_picker_windows/README.md new file mode 100644 index 000000000000..0b256411b2fc --- /dev/null +++ b/packages/image_picker/image_picker_windows/README.md @@ -0,0 +1,16 @@ +# image\_picker\_windows + +A Windows implementation of [`image_picker`][1]. + +### pickImage() +The arguments `source`, `maxWidth`, `maxHeight`, `imageQuality`, and `preferredCameraDevice` are not supported on Windows. + +### pickVideo() +The arguments `source`, `preferredCameraDevice`, and `maxDuration` are not supported on Windows. + +## Usage + +### Import the package + +This package is not yet [endorsed](https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin), which means you need to add +not only the `image_picker`, as well as the `image_picker_windows`. \ No newline at end of file diff --git a/packages/image_picker/image_picker_windows/example/README.md b/packages/image_picker/image_picker_windows/example/README.md new file mode 100644 index 000000000000..ae730a5ec846 --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/README.md @@ -0,0 +1,8 @@ +# image_picker_windows_example + +Demonstrates how to use the image_picker_windows plugin. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](https://flutter.dev/). diff --git a/packages/image_picker/image_picker_windows/example/lib/main.dart b/packages/image_picker/image_picker_windows/example/lib/main.dart new file mode 100644 index 000000000000..af97115676c0 --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/lib/main.dart @@ -0,0 +1,414 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; +import 'package:video_player/video_player.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return const MaterialApp( + title: 'Image Picker Demo', + home: MyHomePage(title: 'Image Picker Example'), + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({Key? key, this.title}) : super(key: key); + + final String? title; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + List? _imageFileList; + + // This must be called from within a setState() callback + set _imageFile(PickedFile? value) { + _imageFileList = value == null ? null : [value]; + } + + dynamic _pickImageError; + bool _isVideo = false; + + VideoPlayerController? _controller; + VideoPlayerController? _toBeDisposed; + String? _retrieveDataError; + + final ImagePickerPlatform _picker = ImagePickerPlatform.instance; + final TextEditingController maxWidthController = TextEditingController(); + final TextEditingController maxHeightController = TextEditingController(); + final TextEditingController qualityController = TextEditingController(); + + Future _playVideo(PickedFile? file) async { + if (file != null && mounted) { + await _disposeVideoController(); + final VideoPlayerController controller = + VideoPlayerController.file(File(file.path)); + _controller = controller; + await controller.setVolume(1.0); + await controller.initialize(); + await controller.setLooping(true); + await controller.play(); + setState(() {}); + } + } + + Future _handleMultiImagePicked(BuildContext? context) async { + await _displayPickImageDialog(context!, + (double? maxWidth, double? maxHeight, int? quality) async { + try { + final List? pickedFileList = await _picker.pickMultiImage( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: quality, + ); + setState(() { + _imageFileList = pickedFileList; + }); + } catch (e) { + setState(() { + _pickImageError = e; + }); + } + }); + } + + Future _handleSingleImagePicked( + BuildContext? context, ImageSource source) async { + await _displayPickImageDialog(context!, + (double? maxWidth, double? maxHeight, int? quality) async { + try { + final PickedFile? pickedFile = await _picker.pickImage( + source: source, + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: quality, + ); + setState(() { + _imageFile = pickedFile; + }); + } catch (e) { + setState(() { + _pickImageError = e; + }); + } + }); + } + + Future _onImageButtonPressed(ImageSource source, + {BuildContext? context, bool isMultiImage = false}) async { + if (_controller != null) { + await _controller!.setVolume(0.0); + } + if (_isVideo) { + final PickedFile? file = await _picker.pickVideo( + source: source, maxDuration: const Duration(seconds: 10)); + await _playVideo(file); + } else if (isMultiImage) { + await _handleMultiImagePicked(context); + } else { + await _handleSingleImagePicked(context, source); + } + } + + @override + void deactivate() { + if (_controller != null) { + _controller!.setVolume(0.0); + _controller!.pause(); + } + super.deactivate(); + } + + @override + void dispose() { + _disposeVideoController(); + maxWidthController.dispose(); + maxHeightController.dispose(); + qualityController.dispose(); + super.dispose(); + } + + Future _disposeVideoController() async { + if (_toBeDisposed != null) { + await _toBeDisposed!.dispose(); + } + _toBeDisposed = _controller; + _controller = null; + } + + Widget _previewVideo() { + final Text? retrieveError = _getRetrieveErrorWidget(); + if (retrieveError != null) { + return retrieveError; + } + if (_controller == null) { + return const Text( + 'You have not yet picked a video', + textAlign: TextAlign.center, + ); + } + return Padding( + padding: const EdgeInsets.all(10.0), + child: AspectRatioVideo(_controller), + ); + } + + Widget _previewImages() { + final Text? retrieveError = _getRetrieveErrorWidget(); + if (retrieveError != null) { + return retrieveError; + } + if (_imageFileList != null) { + return Semantics( + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + return Semantics( + label: 'image_picker_example_picked_image', + child: Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + label: 'image_picker_example_picked_images'); + } else if (_pickImageError != null) { + return Text( + 'Pick image error: $_pickImageError', + textAlign: TextAlign.center, + ); + } else { + return const Text( + 'You have not yet picked an image.', + textAlign: TextAlign.center, + ); + } + } + + Widget _handlePreview() { + if (_isVideo) { + return _previewVideo(); + } else { + return _previewImages(); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title!), + ), + body: Center( + child: _handlePreview(), + ), + floatingActionButton: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Semantics( + label: 'image_picker_example_from_gallery', + child: FloatingActionButton( + onPressed: () { + _isVideo = false; + _onImageButtonPressed(ImageSource.gallery, context: context); + }, + heroTag: 'image0', + tooltip: 'Pick Image from gallery', + child: const Icon(Icons.photo), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + onPressed: () { + _isVideo = false; + _onImageButtonPressed( + ImageSource.gallery, + context: context, + isMultiImage: true, + ); + }, + heroTag: 'image1', + tooltip: 'Pick Multiple Image from gallery', + child: const Icon(Icons.photo_library), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + onPressed: () { + _isVideo = false; + _onImageButtonPressed(ImageSource.camera, context: context); + }, + heroTag: 'image2', + tooltip: 'Take a Photo', + child: const Icon(Icons.camera_alt), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + backgroundColor: Colors.red, + onPressed: () { + _isVideo = true; + _onImageButtonPressed(ImageSource.gallery); + }, + heroTag: 'video0', + tooltip: 'Pick Video from gallery', + child: const Icon(Icons.video_library), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + backgroundColor: Colors.red, + onPressed: () { + _isVideo = true; + _onImageButtonPressed(ImageSource.camera); + }, + heroTag: 'video1', + tooltip: 'Take a Video', + child: const Icon(Icons.videocam), + ), + ), + ], + ), + ); + } + + Text? _getRetrieveErrorWidget() { + if (_retrieveDataError != null) { + final Text result = Text(_retrieveDataError!); + _retrieveDataError = null; + return result; + } + return null; + } + + Future _displayPickImageDialog( + BuildContext context, OnPickImageCallback onPick) async { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Add optional parameters'), + content: Column( + children: [ + TextField( + controller: maxWidthController, + keyboardType: + const TextInputType.numberWithOptions(decimal: true), + decoration: const InputDecoration( + hintText: 'Enter maxWidth if desired'), + ), + TextField( + controller: maxHeightController, + keyboardType: + const TextInputType.numberWithOptions(decimal: true), + decoration: const InputDecoration( + hintText: 'Enter maxHeight if desired'), + ), + TextField( + controller: qualityController, + keyboardType: TextInputType.number, + decoration: const InputDecoration( + hintText: 'Enter quality if desired'), + ), + ], + ), + actions: [ + TextButton( + child: const Text('CANCEL'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: const Text('PICK'), + onPressed: () { + final double? width = maxWidthController.text.isNotEmpty + ? double.parse(maxWidthController.text) + : null; + final double? height = maxHeightController.text.isNotEmpty + ? double.parse(maxHeightController.text) + : null; + final int? quality = qualityController.text.isNotEmpty + ? int.parse(qualityController.text) + : null; + onPick(width, height, quality); + Navigator.of(context).pop(); + }), + ], + ); + }); + } +} + +typedef OnPickImageCallback = void Function( + double? maxWidth, double? maxHeight, int? quality); + +class AspectRatioVideo extends StatefulWidget { + const AspectRatioVideo(this.controller); + + final VideoPlayerController? controller; + + @override + AspectRatioVideoState createState() => AspectRatioVideoState(); +} + +class AspectRatioVideoState extends State { + VideoPlayerController? get controller => widget.controller; + bool initialized = false; + + void _onVideoControllerUpdate() { + if (!mounted) { + return; + } + if (initialized != controller!.value.isInitialized) { + initialized = controller!.value.isInitialized; + setState(() {}); + } + } + + @override + void initState() { + super.initState(); + controller!.addListener(_onVideoControllerUpdate); + } + + @override + void dispose() { + controller!.removeListener(_onVideoControllerUpdate); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (initialized) { + return Center( + child: AspectRatio( + aspectRatio: controller!.value.aspectRatio, + child: VideoPlayer(controller!), + ), + ); + } else { + return Container(); + } + } +} diff --git a/packages/image_picker/image_picker_windows/example/pubspec.yaml b/packages/image_picker/image_picker_windows/example/pubspec.yaml new file mode 100644 index 000000000000..68c9395c6097 --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/pubspec.yaml @@ -0,0 +1,27 @@ +name: example +description: Example for image_picker_windows implementation. +publish_to: 'none' +version: 1.0.0 + +environment: + sdk: ">=2.12.0 <3.0.0" + flutter: ">=2.5.0" + +dependencies: + flutter: + sdk: flutter + image_picker_windows: + # When depending on this package from a real application you should use: + # image_picker_windows: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: .. + video_player: ^2.1.4 + +dev_dependencies: + flutter_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/image_picker/image_picker_windows/example/windows/.gitignore b/packages/image_picker/image_picker_windows/example/windows/.gitignore new file mode 100644 index 000000000000..d492d0d98c8f --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/packages/image_picker/image_picker_windows/example/windows/CMakeLists.txt b/packages/image_picker/image_picker_windows/example/windows/CMakeLists.txt new file mode 100644 index 000000000000..1633297a0c7c --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/CMakeLists.txt @@ -0,0 +1,95 @@ +cmake_minimum_required(VERSION 3.14) +project(example LANGUAGES CXX) + +set(BINARY_NAME "example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() + +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build +add_subdirectory("runner") + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/packages/image_picker/image_picker_windows/example/windows/flutter/CMakeLists.txt b/packages/image_picker/image_picker_windows/example/windows/flutter/CMakeLists.txt new file mode 100644 index 000000000000..b2e4bd8d658b --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,103 @@ +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/packages/image_picker/image_picker_windows/example/windows/flutter/generated_plugins.cmake b/packages/image_picker/image_picker_windows/example/windows/flutter/generated_plugins.cmake new file mode 100644 index 000000000000..63eda9b7b59f --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/flutter/generated_plugins.cmake @@ -0,0 +1,16 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + file_selector_windows +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) diff --git a/packages/image_picker/image_picker_windows/example/windows/runner/CMakeLists.txt b/packages/image_picker/image_picker_windows/example/windows/runner/CMakeLists.txt new file mode 100644 index 000000000000..de2d8916b72b --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/runner/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) +apply_standard_settings(${BINARY_NAME}) +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/packages/image_picker/image_picker_windows/example/windows/runner/Runner.rc b/packages/image_picker/image_picker_windows/example/windows/runner/Runner.rc new file mode 100644 index 000000000000..5fdea291cf19 --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#ifdef FLUTTER_BUILD_NUMBER +#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#else +#define VERSION_AS_NUMBER 1,0,0 +#endif + +#ifdef FLUTTER_BUILD_NAME +#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "example" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2022 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "example.exe" "\0" + VALUE "ProductName", "example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/packages/image_picker/image_picker_windows/example/windows/runner/flutter_window.cpp b/packages/image_picker/image_picker_windows/example/windows/runner/flutter_window.cpp new file mode 100644 index 000000000000..8254bd9ff3c1 --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/runner/flutter_window.cpp @@ -0,0 +1,65 @@ +// 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. + +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/packages/image_picker/image_picker_windows/example/windows/runner/flutter_window.h b/packages/image_picker/image_picker_windows/example/windows/runner/flutter_window.h new file mode 100644 index 000000000000..f1fc669093d0 --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/runner/flutter_window.h @@ -0,0 +1,37 @@ +// 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. + +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/packages/image_picker/image_picker_windows/example/windows/runner/main.cpp b/packages/image_picker/image_picker_windows/example/windows/runner/main.cpp new file mode 100644 index 000000000000..df379fa0be93 --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/runner/main.cpp @@ -0,0 +1,46 @@ +// 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. + +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t* command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/packages/image_picker/image_picker_windows/example/windows/runner/resource.h b/packages/image_picker/image_picker_windows/example/windows/runner/resource.h new file mode 100644 index 000000000000..d5d958dc4257 --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/packages/image_picker/image_picker_windows/example/windows/runner/resources/app_icon.ico b/packages/image_picker/image_picker_windows/example/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/packages/image_picker/image_picker_windows/example/windows/runner/runner.exe.manifest b/packages/image_picker/image_picker_windows/example/windows/runner/runner.exe.manifest new file mode 100644 index 000000000000..c977c4a42589 --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/packages/image_picker/image_picker_windows/example/windows/runner/utils.cpp b/packages/image_picker/image_picker_windows/example/windows/runner/utils.cpp new file mode 100644 index 000000000000..fb7e945a63b7 --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/runner/utils.cpp @@ -0,0 +1,67 @@ +// 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. + +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE* unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = + ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, + nullptr, 0, nullptr, nullptr); + if (target_length == 0) { + return std::string(); + } + std::string utf8_string; + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/packages/image_picker/image_picker_windows/example/windows/runner/utils.h b/packages/image_picker/image_picker_windows/example/windows/runner/utils.h new file mode 100644 index 000000000000..bd81e1e02338 --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/runner/utils.h @@ -0,0 +1,23 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/packages/image_picker/image_picker_windows/example/windows/runner/win32_window.cpp b/packages/image_picker/image_picker_windows/example/windows/runner/win32_window.cpp new file mode 100644 index 000000000000..85aa3614e8ad --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/runner/win32_window.cpp @@ -0,0 +1,241 @@ +// 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. + +#include "win32_window.h" + +#include + +#include "resource.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + FreeLibrary(user32_module); + } +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { ++g_active_window_count; } + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::CreateAndShow(const std::wstring& title, const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { return window_handle_; } + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} diff --git a/packages/image_picker/image_picker_windows/example/windows/runner/win32_window.h b/packages/image_picker/image_picker_windows/example/windows/runner/win32_window.h new file mode 100644 index 000000000000..d2a730052223 --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/runner/win32_window.h @@ -0,0 +1,99 @@ +// 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. + +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates and shows a win32 window with |title| and position and size using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring& title, const Point& origin, + const Size& size); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/packages/image_picker/image_picker_windows/lib/image_picker_windows.dart b/packages/image_picker/image_picker_windows/lib/image_picker_windows.dart new file mode 100644 index 000000000000..9bd26c471b4e --- /dev/null +++ b/packages/image_picker/image_picker_windows/lib/image_picker_windows.dart @@ -0,0 +1,167 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:file_selector_windows/file_selector_windows.dart'; +import 'package:flutter/foundation.dart'; +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; + +/// The Windows implementation of [ImagePickerPlatform]. +/// +/// This class implements the `package:image_picker` functionality for +/// Windows. +class ImagePickerWindows extends ImagePickerPlatform { + /// Constructs a ImagePickerWindows. + ImagePickerWindows(); + + /// List of image extensions used when picking images + @visibleForTesting + static const List imageFormats = [ + 'jpg', + 'jpeg', + 'png', + 'bmp', + 'webp', + 'gif', + 'tif', + 'tiff', + 'apng' + ]; + + /// List of video extensions used when picking videos + @visibleForTesting + static const List videoFormats = [ + 'mov', + 'wmv', + 'mkv', + 'mp4', + 'webm', + 'avi', + 'mpeg', + 'mpg' + ]; + + /// The file selector used to prompt the user to select images or videos. + @visibleForTesting + static late FileSelectorPlatform fileSelector = FileSelectorWindows(); + + /// Registers this class as the default instance of [ImagePickerPlatform]. + static void registerWith() { + ImagePickerPlatform.instance = ImagePickerWindows(); + } + + // `maxWidth`, `maxHeight`, `imageQuality` and `preferredCameraDevice` + // arguments are not supported on Windows. If any of these arguments + // is supplied, it'll be silently ignored by the Windows version of + // the plugin. `source` is not implemented for `ImageSource.camera` + // and will throw an exception. + @override + Future pickImage({ + required ImageSource source, + double? maxWidth, + double? maxHeight, + int? imageQuality, + CameraDevice preferredCameraDevice = CameraDevice.rear, + }) async { + final XFile? file = await getImage( + source: source, + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + preferredCameraDevice: preferredCameraDevice); + if (file != null) { + return PickedFile(file.path); + } + return null; + } + + // `preferredCameraDevice` and `maxDuration` arguments are not + // supported on Windows. If any of these arguments is supplied, + // it'll be silently ignored by the Windows version of the plugin. + // `source` is not implemented for `ImageSource.camera` and will + // throw an exception. + @override + Future pickVideo({ + required ImageSource source, + CameraDevice preferredCameraDevice = CameraDevice.rear, + Duration? maxDuration, + }) async { + final XFile? file = await getVideo( + source: source, + preferredCameraDevice: preferredCameraDevice, + maxDuration: maxDuration); + if (file != null) { + return PickedFile(file.path); + } + return null; + } + + // `maxWidth`, `maxHeight`, `imageQuality`, and `preferredCameraDevice` + // arguments are not supported on Windows. If any of these arguments + // is supplied, it'll be silently ignored by the Windows version + // of the plugin. `source` is not implemented for `ImageSource.camera` + // and will throw an exception. + @override + Future getImage({ + required ImageSource source, + double? maxWidth, + double? maxHeight, + int? imageQuality, + CameraDevice preferredCameraDevice = CameraDevice.rear, + }) async { + if (source != ImageSource.gallery) { + // TODO(azchohfi): Support ImageSource.camera. + // See https://github.com/flutter/flutter/issues/102115 + throw UnimplementedError( + 'ImageSource.gallery is currently the only supported source on Windows'); + } + final XTypeGroup typeGroup = + XTypeGroup(label: 'images', extensions: imageFormats); + final XFile? file = await fileSelector + .openFile(acceptedTypeGroups: [typeGroup]); + return file; + } + + // `preferredCameraDevice` and `maxDuration` arguments are not + // supported on Windows. If any of these arguments is supplied, + // it'll be silently ignored by the Windows version of the plugin. + // `source` is not implemented for `ImageSource.camera` and will + // throw an exception. + @override + Future getVideo({ + required ImageSource source, + CameraDevice preferredCameraDevice = CameraDevice.rear, + Duration? maxDuration, + }) async { + if (source != ImageSource.gallery) { + // TODO(azchohfi): Support ImageSource.camera. + // See https://github.com/flutter/flutter/issues/102115 + throw UnimplementedError( + 'ImageSource.gallery is currently the only supported source on Windows'); + } + final XTypeGroup typeGroup = + XTypeGroup(label: 'videos', extensions: videoFormats); + final XFile? file = await fileSelector + .openFile(acceptedTypeGroups: [typeGroup]); + return file; + } + + // `maxWidth`, `maxHeight`, and `imageQuality` arguments are not + // supported on Windows. If any of these arguments is supplied, + // it'll be silently ignored by the Windows version of the plugin. + @override + Future> getMultiImage({ + double? maxWidth, + double? maxHeight, + int? imageQuality, + }) async { + final XTypeGroup typeGroup = + XTypeGroup(label: 'images', extensions: imageFormats); + final List files = await fileSelector + .openFiles(acceptedTypeGroups: [typeGroup]); + return files; + } +} diff --git a/packages/image_picker/image_picker_windows/pubspec.yaml b/packages/image_picker/image_picker_windows/pubspec.yaml new file mode 100644 index 000000000000..eec41f7bfa0d --- /dev/null +++ b/packages/image_picker/image_picker_windows/pubspec.yaml @@ -0,0 +1,29 @@ +name: image_picker_windows +description: Windows platform implementation of image_picker +repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_windows +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 +version: 0.1.0 + +environment: + sdk: ">=2.12.0 <3.0.0" + flutter: ">=2.5.0" + +flutter: + plugin: + implements: image_picker + platforms: + windows: + dartPluginClass: ImagePickerWindows + +dependencies: + file_selector_platform_interface: ^2.0.4 + file_selector_windows: ^0.8.2 + flutter: + sdk: flutter + image_picker_platform_interface: ^2.4.3 + +dev_dependencies: + build_runner: ^2.1.5 + flutter_test: + sdk: flutter + mockito: ^5.0.16 diff --git a/packages/image_picker/image_picker_windows/test/image_picker_windows_test.dart b/packages/image_picker/image_picker_windows/test/image_picker_windows_test.dart new file mode 100644 index 000000000000..c3df2d80679f --- /dev/null +++ b/packages/image_picker/image_picker_windows/test/image_picker_windows_test.dart @@ -0,0 +1,128 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; +import 'package:image_picker_windows/image_picker_windows.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'image_picker_windows_test.mocks.dart'; + +@GenerateMocks([FileSelectorPlatform]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('$ImagePickerWindows()', () { + final ImagePickerWindows plugin = ImagePickerWindows(); + late MockFileSelectorPlatform mockFileSelectorPlatform; + + setUp(() { + mockFileSelectorPlatform = MockFileSelectorPlatform(); + + when(mockFileSelectorPlatform.openFile( + acceptedTypeGroups: anyNamed('acceptedTypeGroups'))) + .thenAnswer((_) async => null); + + when(mockFileSelectorPlatform.openFiles( + acceptedTypeGroups: anyNamed('acceptedTypeGroups'))) + .thenAnswer((_) async => List.empty()); + + ImagePickerWindows.fileSelector = mockFileSelectorPlatform; + }); + + test('registered instance', () { + ImagePickerWindows.registerWith(); + expect(ImagePickerPlatform.instance, isA()); + }); + + group('images', () { + test('pickImage passes the accepted type groups correctly', () async { + await plugin.pickImage(source: ImageSource.gallery); + + expect( + verify(mockFileSelectorPlatform.openFile( + acceptedTypeGroups: captureAnyNamed('acceptedTypeGroups'))) + .captured + .single[0] + .extensions, + ImagePickerWindows.imageFormats); + }); + + test('pickImage throws UnimplementedError when source is camera', + () async { + expect(() async => await plugin.pickImage(source: ImageSource.camera), + throwsA(isA())); + }); + + test('getImage passes the accepted type groups correctly', () async { + await plugin.getImage(source: ImageSource.gallery); + + expect( + verify(mockFileSelectorPlatform.openFile( + acceptedTypeGroups: captureAnyNamed('acceptedTypeGroups'))) + .captured + .single[0] + .extensions, + ImagePickerWindows.imageFormats); + }); + + test('getImage throws UnimplementedError when source is camera', + () async { + expect(() async => await plugin.getImage(source: ImageSource.camera), + throwsA(isA())); + }); + + test('getMultiImage passes the accepted type groups correctly', () async { + await plugin.getMultiImage(); + + expect( + verify(mockFileSelectorPlatform.openFiles( + acceptedTypeGroups: captureAnyNamed('acceptedTypeGroups'))) + .captured + .single[0] + .extensions, + ImagePickerWindows.imageFormats); + }); + }); + group('videos', () { + test('pickVideo passes the accepted type groups correctly', () async { + await plugin.pickVideo(source: ImageSource.gallery); + + expect( + verify(mockFileSelectorPlatform.openFile( + acceptedTypeGroups: captureAnyNamed('acceptedTypeGroups'))) + .captured + .single[0] + .extensions, + ImagePickerWindows.videoFormats); + }); + + test('pickVideo throws UnimplementedError when source is camera', + () async { + expect(() async => await plugin.pickVideo(source: ImageSource.camera), + throwsA(isA())); + }); + + test('getVideo passes the accepted type groups correctly', () async { + await plugin.getVideo(source: ImageSource.gallery); + + expect( + verify(mockFileSelectorPlatform.openFile( + acceptedTypeGroups: captureAnyNamed('acceptedTypeGroups'))) + .captured + .single[0] + .extensions, + ImagePickerWindows.videoFormats); + }); + + test('getVideo throws UnimplementedError when source is camera', + () async { + expect(() async => await plugin.getVideo(source: ImageSource.camera), + throwsA(isA())); + }); + }); + }); +} diff --git a/packages/image_picker/image_picker_windows/test/image_picker_windows_test.mocks.dart b/packages/image_picker/image_picker_windows/test/image_picker_windows_test.mocks.dart new file mode 100644 index 000000000000..be2dd2ac5768 --- /dev/null +++ b/packages/image_picker/image_picker_windows/test/image_picker_windows_test.mocks.dart @@ -0,0 +1,78 @@ +// Mocks generated by Mockito 5.1.0 from annotations +// in image_picker_windows/example/windows/flutter/ephemeral/.plugin_symlinks/image_picker_windows/test/image_picker_windows_test.dart. +// Do not manually edit this file. + +import 'dart:async' as _i3; + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart' + as _i2; +import 'package:mockito/mockito.dart' as _i1; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types + +/// A class which mocks [FileSelectorPlatform]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockFileSelectorPlatform extends _i1.Mock + implements _i2.FileSelectorPlatform { + MockFileSelectorPlatform() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.Future<_i2.XFile?> openFile( + {List<_i2.XTypeGroup>? acceptedTypeGroups, + String? initialDirectory, + String? confirmButtonText}) => + (super.noSuchMethod( + Invocation.method(#openFile, [], { + #acceptedTypeGroups: acceptedTypeGroups, + #initialDirectory: initialDirectory, + #confirmButtonText: confirmButtonText + }), + returnValue: Future<_i2.XFile?>.value()) as _i3.Future<_i2.XFile?>); + @override + _i3.Future> openFiles( + {List<_i2.XTypeGroup>? acceptedTypeGroups, + String? initialDirectory, + String? confirmButtonText}) => + (super.noSuchMethod( + Invocation.method(#openFiles, [], { + #acceptedTypeGroups: acceptedTypeGroups, + #initialDirectory: initialDirectory, + #confirmButtonText: confirmButtonText + }), + returnValue: Future>.value(<_i2.XFile>[])) + as _i3.Future>); + @override + _i3.Future getSavePath( + {List<_i2.XTypeGroup>? acceptedTypeGroups, + String? initialDirectory, + String? suggestedName, + String? confirmButtonText}) => + (super.noSuchMethod( + Invocation.method(#getSavePath, [], { + #acceptedTypeGroups: acceptedTypeGroups, + #initialDirectory: initialDirectory, + #suggestedName: suggestedName, + #confirmButtonText: confirmButtonText + }), + returnValue: Future.value()) as _i3.Future); + @override + _i3.Future getDirectoryPath( + {String? initialDirectory, String? confirmButtonText}) => + (super.noSuchMethod( + Invocation.method(#getDirectoryPath, [], { + #initialDirectory: initialDirectory, + #confirmButtonText: confirmButtonText + }), + returnValue: Future.value()) as _i3.Future); +} diff --git a/script/configs/exclude_integration_win32.yaml b/script/configs/exclude_integration_win32.yaml index 4626fbd79ce7..09306691e5ed 100644 --- a/script/configs/exclude_integration_win32.yaml +++ b/script/configs/exclude_integration_win32.yaml @@ -1,3 +1,4 @@ # Can't use Flutter integration tests due to native modal UI. - file_selector - file_selector_windows +- image_picker_windows \ No newline at end of file From 755fb82f0d3ca882c6fc14bc4ddc779771f9949a Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 21 Apr 2022 16:08:21 -0400 Subject: [PATCH 435/600] [flutter_plugin_tools] Run pub get for custom-test (#5322) When running a Dart test script for `custom-test`, `pub get` needs to be run first in order for it to work in a clean environment such as CI. This will unblock enabling Pigeon's Dart tests in flutter/packages. --- script/tool/CHANGELOG.md | 3 +- script/tool/lib/src/custom_test_command.dart | 13 ++++- script/tool/pubspec.yaml | 2 +- .../tool/test/custom_test_command_test.dart | 47 ++++++++++++++++++- 4 files changed, 59 insertions(+), 6 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 6e632c67684d..367f258fbeea 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,10 +1,11 @@ -## NEXT +## 0.8.2+1 - Adds a new `readme-check` command. - Updates `publish-plugin` command documentation. - Fixes `all-plugins-app` to preserve the original application's Dart SDK version to avoid changing language feature opt-ins that the template may rely on. +- Fixes `custom-test` to run `pub get` before running Dart test scripts. ## 0.8.2 diff --git a/script/tool/lib/src/custom_test_command.dart b/script/tool/lib/src/custom_test_command.dart index 1c1dfc068a11..cd9ac32606a6 100644 --- a/script/tool/lib/src/custom_test_command.dart +++ b/script/tool/lib/src/custom_test_command.dart @@ -43,10 +43,19 @@ class CustomTestCommand extends PackageLoopingCommand { // Run the custom Dart script if presest. if (script.existsSync()) { - final int exitCode = await processRunner.runAndStream( + // Ensure that dependencies are available. + final int pubGetExitCode = await processRunner.runAndStream( + 'dart', ['pub', 'get'], + workingDir: package.directory); + if (pubGetExitCode != 0) { + return PackageResult.fail( + ['Unable to get script dependencies']); + } + + final int testExitCode = await processRunner.runAndStream( 'dart', ['run', 'tool/$_scriptName'], workingDir: package.directory); - if (exitCode != 0) { + if (testExitCode != 0) { return PackageResult.fail(); } ranTests = true; diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index f005c565be17..14b22a16e3fb 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.8.2 +version: 0.8.2+1 dependencies: args: ^2.1.0 diff --git a/script/tool/test/custom_test_command_test.dart b/script/tool/test/custom_test_command_test.dart index 6a34c2689f6b..bc30d9a1d2e3 100644 --- a/script/tool/test/custom_test_command_test.dart +++ b/script/tool/test/custom_test_command_test.dart @@ -85,6 +85,21 @@ void main() { ])); }); + test('runs pub get before running Dart test script', () async { + final Directory package = createFakePlugin('a_package', packagesDir, + extraFiles: ['tool/run_tests.dart']); + + await runCapturingPrint(runner, ['custom-test']); + + expect( + processRunner.recordedCalls, + containsAll([ + ProcessCall('dart', const ['pub', 'get'], package.path), + ProcessCall('dart', const ['run', 'tool/run_tests.dart'], + package.path), + ])); + }); + test('runs when only legacy is present', () async { final Directory package = createFakePlugin('a_package', packagesDir, extraFiles: ['run_tests.sh']); @@ -128,7 +143,8 @@ void main() { ]); processRunner.mockProcessesForExecutable['dart'] = [ - MockProcess(exitCode: 1), + MockProcess(exitCode: 0), // pub get + MockProcess(exitCode: 1), // test script ]; Error? commandError; @@ -146,6 +162,32 @@ void main() { ])); }); + test('fails if pub get fails', () async { + createFakePlugin('a_package', packagesDir, extraFiles: [ + 'tool/run_tests.dart', + 'run_tests.sh', + ]); + + processRunner.mockProcessesForExecutable['dart'] = [ + MockProcess(exitCode: 1), + ]; + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['custom-test'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The following packages had errors:'), + contains('a_package:\n' + ' Unable to get script dependencies') + ])); + }); + test('fails if legacy fails', () async { final Directory package = createFakePlugin('a_package', packagesDir, extraFiles: [ @@ -260,7 +302,8 @@ void main() { ]); processRunner.mockProcessesForExecutable['dart'] = [ - MockProcess(exitCode: 1), + MockProcess(exitCode: 0), // pub get + MockProcess(exitCode: 1), // test script ]; Error? commandError; From f2dd3d98a3ef316656c328d086a720b5aa180187 Mon Sep 17 00:00:00 2001 From: Ibrahim Cetin Date: Thu, 21 Apr 2022 23:49:08 +0300 Subject: [PATCH 436/600] [video_player] Add setClosedCaptionFile method to VideoPlayerController (#5105) --- .../video_player/video_player/CHANGELOG.md | 3 +- .../video_player/lib/video_player.dart | 55 +++++++++++++------ .../video_player/video_player/pubspec.yaml | 2 +- .../video_player/test/video_player_test.dart | 36 ++++++++++++ 4 files changed, 77 insertions(+), 19 deletions(-) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index d9d1983fe0ad..363546c211e3 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,7 +1,8 @@ -## NEXT +## 2.4.0 * Updates minimum Flutter version to 2.10. * Adds OS version support information to README. +* Adds `setClosedCaptionFile` method to `VideoPlayerController`. ## 2.3.0 diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index b77c530b5831..39cd415dbb79 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -207,8 +207,11 @@ class VideoPlayerController extends ValueNotifier { /// null. The [package] argument must be non-null when the asset comes from a /// package and null otherwise. VideoPlayerController.asset(this.dataSource, - {this.package, this.closedCaptionFile, this.videoPlayerOptions}) - : dataSourceType = DataSourceType.asset, + {this.package, + Future? closedCaptionFile, + this.videoPlayerOptions}) + : _closedCaptionFileFuture = closedCaptionFile, + dataSourceType = DataSourceType.asset, formatHint = null, httpHeaders = const {}, super(VideoPlayerValue(duration: Duration.zero)); @@ -225,10 +228,11 @@ class VideoPlayerController extends ValueNotifier { VideoPlayerController.network( this.dataSource, { this.formatHint, - this.closedCaptionFile, + Future? closedCaptionFile, this.videoPlayerOptions, this.httpHeaders = const {}, - }) : dataSourceType = DataSourceType.network, + }) : _closedCaptionFileFuture = closedCaptionFile, + dataSourceType = DataSourceType.network, package = null, super(VideoPlayerValue(duration: Duration.zero)); @@ -237,8 +241,9 @@ class VideoPlayerController extends ValueNotifier { /// This will load the file from the file-URI given by: /// `'file://${file.path}'`. VideoPlayerController.file(File file, - {this.closedCaptionFile, this.videoPlayerOptions}) - : dataSource = 'file://${file.path}', + {Future? closedCaptionFile, this.videoPlayerOptions}) + : _closedCaptionFileFuture = closedCaptionFile, + dataSource = 'file://${file.path}', dataSourceType = DataSourceType.file, package = null, formatHint = null, @@ -250,9 +255,10 @@ class VideoPlayerController extends ValueNotifier { /// This will load the video from the input content-URI. /// This is supported on Android only. VideoPlayerController.contentUri(Uri contentUri, - {this.closedCaptionFile, this.videoPlayerOptions}) + {Future? closedCaptionFile, this.videoPlayerOptions}) : assert(defaultTargetPlatform == TargetPlatform.android, 'VideoPlayerController.contentUri is only supported on Android.'), + _closedCaptionFileFuture = closedCaptionFile, dataSource = contentUri.toString(), dataSourceType = DataSourceType.contentUri, package = null, @@ -283,13 +289,7 @@ class VideoPlayerController extends ValueNotifier { /// Only set for [asset] videos. The package that the asset was loaded from. final String? package; - /// Optional field to specify a file containing the closed - /// captioning. - /// - /// This future will be awaited and the file will be loaded when - /// [initialize()] is called. - final Future? closedCaptionFile; - + Future? _closedCaptionFileFuture; ClosedCaptionFile? _closedCaptionFile; Timer? _timer; bool _isDisposed = false; @@ -397,9 +397,8 @@ class VideoPlayerController extends ValueNotifier { } } - if (closedCaptionFile != null) { - _closedCaptionFile ??= await closedCaptionFile; - value = value.copyWith(caption: _getCaptionAt(value.position)); + if (_closedCaptionFileFuture != null) { + await _updateClosedCaptionWithFuture(_closedCaptionFileFuture); } void errorListener(Object obj) { @@ -634,6 +633,28 @@ class VideoPlayerController extends ValueNotifier { return Caption.none; } + /// Returns the file containing closed captions for the video, if any. + Future? get closedCaptionFile { + return _closedCaptionFileFuture; + } + + /// Sets a closed caption file. + /// + /// If [closedCaptionFile] is null, closed captions will be removed. + Future setClosedCaptionFile( + Future? closedCaptionFile, + ) async { + await _updateClosedCaptionWithFuture(closedCaptionFile); + _closedCaptionFileFuture = closedCaptionFile; + } + + Future _updateClosedCaptionWithFuture( + Future? closedCaptionFile, + ) async { + _closedCaptionFile = await closedCaptionFile; + value = value.copyWith(caption: _getCaptionAt(value.position)); + } + void _updatePosition(Duration position) { value = value.copyWith( position: position, diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 88e45c5be057..0d654a4330a7 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for displaying inline video with other Flutter widgets on Android, iOS, and web. repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.0 +version: 2.4.0 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player/test/video_player_test.dart b/packages/video_player/video_player/test/video_player_test.dart index 5e31e22c7010..64113337a860 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -72,6 +72,11 @@ class FakeController extends ValueNotifier @override void setCaptionOffset(Duration delay) {} + + @override + Future setClosedCaptionFile( + Future? closedCaptionFile, + ) async {} } Future _loadClosedCaption() async => @@ -672,6 +677,37 @@ void main() { await controller.seekTo(const Duration(milliseconds: 300)); expect(controller.value.caption.text, 'one'); }); + + test('setClosedCapitonFile loads caption file', () async { + final VideoPlayerController controller = VideoPlayerController.network( + 'https://127.0.0.1', + ); + + await controller.initialize(); + expect(controller.closedCaptionFile, null); + + await controller.setClosedCaptionFile(_loadClosedCaption()); + expect( + (await controller.closedCaptionFile)!.captions, + (await _loadClosedCaption()).captions, + ); + }); + + test('setClosedCapitonFile removes/changes caption file', () async { + final VideoPlayerController controller = VideoPlayerController.network( + 'https://127.0.0.1', + closedCaptionFile: _loadClosedCaption(), + ); + + await controller.initialize(); + expect( + (await controller.closedCaptionFile)!.captions, + (await _loadClosedCaption()).captions, + ); + + await controller.setClosedCaptionFile(null); + expect(controller.closedCaptionFile, null); + }); }); group('Platform callbacks', () { From 85000d3cacebd52eae13b56065eb1bf6075425d3 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 21 Apr 2022 17:29:09 -0400 Subject: [PATCH 437/600] [various] Replaces manual hashing with Object.hash (#5314) --- .../camera_platform_interface/CHANGELOG.md | 5 ++++ .../lib/src/events/camera_event.dart | 25 +++++++++---------- .../lib/src/types/camera_description.dart | 2 +- .../camera_platform_interface/pubspec.yaml | 5 ++-- .../test/events/camera_event_test.dart | 22 ++++++++-------- .../test/types/camera_description_test.dart | 8 +++--- .../local_auth_android/CHANGELOG.md | 6 ++++- .../lib/types/auth_messages_android.dart | 23 +++++++++-------- .../local_auth_android/pubspec.yaml | 4 +-- .../local_auth/local_auth_ios/CHANGELOG.md | 6 ++++- .../lib/types/auth_messages_ios.dart | 14 ++++++----- .../local_auth/local_auth_ios/pubspec.yaml | 4 +-- .../CHANGELOG.md | 4 +++ .../lib/types/auth_options.dart | 11 ++++---- .../pubspec.yaml | 4 +-- .../CHANGELOG.md | 5 ++++ .../lib/video_player_platform_interface.dart | 15 +++++------ .../pubspec.yaml | 5 ++-- script/tool/test/util.dart | 3 +-- 19 files changed, 97 insertions(+), 74 deletions(-) diff --git a/packages/camera/camera_platform_interface/CHANGELOG.md b/packages/camera/camera_platform_interface/CHANGELOG.md index 6ed662b2afa0..ef251eab9b93 100644 --- a/packages/camera/camera_platform_interface/CHANGELOG.md +++ b/packages/camera/camera_platform_interface/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.1.6 + +* Adopts `Object.hash`. +* Removes obsolete dependency on `pedantic`. + ## 2.1.5 * Fixes asynchronous exceptions handling of the `initializeCamera` method. diff --git a/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart b/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart index a91e538e4be5..755006cab8ab 100644 --- a/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart +++ b/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart @@ -117,14 +117,15 @@ class CameraInitializedEvent extends CameraEvent { focusPointSupported == other.focusPointSupported; @override - int get hashCode => - super.hashCode ^ - previewWidth.hashCode ^ - previewHeight.hashCode ^ - exposureMode.hashCode ^ - exposurePointSupported.hashCode ^ - focusMode.hashCode ^ - focusPointSupported.hashCode; + int get hashCode => Object.hash( + super.hashCode, + previewWidth, + previewHeight, + exposureMode, + exposurePointSupported, + focusMode, + focusPointSupported, + ); } /// An event fired when the resolution preset of the camera has changed. @@ -171,8 +172,7 @@ class CameraResolutionChangedEvent extends CameraEvent { captureHeight == other.captureHeight; @override - int get hashCode => - super.hashCode ^ captureWidth.hashCode ^ captureHeight.hashCode; + int get hashCode => Object.hash(super.hashCode, captureWidth, captureHeight); } /// An event fired when the camera is going to close. @@ -239,7 +239,7 @@ class CameraErrorEvent extends CameraEvent { description == other.description; @override - int get hashCode => super.hashCode ^ description.hashCode; + int get hashCode => Object.hash(super.hashCode, description); } /// An event fired when a video has finished recording. @@ -284,6 +284,5 @@ class VideoRecordedEvent extends CameraEvent { maxVideoDuration == other.maxVideoDuration; @override - int get hashCode => - super.hashCode ^ file.hashCode ^ maxVideoDuration.hashCode; + int get hashCode => Object.hash(super.hashCode, file, maxVideoDuration); } diff --git a/packages/camera/camera_platform_interface/lib/src/types/camera_description.dart b/packages/camera/camera_platform_interface/lib/src/types/camera_description.dart index df7263631252..0167cf9e17a1 100644 --- a/packages/camera/camera_platform_interface/lib/src/types/camera_description.dart +++ b/packages/camera/camera_platform_interface/lib/src/types/camera_description.dart @@ -50,7 +50,7 @@ class CameraDescription { lensDirection == other.lensDirection; @override - int get hashCode => name.hashCode ^ lensDirection.hashCode; + int get hashCode => Object.hash(name, lensDirection); @override String toString() { diff --git a/packages/camera/camera_platform_interface/pubspec.yaml b/packages/camera/camera_platform_interface/pubspec.yaml index b28008d8d58e..ab163b4e9f3f 100644 --- a/packages/camera/camera_platform_interface/pubspec.yaml +++ b/packages/camera/camera_platform_interface/pubspec.yaml @@ -4,11 +4,11 @@ repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.1.5 +version: 2.1.6 environment: sdk: '>=2.12.0 <3.0.0' - flutter: ">=2.0.0" + flutter: ">=2.8.0" dependencies: cross_file: ^0.3.1 @@ -21,4 +21,3 @@ dev_dependencies: async: ^2.5.0 flutter_test: sdk: flutter - pedantic: ^1.10.0 diff --git a/packages/camera/camera_platform_interface/test/events/camera_event_test.dart b/packages/camera/camera_platform_interface/test/events/camera_event_test.dart index a46486ed252c..8a428e2fd43a 100644 --- a/packages/camera/camera_platform_interface/test/events/camera_event_test.dart +++ b/packages/camera/camera_platform_interface/test/events/camera_event_test.dart @@ -137,13 +137,14 @@ void main() { test('hashCode should match hashCode of all properties', () { const CameraInitializedEvent event = CameraInitializedEvent( 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); - final int expectedHashCode = event.cameraId.hashCode ^ - event.previewWidth.hashCode ^ - event.previewHeight.hashCode ^ - event.exposureMode.hashCode ^ - event.exposurePointSupported.hashCode ^ - event.focusMode.hashCode ^ - event.focusPointSupported.hashCode; + final int expectedHashCode = Object.hash( + event.cameraId, + event.previewWidth, + event.previewHeight, + event.exposureMode, + event.exposurePointSupported, + event.focusMode, + event.focusPointSupported); expect(event.hashCode, expectedHashCode); }); @@ -223,9 +224,8 @@ void main() { test('hashCode should match hashCode of all properties', () { const CameraResolutionChangedEvent event = CameraResolutionChangedEvent(1, 1024, 640); - final int expectedHashCode = event.cameraId.hashCode ^ - event.captureWidth.hashCode ^ - event.captureHeight.hashCode; + final int expectedHashCode = + Object.hash(event.cameraId, event.captureWidth, event.captureHeight); expect(event.hashCode, expectedHashCode); }); @@ -328,7 +328,7 @@ void main() { test('hashCode should match hashCode of all properties', () { const CameraErrorEvent event = CameraErrorEvent(1, 'Error'); final int expectedHashCode = - event.cameraId.hashCode ^ event.description.hashCode; + Object.hash(event.cameraId, event.description); expect(event.hashCode, expectedHashCode); }); diff --git a/packages/camera/camera_platform_interface/test/types/camera_description_test.dart b/packages/camera/camera_platform_interface/test/types/camera_description_test.dart index 3d3aaaeb4086..a86df031ac3a 100644 --- a/packages/camera/camera_platform_interface/test/types/camera_description_test.dart +++ b/packages/camera/camera_platform_interface/test/types/camera_description_test.dart @@ -97,15 +97,15 @@ void main() { expect(firstDescription == secondDescription, true); }); - test('hashCode should match hashCode of all properties', () { + test('hashCode should match hashCode of all equality-tested properties', + () { const CameraDescription description = CameraDescription( name: 'Test', lensDirection: CameraLensDirection.front, sensorOrientation: 0, ); - final int expectedHashCode = description.name.hashCode ^ - description.lensDirection.hashCode ^ - description.sensorOrientation.hashCode; + final int expectedHashCode = + Object.hash(description.name, description.lensDirection); expect(description.hashCode, expectedHashCode); }); diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index 7f198f2d66c0..121daf945fd5 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.1 + +* Adopts `Object.hash`. + ## 1.0.0 -* Initial release from migration to federated architecture. +* Initial release from migration to federated architecture. diff --git a/packages/local_auth/local_auth_android/lib/types/auth_messages_android.dart b/packages/local_auth/local_auth_android/lib/types/auth_messages_android.dart index ea61a4b06d4e..ad901248e63c 100644 --- a/packages/local_auth/local_auth_android/lib/types/auth_messages_android.dart +++ b/packages/local_auth/local_auth_android/lib/types/auth_messages_android.dart @@ -110,17 +110,18 @@ class AndroidAuthMessages extends AuthMessages { signInTitle == other.signInTitle; @override - int get hashCode => - biometricHint.hashCode ^ - biometricNotRecognized.hashCode ^ - biometricRequiredTitle.hashCode ^ - biometricSuccess.hashCode ^ - cancelButton.hashCode ^ - deviceCredentialsRequiredTitle.hashCode ^ - deviceCredentialsSetupDescription.hashCode ^ - goToSettingsButton.hashCode ^ - goToSettingsDescription.hashCode ^ - signInTitle.hashCode; + int get hashCode => Object.hash( + super.hashCode, + biometricHint, + biometricNotRecognized, + biometricRequiredTitle, + biometricSuccess, + cancelButton, + deviceCredentialsRequiredTitle, + deviceCredentialsSetupDescription, + goToSettingsButton, + goToSettingsDescription, + signInTitle); } // Default strings for AndroidAuthMessages. Currently supports English. diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml index ec2991db6a90..5734843d6584 100644 --- a/packages/local_auth/local_auth_android/pubspec.yaml +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_android description: Android implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.0 +version: 1.0.1 environment: sdk: ">=2.14.0 <3.0.0" @@ -26,4 +26,4 @@ dependencies: dev_dependencies: flutter_test: - sdk: flutter \ No newline at end of file + sdk: flutter diff --git a/packages/local_auth/local_auth_ios/CHANGELOG.md b/packages/local_auth/local_auth_ios/CHANGELOG.md index ccacae986d51..466aeb50ed6c 100644 --- a/packages/local_auth/local_auth_ios/CHANGELOG.md +++ b/packages/local_auth/local_auth_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.3 + +* Adopts `Object.hash`. + ## 1.0.2 * Adds support `localizedFallbackTitle` in authenticateWithBiometrics on iOS. @@ -8,4 +12,4 @@ ## 1.0.0 -* Initial release from migration to federated architecture. +* Initial release from migration to federated architecture. diff --git a/packages/local_auth/local_auth_ios/lib/types/auth_messages_ios.dart b/packages/local_auth/local_auth_ios/lib/types/auth_messages_ios.dart index b3236a7ff66e..e5173fc4ab4f 100644 --- a/packages/local_auth/local_auth_ios/lib/types/auth_messages_ios.dart +++ b/packages/local_auth/local_auth_ios/lib/types/auth_messages_ios.dart @@ -65,12 +65,14 @@ class IOSAuthMessages extends AuthMessages { localizedFallbackTitle == other.localizedFallbackTitle; @override - int get hashCode => - lockOut.hashCode ^ - goToSettingsButton.hashCode ^ - goToSettingsDescription.hashCode ^ - cancelButton.hashCode ^ - localizedFallbackTitle.hashCode; + int get hashCode => Object.hash( + super.hashCode, + lockOut, + goToSettingsButton, + goToSettingsDescription, + cancelButton, + localizedFallbackTitle, + ); } // Default Strings for IOSAuthMessages plugin. Currently supports English. diff --git a/packages/local_auth/local_auth_ios/pubspec.yaml b/packages/local_auth/local_auth_ios/pubspec.yaml index 9e69dc7a6617..06d972a01522 100644 --- a/packages/local_auth/local_auth_ios/pubspec.yaml +++ b/packages/local_auth/local_auth_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_ios description: iOS implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.2 +version: 1.0.3 environment: sdk: ">=2.14.0 <3.0.0" @@ -24,4 +24,4 @@ dependencies: dev_dependencies: flutter_test: - sdk: flutter \ No newline at end of file + sdk: flutter diff --git a/packages/local_auth/local_auth_platform_interface/CHANGELOG.md b/packages/local_auth/local_auth_platform_interface/CHANGELOG.md index d16a91ee259a..0ff4d16ed163 100644 --- a/packages/local_auth/local_auth_platform_interface/CHANGELOG.md +++ b/packages/local_auth/local_auth_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.2 + +* Adopts `Object.hash`. + ## 1.0.1 * Export externally used types from local_auth_platform_interface.dart directly. diff --git a/packages/local_auth/local_auth_platform_interface/lib/types/auth_options.dart b/packages/local_auth/local_auth_platform_interface/lib/types/auth_options.dart index c4b646c0b97a..a5af8e73a640 100644 --- a/packages/local_auth/local_auth_platform_interface/lib/types/auth_options.dart +++ b/packages/local_auth/local_auth_platform_interface/lib/types/auth_options.dart @@ -52,9 +52,10 @@ class AuthenticationOptions { biometricOnly == other.biometricOnly; @override - int get hashCode => - useErrorDialogs.hashCode ^ - stickyAuth.hashCode ^ - sensitiveTransaction.hashCode ^ - biometricOnly.hashCode; + int get hashCode => Object.hash( + useErrorDialogs, + stickyAuth, + sensitiveTransaction, + biometricOnly, + ); } diff --git a/packages/local_auth/local_auth_platform_interface/pubspec.yaml b/packages/local_auth/local_auth_platform_interface/pubspec.yaml index cd12a8a2dcab..b5d476d85cbd 100644 --- a/packages/local_auth/local_auth_platform_interface/pubspec.yaml +++ b/packages/local_auth/local_auth_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/l issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 1.0.1 +version: 1.0.2 environment: sdk: ">=2.14.0 <3.0.0" @@ -19,4 +19,4 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - mockito: ^5.0.0 \ No newline at end of file + mockito: ^5.0.0 diff --git a/packages/video_player/video_player_platform_interface/CHANGELOG.md b/packages/video_player/video_player_platform_interface/CHANGELOG.md index 6595a5ce55db..9843fd81e82d 100644 --- a/packages/video_player/video_player_platform_interface/CHANGELOG.md +++ b/packages/video_player/video_player_platform_interface/CHANGELOG.md @@ -1,3 +1,8 @@ +## 5.1.2 + +* Adopts `Object.hash`. +* Removes obsolete dependency on `pedantic`. + ## 5.1.1 * Adds `rotationCorrection` (for Android playing videos recorded in landscapeRight [#60327](https://github.com/flutter/flutter/issues/60327)). diff --git a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart index 706ec61abf7c..78173f1fb63c 100644 --- a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart +++ b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart @@ -249,12 +249,13 @@ class VideoEvent { } @override - int get hashCode => - eventType.hashCode ^ - duration.hashCode ^ - size.hashCode ^ - rotationCorrection.hashCode ^ - buffered.hashCode; + int get hashCode => Object.hash( + eventType, + duration, + size, + rotationCorrection, + buffered, + ); } /// Type of the event. @@ -343,7 +344,7 @@ class DurationRange { end == other.end; @override - int get hashCode => start.hashCode ^ end.hashCode; + int get hashCode => Object.hash(start, end); } /// [VideoPlayerOptions] can be optionally used to set additional player settings diff --git a/packages/video_player/video_player_platform_interface/pubspec.yaml b/packages/video_player/video_player_platform_interface/pubspec.yaml index ea3b17be1ea5..7a18568f22b5 100644 --- a/packages/video_player/video_player_platform_interface/pubspec.yaml +++ b/packages/video_player/video_player_platform_interface/pubspec.yaml @@ -4,11 +4,11 @@ repository: https://github.com/flutter/plugins/tree/main/packages/video_player/v issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 5.1.1 +version: 5.1.2 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" dependencies: flutter: @@ -18,5 +18,4 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - pedantic: ^1.10.0 pigeon: 0.1.21 diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart index 6f7d86e054e9..fab4f39cc10f 100644 --- a/script/tool/test/util.dart +++ b/script/tool/test/util.dart @@ -434,8 +434,7 @@ class ProcessCall { } @override - int get hashCode => - (executable.hashCode) ^ (args.hashCode) ^ (workingDir?.hashCode ?? 0); + int get hashCode => Object.hash(executable, args, workingDir); @override String toString() { From a6c1c95be43e293f92c21da633a8953ed1e846fc Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 21 Apr 2022 20:04:08 -0700 Subject: [PATCH 438/600] [webview_flutter_wkwebview] Instance Manager (#5241) --- .../ios/Runner.xcodeproj/project.pbxproj | 6 +- .../ios/RunnerTests/FWFInstanceManagerTests.m | 40 +++++++++++ .../ios/Classes/FWFInstanceManager.h | 70 ++++++++++++++++++ .../ios/Classes/FWFInstanceManager.m | 72 +++++++++++++++++++ .../ios/Classes/webview-umbrella.h | 1 + 5 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFInstanceManagerTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.m diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj index b681c4704ddd..a444be330949 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ @@ -11,6 +11,7 @@ 334734012669319100DCC49E /* FLTWebViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68BDCAF523C3F97800D9C032 /* FLTWebViewTests.m */; }; 334734022669319400DCC49E /* FLTWKNavigationDelegateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 686B4BF82548DBC7000AEA36 /* FLTWKNavigationDelegateTests.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 8FA6A87928062CD000A4B183 /* FWFInstanceManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -67,6 +68,7 @@ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFInstanceManagerTests.m; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -126,6 +128,7 @@ 68BDCAF523C3F97800D9C032 /* FLTWebViewTests.m */, 68BDCAED23C3F7CB00D9C032 /* Info.plist */, E43693B427512C0F00382F85 /* FLTCookieManagerTests.m */, + 8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */, ); path = RunnerTests; sourceTree = ""; @@ -423,6 +426,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8FA6A87928062CD000A4B183 /* FWFInstanceManagerTests.m in Sources */, 334734012669319100DCC49E /* FLTWebViewTests.m in Sources */, 334734022669319400DCC49E /* FLTWKNavigationDelegateTests.m in Sources */, E43693B527512C0F00382F85 /* FLTCookieManagerTests.m in Sources */, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFInstanceManagerTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFInstanceManagerTests.m new file mode 100644 index 000000000000..7b40da131d23 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFInstanceManagerTests.m @@ -0,0 +1,40 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +@import webview_flutter_wkwebview; + +@interface FWFInstanceManagerTests : XCTestCase +@end + +@implementation FWFInstanceManagerTests +- (void)testAddInstance { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + NSObject *object = [[NSObject alloc] init]; + + [instanceManager addInstance:object withIdentifier:5]; + XCTAssertEqualObjects([instanceManager instanceForIdentifier:5], object); + XCTAssertEqual([instanceManager identifierForInstance:object], 5); +} + +- (void)testRemoveInstance { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + NSObject *object = [[NSObject alloc] init]; + [instanceManager addInstance:object withIdentifier:5]; + + [instanceManager removeInstance:object]; + XCTAssertNil([instanceManager instanceForIdentifier:5]); + XCTAssertEqual([instanceManager identifierForInstance:object], NSNotFound); +} + +- (void)testRemoveInstanceWithIdentifier { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + NSObject *object = [[NSObject alloc] init]; + [instanceManager addInstance:object withIdentifier:5]; + + [instanceManager removeInstanceWithIdentifier:5]; + XCTAssertNil([instanceManager instanceForIdentifier:5]); + XCTAssertEqual([instanceManager identifierForInstance:object], NSNotFound); +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.h new file mode 100644 index 000000000000..79506bf72adc --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.h @@ -0,0 +1,70 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * Maintains instances to intercommunicate with Dart objects. + * + * When an instance is added with an identifier, either can be used to retrieve the other. + */ +@interface FWFInstanceManager : NSObject +// TODO(bparrishMines): Pairs should not be able to be overwritten and this feature +// should be replaced with a call to clear the manager in the event of a hot restart +// instead. +/** + * Adds a new instance to the manager. + * + * If an instance or identifier has already been added, it will be replaced by the new values. The + * Dart InstanceManager is considered the source of truth and has the capability to overwrite stored + * pairs in response to hot restarts. + * + * @param instance The instance to be stored. + * @param instanceIdentifier The identifier to be paired with instance. This value must be >= 0. + */ +- (void)addInstance:(NSObject *)instance withIdentifier:(long)instanceIdentifier; + +/** + * Removes the instance paired with a given identifier from the manager. + * + * @param instanceIdentifier The identifier paired to an instance. + * + * @return The removed instance if the manager contains the given instanceIdentifier, otherwise + * nil. + */ +- (nullable NSObject *)removeInstanceWithIdentifier:(long)instanceIdentifier; + +/** + * Removes the instance from the manager. + * + * @param instance The instance to be removed from the manager. + * + * @return The identifier of the removed instance if the manager contains the given instance, + * otherwise NSNotFound. + */ +- (long)removeInstance:(NSObject *)instance; + +/** + * Retrieves the instance paired with identifier. + * + * @param instanceIdentifier The identifier paired to an instance. + * + * @return The paired instance if the manager contains the given instanceIdentifier, + * otherwise nil. + */ +- (nullable NSObject *)instanceForIdentifier:(long)instanceIdentifier; + +/** + * Retrieves the identifier paired with an instance. + * + * @param instance An instance that may be stored in the manager. + * + * @return The paired identifer if the manager contains the given instance, otherwise NSNotFound. + */ +- (long)identifierForInstance:(NSObject *)instance; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.m new file mode 100644 index 000000000000..0d88d88ba282 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.m @@ -0,0 +1,72 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FWFInstanceManager.h" + +@interface FWFInstanceManager () +@property dispatch_queue_t lockQueue; +@property NSMapTable *identifiersToInstances; +@property NSMapTable *instancesToIdentifiers; +@end + +@implementation FWFInstanceManager +- (instancetype)init { + if (self) { + self.lockQueue = dispatch_queue_create("FWFInstanceManager", DISPATCH_QUEUE_SERIAL); + self.identifiersToInstances = [NSMapTable strongToStrongObjectsMapTable]; + self.instancesToIdentifiers = [NSMapTable strongToStrongObjectsMapTable]; + } + return self; +} + +- (void)addInstance:(nonnull NSObject *)instance withIdentifier:(long)instanceIdentifier { + NSAssert(instance && instanceIdentifier >= 0, + @"Instance must be nonnull and identifier must be >= 0."); + dispatch_async(_lockQueue, ^{ + [self.instancesToIdentifiers setObject:@(instanceIdentifier) forKey:instance]; + [self.identifiersToInstances setObject:instance forKey:@(instanceIdentifier)]; + }); +} + +- (nullable NSObject *)removeInstanceWithIdentifier:(long)instanceIdentifier { + NSObject *__block instance = nil; + dispatch_sync(_lockQueue, ^{ + instance = [self.identifiersToInstances objectForKey:@(instanceIdentifier)]; + if (instance) { + [self.identifiersToInstances removeObjectForKey:@(instanceIdentifier)]; + [self.instancesToIdentifiers removeObjectForKey:instance]; + } + }); + return instance; +} + +- (long)removeInstance:(NSObject *)instance { + NSAssert(instance, @"Instance must be nonnull."); + NSNumber *__block identifierNumber = nil; + dispatch_sync(_lockQueue, ^{ + identifierNumber = [self.instancesToIdentifiers objectForKey:instance]; + if (identifierNumber) { + [self.identifiersToInstances removeObjectForKey:identifierNumber]; + [self.instancesToIdentifiers removeObjectForKey:instance]; + } + }); + return identifierNumber ? identifierNumber.longValue : NSNotFound; +} + +- (nullable NSObject *)instanceForIdentifier:(long)instanceIdentifier { + NSObject *__block instance = nil; + dispatch_sync(_lockQueue, ^{ + instance = [self.identifiersToInstances objectForKey:@(instanceIdentifier)]; + }); + return instance; +} + +- (long)identifierForInstance:(nonnull NSObject *)instance { + NSNumber *__block identifierNumber = nil; + dispatch_sync(_lockQueue, ^{ + identifierNumber = [self.instancesToIdentifiers objectForKey:instance]; + }); + return identifierNumber ? identifierNumber.longValue : NSNotFound; +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h index c37282e886bc..3743f13f4ac7 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h @@ -7,5 +7,6 @@ #import #import #import +#import #import #import From 1925b91b4f1beb9b712dcb23ab6b5b32a10c9a32 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 21 Apr 2022 23:44:12 -0400 Subject: [PATCH 439/600] Roll Flutter from 0dccb58a73dd to 1b58a593deac (1 revision) (#5319) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 627a0ce88e85..23335129b903 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -0dccb58a73dd0448b3cc4b24396b61ddcce4f861 +1b58a593deac48f1c3a6f579d53a0401372dc59f From 83269f39a7a42d65c3c44dc84c5ff0b7fb6a6bb4 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 22 Apr 2022 00:24:05 -0400 Subject: [PATCH 440/600] [local_auth] Fix default deviceSupportsBiometrics (#5321) --- .../CHANGELOG.md | 6 ++++ .../lib/default_method_channel_platform.dart | 11 +++++- .../pubspec.yaml | 2 +- .../default_method_channel_platform_test.dart | 34 ++++++++++++++++--- 4 files changed, 46 insertions(+), 7 deletions(-) diff --git a/packages/local_auth/local_auth_platform_interface/CHANGELOG.md b/packages/local_auth/local_auth_platform_interface/CHANGELOG.md index 0ff4d16ed163..9a2ae7aea8e8 100644 --- a/packages/local_auth/local_auth_platform_interface/CHANGELOG.md +++ b/packages/local_auth/local_auth_platform_interface/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.0.3 + +* Fixes regression in the default method channel implementation of + `deviceSupportsBiometrics` from federation that would cause it to return true + only if something is enrolled. + ## 1.0.2 * Adopts `Object.hash`. diff --git a/packages/local_auth/local_auth_platform_interface/lib/default_method_channel_platform.dart b/packages/local_auth/local_auth_platform_interface/lib/default_method_channel_platform.dart index c68a3bfb8371..3e695fa41f17 100644 --- a/packages/local_auth/local_auth_platform_interface/lib/default_method_channel_platform.dart +++ b/packages/local_auth/local_auth_platform_interface/lib/default_method_channel_platform.dart @@ -57,6 +57,8 @@ class DefaultLocalAuthPlatform extends LocalAuthPlatform { biometrics.add(BiometricType.iris); break; case 'undefined': + // Sentinel value for the case when nothing is enrolled, but hardware + // support for biometrics is available. break; } } @@ -65,7 +67,14 @@ class DefaultLocalAuthPlatform extends LocalAuthPlatform { @override Future deviceSupportsBiometrics() async { - return (await getEnrolledBiometrics()).isNotEmpty; + final List availableBiometrics = + (await _channel.invokeListMethod( + 'getAvailableBiometrics', + )) ?? + []; + // If anything, including the 'undefined' sentinel, is returned, then there + // is device support for biometrics. + return availableBiometrics.isNotEmpty; } @override diff --git a/packages/local_auth/local_auth_platform_interface/pubspec.yaml b/packages/local_auth/local_auth_platform_interface/pubspec.yaml index b5d476d85cbd..ee6c1e9d6577 100644 --- a/packages/local_auth/local_auth_platform_interface/pubspec.yaml +++ b/packages/local_auth/local_auth_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/l issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 1.0.2 +version: 1.0.3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_platform_interface/test/default_method_channel_platform_test.dart b/packages/local_auth/local_auth_platform_interface/test/default_method_channel_platform_test.dart index 3853fd84c6fc..96e78e17cfa8 100644 --- a/packages/local_auth/local_auth_platform_interface/test/default_method_channel_platform_test.dart +++ b/packages/local_auth/local_auth_platform_interface/test/default_method_channel_platform_test.dart @@ -10,7 +10,6 @@ import 'package:local_auth_platform_interface/default_method_channel_platform.da import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; import 'package:local_auth_platform_interface/types/auth_messages.dart'; import 'package:local_auth_platform_interface/types/auth_options.dart'; -import 'package:local_auth_platform_interface/types/biometric_type.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -19,9 +18,13 @@ void main() { 'plugins.flutter.io/local_auth', ); - final List log = []; + late List log; late LocalAuthPlatform localAuthentication; + setUp(() async { + log = []; + }); + test( 'DefaultLocalAuthPlatform is registered as the default platform implementation', () async { @@ -32,10 +35,9 @@ void main() { test('getAvailableBiometrics', () async { channel.setMockMethodCallHandler((MethodCall methodCall) { log.add(methodCall); - return Future.value([]); + return Future.value([]); }); localAuthentication = DefaultLocalAuthPlatform(); - log.clear(); await localAuthentication.getEnrolledBiometrics(); expect( log, @@ -45,6 +47,29 @@ void main() { ); }); + test('deviceSupportsBiometrics handles special sentinal value', () async { + // The pre-federation implementation of the platform channels, which the + // default implementation retains compatibility with for the benefit of any + // existing unendorsed implementations, used 'undefined' as a special + // return value from `getAvailableBiometrics` to indicate that nothing was + // enrolled, but that the hardware does support biometrics. + channel.setMockMethodCallHandler((MethodCall methodCall) { + log.add(methodCall); + return Future.value(['undefined']); + }); + + localAuthentication = DefaultLocalAuthPlatform(); + final bool supportsBiometrics = + await localAuthentication.deviceSupportsBiometrics(); + expect(supportsBiometrics, true); + expect( + log, + [ + isMethodCall('getAvailableBiometrics', arguments: null), + ], + ); + }); + group('Boolean returning methods', () { setUp(() { channel.setMockMethodCallHandler((MethodCall methodCall) { @@ -52,7 +77,6 @@ void main() { return Future.value(true); }); localAuthentication = DefaultLocalAuthPlatform(); - log.clear(); }); test('isDeviceSupported', () async { From bde713a230d3931f17105f2b4f8439cf448d031a Mon Sep 17 00:00:00 2001 From: BeMacized Date: Fri, 22 Apr 2022 10:04:09 +0200 Subject: [PATCH 441/600] [local_auth] Fix getEnrolledBiometrics returning non-enrolled biometrics on Android. (#5309) --- .../local_auth_android/CHANGELOG.md | 7 + .../plugins/localauth/LocalAuthPlugin.java | 44 ++--- .../plugins/localauth/LocalAuthTest.java | 166 ++++++++++++++++++ .../local_auth_android/example/lib/main.dart | 23 +-- .../lib/local_auth_android.dart | 18 +- .../local_auth_android/pubspec.yaml | 2 +- .../test/local_auth_test.dart | 16 +- 7 files changed, 224 insertions(+), 52 deletions(-) diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index 121daf945fd5..cbf686a2abcb 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,3 +1,10 @@ +## 1.0.2 + +* Fixes `getEnrolledBiometrics` to match documented behaviour: + Present biometrics that are not enrolled are no longer returned. +* `getEnrolledBiometrics` now only returns `weak` and `strong` biometric types. +* `deviceSupportsBiometrics` now returns the correct value regardless of enrollment state. + ## 1.0.1 * Adopts `Object.hash`. diff --git a/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java b/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java index 49a6b788fe46..3c5ecad16329 100644 --- a/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java +++ b/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java @@ -11,7 +11,6 @@ import android.app.KeyguardManager; import android.content.Context; import android.content.Intent; -import android.content.pm.PackageManager; import android.hardware.fingerprint.FingerprintManager; import android.os.Build; import androidx.annotation.NonNull; @@ -101,8 +100,8 @@ public void onMethodCall(MethodCall call, @NonNull final Result result) { case "authenticate": authenticate(call, result); break; - case "getAvailableBiometrics": - getAvailableBiometrics(result); + case "getEnrolledBiometrics": + getEnrolledBiometrics(result); break; case "isDeviceSupported": isDeviceSupported(result); @@ -110,6 +109,9 @@ public void onMethodCall(MethodCall call, @NonNull final Result result) { case "stopAuthentication": stopAuthentication(result); break; + case "deviceSupportsBiometrics": + deviceSupportsBiometrics(result); + break; default: result.notImplemented(); break; @@ -248,42 +250,39 @@ private void stopAuthentication(Result result) { } } + private void deviceSupportsBiometrics(final Result result) { + result.success(hasBiometricHardware()); + } + /* - * Returns biometric types available on device + * Returns enrolled biometric types available on device. */ - private void getAvailableBiometrics(final Result result) { + private void getEnrolledBiometrics(final Result result) { try { if (activity == null || activity.isFinishing()) { result.error("no_activity", "local_auth plugin requires a foreground activity", null); return; } - ArrayList biometrics = getAvailableBiometrics(); + ArrayList biometrics = getEnrolledBiometrics(); result.success(biometrics); } catch (Exception e) { result.error("no_biometrics_available", e.getMessage(), null); } } - private ArrayList getAvailableBiometrics() { + private ArrayList getEnrolledBiometrics() { ArrayList biometrics = new ArrayList<>(); if (activity == null || activity.isFinishing()) { return biometrics; } - PackageManager packageManager = activity.getPackageManager(); - if (Build.VERSION.SDK_INT >= 23) { - if (packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) { - biometrics.add("fingerprint"); - } + if (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) + == BiometricManager.BIOMETRIC_SUCCESS) { + biometrics.add("weak"); } - if (Build.VERSION.SDK_INT >= 29) { - if (packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) { - biometrics.add("face"); - } - if (packageManager.hasSystemFeature(PackageManager.FEATURE_IRIS)) { - biometrics.add("iris"); - } + if (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG) + == BiometricManager.BIOMETRIC_SUCCESS) { + biometrics.add("strong"); } - return biometrics; } @@ -359,4 +358,9 @@ public void onDetachedFromActivity() { final Activity getActivity() { return activity; } + + @VisibleForTesting + void setBiometricManager(BiometricManager biometricManager) { + this.biometricManager = biometricManager; + } } diff --git a/packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java b/packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java index 41868e603ad8..5fbda46b984f 100644 --- a/packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java +++ b/packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java @@ -6,12 +6,14 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Activity; import android.content.Context; +import androidx.biometric.BiometricManager; import androidx.lifecycle.Lifecycle; import io.flutter.embedding.engine.FlutterEngine; import io.flutter.embedding.engine.dart.DartExecutor; @@ -20,6 +22,8 @@ import io.flutter.embedding.engine.plugins.lifecycle.HiddenLifecycleReference; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; +import java.util.ArrayList; +import java.util.Collections; import org.junit.Test; public class LocalAuthTest { @@ -31,6 +35,50 @@ public void isDeviceSupportedReturnsFalse() { verify(mockResult).success(false); } + @Test + public void deviceSupportsBiometrics_returnsTrueForPresentNonEnrolledBiometrics() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + final BiometricManager mockBiometricManager = mock(BiometricManager.class); + when(mockBiometricManager.canAuthenticate()) + .thenReturn(BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED); + plugin.setBiometricManager(mockBiometricManager); + plugin.onMethodCall(new MethodCall("deviceSupportsBiometrics", null), mockResult); + verify(mockResult).success(true); + } + + @Test + public void deviceSupportsBiometrics_returnsTrueForPresentEnrolledBiometrics() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + final BiometricManager mockBiometricManager = mock(BiometricManager.class); + when(mockBiometricManager.canAuthenticate()).thenReturn(BiometricManager.BIOMETRIC_SUCCESS); + plugin.setBiometricManager(mockBiometricManager); + plugin.onMethodCall(new MethodCall("deviceSupportsBiometrics", null), mockResult); + verify(mockResult).success(true); + } + + @Test + public void deviceSupportsBiometrics_returnsFalseForNoBiometricHardware() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + final BiometricManager mockBiometricManager = mock(BiometricManager.class); + when(mockBiometricManager.canAuthenticate()) + .thenReturn(BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE); + plugin.setBiometricManager(mockBiometricManager); + plugin.onMethodCall(new MethodCall("deviceSupportsBiometrics", null), mockResult); + verify(mockResult).success(false); + } + + @Test + public void deviceSupportsBiometrics_returnsFalseForNullBiometricManager() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + plugin.setBiometricManager(null); + plugin.onMethodCall(new MethodCall("deviceSupportsBiometrics", null), mockResult); + verify(mockResult).success(false); + } + @Test public void onDetachedFromActivity_ShouldReleaseActivity() { final Activity mockActivity = mock(Activity.class); @@ -61,4 +109,122 @@ public void onDetachedFromActivity_ShouldReleaseActivity() { plugin.onDetachedFromActivity(); assertNull(plugin.getActivity()); } + + @Test + public void getEnrolledBiometrics_shouldReturnError_whenNoActivity() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + + plugin.onMethodCall(new MethodCall("getEnrolledBiometrics", null), mockResult); + verify(mockResult) + .error("no_activity", "local_auth plugin requires a foreground activity", null); + } + + @Test + public void getEnrolledBiometrics_shouldReturnError_whenFinishingActivity() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + final Activity mockActivity = buildMockActivity(); + when(mockActivity.isFinishing()).thenReturn(true); + setPluginActivity(plugin, mockActivity); + + plugin.onMethodCall(new MethodCall("getEnrolledBiometrics", null), mockResult); + verify(mockResult) + .error("no_activity", "local_auth plugin requires a foreground activity", null); + } + + @Test + public void getEnrolledBiometrics_shouldReturnEmptyList_withoutHardwarePresent() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + setPluginActivity(plugin, buildMockActivity()); + final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + final BiometricManager mockBiometricManager = mock(BiometricManager.class); + when(mockBiometricManager.canAuthenticate(anyInt())) + .thenReturn(BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE); + plugin.setBiometricManager(mockBiometricManager); + + plugin.onMethodCall(new MethodCall("getEnrolledBiometrics", null), mockResult); + verify(mockResult).success(Collections.emptyList()); + } + + @Test + public void getEnrolledBiometrics_shouldReturnEmptyList_withNoMethodsEnrolled() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + setPluginActivity(plugin, buildMockActivity()); + final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + final BiometricManager mockBiometricManager = mock(BiometricManager.class); + when(mockBiometricManager.canAuthenticate(anyInt())) + .thenReturn(BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED); + plugin.setBiometricManager(mockBiometricManager); + + plugin.onMethodCall(new MethodCall("getEnrolledBiometrics", null), mockResult); + verify(mockResult).success(Collections.emptyList()); + } + + @Test + public void getEnrolledBiometrics_shouldOnlyAddEnrolledBiometrics() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + setPluginActivity(plugin, buildMockActivity()); + final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + final BiometricManager mockBiometricManager = mock(BiometricManager.class); + when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)) + .thenReturn(BiometricManager.BIOMETRIC_SUCCESS); + when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG)) + .thenReturn(BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED); + plugin.setBiometricManager(mockBiometricManager); + + plugin.onMethodCall(new MethodCall("getEnrolledBiometrics", null), mockResult); + verify(mockResult) + .success( + new ArrayList() { + { + add("weak"); + } + }); + } + + @Test + public void getEnrolledBiometrics_shouldAddStrongBiometrics() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + setPluginActivity(plugin, buildMockActivity()); + final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + final BiometricManager mockBiometricManager = mock(BiometricManager.class); + when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)) + .thenReturn(BiometricManager.BIOMETRIC_SUCCESS); + when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG)) + .thenReturn(BiometricManager.BIOMETRIC_SUCCESS); + plugin.setBiometricManager(mockBiometricManager); + + plugin.onMethodCall(new MethodCall("getEnrolledBiometrics", null), mockResult); + verify(mockResult) + .success( + new ArrayList() { + { + add("weak"); + add("strong"); + } + }); + } + + private Activity buildMockActivity() { + final Activity mockActivity = mock(Activity.class); + final Context mockContext = mock(Context.class); + when(mockActivity.getBaseContext()).thenReturn(mockContext); + when(mockActivity.getApplicationContext()).thenReturn(mockContext); + return mockActivity; + } + + private void setPluginActivity(LocalAuthPlugin plugin, Activity activity) { + final HiddenLifecycleReference mockLifecycleReference = mock(HiddenLifecycleReference.class); + final FlutterPluginBinding mockPluginBinding = mock(FlutterPluginBinding.class); + final ActivityPluginBinding mockActivityBinding = mock(ActivityPluginBinding.class); + final FlutterEngine mockFlutterEngine = mock(FlutterEngine.class); + final DartExecutor mockDartExecutor = mock(DartExecutor.class); + when(mockPluginBinding.getFlutterEngine()).thenReturn(mockFlutterEngine); + when(mockFlutterEngine.getDartExecutor()).thenReturn(mockDartExecutor); + when(mockActivityBinding.getActivity()).thenReturn(activity); + when(mockActivityBinding.getLifecycle()).thenReturn(mockLifecycleReference); + plugin.onAttachedToEngine(mockPluginBinding); + plugin.onAttachedToActivity(mockActivityBinding); + } } diff --git a/packages/local_auth/local_auth_android/example/lib/main.dart b/packages/local_auth/local_auth_android/example/lib/main.dart index 4c045214734d..29b1d66440eb 100644 --- a/packages/local_auth/local_auth_android/example/lib/main.dart +++ b/packages/local_auth/local_auth_android/example/lib/main.dart @@ -22,8 +22,8 @@ class MyApp extends StatefulWidget { class _MyAppState extends State { _SupportState _supportState = _SupportState.unknown; - bool? _canCheckBiometrics; - List? _availableBiometrics; + bool? _deviceSupportsBiometrics; + List? _enrolledBiometrics; String _authorized = 'Not Authorized'; bool _isAuthenticating = false; @@ -38,12 +38,12 @@ class _MyAppState extends State { } Future _checkBiometrics() async { - late bool canCheckBiometrics; + late bool deviceSupportsBiometrics; try { - canCheckBiometrics = - (await LocalAuthPlatform.instance.getEnrolledBiometrics()).isNotEmpty; + deviceSupportsBiometrics = + await LocalAuthPlatform.instance.deviceSupportsBiometrics(); } on PlatformException catch (e) { - canCheckBiometrics = false; + deviceSupportsBiometrics = false; print(e); } if (!mounted) { @@ -51,7 +51,7 @@ class _MyAppState extends State { } setState(() { - _canCheckBiometrics = canCheckBiometrics; + _deviceSupportsBiometrics = deviceSupportsBiometrics; }); } @@ -69,7 +69,7 @@ class _MyAppState extends State { } setState(() { - _availableBiometrics = availableBiometrics; + _enrolledBiometrics = availableBiometrics; }); } @@ -171,15 +171,16 @@ class _MyAppState extends State { else const Text('This device is not supported'), const Divider(height: 100), - Text('Can check biometrics: $_canCheckBiometrics\n'), + Text( + 'Device supports biometrics: $_deviceSupportsBiometrics\n'), ElevatedButton( child: const Text('Check biometrics'), onPressed: _checkBiometrics, ), const Divider(height: 100), - Text('Available biometrics: $_availableBiometrics\n'), + Text('Enrolled biometrics: $_enrolledBiometrics\n'), ElevatedButton( - child: const Text('Get available biometrics'), + child: const Text('Get enrolled biometrics'), onPressed: _getEnrolledBiometrics, ), const Divider(height: 100), diff --git a/packages/local_auth/local_auth_android/lib/local_auth_android.dart b/packages/local_auth/local_auth_android/lib/local_auth_android.dart index a3f314e3347b..2ce0f88477c7 100644 --- a/packages/local_auth/local_auth_android/lib/local_auth_android.dart +++ b/packages/local_auth/local_auth_android/lib/local_auth_android.dart @@ -49,28 +49,24 @@ class LocalAuthAndroid extends LocalAuthPlatform { @override Future deviceSupportsBiometrics() async { - return (await getEnrolledBiometrics()).isNotEmpty; + return (await _channel.invokeMethod('deviceSupportsBiometrics')) ?? + false; } @override Future> getEnrolledBiometrics() async { final List result = (await _channel.invokeListMethod( - 'getAvailableBiometrics', + 'getEnrolledBiometrics', )) ?? []; final List biometrics = []; for (final String value in result) { switch (value) { - case 'face': - biometrics.add(BiometricType.face); + case 'weak': + biometrics.add(BiometricType.weak); break; - case 'fingerprint': - biometrics.add(BiometricType.fingerprint); - break; - case 'iris': - biometrics.add(BiometricType.iris); - break; - case 'undefined': + case 'strong': + biometrics.add(BiometricType.strong); break; } } diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml index 5734843d6584..aad0b9b92e70 100644 --- a/packages/local_auth/local_auth_android/pubspec.yaml +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_android description: Android implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.1 +version: 1.0.2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_android/test/local_auth_test.dart b/packages/local_auth/local_auth_android/test/local_auth_test.dart index 31f5e5796445..55ac92626aea 100644 --- a/packages/local_auth/local_auth_android/test/local_auth_test.dart +++ b/packages/local_auth/local_auth_android/test/local_auth_test.dart @@ -24,9 +24,8 @@ void main() { channel.setMockMethodCallHandler((MethodCall methodCall) { log.add(methodCall); switch (methodCall.method) { - case 'getAvailableBiometrics': - return Future>.value( - ['face', 'fingerprint', 'iris', 'undefined']); + case 'getEnrolledBiometrics': + return Future>.value(['weak', 'strong']); default: return Future.value(true); } @@ -35,13 +34,13 @@ void main() { log.clear(); }); - test('deviceSupportsBiometrics calls getEnrolledBiometrics', () async { + test('deviceSupportsBiometrics calls platform', () async { final bool result = await localAuthentication.deviceSupportsBiometrics(); expect( log, [ - isMethodCall('getAvailableBiometrics', arguments: null), + isMethodCall('deviceSupportsBiometrics', arguments: null), ], ); expect(result, true); @@ -54,13 +53,12 @@ void main() { expect( log, [ - isMethodCall('getAvailableBiometrics', arguments: null), + isMethodCall('getEnrolledBiometrics', arguments: null), ], ); expect(result, [ - BiometricType.face, - BiometricType.fingerprint, - BiometricType.iris + BiometricType.weak, + BiometricType.strong, ]); }); From ca63d964d9fdfff9be36533ea4248d0d0ca28fd0 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 22 Apr 2022 08:04:07 -0400 Subject: [PATCH 442/600] [url_launcher] Replace primary APIs with cleaner versions (#5310) --- .../url_launcher/url_launcher/CHANGELOG.md | 17 +- packages/url_launcher/url_launcher/README.md | 106 ++++--- .../integration_test/url_launcher_test.dart | 17 +- .../url_launcher/example/lib/main.dart | 74 +++-- .../url_launcher/lib/src/legacy_api.dart | 154 ++++++++++ .../url_launcher/lib/src/link.dart | 25 +- .../url_launcher/lib/src/types.dart | 54 ++++ .../lib/src/url_launcher_string.dart | 65 ++++ .../lib/src/url_launcher_uri.dart | 90 ++++++ .../url_launcher/lib/url_launcher.dart | 150 +--------- .../url_launcher/lib/url_launcher_string.dart | 13 + .../url_launcher/url_launcher/pubspec.yaml | 2 +- .../url_launcher/test/link_test.dart | 10 +- .../mock_url_launcher_platform.dart | 0 .../legacy_api_test.dart} | 4 +- .../test/src/url_launcher_string_test.dart | 279 ++++++++++++++++++ .../test/src/url_launcher_uri_test.dart | 262 ++++++++++++++++ 17 files changed, 1058 insertions(+), 264 deletions(-) create mode 100644 packages/url_launcher/url_launcher/lib/src/legacy_api.dart create mode 100644 packages/url_launcher/url_launcher/lib/src/types.dart create mode 100644 packages/url_launcher/url_launcher/lib/src/url_launcher_string.dart create mode 100644 packages/url_launcher/url_launcher/lib/src/url_launcher_uri.dart create mode 100644 packages/url_launcher/url_launcher/lib/url_launcher_string.dart rename packages/url_launcher/url_launcher/test/{ => mocks}/mock_url_launcher_platform.dart (100%) rename packages/url_launcher/url_launcher/test/{url_launcher_test.dart => src/legacy_api_test.dart} (99%) create mode 100644 packages/url_launcher/url_launcher/test/src/url_launcher_string_test.dart create mode 100644 packages/url_launcher/url_launcher/test/src/url_launcher_uri_test.dart diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index 0c38f4847d9b..b1ebc4b35a8e 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,6 +1,19 @@ -## NEXT - +## 6.1.0 + +* Introduces new `launchUrl` and `canLaunchUrl` APIs; `launch` and `canLaunch` + are now deprecated. These new APIs: + * replace the `String` URL argument with a `Uri`, to prevent common issues + with providing invalid URL strings. + * replace `forceSafariVC` and `forceWebView` with `LaunchMode`, which makes + the API platform-neutral, and standardizes the default behavior between + Android and iOS. + * move web view configuration options into a new `WebViewConfiguration` + object. The default behavior for JavaScript and DOM storage is now enabled + rather than disabled. +* Also deprecates `closeWebView` in favor of `closeInAppWebView` to clarify + that it is specific to the in-app web view launch option. * Adds OS version support information to README. +* Reorganizes and clarifies README. ## 6.0.20 diff --git a/packages/url_launcher/url_launcher/README.md b/packages/url_launcher/url_launcher/README.md index 7f8699eceba7..0cdbe1b9859e 100644 --- a/packages/url_launcher/url_launcher/README.md +++ b/packages/url_launcher/url_launcher/README.md @@ -18,14 +18,14 @@ To use this plugin, add `url_launcher` as a [dependency in your pubspec.yaml fil import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; -const String _url = 'https://flutter.dev'; +final Uri _url = Uri.parse('https://flutter.dev'); void main() => runApp( const MaterialApp( home: Material( child: Center( child: RaisedButton( - onPressed: _launchURL, + onPressed: _launchUrl, child: Text('Show Flutter homepage'), ), ), @@ -33,8 +33,8 @@ void main() => runApp( ), ); -void _launchURL() async { - if (!await launch(_url)) throw 'Could not launch $_url'; +void _launchUrl() async { + if (!await launchUrl(_url)) throw 'Could not launch $_url'; } ``` @@ -43,7 +43,7 @@ See the example app for more complex examples. ## Configuration ### iOS -Add any URL schemes passed to `canLaunch` as `LSApplicationQueriesSchemes` entries in your Info.plist file. +Add any URL schemes passed to `canLaunchUrl` as `LSApplicationQueriesSchemes` entries in your Info.plist file. Example: ``` @@ -59,7 +59,7 @@ See [`-[UIApplication canOpenURL:]`](https://developer.apple.com/documentation/u ### Android Starting from API 30 Android requires package visibility configuration in your -`AndroidManifest.xml` otherwise `canLaunch` will return `false`. A `` +`AndroidManifest.xml` otherwise `canLaunchUrl` will return `false`. A `` element must be added to your manifest as a child of the root element. The snippet below shows an example for an application that uses `https`, `tel`, @@ -94,34 +94,53 @@ for examples of other queries. ## Supported URL schemes -The [`launch`](https://pub.dev/documentation/url_launcher/latest/url_launcher/launch.html) method -takes a string argument containing a URL. This URL -can be formatted using a number of different URL schemes. The supported -URL schemes depend on the underlying platform and installed apps. +The provided URL is passed directly to the host platform for handling. The +supported URL schemes therefore depend on the platform and installed apps. Commonly used schemes include: | Scheme | Example | Action | |:---|:---|:---| -| `https:` | `https://flutter.dev` | Open URL in the default browser | -| `mailto:?subject=&body=` | `mailto:smith@example.org?subject=News&body=New%20plugin` | Create email to in the default email app | -| `tel:` | `tel:+1-555-010-999` | Make a phone call to using the default phone app | -| `sms:` | `sms:5550101234` | Send an SMS message to using the default messaging app | +| `https:` | `https://flutter.dev` | Open `` in the default browser | +| `mailto:?subject=&body=` | `mailto:smith@example.org?subject=News&body=New%20plugin` | Create email to `` in the default email app | +| `tel:` | `tel:+1-555-010-999` | Make a phone call to `` using the default phone app | +| `sms:` | `sms:5550101234` | Send an SMS message to `` using the default messaging app | | `file:` | `file:/home` | Open file or folder using default app association, supported on desktop platforms | More details can be found here for [iOS](https://developer.apple.com/library/content/featuredarticles/iPhoneURLScheme_Reference/Introduction/Introduction.html) and [Android](https://developer.android.com/guide/components/intents-common.html) -**Note**: URL schemes are only supported if there are apps installed on the device that can +URL schemes are only supported if there are apps installed on the device that can support them. For example, iOS simulators don't have a default email or phone apps installed, so can't open `tel:` or `mailto:` links. +### Checking supported schemes + +If you need to know at runtime whether a scheme is guaranteed to work before +using it (for instance, to adjust your UI based on what is available), you can +check with [`canLaunchUrl`](https://pub.dev/documentation/url_launcher/latest/url_launcher/canLaunchUrl.html). + +However, `canLaunchUrl` can return false even if `launchUrl` would work in +some circumstances (in web applications, on mobile without the necessary +configuration as described above, etc.), so in cases where you can provide +fallback behavior it is better to use `launchUrl` directly and handle failure. +For example, a UI button that would have sent feedback email using a `mailto` URL +might instead open a web-based feedback form using an `https` URL on failure, +rather than disabling the button if `canLaunchUrl` returns false for `mailto`. + ### Encoding URLs URLs must be properly encoded, especially when including spaces or other special -characters. This can be done using the +characters. In general this is handled automatically by the [`Uri` class](https://api.dart.dev/dart-core/Uri-class.html). -For example: + +**However**, for any scheme other than `http` or `https`, you should use the +`query` parameter and the `encodeQueryParameters` function shown below rather +than `Uri`'s `queryParameters` constructor argument for any query parameters, +due to [a bug](https://github.com/dart-lang/sdk/issues/43838) in the way `Uri` +encodes query parameters. Using `queryParameters` will result in spaces being +converted to `+` in many cases. + ```dart String? encodeQueryParameters(Map params) { return params.entries @@ -137,43 +156,24 @@ final Uri emailLaunchUri = Uri( }), ); -launch(emailLaunchUri.toString()); +launchUrl(emailLaunchUri); ``` -**Warning**: For any scheme other than `http` or `https`, you should use the -`query` parameter and the `encodeQueryParameters` function shown above rather -than `Uri`'s `queryParameters` constructor argument, due to -[a bug](https://github.com/dart-lang/sdk/issues/43838) in the way `Uri` -encodes query parameters. Using `queryParameters` will result in spaces being -converted to `+` in many cases. - -### Handling missing URL receivers +### URLs not handled by `Uri` -A particular mobile device may not be able to receive all supported URL schemes. -For example, a tablet may not have a cellular radio and thus no support for -launching a URL using the `sms` scheme, or a device may not have an email app -and thus no support for launching a URL using the `mailto` scheme. +In rare cases, you may need to launch a URL that the host system considers +valid, but cannot be expressed by `Uri`. For those cases, alternate APIs using +strings are available by importing `url_launcher_string.dart`. -We recommend checking which URL schemes are supported using the -[`canLaunch`](https://pub.dev/documentation/url_launcher/latest/url_launcher/canLaunch.html) -in most cases. If the `canLaunch` method returns false, as a -best practice we suggest adjusting the application UI so that the unsupported -URL is never triggered; for example, if the `mailto` scheme is not supported, a -UI button that would have sent feedback email could be changed to instead open -a web-based feedback form using an `https` URL. +Using these APIs in any other cases is **strongly discouraged**, as providing +invalid URL strings was a very common source of errors with this plugin's +original APIs. -## Browser vs In-app Handling -By default, Android opens up a browser when handling URLs. You can pass -`forceWebView: true` parameter to tell the plugin to open a WebView instead. -If you do this for a URL of a page containing JavaScript, make sure to pass in -`enableJavaScript: true`, or else the launch method will not work properly. On -iOS, the default behavior is to open all web URLs within the app. Everything -else is redirected to the app handler. +### File scheme handling -## File scheme handling -`file:` scheme can be used on desktop platforms: `macOS`, `Linux` and `Windows`. +`file:` scheme can be used on desktop platforms: Windows, macOS, and Linux. -We recommend checking first whether the directory or file exists before calling `launch`. +We recommend checking first whether the directory or file exists before calling `launchUrl`. Example: ```dart @@ -181,13 +181,21 @@ var filePath = '/path/to/file'; final Uri uri = Uri.file(filePath); if (await File(uri.toFilePath()).exists()) { - if (!await launch(uri.toString())) { + if (!await launchUrl(uri)) { throw 'Could not launch $uri'; } } ``` -### macOS file access configuration +#### macOS file access configuration If you need to access files outside of your application's sandbox, you will need to have the necessary [entitlements](https://docs.flutter.dev/desktop#entitlements-and-the-app-sandbox). + +## Browser vs in-app Handling + +On some platforms, web URLs can be launched either in an in-app web view, or +in the default browser. The default behavior depends on the platform (see +[`launchUrl`](https://pub.dev/documentation/url_launcher/latest/url_launcher/launchUrl.html) +for details), but a specific mode can be used on supported platforms by +passing a `LaunchMode`. diff --git a/packages/url_launcher/url_launcher/example/integration_test/url_launcher_test.dart b/packages/url_launcher/url_launcher/example/integration_test/url_launcher_test.dart index b527c22390dc..51c2ec892400 100644 --- a/packages/url_launcher/url_launcher/example/integration_test/url_launcher_test.dart +++ b/packages/url_launcher/url_launcher/example/integration_test/url_launcher_test.dart @@ -13,18 +13,23 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('canLaunch', (WidgetTester _) async { - expect(await canLaunch('randomstring'), false); + expect( + await canLaunchUrl(Uri(scheme: 'randomscheme', path: 'a_path')), false); // Generally all devices should have some default browser. - expect(await canLaunch('http://flutter.dev'), true); - expect(await canLaunch('https://www.google.com/404'), true); + expect(await canLaunchUrl(Uri(scheme: 'http', host: 'flutter.dev')), true); + expect(await canLaunchUrl(Uri(scheme: 'https', host: 'flutter.dev')), true); // SMS handling is available by default on most platforms. if (kIsWeb || !(Platform.isLinux || Platform.isWindows)) { - expect(await canLaunch('sms:5555555555'), true); + expect(await canLaunchUrl(Uri(scheme: 'sms', path: '5555555555')), true); } - // tel: and mailto: links may not be openable on every device. iOS - // simulators notably can't open these link types. + // Sanity-check legacy API. + // ignore: deprecated_member_use + expect(await canLaunch('randomstring'), false); + // Generally all devices should have some default browser. + // ignore: deprecated_member_use + expect(await canLaunch('https://flutter.dev'), true); }); } diff --git a/packages/url_launcher/url_launcher/example/lib/main.dart b/packages/url_launcher/url_launcher/example/lib/main.dart index a5e38ceecc84..898e80661296 100644 --- a/packages/url_launcher/url_launcher/example/lib/main.dart +++ b/packages/url_launcher/url_launcher/example/lib/main.dart @@ -44,67 +44,62 @@ class _MyHomePageState extends State { void initState() { super.initState(); // Check for phone call support. - canLaunch('tel:123').then((bool result) { + canLaunchUrl(Uri(scheme: 'tel', path: '123')).then((bool result) { setState(() { _hasCallSupport = result; }); }); } - Future _launchInBrowser(String url) async { - if (!await launch( + Future _launchInBrowser(Uri url) async { + if (!await launchUrl( url, - forceSafariVC: false, - forceWebView: false, - headers: {'my_header_key': 'my_header_value'}, + mode: LaunchMode.externalApplication, )) { throw 'Could not launch $url'; } } - Future _launchInWebViewOrVC(String url) async { - if (!await launch( + Future _launchInWebViewOrVC(Uri url) async { + if (!await launchUrl( url, - forceSafariVC: true, - forceWebView: true, - headers: {'my_header_key': 'my_header_value'}, + mode: LaunchMode.inAppWebView, + webViewConfiguration: const WebViewConfiguration( + headers: {'my_header_key': 'my_header_value'}), )) { throw 'Could not launch $url'; } } - Future _launchInWebViewWithJavaScript(String url) async { - if (!await launch( + Future _launchInWebViewWithoutJavaScript(Uri url) async { + if (!await launchUrl( url, - forceSafariVC: true, - forceWebView: true, - enableJavaScript: true, + mode: LaunchMode.inAppWebView, + webViewConfiguration: const WebViewConfiguration(enableJavaScript: false), )) { throw 'Could not launch $url'; } } - Future _launchInWebViewWithDomStorage(String url) async { - if (!await launch( + Future _launchInWebViewWithoutDomStorage(Uri url) async { + if (!await launchUrl( url, - forceSafariVC: true, - forceWebView: true, - enableDomStorage: true, + mode: LaunchMode.inAppWebView, + webViewConfiguration: const WebViewConfiguration(enableDomStorage: false), )) { throw 'Could not launch $url'; } } - Future _launchUniversalLinkIos(String url) async { - final bool nativeAppLaunchSucceeded = await launch( + Future _launchUniversalLinkIos(Uri url) async { + final bool nativeAppLaunchSucceeded = await launchUrl( url, - forceSafariVC: false, - universalLinksOnly: true, + mode: LaunchMode.externalNonBrowserApplication, ); if (!nativeAppLaunchSucceeded) { - await launch( + await launchUrl( url, - forceSafariVC: true, + mode: LaunchMode.inAppWebView, ); } } @@ -118,22 +113,19 @@ class _MyHomePageState extends State { } Future _makePhoneCall(String phoneNumber) async { - // Use `Uri` to ensure that `phoneNumber` is properly URL-encoded. - // Just using 'tel:$phoneNumber' would create invalid URLs in some cases, - // such as spaces in the input, which would cause `launch` to fail on some - // platforms. final Uri launchUri = Uri( scheme: 'tel', path: phoneNumber, ); - await launch(launchUri.toString()); + await launchUrl(launchUri); } @override Widget build(BuildContext context) { // onPressed calls using this URL are not gated on a 'canLaunch' check // because the assumption is that every device can launch a web URL. - const String toLaunch = 'https://www.cylog.org/headers/'; + final Uri toLaunch = + Uri(scheme: 'https', host: 'www.cylog.org', path: 'headers/'); return Scaffold( appBar: AppBar( title: Text(widget.title), @@ -160,9 +152,9 @@ class _MyHomePageState extends State { ? const Text('Make phone call') : const Text('Calling not supported'), ), - const Padding( - padding: EdgeInsets.all(16.0), - child: Text(toLaunch), + Padding( + padding: const EdgeInsets.all(16.0), + child: Text(toLaunch.toString()), ), ElevatedButton( onPressed: () => setState(() { @@ -179,15 +171,15 @@ class _MyHomePageState extends State { ), ElevatedButton( onPressed: () => setState(() { - _launched = _launchInWebViewWithJavaScript(toLaunch); + _launched = _launchInWebViewWithoutJavaScript(toLaunch); }), - child: const Text('Launch in app(JavaScript ON)'), + child: const Text('Launch in app (JavaScript OFF)'), ), ElevatedButton( onPressed: () => setState(() { - _launched = _launchInWebViewWithDomStorage(toLaunch); + _launched = _launchInWebViewWithoutDomStorage(toLaunch); }), - child: const Text('Launch in app(DOM storage ON)'), + child: const Text('Launch in app (DOM storage OFF)'), ), const Padding(padding: EdgeInsets.all(16.0)), ElevatedButton( @@ -203,7 +195,7 @@ class _MyHomePageState extends State { _launched = _launchInWebViewOrVC(toLaunch); Timer(const Duration(seconds: 5), () { print('Closing WebView after 5 seconds...'); - closeWebView(); + closeInAppWebView(); }); }), child: const Text('Launch in app + close after 5 seconds'), diff --git a/packages/url_launcher/url_launcher/lib/src/legacy_api.dart b/packages/url_launcher/url_launcher/lib/src/legacy_api.dart new file mode 100644 index 000000000000..a61b200003a0 --- /dev/null +++ b/packages/url_launcher/url_launcher/lib/src/legacy_api.dart @@ -0,0 +1,154 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +/// Parses the specified URL string and delegates handling of it to the +/// underlying platform. +/// +/// The returned future completes with a [PlatformException] on invalid URLs and +/// schemes which cannot be handled, that is when [canLaunch] would complete +/// with false. +/// +/// By default when [forceSafariVC] is unset, the launcher +/// opens web URLs in the Safari View Controller, anything else is opened +/// using the default handler on the platform. If set to true, it opens the +/// URL in the Safari View Controller. If false, the URL is opened in the +/// default browser of the phone. Note that to work with universal links on iOS, +/// this must be set to false to let the platform's system handle the URL. +/// Set this to false if you want to use the cookies/context of the main browser +/// of the app (such as SSO flows). This setting will nullify [universalLinksOnly] +/// and will always launch a web content in the built-in Safari View Controller regardless +/// if the url is a universal link or not. +/// +/// [universalLinksOnly] is only used in iOS with iOS version >= 10.0. This setting is only validated +/// when [forceSafariVC] is set to false. The default value of this setting is false. +/// By default (when unset), the launcher will either launch the url in a browser (when the +/// url is not a universal link), or launch the respective native app content (when +/// the url is a universal link). When set to true, the launcher will only launch +/// the content if the url is a universal link and the respective app for the universal +/// link is installed on the user's device; otherwise throw a [PlatformException]. +/// +/// [forceWebView] is an Android only setting. If null or false, the URL is +/// always launched with the default browser on device. If set to true, the URL +/// is launched in a WebView. Unlike iOS, browser context is shared across +/// WebViews. +/// [enableJavaScript] is an Android only setting. If true, WebView enable +/// javascript. +/// [enableDomStorage] is an Android only setting. If true, WebView enable +/// DOM storage. +/// [headers] is an Android only setting that adds headers to the WebView. +/// When not using a WebView, the header information is passed to the browser, +/// some Android browsers do not support the [Browser.EXTRA_HEADERS](https://developer.android.com/reference/android/provider/Browser#EXTRA_HEADERS) +/// intent extra and the header information will be lost. +/// [webOnlyWindowName] is an Web only setting . _blank opens the new url in new tab , +/// _self opens the new url in current tab. +/// Default behaviour is to open the url in new tab. +/// +/// Note that if any of the above are set to true but the URL is not a web URL, +/// this will throw a [PlatformException]. +/// +/// [statusBarBrightness] Sets the status bar brightness of the application +/// after opening a link on iOS. Does nothing if no value is passed. This does +/// not handle resetting the previous status bar style. +/// +/// Returns true if launch url is successful; false is only returned when [universalLinksOnly] +/// is set to true and the universal link failed to launch. +@Deprecated('Use launchUrl instead') +Future launch( + String urlString, { + bool? forceSafariVC, + bool forceWebView = false, + bool enableJavaScript = false, + bool enableDomStorage = false, + bool universalLinksOnly = false, + Map headers = const {}, + Brightness? statusBarBrightness, + String? webOnlyWindowName, +}) async { + final Uri? url = Uri.tryParse(urlString.trimLeft()); + final bool isWebURL = + url != null && (url.scheme == 'http' || url.scheme == 'https'); + + if ((forceSafariVC == true || forceWebView == true) && !isWebURL) { + throw PlatformException( + code: 'NOT_A_WEB_SCHEME', + message: 'To use webview or safariVC, you need to pass' + 'in a web URL. This $urlString is not a web URL.'); + } + + /// [true] so that ui is automatically computed if [statusBarBrightness] is set. + bool previousAutomaticSystemUiAdjustment = true; + if (statusBarBrightness != null && + defaultTargetPlatform == TargetPlatform.iOS && + _ambiguate(WidgetsBinding.instance) != null) { + previousAutomaticSystemUiAdjustment = _ambiguate(WidgetsBinding.instance)! + .renderView + .automaticSystemUiAdjustment; + _ambiguate(WidgetsBinding.instance)! + .renderView + .automaticSystemUiAdjustment = false; + SystemChrome.setSystemUIOverlayStyle(statusBarBrightness == Brightness.light + ? SystemUiOverlayStyle.dark + : SystemUiOverlayStyle.light); + } + + final bool result = await UrlLauncherPlatform.instance.launch( + urlString, + useSafariVC: forceSafariVC ?? isWebURL, + useWebView: forceWebView, + enableJavaScript: enableJavaScript, + enableDomStorage: enableDomStorage, + universalLinksOnly: universalLinksOnly, + headers: headers, + webOnlyWindowName: webOnlyWindowName, + ); + + if (statusBarBrightness != null && + _ambiguate(WidgetsBinding.instance) != null) { + _ambiguate(WidgetsBinding.instance)! + .renderView + .automaticSystemUiAdjustment = previousAutomaticSystemUiAdjustment; + } + + return result; +} + +/// Checks whether the specified URL can be handled by some app installed on the +/// device. +/// +/// On some systems, such as recent versions of Android and iOS, this will +/// always return false unless the application has been configuration to allow +/// querying the system for launch support. See +/// [the README](https://pub.dev/packages/url_launcher#configuration) for +/// details. +@Deprecated('Use canLaunchUrl instead') +Future canLaunch(String urlString) async { + return await UrlLauncherPlatform.instance.canLaunch(urlString); +} + +/// Closes the current WebView, if one was previously opened via a call to [launch]. +/// +/// If [launch] was never called, then this call will not have any effect. +/// +/// On Android systems, if [launch] was called without `forceWebView` being set to `true` +/// Or on IOS systems, if [launch] was called without `forceSafariVC` being set to `true`, +/// this call will not do anything either, simply because there is no +/// WebView/SafariViewController available to be closed. +@Deprecated('Use closeInAppWebView instead') +Future closeWebView() async { + return await UrlLauncherPlatform.instance.closeWebView(); +} + +/// This allows a value of type T or T? to be treated as a value of type T?. +/// +/// We use this so that APIs that have become non-nullable can still be used +/// with `!` and `?` on the stable branch. +// TODO(ianh): Remove this once we roll stable in late 2021. +T? _ambiguate(T? value) => value; diff --git a/packages/url_launcher/url_launcher/lib/src/link.dart b/packages/url_launcher/url_launcher/lib/src/link.dart index 016f97daacbf..76cb97748003 100644 --- a/packages/url_launcher/url_launcher/lib/src/link.dart +++ b/packages/url_launcher/url_launcher/lib/src/link.dart @@ -6,10 +6,12 @@ import 'dart:async'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; -import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher_platform_interface/link.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; +import 'types.dart'; +import 'url_launcher_uri.dart'; + /// The function used to push routes to the Flutter framework. @visibleForTesting Future Function(Object?, String) pushRouteToFrameworkFunction = @@ -107,7 +109,8 @@ class DefaultLinkDelegate extends StatelessWidget { } Future _followLink(BuildContext context) async { - if (!link.uri!.hasScheme) { + final Uri url = link.uri!; + if (!url.hasScheme) { // A uri that doesn't have a scheme is an internal route name. In this // case, we push it via Flutter's navigation system instead of letting the // browser handle it. @@ -116,18 +119,18 @@ class DefaultLinkDelegate extends StatelessWidget { return; } - // At this point, we know that the link is external. So we use the `launch` - // API to open the link. - final String urlString = link.uri.toString(); - if (await canLaunch(urlString)) { - await launch( - urlString, - forceSafariVC: _useWebView, - forceWebView: _useWebView, + // At this point, we know that the link is external. So we use the + // `launchUrl` API to open the link. + if (await canLaunchUrl(url)) { + await launchUrl( + url, + mode: _useWebView + ? LaunchMode.inAppWebView + : LaunchMode.externalApplication, ); } else { FlutterError.reportError(FlutterErrorDetails( - exception: 'Could not launch link $urlString', + exception: 'Could not launch link ${url.toString()}', stack: StackTrace.current, library: 'url_launcher', context: ErrorDescription('during launching a link'), diff --git a/packages/url_launcher/url_launcher/lib/src/types.dart b/packages/url_launcher/url_launcher/lib/src/types.dart new file mode 100644 index 000000000000..bcfcb7887b17 --- /dev/null +++ b/packages/url_launcher/url_launcher/lib/src/types.dart @@ -0,0 +1,54 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; + +/// The desired mode to launch a URL. +/// +/// Support for these modes varies by platform. Platforms that do not support +/// the requested mode may substitute another mode. See [launchUrl] for more +/// details. +enum LaunchMode { + /// Leaves the decision of how to launch the URL to the platform + /// implementation. + platformDefault, + + /// Loads the URL in an in-app web view (e.g., Safari View Controller). + inAppWebView, + + /// Passes the URL to the OS to be handled by another application. + externalApplication, + + /// Passes the URL to the OS to be handled by another non-browser application. + externalNonBrowserApplication, +} + +/// Additional configuration options for [LaunchMode.inAppWebView]. +@immutable +class WebViewConfiguration { + /// Creates a new WebViewConfiguration with the given settings. + const WebViewConfiguration({ + this.enableJavaScript = true, + this.enableDomStorage = true, + this.headers = const {}, + }); + + /// Whether or not JavaScript is enabled for the web content. + /// + /// Disabling this may not be supported on all platforms. + final bool enableJavaScript; + + /// Whether or not DOM storage is enabled for the web content. + /// + /// Disabling this may not be supported on all platforms. + final bool enableDomStorage; + + /// Additional headers to pass in the load request. + /// + /// On Android, this may work even when not loading in an in-app web view. + /// When loading in an external browsers, this sets + /// [Browser.EXTRA_HEADERS](https://developer.android.com/reference/android/provider/Browser#EXTRA_HEADERS) + /// Not all browsers support this, so it is not guaranteed to be honored. + final Map headers; +} diff --git a/packages/url_launcher/url_launcher/lib/src/url_launcher_string.dart b/packages/url_launcher/url_launcher/lib/src/url_launcher_string.dart new file mode 100644 index 000000000000..bee2a80a59c0 --- /dev/null +++ b/packages/url_launcher/url_launcher/lib/src/url_launcher_string.dart @@ -0,0 +1,65 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +import 'types.dart'; + +/// String version of [launchUrl]. +/// +/// This should be used only in the very rare case of needing to launch a URL +/// that is considered valid by the host platform, but not by Dart's [Uri] +/// class. In all other cases, use [launchUrl] instead, as that will ensure +/// that you are providing a valid URL. +/// +/// The behavior of this method when passing an invalid URL is entirely +/// platform-specific; no effort is made by the plugin to make the URL valid. +/// Some platforms may provide best-effort interpretation of an invalid URL, +/// others will immediately fail if the URL can't be parsed according to the +/// official standards that define URL formats. +Future launchUrlString( + String urlString, { + LaunchMode mode = LaunchMode.platformDefault, + WebViewConfiguration webViewConfiguration = const WebViewConfiguration(), + String? webOnlyWindowName, +}) async { + final bool isWebURL = + urlString.startsWith('http:') || urlString.startsWith('https:'); + if (mode == LaunchMode.inAppWebView && !isWebURL) { + throw ArgumentError.value(urlString, 'urlString', + 'To use an in-app web view, you must provide an http(s) URL.'); + } + final bool useWebView = mode == LaunchMode.inAppWebView || + (isWebURL && mode == LaunchMode.platformDefault); + + // TODO(stuartmorgan): Create a replacement platform interface method that + // uses something more like the new argument structure, and switch to using + // that, to support launch mode on more platforms. + return await UrlLauncherPlatform.instance.launch( + urlString, + useSafariVC: useWebView, + useWebView: useWebView, + enableJavaScript: webViewConfiguration.enableJavaScript, + enableDomStorage: webViewConfiguration.enableDomStorage, + universalLinksOnly: mode == LaunchMode.externalNonBrowserApplication, + headers: webViewConfiguration.headers, + webOnlyWindowName: webOnlyWindowName, + ); +} + +/// String version of [canLaunchUrl]. +/// +/// This should be used only in the very rare case of needing to check a URL +/// that is considered valid by the host platform, but not by Dart's [Uri] +/// class. In all other cases, use [canLaunchUrl] instead, as that will ensure +/// that you are providing a valid URL. +/// +/// The behavior of this method when passing an invalid URL is entirely +/// platform-specific; no effort is made by the plugin to make the URL valid. +/// Some platforms may provide best-effort interpretation of an invalid URL, +/// others will immediately fail if the URL can't be parsed according to the +/// official standards that define URL formats. +Future canLaunchUrlString(String urlString) async { + return await UrlLauncherPlatform.instance.canLaunch(urlString); +} diff --git a/packages/url_launcher/url_launcher/lib/src/url_launcher_uri.dart b/packages/url_launcher/url_launcher/lib/src/url_launcher_uri.dart new file mode 100644 index 000000000000..1ca787f44180 --- /dev/null +++ b/packages/url_launcher/url_launcher/lib/src/url_launcher_uri.dart @@ -0,0 +1,90 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:url_launcher/url_launcher_string.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +import 'types.dart'; + +/// Passes [url] to the underlying platform for handling. +/// +/// [mode] support varies significantly by platform: +/// - [LaunchMode.platformDefault] is supported on all platforms: +/// - On iOS and Android, this treats web URLs as +/// [LaunchMode.inAppWebView], and all other URLs as +/// [LaunchMode.externalApplication]. +/// - On Windows, macOS, and Linux this behaves like +/// [LaunchMode.externalApplication]. +/// - On web, this uses `webOnlyWindowName` for web URLs, and behaves like +/// [LaunchMode.externalApplication] for any other content. +/// - [LaunchMode.inAppWebView] is currently only supported on iOS and +/// Android. If a non-web URL is passed with this mode, an [ArgumentError] +/// will be thrown. +/// - [LaunchMode.externalApplication] is supported on all platforms. +/// On iOS, this should be used in cases where sharing the cookies of the +/// user's browser is important, such as SSO flows, since Safari View +/// Controller does not share the browser's context. +/// - [LaunchMode.externalNonBrowserApplication] is supported on iOS 10+. +/// This setting is used to require universal links to open in a non-browser +/// application. +/// +/// For web, [webOnlyWindowName] specifies a target for the launch. This +/// supports the standard special link target names. For example: +/// - "_blank" opens the new URL in a new tab. +/// - "_self" opens the new URL in the current tab. +/// Default behaviour when unset is to open the url in a new tab. +/// +/// Returns true if the URL was launched successful, otherwise either returns +/// false or throws a [PlatformException] depending on the failure. +Future launchUrl( + Uri url, { + LaunchMode mode = LaunchMode.platformDefault, + WebViewConfiguration webViewConfiguration = const WebViewConfiguration(), + String? webOnlyWindowName, +}) async { + final bool isWebURL = url.scheme == 'http' || url.scheme == 'https'; + if (mode == LaunchMode.inAppWebView && !isWebURL) { + throw ArgumentError.value(url, 'url', + 'To use an in-app web view, you must provide an http(s) URL.'); + } + // TODO(stuartmorgan): Use UrlLauncherPlatform directly once a new API + // that better matches these parameters has been added. For now, delegate to + // launchUrlString so that there's only one copy of the parameter translation + // logic. + return await launchUrlString( + url.toString(), + mode: mode, + webViewConfiguration: webViewConfiguration, + webOnlyWindowName: webOnlyWindowName, + ); +} + +/// Checks whether the specified URL can be handled by some app installed on the +/// device. +/// +/// Returns true if it is possible to verify that there is a handler available. +/// A false return value can indicate either that there is no handler available, +/// or that the application does not have permission to check. For example: +/// - On recent versions of Android and iOS, this will always return false +/// unless the application has been configuration to allow +/// querying the system for launch support. See +/// [the README](https://pub.dev/packages/url_launcher#configuration) for +/// details. +/// - On web, this will always return false except for a few specific schemes +/// that are always assumed to be supported (such as http(s)), as web pages +/// are never allowed to query installed applications. +Future canLaunchUrl(Uri url) async { + return await UrlLauncherPlatform.instance.canLaunch(url.toString()); +} + +/// Closes the current in-app web view, if one was previously opened by +/// [launchUrl]. +/// +/// If [launchUrl] was never called with [LaunchMode.inAppWebView], then this +/// call will have no effect. +Future closeInAppWebView() async { + return await UrlLauncherPlatform.instance.closeWebView(); +} diff --git a/packages/url_launcher/url_launcher/lib/url_launcher.dart b/packages/url_launcher/url_launcher/lib/url_launcher.dart index f28c460cce4f..36c7b60fdacd 100644 --- a/packages/url_launcher/url_launcher/lib/url_launcher.dart +++ b/packages/url_launcher/url_launcher/lib/url_launcher.dart @@ -2,150 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; -import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; - -/// Parses the specified URL string and delegates handling of it to the -/// underlying platform. -/// -/// The returned future completes with a [PlatformException] on invalid URLs and -/// schemes which cannot be handled, that is when [canLaunch] would complete -/// with false. -/// -/// By default when [forceSafariVC] is unset, the launcher -/// opens web URLs in the Safari View Controller, anything else is opened -/// using the default handler on the platform. If set to true, it opens the -/// URL in the Safari View Controller. If false, the URL is opened in the -/// default browser of the phone. Note that to work with universal links on iOS, -/// this must be set to false to let the platform's system handle the URL. -/// Set this to false if you want to use the cookies/context of the main browser -/// of the app (such as SSO flows). This setting will nullify [universalLinksOnly] -/// and will always launch a web content in the built-in Safari View Controller regardless -/// if the url is a universal link or not. -/// -/// [universalLinksOnly] is only used in iOS with iOS version >= 10.0. This setting is only validated -/// when [forceSafariVC] is set to false. The default value of this setting is false. -/// By default (when unset), the launcher will either launch the url in a browser (when the -/// url is not a universal link), or launch the respective native app content (when -/// the url is a universal link). When set to true, the launcher will only launch -/// the content if the url is a universal link and the respective app for the universal -/// link is installed on the user's device; otherwise throw a [PlatformException]. -/// -/// [forceWebView] is an Android only setting. If null or false, the URL is -/// always launched with the default browser on device. If set to true, the URL -/// is launched in a WebView. Unlike iOS, browser context is shared across -/// WebViews. -/// [enableJavaScript] is an Android only setting. If true, WebView enable -/// javascript. -/// [enableDomStorage] is an Android only setting. If true, WebView enable -/// DOM storage. -/// [headers] is an Android only setting that adds headers to the WebView. -/// When not using a WebView, the header information is passed to the browser, -/// some Android browsers do not support the [Browser.EXTRA_HEADERS](https://developer.android.com/reference/android/provider/Browser#EXTRA_HEADERS) -/// intent extra and the header information will be lost. -/// [webOnlyWindowName] is an Web only setting . _blank opens the new url in new tab , -/// _self opens the new url in current tab. -/// Default behaviour is to open the url in new tab. -/// -/// Note that if any of the above are set to true but the URL is not a web URL, -/// this will throw a [PlatformException]. -/// -/// [statusBarBrightness] Sets the status bar brightness of the application -/// after opening a link on iOS. Does nothing if no value is passed. This does -/// not handle resetting the previous status bar style. -/// -/// Returns true if launch url is successful; false is only returned when [universalLinksOnly] -/// is set to true and the universal link failed to launch. -Future launch( - String urlString, { - bool? forceSafariVC, - bool forceWebView = false, - bool enableJavaScript = false, - bool enableDomStorage = false, - bool universalLinksOnly = false, - Map headers = const {}, - Brightness? statusBarBrightness, - String? webOnlyWindowName, -}) async { - final Uri? url = Uri.tryParse(urlString.trimLeft()); - final bool isWebURL = - url != null && (url.scheme == 'http' || url.scheme == 'https'); - - if ((forceSafariVC == true || forceWebView == true) && !isWebURL) { - throw PlatformException( - code: 'NOT_A_WEB_SCHEME', - message: 'To use webview or safariVC, you need to pass' - 'in a web URL. This $urlString is not a web URL.'); - } - - /// [true] so that ui is automatically computed if [statusBarBrightness] is set. - bool previousAutomaticSystemUiAdjustment = true; - if (statusBarBrightness != null && - defaultTargetPlatform == TargetPlatform.iOS && - _ambiguate(WidgetsBinding.instance) != null) { - previousAutomaticSystemUiAdjustment = _ambiguate(WidgetsBinding.instance)! - .renderView - .automaticSystemUiAdjustment; - _ambiguate(WidgetsBinding.instance)! - .renderView - .automaticSystemUiAdjustment = false; - SystemChrome.setSystemUIOverlayStyle(statusBarBrightness == Brightness.light - ? SystemUiOverlayStyle.dark - : SystemUiOverlayStyle.light); - } - - final bool result = await UrlLauncherPlatform.instance.launch( - urlString, - useSafariVC: forceSafariVC ?? isWebURL, - useWebView: forceWebView, - enableJavaScript: enableJavaScript, - enableDomStorage: enableDomStorage, - universalLinksOnly: universalLinksOnly, - headers: headers, - webOnlyWindowName: webOnlyWindowName, - ); - - if (statusBarBrightness != null && - _ambiguate(WidgetsBinding.instance) != null) { - _ambiguate(WidgetsBinding.instance)! - .renderView - .automaticSystemUiAdjustment = previousAutomaticSystemUiAdjustment; - } - - return result; -} - -/// Checks whether the specified URL can be handled by some app installed on the -/// device. -/// -/// On some systems, such as recent versions of Android and iOS, this will -/// always return false unless the application has been configuration to allow -/// querying the system for launch support. See -/// [the README](https://pub.dev/packages/url_launcher#configuration) for -/// details. -Future canLaunch(String urlString) async { - return await UrlLauncherPlatform.instance.canLaunch(urlString); -} - -/// Closes the current WebView, if one was previously opened via a call to [launch]. -/// -/// If [launch] was never called, then this call will not have any effect. -/// -/// On Android systems, if [launch] was called without `forceWebView` being set to `true` -/// Or on IOS systems, if [launch] was called without `forceSafariVC` being set to `true`, -/// this call will not do anything either, simply because there is no -/// WebView/SafariViewController available to be closed. -Future closeWebView() async { - return await UrlLauncherPlatform.instance.closeWebView(); -} - -/// This allows a value of type T or T? to be treated as a value of type T?. -/// -/// We use this so that APIs that have become non-nullable can still be used -/// with `!` and `?` on the stable branch. -// TODO(ianh): Remove this once we roll stable in late 2021. -T? _ambiguate(T? value) => value; +export 'src/legacy_api.dart'; +export 'src/types.dart'; +export 'src/url_launcher_uri.dart'; diff --git a/packages/url_launcher/url_launcher/lib/url_launcher_string.dart b/packages/url_launcher/url_launcher/lib/url_launcher_string.dart new file mode 100644 index 000000000000..b5a12b1e39ca --- /dev/null +++ b/packages/url_launcher/url_launcher/lib/url_launcher_string.dart @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Provides a String-based alterantive to the Uri-based primary API. +// +// This is provided as a separate import because it's much easier to use +// incorrectly, so should require explicit opt-in (to avoid issues such as +// IDE auto-complete to the more error-prone APIs just by importing the +// main API). + +export 'src/types.dart'; +export 'src/url_launcher_string.dart'; diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index feb0a2c9ad95..6803d71032cb 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.0.20 +version: 6.1.0 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher/test/link_test.dart b/packages/url_launcher/url_launcher/test/link_test.dart index f7a98a0bf2f0..6242397c5ed7 100644 --- a/packages/url_launcher/url_launcher/test/link_test.dart +++ b/packages/url_launcher/url_launcher/test/link_test.dart @@ -9,7 +9,7 @@ import 'package:url_launcher/link.dart'; import 'package:url_launcher/src/link.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; -import 'mock_url_launcher_platform.dart'; +import 'mocks/mock_url_launcher_platform.dart'; void main() { late MockUrlLauncher mock; @@ -58,8 +58,8 @@ void main() { useSafariVC: false, useWebView: false, universalLinksOnly: false, - enableJavaScript: false, - enableDomStorage: false, + enableJavaScript: true, + enableDomStorage: true, headers: {}, webOnlyWindowName: null, ) @@ -88,8 +88,8 @@ void main() { useSafariVC: true, useWebView: true, universalLinksOnly: false, - enableJavaScript: false, - enableDomStorage: false, + enableJavaScript: true, + enableDomStorage: true, headers: {}, webOnlyWindowName: null, ) diff --git a/packages/url_launcher/url_launcher/test/mock_url_launcher_platform.dart b/packages/url_launcher/url_launcher/test/mocks/mock_url_launcher_platform.dart similarity index 100% rename from packages/url_launcher/url_launcher/test/mock_url_launcher_platform.dart rename to packages/url_launcher/url_launcher/test/mocks/mock_url_launcher_platform.dart diff --git a/packages/url_launcher/url_launcher/test/url_launcher_test.dart b/packages/url_launcher/url_launcher/test/src/legacy_api_test.dart similarity index 99% rename from packages/url_launcher/url_launcher/test/url_launcher_test.dart rename to packages/url_launcher/url_launcher/test/src/legacy_api_test.dart index 4e980cb37253..4594ab21bffc 100644 --- a/packages/url_launcher/url_launcher/test/url_launcher_test.dart +++ b/packages/url_launcher/url_launcher/test/src/legacy_api_test.dart @@ -8,10 +8,10 @@ import 'dart:ui'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart' show PlatformException; import 'package:flutter_test/flutter_test.dart'; -import 'package:url_launcher/url_launcher.dart'; +import 'package:url_launcher/src/legacy_api.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; -import 'mock_url_launcher_platform.dart'; +import '../mocks/mock_url_launcher_platform.dart'; void main() { final MockUrlLauncher mock = MockUrlLauncher(); diff --git a/packages/url_launcher/url_launcher/test/src/url_launcher_string_test.dart b/packages/url_launcher/url_launcher/test/src/url_launcher_string_test.dart new file mode 100644 index 000000000000..95b2f5c27bf3 --- /dev/null +++ b/packages/url_launcher/url_launcher/test/src/url_launcher_string_test.dart @@ -0,0 +1,279 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:url_launcher/src/types.dart'; +import 'package:url_launcher/src/url_launcher_string.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +import '../mocks/mock_url_launcher_platform.dart'; + +void main() { + final MockUrlLauncher mock = MockUrlLauncher(); + UrlLauncherPlatform.instance = mock; + + group('canLaunchUrlString', () { + test('handles returning true', () async { + const String urlString = 'https://flutter.dev'; + mock + ..setCanLaunchExpectations(urlString) + ..setResponse(true); + + final bool result = await canLaunchUrlString(urlString); + + expect(result, isTrue); + }); + + test('handles returning false', () async { + const String urlString = 'https://flutter.dev'; + mock + ..setCanLaunchExpectations(urlString) + ..setResponse(false); + + final bool result = await canLaunchUrlString(urlString); + + expect(result, isFalse); + }); + }); + + group('launchUrlString', () { + test('default behavior with web URL', () async { + const String urlString = 'https://flutter.dev'; + mock + ..setLaunchExpectations( + url: urlString, + useSafariVC: true, + useWebView: true, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect(await launchUrlString(urlString), isTrue); + }); + + test('default behavior with non-web URL', () async { + const String urlString = 'customscheme:foo'; + mock + ..setLaunchExpectations( + url: urlString, + useSafariVC: false, + useWebView: false, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect(await launchUrlString(urlString), isTrue); + }); + + test('explicit default launch mode with web URL', () async { + const String urlString = 'https://flutter.dev'; + mock + ..setLaunchExpectations( + url: urlString, + useSafariVC: true, + useWebView: true, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect(await launchUrlString(urlString, mode: LaunchMode.platformDefault), + isTrue); + }); + + test('explicit default launch mode with non-web URL', () async { + const String urlString = 'customscheme:foo'; + mock + ..setLaunchExpectations( + url: urlString, + useSafariVC: false, + useWebView: false, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect(await launchUrlString(urlString, mode: LaunchMode.platformDefault), + isTrue); + }); + + test('in-app webview', () async { + const String urlString = 'https://flutter.dev'; + mock + ..setLaunchExpectations( + url: urlString, + useSafariVC: true, + useWebView: true, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect(await launchUrlString(urlString, mode: LaunchMode.inAppWebView), + isTrue); + }); + + test('external browser', () async { + const String urlString = 'https://flutter.dev'; + mock + ..setLaunchExpectations( + url: urlString, + useSafariVC: false, + useWebView: false, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect( + await launchUrlString(urlString, + mode: LaunchMode.externalApplication), + isTrue); + }); + + test('external non-browser only', () async { + const String urlString = 'https://flutter.dev'; + mock + ..setLaunchExpectations( + url: urlString, + useSafariVC: false, + useWebView: false, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: true, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect( + await launchUrlString(urlString, + mode: LaunchMode.externalNonBrowserApplication), + isTrue); + }); + + test('in-app webview without javascript', () async { + const String urlString = 'https://flutter.dev'; + mock + ..setLaunchExpectations( + url: urlString, + useSafariVC: true, + useWebView: true, + enableJavaScript: false, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect( + await launchUrlString(urlString, + mode: LaunchMode.inAppWebView, + webViewConfiguration: + const WebViewConfiguration(enableJavaScript: false)), + isTrue); + }); + + test('in-app webview without DOM storage', () async { + const String urlString = 'https://flutter.dev'; + mock + ..setLaunchExpectations( + url: urlString, + useSafariVC: true, + useWebView: true, + enableJavaScript: true, + enableDomStorage: false, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect( + await launchUrlString(urlString, + mode: LaunchMode.inAppWebView, + webViewConfiguration: + const WebViewConfiguration(enableDomStorage: false)), + isTrue); + }); + + test('in-app webview with headers', () async { + const String urlString = 'https://flutter.dev'; + mock + ..setLaunchExpectations( + url: urlString, + useSafariVC: true, + useWebView: true, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {'key': 'value'}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect( + await launchUrlString(urlString, + mode: LaunchMode.inAppWebView, + webViewConfiguration: const WebViewConfiguration( + headers: {'key': 'value'})), + isTrue); + }); + + test('cannot launch a non-web URL in a webview', () async { + expect( + () async => await launchUrlString('tel:555-555-5555', + mode: LaunchMode.inAppWebView), + throwsA(isA())); + }); + + test('non-web URL with default options', () async { + const String emailLaunchUrlString = + 'mailto:smith@example.com?subject=Hello'; + mock + ..setLaunchExpectations( + url: emailLaunchUrlString, + useSafariVC: false, + useWebView: false, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect(await launchUrlString(emailLaunchUrlString), isTrue); + }); + + test('allows non-parseable url', () async { + // Not a valid Dart [Uri], but a valid URL on at least some platforms. + const String urlString = + 'rdp://full%20address=s:mypc:3389&audiomode=i:2&disable%20themes=i:1'; + mock + ..setLaunchExpectations( + url: urlString, + useSafariVC: false, + useWebView: false, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect(await launchUrlString(urlString), isTrue); + }); + }); +} diff --git a/packages/url_launcher/url_launcher/test/src/url_launcher_uri_test.dart b/packages/url_launcher/url_launcher/test/src/url_launcher_uri_test.dart new file mode 100644 index 000000000000..8286e0c43d20 --- /dev/null +++ b/packages/url_launcher/url_launcher/test/src/url_launcher_uri_test.dart @@ -0,0 +1,262 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:url_launcher/src/types.dart'; +import 'package:url_launcher/src/url_launcher_uri.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +import '../mocks/mock_url_launcher_platform.dart'; + +void main() { + final MockUrlLauncher mock = MockUrlLauncher(); + UrlLauncherPlatform.instance = mock; + + test('closeInAppWebView', () async { + await closeInAppWebView(); + expect(mock.closeWebViewCalled, isTrue); + }); + + group('canLaunchUrl', () { + test('handles returning true', () async { + final Uri url = Uri.parse('https://flutter.dev'); + mock + ..setCanLaunchExpectations(url.toString()) + ..setResponse(true); + + final bool result = await canLaunchUrl(url); + + expect(result, isTrue); + }); + + test('handles returning false', () async { + final Uri url = Uri.parse('https://flutter.dev'); + mock + ..setCanLaunchExpectations(url.toString()) + ..setResponse(false); + + final bool result = await canLaunchUrl(url); + + expect(result, isFalse); + }); + }); + + group('launchUrl', () { + test('default behavior with web URL', () async { + final Uri url = Uri.parse('https://flutter.dev'); + mock + ..setLaunchExpectations( + url: url.toString(), + useSafariVC: true, + useWebView: true, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect(await launchUrl(url), isTrue); + }); + + test('default behavior with non-web URL', () async { + final Uri url = Uri.parse('customscheme:foo'); + mock + ..setLaunchExpectations( + url: url.toString(), + useSafariVC: false, + useWebView: false, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect(await launchUrl(url), isTrue); + }); + + test('explicit default launch mode with web URL', () async { + final Uri url = Uri.parse('https://flutter.dev'); + mock + ..setLaunchExpectations( + url: url.toString(), + useSafariVC: true, + useWebView: true, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect(await launchUrl(url, mode: LaunchMode.platformDefault), isTrue); + }); + + test('explicit default launch mode with non-web URL', () async { + final Uri url = Uri.parse('customscheme:foo'); + mock + ..setLaunchExpectations( + url: url.toString(), + useSafariVC: false, + useWebView: false, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect(await launchUrl(url, mode: LaunchMode.platformDefault), isTrue); + }); + + test('in-app webview', () async { + final Uri url = Uri.parse('https://flutter.dev'); + mock + ..setLaunchExpectations( + url: url.toString(), + useSafariVC: true, + useWebView: true, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect(await launchUrl(url, mode: LaunchMode.inAppWebView), isTrue); + }); + + test('external browser', () async { + final Uri url = Uri.parse('https://flutter.dev'); + mock + ..setLaunchExpectations( + url: url.toString(), + useSafariVC: false, + useWebView: false, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect( + await launchUrl(url, mode: LaunchMode.externalApplication), isTrue); + }); + + test('external non-browser only', () async { + final Uri url = Uri.parse('https://flutter.dev'); + mock + ..setLaunchExpectations( + url: url.toString(), + useSafariVC: false, + useWebView: false, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: true, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect( + await launchUrl(url, mode: LaunchMode.externalNonBrowserApplication), + isTrue); + }); + + test('in-app webview without javascript', () async { + final Uri url = Uri.parse('https://flutter.dev'); + mock + ..setLaunchExpectations( + url: url.toString(), + useSafariVC: true, + useWebView: true, + enableJavaScript: false, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect( + await launchUrl(url, + mode: LaunchMode.inAppWebView, + webViewConfiguration: + const WebViewConfiguration(enableJavaScript: false)), + isTrue); + }); + + test('in-app webview without DOM storage', () async { + final Uri url = Uri.parse('https://flutter.dev'); + mock + ..setLaunchExpectations( + url: url.toString(), + useSafariVC: true, + useWebView: true, + enableJavaScript: true, + enableDomStorage: false, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect( + await launchUrl(url, + mode: LaunchMode.inAppWebView, + webViewConfiguration: + const WebViewConfiguration(enableDomStorage: false)), + isTrue); + }); + + test('in-app webview with headers', () async { + final Uri url = Uri.parse('https://flutter.dev'); + mock + ..setLaunchExpectations( + url: url.toString(), + useSafariVC: true, + useWebView: true, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {'key': 'value'}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect( + await launchUrl(url, + mode: LaunchMode.inAppWebView, + webViewConfiguration: const WebViewConfiguration( + headers: {'key': 'value'})), + isTrue); + }); + + test('cannot launch a non-web URL in a webview', () async { + expect( + () async => await launchUrl(Uri(scheme: 'tel', path: '555-555-5555'), + mode: LaunchMode.inAppWebView), + throwsA(isA())); + }); + + test('non-web URL with default options', () async { + final Uri emailLaunchUrl = Uri( + scheme: 'mailto', + path: 'smith@example.com', + queryParameters: {'subject': 'Hello'}, + ); + mock + ..setLaunchExpectations( + url: emailLaunchUrl.toString(), + useSafariVC: false, + useWebView: false, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect(await launchUrl(emailLaunchUrl), isTrue); + }); + }); +} From 4035b518b348be34ad317e8d741e7e49b43e5167 Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Fri, 22 Apr 2022 12:04:05 -0700 Subject: [PATCH 443/600] Added gaaclarke as codeowner (#5323) --- CODEOWNERS | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index fdf0e30550f7..bc3e392d1766 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -5,7 +5,10 @@ # reviewed by someone else. # Plugin-level rules. -packages/webview_flutter/** @bparrishMines +packages/path_provider/** @gaaclarke +packages/shared_preferences/** @gaaclarke +packages/video_player/** @gaaclarke +packages/webview_flutter/** @bparrishMines # Sub-package-level rules. These should stay last, since the last matching # entry takes precedence. From 10c8f9e3fba3ff98dd9344a09b1b6bb98efe3959 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 22 Apr 2022 16:14:09 -0400 Subject: [PATCH 444/600] [image_picker] Migrate iOS to Pigeon and improve state handling (#5285) --- .../image_picker_ios/CHANGELOG.md | 6 + .../ios/RunnerTests/ImagePickerPluginTests.m | 140 ++- .../ios/Classes/FLTImagePickerPlugin.m | 404 ++++---- .../ios/Classes/FLTImagePickerPlugin_Test.h | 67 +- .../image_picker_ios/ios/Classes/messages.g.h | 61 ++ .../image_picker_ios/ios/Classes/messages.g.m | 216 ++++ .../lib/image_picker_ios.dart | 209 ++++ .../image_picker_ios/lib/src/messages.g.dart | 194 ++++ .../image_picker_ios/pigeons/copyright.txt | 3 + .../image_picker_ios/pigeons/messages.dart | 47 + .../image_picker_ios/pubspec.yaml | 6 +- .../test/image_picker_ios_test.dart | 937 ++++++++++++++++++ .../image_picker_ios/test/test_api.dart | 127 +++ 13 files changed, 2129 insertions(+), 288 deletions(-) create mode 100644 packages/image_picker/image_picker_ios/ios/Classes/messages.g.h create mode 100644 packages/image_picker/image_picker_ios/ios/Classes/messages.g.m create mode 100644 packages/image_picker/image_picker_ios/lib/image_picker_ios.dart create mode 100644 packages/image_picker/image_picker_ios/lib/src/messages.g.dart create mode 100644 packages/image_picker/image_picker_ios/pigeons/copyright.txt create mode 100644 packages/image_picker/image_picker_ios/pigeons/messages.dart create mode 100644 packages/image_picker/image_picker_ios/test/image_picker_ios_test.dart create mode 100644 packages/image_picker/image_picker_ios/test/test_api.dart diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index 3472ade28d5b..3380d14418c2 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.8.5 + +* Switches to an in-package method channel based on Pigeon. +* Fixes invalid casts when selecting multiple images on versions of iOS before + 14.0. + ## 0.8.4+11 * Splits from `image_picker` as a federated implementation. diff --git a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m index 8df5299e54d9..04d491131d5b 100644 --- a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m +++ b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m @@ -47,14 +47,15 @@ - (void)testPluginPickImageDeviceBack { // Run test FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; - FlutterMethodCall *call = - [FlutterMethodCall methodCallWithMethodName:@"pickImage" - arguments:@{@"source" : @(0), @"cameraDevice" : @(0)}]; UIImagePickerController *controller = [[UIImagePickerController alloc] init]; [plugin setImagePickerControllerOverrides:@[ controller ]]; - [plugin handleMethodCall:call - result:^(id _Nullable r){ - }]; + + [plugin pickImageWithSource:[FLTSourceSpecification makeWithType:FLTSourceTypeCamera + camera:FLTSourceCameraRear] + maxSize:[[FLTMaxSize alloc] init] + quality:nil + completion:^(NSString *_Nullable result, FlutterError *_Nullable error){ + }]; XCTAssertEqual(controller.cameraDevice, UIImagePickerControllerCameraDeviceRear); } @@ -78,14 +79,15 @@ - (void)testPluginPickImageDeviceFront { // Run test FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; - FlutterMethodCall *call = - [FlutterMethodCall methodCallWithMethodName:@"pickImage" - arguments:@{@"source" : @(0), @"cameraDevice" : @(1)}]; UIImagePickerController *controller = [[UIImagePickerController alloc] init]; [plugin setImagePickerControllerOverrides:@[ controller ]]; - [plugin handleMethodCall:call - result:^(id _Nullable r){ - }]; + + [plugin pickImageWithSource:[FLTSourceSpecification makeWithType:FLTSourceTypeCamera + camera:FLTSourceCameraFront] + maxSize:[[FLTMaxSize alloc] init] + quality:nil + completion:^(NSString *_Nullable result, FlutterError *_Nullable error){ + }]; XCTAssertEqual(controller.cameraDevice, UIImagePickerControllerCameraDeviceFront); } @@ -109,14 +111,14 @@ - (void)testPluginPickVideoDeviceBack { // Run test FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; - FlutterMethodCall *call = - [FlutterMethodCall methodCallWithMethodName:@"pickVideo" - arguments:@{@"source" : @(0), @"cameraDevice" : @(0)}]; UIImagePickerController *controller = [[UIImagePickerController alloc] init]; [plugin setImagePickerControllerOverrides:@[ controller ]]; - [plugin handleMethodCall:call - result:^(id _Nullable r){ - }]; + + [plugin pickVideoWithSource:[FLTSourceSpecification makeWithType:FLTSourceTypeCamera + camera:FLTSourceCameraRear] + maxDuration:nil + completion:^(NSString *_Nullable result, FlutterError *_Nullable error){ + }]; XCTAssertEqual(controller.cameraDevice, UIImagePickerControllerCameraDeviceRear); } @@ -141,14 +143,14 @@ - (void)testPluginPickVideoDeviceFront { // Run test FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; - FlutterMethodCall *call = - [FlutterMethodCall methodCallWithMethodName:@"pickVideo" - arguments:@{@"source" : @(0), @"cameraDevice" : @(1)}]; UIImagePickerController *controller = [[UIImagePickerController alloc] init]; [plugin setImagePickerControllerOverrides:@[ controller ]]; - [plugin handleMethodCall:call - result:^(id _Nullable r){ - }]; + + [plugin pickVideoWithSource:[FLTSourceSpecification makeWithType:FLTSourceTypeCamera + camera:FLTSourceCameraFront] + maxDuration:nil + completion:^(NSString *_Nullable result, FlutterError *_Nullable error){ + }]; XCTAssertEqual(controller.cameraDevice, UIImagePickerControllerCameraDeviceFront); } @@ -165,17 +167,12 @@ - (void)testPickMultiImageShouldUseUIImagePickerControllerOnPreiOS14 { FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; [plugin setImagePickerControllerOverrides:@[ mockUIImagePicker ]]; - FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"pickMultiImage" - arguments:@{ - @"maxWidth" : @(100), - @"maxHeight" : @(200), - @"imageQuality" : @(50), - }]; - - [plugin handleMethodCall:call - result:^(id _Nullable r){ - }]; + [plugin pickMultiImageWithMaxSize:[FLTMaxSize makeWithWidth:@(100) height:@(200)] + quality:@(50) + completion:^(NSArray *_Nullable result, + FlutterError *_Nullable error){ + }]; OCMVerify(times(1), [mockUIImagePicker setSourceType:UIImagePickerControllerSourceTypePhotoLibrary]); } @@ -187,17 +184,15 @@ - (void)testPluginPickImageDeviceCancelClickMultipleTimes { return; } FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; - FlutterMethodCall *call = - [FlutterMethodCall methodCallWithMethodName:@"pickImage" - arguments:@{@"source" : @(0), @"cameraDevice" : @(1)}]; UIImagePickerController *controller = [[UIImagePickerController alloc] init]; plugin.imagePickerControllerOverrides = @[ controller ]; - [plugin handleMethodCall:call - result:^(id _Nullable r){ - }]; - plugin.result = ^(id result) { - }; + [plugin pickImageWithSource:[FLTSourceSpecification makeWithType:FLTSourceTypeCamera + camera:FLTSourceCameraRear] + maxSize:[[FLTMaxSize alloc] init] + quality:nil + completion:^(NSString *_Nullable result, FlutterError *_Nullable error){ + }]; // To ensure the flow does not crash by multiple cancel call [plugin imagePickerControllerDidCancel:controller]; @@ -208,14 +203,15 @@ - (void)testPluginPickImageDeviceCancelClickMultipleTimes { - (void)testPickingVideoWithDuration { FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; - FlutterMethodCall *call = [FlutterMethodCall - methodCallWithMethodName:@"pickVideo" - arguments:@{@"source" : @(0), @"cameraDevice" : @(0), @"maxDuration" : @95}]; UIImagePickerController *controller = [[UIImagePickerController alloc] init]; [plugin setImagePickerControllerOverrides:@[ controller ]]; - [plugin handleMethodCall:call - result:^(id _Nullable r){ - }]; + + [plugin pickVideoWithSource:[FLTSourceSpecification makeWithType:FLTSourceTypeCamera + camera:FLTSourceCameraRear] + maxDuration:@(95) + completion:^(NSString *_Nullable result, FlutterError *_Nullable error){ + }]; + XCTAssertEqual(controller.videoMaximumDuration, 95); } @@ -231,37 +227,17 @@ - (void)testViewController { XCTAssertEqual([plugin viewControllerWithWindow:window], vc2); } -- (void)testPluginMultiImagePathIsNil { - FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; - - dispatch_semaphore_t resultSemaphore = dispatch_semaphore_create(0); - __block FlutterError *pickImageResult = nil; - - plugin.result = ^(id _Nullable r) { - pickImageResult = r; - dispatch_semaphore_signal(resultSemaphore); - }; - [plugin handleSavedPathList:nil]; - - dispatch_semaphore_wait(resultSemaphore, DISPATCH_TIME_FOREVER); - - XCTAssertEqualObjects(pickImageResult.code, @"create_error"); -} - - (void)testPluginMultiImagePathHasNullItem { FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; - NSMutableArray *pathList = [NSMutableArray new]; - - [pathList addObject:[NSNull null]]; dispatch_semaphore_t resultSemaphore = dispatch_semaphore_create(0); __block FlutterError *pickImageResult = nil; - - plugin.result = ^(id _Nullable r) { - pickImageResult = r; - dispatch_semaphore_signal(resultSemaphore); - }; - [plugin handleSavedPathList:pathList]; + plugin.callContext = [[FLTImagePickerMethodCallContext alloc] + initWithResult:^(NSArray *_Nullable result, FlutterError *_Nullable error) { + pickImageResult = error; + dispatch_semaphore_signal(resultSemaphore); + }]; + [plugin sendCallResultWithSavedPathList:@[ [NSNull null] ]]; dispatch_semaphore_wait(resultSemaphore, DISPATCH_TIME_FOREVER); @@ -270,19 +246,17 @@ - (void)testPluginMultiImagePathHasNullItem { - (void)testPluginMultiImagePathHasItem { FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; - NSString *savedPath = @"test"; - NSMutableArray *pathList = [NSMutableArray new]; - - [pathList addObject:savedPath]; + NSArray *pathList = @[ @"test" ]; dispatch_semaphore_t resultSemaphore = dispatch_semaphore_create(0); __block id pickImageResult = nil; - plugin.result = ^(id _Nullable r) { - pickImageResult = r; - dispatch_semaphore_signal(resultSemaphore); - }; - [plugin handleSavedPathList:pathList]; + plugin.callContext = [[FLTImagePickerMethodCallContext alloc] + initWithResult:^(NSArray *_Nullable result, FlutterError *_Nullable error) { + pickImageResult = result; + dispatch_semaphore_signal(resultSemaphore); + }]; + [plugin sendCallResultWithSavedPathList:pathList]; dispatch_semaphore_wait(resultSemaphore, DISPATCH_TIME_FOREVER); diff --git a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m index cc841d6db447..76ed9623a57c 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m @@ -16,31 +16,24 @@ #import "FLTImagePickerMetaDataUtil.h" #import "FLTImagePickerPhotoAssetUtil.h" #import "FLTPHPickerSaveImageToPathOperation.h" +#import "messages.g.h" -/** - * Returns the value for the given key in 'dict', or nil if the value is - * NSNull. - */ -id GetNullableValueForKey(NSDictionary *dict, NSString *key) { - id value = dict[key]; - return value == [NSNull null] ? nil : value; +@implementation FLTImagePickerMethodCallContext +- (instancetype)initWithResult:(nonnull FlutterResultAdapter)result { + if (self = [super init]) { + _result = [result copy]; + } + return self; } +@end + +#pragma mark - @interface FLTImagePickerPlugin () -/** - * The maximum amount of images that are allowed to be picked. - */ -@property(assign, nonatomic) int maxImagesAllowed; - -/** - * The arguments that are passed in from the Flutter method call. - */ -@property(copy, nonatomic) NSDictionary *arguments; - /** * The PHPickerViewController instance used to pick multiple * images. @@ -58,19 +51,13 @@ @interface FLTImagePickerPlugin () *)registrar { - FlutterMethodChannel *channel = - [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/image_picker" - binaryMessenger:[registrar messenger]]; FLTImagePickerPlugin *instance = [FLTImagePickerPlugin new]; - [registrar addMethodCallDelegate:instance channel:channel]; + FLTImagePickerApiSetup(registrar.messenger, instance); } - (UIImagePickerController *)createImagePickerController { @@ -107,130 +94,180 @@ - (UIViewController *)viewControllerWithWindow:(UIWindow *)window { } /** - * Returns the UIImagePickerControllerCameraDevice to use given [arguments]. - * - * If the cameraDevice value that is fetched from arguments is 1 then returns - * UIImagePickerControllerCameraDeviceFront. If the cameraDevice value that is fetched - * from arguments is 0 then returns UIImagePickerControllerCameraDeviceRear. + * Returns the UIImagePickerControllerCameraDevice to use given [source]. * - * @param arguments that should be used to get cameraDevice value. + * @param source The source specification from Dart. */ -- (UIImagePickerControllerCameraDevice)getCameraDeviceFromArguments:(NSDictionary *)arguments { - NSInteger cameraDevice = [arguments[@"cameraDevice"] intValue]; - return (cameraDevice == 1) ? UIImagePickerControllerCameraDeviceFront - : UIImagePickerControllerCameraDeviceRear; +- (UIImagePickerControllerCameraDevice)cameraDeviceForSource:(FLTSourceSpecification *)source { + switch (source.camera) { + case FLTSourceCameraFront: + return UIImagePickerControllerCameraDeviceFront; + case FLTSourceCameraRear: + return UIImagePickerControllerCameraDeviceRear; + } } -- (void)pickImageWithPHPicker:(int)maxImagesAllowed API_AVAILABLE(ios(14)) { +- (void)launchPHPickerWithContext:(nonnull FLTImagePickerMethodCallContext *)context + API_AVAILABLE(ios(14)) { PHPickerConfiguration *config = [[PHPickerConfiguration alloc] initWithPhotoLibrary:PHPhotoLibrary.sharedPhotoLibrary]; - config.selectionLimit = maxImagesAllowed; // Setting to zero allow us to pick unlimited photos + config.selectionLimit = context.maxImageCount; config.filter = [PHPickerFilter imagesFilter]; _pickerViewController = [[PHPickerViewController alloc] initWithConfiguration:config]; _pickerViewController.delegate = self; _pickerViewController.presentationController.delegate = self; - - self.maxImagesAllowed = maxImagesAllowed; + self.callContext = context; [self checkPhotoAuthorizationForAccessLevel]; } -- (void)launchUIImagePickerWithSource:(int)imageSource { +- (void)launchUIImagePickerWithSource:(nonnull FLTSourceSpecification *)source + context:(nonnull FLTImagePickerMethodCallContext *)context { UIImagePickerController *imagePickerController = [self createImagePickerController]; imagePickerController.modalPresentationStyle = UIModalPresentationCurrentContext; imagePickerController.delegate = self; imagePickerController.mediaTypes = @[ (NSString *)kUTTypeImage ]; + self.callContext = context; - self.maxImagesAllowed = 1; - - switch (imageSource) { - case SOURCE_CAMERA: - [self checkCameraAuthorizationWithImagePicker:imagePickerController]; + switch (source.type) { + case FLTSourceTypeCamera: + [self checkCameraAuthorizationWithImagePicker:imagePickerController + camera:[self cameraDeviceForSource:source]]; break; - case SOURCE_GALLERY: + case FLTSourceTypeGallery: [self checkPhotoAuthorizationWithImagePicker:imagePickerController]; break; default: - self.result([FlutterError errorWithCode:@"invalid_source" - message:@"Invalid image source." - details:nil]); + [self sendCallResultWithError:[FlutterError errorWithCode:@"invalid_source" + message:@"Invalid image source." + details:nil]]; break; } } -- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { - if (self.result) { - self.result([FlutterError errorWithCode:@"multiple_request" - message:@"Cancelled by a second request" - details:nil]); - self.result = nil; - } - - self.result = result; - _arguments = call.arguments; - - if ([@"pickImage" isEqualToString:call.method]) { - int imageSource = [call.arguments[@"source"] intValue]; +#pragma mark - FLTImagePickerApi + +- (void)pickImageWithSource:(nonnull FLTSourceSpecification *)source + maxSize:(nonnull FLTMaxSize *)maxSize + quality:(nullable NSNumber *)imageQuality + completion: + (nonnull void (^)(NSString *_Nullable, FlutterError *_Nullable))completion { + [self cancelInProgressCall]; + FLTImagePickerMethodCallContext *context = [[FLTImagePickerMethodCallContext alloc] + initWithResult:^void(NSArray *paths, FlutterError *error) { + if (paths && paths.count != 1) { + completion(nil, [FlutterError errorWithCode:@"invalid_result" + message:@"Incorrect number of return paths provided" + details:nil]); + } + completion(paths.firstObject, error); + }]; + context.maxSize = maxSize; + context.imageQuality = imageQuality; + context.maxImageCount = 1; - if (imageSource == SOURCE_GALLERY) { // Capture is not possible with PHPicker - if (@available(iOS 14, *)) { - // PHPicker is used - [self pickImageWithPHPicker:1]; - } else { - // UIImagePicker is used - [self launchUIImagePickerWithSource:imageSource]; - } - } else { - [self launchUIImagePickerWithSource:imageSource]; - } - } else if ([@"pickMultiImage" isEqualToString:call.method]) { + if (source.type == FLTSourceTypeGallery) { // Capture is not possible with PHPicker if (@available(iOS 14, *)) { - [self pickImageWithPHPicker:0]; + [self launchPHPickerWithContext:context]; } else { - [self launchUIImagePickerWithSource:SOURCE_GALLERY]; - } - } else if ([@"pickVideo" isEqualToString:call.method]) { - UIImagePickerController *imagePickerController = [self createImagePickerController]; - imagePickerController.modalPresentationStyle = UIModalPresentationCurrentContext; - imagePickerController.delegate = self; - imagePickerController.mediaTypes = @[ - (NSString *)kUTTypeMovie, (NSString *)kUTTypeAVIMovie, (NSString *)kUTTypeVideo, - (NSString *)kUTTypeMPEG4 - ]; - imagePickerController.videoQuality = UIImagePickerControllerQualityTypeHigh; - - int imageSource = [call.arguments[@"source"] intValue]; - if ([call.arguments[@"maxDuration"] isKindOfClass:[NSNumber class]]) { - NSTimeInterval max = [call.arguments[@"maxDuration"] doubleValue]; - imagePickerController.videoMaximumDuration = max; + [self launchUIImagePickerWithSource:source context:context]; } + } else { + [self launchUIImagePickerWithSource:source context:context]; + } +} - switch (imageSource) { - case SOURCE_CAMERA: - [self checkCameraAuthorizationWithImagePicker:imagePickerController]; - break; - case SOURCE_GALLERY: - [self checkPhotoAuthorizationWithImagePicker:imagePickerController]; - break; - default: - result([FlutterError errorWithCode:@"invalid_source" - message:@"Invalid video source." - details:nil]); - break; - } +- (void)pickMultiImageWithMaxSize:(nonnull FLTMaxSize *)maxSize + quality:(nullable NSNumber *)imageQuality + completion:(nonnull void (^)(NSArray *_Nullable, + FlutterError *_Nullable))completion { + FLTImagePickerMethodCallContext *context = + [[FLTImagePickerMethodCallContext alloc] initWithResult:completion]; + context.maxSize = maxSize; + context.imageQuality = imageQuality; + + if (@available(iOS 14, *)) { + [self launchPHPickerWithContext:context]; } else { - result(FlutterMethodNotImplemented); + // Camera is ignored for gallery mode, so the value here is arbitrary. + [self launchUIImagePickerWithSource:[FLTSourceSpecification makeWithType:FLTSourceTypeGallery + camera:FLTSourceCameraRear] + context:context]; } } -- (void)showCameraWithImagePicker:(UIImagePickerController *)imagePickerController { +- (void)pickVideoWithSource:(nonnull FLTSourceSpecification *)source + maxDuration:(nullable NSNumber *)maxDurationSeconds + completion: + (nonnull void (^)(NSString *_Nullable, FlutterError *_Nullable))completion { + FLTImagePickerMethodCallContext *context = [[FLTImagePickerMethodCallContext alloc] + initWithResult:^void(NSArray *paths, FlutterError *error) { + if (paths && paths.count != 1) { + completion(nil, [FlutterError errorWithCode:@"invalid_result" + message:@"Incorrect number of return paths provided" + details:nil]); + } + completion(paths.firstObject, error); + }]; + context.maxImageCount = 1; + + UIImagePickerController *imagePickerController = [self createImagePickerController]; + imagePickerController.modalPresentationStyle = UIModalPresentationCurrentContext; + imagePickerController.delegate = self; + imagePickerController.mediaTypes = @[ + (NSString *)kUTTypeMovie, (NSString *)kUTTypeAVIMovie, (NSString *)kUTTypeVideo, + (NSString *)kUTTypeMPEG4 + ]; + imagePickerController.videoQuality = UIImagePickerControllerQualityTypeHigh; + + if (maxDurationSeconds) { + NSTimeInterval max = [maxDurationSeconds doubleValue]; + imagePickerController.videoMaximumDuration = max; + } + + self.callContext = context; + + switch (source.type) { + case FLTSourceTypeCamera: + [self checkCameraAuthorizationWithImagePicker:imagePickerController + camera:[self cameraDeviceForSource:source]]; + break; + case FLTSourceTypeGallery: + [self checkPhotoAuthorizationWithImagePicker:imagePickerController]; + break; + default: + [self sendCallResultWithError:[FlutterError errorWithCode:@"invalid_source" + message:@"Invalid video source." + details:nil]]; + break; + } +} + +#pragma mark - + +/** + * If a call is still in progress, cancels it by returning an error and then clearing state. + * + * TODO(stuartmorgan): Eliminate this, and instead track context per image picker (e.g., using + * associated objects). + */ +- (void)cancelInProgressCall { + if (self.callContext) { + [self sendCallResultWithError:[FlutterError errorWithCode:@"multiple_request" + message:@"Cancelled by a second request" + details:nil]]; + self.callContext = nil; + } +} + +- (void)showCamera:(UIImagePickerControllerCameraDevice)device + withImagePicker:(UIImagePickerController *)imagePickerController { @synchronized(self) { if (imagePickerController.beingPresented) { return; } } - UIImagePickerControllerCameraDevice device = [self getCameraDeviceFromArguments:_arguments]; // Camera is not available on simulators if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera] && [UIImagePickerController isCameraDeviceAvailable:device]) { @@ -254,25 +291,24 @@ - (void)showCameraWithImagePicker:(UIImagePickerController *)imagePickerControll [[self viewControllerWithWindow:nil] presentViewController:cameraErrorAlert animated:YES completion:nil]; - self.result(nil); - self.result = nil; - _arguments = nil; + [self sendCallResultWithSavedPathList:nil]; } } -- (void)checkCameraAuthorizationWithImagePicker:(UIImagePickerController *)imagePickerController { +- (void)checkCameraAuthorizationWithImagePicker:(UIImagePickerController *)imagePickerController + camera:(UIImagePickerControllerCameraDevice)device { AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; switch (status) { case AVAuthorizationStatusAuthorized: - [self showCameraWithImagePicker:imagePickerController]; + [self showCamera:device withImagePicker:imagePickerController]; break; case AVAuthorizationStatusNotDetermined: { [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) { dispatch_async(dispatch_get_main_queue(), ^{ if (granted) { - [self showCameraWithImagePicker:imagePickerController]; + [self showCamera:device withImagePicker:imagePickerController]; } else { [self errorNoCameraAccess:AVAuthorizationStatusDenied]; } @@ -352,15 +388,17 @@ - (void)checkPhotoAuthorizationForAccessLevel API_AVAILABLE(ios(14)) { - (void)errorNoCameraAccess:(AVAuthorizationStatus)status { switch (status) { case AVAuthorizationStatusRestricted: - self.result([FlutterError errorWithCode:@"camera_access_restricted" - message:@"The user is not allowed to use the camera." - details:nil]); + [self sendCallResultWithError:[FlutterError + errorWithCode:@"camera_access_restricted" + message:@"The user is not allowed to use the camera." + details:nil]]; break; case AVAuthorizationStatusDenied: default: - self.result([FlutterError errorWithCode:@"camera_access_denied" - message:@"The user did not allow camera access." - details:nil]); + [self sendCallResultWithError:[FlutterError + errorWithCode:@"camera_access_denied" + message:@"The user did not allow camera access." + details:nil]]; break; } } @@ -368,15 +406,17 @@ - (void)errorNoCameraAccess:(AVAuthorizationStatus)status { - (void)errorNoPhotoAccess:(PHAuthorizationStatus)status { switch (status) { case PHAuthorizationStatusRestricted: - self.result([FlutterError errorWithCode:@"photo_access_restricted" - message:@"The user is not allowed to use the photo." - details:nil]); + [self sendCallResultWithError:[FlutterError + errorWithCode:@"photo_access_restricted" + message:@"The user is not allowed to use the photo." + details:nil]]; break; case PHAuthorizationStatusDenied: default: - self.result([FlutterError errorWithCode:@"photo_access_denied" - message:@"The user did not allow photo access." - details:nil]); + [self sendCallResultWithError:[FlutterError + errorWithCode:@"photo_access_denied" + message:@"The user did not allow photo access." + details:nil]]; break; } } @@ -406,31 +446,27 @@ - (NSNumber *)getDesiredImageQuality:(NSNumber *)imageQuality { return imageQuality; } +#pragma mark - UIAdaptivePresentationControllerDelegate + - (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController { - if (self.result != nil) { - self.result(nil); - self.result = nil; - self->_arguments = nil; - } + [self sendCallResultWithSavedPathList:nil]; } +#pragma mark - PHPickerViewControllerDelegate + - (void)picker:(PHPickerViewController *)picker didFinishPicking:(NSArray *)results API_AVAILABLE(ios(14)) { [picker dismissViewControllerAnimated:YES completion:nil]; if (results.count == 0) { - if (self.result != nil) { - self.result(nil); - self.result = nil; - self->_arguments = nil; - } + [self sendCallResultWithSavedPathList:nil]; return; } dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); dispatch_async(backgroundQueue, ^{ - NSNumber *maxWidth = GetNullableValueForKey(self->_arguments, @"maxWidth"); - NSNumber *maxHeight = GetNullableValueForKey(self->_arguments, @"maxHeight"); - NSNumber *imageQuality = GetNullableValueForKey(self->_arguments, @"imageQuality"); + NSNumber *maxWidth = self.callContext.maxSize.width; + NSNumber *maxHeight = self.callContext.maxSize.height; + NSNumber *imageQuality = self.callContext.imageQuality; NSNumber *desiredImageQuality = [self getDesiredImageQuality:imageQuality]; NSOperationQueue *operationQueue = [NSOperationQueue new]; NSMutableArray *pathList = [self createNSMutableArrayWithSize:results.count]; @@ -449,11 +485,13 @@ - (void)picker:(PHPickerViewController *)picker } [operationQueue waitUntilAllOperationsAreFinished]; dispatch_async(dispatch_get_main_queue(), ^{ - [self handleSavedPathList:pathList]; + [self sendCallResultWithSavedPathList:pathList]; }); }); } +#pragma mark - + /** * Creates an NSMutableArray of a certain size filled with NSNull objects. * @@ -470,6 +508,8 @@ - (NSMutableArray *)createNSMutableArrayWithSize:(NSUInteger)size { return mutableArray; } +#pragma mark - UIImagePickerControllerDelegate + - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { NSURL *videoURL = info[UIImagePickerControllerMediaURL]; @@ -478,7 +518,7 @@ - (void)imagePickerController:(UIImagePickerController *)picker // further didFinishPickingMediaWithInfo invocations. A nil check is necessary // to prevent below code to be unwantly executed multiple times and cause a // crash. - if (!self.result) { + if (!self.callContext) { return; } if (videoURL != nil) { @@ -493,27 +533,25 @@ - (void)imagePickerController:(UIImagePickerController *)picker [[NSFileManager defaultManager] copyItemAtURL:videoURL toURL:destination error:&error]; if (error) { - self.result([FlutterError errorWithCode:@"flutter_image_picker_copy_video_error" - message:@"Could not cache the video file." - details:nil]); - self.result = nil; + [self sendCallResultWithError:[FlutterError + errorWithCode:@"flutter_image_picker_copy_video_error" + message:@"Could not cache the video file." + details:nil]]; return; } } videoURL = destination; } } - self.result(videoURL.path); - self.result = nil; - _arguments = nil; + [self sendCallResultWithSavedPathList:@[ videoURL.path ]]; } else { UIImage *image = info[UIImagePickerControllerEditedImage]; if (image == nil) { image = info[UIImagePickerControllerOriginalImage]; } - NSNumber *maxWidth = GetNullableValueForKey(_arguments, @"maxWidth"); - NSNumber *maxHeight = GetNullableValueForKey(_arguments, @"maxHeight"); - NSNumber *imageQuality = GetNullableValueForKey(_arguments, @"imageQuality"); + NSNumber *maxWidth = self.callContext.maxSize.width; + NSNumber *maxHeight = self.callContext.maxSize.height; + NSNumber *imageQuality = self.callContext.imageQuality; NSNumber *desiredImageQuality = [self getDesiredImageQuality:imageQuality]; PHAsset *originalAsset = [FLTImagePickerPhotoAssetUtil getAssetFromImagePickerInfo:info]; @@ -547,14 +585,11 @@ - (void)imagePickerController:(UIImagePickerController *)picker - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker { [picker dismissViewControllerAnimated:YES completion:nil]; - if (!self.result) { - return; - } - self.result(nil); - self.result = nil; - _arguments = nil; + [self sendCallResultWithSavedPathList:nil]; } +#pragma mark - + - (void)saveImageWithOriginalImageData:(NSData *)originalImageData image:(UIImage *)image maxWidth:(NSNumber *)maxWidth @@ -566,7 +601,7 @@ - (void)saveImageWithOriginalImageData:(NSData *)originalImageData maxWidth:maxWidth maxHeight:maxHeight imageQuality:imageQuality]; - [self handleSavedPathList:@[ savedPath ]]; + [self sendCallResultWithSavedPathList:@[ savedPath ]]; } - (void)saveImageWithPickerInfo:(NSDictionary *)info @@ -575,47 +610,36 @@ - (void)saveImageWithPickerInfo:(NSDictionary *)info NSString *savedPath = [FLTImagePickerPhotoAssetUtil saveImageWithPickerInfo:info image:image imageQuality:imageQuality]; - [self handleSavedPathList:@[ savedPath ]]; + [self sendCallResultWithSavedPathList:@[ savedPath ]]; } -/** - * Applies NSMutableArray on the FLutterResult. - * - * NSString must be returned by FlutterResult if the single image - * mode is active. It is checked by maxImagesAllowed and - * returns the first object of the pathlist. - * - * NSMutableArray must be returned by FlutterResult if the multi-image - * mode is active. After the pathlist count is checked then it returns - * the pathlist. - * - * @param pathList that should be applied to FlutterResult. - */ -- (void)handleSavedPathList:(NSArray *)pathList { - if (!self.result) { +- (void)sendCallResultWithSavedPathList:(nullable NSArray *)pathList { + if (!self.callContext) { return; } - if (pathList) { - if (![pathList containsObject:[NSNull null]]) { - if ((self.maxImagesAllowed == 1)) { - self.result(pathList.firstObject); - } else { - self.result(pathList); - } - } else { - self.result([FlutterError errorWithCode:@"create_error" - message:@"pathList's items should not be null" - details:nil]); - } + if ([pathList containsObject:[NSNull null]]) { + self.callContext.result(nil, [FlutterError errorWithCode:@"create_error" + message:@"pathList's items should not be null" + details:nil]); } else { - // This should never happen. - self.result([FlutterError errorWithCode:@"create_error" - message:@"pathList should not be nil" - details:nil]); + self.callContext.result(pathList, nil); + } + self.callContext = nil; +} + +/** + * Sends the given error via `callContext.result` as the result of the original platform channel + * method call, clearing the in-progress call state. + * + * @param error The error to return. + */ +- (void)sendCallResultWithError:(FlutterError *)error { + if (!self.callContext) { + return; } - self.result = nil; - _arguments = nil; + self.callContext.result(nil, error); + self.callContext = nil; } @end diff --git a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin_Test.h b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin_Test.h index 039f76de427d..2c4167746c8e 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin_Test.h +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin_Test.h @@ -6,26 +6,65 @@ #import -/** Methods exposed for unit testing. */ -@interface FLTImagePickerPlugin () +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * The return hander used for all method calls, which internally adapts the provided result list + * to return either a list or a single element depending on the original call. + */ +typedef void (^FlutterResultAdapter)(NSArray *_Nullable, FlutterError *_Nullable); + +/** + * A container class for context to use when handling a method call from the Dart side. + */ +@interface FLTImagePickerMethodCallContext : NSObject + +/** + * Initializes a new context that calls |result| on completion of the operation. + */ +- (instancetype)initWithResult:(nonnull FlutterResultAdapter)result; -/** The Flutter result callback use to report results back to Flutter App. */ -@property(copy, nonatomic) FlutterResult result; +/** The callback to provide results to the Dart caller. */ +@property(nonatomic, copy, nonnull) FlutterResultAdapter result; /** - * Applies NSMutableArray on the FLutterResult. + * The maximum size to enforce on the results. * - * NSString must be returned by FlutterResult if the single image - * mode is active. It is checked by maxImagesAllowed and - * returns the first object of the pathlist. + * If nil, no resizing is done. + */ +@property(nonatomic, strong, nullable) FLTMaxSize *maxSize; + +/** + * The image quality to resample the results to. * - * NSMutableArray must be returned by FlutterResult if the multi-image - * mode is active. After the pathlist count is checked then it returns - * the pathlist. + * If nil, no resampling is done. + */ +@property(nonatomic, strong, nullable) NSNumber *imageQuality; + +/** Maximum number of images to select. 0 indicates no maximum. */ +@property(nonatomic, assign) int maxImageCount; + +@end + +#pragma mark - + +/** Methods exposed for unit testing. */ +@interface FLTImagePickerPlugin () + +/** + * The context of the Flutter method call that is currently being handled, if any. + */ +@property(strong, nonatomic, nullable) FLTImagePickerMethodCallContext *callContext; + +/** + * Validates the provided paths list, then sends it via `callContext.result` as the result of the + * original platform channel method call, clearing the in-progress call state. * - * @param pathList that should be applied to FlutterResult. + * @param pathList The paths to return. nil indicates a cancelled operation. */ -- (void)handleSavedPathList:(NSArray *)pathList; +- (void)sendCallResultWithSavedPathList:(nullable NSArray *)pathList; /** * Tells the delegate that the user cancelled the pick operation. @@ -52,3 +91,5 @@ (NSArray *)imagePickerControllers; @end + +NS_ASSUME_NONNULL_END diff --git a/packages/image_picker/image_picker_ios/ios/Classes/messages.g.h b/packages/image_picker/image_picker_ios/ios/Classes/messages.g.h new file mode 100644 index 000000000000..310165f72f4f --- /dev/null +++ b/packages/image_picker/image_picker_ios/ios/Classes/messages.g.h @@ -0,0 +1,61 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v3.0.2), do not edit directly. +// See also: https://pub.dev/packages/pigeon +#import +@protocol FlutterBinaryMessenger; +@protocol FlutterMessageCodec; +@class FlutterError; +@class FlutterStandardTypedData; + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSUInteger, FLTSourceCamera) { + FLTSourceCameraRear = 0, + FLTSourceCameraFront = 1, +}; + +typedef NS_ENUM(NSUInteger, FLTSourceType) { + FLTSourceTypeCamera = 0, + FLTSourceTypeGallery = 1, +}; + +@class FLTMaxSize; +@class FLTSourceSpecification; + +@interface FLTMaxSize : NSObject ++ (instancetype)makeWithWidth:(nullable NSNumber *)width height:(nullable NSNumber *)height; +@property(nonatomic, strong, nullable) NSNumber *width; +@property(nonatomic, strong, nullable) NSNumber *height; +@end + +@interface FLTSourceSpecification : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithType:(FLTSourceType)type camera:(FLTSourceCamera)camera; +@property(nonatomic, assign) FLTSourceType type; +@property(nonatomic, assign) FLTSourceCamera camera; +@end + +/// The codec used by FLTImagePickerApi. +NSObject *FLTImagePickerApiGetCodec(void); + +@protocol FLTImagePickerApi +- (void)pickImageWithSource:(FLTSourceSpecification *)source + maxSize:(FLTMaxSize *)maxSize + quality:(nullable NSNumber *)imageQuality + completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; +- (void)pickMultiImageWithMaxSize:(FLTMaxSize *)maxSize + quality:(nullable NSNumber *)imageQuality + completion:(void (^)(NSArray *_Nullable, + FlutterError *_Nullable))completion; +- (void)pickVideoWithSource:(FLTSourceSpecification *)source + maxDuration:(nullable NSNumber *)maxDurationSeconds + completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; +@end + +extern void FLTImagePickerApiSetup(id binaryMessenger, + NSObject *_Nullable api); + +NS_ASSUME_NONNULL_END diff --git a/packages/image_picker/image_picker_ios/ios/Classes/messages.g.m b/packages/image_picker/image_picker_ios/ios/Classes/messages.g.m new file mode 100644 index 000000000000..6c91c0ab264f --- /dev/null +++ b/packages/image_picker/image_picker_ios/ios/Classes/messages.g.m @@ -0,0 +1,216 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v3.0.2), do not edit directly. +// See also: https://pub.dev/packages/pigeon +#import "messages.g.h" +#import + +#if !__has_feature(objc_arc) +#error File requires ARC to be enabled. +#endif + +static NSDictionary *wrapResult(id result, FlutterError *error) { + NSDictionary *errorDict = (NSDictionary *)[NSNull null]; + if (error) { + errorDict = @{ + @"code" : (error.code ? error.code : [NSNull null]), + @"message" : (error.message ? error.message : [NSNull null]), + @"details" : (error.details ? error.details : [NSNull null]), + }; + } + return @{ + @"result" : (result ? result : [NSNull null]), + @"error" : errorDict, + }; +} +static id GetNullableObject(NSDictionary *dict, id key) { + id result = dict[key]; + return (result == [NSNull null]) ? nil : result; +} +static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { + id result = array[key]; + return (result == [NSNull null]) ? nil : result; +} + +@interface FLTMaxSize () ++ (FLTMaxSize *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end +@interface FLTSourceSpecification () ++ (FLTSourceSpecification *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end + +@implementation FLTMaxSize ++ (instancetype)makeWithWidth:(nullable NSNumber *)width height:(nullable NSNumber *)height { + FLTMaxSize *pigeonResult = [[FLTMaxSize alloc] init]; + pigeonResult.width = width; + pigeonResult.height = height; + return pigeonResult; +} ++ (FLTMaxSize *)fromMap:(NSDictionary *)dict { + FLTMaxSize *pigeonResult = [[FLTMaxSize alloc] init]; + pigeonResult.width = GetNullableObject(dict, @"width"); + pigeonResult.height = GetNullableObject(dict, @"height"); + return pigeonResult; +} +- (NSDictionary *)toMap { + return [NSDictionary + dictionaryWithObjectsAndKeys:(self.width ? self.width : [NSNull null]), @"width", + (self.height ? self.height : [NSNull null]), @"height", nil]; +} +@end + +@implementation FLTSourceSpecification ++ (instancetype)makeWithType:(FLTSourceType)type camera:(FLTSourceCamera)camera { + FLTSourceSpecification *pigeonResult = [[FLTSourceSpecification alloc] init]; + pigeonResult.type = type; + pigeonResult.camera = camera; + return pigeonResult; +} ++ (FLTSourceSpecification *)fromMap:(NSDictionary *)dict { + FLTSourceSpecification *pigeonResult = [[FLTSourceSpecification alloc] init]; + pigeonResult.type = [GetNullableObject(dict, @"type") integerValue]; + pigeonResult.camera = [GetNullableObject(dict, @"camera") integerValue]; + return pigeonResult; +} +- (NSDictionary *)toMap { + return [NSDictionary + dictionaryWithObjectsAndKeys:@(self.type), @"type", @(self.camera), @"camera", nil]; +} +@end + +@interface FLTImagePickerApiCodecReader : FlutterStandardReader +@end +@implementation FLTImagePickerApiCodecReader +- (nullable id)readValueOfType:(UInt8)type { + switch (type) { + case 128: + return [FLTMaxSize fromMap:[self readValue]]; + + case 129: + return [FLTSourceSpecification fromMap:[self readValue]]; + + default: + return [super readValueOfType:type]; + } +} +@end + +@interface FLTImagePickerApiCodecWriter : FlutterStandardWriter +@end +@implementation FLTImagePickerApiCodecWriter +- (void)writeValue:(id)value { + if ([value isKindOfClass:[FLTMaxSize class]]) { + [self writeByte:128]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FLTSourceSpecification class]]) { + [self writeByte:129]; + [self writeValue:[value toMap]]; + } else { + [super writeValue:value]; + } +} +@end + +@interface FLTImagePickerApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FLTImagePickerApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FLTImagePickerApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FLTImagePickerApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FLTImagePickerApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FLTImagePickerApiCodecReaderWriter *readerWriter = + [[FLTImagePickerApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FLTImagePickerApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickImage" + binaryMessenger:binaryMessenger + codec:FLTImagePickerApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(pickImageWithSource:maxSize:quality:completion:)], + @"FLTImagePickerApi api (%@) doesn't respond to " + @"@selector(pickImageWithSource:maxSize:quality:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FLTSourceSpecification *arg_source = GetNullableObjectAtIndex(args, 0); + FLTMaxSize *arg_maxSize = GetNullableObjectAtIndex(args, 1); + NSNumber *arg_imageQuality = GetNullableObjectAtIndex(args, 2); + [api pickImageWithSource:arg_source + maxSize:arg_maxSize + quality:arg_imageQuality + completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickMultiImage" + binaryMessenger:binaryMessenger + codec:FLTImagePickerApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(pickMultiImageWithMaxSize:quality:completion:)], + @"FLTImagePickerApi api (%@) doesn't respond to " + @"@selector(pickMultiImageWithMaxSize:quality:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FLTMaxSize *arg_maxSize = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_imageQuality = GetNullableObjectAtIndex(args, 1); + [api pickMultiImageWithMaxSize:arg_maxSize + quality:arg_imageQuality + completion:^(NSArray *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickVideo" + binaryMessenger:binaryMessenger + codec:FLTImagePickerApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(pickVideoWithSource:maxDuration:completion:)], + @"FLTImagePickerApi api (%@) doesn't respond to " + @"@selector(pickVideoWithSource:maxDuration:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FLTSourceSpecification *arg_source = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_maxDurationSeconds = GetNullableObjectAtIndex(args, 1); + [api pickVideoWithSource:arg_source + maxDuration:arg_maxDurationSeconds + completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} diff --git a/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart b/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart new file mode 100644 index 000000000000..3d1413cf0cce --- /dev/null +++ b/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart @@ -0,0 +1,209 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; + +import 'src/messages.g.dart'; + +// Converts an [ImageSource] to the corresponding Pigeon API enum value. +SourceType _convertSource(ImageSource source) { + switch (source) { + case ImageSource.camera: + return SourceType.camera; + case ImageSource.gallery: + return SourceType.gallery; + default: + throw UnimplementedError('Unknown source: $source'); + } +} + +// Converts a [CameraDevice] to the corresponding Pigeon API enum value. +SourceCamera _convertCamera(CameraDevice camera) { + switch (camera) { + case CameraDevice.front: + return SourceCamera.front; + case CameraDevice.rear: + return SourceCamera.rear; + default: + throw UnimplementedError('Unknown camera: $camera'); + } +} + +/// An implementation of [ImagePickerPlatform] for iOS. +class ImagePickerIOS extends ImagePickerPlatform { + final ImagePickerApi _hostApi = ImagePickerApi(); + + /// Registers this class as the default platform implementation. + static void registerWith() { + ImagePickerPlatform.instance = ImagePickerIOS(); + } + + @override + Future pickImage({ + required ImageSource source, + double? maxWidth, + double? maxHeight, + int? imageQuality, + CameraDevice preferredCameraDevice = CameraDevice.rear, + }) async { + final String? path = await _pickImageAsPath( + source: source, + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + preferredCameraDevice: preferredCameraDevice, + ); + return path != null ? PickedFile(path) : null; + } + + @override + Future?> pickMultiImage({ + double? maxWidth, + double? maxHeight, + int? imageQuality, + }) async { + final List? paths = await _pickMultiImageAsPath( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + ); + if (paths == null) { + return null; + } + + return paths.map((dynamic path) => PickedFile(path as String)).toList(); + } + + Future?> _pickMultiImageAsPath({ + double? maxWidth, + double? maxHeight, + int? imageQuality, + }) async { + if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) { + throw ArgumentError.value( + imageQuality, 'imageQuality', 'must be between 0 and 100'); + } + + if (maxWidth != null && maxWidth < 0) { + throw ArgumentError.value(maxWidth, 'maxWidth', 'cannot be negative'); + } + + if (maxHeight != null && maxHeight < 0) { + throw ArgumentError.value(maxHeight, 'maxHeight', 'cannot be negative'); + } + + // TODO(stuartmorgan): Remove the cast once Pigeon supports non-nullable + // generics, https://github.com/flutter/flutter/issues/97848 + return (await _hostApi.pickMultiImage( + MaxSize(width: maxWidth, height: maxHeight), imageQuality)) + ?.cast(); + } + + Future _pickImageAsPath({ + required ImageSource source, + double? maxWidth, + double? maxHeight, + int? imageQuality, + CameraDevice preferredCameraDevice = CameraDevice.rear, + }) { + if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) { + throw ArgumentError.value( + imageQuality, 'imageQuality', 'must be between 0 and 100'); + } + + if (maxWidth != null && maxWidth < 0) { + throw ArgumentError.value(maxWidth, 'maxWidth', 'cannot be negative'); + } + + if (maxHeight != null && maxHeight < 0) { + throw ArgumentError.value(maxHeight, 'maxHeight', 'cannot be negative'); + } + + return _hostApi.pickImage( + SourceSpecification( + type: _convertSource(source), + camera: _convertCamera(preferredCameraDevice)), + MaxSize(width: maxWidth, height: maxHeight), + imageQuality, + ); + } + + @override + Future pickVideo({ + required ImageSource source, + CameraDevice preferredCameraDevice = CameraDevice.rear, + Duration? maxDuration, + }) async { + final String? path = await _pickVideoAsPath( + source: source, + maxDuration: maxDuration, + preferredCameraDevice: preferredCameraDevice, + ); + return path != null ? PickedFile(path) : null; + } + + Future _pickVideoAsPath({ + required ImageSource source, + CameraDevice preferredCameraDevice = CameraDevice.rear, + Duration? maxDuration, + }) { + return _hostApi.pickVideo( + SourceSpecification( + type: _convertSource(source), + camera: _convertCamera(preferredCameraDevice)), + maxDuration?.inSeconds); + } + + @override + Future getImage({ + required ImageSource source, + double? maxWidth, + double? maxHeight, + int? imageQuality, + CameraDevice preferredCameraDevice = CameraDevice.rear, + }) async { + final String? path = await _pickImageAsPath( + source: source, + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + preferredCameraDevice: preferredCameraDevice, + ); + return path != null ? XFile(path) : null; + } + + @override + Future?> getMultiImage({ + double? maxWidth, + double? maxHeight, + int? imageQuality, + }) async { + final List? paths = await _pickMultiImageAsPath( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + ); + if (paths == null) { + return null; + } + + return paths.map((String path) => XFile(path)).toList(); + } + + @override + Future getVideo({ + required ImageSource source, + CameraDevice preferredCameraDevice = CameraDevice.rear, + Duration? maxDuration, + }) async { + final String? path = await _pickVideoAsPath( + source: source, + maxDuration: maxDuration, + preferredCameraDevice: preferredCameraDevice, + ); + return path != null ? XFile(path) : null; + } +} diff --git a/packages/image_picker/image_picker_ios/lib/src/messages.g.dart b/packages/image_picker/image_picker_ios/lib/src/messages.g.dart new file mode 100644 index 000000000000..0c5859e80ac9 --- /dev/null +++ b/packages/image_picker/image_picker_ios/lib/src/messages.g.dart @@ -0,0 +1,194 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v3.0.2), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name +// @dart = 2.12 +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; + +import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; +import 'package:flutter/services.dart'; + +enum SourceCamera { + rear, + front, +} + +enum SourceType { + camera, + gallery, +} + +class MaxSize { + MaxSize({ + this.width, + this.height, + }); + + double? width; + double? height; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['width'] = width; + pigeonMap['height'] = height; + return pigeonMap; + } + + static MaxSize decode(Object message) { + final Map pigeonMap = message as Map; + return MaxSize( + width: pigeonMap['width'] as double?, + height: pigeonMap['height'] as double?, + ); + } +} + +class SourceSpecification { + SourceSpecification({ + required this.type, + this.camera, + }); + + SourceType type; + SourceCamera? camera; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['type'] = type.index; + pigeonMap['camera'] = camera?.index; + return pigeonMap; + } + + static SourceSpecification decode(Object message) { + final Map pigeonMap = message as Map; + return SourceSpecification( + type: SourceType.values[pigeonMap['type']! as int], + camera: pigeonMap['camera'] != null + ? SourceCamera.values[pigeonMap['camera']! as int] + : null, + ); + } +} + +class _ImagePickerApiCodec extends StandardMessageCodec { + const _ImagePickerApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is MaxSize) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is SourceSpecification) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return MaxSize.decode(readValue(buffer)!); + + case 129: + return SourceSpecification.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +class ImagePickerApi { + /// Constructor for [ImagePickerApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + ImagePickerApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _ImagePickerApiCodec(); + + Future pickImage(SourceSpecification arg_source, MaxSize arg_maxSize, + int? arg_imageQuality) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ImagePickerApi.pickImage', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_source, arg_maxSize, arg_imageQuality]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return (replyMap['result'] as String?); + } + } + + Future?> pickMultiImage( + MaxSize arg_maxSize, int? arg_imageQuality) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ImagePickerApi.pickMultiImage', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_maxSize, arg_imageQuality]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return (replyMap['result'] as List?)?.cast(); + } + } + + Future pickVideo( + SourceSpecification arg_source, int? arg_maxDurationSeconds) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ImagePickerApi.pickVideo', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_source, arg_maxDurationSeconds]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return (replyMap['result'] as String?); + } + } +} diff --git a/packages/image_picker/image_picker_ios/pigeons/copyright.txt b/packages/image_picker/image_picker_ios/pigeons/copyright.txt new file mode 100644 index 000000000000..1236b63caf3a --- /dev/null +++ b/packages/image_picker/image_picker_ios/pigeons/copyright.txt @@ -0,0 +1,3 @@ +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. diff --git a/packages/image_picker/image_picker_ios/pigeons/messages.dart b/packages/image_picker/image_picker_ios/pigeons/messages.dart new file mode 100644 index 000000000000..94ac034606e9 --- /dev/null +++ b/packages/image_picker/image_picker_ios/pigeons/messages.dart @@ -0,0 +1,47 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:pigeon/pigeon.dart'; + +@ConfigurePigeon(PigeonOptions( + dartOut: 'lib/src/messages.g.dart', + dartTestOut: 'test/test_api.dart', + objcHeaderOut: 'ios/Classes/messages.g.h', + objcSourceOut: 'ios/Classes/messages.g.m', + objcOptions: ObjcOptions( + prefix: 'FLT', + ), + copyrightHeader: 'pigeons/copyright.txt', +)) +class MaxSize { + MaxSize(this.width, this.height); + double? width; + double? height; +} + +// Corresponds to `CameraDevice` from the platform interface package. +enum SourceCamera { rear, front } + +// Corresponds to `ImageSource` from the platform interface package. +enum SourceType { camera, gallery } + +class SourceSpecification { + SourceSpecification(this.type, this.camera); + SourceType type; + SourceCamera? camera; +} + +@HostApi(dartHostTestHandler: 'TestHostImagePickerApi') +abstract class ImagePickerApi { + @async + @ObjCSelector('pickImageWithSource:maxSize:quality:') + String? pickImage( + SourceSpecification source, MaxSize maxSize, int? imageQuality); + @async + @ObjCSelector('pickMultiImageWithMaxSize:quality:') + List? pickMultiImage(MaxSize maxSize, int? imageQuality); + @async + @ObjCSelector('pickVideoWithSource:maxDuration:') + String? pickVideo(SourceSpecification source, int? maxDurationSeconds); +} diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index 2587c9a0d15b..a9cd052be56a 100755 --- a/packages/image_picker/image_picker_ios/pubspec.yaml +++ b/packages/image_picker/image_picker_ios/pubspec.yaml @@ -2,17 +2,18 @@ name: image_picker_ios description: iOS implementation of the video_picker plugin. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.4+11 +version: 0.8.5 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: implements: image_picker platforms: ios: + dartPluginClass: ImagePickerIOS pluginClass: FLTImagePickerPlugin dependencies: @@ -24,3 +25,4 @@ dev_dependencies: flutter_test: sdk: flutter mockito: ^5.0.0 + pigeon: ^3.0.2 diff --git a/packages/image_picker/image_picker_ios/test/image_picker_ios_test.dart b/packages/image_picker/image_picker_ios/test/image_picker_ios_test.dart new file mode 100644 index 000000000000..09517f1ef96b --- /dev/null +++ b/packages/image_picker/image_picker_ios/test/image_picker_ios_test.dart @@ -0,0 +1,937 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:image_picker_ios/image_picker_ios.dart'; +import 'package:image_picker_ios/src/messages.g.dart'; +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; + +import 'test_api.dart'; + +@immutable +class _LoggedMethodCall { + const _LoggedMethodCall(this.name, {required this.arguments}); + final String name; + final Map arguments; + + @override + bool operator ==(Object other) { + return other is _LoggedMethodCall && + name == other.name && + mapEquals(arguments, other.arguments); + } + + @override + int get hashCode => Object.hash(name, arguments); + + @override + String toString() { + return 'MethodCall: $name $arguments'; + } +} + +class _ApiLogger implements TestHostImagePickerApi { + // The value to return from future calls. + dynamic returnValue = ''; + final List<_LoggedMethodCall> calls = <_LoggedMethodCall>[]; + + @override + Future pickImage( + SourceSpecification source, MaxSize maxSize, int? imageQuality) async { + // Flatten arguments for easy comparison. + calls.add(_LoggedMethodCall('pickImage', arguments: { + 'source': source.type, + 'cameraDevice': source.camera, + 'maxWidth': maxSize.width, + 'maxHeight': maxSize.height, + 'imageQuality': imageQuality, + })); + return returnValue as String?; + } + + @override + Future?> pickMultiImage( + MaxSize maxSize, int? imageQuality) async { + calls.add(_LoggedMethodCall('pickMultiImage', arguments: { + 'maxWidth': maxSize.width, + 'maxHeight': maxSize.height, + 'imageQuality': imageQuality, + })); + return returnValue as List?; + } + + @override + Future pickVideo( + SourceSpecification source, int? maxDurationSeconds) async { + calls.add(_LoggedMethodCall('pickVideo', arguments: { + 'source': source.type, + 'cameraDevice': source.camera, + 'maxDuration': maxDurationSeconds, + })); + return returnValue as String?; + } +} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + final ImagePickerIOS picker = ImagePickerIOS(); + late _ApiLogger log; + + setUp(() { + log = _ApiLogger(); + TestHostImagePickerApi.setup(log); + }); + + test('registration', () async { + ImagePickerIOS.registerWith(); + expect(ImagePickerPlatform.instance, isA()); + }); + + group('#pickImage', () { + test('passes the image source argument correctly', () async { + await picker.pickImage(source: ImageSource.camera); + await picker.pickImage(source: ImageSource.gallery); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.gallery, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear + }), + ], + ); + }); + + test('passes the width and height arguments correctly', () async { + await picker.pickImage(source: ImageSource.camera); + await picker.pickImage( + source: ImageSource.camera, + maxWidth: 10.0, + ); + await picker.pickImage( + source: ImageSource.camera, + maxHeight: 10.0, + ); + await picker.pickImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + ); + await picker.pickImage( + source: ImageSource.camera, + maxWidth: 10.0, + imageQuality: 70, + ); + await picker.pickImage( + source: ImageSource.camera, + maxHeight: 10.0, + imageQuality: 70, + ); + await picker.pickImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': 70, + 'cameraDevice': SourceCamera.rear + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': 70, + 'cameraDevice': SourceCamera.rear + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': 70, + 'cameraDevice': SourceCamera.rear + }), + ], + ); + }); + + test('does not accept a invalid imageQuality argument', () { + expect( + () => picker.pickImage(imageQuality: -1, source: ImageSource.gallery), + throwsArgumentError, + ); + + expect( + () => picker.pickImage(imageQuality: 101, source: ImageSource.gallery), + throwsArgumentError, + ); + + expect( + () => picker.pickImage(imageQuality: -1, source: ImageSource.camera), + throwsArgumentError, + ); + + expect( + () => picker.pickImage(imageQuality: 101, source: ImageSource.camera), + throwsArgumentError, + ); + }); + + test('does not accept a negative width or height argument', () { + expect( + () => picker.pickImage(source: ImageSource.camera, maxWidth: -1.0), + throwsArgumentError, + ); + + expect( + () => picker.pickImage(source: ImageSource.camera, maxHeight: -1.0), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + log.returnValue = null; + + expect(await picker.pickImage(source: ImageSource.gallery), isNull); + expect(await picker.pickImage(source: ImageSource.camera), isNull); + }); + + test('camera position defaults to back', () async { + await picker.pickImage(source: ImageSource.camera); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear, + }), + ], + ); + }); + + test('camera position can set to front', () async { + await picker.pickImage( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.front, + }), + ], + ); + }); + }); + + group('#pickMultiImage', () { + test('calls the method correctly', () async { + log.returnValue = ['0', '1']; + await picker.pickMultiImage(); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + }), + ], + ); + }); + + test('passes the width and height arguments correctly', () async { + log.returnValue = ['0', '1']; + await picker.pickMultiImage(); + await picker.pickMultiImage( + maxWidth: 10.0, + ); + await picker.pickMultiImage( + maxHeight: 10.0, + ); + await picker.pickMultiImage( + maxWidth: 10.0, + maxHeight: 20.0, + ); + await picker.pickMultiImage( + maxWidth: 10.0, + imageQuality: 70, + ); + await picker.pickMultiImage( + maxHeight: 10.0, + imageQuality: 70, + ); + await picker.pickMultiImage( + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': null, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': null, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': null, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': 70, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': 70, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': 70, + }), + ], + ); + }); + + test('does not accept a negative width or height argument', () { + expect( + () => picker.pickMultiImage(maxWidth: -1.0), + throwsArgumentError, + ); + + expect( + () => picker.pickMultiImage(maxHeight: -1.0), + throwsArgumentError, + ); + }); + + test('does not accept a invalid imageQuality argument', () { + expect( + () => picker.pickMultiImage(imageQuality: -1), + throwsArgumentError, + ); + + expect( + () => picker.pickMultiImage(imageQuality: 101), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + log.returnValue = null; + + expect(await picker.pickMultiImage(), isNull); + }); + }); + + group('#pickVideo', () { + test('passes the image source argument correctly', () async { + await picker.pickVideo(source: ImageSource.camera); + await picker.pickVideo(source: ImageSource.gallery); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.camera, + 'cameraDevice': SourceCamera.rear, + 'maxDuration': null, + }), + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.gallery, + 'cameraDevice': SourceCamera.rear, + 'maxDuration': null, + }), + ], + ); + }); + + test('passes the duration argument correctly', () async { + await picker.pickVideo(source: ImageSource.camera); + await picker.pickVideo( + source: ImageSource.camera, + maxDuration: const Duration(seconds: 10), + ); + await picker.pickVideo( + source: ImageSource.camera, + maxDuration: const Duration(minutes: 1), + ); + await picker.pickVideo( + source: ImageSource.camera, + maxDuration: const Duration(hours: 1), + ); + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.camera, + 'maxDuration': null, + 'cameraDevice': SourceCamera.rear, + }), + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.camera, + 'maxDuration': 10, + 'cameraDevice': SourceCamera.rear, + }), + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.camera, + 'maxDuration': 60, + 'cameraDevice': SourceCamera.rear, + }), + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.camera, + 'maxDuration': 3600, + 'cameraDevice': SourceCamera.rear, + }), + ], + ); + }); + + test('handles a null video path response gracefully', () async { + log.returnValue = null; + + expect(await picker.pickVideo(source: ImageSource.gallery), isNull); + expect(await picker.pickVideo(source: ImageSource.camera), isNull); + }); + + test('camera position defaults to back', () async { + await picker.pickVideo(source: ImageSource.camera); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.camera, + 'cameraDevice': SourceCamera.rear, + 'maxDuration': null, + }), + ], + ); + }); + + test('camera position can set to front', () async { + await picker.pickVideo( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front, + ); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.camera, + 'maxDuration': null, + 'cameraDevice': SourceCamera.front, + }), + ], + ); + }); + }); + + group('#getImage', () { + test('passes the image source argument correctly', () async { + await picker.getImage(source: ImageSource.camera); + await picker.getImage(source: ImageSource.gallery); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.gallery, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear + }), + ], + ); + }); + + test('passes the width and height arguments correctly', () async { + await picker.getImage(source: ImageSource.camera); + await picker.getImage( + source: ImageSource.camera, + maxWidth: 10.0, + ); + await picker.getImage( + source: ImageSource.camera, + maxHeight: 10.0, + ); + await picker.getImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + ); + await picker.getImage( + source: ImageSource.camera, + maxWidth: 10.0, + imageQuality: 70, + ); + await picker.getImage( + source: ImageSource.camera, + maxHeight: 10.0, + imageQuality: 70, + ); + await picker.getImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': 70, + 'cameraDevice': SourceCamera.rear + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': 70, + 'cameraDevice': SourceCamera.rear + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': 70, + 'cameraDevice': SourceCamera.rear + }), + ], + ); + }); + + test('does not accept a invalid imageQuality argument', () { + expect( + () => picker.getImage(imageQuality: -1, source: ImageSource.gallery), + throwsArgumentError, + ); + + expect( + () => picker.getImage(imageQuality: 101, source: ImageSource.gallery), + throwsArgumentError, + ); + + expect( + () => picker.getImage(imageQuality: -1, source: ImageSource.camera), + throwsArgumentError, + ); + + expect( + () => picker.getImage(imageQuality: 101, source: ImageSource.camera), + throwsArgumentError, + ); + }); + + test('does not accept a negative width or height argument', () { + expect( + () => picker.getImage(source: ImageSource.camera, maxWidth: -1.0), + throwsArgumentError, + ); + + expect( + () => picker.getImage(source: ImageSource.camera, maxHeight: -1.0), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + log.returnValue = null; + + expect(await picker.getImage(source: ImageSource.gallery), isNull); + expect(await picker.getImage(source: ImageSource.camera), isNull); + }); + + test('camera position defaults to back', () async { + await picker.getImage(source: ImageSource.camera); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear, + }), + ], + ); + }); + + test('camera position can set to front', () async { + await picker.getImage( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.front, + }), + ], + ); + }); + }); + + group('#getMultiImage', () { + test('calls the method correctly', () async { + log.returnValue = ['0', '1']; + await picker.getMultiImage(); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + }), + ], + ); + }); + + test('passes the width and height arguments correctly', () async { + log.returnValue = ['0', '1']; + await picker.getMultiImage(); + await picker.getMultiImage( + maxWidth: 10.0, + ); + await picker.getMultiImage( + maxHeight: 10.0, + ); + await picker.getMultiImage( + maxWidth: 10.0, + maxHeight: 20.0, + ); + await picker.getMultiImage( + maxWidth: 10.0, + imageQuality: 70, + ); + await picker.getMultiImage( + maxHeight: 10.0, + imageQuality: 70, + ); + await picker.getMultiImage( + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': null, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': null, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': null, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': 70, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': 70, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': 70, + }), + ], + ); + }); + + test('does not accept a negative width or height argument', () { + log.returnValue = ['0', '1']; + expect( + () => picker.getMultiImage(maxWidth: -1.0), + throwsArgumentError, + ); + + expect( + () => picker.getMultiImage(maxHeight: -1.0), + throwsArgumentError, + ); + }); + + test('does not accept a invalid imageQuality argument', () { + log.returnValue = ['0', '1']; + expect( + () => picker.getMultiImage(imageQuality: -1), + throwsArgumentError, + ); + + expect( + () => picker.getMultiImage(imageQuality: 101), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + log.returnValue = null; + + expect(await picker.getMultiImage(), isNull); + expect(await picker.getMultiImage(), isNull); + }); + }); + + group('#getVideo', () { + test('passes the image source argument correctly', () async { + await picker.getVideo(source: ImageSource.camera); + await picker.getVideo(source: ImageSource.gallery); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.camera, + 'cameraDevice': SourceCamera.rear, + 'maxDuration': null, + }), + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.gallery, + 'cameraDevice': SourceCamera.rear, + 'maxDuration': null, + }), + ], + ); + }); + + test('passes the duration argument correctly', () async { + await picker.getVideo(source: ImageSource.camera); + await picker.getVideo( + source: ImageSource.camera, + maxDuration: const Duration(seconds: 10), + ); + await picker.getVideo( + source: ImageSource.camera, + maxDuration: const Duration(minutes: 1), + ); + await picker.getVideo( + source: ImageSource.camera, + maxDuration: const Duration(hours: 1), + ); + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.camera, + 'maxDuration': null, + 'cameraDevice': SourceCamera.rear, + }), + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.camera, + 'maxDuration': 10, + 'cameraDevice': SourceCamera.rear, + }), + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.camera, + 'maxDuration': 60, + 'cameraDevice': SourceCamera.rear, + }), + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.camera, + 'maxDuration': 3600, + 'cameraDevice': SourceCamera.rear, + }), + ], + ); + }); + + test('handles a null video path response gracefully', () async { + log.returnValue = null; + + expect(await picker.getVideo(source: ImageSource.gallery), isNull); + expect(await picker.getVideo(source: ImageSource.camera), isNull); + }); + + test('camera position defaults to back', () async { + await picker.getVideo(source: ImageSource.camera); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.camera, + 'cameraDevice': SourceCamera.rear, + 'maxDuration': null, + }), + ], + ); + }); + + test('camera position can set to front', () async { + await picker.getVideo( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front, + ); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.camera, + 'maxDuration': null, + 'cameraDevice': SourceCamera.front, + }), + ], + ); + }); + }); +} diff --git a/packages/image_picker/image_picker_ios/test/test_api.dart b/packages/image_picker/image_picker_ios/test/test_api.dart new file mode 100644 index 000000000000..1f76e871521d --- /dev/null +++ b/packages/image_picker/image_picker_ios/test/test_api.dart @@ -0,0 +1,127 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v3.0.2), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis +// ignore_for_file: avoid_relative_lib_imports +// @dart = 2.12 +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; +import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +// Manually changed due to https://github.com/flutter/flutter/issues/97744 +import 'package:image_picker_ios/src/messages.g.dart'; + +class _TestHostImagePickerApiCodec extends StandardMessageCodec { + const _TestHostImagePickerApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is MaxSize) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is SourceSpecification) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return MaxSize.decode(readValue(buffer)!); + + case 129: + return SourceSpecification.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class TestHostImagePickerApi { + static const MessageCodec codec = _TestHostImagePickerApiCodec(); + + Future pickImage( + SourceSpecification source, MaxSize maxSize, int? imageQuality); + Future?> pickMultiImage(MaxSize maxSize, int? imageQuality); + Future pickVideo( + SourceSpecification source, int? maxDurationSeconds); + static void setup(TestHostImagePickerApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ImagePickerApi.pickImage', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImage was null.'); + final List args = (message as List?)!; + final SourceSpecification? arg_source = + (args[0] as SourceSpecification?); + assert(arg_source != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImage was null, expected non-null SourceSpecification.'); + final MaxSize? arg_maxSize = (args[1] as MaxSize?); + assert(arg_maxSize != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImage was null, expected non-null MaxSize.'); + final int? arg_imageQuality = (args[2] as int?); + final String? output = + await api.pickImage(arg_source!, arg_maxSize!, arg_imageQuality); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ImagePickerApi.pickMultiImage', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickMultiImage was null.'); + final List args = (message as List?)!; + final MaxSize? arg_maxSize = (args[0] as MaxSize?); + assert(arg_maxSize != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickMultiImage was null, expected non-null MaxSize.'); + final int? arg_imageQuality = (args[1] as int?); + final List? output = + await api.pickMultiImage(arg_maxSize!, arg_imageQuality); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ImagePickerApi.pickVideo', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickVideo was null.'); + final List args = (message as List?)!; + final SourceSpecification? arg_source = + (args[0] as SourceSpecification?); + assert(arg_source != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickVideo was null, expected non-null SourceSpecification.'); + final int? arg_maxDurationSeconds = (args[1] as int?); + final String? output = + await api.pickVideo(arg_source!, arg_maxDurationSeconds); + return {'result': output}; + }); + } + } + } +} From 4ff0157340ea1874ca753bf921807e62c1943099 Mon Sep 17 00:00:00 2001 From: BeMacized Date: Mon, 25 Apr 2022 14:00:24 +0200 Subject: [PATCH 445/600] [local_auth] Fix deviceSupportsBiometrics returning false on iOS when present biometric hardware is not enrolled. (#5329) * Fix deviceSupportsBiometrics returning false when hardware is available but not enrolled. * Fix label * Format * Add unit tests * Fix changelog * Revert additions to project.pbxproj * Implement PR feedback --- .../local_auth/local_auth_ios/CHANGELOG.md | 5 + .../ios/Runner.xcodeproj/project.pbxproj | 2 +- .../ios/RunnerTests/FLTLocalAuthPluginTests.m | 205 ++++++++++++++++++ .../local_auth_ios/example/lib/main.dart | 26 +-- .../ios/Classes/FLTLocalAuthPlugin.m | 39 +++- .../local_auth_ios/lib/local_auth_ios.dart | 7 +- .../local_auth/local_auth_ios/pubspec.yaml | 2 +- .../local_auth_ios/test/local_auth_test.dart | 8 +- 8 files changed, 265 insertions(+), 29 deletions(-) diff --git a/packages/local_auth/local_auth_ios/CHANGELOG.md b/packages/local_auth/local_auth_ios/CHANGELOG.md index 466aeb50ed6c..0fc716e19f25 100644 --- a/packages/local_auth/local_auth_ios/CHANGELOG.md +++ b/packages/local_auth/local_auth_ios/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.0.4 + +* Fixes `deviceSupportsBiometrics` to return true when biometric hardware + is available but not enrolled. + ## 1.0.3 * Adopts `Object.hash`. diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/project.pbxproj index f8b4056d135d..cbf16eef4060 100644 --- a/packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ diff --git a/packages/local_auth/local_auth_ios/example/ios/RunnerTests/FLTLocalAuthPluginTests.m b/packages/local_auth/local_auth_ios/example/ios/RunnerTests/FLTLocalAuthPluginTests.m index 744d9124f7b1..50dbb1a6907b 100644 --- a/packages/local_auth/local_auth_ios/example/ios/RunnerTests/FLTLocalAuthPluginTests.m +++ b/packages/local_auth/local_auth_ios/example/ios/RunnerTests/FLTLocalAuthPluginTests.m @@ -269,4 +269,209 @@ - (void)testSkippedLocalizedFallbackTitle { [self waitForExpectationsWithTimeout:kTimeout handler:nil]; } +- (void)testDeviceSupportsBiometrics_withEnrolledHardware { + FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc] init]; + id mockAuthContext = OCMClassMock([LAContext class]); + plugin.authContextOverrides = @[ mockAuthContext ]; + + const LAPolicy policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics; + OCMStub([mockAuthContext canEvaluatePolicy:policy error:[OCMArg setTo:nil]]).andReturn(YES); + + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"deviceSupportsBiometrics" + arguments:@{}]; + XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"]; + [plugin handleMethodCall:call + result:^(id _Nullable result) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertTrue([result isKindOfClass:[NSNumber class]]); + XCTAssertTrue([result boolValue]); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeout handler:nil]; +} + +- (void)testDeviceSupportsBiometrics_withNonEnrolledHardware_iOS11 { + if (@available(iOS 11, *)) { + FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc] init]; + id mockAuthContext = OCMClassMock([LAContext class]); + plugin.authContextOverrides = @[ mockAuthContext ]; + + const LAPolicy policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics; + void (^canEvaluatePolicyHandler)(NSInvocation *) = ^(NSInvocation *invocation) { + // Write error + NSError *__autoreleasing *authError; + [invocation getArgument:&authError atIndex:3]; + *authError = [NSError errorWithDomain:@"error" code:LAErrorBiometryNotEnrolled userInfo:nil]; + // Write return value + BOOL returnValue = NO; + NSValue *nsReturnValue = [NSValue valueWithBytes:&returnValue objCType:@encode(BOOL)]; + [invocation setReturnValue:&nsReturnValue]; + }; + OCMStub([mockAuthContext canEvaluatePolicy:policy + error:(NSError * __autoreleasing *)[OCMArg anyPointer]]) + .andDo(canEvaluatePolicyHandler); + + FlutterMethodCall *call = + [FlutterMethodCall methodCallWithMethodName:@"deviceSupportsBiometrics" arguments:@{}]; + XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"]; + [plugin handleMethodCall:call + result:^(id _Nullable result) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertTrue([result isKindOfClass:[NSNumber class]]); + XCTAssertTrue([result boolValue]); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeout handler:nil]; + } +} + +- (void)testDeviceSupportsBiometrics_withNoBiometricHardware { + FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc] init]; + id mockAuthContext = OCMClassMock([LAContext class]); + plugin.authContextOverrides = @[ mockAuthContext ]; + + const LAPolicy policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics; + void (^canEvaluatePolicyHandler)(NSInvocation *) = ^(NSInvocation *invocation) { + // Write error + NSError *__autoreleasing *authError; + [invocation getArgument:&authError atIndex:3]; + *authError = [NSError errorWithDomain:@"error" code:0 userInfo:nil]; + // Write return value + BOOL returnValue = NO; + NSValue *nsReturnValue = [NSValue valueWithBytes:&returnValue objCType:@encode(BOOL)]; + [invocation setReturnValue:&nsReturnValue]; + }; + OCMStub([mockAuthContext canEvaluatePolicy:policy + error:(NSError * __autoreleasing *)[OCMArg anyPointer]]) + .andDo(canEvaluatePolicyHandler); + + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"deviceSupportsBiometrics" + arguments:@{}]; + XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"]; + [plugin handleMethodCall:call + result:^(id _Nullable result) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertTrue([result isKindOfClass:[NSNumber class]]); + XCTAssertFalse([result boolValue]); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeout handler:nil]; +} + +- (void)testGetEnrolledBiometrics_withFaceID_iOS11 { + if (@available(iOS 11, *)) { + FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc] init]; + id mockAuthContext = OCMClassMock([LAContext class]); + plugin.authContextOverrides = @[ mockAuthContext ]; + + const LAPolicy policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics; + OCMStub([mockAuthContext canEvaluatePolicy:policy error:[OCMArg setTo:nil]]).andReturn(YES); + OCMStub([mockAuthContext biometryType]).andReturn(LABiometryTypeFaceID); + + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"getEnrolledBiometrics" + arguments:@{}]; + XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"]; + [plugin handleMethodCall:call + result:^(id _Nullable result) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertTrue([result isKindOfClass:[NSArray class]]); + XCTAssertEqual([result count], 1); + XCTAssertEqualObjects(result[0], @"face"); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeout handler:nil]; + } +} + +- (void)testGetEnrolledBiometrics_withTouchID_iOS11 { + if (@available(iOS 11, *)) { + FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc] init]; + id mockAuthContext = OCMClassMock([LAContext class]); + plugin.authContextOverrides = @[ mockAuthContext ]; + + const LAPolicy policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics; + OCMStub([mockAuthContext canEvaluatePolicy:policy error:[OCMArg setTo:nil]]).andReturn(YES); + OCMStub([mockAuthContext biometryType]).andReturn(LABiometryTypeTouchID); + + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"getEnrolledBiometrics" + arguments:@{}]; + XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"]; + [plugin handleMethodCall:call + result:^(id _Nullable result) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertTrue([result isKindOfClass:[NSArray class]]); + XCTAssertEqual([result count], 1); + XCTAssertEqualObjects(result[0], @"fingerprint"); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeout handler:nil]; + } +} + +- (void)testGetEnrolledBiometrics_withTouchID_preIOS11 { + if (@available(iOS 11, *)) { + return; + } + FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc] init]; + id mockAuthContext = OCMClassMock([LAContext class]); + plugin.authContextOverrides = @[ mockAuthContext ]; + + const LAPolicy policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics; + OCMStub([mockAuthContext canEvaluatePolicy:policy error:[OCMArg setTo:nil]]).andReturn(YES); + + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"getEnrolledBiometrics" + arguments:@{}]; + XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"]; + [plugin handleMethodCall:call + result:^(id _Nullable result) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertTrue([result isKindOfClass:[NSArray class]]); + XCTAssertEqual([result count], 1); + XCTAssertEqualObjects(result[0], @"fingerprint"); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeout handler:nil]; +} + +- (void)testGetEnrolledBiometrics_withoutEnrolledHardware_iOS11 { + if (@available(iOS 11, *)) { + FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc] init]; + id mockAuthContext = OCMClassMock([LAContext class]); + plugin.authContextOverrides = @[ mockAuthContext ]; + + const LAPolicy policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics; + void (^canEvaluatePolicyHandler)(NSInvocation *) = ^(NSInvocation *invocation) { + // Write error + NSError *__autoreleasing *authError; + [invocation getArgument:&authError atIndex:3]; + *authError = [NSError errorWithDomain:@"error" code:LAErrorBiometryNotEnrolled userInfo:nil]; + // Write return value + BOOL returnValue = NO; + NSValue *nsReturnValue = [NSValue valueWithBytes:&returnValue objCType:@encode(BOOL)]; + [invocation setReturnValue:&nsReturnValue]; + }; + OCMStub([mockAuthContext canEvaluatePolicy:policy + error:(NSError * __autoreleasing *)[OCMArg anyPointer]]) + .andDo(canEvaluatePolicyHandler); + + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"getEnrolledBiometrics" + arguments:@{}]; + XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"]; + [plugin handleMethodCall:call + result:^(id _Nullable result) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertTrue([result isKindOfClass:[NSArray class]]); + XCTAssertEqual([result count], 0); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeout handler:nil]; + } +} @end diff --git a/packages/local_auth/local_auth_ios/example/lib/main.dart b/packages/local_auth/local_auth_ios/example/lib/main.dart index 9346be10fd78..c966d67ec9c6 100644 --- a/packages/local_auth/local_auth_ios/example/lib/main.dart +++ b/packages/local_auth/local_auth_ios/example/lib/main.dart @@ -25,7 +25,7 @@ class MyApp extends StatefulWidget { class _MyAppState extends State { _SupportState _supportState = _SupportState.unknown; bool? _canCheckBiometrics; - List? _availableBiometrics; + List? _enrolledBiometrics; String _authorized = 'Not Authorized'; bool _isAuthenticating = false; @@ -40,12 +40,12 @@ class _MyAppState extends State { } Future _checkBiometrics() async { - late bool canCheckBiometrics; + late bool deviceSupportsBiometrics; try { - canCheckBiometrics = - (await LocalAuthPlatform.instance.getEnrolledBiometrics()).isNotEmpty; + deviceSupportsBiometrics = + await LocalAuthPlatform.instance.deviceSupportsBiometrics(); } on PlatformException catch (e) { - canCheckBiometrics = false; + deviceSupportsBiometrics = false; print(e); } if (!mounted) { @@ -53,17 +53,17 @@ class _MyAppState extends State { } setState(() { - _canCheckBiometrics = canCheckBiometrics; + _canCheckBiometrics = deviceSupportsBiometrics; }); } Future _getEnrolledBiometrics() async { - late List availableBiometrics; + late List enrolledBiometrics; try { - availableBiometrics = + enrolledBiometrics = await LocalAuthPlatform.instance.getEnrolledBiometrics(); } on PlatformException catch (e) { - availableBiometrics = []; + enrolledBiometrics = []; print(e); } if (!mounted) { @@ -71,7 +71,7 @@ class _MyAppState extends State { } setState(() { - _availableBiometrics = availableBiometrics; + _enrolledBiometrics = enrolledBiometrics; }); } @@ -173,15 +173,15 @@ class _MyAppState extends State { else const Text('This device is not supported'), const Divider(height: 100), - Text('Can check biometrics: $_canCheckBiometrics\n'), + Text('Device supports biometrics: $_canCheckBiometrics\n'), ElevatedButton( child: const Text('Check biometrics'), onPressed: _checkBiometrics, ), const Divider(height: 100), - Text('Available biometrics: $_availableBiometrics\n'), + Text('Enrolled biometrics: $_enrolledBiometrics\n'), ElevatedButton( - child: const Text('Get available biometrics'), + child: const Text('Get enrolled biometrics'), onPressed: _getEnrolledBiometrics, ), const Divider(height: 100), diff --git a/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m b/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m index ffd1dd2a869d..eb7f637f7850 100644 --- a/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m +++ b/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m @@ -35,8 +35,10 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result } else { [self authenticate:call.arguments withFlutterResult:result]; } - } else if ([@"getAvailableBiometrics" isEqualToString:call.method]) { - [self getAvailableBiometrics:result]; + } else if ([@"getEnrolledBiometrics" isEqualToString:call.method]) { + [self getEnrolledBiometrics:result]; + } else if ([@"deviceSupportsBiometrics" isEqualToString:call.method]) { + [self deviceSupportsBiometrics:result]; } else if ([@"isDeviceSupported" isEqualToString:call.method]) { result(@YES); } else { @@ -93,14 +95,41 @@ - (void)alertMessage:(NSString *)message completion:nil]; } -- (void)getAvailableBiometrics:(FlutterResult)result { +- (void)deviceSupportsBiometrics:(FlutterResult)result { + LAContext *context = self.createAuthContext; + NSError *authError = nil; + // Check if authentication with biometrics is possible. + if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics + error:&authError]) { + if (authError == nil) { + result(@YES); + return; + } + } + // If not, check if it is because no biometrics are enrolled (but still present). + if (authError != nil) { + if (@available(iOS 11, *)) { + if (authError.code == LAErrorBiometryNotEnrolled) { + result(@YES); + return; + } + } else if (authError.code == LAErrorTouchIDNotEnrolled) { + result(@YES); + return; + } + } + + result(@NO); +} + +- (void)getEnrolledBiometrics:(FlutterResult)result { LAContext *context = self.createAuthContext; NSError *authError = nil; NSMutableArray *biometrics = [[NSMutableArray alloc] init]; if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) { if (authError == nil) { - if (@available(iOS 11.0.1, *)) { + if (@available(iOS 11, *)) { if (context.biometryType == LABiometryTypeFaceID) { [biometrics addObject:@"face"]; } else if (context.biometryType == LABiometryTypeTouchID) { @@ -110,8 +139,6 @@ - (void)getAvailableBiometrics:(FlutterResult)result { [biometrics addObject:@"fingerprint"]; } } - } else if (authError.code == LAErrorTouchIDNotEnrolled) { - [biometrics addObject:@"undefined"]; } result(biometrics); } diff --git a/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart b/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart index d92d076d79fb..ae25bd50ef5e 100644 --- a/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart +++ b/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart @@ -49,13 +49,14 @@ class LocalAuthIOS extends LocalAuthPlatform { @override Future deviceSupportsBiometrics() async { - return (await getEnrolledBiometrics()).isNotEmpty; + return (await _channel.invokeMethod('deviceSupportsBiometrics')) ?? + false; } @override Future> getEnrolledBiometrics() async { final List result = (await _channel.invokeListMethod( - 'getAvailableBiometrics', + 'getEnrolledBiometrics', )) ?? []; final List biometrics = []; @@ -70,8 +71,6 @@ class LocalAuthIOS extends LocalAuthPlatform { case 'iris': biometrics.add(BiometricType.iris); break; - case 'undefined': - break; } } return biometrics; diff --git a/packages/local_auth/local_auth_ios/pubspec.yaml b/packages/local_auth/local_auth_ios/pubspec.yaml index 06d972a01522..77ab74d383c8 100644 --- a/packages/local_auth/local_auth_ios/pubspec.yaml +++ b/packages/local_auth/local_auth_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_ios description: iOS implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.3 +version: 1.0.4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_ios/test/local_auth_test.dart b/packages/local_auth/local_auth_ios/test/local_auth_test.dart index aa94b8aa48b8..192d69ca8ec4 100644 --- a/packages/local_auth/local_auth_ios/test/local_auth_test.dart +++ b/packages/local_auth/local_auth_ios/test/local_auth_test.dart @@ -24,7 +24,7 @@ void main() { channel.setMockMethodCallHandler((MethodCall methodCall) { log.add(methodCall); switch (methodCall.method) { - case 'getAvailableBiometrics': + case 'getEnrolledBiometrics': return Future>.value( ['face', 'fingerprint', 'iris', 'undefined']); default: @@ -35,13 +35,13 @@ void main() { log.clear(); }); - test('deviceSupportsBiometrics calls getEnrolledBiometrics', () async { + test('deviceSupportsBiometrics calls platform', () async { final bool result = await localAuthentication.deviceSupportsBiometrics(); expect( log, [ - isMethodCall('getAvailableBiometrics', arguments: null), + isMethodCall('deviceSupportsBiometrics', arguments: null), ], ); expect(result, true); @@ -54,7 +54,7 @@ void main() { expect( log, [ - isMethodCall('getAvailableBiometrics', arguments: null), + isMethodCall('getEnrolledBiometrics', arguments: null), ], ); expect(result, [ From e55659835427fdc2b3c838548ce7e49267f100fd Mon Sep 17 00:00:00 2001 From: Jesse Seales <103135467+sealesj@users.noreply.github.com> Date: Mon, 25 Apr 2022 22:04:07 -0400 Subject: [PATCH 446/600] Pin dockerfile version (#5377) --- .ci/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/Dockerfile b/.ci/Dockerfile index 73efa3103922..59d4064f0a91 100644 --- a/.ci/Dockerfile +++ b/.ci/Dockerfile @@ -1,7 +1,7 @@ # The Flutter version is not important here, since the CI scripts update Flutter # before running. What matters is that the base image is pinned to minimize # unintended changes when modifying this file. -FROM cirrusci/flutter:2.8.0 +FROM cirrusci/flutter@sha256:505fe8bce2896c75b4df9ccf500b1604155bf932af7465ffcc66fcae8612f82f RUN apt-get update -y From 19414fe0ff65ab34262029c1b41cd7cf106e709d Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 26 Apr 2022 16:39:09 -0400 Subject: [PATCH 447/600] [flutter_plugin_tool] Allow re-pathifying dependencies (#5376) --- script/tool/CHANGELOG.md | 5 ++ .../lib/src/make_deps_path_based_command.dart | 45 ++++++++++++------ .../make_deps_path_based_command_test.dart | 46 +++++++++++++++++++ 3 files changed, 81 insertions(+), 15 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 367f258fbeea..bfc0cafa88c3 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,8 @@ +## NEXT + +- Allows `make-deps-path-based` to skip packages it has alredy rewritten, so + that running multiple times won't fail after the first time. + ## 0.8.2+1 - Adds a new `readme-check` command. diff --git a/script/tool/lib/src/make_deps_path_based_command.dart b/script/tool/lib/src/make_deps_path_based_command.dart index c09060310e97..45a4427d3217 100644 --- a/script/tool/lib/src/make_deps_path_based_command.dart +++ b/script/tool/lib/src/make_deps_path_based_command.dart @@ -16,6 +16,8 @@ import 'common/plugin_command.dart'; const int _exitPackageNotFound = 3; const int _exitCannotUpdatePubspec = 4; +enum _RewriteOutcome { changed, noChangesNeeded, alreadyChanged } + /// Converts all dependencies on target packages to path-based dependencies. /// /// This is to allow for pre-publish testing of changes that could affect other @@ -48,6 +50,10 @@ class MakeDepsPathBasedCommand extends PluginCommand { static const String _targetDependenciesWithNonBreakingUpdatesArg = 'target-dependencies-with-non-breaking-updates'; + // The comment to add to temporary dependency overrides. + static const String _dependencyOverrideWarningComment = + '# FOR TESTING ONLY. DO NOT MERGE.'; + @override final String name = 'make-deps-path-based'; @@ -73,12 +79,19 @@ class MakeDepsPathBasedCommand extends PluginCommand { final String repoRootPath = (await gitDir).path; for (final File pubspec in await _getAllPubspecs()) { - if (await _addDependencyOverridesIfNecessary( - pubspec, localDependencyPackages)) { - // Print the relative path of the changed pubspec. - final String displayPath = p.posix.joinAll(path - .split(path.relative(pubspec.absolute.path, from: repoRootPath))); - print(' Modified $displayPath'); + final String displayPath = p.posix.joinAll( + path.split(path.relative(pubspec.absolute.path, from: repoRootPath))); + final _RewriteOutcome outcome = await _addDependencyOverridesIfNecessary( + pubspec, localDependencyPackages); + switch (outcome) { + case _RewriteOutcome.changed: + print(' Modified $displayPath'); + break; + case _RewriteOutcome.alreadyChanged: + print(' Skipped $displayPath - Already rewritten'); + break; + case _RewriteOutcome.noChangesNeeded: + break; } } } @@ -125,16 +138,18 @@ class MakeDepsPathBasedCommand extends PluginCommand { /// If [pubspecFile] has any dependencies on packages in [localDependencies], /// adds dependency_overrides entries to redirect them to the local version /// using path-based dependencies. - /// - /// Returns true if any changes were made. - Future _addDependencyOverridesIfNecessary(File pubspecFile, + Future<_RewriteOutcome> _addDependencyOverridesIfNecessary(File pubspecFile, Map localDependencies) async { final String pubspecContents = pubspecFile.readAsStringSync(); final Pubspec pubspec = Pubspec.parse(pubspecContents); - // Fail if there are any dependency overrides already. If support for that - // is needed at some point, it can be added, but currently it's not and - // relying on that makes the logic here much simpler. + // Fail if there are any dependency overrides already, other than ones + // created by this script. If support for that is needed at some point, it + // can be added, but currently it's not and relying on that makes the logic + // here much simpler. if (pubspec.dependencyOverrides.isNotEmpty) { + if (pubspecContents.contains(_dependencyOverrideWarningComment)) { + return _RewriteOutcome.alreadyChanged; + } printError( 'Plugins with dependency overrides are not currently supported.'); throw ToolExit(_exitCannotUpdatePubspec); @@ -158,7 +173,7 @@ class MakeDepsPathBasedCommand extends PluginCommand { String newPubspecContents = pubspecContents + ''' -# FOR TESTING ONLY. DO NOT MERGE. +$_dependencyOverrideWarningComment dependency_overrides: '''; for (final String packageName in packagesToOverride) { @@ -175,9 +190,9 @@ dependency_overrides: '''; } pubspecFile.writeAsStringSync(newPubspecContents); - return true; + return _RewriteOutcome.changed; } - return false; + return _RewriteOutcome.noChangesNeeded; } /// Returns all pubspecs anywhere under the packages directory. diff --git a/script/tool/test/make_deps_path_based_command_test.dart b/script/tool/test/make_deps_path_based_command_test.dart index 2021f24079e3..da241c3d83f7 100644 --- a/script/tool/test/make_deps_path_based_command_test.dart +++ b/script/tool/test/make_deps_path_based_command_test.dart @@ -144,6 +144,52 @@ void main() { ])); }); + // This test case ensures that running CI using this command on an interim + // PR that itself used this command won't fail on the rewrite step. + test('running a second time no-ops without failing', () async { + final RepositoryPackage simplePackage = RepositoryPackage( + createFakePackage('foo', packagesDir, isFlutter: true)); + final Directory pluginGroup = packagesDir.childDirectory('bar'); + + RepositoryPackage(createFakePackage('bar_platform_interface', pluginGroup, + isFlutter: true)); + final RepositoryPackage pluginImplementation = + RepositoryPackage(createFakePlugin('bar_android', pluginGroup)); + final RepositoryPackage pluginAppFacing = + RepositoryPackage(createFakePlugin('bar', pluginGroup)); + + _addDependencies(simplePackage, [ + 'bar', + 'bar_android', + 'bar_platform_interface', + ]); + _addDependencies(pluginAppFacing, [ + 'bar_platform_interface', + 'bar_android', + ]); + _addDependencies(pluginImplementation, [ + 'bar_platform_interface', + ]); + + await runCapturingPrint(runner, [ + 'make-deps-path-based', + '--target-dependencies=bar,bar_platform_interface' + ]); + final List output = await runCapturingPrint(runner, [ + 'make-deps-path-based', + '--target-dependencies=bar,bar_platform_interface' + ]); + + expect( + output, + containsAll([ + 'Rewriting references to: bar, bar_platform_interface...', + ' Skipped packages/bar/bar/pubspec.yaml - Already rewritten', + ' Skipped packages/bar/bar_android/pubspec.yaml - Already rewritten', + ' Skipped packages/foo/pubspec.yaml - Already rewritten', + ])); + }); + group('target-dependencies-with-non-breaking-updates', () { test('no-ops for no published changes', () async { final Directory package = createFakePackage('foo', packagesDir); From a59777fc77974724bac6a9c44d5677b2c56ef059 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 26 Apr 2022 17:24:11 -0400 Subject: [PATCH 448/600] [url_launcher] Add canLaunch fallback for web on Android (#5399) --- .../url_launcher_android/CHANGELOG.md | 5 + .../example/lib/main.dart | 156 ++++++++++-------- .../lib/url_launcher_android.dart | 31 +++- .../url_launcher_android/pubspec.yaml | 2 +- .../test/url_launcher_android_test.dart | 100 ++++++++--- 5 files changed, 196 insertions(+), 98 deletions(-) diff --git a/packages/url_launcher/url_launcher_android/CHANGELOG.md b/packages/url_launcher/url_launcher_android/CHANGELOG.md index 9ec1f65911c6..69b96156d849 100644 --- a/packages/url_launcher/url_launcher_android/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 6.0.16 + +* Adds fallback querying for `canLaunch` with web URLs, to avoid false negatives + when there is a custom scheme handler. + ## 6.0.15 * Switches to an in-package method channel implementation. diff --git a/packages/url_launcher/url_launcher_android/example/lib/main.dart b/packages/url_launcher/url_launcher_android/example/lib/main.dart index 8721c587075e..7abc73430e8b 100644 --- a/packages/url_launcher/url_launcher_android/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_android/example/lib/main.dart @@ -35,73 +35,74 @@ class MyHomePage extends StatefulWidget { } class _MyHomePageState extends State { + final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + bool _hasCallSupport = false; Future? _launched; String _phone = ''; + @override + void initState() { + super.initState(); + // Check for phone call support. + launcher.canLaunch('tel:123').then((bool result) { + setState(() { + _hasCallSupport = result; + }); + }); + } + Future _launchInBrowser(String url) async { - final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; - if (await launcher.canLaunch(url)) { - await launcher.launch( - url, - useSafariVC: false, - useWebView: false, - enableJavaScript: false, - enableDomStorage: false, - universalLinksOnly: false, - headers: {'my_header_key': 'my_header_value'}, - ); - } else { + if (!await launcher.launch( + url, + useSafariVC: false, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: {}, + )) { throw 'Could not launch $url'; } } - Future _launchInWebViewOrVC(String url) async { - final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; - if (await launcher.canLaunch(url)) { - await launcher.launch( - url, - useSafariVC: true, - useWebView: true, - enableJavaScript: false, - enableDomStorage: false, - universalLinksOnly: false, - headers: {'my_header_key': 'my_header_value'}, - ); - } else { + Future _launchInWebView(String url) async { + if (!await launcher.launch( + url, + useSafariVC: true, + useWebView: true, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: {'my_header_key': 'my_header_value'}, + )) { throw 'Could not launch $url'; } } Future _launchInWebViewWithJavaScript(String url) async { - final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; - if (await launcher.canLaunch(url)) { - await launcher.launch( - url, - useSafariVC: true, - useWebView: true, - enableJavaScript: true, - enableDomStorage: false, - universalLinksOnly: false, - headers: {}, - ); - } else { + if (!await launcher.launch( + url, + useSafariVC: true, + useWebView: true, + enableJavaScript: true, + enableDomStorage: false, + universalLinksOnly: false, + headers: {}, + )) { throw 'Could not launch $url'; } } Future _launchInWebViewWithDomStorage(String url) async { - final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; - if (await launcher.canLaunch(url)) { - await launcher.launch( - url, - useSafariVC: true, - useWebView: true, - enableJavaScript: false, - enableDomStorage: true, - universalLinksOnly: false, - headers: {}, - ); - } else { + if (!await launcher.launch( + url, + useSafariVC: true, + useWebView: true, + enableJavaScript: false, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + )) { throw 'Could not launch $url'; } } @@ -114,25 +115,30 @@ class _MyHomePageState extends State { } } - Future _makePhoneCall(String url) async { - final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; - if (await launcher.canLaunch(url)) { - await launcher.launch( - url, - useSafariVC: false, - useWebView: false, - enableJavaScript: false, - enableDomStorage: false, - universalLinksOnly: true, - headers: {}, - ); - } else { - throw 'Could not launch $url'; - } + Future _makePhoneCall(String phoneNumber) async { + // Use `Uri` to ensure that `phoneNumber` is properly URL-encoded. + // Just using 'tel:$phoneNumber' would create invalid URLs in some cases, + // such as spaces in the input, which would cause `launch` to fail on some + // platforms. + final Uri launchUri = Uri( + scheme: 'tel', + path: phoneNumber, + ); + await launcher.launch( + launchUri.toString(), + useSafariVC: false, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: true, + headers: {}, + ); } @override Widget build(BuildContext context) { + // onPressed calls using this URL are not gated on a 'canLaunch' check + // because the assumption is that every device can launch a web URL. const String toLaunch = 'https://www.cylog.org/headers/'; return Scaffold( appBar: AppBar( @@ -151,10 +157,14 @@ class _MyHomePageState extends State { hintText: 'Input the phone number to launch')), ), ElevatedButton( - onPressed: () => setState(() { - _launched = _makePhoneCall('tel:$_phone'); - }), - child: const Text('Make phone call'), + onPressed: _hasCallSupport + ? () => setState(() { + _launched = _makePhoneCall(_phone); + }) + : null, + child: _hasCallSupport + ? const Text('Make phone call') + : const Text('Calling not supported'), ), const Padding( padding: EdgeInsets.all(16.0), @@ -169,7 +179,7 @@ class _MyHomePageState extends State { const Padding(padding: EdgeInsets.all(16.0)), ElevatedButton( onPressed: () => setState(() { - _launched = _launchInWebViewOrVC(toLaunch); + _launched = _launchInWebView(toLaunch); }), child: const Text('Launch in app'), ), @@ -177,21 +187,21 @@ class _MyHomePageState extends State { onPressed: () => setState(() { _launched = _launchInWebViewWithJavaScript(toLaunch); }), - child: const Text('Launch in app(JavaScript ON)'), + child: const Text('Launch in app (JavaScript ON)'), ), ElevatedButton( onPressed: () => setState(() { _launched = _launchInWebViewWithDomStorage(toLaunch); }), - child: const Text('Launch in app(DOM storage ON)'), + child: const Text('Launch in app (DOM storage ON)'), ), const Padding(padding: EdgeInsets.all(16.0)), ElevatedButton( onPressed: () => setState(() { - _launched = _launchInWebViewOrVC(toLaunch); + _launched = _launchInWebView(toLaunch); Timer(const Duration(seconds: 5), () { print('Closing WebView after 5 seconds...'); - UrlLauncherPlatform.instance.closeWebView(); + launcher.closeWebView(); }); }), child: const Text('Launch in app + close after 5 seconds'), diff --git a/packages/url_launcher/url_launcher_android/lib/url_launcher_android.dart b/packages/url_launcher/url_launcher_android/lib/url_launcher_android.dart index 52c46356489d..1aa093a36451 100644 --- a/packages/url_launcher/url_launcher_android/lib/url_launcher_android.dart +++ b/packages/url_launcher/url_launcher_android/lib/url_launcher_android.dart @@ -22,7 +22,24 @@ class UrlLauncherAndroid extends UrlLauncherPlatform { final LinkDelegate? linkDelegate = null; @override - Future canLaunch(String url) { + Future canLaunch(String url) async { + final bool canLaunchSpecificUrl = await _canLaunchUrl(url); + if (!canLaunchSpecificUrl) { + final String scheme = _getUrlScheme(url); + // canLaunch can return false when a custom application is registered to + // handle a web URL, but the caller doesn't have permission to see what + // that handler is. If that happens, try a web URL (with the same scheme + // variant, to be safe) that should not have a custom handler. If that + // returns true, then there is a browser, which means that there is + // at least one handler for the original URL. + if (scheme == 'http' || scheme == 'https') { + return await _canLaunchUrl('$scheme://flutter.dev'); + } + } + return canLaunchSpecificUrl; + } + + Future _canLaunchUrl(String url) { return _channel.invokeMethod( 'canLaunch', {'url': url}, @@ -57,4 +74,16 @@ class UrlLauncherAndroid extends UrlLauncherPlatform { }, ).then((bool? value) => value ?? false); } + + // Returns the part of [url] up to the first ':', or an empty string if there + // is no ':'. This deliberately does not use [Uri] to extract the scheme + // so that it works on strings that aren't actually valid URLs, since Android + // is very lenient about what it accepts for launching. + String _getUrlScheme(String url) { + final int schemeEnd = url.indexOf(':'); + if (schemeEnd == -1) { + return ''; + } + return url.substring(0, schemeEnd); + } } diff --git a/packages/url_launcher/url_launcher_android/pubspec.yaml b/packages/url_launcher/url_launcher_android/pubspec.yaml index b8706aeb13f2..3230dfeffd2e 100644 --- a/packages/url_launcher/url_launcher_android/pubspec.yaml +++ b/packages/url_launcher/url_launcher_android/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_android description: Android implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.0.15 +version: 6.0.16 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_android/test/url_launcher_android_test.dart b/packages/url_launcher/url_launcher_android/test/url_launcher_android_test.dart index 909d2c100ecf..eebd8cd4c059 100644 --- a/packages/url_launcher/url_launcher_android/test/url_launcher_android_test.dart +++ b/packages/url_launcher/url_launcher_android/test/url_launcher_android_test.dart @@ -10,10 +10,12 @@ import 'package:url_launcher_platform_interface/url_launcher_platform_interface. void main() { TestWidgetsFlutterBinding.ensureInitialized(); - group('$UrlLauncherAndroid', () { - const MethodChannel channel = - MethodChannel('plugins.flutter.io/url_launcher_android'); - final List log = []; + const MethodChannel channel = + MethodChannel('plugins.flutter.io/url_launcher_android'); + late List log; + + setUp(() { + log = []; channel.setMockMethodCallHandler((MethodCall methodCall) async { log.add(methodCall); @@ -21,19 +23,21 @@ void main() { // returned by the method channel if no return statement is specified. return null; }); + }); - tearDown(() { - log.clear(); - }); - - test('registers instance', () { - UrlLauncherAndroid.registerWith(); - expect(UrlLauncherPlatform.instance, isA()); - }); + test('registers instance', () { + UrlLauncherAndroid.registerWith(); + expect(UrlLauncherPlatform.instance, isA()); + }); - test('canLaunch', () async { + group('canLaunch', () { + test('calls through', () async { + channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + return true; + }); final UrlLauncherAndroid launcher = UrlLauncherAndroid(); - await launcher.canLaunch('http://example.com/'); + final bool canLaunch = await launcher.canLaunch('http://example.com/'); expect( log, [ @@ -42,16 +46,64 @@ void main() { }) ], ); + expect(canLaunch, true); }); - test('canLaunch should return false if platform returns null', () async { + test('returns false if platform returns null', () async { final UrlLauncherAndroid launcher = UrlLauncherAndroid(); final bool canLaunch = await launcher.canLaunch('http://example.com/'); expect(canLaunch, false); }); - test('launch', () async { + test('checks a generic URL if an http URL returns false', () async { + const String specificUrl = 'http://example.com/'; + const String genericUrl = 'http://flutter.dev'; + channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + return methodCall.arguments['url'] != specificUrl; + }); + + final UrlLauncherAndroid launcher = UrlLauncherAndroid(); + final bool canLaunch = await launcher.canLaunch(specificUrl); + + expect(canLaunch, true); + expect(log.length, 2); + expect(log[1].arguments['url'], genericUrl); + }); + + test('checks a generic URL if an https URL returns false', () async { + const String specificUrl = 'https://example.com/'; + const String genericUrl = 'https://flutter.dev'; + channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + return methodCall.arguments['url'] != specificUrl; + }); + + final UrlLauncherAndroid launcher = UrlLauncherAndroid(); + final bool canLaunch = await launcher.canLaunch(specificUrl); + + expect(canLaunch, true); + expect(log.length, 2); + expect(log[1].arguments['url'], genericUrl); + }); + + test('does not a generic URL if a non-web URL returns false', () async { + channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + return false; + }); + + final UrlLauncherAndroid launcher = UrlLauncherAndroid(); + final bool canLaunch = await launcher.canLaunch('sms:12345'); + + expect(canLaunch, false); + expect(log.length, 1); + }); + }); + + group('launch', () { + test('calls through', () async { final UrlLauncherAndroid launcher = UrlLauncherAndroid(); await launcher.launch( 'http://example.com/', @@ -77,7 +129,7 @@ void main() { ); }); - test('launch with headers', () async { + test('passes headers', () async { final UrlLauncherAndroid launcher = UrlLauncherAndroid(); await launcher.launch( 'http://example.com/', @@ -103,7 +155,7 @@ void main() { ); }); - test('launch universal links only', () async { + test('handles universal links only', () async { final UrlLauncherAndroid launcher = UrlLauncherAndroid(); await launcher.launch( 'http://example.com/', @@ -129,7 +181,7 @@ void main() { ); }); - test('launch force WebView', () async { + test('handles force WebView', () async { final UrlLauncherAndroid launcher = UrlLauncherAndroid(); await launcher.launch( 'http://example.com/', @@ -155,7 +207,7 @@ void main() { ); }); - test('launch force WebView enable javascript', () async { + test('handles force WebView with javascript', () async { final UrlLauncherAndroid launcher = UrlLauncherAndroid(); await launcher.launch( 'http://example.com/', @@ -181,7 +233,7 @@ void main() { ); }); - test('launch force WebView enable DOM storage', () async { + test('handles force WebView with DOM storage', () async { final UrlLauncherAndroid launcher = UrlLauncherAndroid(); await launcher.launch( 'http://example.com/', @@ -207,7 +259,7 @@ void main() { ); }); - test('launch should return false if platform returns null', () async { + test('returns false if platform returns null', () async { final UrlLauncherAndroid launcher = UrlLauncherAndroid(); final bool launched = await launcher.launch( 'http://example.com/', @@ -221,8 +273,10 @@ void main() { expect(launched, false); }); + }); - test('closeWebView default behavior', () async { + group('closeWebView', () { + test('calls through', () async { final UrlLauncherAndroid launcher = UrlLauncherAndroid(); await launcher.closeWebView(); expect( From 3e43f590d4d0143972e702eb14f14658cdc2decb Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Tue, 26 Apr 2022 15:29:06 -0700 Subject: [PATCH 449/600] [path_provider] Fix Unchecked/Unsafe Operation Warning (#5267) --- .../path_provider_android/CHANGELOG.md | 4 ++++ .../pathprovider/PathProviderPlugin.java | 24 ++++++------------- .../path_provider_android/pubspec.yaml | 4 ++-- 3 files changed, 13 insertions(+), 19 deletions(-) diff --git a/packages/path_provider/path_provider_android/CHANGELOG.md b/packages/path_provider/path_provider_android/CHANGELOG.md index 7b04e90d5e0f..31f8c81f8a65 100644 --- a/packages/path_provider/path_provider_android/CHANGELOG.md +++ b/packages/path_provider/path_provider_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.13 + +* Fixes typing build warning. + ## 2.0.12 * Returns to using a different platform channel name, undoing the revert in diff --git a/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java index 278ff58b59dc..6dcf9595ac86 100644 --- a/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java +++ b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java @@ -17,16 +17,14 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder; import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugin.common.BinaryMessenger.TaskQueue; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel.MethodCallHandler; import io.flutter.plugin.common.MethodChannel.Result; -import io.flutter.plugin.common.MethodCodec; import io.flutter.plugin.common.StandardMethodCodec; import io.flutter.util.PathUtils; import java.io.File; -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; @@ -154,25 +152,17 @@ public PathProviderPlugin() {} private void setup(BinaryMessenger messenger, Context context) { String channelName = "plugins.flutter.io/path_provider_android"; - // TODO(gaaclarke): Remove reflection guard when https://github.com/flutter/engine/pull/29147 - // becomes available on the stable branch. + TaskQueue taskQueue = messenger.makeBackgroundTaskQueue(); + try { - Class methodChannelClass = Class.forName("io.flutter.plugin.common.MethodChannel"); - Class taskQueueClass = Class.forName("io.flutter.plugin.common.BinaryMessenger$TaskQueue"); - Method makeBackgroundTaskQueue = messenger.getClass().getMethod("makeBackgroundTaskQueue"); - Object taskQueue = makeBackgroundTaskQueue.invoke(messenger); - Constructor constructor = - methodChannelClass.getConstructor( - BinaryMessenger.class, String.class, MethodCodec.class, taskQueueClass); channel = - constructor.newInstance(messenger, channelName, StandardMethodCodec.INSTANCE, taskQueue); + (MethodChannel) + new MethodChannel(messenger, channelName, StandardMethodCodec.INSTANCE, taskQueue); impl = new PathProviderBackgroundThread(); - Log.d(TAG, "Use TaskQueues."); } catch (Exception ex) { - channel = new MethodChannel(messenger, channelName); - impl = new PathProviderPlatformThread(); - Log.d(TAG, "Don't use TaskQueues."); + Log.e(TAG, "Received exception while setting up PathProviderPlugin", ex); } + this.context = context; channel.setMethodCallHandler(this); } diff --git a/packages/path_provider/path_provider_android/pubspec.yaml b/packages/path_provider/path_provider_android/pubspec.yaml index 63b9330a89f9..93ed9848f75b 100644 --- a/packages/path_provider/path_provider_android/pubspec.yaml +++ b/packages/path_provider/path_provider_android/pubspec.yaml @@ -2,11 +2,11 @@ name: path_provider_android description: Android implementation of the path_provider plugin. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.12 +version: 2.0.13 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.8.1" flutter: plugin: From a9d5a93fd23c63238fa56958a138b36e6079f69d Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 27 Apr 2022 14:59:06 -0400 Subject: [PATCH 450/600] [google_sign_in] Switch to internal method channels (#5396) --- .../google_sign_in_android/CHANGELOG.md | 4 + .../googlesignin/GoogleSignInPlugin.java | 2 +- .../integration_test/google_sign_in_test.dart | 8 + .../lib/google_sign_in_android.dart | 95 +++++++++++ .../google_sign_in_android/lib/src/utils.dart | 28 ++++ .../google_sign_in_android/pubspec.yaml | 5 +- .../test/google_sign_in_android_test.dart | 143 +++++++++++++++++ .../google_sign_in_ios/CHANGELOG.md | 4 + .../integration_test/google_sign_in_test.dart | 8 + .../ios/RunnerTests/GoogleSignInTests.m | 27 ---- .../ios/Classes/FLTGoogleSignInPlugin.m | 56 +++---- .../lib/google_sign_in_ios.dart | 97 +++++++++++ .../google_sign_in_ios/lib/src/utils.dart | 28 ++++ .../google_sign_in_ios/pubspec.yaml | 5 +- .../test/google_sign_in_ios_test.dart | 151 ++++++++++++++++++ 15 files changed, 595 insertions(+), 66 deletions(-) create mode 100644 packages/google_sign_in/google_sign_in_android/lib/google_sign_in_android.dart create mode 100644 packages/google_sign_in/google_sign_in_android/lib/src/utils.dart create mode 100644 packages/google_sign_in/google_sign_in_android/test/google_sign_in_android_test.dart create mode 100644 packages/google_sign_in/google_sign_in_ios/lib/google_sign_in_ios.dart create mode 100644 packages/google_sign_in/google_sign_in_ios/lib/src/utils.dart create mode 100644 packages/google_sign_in/google_sign_in_ios/test/google_sign_in_ios_test.dart diff --git a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md index e7c6847e7750..3ffa6b5b7d6b 100644 --- a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 5.2.6 + +* Switches to an internal method channel, rather than the default. + ## 5.2.5 * Splits from `video_player` as a federated implementation. diff --git a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java index 1be023c678bb..a1237f0013a1 100644 --- a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java +++ b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java @@ -44,7 +44,7 @@ /** Google sign-in plugin for Flutter. */ public class GoogleSignInPlugin implements MethodCallHandler, FlutterPlugin, ActivityAware { - private static final String CHANNEL_NAME = "plugins.flutter.io/google_sign_in"; + private static final String CHANNEL_NAME = "plugins.flutter.io/google_sign_in_android"; private static final String METHOD_INIT = "init"; private static final String METHOD_SIGN_IN_SILENTLY = "signInSilently"; diff --git a/packages/google_sign_in/google_sign_in_android/example/integration_test/google_sign_in_test.dart b/packages/google_sign_in/google_sign_in_android/example/integration_test/google_sign_in_test.dart index d4631f6a6fd3..f1388ce86d67 100644 --- a/packages/google_sign_in/google_sign_in_android/example/integration_test/google_sign_in_test.dart +++ b/packages/google_sign_in/google_sign_in_android/example/integration_test/google_sign_in_test.dart @@ -13,4 +13,12 @@ void main() { final GoogleSignInPlatform signIn = GoogleSignInPlatform.instance; expect(signIn, isNotNull); }); + + testWidgets('Method channel handler is present', (WidgetTester tester) async { + // isSignedIn can be called without initialization, so use it to validate + // that the native method handler is present (e.g., that the channel name + // is correct). + final GoogleSignInPlatform signIn = GoogleSignInPlatform.instance; + await expectLater(signIn.isSignedIn(), completes); + }); } diff --git a/packages/google_sign_in/google_sign_in_android/lib/google_sign_in_android.dart b/packages/google_sign_in/google_sign_in_android/lib/google_sign_in_android.dart new file mode 100644 index 000000000000..d96328de695a --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/lib/google_sign_in_android.dart @@ -0,0 +1,95 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/foundation.dart' show visibleForTesting; +import 'package:flutter/services.dart'; +import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; + +import 'src/utils.dart'; + +/// Android implementation of [GoogleSignInPlatform]. +class GoogleSignInAndroid extends GoogleSignInPlatform { + /// This is only exposed for test purposes. It shouldn't be used by clients of + /// the plugin as it may break or change at any time. + @visibleForTesting + MethodChannel channel = + const MethodChannel('plugins.flutter.io/google_sign_in_android'); + + /// Registers this class as the default instance of [GoogleSignInPlatform]. + static void registerWith() { + GoogleSignInPlatform.instance = GoogleSignInAndroid(); + } + + @override + Future init({ + List scopes = const [], + SignInOption signInOption = SignInOption.standard, + String? hostedDomain, + String? clientId, + }) { + return channel.invokeMethod('init', { + 'signInOption': signInOption.toString(), + 'scopes': scopes, + 'hostedDomain': hostedDomain, + 'clientId': clientId, + }); + } + + @override + Future signInSilently() { + return channel + .invokeMapMethod('signInSilently') + .then(getUserDataFromMap); + } + + @override + Future signIn() { + return channel + .invokeMapMethod('signIn') + .then(getUserDataFromMap); + } + + @override + Future getTokens( + {required String email, bool? shouldRecoverAuth = true}) { + return channel + .invokeMapMethod('getTokens', { + 'email': email, + 'shouldRecoverAuth': shouldRecoverAuth, + }).then((Map? result) => getTokenDataFromMap(result!)); + } + + @override + Future signOut() { + return channel.invokeMapMethod('signOut'); + } + + @override + Future disconnect() { + return channel.invokeMapMethod('disconnect'); + } + + @override + Future isSignedIn() async { + return (await channel.invokeMethod('isSignedIn'))!; + } + + @override + Future clearAuthCache({String? token}) { + return channel.invokeMethod( + 'clearAuthCache', + {'token': token}, + ); + } + + @override + Future requestScopes(List scopes) async { + return (await channel.invokeMethod( + 'requestScopes', + >{'scopes': scopes}, + ))!; + } +} diff --git a/packages/google_sign_in/google_sign_in_android/lib/src/utils.dart b/packages/google_sign_in/google_sign_in_android/lib/src/utils.dart new file mode 100644 index 000000000000..5cd7c20b829a --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/lib/src/utils.dart @@ -0,0 +1,28 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; + +/// Converts user data coming from native code into the proper platform interface type. +GoogleSignInUserData? getUserDataFromMap(Map? data) { + if (data == null) { + return null; + } + return GoogleSignInUserData( + email: data['email']! as String, + id: data['id']! as String, + displayName: data['displayName'] as String?, + photoUrl: data['photoUrl'] as String?, + idToken: data['idToken'] as String?, + serverAuthCode: data['serverAuthCode'] as String?); +} + +/// Converts token data coming from native code into the proper platform interface type. +GoogleSignInTokenData getTokenDataFromMap(Map data) { + return GoogleSignInTokenData( + idToken: data['idToken'] as String?, + accessToken: data['accessToken'] as String?, + serverAuthCode: data['serverAuthCode'] as String?, + ); +} diff --git a/packages/google_sign_in/google_sign_in_android/pubspec.yaml b/packages/google_sign_in/google_sign_in_android/pubspec.yaml index efd679ca399b..fa3dc1489b26 100644 --- a/packages/google_sign_in/google_sign_in_android/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_android/pubspec.yaml @@ -2,17 +2,18 @@ name: google_sign_in_android description: Android implementation of the google_sign_in plugin. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.2.5 +version: 5.2.6 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: implements: google_sign_in platforms: android: + dartPluginClass: GoogleSignInAndroid package: io.flutter.plugins.googlesignin pluginClass: GoogleSignInPlugin diff --git a/packages/google_sign_in/google_sign_in_android/test/google_sign_in_android_test.dart b/packages/google_sign_in/google_sign_in_android/test/google_sign_in_android_test.dart new file mode 100644 index 000000000000..7d39ae5f0232 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/test/google_sign_in_android_test.dart @@ -0,0 +1,143 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:google_sign_in_android/google_sign_in_android.dart'; +import 'package:google_sign_in_android/src/utils.dart'; +import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; + +const Map kUserData = { + 'email': 'john.doe@gmail.com', + 'id': '8162538176523816253123', + 'photoUrl': 'https://lh5.googleusercontent.com/photo.jpg', + 'displayName': 'John Doe', + 'idToken': '123', + 'serverAuthCode': '789', +}; + +const Map kTokenData = { + 'idToken': '123', + 'accessToken': '456', + 'serverAuthCode': '789', +}; + +const Map kDefaultResponses = { + 'init': null, + 'signInSilently': kUserData, + 'signIn': kUserData, + 'signOut': null, + 'disconnect': null, + 'isSignedIn': true, + 'getTokens': kTokenData, + 'requestScopes': true, +}; + +final GoogleSignInUserData? kUser = getUserDataFromMap(kUserData); +final GoogleSignInTokenData kToken = + getTokenDataFromMap(kTokenData as Map); + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + final GoogleSignInAndroid googleSignIn = GoogleSignInAndroid(); + final MethodChannel channel = googleSignIn.channel; + + final List log = []; + late Map + responses; // Some tests mutate some kDefaultResponses + + setUp(() { + responses = Map.from(kDefaultResponses); + channel.setMockMethodCallHandler((MethodCall methodCall) { + log.add(methodCall); + final dynamic response = responses[methodCall.method]; + if (response != null && response is Exception) { + return Future.error('$response'); + } + return Future.value(response); + }); + log.clear(); + }); + + test('registered instance', () { + GoogleSignInAndroid.registerWith(); + expect(GoogleSignInPlatform.instance, isA()); + }); + + test('signInSilently transforms platform data to GoogleSignInUserData', + () async { + final dynamic response = await googleSignIn.signInSilently(); + expect(response, kUser); + }); + test('signInSilently Exceptions -> throws', () async { + responses['signInSilently'] = Exception('Not a user'); + expect(googleSignIn.signInSilently(), + throwsA(isInstanceOf())); + }); + + test('signIn transforms platform data to GoogleSignInUserData', () async { + final dynamic response = await googleSignIn.signIn(); + expect(response, kUser); + }); + test('signIn Exceptions -> throws', () async { + responses['signIn'] = Exception('Not a user'); + expect(googleSignIn.signIn(), throwsA(isInstanceOf())); + }); + + test('getTokens transforms platform data to GoogleSignInTokenData', () async { + final dynamic response = await googleSignIn.getTokens( + email: 'example@example.com', shouldRecoverAuth: false); + expect(response, kToken); + expect( + log[0], + isMethodCall('getTokens', arguments: { + 'email': 'example@example.com', + 'shouldRecoverAuth': false, + })); + }); + + test('Other functions pass through arguments to the channel', () async { + final Map tests = { + () { + googleSignIn.init( + hostedDomain: 'example.com', + scopes: ['two', 'scopes'], + signInOption: SignInOption.games, + clientId: 'fakeClientId'); + }: isMethodCall('init', arguments: { + 'hostedDomain': 'example.com', + 'scopes': ['two', 'scopes'], + 'signInOption': 'SignInOption.games', + 'clientId': 'fakeClientId', + }), + () { + googleSignIn.getTokens( + email: 'example@example.com', shouldRecoverAuth: false); + }: isMethodCall('getTokens', arguments: { + 'email': 'example@example.com', + 'shouldRecoverAuth': false, + }), + () { + googleSignIn.clearAuthCache(token: 'abc'); + }: isMethodCall('clearAuthCache', arguments: { + 'token': 'abc', + }), + () { + googleSignIn.requestScopes(['newScope', 'anotherScope']); + }: isMethodCall('requestScopes', arguments: { + 'scopes': ['newScope', 'anotherScope'], + }), + googleSignIn.signOut: isMethodCall('signOut', arguments: null), + googleSignIn.disconnect: isMethodCall('disconnect', arguments: null), + googleSignIn.isSignedIn: isMethodCall('isSignedIn', arguments: null), + }; + + for (final Function f in tests.keys) { + f(); + } + + expect(log, tests.values); + }); +} diff --git a/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md b/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md index e7c6847e7750..3ffa6b5b7d6b 100644 --- a/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 5.2.6 + +* Switches to an internal method channel, rather than the default. + ## 5.2.5 * Splits from `video_player` as a federated implementation. diff --git a/packages/google_sign_in/google_sign_in_ios/example/integration_test/google_sign_in_test.dart b/packages/google_sign_in/google_sign_in_ios/example/integration_test/google_sign_in_test.dart index d4631f6a6fd3..f1388ce86d67 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/integration_test/google_sign_in_test.dart +++ b/packages/google_sign_in/google_sign_in_ios/example/integration_test/google_sign_in_test.dart @@ -13,4 +13,12 @@ void main() { final GoogleSignInPlatform signIn = GoogleSignInPlatform.instance; expect(signIn, isNotNull); }); + + testWidgets('Method channel handler is present', (WidgetTester tester) async { + // isSignedIn can be called without initialization, so use it to validate + // that the native method handler is present (e.g., that the channel name + // is correct). + final GoogleSignInPlatform signIn = GoogleSignInPlatform.instance; + await expectLater(signIn.isSignedIn(), completes); + }); } diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m b/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m index dbbd65397ac5..3bc08d18604a 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m @@ -73,35 +73,8 @@ - (void)testDisconnect { OCMVerify([self.mockSignIn disconnect]); } -- (void)testClearAuthCache { - FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"clearAuthCache" - arguments:nil]; - - XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; - [self.plugin handleMethodCall:methodCall - result:^(id result) { - XCTAssertNil(result); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:5.0 handler:nil]; -} - #pragma mark - Init -- (void)testInitGamesSignInUnsupported { - FlutterMethodCall *methodCall = - [FlutterMethodCall methodCallWithMethodName:@"init" - arguments:@{@"signInOption" : @"SignInOption.games"}]; - - XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; - [self.plugin handleMethodCall:methodCall - result:^(FlutterError *result) { - XCTAssertEqualObjects(result.code, @"unsupported-options"); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:5.0 handler:nil]; -} - - (void)testInitGoogleServiceInfoPlist { FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"init" diff --git a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m index d13d64d2ba04..5ad69e2ad052 100644 --- a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m +++ b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m @@ -50,7 +50,7 @@ @implementation FLTGoogleSignInPlugin { + (void)registerWithRegistrar:(NSObject *)registrar { FlutterMethodChannel *channel = - [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/google_sign_in" + [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/google_sign_in_ios" binaryMessenger:[registrar messenger]]; FLTGoogleSignInPlugin *instance = [[FLTGoogleSignInPlugin alloc] init]; [registrar addApplicationDelegate:instance]; @@ -78,38 +78,30 @@ - (instancetype)initWithSignIn:(GIDSignIn *)signIn { - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { if ([call.method isEqualToString:@"init"]) { - NSString *signInOption = call.arguments[@"signInOption"]; - if ([signInOption isEqualToString:@"SignInOption.games"]) { - result([FlutterError errorWithCode:@"unsupported-options" - message:@"Games sign in is not supported on iOS" - details:nil]); - } else { - NSString *path = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info" - ofType:@"plist"]; - if (path) { - NSMutableDictionary *plist = - [[NSMutableDictionary alloc] initWithContentsOfFile:path]; - BOOL hasDynamicClientId = [call.arguments[@"clientId"] isKindOfClass:[NSString class]]; - - if (hasDynamicClientId) { - self.signIn.clientID = call.arguments[@"clientId"]; - } else { - self.signIn.clientID = plist[kClientIdKey]; - } + NSString *path = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info" ofType:@"plist"]; + if (path) { + NSMutableDictionary *plist = + [[NSMutableDictionary alloc] initWithContentsOfFile:path]; + BOOL hasDynamicClientId = [call.arguments[@"clientId"] isKindOfClass:[NSString class]]; + + if (hasDynamicClientId) { + self.signIn.clientID = call.arguments[@"clientId"]; + } else { + self.signIn.clientID = plist[kClientIdKey]; + } - self.signIn.serverClientID = plist[kServerClientIdKey]; - self.signIn.scopes = call.arguments[@"scopes"]; - if (call.arguments[@"hostedDomain"] == [NSNull null]) { - self.signIn.hostedDomain = nil; - } else { - self.signIn.hostedDomain = call.arguments[@"hostedDomain"]; - } - result(nil); + self.signIn.serverClientID = plist[kServerClientIdKey]; + self.signIn.scopes = call.arguments[@"scopes"]; + if (call.arguments[@"hostedDomain"] == [NSNull null]) { + self.signIn.hostedDomain = nil; } else { - result([FlutterError errorWithCode:@"missing-config" - message:@"GoogleService-Info.plist file not found" - details:nil]); + self.signIn.hostedDomain = call.arguments[@"hostedDomain"]; } + result(nil); + } else { + result([FlutterError errorWithCode:@"missing-config" + message:@"GoogleService-Info.plist file not found" + details:nil]); } } else if ([call.method isEqualToString:@"signInSilently"]) { if ([self setAccountRequest:result]) { @@ -144,10 +136,6 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result if ([self setAccountRequest:result]) { [self.signIn disconnect]; } - } else if ([call.method isEqualToString:@"clearAuthCache"]) { - // There's nothing to be done here on iOS since the expired/invalid - // tokens are refreshed automatically by getTokensWithHandler. - result(nil); } else if ([call.method isEqualToString:@"requestScopes"]) { GIDGoogleUser *user = self.signIn.currentUser; if (user == nil) { diff --git a/packages/google_sign_in/google_sign_in_ios/lib/google_sign_in_ios.dart b/packages/google_sign_in/google_sign_in_ios/lib/google_sign_in_ios.dart new file mode 100644 index 000000000000..ce8865664507 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/lib/google_sign_in_ios.dart @@ -0,0 +1,97 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/foundation.dart' show visibleForTesting; +import 'package:flutter/services.dart'; +import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; + +import 'src/utils.dart'; + +/// iOS implementation of [GoogleSignInPlatform]. +class GoogleSignInIOS extends GoogleSignInPlatform { + /// This is only exposed for test purposes. It shouldn't be used by clients of + /// the plugin as it may break or change at any time. + @visibleForTesting + MethodChannel channel = + const MethodChannel('plugins.flutter.io/google_sign_in_ios'); + + /// Registers this class as the default instance of [GoogleSignInPlatform]. + static void registerWith() { + GoogleSignInPlatform.instance = GoogleSignInIOS(); + } + + @override + Future init({ + List scopes = const [], + SignInOption signInOption = SignInOption.standard, + String? hostedDomain, + String? clientId, + }) { + if (signInOption == SignInOption.games) { + throw PlatformException( + code: 'unsupported-options', + message: 'Games sign in is not supported on iOS'); + } + return channel.invokeMethod('init', { + 'scopes': scopes, + 'hostedDomain': hostedDomain, + 'clientId': clientId, + }); + } + + @override + Future signInSilently() { + return channel + .invokeMapMethod('signInSilently') + .then(getUserDataFromMap); + } + + @override + Future signIn() { + return channel + .invokeMapMethod('signIn') + .then(getUserDataFromMap); + } + + @override + Future getTokens( + {required String email, bool? shouldRecoverAuth = true}) { + return channel + .invokeMapMethod('getTokens', { + 'email': email, + 'shouldRecoverAuth': shouldRecoverAuth, + }).then((Map? result) => getTokenDataFromMap(result!)); + } + + @override + Future signOut() { + return channel.invokeMapMethod('signOut'); + } + + @override + Future disconnect() { + return channel.invokeMapMethod('disconnect'); + } + + @override + Future isSignedIn() async { + return (await channel.invokeMethod('isSignedIn'))!; + } + + @override + Future clearAuthCache({String? token}) async { + // There's nothing to be done here on iOS since the expired/invalid + // tokens are refreshed automatically by getTokens. + } + + @override + Future requestScopes(List scopes) async { + return (await channel.invokeMethod( + 'requestScopes', + >{'scopes': scopes}, + ))!; + } +} diff --git a/packages/google_sign_in/google_sign_in_ios/lib/src/utils.dart b/packages/google_sign_in/google_sign_in_ios/lib/src/utils.dart new file mode 100644 index 000000000000..5cd7c20b829a --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/lib/src/utils.dart @@ -0,0 +1,28 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; + +/// Converts user data coming from native code into the proper platform interface type. +GoogleSignInUserData? getUserDataFromMap(Map? data) { + if (data == null) { + return null; + } + return GoogleSignInUserData( + email: data['email']! as String, + id: data['id']! as String, + displayName: data['displayName'] as String?, + photoUrl: data['photoUrl'] as String?, + idToken: data['idToken'] as String?, + serverAuthCode: data['serverAuthCode'] as String?); +} + +/// Converts token data coming from native code into the proper platform interface type. +GoogleSignInTokenData getTokenDataFromMap(Map data) { + return GoogleSignInTokenData( + idToken: data['idToken'] as String?, + accessToken: data['accessToken'] as String?, + serverAuthCode: data['serverAuthCode'] as String?, + ); +} diff --git a/packages/google_sign_in/google_sign_in_ios/pubspec.yaml b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml index 52c4f77feb4b..e5ef3832ff0e 100644 --- a/packages/google_sign_in/google_sign_in_ios/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml @@ -2,17 +2,18 @@ name: google_sign_in_ios description: Android implementation of the google_sign_in plugin. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.2.5 +version: 5.2.6 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: implements: google_sign_in platforms: ios: + dartPluginClass: GoogleSignInIOS pluginClass: FLTGoogleSignInPlugin dependencies: diff --git a/packages/google_sign_in/google_sign_in_ios/test/google_sign_in_ios_test.dart b/packages/google_sign_in/google_sign_in_ios/test/google_sign_in_ios_test.dart new file mode 100644 index 000000000000..92637e938fd9 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/test/google_sign_in_ios_test.dart @@ -0,0 +1,151 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:google_sign_in_ios/google_sign_in_ios.dart'; +import 'package:google_sign_in_ios/src/utils.dart'; +import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; + +const Map kUserData = { + 'email': 'john.doe@gmail.com', + 'id': '8162538176523816253123', + 'photoUrl': 'https://lh5.googleusercontent.com/photo.jpg', + 'displayName': 'John Doe', + 'idToken': '123', + 'serverAuthCode': '789', +}; + +const Map kTokenData = { + 'idToken': '123', + 'accessToken': '456', + 'serverAuthCode': '789', +}; + +const Map kDefaultResponses = { + 'init': null, + 'signInSilently': kUserData, + 'signIn': kUserData, + 'signOut': null, + 'disconnect': null, + 'isSignedIn': true, + 'getTokens': kTokenData, + 'requestScopes': true, +}; + +final GoogleSignInUserData? kUser = getUserDataFromMap(kUserData); +final GoogleSignInTokenData kToken = + getTokenDataFromMap(kTokenData as Map); + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + final GoogleSignInIOS googleSignIn = GoogleSignInIOS(); + final MethodChannel channel = googleSignIn.channel; + + late List log; + late Map + responses; // Some tests mutate some kDefaultResponses + + setUp(() { + responses = Map.from(kDefaultResponses); + log = []; + channel.setMockMethodCallHandler((MethodCall methodCall) { + log.add(methodCall); + final dynamic response = responses[methodCall.method]; + if (response != null && response is Exception) { + return Future.error('$response'); + } + return Future.value(response); + }); + }); + + test('registered instance', () { + GoogleSignInIOS.registerWith(); + expect(GoogleSignInPlatform.instance, isA()); + }); + + test('init throws for SignInOptions.games', () async { + expect( + () => googleSignIn.init( + hostedDomain: 'example.com', + signInOption: SignInOption.games, + clientId: 'fakeClientId'), + throwsA(isInstanceOf().having( + (PlatformException e) => e.code, 'code', 'unsupported-options'))); + }); + + test('signInSilently transforms platform data to GoogleSignInUserData', + () async { + final dynamic response = await googleSignIn.signInSilently(); + expect(response, kUser); + }); + test('signInSilently Exceptions -> throws', () async { + responses['signInSilently'] = Exception('Not a user'); + expect(googleSignIn.signInSilently(), + throwsA(isInstanceOf())); + }); + + test('signIn transforms platform data to GoogleSignInUserData', () async { + final dynamic response = await googleSignIn.signIn(); + expect(response, kUser); + }); + test('signIn Exceptions -> throws', () async { + responses['signIn'] = Exception('Not a user'); + expect(googleSignIn.signIn(), throwsA(isInstanceOf())); + }); + + test('getTokens transforms platform data to GoogleSignInTokenData', () async { + final dynamic response = await googleSignIn.getTokens( + email: 'example@example.com', shouldRecoverAuth: false); + expect(response, kToken); + expect( + log[0], + isMethodCall('getTokens', arguments: { + 'email': 'example@example.com', + 'shouldRecoverAuth': false, + })); + }); + + test('clearAuthCache is a no-op', () async { + await googleSignIn.clearAuthCache(token: 'abc'); + expect(log.isEmpty, true); + }); + + test('Other functions pass through arguments to the channel', () async { + final Map tests = { + () { + googleSignIn.init( + hostedDomain: 'example.com', + scopes: ['two', 'scopes'], + clientId: 'fakeClientId'); + }: isMethodCall('init', arguments: { + 'hostedDomain': 'example.com', + 'scopes': ['two', 'scopes'], + 'clientId': 'fakeClientId', + }), + () { + googleSignIn.getTokens( + email: 'example@example.com', shouldRecoverAuth: false); + }: isMethodCall('getTokens', arguments: { + 'email': 'example@example.com', + 'shouldRecoverAuth': false, + }), + () { + googleSignIn.requestScopes(['newScope', 'anotherScope']); + }: isMethodCall('requestScopes', arguments: { + 'scopes': ['newScope', 'anotherScope'], + }), + googleSignIn.signOut: isMethodCall('signOut', arguments: null), + googleSignIn.disconnect: isMethodCall('disconnect', arguments: null), + googleSignIn.isSignedIn: isMethodCall('isSignedIn', arguments: null), + }; + + for (final Function f in tests.keys) { + f(); + } + + expect(log, tests.values); + }); +} From d78cc8eaaf693848867e9b76933a94a78b3fb799 Mon Sep 17 00:00:00 2001 From: Mahesh Jamdade <31410839+maheshmnj@users.noreply.github.com> Date: Thu, 28 Apr 2022 01:29:07 +0530 Subject: [PATCH 451/600] [google_maps_flutter] update maps sdk for Android (#4984) --- packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md | 3 ++- .../google_maps_flutter/android/build.gradle | 4 ++-- .../io/flutter/plugins/googlemaps/CircleControllerTest.java | 4 ++-- .../io/flutter/plugins/googlemaps/PolygonControllerTest.java | 4 ++-- .../io/flutter/plugins/googlemaps/PolylineControllerTest.java | 4 ++-- .../google_maps_flutter/example/android/build.gradle | 2 +- packages/google_maps_flutter/google_maps_flutter/pubspec.yaml | 2 +- 7 files changed, 12 insertions(+), 11 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index 3ecea7c465e7..61b151427520 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.1.4 +* Updates Android Google maps sdk version to `18.0.2`. * Adds OS version support information to README. ## 2.1.3 diff --git a/packages/google_maps_flutter/google_maps_flutter/android/build.gradle b/packages/google_maps_flutter/google_maps_flutter/android/build.gradle index bf283bea9ef9..356e3c5e22f7 100644 --- a/packages/google_maps_flutter/google_maps_flutter/android/build.gradle +++ b/packages/google_maps_flutter/google_maps_flutter/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:3.5.4' } } @@ -35,7 +35,7 @@ android { dependencies { implementation "androidx.annotation:annotation:1.1.0" - implementation 'com.google.android.gms:play-services-maps:17.0.0' + implementation 'com.google.android.gms:play-services-maps:18.0.2' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test:rules:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/CircleControllerTest.java b/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/CircleControllerTest.java index 72a8cab626b5..064c8c3591eb 100644 --- a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/CircleControllerTest.java +++ b/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/CircleControllerTest.java @@ -7,7 +7,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; -import com.google.android.gms.internal.maps.zzh; +import com.google.android.gms.internal.maps.zzl; import com.google.android.gms.maps.model.Circle; import org.junit.Test; import org.mockito.Mockito; @@ -16,7 +16,7 @@ public class CircleControllerTest { @Test public void controller_SetsStrokeDensity() { - final zzh z = mock(zzh.class); + final zzl z = mock(zzl.class); final Circle circle = spy(new Circle(z)); final float density = 5; diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolygonControllerTest.java b/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolygonControllerTest.java index 29234b6adb42..5c73a3f3e449 100644 --- a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolygonControllerTest.java +++ b/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolygonControllerTest.java @@ -7,7 +7,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; -import com.google.android.gms.internal.maps.zzw; +import com.google.android.gms.internal.maps.zzaa; import com.google.android.gms.maps.model.Polygon; import org.junit.Test; import org.mockito.Mockito; @@ -16,7 +16,7 @@ public class PolygonControllerTest { @Test public void controller_SetsStrokeDensity() { - final zzw z = mock(zzw.class); + final zzaa z = mock(zzaa.class); final Polygon polygon = spy(new Polygon(z)); final float density = 5; diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolylineControllerTest.java b/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolylineControllerTest.java index bb7653aa2293..db570174e215 100644 --- a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolylineControllerTest.java +++ b/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolylineControllerTest.java @@ -7,7 +7,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; -import com.google.android.gms.internal.maps.zzz; +import com.google.android.gms.internal.maps.zzad; import com.google.android.gms.maps.model.Polyline; import org.junit.Test; import org.mockito.Mockito; @@ -16,7 +16,7 @@ public class PolylineControllerTest { @Test public void controller_SetsStrokeDensity() { - final zzz z = mock(zzz.class); + final zzad z = mock(zzad.class); final Polyline polyline = spy(new Polyline(z)); final float density = 5; diff --git a/packages/google_maps_flutter/google_maps_flutter/example/android/build.gradle b/packages/google_maps_flutter/google_maps_flutter/example/android/build.gradle index 456d020f6e2c..4d8d45d13a0b 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/android/build.gradle +++ b/packages/google_maps_flutter/google_maps_flutter/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:3.5.4' } } diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index 741fe6910037..c10f9c679a17 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter description: A Flutter plugin for integrating Google Maps in iOS and Android applications. repository: https://github.com/flutter/plugins/tree/main/packages/google_maps_flutter/google_maps_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 2.1.3 +version: 2.1.4 environment: sdk: ">=2.14.0 <3.0.0" From d8f6ddbaad39e1001fe7fb3d7acd811ee66ea721 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Wed, 27 Apr 2022 17:09:11 -0700 Subject: [PATCH 452/600] ci: remove uwp test (#5427) --- .ci.yaml | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/.ci.yaml b/.ci.yaml index c00fcf0b7865..14772f475af6 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -85,19 +85,6 @@ targets: {"dependency": "vs_build"} ] - - name: Windows uwp-platform_tests master - recipe: plugins/plugins - timeout: 30 - properties: - add_recipes_cq: "true" - target_file: uwp_build_and_platform_tests.yaml - channel: master - version_file: flutter_master.version - dependencies: > - [ - {"dependency": "vs_build"} - ] - - name: Windows plugin_tools_tests recipe: plugins/plugins timeout: 30 From ac3167f83402c056e97dea7599a6f3949d3aa8cb Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 28 Apr 2022 12:34:11 -0400 Subject: [PATCH 453/600] [flutter_plugin_tools] Adds update-excerpts command (#5339) --- .cirrus.yml | 4 + .gitmodules | 3 + packages/camera/camera/CHANGELOG.md | 4 + packages/camera/camera/README.md | 41 +-- .../camera/camera/example/build.excerpt.yaml | 15 + packages/camera/camera/example/lib/main.dart | 2 + .../example/lib/readme_full_example.dart | 56 ++++ packages/camera/camera/example/pubspec.yaml | 1 + packages/camera/camera/pubspec.yaml | 2 +- script/tool/CHANGELOG.md | 5 +- script/tool/README.md | 11 + .../tool/lib/src/license_check_command.dart | 33 +- script/tool/lib/src/main.dart | 2 + .../tool/lib/src/update_excerpts_command.dart | 225 ++++++++++++++ script/tool/pubspec.yaml | 3 +- script/tool/test/format_command_test.dart | 2 +- .../tool/test/license_check_command_test.dart | 45 ++- .../test/update_excerpts_command_test.dart | 284 ++++++++++++++++++ site-shared | 1 + 19 files changed, 713 insertions(+), 26 deletions(-) create mode 100644 .gitmodules create mode 100644 packages/camera/camera/example/build.excerpt.yaml create mode 100644 packages/camera/camera/example/lib/readme_full_example.dart create mode 100644 script/tool/lib/src/update_excerpts_command.dart create mode 100644 script/tool/test/update_excerpts_command_test.dart create mode 160000 site-shared diff --git a/.cirrus.yml b/.cirrus.yml index 66e8336301d4..c256ab19426e 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -155,6 +155,10 @@ task: analyze_script: - ./script/tool_runner.sh analyze --skip-if-not-supporting-flutter-version="$CHANNEL" --custom-analysis=script/configs/custom_analysis.yaml - echo "If this test fails, the minumum Flutter version should be updated" + - name: readme_excerpts + env: + CIRRUS_CLONE_SUBMODULES: true + script: ./script/tool_runner.sh update-excerpts --fail-on-change ### Web tasks ### - name: web-build_all_plugins env: diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000000..1d3bb5da1bfb --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "site-shared"] + path = site-shared + url = https://github.com/dart-lang/site-shared diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index b86443249dad..a9986697b048 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.4+21 + +* Fixes README code samples. + ## 0.9.4+20 * Fixes an issue with the orientation of videos recorded in landscape on Android. diff --git a/packages/camera/camera/README.md b/packages/camera/camera/README.md index a313c3a1655f..a1c60a08950d 100644 --- a/packages/camera/camera/README.md +++ b/packages/camera/camera/README.md @@ -1,5 +1,7 @@ # Camera Plugin + + [![pub package](https://img.shields.io/pub/v/camera.svg)](https://pub.dev/packages/camera) A Flutter plugin for iOS, Android and Web allowing access to the device cameras. @@ -59,33 +61,35 @@ For web integration details, see the As of version [0.5.0](https://github.com/flutter/plugins/blob/master/packages/camera/CHANGELOG.md#050) of the camera plugin, lifecycle changes are no longer handled by the plugin. This means developers are now responsible to control camera resources when the lifecycle state is updated. Failure to do so might lead to unexpected behavior (for example as described in issue [#39109](https://github.com/flutter/flutter/issues/39109)). Handling lifecycle changes can be done by overriding the `didChangeAppLifecycleState` method like so: + ```dart - @override - void didChangeAppLifecycleState(AppLifecycleState state) { - // App state changed before we got the chance to initialize. - if (controller == null || !controller.value.isInitialized) { - return; - } - if (state == AppLifecycleState.inactive) { - controller?.dispose(); - } else if (state == AppLifecycleState.resumed) { - if (controller != null) { - onNewCameraSelected(controller.description); - } - } +@override +void didChangeAppLifecycleState(AppLifecycleState state) { + final CameraController? cameraController = controller; + + // App state changed before we got the chance to initialize. + if (cameraController == null || !cameraController.value.isInitialized) { + return; } + + if (state == AppLifecycleState.inactive) { + cameraController.dispose(); + } else if (state == AppLifecycleState.resumed) { + onNewCameraSelected(cameraController.description); + } +} ``` ### Example Here is a small example flutter app displaying a full screen camera preview. + ```dart -import 'dart:async'; -import 'package:flutter/material.dart'; import 'package:camera/camera.dart'; +import 'package:flutter/material.dart'; -List cameras; +late List cameras; Future main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -100,7 +104,7 @@ class CameraApp extends StatefulWidget { } class _CameraAppState extends State { - CameraController controller; + late CameraController controller; @override void initState() { @@ -116,7 +120,7 @@ class _CameraAppState extends State { @override void dispose() { - controller?.dispose(); + controller.dispose(); super.dispose(); } @@ -130,7 +134,6 @@ class _CameraAppState extends State { ); } } - ``` For a more elaborate usage example see [here](https://github.com/flutter/plugins/tree/main/packages/camera/camera/example). diff --git a/packages/camera/camera/example/build.excerpt.yaml b/packages/camera/camera/example/build.excerpt.yaml new file mode 100644 index 000000000000..e317efa11cb3 --- /dev/null +++ b/packages/camera/camera/example/build.excerpt.yaml @@ -0,0 +1,15 @@ +targets: + $default: + sources: + include: + - lib/** + # Some default includes that aren't really used here but will prevent + # false-negative warnings: + - $package$ + - lib/$lib$ + exclude: + - '**/.*/**' + - '**/build/**' + builders: + code_excerpter|code_excerpter: + enabled: true diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index f9f1378176a3..aabbe249313d 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -106,6 +106,7 @@ class _CameraExampleHomeState extends State super.dispose(); } + // #docregion AppLifecycle @override void didChangeAppLifecycleState(AppLifecycleState state) { final CameraController? cameraController = controller; @@ -121,6 +122,7 @@ class _CameraExampleHomeState extends State onNewCameraSelected(cameraController.description); } } + // #enddocregion AppLifecycle @override Widget build(BuildContext context) { diff --git a/packages/camera/camera/example/lib/readme_full_example.dart b/packages/camera/camera/example/lib/readme_full_example.dart new file mode 100644 index 000000000000..b25e637a0c95 --- /dev/null +++ b/packages/camera/camera/example/lib/readme_full_example.dart @@ -0,0 +1,56 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +// #docregion FullAppExample +import 'package:camera/camera.dart'; +import 'package:flutter/material.dart'; + +late List cameras; + +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + + cameras = await availableCameras(); + runApp(CameraApp()); +} + +class CameraApp extends StatefulWidget { + @override + _CameraAppState createState() => _CameraAppState(); +} + +class _CameraAppState extends State { + late CameraController controller; + + @override + void initState() { + super.initState(); + controller = CameraController(cameras[0], ResolutionPreset.max); + controller.initialize().then((_) { + if (!mounted) { + return; + } + setState(() {}); + }); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (!controller.value.isInitialized) { + return Container(); + } + return MaterialApp( + home: CameraPreview(controller), + ); + } +} +// #enddocregion FullAppExample diff --git a/packages/camera/camera/example/pubspec.yaml b/packages/camera/camera/example/pubspec.yaml index 1700074f1f88..af4d078ff836 100644 --- a/packages/camera/camera/example/pubspec.yaml +++ b/packages/camera/camera/example/pubspec.yaml @@ -20,6 +20,7 @@ dependencies: video_player: ^2.1.4 dev_dependencies: + build_runner: ^2.1.10 flutter_driver: sdk: flutter flutter_test: diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index feb83f95cd8d..f62777044617 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+20 +version: 0.9.4+21 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index bfc0cafa88c3..82a31154e132 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,5 +1,8 @@ -## NEXT +## 0.8.3 +- Adds a new `update-excerpts` command to maintain README files using the + `code-excerpter` package from flutter/site-shared. +- `license-check` now ignores submodules. - Allows `make-deps-path-based` to skip packages it has alredy rewritten, so that running multiple times won't fail after the first time. diff --git a/script/tool/README.md b/script/tool/README.md index 05be917014f2..d52ee08fdc34 100644 --- a/script/tool/README.md +++ b/script/tool/README.md @@ -107,6 +107,17 @@ dart run ./script/tool/bin/flutter_plugin_tools.dart native-test --ios --android dart run ./script/tool/bin/flutter_plugin_tools.dart native-test --macos --packages plugin_name ``` +### Update README.md from Example Sources + +`update-excerpts` requires sources that are in a submodule. If you didn't clone +with submodules, you will need to `git submodule update --init --recursive` +before running this command. + +```sh +cd +dart run ./script/tool/bin/flutter_plugin_tools.dart update-excerpts --packages plugin_name +``` + ### Publish a Release **Releases are automated for `flutter/plugins` and `flutter/packages`.** diff --git a/script/tool/lib/src/license_check_command.dart b/script/tool/lib/src/license_check_command.dart index d2c129ff7b48..87e4c8b14861 100644 --- a/script/tool/lib/src/license_check_command.dart +++ b/script/tool/lib/src/license_check_command.dart @@ -3,7 +3,9 @@ // found in the LICENSE file. import 'package:file/file.dart'; +import 'package:git/git.dart'; import 'package:path/path.dart' as p; +import 'package:platform/platform.dart'; import 'common/core.dart'; import 'common/plugin_command.dart'; @@ -105,7 +107,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /// Validates that code files have copyright and license blocks. class LicenseCheckCommand extends PluginCommand { /// Creates a new license check command for [packagesDir]. - LicenseCheckCommand(Directory packagesDir) : super(packagesDir); + LicenseCheckCommand(Directory packagesDir, + {Platform platform = const LocalPlatform(), GitDir? gitDir}) + : super(packagesDir, platform: platform, gitDir: gitDir); @override final String name = 'license-check'; @@ -116,7 +120,14 @@ class LicenseCheckCommand extends PluginCommand { @override Future run() async { - final Iterable allFiles = await _getAllFiles(); + // Create a set of absolute paths to submodule directories, with trailing + // separator, to do prefix matching with to test directory inclusion. + final Iterable submodulePaths = (await _getSubmoduleDirectories()) + .map( + (Directory dir) => '${dir.absolute.path}${platform.pathSeparator}'); + + final Iterable allFiles = (await _getAllFiles()).where( + (File file) => !submodulePaths.any(file.absolute.path.startsWith)); final Iterable codeFiles = allFiles.where((File file) => _codeFileExtensions.contains(p.extension(file.path)) && @@ -275,6 +286,24 @@ class LicenseCheckCommand extends PluginCommand { .where((FileSystemEntity entity) => entity is File) .map((FileSystemEntity file) => file as File) .toList(); + + // Returns the directories containing mapped submodules, if any. + Future> _getSubmoduleDirectories() async { + final List submodulePaths = []; + final Directory repoRoot = + packagesDir.fileSystem.directory((await gitDir).path); + final File submoduleSpec = repoRoot.childFile('.gitmodules'); + if (submoduleSpec.existsSync()) { + final RegExp pathLine = RegExp(r'path\s*=\s*(.*)'); + for (final String line in submoduleSpec.readAsLinesSync()) { + final RegExpMatch? match = pathLine.firstMatch(line); + if (match != null) { + submodulePaths.add(repoRoot.childDirectory(match.group(1)!.trim())); + } + } + } + return submodulePaths; + } } enum _LicenseFailureType { incorrectFirstParty, unknownThirdParty } diff --git a/script/tool/lib/src/main.dart b/script/tool/lib/src/main.dart index aa1cf300bd2b..9c572ee270e0 100644 --- a/script/tool/lib/src/main.dart +++ b/script/tool/lib/src/main.dart @@ -28,6 +28,7 @@ import 'publish_plugin_command.dart'; import 'pubspec_check_command.dart'; import 'readme_check_command.dart'; import 'test_command.dart'; +import 'update_excerpts_command.dart'; import 'version_check_command.dart'; import 'xcode_analyze_command.dart'; @@ -68,6 +69,7 @@ void main(List args) { ..addCommand(PubspecCheckCommand(packagesDir)) ..addCommand(ReadmeCheckCommand(packagesDir)) ..addCommand(TestCommand(packagesDir)) + ..addCommand(UpdateExcerptsCommand(packagesDir)) ..addCommand(VersionCheckCommand(packagesDir)) ..addCommand(XcodeAnalyzeCommand(packagesDir)); diff --git a/script/tool/lib/src/update_excerpts_command.dart b/script/tool/lib/src/update_excerpts_command.dart new file mode 100644 index 000000000000..320a3c596323 --- /dev/null +++ b/script/tool/lib/src/update_excerpts_command.dart @@ -0,0 +1,225 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io' as io; + +import 'package:file/file.dart'; +import 'package:flutter_plugin_tools/src/common/core.dart'; +import 'package:git/git.dart'; +import 'package:platform/platform.dart'; +import 'package:yaml/yaml.dart'; +import 'package:yaml_edit/yaml_edit.dart'; + +import 'common/package_looping_command.dart'; +import 'common/process_runner.dart'; +import 'common/repository_package.dart'; + +/// A command to update README code excerpts from code files. +class UpdateExcerptsCommand extends PackageLoopingCommand { + /// Creates a excerpt updater command instance. + UpdateExcerptsCommand( + Directory packagesDir, { + ProcessRunner processRunner = const ProcessRunner(), + Platform platform = const LocalPlatform(), + GitDir? gitDir, + }) : super( + packagesDir, + processRunner: processRunner, + platform: platform, + gitDir: gitDir, + ) { + argParser.addFlag(_failOnChangeFlag, hide: true); + } + + static const String _failOnChangeFlag = 'fail-on-change'; + + static const String _buildRunnerConfigName = 'excerpt'; + // The name of the build_runner configuration file that will be in an example + // directory if the package is set up to use `code-excerpt`. + static const String _buildRunnerConfigFile = + 'build.$_buildRunnerConfigName.yaml'; + + // The relative directory path to put the extracted excerpt yaml files. + static const String _excerptOutputDir = 'excerpts'; + + // The filename to store the pre-modification copy of the pubspec. + static const String _originalPubspecFilename = + 'pubspec.plugin_tools_original.yaml'; + + @override + final String name = 'update-excerpts'; + + @override + final String description = 'Updates code excerpts in README.md files, based ' + 'on code from code files, via code-excerpt'; + + @override + Future runForPackage(RepositoryPackage package) async { + final Iterable configuredExamples = package + .getExamples() + .where((RepositoryPackage example) => + example.directory.childFile(_buildRunnerConfigFile).existsSync()); + + if (configuredExamples.isEmpty) { + return PackageResult.skip( + 'No $_buildRunnerConfigFile found in example(s).'); + } + + final Directory repoRoot = + packagesDir.fileSystem.directory((await gitDir).path); + + for (final RepositoryPackage example in configuredExamples) { + _addSubmoduleDependencies(example, repoRoot: repoRoot); + + try { + // Ensure that dependencies are available. + final int pubGetExitCode = await processRunner.runAndStream( + 'dart', ['pub', 'get'], + workingDir: example.directory); + if (pubGetExitCode != 0) { + return PackageResult.fail( + ['Unable to get script dependencies']); + } + + // Update the excerpts. + if (!await _extractSnippets(example)) { + return PackageResult.fail(['Unable to extract excerpts']); + } + if (!await _injectSnippets(example, targetPackage: package)) { + return PackageResult.fail(['Unable to inject excerpts']); + } + } finally { + // Clean up the pubspec changes and extracted excerpts directory. + _undoPubspecChanges(example); + final Directory excerptDirectory = + example.directory.childDirectory(_excerptOutputDir); + if (excerptDirectory.existsSync()) { + excerptDirectory.deleteSync(recursive: true); + } + } + } + + if (getBoolArg(_failOnChangeFlag)) { + final String? stateError = await _validateRepositoryState(); + if (stateError != null) { + printError('README.md is out of sync with its source excerpts.\n\n' + 'If you edited code in README.md directly, you should instead edit ' + 'the example source files. If you edited source files, run the ' + 'repository tooling\'s "$name" command on this package, and update ' + 'your PR with the resulting changes.'); + return PackageResult.fail([stateError]); + } + } + + return PackageResult.success(); + } + + /// Runs the extraction step to create the excerpt files for the given + /// example, returning true on success. + Future _extractSnippets(RepositoryPackage example) async { + final int exitCode = await processRunner.runAndStream( + 'dart', + [ + 'run', + 'build_runner', + 'build', + '--config', + _buildRunnerConfigName, + '--output', + _excerptOutputDir, + '--delete-conflicting-outputs', + ], + workingDir: example.directory); + return exitCode == 0; + } + + /// Runs the injection step to update [targetPackage]'s README with the latest + /// excerpts from [example], returning true on success. + Future _injectSnippets( + RepositoryPackage example, { + required RepositoryPackage targetPackage, + }) async { + final String relativeReadmePath = + getRelativePosixPath(targetPackage.readmeFile, from: example.directory); + final int exitCode = await processRunner.runAndStream( + 'dart', + [ + 'run', + 'code_excerpt_updater', + '--write-in-place', + '--yaml', + '--no-escape-ng-interpolation', + relativeReadmePath, + ], + workingDir: example.directory); + return exitCode == 0; + } + + /// Adds `code_excerpter` and `code_excerpt_updater` to [package]'s + /// `dev_dependencies` using path-based references to the submodule copies. + /// + /// This is done on the fly rather than being checked in so that: + /// - Just building examples don't require everyone to check out submodules. + /// - Examples can be analyzed/built even on versions of Flutter that these + /// submodules do not support. + void _addSubmoduleDependencies(RepositoryPackage package, + {required Directory repoRoot}) { + final String pubspecContents = package.pubspecFile.readAsStringSync(); + // Save aside a copy of the current pubspec state. This allows restoration + // to the previous state regardless of its git status at the time the script + // ran. + package.directory + .childFile(_originalPubspecFilename) + .writeAsStringSync(pubspecContents); + + // Update the actual pubspec. + final YamlEditor editablePubspec = YamlEditor(pubspecContents); + const String devDependenciesKey = 'dev_dependencies'; + final YamlNode root = editablePubspec.parseAt([]); + // Ensure that there's a `dev_dependencies` entry to update. + if ((root as YamlMap)[devDependenciesKey] == null) { + editablePubspec.update(['dev_dependencies'], YamlMap()); + } + final Set submoduleDependencies = { + 'code_excerpter', + 'code_excerpt_updater', + }; + final String relativeRootPath = + getRelativePosixPath(repoRoot, from: package.directory); + for (final String dependency in submoduleDependencies) { + editablePubspec.update([ + devDependenciesKey, + dependency + ], { + 'path': '$relativeRootPath/site-shared/packages/$dependency' + }); + } + package.pubspecFile.writeAsStringSync(editablePubspec.toString()); + } + + /// Restores the version of the pubspec that was present before running + /// [_addSubmoduleDependencies]. + void _undoPubspecChanges(RepositoryPackage package) { + package.directory + .childFile(_originalPubspecFilename) + .renameSync(package.pubspecFile.path); + } + + /// Checks the git state, returning an error string unless nothing has + /// changed. + Future _validateRepositoryState() async { + final io.ProcessResult modifiedFiles = await processRunner.run( + 'git', + ['ls-files', '--modified'], + workingDir: packagesDir, + logOnError: true, + ); + if (modifiedFiles.exitCode != 0) { + return 'Unable to determine local file state'; + } + + final String stdout = modifiedFiles.stdout as String; + return stdout.trim().isEmpty ? null : 'Snippets are out of sync'; + } +} diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index 14b22a16e3fb..9f9910f934f7 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.8.2+1 +version: 0.8.3 dependencies: args: ^2.1.0 @@ -21,6 +21,7 @@ dependencies: test: ^1.17.3 uuid: ^3.0.4 yaml: ^3.1.0 + yaml_edit: ^2.0.2 dev_dependencies: build_runner: ^2.0.3 diff --git a/script/tool/test/format_command_test.dart b/script/tool/test/format_command_test.dart index 2890c528e4c1..6c10a7dc3209 100644 --- a/script/tool/test/format_command_test.dart +++ b/script/tool/test/format_command_test.dart @@ -448,7 +448,7 @@ void main() { ])); }); - test('fails if files are changed with --file-on-change', () async { + test('fails if files are changed with --fail-on-change', () async { const List files = [ 'linux/foo_plugin.cc', 'macos/Classes/Foo.h', diff --git a/script/tool/test/license_check_command_test.dart b/script/tool/test/license_check_command_test.dart index e97274afd09e..efaf969c83fb 100644 --- a/script/tool/test/license_check_command_test.dart +++ b/script/tool/test/license_check_command_test.dart @@ -7,24 +7,35 @@ import 'package:file/file.dart'; import 'package:file/memory.dart'; import 'package:flutter_plugin_tools/src/common/core.dart'; import 'package:flutter_plugin_tools/src/license_check_command.dart'; +import 'package:mockito/mockito.dart'; +import 'package:platform/platform.dart'; import 'package:test/test.dart'; +import 'common/plugin_command_test.mocks.dart'; +import 'mocks.dart'; import 'util.dart'; void main() { - group('$LicenseCheckCommand', () { + group('LicenseCheckCommand', () { late CommandRunner runner; late FileSystem fileSystem; + late Platform platform; late Directory root; setUp(() { fileSystem = MemoryFileSystem(); + platform = MockPlatformWithSeparator(); final Directory packagesDir = fileSystem.currentDirectory.childDirectory('packages'); root = packagesDir.parent; + final MockGitDir gitDir = MockGitDir(); + when(gitDir.path).thenReturn(packagesDir.parent.path); + final LicenseCheckCommand command = LicenseCheckCommand( packagesDir, + platform: platform, + gitDir: gitDir, ); runner = CommandRunner('license_test', 'Test for $LicenseCheckCommand'); @@ -123,6 +134,33 @@ void main() { } }); + test('ignores submodules', () async { + const String submoduleName = 'a_submodule'; + + final File submoduleSpec = root.childFile('.gitmodules'); + submoduleSpec.writeAsStringSync(''' +[submodule "$submoduleName"] + path = $submoduleName + url = https://github.com/foo/$submoduleName +'''); + + const List submoduleFiles = [ + '$submoduleName/foo.dart', + '$submoduleName/a/b/bar.dart', + '$submoduleName/LICENSE', + ]; + for (final String filePath in submoduleFiles) { + root.childFile(filePath).createSync(recursive: true); + } + + final List output = + await runCapturingPrint(runner, ['license-check']); + + for (final String filePath in submoduleFiles) { + expect(output, isNot(contains('Checking $filePath'))); + } + }); + test('passes if all checked files have license blocks', () async { final File checked = root.childFile('checked.cc'); checked.createSync(); @@ -509,6 +547,11 @@ void main() { }); } +class MockPlatformWithSeparator extends MockPlatform { + @override + String get pathSeparator => isWindows ? r'\' : '/'; +} + const String _correctLicenseFileText = ''' Copyright 2013 The Flutter Authors. All rights reserved. diff --git a/script/tool/test/update_excerpts_command_test.dart b/script/tool/test/update_excerpts_command_test.dart new file mode 100644 index 000000000000..30189cf23a00 --- /dev/null +++ b/script/tool/test/update_excerpts_command_test.dart @@ -0,0 +1,284 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io' as io; + +import 'package:args/command_runner.dart'; +import 'package:file/file.dart'; +import 'package:file/memory.dart'; +import 'package:flutter_plugin_tools/src/common/core.dart'; +import 'package:flutter_plugin_tools/src/common/repository_package.dart'; +import 'package:flutter_plugin_tools/src/update_excerpts_command.dart'; +import 'package:mockito/mockito.dart'; +import 'package:test/test.dart'; + +import 'common/plugin_command_test.mocks.dart'; +import 'mocks.dart'; +import 'util.dart'; + +void main() { + late FileSystem fileSystem; + late Directory packagesDir; + late RecordingProcessRunner processRunner; + late CommandRunner runner; + + setUp(() { + fileSystem = MemoryFileSystem(); + packagesDir = createPackagesDirectory(fileSystem: fileSystem); + final MockGitDir gitDir = MockGitDir(); + when(gitDir.path).thenReturn(packagesDir.parent.path); + processRunner = RecordingProcessRunner(); + final UpdateExcerptsCommand command = UpdateExcerptsCommand( + packagesDir, + processRunner: processRunner, + platform: MockPlatform(), + gitDir: gitDir, + ); + + runner = CommandRunner( + 'update_excerpts_command', 'Test for update_excerpts_command'); + runner.addCommand(command); + }); + + test('runs pub get before running scripts', () async { + final Directory package = createFakePlugin('a_package', packagesDir, + extraFiles: ['example/build.excerpt.yaml']); + final Directory example = package.childDirectory('example'); + + await runCapturingPrint(runner, ['update-excerpts']); + + expect( + processRunner.recordedCalls, + containsAll([ + ProcessCall('dart', const ['pub', 'get'], example.path), + ProcessCall( + 'dart', + const [ + 'run', + 'build_runner', + 'build', + '--config', + 'excerpt', + '--output', + 'excerpts', + '--delete-conflicting-outputs', + ], + example.path), + ])); + }); + + test('runs when config is present', () async { + final Directory package = createFakePlugin('a_package', packagesDir, + extraFiles: ['example/build.excerpt.yaml']); + final Directory example = package.childDirectory('example'); + + final List output = + await runCapturingPrint(runner, ['update-excerpts']); + + expect( + processRunner.recordedCalls, + containsAll([ + ProcessCall( + 'dart', + const [ + 'run', + 'build_runner', + 'build', + '--config', + 'excerpt', + '--output', + 'excerpts', + '--delete-conflicting-outputs', + ], + example.path), + ProcessCall( + 'dart', + const [ + 'run', + 'code_excerpt_updater', + '--write-in-place', + '--yaml', + '--no-escape-ng-interpolation', + '../README.md', + ], + example.path), + ])); + + expect( + output, + containsAllInOrder([ + contains('Ran for 1 package(s)'), + ])); + }); + + test('skips when no config is present', () async { + createFakePlugin('a_package', packagesDir); + + final List output = + await runCapturingPrint(runner, ['update-excerpts']); + + expect(processRunner.recordedCalls, isEmpty); + + expect( + output, + containsAllInOrder([ + contains('Skipped 1 package(s)'), + ])); + }); + + test('restores pubspec even if running the script fails', () async { + final Directory package = createFakePlugin('a_package', packagesDir, + extraFiles: ['example/build.excerpt.yaml']); + + processRunner.mockProcessesForExecutable['dart'] = [ + MockProcess(exitCode: 1), // dart pub get + ]; + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['update-excerpts'], errorHandler: (Error e) { + commandError = e; + }); + + // Check that it's definitely a failure in a step between making the changes + // and restoring the original. + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The following packages had errors:'), + contains('a_package:\n' + ' Unable to get script dependencies') + ])); + + final String examplePubspecContent = RepositoryPackage(package) + .getExamples() + .first + .pubspecFile + .readAsStringSync(); + expect(examplePubspecContent, isNot(contains('code_excerpter'))); + expect(examplePubspecContent, isNot(contains('code_excerpt_updater'))); + }); + + test('fails if pub get fails', () async { + createFakePlugin('a_package', packagesDir, + extraFiles: ['example/build.excerpt.yaml']); + + processRunner.mockProcessesForExecutable['dart'] = [ + MockProcess(exitCode: 1), // dart pub get + ]; + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['update-excerpts'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The following packages had errors:'), + contains('a_package:\n' + ' Unable to get script dependencies') + ])); + }); + + test('fails if extraction fails', () async { + createFakePlugin('a_package', packagesDir, + extraFiles: ['example/build.excerpt.yaml']); + + processRunner.mockProcessesForExecutable['dart'] = [ + MockProcess(exitCode: 0), // dart pub get + MockProcess(exitCode: 1), // dart run build_runner ... + ]; + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['update-excerpts'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The following packages had errors:'), + contains('a_package:\n' + ' Unable to extract excerpts') + ])); + }); + + test('fails if injection fails', () async { + createFakePlugin('a_package', packagesDir, + extraFiles: ['example/build.excerpt.yaml']); + + processRunner.mockProcessesForExecutable['dart'] = [ + MockProcess(exitCode: 0), // dart pub get + MockProcess(exitCode: 0), // dart run build_runner ... + MockProcess(exitCode: 1), // dart run code_excerpt_updater ... + ]; + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['update-excerpts'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The following packages had errors:'), + contains('a_package:\n' + ' Unable to inject excerpts') + ])); + }); + + test('fails if files are changed with --fail-on-change', () async { + createFakePlugin('a_plugin', packagesDir, + extraFiles: ['example/build.excerpt.yaml']); + + const String changedFilePath = 'packages/a_plugin/linux/foo_plugin.cc'; + processRunner.mockProcessesForExecutable['git'] = [ + MockProcess(stdout: changedFilePath), + ]; + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['update-excerpts', '--fail-on-change'], + errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('README.md is out of sync with its source excerpts'), + ])); + }); + + test('fails if git ls-files fails', () async { + createFakePlugin('a_plugin', packagesDir, + extraFiles: ['example/build.excerpt.yaml']); + + processRunner.mockProcessesForExecutable['git'] = [ + MockProcess(exitCode: 1) + ]; + Error? commandError; + final List output = await runCapturingPrint( + runner, ['update-excerpts', '--fail-on-change'], + errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('Unable to determine local file state'), + ])); + }); +} diff --git a/site-shared b/site-shared new file mode 160000 index 000000000000..142de133477b --- /dev/null +++ b/site-shared @@ -0,0 +1 @@ +Subproject commit 142de133477bdede1746f992e656c4b43c4c7442 From c09ef582e3455fa0988296eda33d4cb80b761cb7 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 28 Apr 2022 13:19:12 -0400 Subject: [PATCH 454/600] [flutter_plugin_tools] Remove UWP (#5432) --- .ci/scripts/build_examples_uwp.sh | 7 -- .ci/targets/uwp_build_and_platform_tests.yaml | 5 -- script/tool/CHANGELOG.md | 1 + .../tool/lib/src/build_examples_command.dart | 48 ++-------- script/tool/lib/src/common/core.dart | 19 ---- script/tool/lib/src/common/plugin_utils.dart | 21 ----- .../tool/lib/src/drive_examples_command.dart | 24 +---- .../test/build_examples_command_test.dart | 90 +------------------ .../tool/test/common/plugin_utils_test.dart | 79 ---------------- .../test/drive_examples_command_test.dart | 34 ------- script/tool/test/util.dart | 16 ---- 11 files changed, 12 insertions(+), 332 deletions(-) delete mode 100644 .ci/scripts/build_examples_uwp.sh delete mode 100644 .ci/targets/uwp_build_and_platform_tests.yaml diff --git a/.ci/scripts/build_examples_uwp.sh b/.ci/scripts/build_examples_uwp.sh deleted file mode 100644 index 04b8256891bd..000000000000 --- a/.ci/scripts/build_examples_uwp.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -# 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. - -dart ./script/tool/bin/flutter_plugin_tools.dart build-examples --winuwp \ - --packages-for-branch --log-timing diff --git a/.ci/targets/uwp_build_and_platform_tests.yaml b/.ci/targets/uwp_build_and_platform_tests.yaml deleted file mode 100644 index a7f070776ff1..000000000000 --- a/.ci/targets/uwp_build_and_platform_tests.yaml +++ /dev/null @@ -1,5 +0,0 @@ -tasks: - - name: prepare tool - script: .ci/scripts/prepare_tool.sh - - name: build examples (UWP) - script: .ci/scripts/build_examples_uwp.sh diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 82a31154e132..7587ff33f027 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -5,6 +5,7 @@ - `license-check` now ignores submodules. - Allows `make-deps-path-based` to skip packages it has alredy rewritten, so that running multiple times won't fail after the first time. +- Removes UWP support, since Flutter has dropped support for UWP. ## 0.8.2+1 diff --git a/script/tool/lib/src/build_examples_command.dart b/script/tool/lib/src/build_examples_command.dart index b88cfe309258..1aade3575559 100644 --- a/script/tool/lib/src/build_examples_command.dart +++ b/script/tool/lib/src/build_examples_command.dart @@ -37,8 +37,7 @@ const String _flutterBuildTypeIOS = 'ios'; const String _flutterBuildTypeLinux = 'linux'; const String _flutterBuildTypeMacOS = 'macos'; const String _flutterBuildTypeWeb = 'web'; -const String _flutterBuildTypeWin32 = 'windows'; -const String _flutterBuildTypeWinUwp = 'winuwp'; +const String _flutterBuildTypeWindows = 'windows'; /// A command to build the example applications for packages. class BuildExamplesCommand extends PackageLoopingCommand { @@ -52,7 +51,6 @@ class BuildExamplesCommand extends PackageLoopingCommand { argParser.addFlag(platformMacOS); argParser.addFlag(platformWeb); argParser.addFlag(platformWindows); - argParser.addFlag(platformWinUwp); argParser.addFlag(platformIOS); argParser.addFlag(_platformFlagApk); argParser.addOption( @@ -93,16 +91,9 @@ class BuildExamplesCommand extends PackageLoopingCommand { flutterBuildType: _flutterBuildTypeWeb, ), platformWindows: const _PlatformDetails( - 'Win32', + 'Windows', pluginPlatform: platformWindows, - pluginPlatformVariant: platformVariantWin32, - flutterBuildType: _flutterBuildTypeWin32, - ), - platformWinUwp: const _PlatformDetails( - 'UWP', - pluginPlatform: platformWindows, - pluginPlatformVariant: platformVariantWinUwp, - flutterBuildType: _flutterBuildTypeWinUwp, + flutterBuildType: _flutterBuildTypeWindows, ), }; @@ -146,9 +137,8 @@ class BuildExamplesCommand extends PackageLoopingCommand { // no package-level platform information for non-plugin packages. final Set<_PlatformDetails> buildPlatforms = isPlugin ? requestedPlatforms - .where((_PlatformDetails platform) => pluginSupportsPlatform( - platform.pluginPlatform, package, - variant: platform.pluginPlatformVariant)) + .where((_PlatformDetails platform) => + pluginSupportsPlatform(platform.pluginPlatform, package)) .toSet() : requestedPlatforms.toSet(); @@ -280,22 +270,6 @@ class BuildExamplesCommand extends PackageLoopingCommand { }) async { final String enableExperiment = getStringArg(kEnableExperiment); - // The UWP template is not yet stable, so the UWP directory - // needs to be created on the fly with 'flutter create .' - Directory? temporaryPlatformDirectory; - if (flutterBuildType == _flutterBuildTypeWinUwp) { - final Directory uwpDirectory = example.directory.childDirectory('winuwp'); - if (!uwpDirectory.existsSync()) { - print('Creating temporary winuwp folder'); - final int exitCode = await processRunner.runAndStream(flutterCommand, - ['create', '--platforms=$platformWinUwp', '.'], - workingDir: example.directory); - if (exitCode == 0) { - temporaryPlatformDirectory = uwpDirectory; - } - } - } - final int exitCode = await processRunner.runAndStream( flutterCommand, [ @@ -308,13 +282,6 @@ class BuildExamplesCommand extends PackageLoopingCommand { ], workingDir: example.directory, ); - - if (temporaryPlatformDirectory != null && - temporaryPlatformDirectory.existsSync()) { - print('Cleaning up ${temporaryPlatformDirectory.path}'); - temporaryPlatformDirectory.deleteSync(recursive: true); - } - return exitCode == 0; } } @@ -324,7 +291,6 @@ class _PlatformDetails { const _PlatformDetails( this.label, { required this.pluginPlatform, - this.pluginPlatformVariant, required this.flutterBuildType, this.extraBuildFlags = const [], }); @@ -335,10 +301,6 @@ class _PlatformDetails { /// The key in a pubspec's platform: entry. final String pluginPlatform; - /// The supportedVariants key under a plugin's [pluginPlatform] entry, if - /// applicable. - final String? pluginPlatformVariant; - /// The `flutter build` build type. final String flutterBuildType; diff --git a/script/tool/lib/src/common/core.dart b/script/tool/lib/src/common/core.dart index 15a0d6f1f3b2..de1cefd7225a 100644 --- a/script/tool/lib/src/common/core.dart +++ b/script/tool/lib/src/common/core.dart @@ -26,27 +26,8 @@ const String platformMacOS = 'macos'; const String platformWeb = 'web'; /// Key for windows platform. -/// -/// Note that this corresponds to the Win32 variant for flutter commands like -/// `build` and `run`, but is a general platform containing all Windows -/// variants for purposes of the `platform` section of a plugin pubspec). const String platformWindows = 'windows'; -/// Key for WinUWP platform. -/// -/// Note that UWP is a platform for the purposes of flutter commands like -/// `build` and `run`, but a variant of the `windows` platform for the purposes -/// of plugin pubspecs). -const String platformWinUwp = 'winuwp'; - -/// Key for Win32 variant of the Windows platform. -const String platformVariantWin32 = 'win32'; - -/// Key for UWP variant of the Windows platform. -/// -/// See the note on [platformWinUwp]. -const String platformVariantWinUwp = 'uwp'; - /// Key for enable experiment. const String kEnableExperiment = 'enable-experiment'; diff --git a/script/tool/lib/src/common/plugin_utils.dart b/script/tool/lib/src/common/plugin_utils.dart index 081ce7f1e815..94f294ebd964 100644 --- a/script/tool/lib/src/common/plugin_utils.dart +++ b/script/tool/lib/src/common/plugin_utils.dart @@ -37,7 +37,6 @@ bool pluginSupportsPlatform( String platform, RepositoryPackage plugin, { PlatformSupport? requiredMode, - String? variant, }) { assert(platform == platformIOS || platform == platformAndroid || @@ -61,26 +60,6 @@ bool pluginSupportsPlatform( } } - // If a variant is specified, check for that variant. - if (variant != null) { - const String variantsKey = 'supportedVariants'; - if (platformEntry.containsKey(variantsKey)) { - if (!(platformEntry['supportedVariants']! as YamlList) - .contains(variant)) { - return false; - } - } else { - // Platforms with variants have a default variant when unspecified for - // backward compatibility. Must match the flutter tool logic. - const Map defaultVariants = { - platformWindows: platformVariantWin32, - }; - if (variant != defaultVariants[platform]) { - return false; - } - } - } - return true; } diff --git a/script/tool/lib/src/drive_examples_command.dart b/script/tool/lib/src/drive_examples_command.dart index d81153a0fefa..0e1efa842eda 100644 --- a/script/tool/lib/src/drive_examples_command.dart +++ b/script/tool/lib/src/drive_examples_command.dart @@ -36,10 +36,7 @@ class DriveExamplesCommand extends PackageLoopingCommand { argParser.addFlag(platformWeb, help: 'Runs the web implementation of the examples'); argParser.addFlag(platformWindows, - help: 'Runs the Windows (Win32) implementation of the examples'); - argParser.addFlag(platformWinUwp, - help: - 'Runs the UWP implementation of the examples [currently a no-op]'); + help: 'Runs the Windows implementation of the examples'); argParser.addOption( kEnableExperiment, defaultsTo: '', @@ -70,7 +67,6 @@ class DriveExamplesCommand extends PackageLoopingCommand { platformMacOS, platformWeb, platformWindows, - platformWinUwp, ]; final int platformCount = platformSwitches .where((String platform) => getBoolArg(platform)) @@ -85,10 +81,6 @@ class DriveExamplesCommand extends PackageLoopingCommand { throw ToolExit(_exitNoPlatformFlags); } - if (getBoolArg(platformWinUwp)) { - logWarning('Driving UWP applications is not yet supported'); - } - String? androidDevice; if (getBoolArg(platformAndroid)) { final List devices = await _getDevicesForPlatform('android'); @@ -126,9 +118,6 @@ class DriveExamplesCommand extends PackageLoopingCommand { ], if (getBoolArg(platformWindows)) platformWindows: ['-d', 'windows'], - // TODO(stuartmorgan): Check these flags once drive supports UWP: - // https://github.com/flutter/flutter/issues/82821 - if (getBoolArg(platformWinUwp)) platformWinUwp: ['-d', 'winuwp'], }; } @@ -146,16 +135,7 @@ class DriveExamplesCommand extends PackageLoopingCommand { for (final MapEntry> entry in _targetDeviceFlags.entries) { final String platform = entry.key; - String? variant; - if (platform == platformWindows) { - variant = platformVariantWin32; - } else if (platform == platformWinUwp) { - variant = platformVariantWinUwp; - // TODO(stuartmorgan): Remove this once drive supports UWP. - // https://github.com/flutter/flutter/issues/82821 - return PackageResult.skip('Drive does not yet support UWP'); - } - if (pluginSupportsPlatform(platform, package, variant: variant)) { + if (pluginSupportsPlatform(platform, package)) { deviceFlags.addAll(entry.value); } else { print('Skipping unsupported platform ${entry.key}...'); diff --git a/script/tool/test/build_examples_command_test.dart b/script/tool/test/build_examples_command_test.dart index 29a879071657..2bdb1bc0c2ba 100644 --- a/script/tool/test/build_examples_command_test.dart +++ b/script/tool/test/build_examples_command_test.dart @@ -313,7 +313,7 @@ void main() { }); test( - 'building for win32 when plugin is not set up for Windows results in no-op', + 'building for Windows when plugin is not set up for Windows results in no-op', () async { mockPlatform.isWindows = true; createFakePlugin('plugin', packagesDir); @@ -325,7 +325,7 @@ void main() { output, containsAllInOrder([ contains('Running for plugin'), - contains('Win32 is not supported by this plugin'), + contains('Windows is not supported by this plugin'), ]), ); @@ -334,7 +334,7 @@ void main() { expect(processRunner.recordedCalls, orderedEquals([])); }); - test('building for win32', () async { + test('building for Windows', () async { mockPlatform.isWindows = true; final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, platformSupport: { @@ -350,7 +350,7 @@ void main() { expect( output, containsAllInOrder([ - '\nBUILDING plugin/example for Win32 (windows)', + '\nBUILDING plugin/example for Windows', ]), ); @@ -364,88 +364,6 @@ void main() { ])); }); - test('building for UWP when plugin does not support UWP is a no-op', - () async { - createFakePlugin('plugin', packagesDir); - - final List output = await runCapturingPrint( - runner, ['build-examples', '--winuwp']); - - expect( - output, - containsAllInOrder([ - contains('Running for plugin'), - contains('UWP is not supported by this plugin'), - ]), - ); - - // Output should be empty since running build-examples --macos with no macos - // implementation is a no-op. - expect(processRunner.recordedCalls, orderedEquals([])); - }); - - test('building for UWP', () async { - final Directory pluginDirectory = - createFakePlugin('plugin', packagesDir, extraFiles: [ - 'example/test', - ], platformSupport: { - platformWindows: const PlatformDetails(PlatformSupport.federated, - variants: [platformVariantWinUwp]), - }); - - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); - - final List output = await runCapturingPrint( - runner, ['build-examples', '--winuwp']); - - expect( - output, - containsAllInOrder([ - contains('BUILDING plugin/example for UWP (winuwp)'), - ]), - ); - - expect( - processRunner.recordedCalls, - containsAll([ - ProcessCall(getFlutterCommand(mockPlatform), - const ['build', 'winuwp'], pluginExampleDirectory.path), - ])); - }); - - test('building for UWP creates a folder if necessary', () async { - final Directory pluginDirectory = - createFakePlugin('plugin', packagesDir, extraFiles: [ - 'example/test', - ], platformSupport: { - platformWindows: const PlatformDetails(PlatformSupport.federated, - variants: [platformVariantWinUwp]), - }); - - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); - - final List output = await runCapturingPrint( - runner, ['build-examples', '--winuwp']); - - expect( - output, - contains('Creating temporary winuwp folder'), - ); - - expect( - processRunner.recordedCalls, - orderedEquals([ - ProcessCall( - getFlutterCommand(mockPlatform), - const ['create', '--platforms=winuwp', '.'], - pluginExampleDirectory.path), - ProcessCall(getFlutterCommand(mockPlatform), - const ['build', 'winuwp'], pluginExampleDirectory.path), - ])); - }); - test( 'building for Android when plugin is not set up for Android results in no-op', () async { diff --git a/script/tool/test/common/plugin_utils_test.dart b/script/tool/test/common/plugin_utils_test.dart index cedd40acb7d6..af5cb7dfe4c6 100644 --- a/script/tool/test/common/plugin_utils_test.dart +++ b/script/tool/test/common/plugin_utils_test.dart @@ -193,85 +193,6 @@ void main() { requiredMode: PlatformSupport.inline), isFalse); }); - - test('windows without variants is only win32', () async { - final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( - 'plugin', - packagesDir, - platformSupport: { - platformWindows: const PlatformDetails(PlatformSupport.inline), - }, - )); - - expect( - pluginSupportsPlatform(platformWindows, plugin, - variant: platformVariantWin32), - isTrue); - expect( - pluginSupportsPlatform(platformWindows, plugin, - variant: platformVariantWinUwp), - isFalse); - }); - - test('windows with both variants matches win32 and winuwp', () async { - final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( - 'plugin', packagesDir, - platformSupport: { - platformWindows: const PlatformDetails( - PlatformSupport.federated, - variants: [platformVariantWin32, platformVariantWinUwp], - ), - })); - - expect( - pluginSupportsPlatform(platformWindows, plugin, - variant: platformVariantWin32), - isTrue); - expect( - pluginSupportsPlatform(platformWindows, plugin, - variant: platformVariantWinUwp), - isTrue); - }); - - test('win32 plugin is only win32', () async { - final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( - 'plugin', packagesDir, - platformSupport: { - platformWindows: const PlatformDetails( - PlatformSupport.federated, - variants: [platformVariantWin32], - ), - })); - - expect( - pluginSupportsPlatform(platformWindows, plugin, - variant: platformVariantWin32), - isTrue); - expect( - pluginSupportsPlatform(platformWindows, plugin, - variant: platformVariantWinUwp), - isFalse); - }); - - test('winup plugin is only winuwp', () async { - final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( - 'plugin', - packagesDir, - platformSupport: { - platformWindows: const PlatformDetails(PlatformSupport.federated, - variants: [platformVariantWinUwp]), - }, - )); - - expect( - pluginSupportsPlatform(platformWindows, plugin, - variant: platformVariantWin32), - isFalse); - expect( - pluginSupportsPlatform(platformWindows, plugin, - variant: platformVariantWinUwp), - isTrue); - }); }); group('pluginHasNativeCodeForPlatform', () { diff --git a/script/tool/test/drive_examples_command_test.dart b/script/tool/test/drive_examples_command_test.dart index 9372c571b6f7..ac57eb7251a2 100644 --- a/script/tool/test/drive_examples_command_test.dart +++ b/script/tool/test/drive_examples_command_test.dart @@ -708,40 +708,6 @@ void main() { ])); }); - test('driving UWP is a no-op', () async { - createFakePlugin( - 'plugin', - packagesDir, - extraFiles: [ - 'example/test_driver/plugin_test.dart', - 'example/test_driver/plugin.dart', - ], - platformSupport: { - platformWindows: const PlatformDetails(PlatformSupport.inline, - variants: [platformVariantWinUwp]), - }, - ); - - final List output = await runCapturingPrint(runner, [ - 'drive-examples', - '--winuwp', - ]); - - expect( - output, - containsAllInOrder([ - contains('Driving UWP applications is not yet supported'), - contains('Running for plugin'), - contains('SKIPPING: Drive does not yet support UWP'), - contains('No issues found!'), - ]), - ); - - // Output should be empty since running drive-examples --windows on a - // non-Windows plugin is a no-op. - expect(processRunner.recordedCalls, []); - }); - test('driving on an Android plugin', () async { final Directory pluginDirectory = createFakePlugin( 'plugin', diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart index fab4f39cc10f..2b1719ee8000 100644 --- a/script/tool/test/util.dart +++ b/script/tool/test/util.dart @@ -47,7 +47,6 @@ Directory createPackagesDirectory( class PlatformDetails { const PlatformDetails( this.type, { - this.variants = const [], this.hasNativeCode = true, this.hasDartCode = false, }); @@ -55,9 +54,6 @@ class PlatformDetails { /// The type of support for the platform. final PlatformSupport type; - /// Any 'supportVariants' to list in the pubspec. - final List variants; - /// Whether or not the plugin includes native code. /// /// Ignored for web, which does not have native code. @@ -293,18 +289,6 @@ String _pluginPlatformSection( entry = lines.join('\n') + '\n'; } - // Add any variants. - if (support.variants.isNotEmpty) { - entry += ''' - supportedVariants: -'''; - for (final String variant in support.variants) { - entry += ''' - - $variant -'''; - } - } - return entry; } From 3876f54bf2ad091f6478995da5628e08034ae968 Mon Sep 17 00:00:00 2001 From: Casey Hillers Date: Thu, 28 Apr 2022 11:34:10 -0700 Subject: [PATCH 455/600] [ci.yaml] Remove explicit caches (#5434) --- .ci.yaml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.ci.yaml b/.ci.yaml index 14772f475af6..a8fa7a5a1577 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -11,9 +11,6 @@ enabled_branches: platform_properties: linux: properties: - caches: >- - [ - ] dependencies: > [ {"dependency": "curl"} @@ -22,11 +19,6 @@ platform_properties: os: Linux windows: properties: - caches: >- - [ - {"name": "vsbuild", "path": "vsbuild"}, - {"name": "pub_cache", "path": ".pub-cache"} - ] dependencies: > [ {"dependency": "certs"} From 0605d876dc6bbb99cbf94335765d3b2c7ceaeb2a Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 28 Apr 2022 13:30:17 -0700 Subject: [PATCH 456/600] [webview_flutter_android] Updates `pigeon` version to support null safety (#5395) --- .../webview_flutter_android/CHANGELOG.md | 4 + .../webview_flutter_android/README.md | 8 +- .../GeneratedAndroidWebView.java | 563 ++++++++++++------ .../WebViewClientFlutterApiImpl.java | 40 +- .../webviewflutter/WebViewHostApiImpl.java | 30 +- .../webviewflutter/WebViewClientTest.java | 22 + .../plugins/webviewflutter/WebViewTest.java | 10 +- .../generatePigeons.sh | 10 - .../lib/src/android_webview.dart | 60 +- .../lib/src/android_webview.pigeon.dart | 275 ++++----- .../lib/src/android_webview_api_impls.dart | 47 +- .../pigeons/android_webview.dart | 71 ++- .../webview_flutter_android/pubspec.yaml | 4 +- .../test/android_webview_test.dart | 44 +- .../test/android_webview_test.mocks.dart | 17 +- ....dart => test_android_webview.pigeon.dart} | 62 +- .../test/webview_android_widget_test.dart | 2 +- 17 files changed, 722 insertions(+), 547 deletions(-) delete mode 100755 packages/webview_flutter/webview_flutter_android/generatePigeons.sh rename packages/webview_flutter/webview_flutter_android/test/{android_webview.pigeon.dart => test_android_webview.pigeon.dart} (95%) diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index fb5a2b85ffed..c9d023987bb1 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.8.6 + +* Updates pigeon developer dependency to the latest version which adds support for null safety. + ## 2.8.5 * Migrates deprecated `Scaffold.showSnackBar` to `ScaffoldMessenger` in example app. diff --git a/packages/webview_flutter/webview_flutter_android/README.md b/packages/webview_flutter/webview_flutter_android/README.md index 020f1fcfbbf1..04cbde292356 100644 --- a/packages/webview_flutter/webview_flutter_android/README.md +++ b/packages/webview_flutter/webview_flutter_android/README.md @@ -9,7 +9,10 @@ normally. This package will be automatically included in your app when you do. ## Contributing -This package uses [pigeon][3] to generate the communication layer between Flutter and the host platform (Android). The communication interface is defined in the `pigeons/android_webview.dart` file. After editing the communication interface regenerate the communication layer by running the `./generatePigeons.sh` shell script. +This package uses [pigeon][3] to generate the communication layer between Flutter and the host +platform (Android). The communication interface is defined in the `pigeons/android_webview.dart` +file. After editing the communication interface regenerate the communication layer by running +`flutter pub run pigeon --input pigeons/android_webview.dart`. Due to [flutter/flutter#97744](https://github.com/flutter/flutter/issues/97744), the generated test pigeon file needs one of its imports updated to properly work with `mockito`. @@ -26,7 +29,8 @@ to import 'package:webview_flutter_android/src/android_webview.pigeon.dart'; ``` -Besides [pigeon][3] this package also uses [mockito][4] to generate mock objects for testing purposes. To generate the mock objects run the following command: +Besides [pigeon][3] this package also uses [mockito][4] to generate mock objects for testing +purposes. To generate the mock objects run the following command: ```bash flutter packages pub run build_runner build --delete-conflicting-outputs ``` diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java index afca5ee12747..2e163311d6d4 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java @@ -1,12 +1,14 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - -// Autogenerated from Pigeon (v1.0.9), do not edit directly. +// Autogenerated from Pigeon (v3.0.3), do not edit directly. // See also: https://pub.dev/packages/pigeon package io.flutter.plugins.webviewflutter; +import android.util.Log; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import io.flutter.plugin.common.BasicMessageChannel; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.MessageCodec; @@ -25,66 +27,140 @@ public class GeneratedAndroidWebView { /** Generated class from Pigeon that represents data sent in messages. */ public static class WebResourceRequestData { - private String url; + private @NonNull String url; - public String getUrl() { + public @NonNull String getUrl() { return url; } - public void setUrl(String setterArg) { + public void setUrl(@NonNull String setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"url\" is null."); + } this.url = setterArg; } - private Boolean isForMainFrame; + private @NonNull Boolean isForMainFrame; - public Boolean getIsForMainFrame() { + public @NonNull Boolean getIsForMainFrame() { return isForMainFrame; } - public void setIsForMainFrame(Boolean setterArg) { + public void setIsForMainFrame(@NonNull Boolean setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"isForMainFrame\" is null."); + } this.isForMainFrame = setterArg; } - private Boolean isRedirect; + private @Nullable Boolean isRedirect; - public Boolean getIsRedirect() { + public @Nullable Boolean getIsRedirect() { return isRedirect; } - public void setIsRedirect(Boolean setterArg) { + public void setIsRedirect(@Nullable Boolean setterArg) { this.isRedirect = setterArg; } - private Boolean hasGesture; + private @NonNull Boolean hasGesture; - public Boolean getHasGesture() { + public @NonNull Boolean getHasGesture() { return hasGesture; } - public void setHasGesture(Boolean setterArg) { + public void setHasGesture(@NonNull Boolean setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"hasGesture\" is null."); + } this.hasGesture = setterArg; } - private String method; + private @NonNull String method; - public String getMethod() { + public @NonNull String getMethod() { return method; } - public void setMethod(String setterArg) { + public void setMethod(@NonNull String setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"method\" is null."); + } this.method = setterArg; } - private Map requestHeaders; + private @NonNull Map requestHeaders; - public Map getRequestHeaders() { + public @NonNull Map getRequestHeaders() { return requestHeaders; } - public void setRequestHeaders(Map setterArg) { + public void setRequestHeaders(@NonNull Map setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"requestHeaders\" is null."); + } this.requestHeaders = setterArg; } + /** Constructor is private to enforce null safety; use Builder. */ + private WebResourceRequestData() {} + + public static final class Builder { + private @Nullable String url; + + public @NonNull Builder setUrl(@NonNull String setterArg) { + this.url = setterArg; + return this; + } + + private @Nullable Boolean isForMainFrame; + + public @NonNull Builder setIsForMainFrame(@NonNull Boolean setterArg) { + this.isForMainFrame = setterArg; + return this; + } + + private @Nullable Boolean isRedirect; + + public @NonNull Builder setIsRedirect(@Nullable Boolean setterArg) { + this.isRedirect = setterArg; + return this; + } + + private @Nullable Boolean hasGesture; + + public @NonNull Builder setHasGesture(@NonNull Boolean setterArg) { + this.hasGesture = setterArg; + return this; + } + + private @Nullable String method; + + public @NonNull Builder setMethod(@NonNull String setterArg) { + this.method = setterArg; + return this; + } + + private @Nullable Map requestHeaders; + + public @NonNull Builder setRequestHeaders(@NonNull Map setterArg) { + this.requestHeaders = setterArg; + return this; + } + + public @NonNull WebResourceRequestData build() { + WebResourceRequestData pigeonReturn = new WebResourceRequestData(); + pigeonReturn.setUrl(url); + pigeonReturn.setIsForMainFrame(isForMainFrame); + pigeonReturn.setIsRedirect(isRedirect); + pigeonReturn.setHasGesture(hasGesture); + pigeonReturn.setMethod(method); + pigeonReturn.setRequestHeaders(requestHeaders); + return pigeonReturn; + } + } + + @NonNull Map toMap() { Map toMapResult = new HashMap<>(); toMapResult.put("url", url); @@ -96,46 +172,79 @@ Map toMap() { return toMapResult; } - static WebResourceRequestData fromMap(Map map) { - WebResourceRequestData fromMapResult = new WebResourceRequestData(); + static @NonNull WebResourceRequestData fromMap(@NonNull Map map) { + WebResourceRequestData pigeonResult = new WebResourceRequestData(); Object url = map.get("url"); - fromMapResult.url = (String) url; + pigeonResult.setUrl((String) url); Object isForMainFrame = map.get("isForMainFrame"); - fromMapResult.isForMainFrame = (Boolean) isForMainFrame; + pigeonResult.setIsForMainFrame((Boolean) isForMainFrame); Object isRedirect = map.get("isRedirect"); - fromMapResult.isRedirect = (Boolean) isRedirect; + pigeonResult.setIsRedirect((Boolean) isRedirect); Object hasGesture = map.get("hasGesture"); - fromMapResult.hasGesture = (Boolean) hasGesture; + pigeonResult.setHasGesture((Boolean) hasGesture); Object method = map.get("method"); - fromMapResult.method = (String) method; + pigeonResult.setMethod((String) method); Object requestHeaders = map.get("requestHeaders"); - fromMapResult.requestHeaders = (Map) requestHeaders; - return fromMapResult; + pigeonResult.setRequestHeaders((Map) requestHeaders); + return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ public static class WebResourceErrorData { - private Long errorCode; + private @NonNull Long errorCode; - public Long getErrorCode() { + public @NonNull Long getErrorCode() { return errorCode; } - public void setErrorCode(Long setterArg) { + public void setErrorCode(@NonNull Long setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"errorCode\" is null."); + } this.errorCode = setterArg; } - private String description; + private @NonNull String description; - public String getDescription() { + public @NonNull String getDescription() { return description; } - public void setDescription(String setterArg) { + public void setDescription(@NonNull String setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"description\" is null."); + } this.description = setterArg; } + /** Constructor is private to enforce null safety; use Builder. */ + private WebResourceErrorData() {} + + public static final class Builder { + private @Nullable Long errorCode; + + public @NonNull Builder setErrorCode(@NonNull Long setterArg) { + this.errorCode = setterArg; + return this; + } + + private @Nullable String description; + + public @NonNull Builder setDescription(@NonNull String setterArg) { + this.description = setterArg; + return this; + } + + public @NonNull WebResourceErrorData build() { + WebResourceErrorData pigeonReturn = new WebResourceErrorData(); + pigeonReturn.setErrorCode(errorCode); + pigeonReturn.setDescription(description); + return pigeonReturn; + } + } + + @NonNull Map toMap() { Map toMapResult = new HashMap<>(); toMapResult.put("errorCode", errorCode); @@ -143,16 +252,16 @@ Map toMap() { return toMapResult; } - static WebResourceErrorData fromMap(Map map) { - WebResourceErrorData fromMapResult = new WebResourceErrorData(); + static @NonNull WebResourceErrorData fromMap(@NonNull Map map) { + WebResourceErrorData pigeonResult = new WebResourceErrorData(); Object errorCode = map.get("errorCode"); - fromMapResult.errorCode = + pigeonResult.setErrorCode( (errorCode == null) ? null - : ((errorCode instanceof Integer) ? (Integer) errorCode : (Long) errorCode); + : ((errorCode instanceof Integer) ? (Integer) errorCode : (Long) errorCode)); Object description = map.get("description"); - fromMapResult.description = (String) description; - return fromMapResult; + pigeonResult.setDescription((String) description); + return pigeonResult; } } @@ -172,7 +281,7 @@ private CookieManagerHostApiCodec() {} public interface CookieManagerHostApi { void clearCookies(Result result); - void setCookie(String url, String value); + void setCookie(@NonNull String url, @NonNull String value); /** The codec used by CookieManagerHostApi. */ static MessageCodec getCodec() { @@ -258,63 +367,76 @@ private WebViewHostApiCodec() {} /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface WebViewHostApi { - void create(Long instanceId, Boolean useHybridComposition); + void create(@NonNull Long instanceId, @NonNull Boolean useHybridComposition); - void dispose(Long instanceId); + void dispose(@NonNull Long instanceId); - void loadData(Long instanceId, String data, String mimeType, String encoding); + void loadData( + @NonNull Long instanceId, + @NonNull String data, + @Nullable String mimeType, + @Nullable String encoding); void loadDataWithBaseUrl( - Long instanceId, - String baseUrl, - String data, - String mimeType, - String encoding, - String historyUrl); + @NonNull Long instanceId, + @Nullable String baseUrl, + @NonNull String data, + @Nullable String mimeType, + @Nullable String encoding, + @Nullable String historyUrl); - void loadUrl(Long instanceId, String url, Map headers); + void loadUrl( + @NonNull Long instanceId, @NonNull String url, @NonNull Map headers); - void postUrl(Long instanceId, String url, byte[] data); + void postUrl(@NonNull Long instanceId, @NonNull String url, @NonNull byte[] data); - String getUrl(Long instanceId); + @Nullable + String getUrl(@NonNull Long instanceId); - Boolean canGoBack(Long instanceId); + @NonNull + Boolean canGoBack(@NonNull Long instanceId); - Boolean canGoForward(Long instanceId); + @NonNull + Boolean canGoForward(@NonNull Long instanceId); - void goBack(Long instanceId); + void goBack(@NonNull Long instanceId); - void goForward(Long instanceId); + void goForward(@NonNull Long instanceId); - void reload(Long instanceId); + void reload(@NonNull Long instanceId); - void clearCache(Long instanceId, Boolean includeDiskFiles); + void clearCache(@NonNull Long instanceId, @NonNull Boolean includeDiskFiles); - void evaluateJavascript(Long instanceId, String javascriptString, Result result); + void evaluateJavascript( + @NonNull Long instanceId, @NonNull String javascriptString, Result result); - String getTitle(Long instanceId); + @Nullable + String getTitle(@NonNull Long instanceId); - void scrollTo(Long instanceId, Long x, Long y); + void scrollTo(@NonNull Long instanceId, @NonNull Long x, @NonNull Long y); - void scrollBy(Long instanceId, Long x, Long y); + void scrollBy(@NonNull Long instanceId, @NonNull Long x, @NonNull Long y); - Long getScrollX(Long instanceId); + @NonNull + Long getScrollX(@NonNull Long instanceId); - Long getScrollY(Long instanceId); + @NonNull + Long getScrollY(@NonNull Long instanceId); - void setWebContentsDebuggingEnabled(Boolean enabled); + void setWebContentsDebuggingEnabled(@NonNull Boolean enabled); - void setWebViewClient(Long instanceId, Long webViewClientInstanceId); + void setWebViewClient(@NonNull Long instanceId, @NonNull Long webViewClientInstanceId); - void addJavaScriptChannel(Long instanceId, Long javaScriptChannelInstanceId); + void addJavaScriptChannel(@NonNull Long instanceId, @NonNull Long javaScriptChannelInstanceId); - void removeJavaScriptChannel(Long instanceId, Long javaScriptChannelInstanceId); + void removeJavaScriptChannel( + @NonNull Long instanceId, @NonNull Long javaScriptChannelInstanceId); - void setDownloadListener(Long instanceId, Long listenerInstanceId); + void setDownloadListener(@NonNull Long instanceId, @Nullable Long listenerInstanceId); - void setWebChromeClient(Long instanceId, Long clientInstanceId); + void setWebChromeClient(@NonNull Long instanceId, @Nullable Long clientInstanceId); - void setBackgroundColor(Long instanceId, Long color); + void setBackgroundColor(@NonNull Long instanceId, @NonNull Long color); /** The codec used by WebViewHostApi. */ static MessageCodec getCodec() { @@ -341,7 +463,9 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (useHybridCompositionArg == null) { throw new NullPointerException("useHybridCompositionArg unexpectedly null."); } - api.create(instanceIdArg.longValue(), useHybridCompositionArg); + api.create( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + useHybridCompositionArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -366,7 +490,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - api.dispose(instanceIdArg.longValue()); + api.dispose((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -396,14 +520,12 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { throw new NullPointerException("dataArg unexpectedly null."); } String mimeTypeArg = (String) args.get(2); - if (mimeTypeArg == null) { - throw new NullPointerException("mimeTypeArg unexpectedly null."); - } String encodingArg = (String) args.get(3); - if (encodingArg == null) { - throw new NullPointerException("encodingArg unexpectedly null."); - } - api.loadData(instanceIdArg.longValue(), dataArg, mimeTypeArg, encodingArg); + api.loadData( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + dataArg, + mimeTypeArg, + encodingArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -431,27 +553,15 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { throw new NullPointerException("instanceIdArg unexpectedly null."); } String baseUrlArg = (String) args.get(1); - if (baseUrlArg == null) { - throw new NullPointerException("baseUrlArg unexpectedly null."); - } String dataArg = (String) args.get(2); if (dataArg == null) { throw new NullPointerException("dataArg unexpectedly null."); } String mimeTypeArg = (String) args.get(3); - if (mimeTypeArg == null) { - throw new NullPointerException("mimeTypeArg unexpectedly null."); - } String encodingArg = (String) args.get(4); - if (encodingArg == null) { - throw new NullPointerException("encodingArg unexpectedly null."); - } String historyUrlArg = (String) args.get(5); - if (historyUrlArg == null) { - throw new NullPointerException("historyUrlArg unexpectedly null."); - } api.loadDataWithBaseUrl( - instanceIdArg.longValue(), + (instanceIdArg == null) ? null : instanceIdArg.longValue(), baseUrlArg, dataArg, mimeTypeArg, @@ -489,7 +599,10 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (headersArg == null) { throw new NullPointerException("headersArg unexpectedly null."); } - api.loadUrl(instanceIdArg.longValue(), urlArg, headersArg); + api.loadUrl( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + urlArg, + headersArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -522,7 +635,8 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (dataArg == null) { throw new NullPointerException("dataArg unexpectedly null."); } - api.postUrl(instanceIdArg.longValue(), urlArg, dataArg); + api.postUrl( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), urlArg, dataArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -547,7 +661,8 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - String output = api.getUrl(instanceIdArg.longValue()); + String output = + api.getUrl((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", output); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -572,7 +687,8 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - Boolean output = api.canGoBack(instanceIdArg.longValue()); + Boolean output = + api.canGoBack((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", output); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -597,7 +713,8 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - Boolean output = api.canGoForward(instanceIdArg.longValue()); + Boolean output = + api.canGoForward((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", output); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -622,7 +739,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - api.goBack(instanceIdArg.longValue()); + api.goBack((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -647,7 +764,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - api.goForward(instanceIdArg.longValue()); + api.goForward((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -672,7 +789,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - api.reload(instanceIdArg.longValue()); + api.reload((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -701,7 +818,9 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (includeDiskFilesArg == null) { throw new NullPointerException("includeDiskFilesArg unexpectedly null."); } - api.clearCache(instanceIdArg.longValue(), includeDiskFilesArg); + api.clearCache( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + includeDiskFilesArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -746,7 +865,9 @@ public void error(Throwable error) { }; api.evaluateJavascript( - instanceIdArg.longValue(), javascriptStringArg, resultCallback); + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + javascriptStringArg, + resultCallback); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); reply.reply(wrapped); @@ -770,7 +891,8 @@ public void error(Throwable error) { if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - String output = api.getTitle(instanceIdArg.longValue()); + String output = + api.getTitle((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", output); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -803,7 +925,10 @@ public void error(Throwable error) { if (yArg == null) { throw new NullPointerException("yArg unexpectedly null."); } - api.scrollTo(instanceIdArg.longValue(), xArg.longValue(), yArg.longValue()); + api.scrollTo( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + (xArg == null) ? null : xArg.longValue(), + (yArg == null) ? null : yArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -836,7 +961,10 @@ public void error(Throwable error) { if (yArg == null) { throw new NullPointerException("yArg unexpectedly null."); } - api.scrollBy(instanceIdArg.longValue(), xArg.longValue(), yArg.longValue()); + api.scrollBy( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + (xArg == null) ? null : xArg.longValue(), + (yArg == null) ? null : yArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -861,7 +989,8 @@ public void error(Throwable error) { if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - Long output = api.getScrollX(instanceIdArg.longValue()); + Long output = + api.getScrollX((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", output); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -886,7 +1015,8 @@ public void error(Throwable error) { if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - Long output = api.getScrollY(instanceIdArg.longValue()); + Long output = + api.getScrollY((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", output); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -943,7 +1073,10 @@ public void error(Throwable error) { throw new NullPointerException("webViewClientInstanceIdArg unexpectedly null."); } api.setWebViewClient( - instanceIdArg.longValue(), webViewClientInstanceIdArg.longValue()); + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + (webViewClientInstanceIdArg == null) + ? null + : webViewClientInstanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -976,7 +1109,10 @@ public void error(Throwable error) { "javaScriptChannelInstanceIdArg unexpectedly null."); } api.addJavaScriptChannel( - instanceIdArg.longValue(), javaScriptChannelInstanceIdArg.longValue()); + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + (javaScriptChannelInstanceIdArg == null) + ? null + : javaScriptChannelInstanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1009,7 +1145,10 @@ public void error(Throwable error) { "javaScriptChannelInstanceIdArg unexpectedly null."); } api.removeJavaScriptChannel( - instanceIdArg.longValue(), javaScriptChannelInstanceIdArg.longValue()); + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + (javaScriptChannelInstanceIdArg == null) + ? null + : javaScriptChannelInstanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1037,11 +1176,9 @@ public void error(Throwable error) { throw new NullPointerException("instanceIdArg unexpectedly null."); } Number listenerInstanceIdArg = (Number) args.get(1); - if (listenerInstanceIdArg == null) { - throw new NullPointerException("listenerInstanceIdArg unexpectedly null."); - } api.setDownloadListener( - instanceIdArg.longValue(), listenerInstanceIdArg.longValue()); + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + (listenerInstanceIdArg == null) ? null : listenerInstanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1069,11 +1206,9 @@ public void error(Throwable error) { throw new NullPointerException("instanceIdArg unexpectedly null."); } Number clientInstanceIdArg = (Number) args.get(1); - if (clientInstanceIdArg == null) { - throw new NullPointerException("clientInstanceIdArg unexpectedly null."); - } api.setWebChromeClient( - instanceIdArg.longValue(), clientInstanceIdArg.longValue()); + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + (clientInstanceIdArg == null) ? null : clientInstanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1104,7 +1239,9 @@ public void error(Throwable error) { if (colorArg == null) { throw new NullPointerException("colorArg unexpectedly null."); } - api.setBackgroundColor(instanceIdArg.longValue(), colorArg.longValue()); + api.setBackgroundColor( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + (colorArg == null) ? null : colorArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1126,33 +1263,33 @@ private WebSettingsHostApiCodec() {} /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface WebSettingsHostApi { - void create(Long instanceId, Long webViewInstanceId); + void create(@NonNull Long instanceId, @NonNull Long webViewInstanceId); - void dispose(Long instanceId); + void dispose(@NonNull Long instanceId); - void setDomStorageEnabled(Long instanceId, Boolean flag); + void setDomStorageEnabled(@NonNull Long instanceId, @NonNull Boolean flag); - void setJavaScriptCanOpenWindowsAutomatically(Long instanceId, Boolean flag); + void setJavaScriptCanOpenWindowsAutomatically(@NonNull Long instanceId, @NonNull Boolean flag); - void setSupportMultipleWindows(Long instanceId, Boolean support); + void setSupportMultipleWindows(@NonNull Long instanceId, @NonNull Boolean support); - void setJavaScriptEnabled(Long instanceId, Boolean flag); + void setJavaScriptEnabled(@NonNull Long instanceId, @NonNull Boolean flag); - void setUserAgentString(Long instanceId, String userAgentString); + void setUserAgentString(@NonNull Long instanceId, @Nullable String userAgentString); - void setMediaPlaybackRequiresUserGesture(Long instanceId, Boolean require); + void setMediaPlaybackRequiresUserGesture(@NonNull Long instanceId, @NonNull Boolean require); - void setSupportZoom(Long instanceId, Boolean support); + void setSupportZoom(@NonNull Long instanceId, @NonNull Boolean support); - void setLoadWithOverviewMode(Long instanceId, Boolean overview); + void setLoadWithOverviewMode(@NonNull Long instanceId, @NonNull Boolean overview); - void setUseWideViewPort(Long instanceId, Boolean use); + void setUseWideViewPort(@NonNull Long instanceId, @NonNull Boolean use); - void setDisplayZoomControls(Long instanceId, Boolean enabled); + void setDisplayZoomControls(@NonNull Long instanceId, @NonNull Boolean enabled); - void setBuiltInZoomControls(Long instanceId, Boolean enabled); + void setBuiltInZoomControls(@NonNull Long instanceId, @NonNull Boolean enabled); - void setAllowFileAccess(Long instanceId, Boolean enabled); + void setAllowFileAccess(@NonNull Long instanceId, @NonNull Boolean enabled); /** The codec used by WebSettingsHostApi. */ static MessageCodec getCodec() { @@ -1181,7 +1318,9 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (webViewInstanceIdArg == null) { throw new NullPointerException("webViewInstanceIdArg unexpectedly null."); } - api.create(instanceIdArg.longValue(), webViewInstanceIdArg.longValue()); + api.create( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + (webViewInstanceIdArg == null) ? null : webViewInstanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1206,7 +1345,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - api.dispose(instanceIdArg.longValue()); + api.dispose((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1237,7 +1376,8 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (flagArg == null) { throw new NullPointerException("flagArg unexpectedly null."); } - api.setDomStorageEnabled(instanceIdArg.longValue(), flagArg); + api.setDomStorageEnabled( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), flagArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1268,7 +1408,8 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (flagArg == null) { throw new NullPointerException("flagArg unexpectedly null."); } - api.setJavaScriptCanOpenWindowsAutomatically(instanceIdArg.longValue(), flagArg); + api.setJavaScriptCanOpenWindowsAutomatically( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), flagArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1299,7 +1440,8 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (supportArg == null) { throw new NullPointerException("supportArg unexpectedly null."); } - api.setSupportMultipleWindows(instanceIdArg.longValue(), supportArg); + api.setSupportMultipleWindows( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), supportArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1330,7 +1472,8 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (flagArg == null) { throw new NullPointerException("flagArg unexpectedly null."); } - api.setJavaScriptEnabled(instanceIdArg.longValue(), flagArg); + api.setJavaScriptEnabled( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), flagArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1358,10 +1501,9 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { throw new NullPointerException("instanceIdArg unexpectedly null."); } String userAgentStringArg = (String) args.get(1); - if (userAgentStringArg == null) { - throw new NullPointerException("userAgentStringArg unexpectedly null."); - } - api.setUserAgentString(instanceIdArg.longValue(), userAgentStringArg); + api.setUserAgentString( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + userAgentStringArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1392,7 +1534,8 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (requireArg == null) { throw new NullPointerException("requireArg unexpectedly null."); } - api.setMediaPlaybackRequiresUserGesture(instanceIdArg.longValue(), requireArg); + api.setMediaPlaybackRequiresUserGesture( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), requireArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1423,7 +1566,8 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (supportArg == null) { throw new NullPointerException("supportArg unexpectedly null."); } - api.setSupportZoom(instanceIdArg.longValue(), supportArg); + api.setSupportZoom( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), supportArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1454,7 +1598,8 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (overviewArg == null) { throw new NullPointerException("overviewArg unexpectedly null."); } - api.setLoadWithOverviewMode(instanceIdArg.longValue(), overviewArg); + api.setLoadWithOverviewMode( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), overviewArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1485,7 +1630,8 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (useArg == null) { throw new NullPointerException("useArg unexpectedly null."); } - api.setUseWideViewPort(instanceIdArg.longValue(), useArg); + api.setUseWideViewPort( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), useArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1516,7 +1662,8 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (enabledArg == null) { throw new NullPointerException("enabledArg unexpectedly null."); } - api.setDisplayZoomControls(instanceIdArg.longValue(), enabledArg); + api.setDisplayZoomControls( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), enabledArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1547,7 +1694,8 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (enabledArg == null) { throw new NullPointerException("enabledArg unexpectedly null."); } - api.setBuiltInZoomControls(instanceIdArg.longValue(), enabledArg); + api.setBuiltInZoomControls( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), enabledArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1578,7 +1726,8 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (enabledArg == null) { throw new NullPointerException("enabledArg unexpectedly null."); } - api.setAllowFileAccess(instanceIdArg.longValue(), enabledArg); + api.setAllowFileAccess( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), enabledArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1601,7 +1750,7 @@ private JavaScriptChannelHostApiCodec() {} /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface JavaScriptChannelHostApi { - void create(Long instanceId, String channelName); + void create(@NonNull Long instanceId, @NonNull String channelName); /** The codec used by JavaScriptChannelHostApi. */ static MessageCodec getCodec() { @@ -1631,7 +1780,8 @@ static void setup(BinaryMessenger binaryMessenger, JavaScriptChannelHostApi api) if (channelNameArg == null) { throw new NullPointerException("channelNameArg unexpectedly null."); } - api.create(instanceIdArg.longValue(), channelNameArg); + api.create( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), channelNameArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1668,7 +1818,7 @@ static MessageCodec getCodec() { return JavaScriptChannelFlutterApiCodec.INSTANCE; } - public void dispose(Long instanceIdArg, Reply callback) { + public void dispose(@NonNull Long instanceIdArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, @@ -1681,7 +1831,8 @@ public void dispose(Long instanceIdArg, Reply callback) { }); } - public void postMessage(Long instanceIdArg, String messageArg, Reply callback) { + public void postMessage( + @NonNull Long instanceIdArg, @NonNull String messageArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, @@ -1703,7 +1854,7 @@ private WebViewClientHostApiCodec() {} /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface WebViewClientHostApi { - void create(Long instanceId, Boolean shouldOverrideUrlLoading); + void create(@NonNull Long instanceId, @NonNull Boolean shouldOverrideUrlLoading); /** The codec used by WebViewClientHostApi. */ static MessageCodec getCodec() { @@ -1734,7 +1885,9 @@ static void setup(BinaryMessenger binaryMessenger, WebViewClientHostApi api) { throw new NullPointerException( "shouldOverrideUrlLoadingArg unexpectedly null."); } - api.create(instanceIdArg.longValue(), shouldOverrideUrlLoadingArg); + api.create( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + shouldOverrideUrlLoadingArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1797,7 +1950,7 @@ static MessageCodec getCodec() { return WebViewClientFlutterApiCodec.INSTANCE; } - public void dispose(Long instanceIdArg, Reply callback) { + public void dispose(@NonNull Long instanceIdArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, "dev.flutter.pigeon.WebViewClientFlutterApi.dispose", getCodec()); @@ -1809,7 +1962,10 @@ public void dispose(Long instanceIdArg, Reply callback) { } public void onPageStarted( - Long instanceIdArg, Long webViewInstanceIdArg, String urlArg, Reply callback) { + @NonNull Long instanceIdArg, + @NonNull Long webViewInstanceIdArg, + @NonNull String urlArg, + Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, @@ -1823,7 +1979,10 @@ public void onPageStarted( } public void onPageFinished( - Long instanceIdArg, Long webViewInstanceIdArg, String urlArg, Reply callback) { + @NonNull Long instanceIdArg, + @NonNull Long webViewInstanceIdArg, + @NonNull String urlArg, + Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, @@ -1837,10 +1996,10 @@ public void onPageFinished( } public void onReceivedRequestError( - Long instanceIdArg, - Long webViewInstanceIdArg, - WebResourceRequestData requestArg, - WebResourceErrorData errorArg, + @NonNull Long instanceIdArg, + @NonNull Long webViewInstanceIdArg, + @NonNull WebResourceRequestData requestArg, + @NonNull WebResourceErrorData errorArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>( @@ -1856,11 +2015,11 @@ public void onReceivedRequestError( } public void onReceivedError( - Long instanceIdArg, - Long webViewInstanceIdArg, - Long errorCodeArg, - String descriptionArg, - String failingUrlArg, + @NonNull Long instanceIdArg, + @NonNull Long webViewInstanceIdArg, + @NonNull Long errorCodeArg, + @NonNull String descriptionArg, + @NonNull String failingUrlArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>( @@ -1881,9 +2040,9 @@ public void onReceivedError( } public void requestLoading( - Long instanceIdArg, - Long webViewInstanceIdArg, - WebResourceRequestData requestArg, + @NonNull Long instanceIdArg, + @NonNull Long webViewInstanceIdArg, + @NonNull WebResourceRequestData requestArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>( @@ -1898,7 +2057,10 @@ public void requestLoading( } public void urlLoading( - Long instanceIdArg, Long webViewInstanceIdArg, String urlArg, Reply callback) { + @NonNull Long instanceIdArg, + @NonNull Long webViewInstanceIdArg, + @NonNull String urlArg, + Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, "dev.flutter.pigeon.WebViewClientFlutterApi.urlLoading", getCodec()); @@ -1918,7 +2080,7 @@ private DownloadListenerHostApiCodec() {} /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface DownloadListenerHostApi { - void create(Long instanceId); + void create(@NonNull Long instanceId); /** The codec used by DownloadListenerHostApi. */ static MessageCodec getCodec() { @@ -1944,7 +2106,7 @@ static void setup(BinaryMessenger binaryMessenger, DownloadListenerHostApi api) if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - api.create(instanceIdArg.longValue()); + api.create((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1981,7 +2143,7 @@ static MessageCodec getCodec() { return DownloadListenerFlutterApiCodec.INSTANCE; } - public void dispose(Long instanceIdArg, Reply callback) { + public void dispose(@NonNull Long instanceIdArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, "dev.flutter.pigeon.DownloadListenerFlutterApi.dispose", getCodec()); @@ -1993,12 +2155,12 @@ public void dispose(Long instanceIdArg, Reply callback) { } public void onDownloadStart( - Long instanceIdArg, - String urlArg, - String userAgentArg, - String contentDispositionArg, - String mimetypeArg, - Long contentLengthArg, + @NonNull Long instanceIdArg, + @NonNull String urlArg, + @NonNull String userAgentArg, + @NonNull String contentDispositionArg, + @NonNull String mimetypeArg, + @NonNull Long contentLengthArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>( @@ -2028,7 +2190,7 @@ private WebChromeClientHostApiCodec() {} /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface WebChromeClientHostApi { - void create(Long instanceId, Long webViewClientInstanceId); + void create(@NonNull Long instanceId, @NonNull Long webViewClientInstanceId); /** The codec used by WebChromeClientHostApi. */ static MessageCodec getCodec() { @@ -2058,7 +2220,11 @@ static void setup(BinaryMessenger binaryMessenger, WebChromeClientHostApi api) { if (webViewClientInstanceIdArg == null) { throw new NullPointerException("webViewClientInstanceIdArg unexpectedly null."); } - api.create(instanceIdArg.longValue(), webViewClientInstanceIdArg.longValue()); + api.create( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + (webViewClientInstanceIdArg == null) + ? null + : webViewClientInstanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -2081,9 +2247,11 @@ private FlutterAssetManagerHostApiCodec() {} /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface FlutterAssetManagerHostApi { - List list(String path); + @NonNull + List list(@NonNull String path); - String getAssetFilePathByName(String name); + @NonNull + String getAssetFilePathByName(@NonNull String name); /** The codec used by FlutterAssetManagerHostApi. */ static MessageCodec getCodec() { @@ -2173,7 +2341,7 @@ static MessageCodec getCodec() { return WebChromeClientFlutterApiCodec.INSTANCE; } - public void dispose(Long instanceIdArg, Reply callback) { + public void dispose(@NonNull Long instanceIdArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, "dev.flutter.pigeon.WebChromeClientFlutterApi.dispose", getCodec()); @@ -2185,7 +2353,10 @@ public void dispose(Long instanceIdArg, Reply callback) { } public void onProgressChanged( - Long instanceIdArg, Long webViewInstanceIdArg, Long progressArg, Reply callback) { + @NonNull Long instanceIdArg, + @NonNull Long webViewInstanceIdArg, + @NonNull Long progressArg, + Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, @@ -2207,9 +2378,9 @@ private WebStorageHostApiCodec() {} /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface WebStorageHostApi { - void create(Long instanceId); + void create(@NonNull Long instanceId); - void deleteAllData(Long instanceId); + void deleteAllData(@NonNull Long instanceId); /** The codec used by WebStorageHostApi. */ static MessageCodec getCodec() { @@ -2234,7 +2405,7 @@ static void setup(BinaryMessenger binaryMessenger, WebStorageHostApi api) { if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - api.create(instanceIdArg.longValue()); + api.create((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -2259,7 +2430,7 @@ static void setup(BinaryMessenger binaryMessenger, WebStorageHostApi api) { if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - api.deleteAllData(instanceIdArg.longValue()); + api.deleteAllData((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -2277,7 +2448,9 @@ private static Map wrapError(Throwable exception) { Map errorMap = new HashMap<>(); errorMap.put("message", exception.toString()); errorMap.put("code", exception.getClass().getSimpleName()); - errorMap.put("details", null); + errorMap.put( + "details", + "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); return errorMap; } } diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientFlutterApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientFlutterApiImpl.java index 9e462faa58a7..b4885688f7ac 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientFlutterApiImpl.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientFlutterApiImpl.java @@ -14,6 +14,7 @@ import androidx.webkit.WebResourceErrorCompat; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebViewClientFlutterApi; +import java.util.HashMap; /** * Flutter Api implementation for {@link WebViewClient}. @@ -26,40 +27,39 @@ public class WebViewClientFlutterApiImpl extends WebViewClientFlutterApi { @RequiresApi(api = Build.VERSION_CODES.M) static GeneratedAndroidWebView.WebResourceErrorData createWebResourceErrorData( WebResourceError error) { - final GeneratedAndroidWebView.WebResourceErrorData errorData = - new GeneratedAndroidWebView.WebResourceErrorData(); - errorData.setErrorCode((long) error.getErrorCode()); - errorData.setDescription(error.getDescription().toString()); - - return errorData; + return new GeneratedAndroidWebView.WebResourceErrorData.Builder() + .setErrorCode((long) error.getErrorCode()) + .setDescription(error.getDescription().toString()) + .build(); } @SuppressLint("RequiresFeature") static GeneratedAndroidWebView.WebResourceErrorData createWebResourceErrorData( WebResourceErrorCompat error) { - final GeneratedAndroidWebView.WebResourceErrorData errorData = - new GeneratedAndroidWebView.WebResourceErrorData(); - errorData.setErrorCode((long) error.getErrorCode()); - errorData.setDescription(error.getDescription().toString()); - - return errorData; + return new GeneratedAndroidWebView.WebResourceErrorData.Builder() + .setErrorCode((long) error.getErrorCode()) + .setDescription(error.getDescription().toString()) + .build(); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) static GeneratedAndroidWebView.WebResourceRequestData createWebResourceRequestData( WebResourceRequest request) { - final GeneratedAndroidWebView.WebResourceRequestData requestData = - new GeneratedAndroidWebView.WebResourceRequestData(); - requestData.setUrl(request.getUrl().toString()); - requestData.setIsForMainFrame(request.isForMainFrame()); + final GeneratedAndroidWebView.WebResourceRequestData.Builder requestData = + new GeneratedAndroidWebView.WebResourceRequestData.Builder() + .setUrl(request.getUrl().toString()) + .setIsForMainFrame(request.isForMainFrame()) + .setHasGesture(request.hasGesture()) + .setMethod(request.getMethod()) + .setRequestHeaders( + request.getRequestHeaders() != null + ? request.getRequestHeaders() + : new HashMap<>()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { requestData.setIsRedirect(request.isRedirect()); } - requestData.setHasGesture(request.hasGesture()); - requestData.setMethod(request.getMethod()); - requestData.setRequestHeaders(request.getRequestHeaders()); - return requestData; + return requestData.build(); } /** 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 0f3161355dcb..afc3efee80ff 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 @@ -28,11 +28,6 @@ *

Handles creating {@link WebView}s that intercommunicate with a paired Dart object. */ public class WebViewHostApiImpl implements WebViewHostApi { - // TODO(bparrishMines): This can be removed once pigeon supports null values: https://github.com/flutter/flutter/issues/59118 - // Workaround to represent null Strings since pigeon doesn't support null - // values. - private static final String nullStringIdentifier = ""; - private final InstanceManager instanceManager; private final WebViewProxy webViewProxy; // Only used with WebView using virtual displays. @@ -355,8 +350,7 @@ public void dispose(Long instanceId) { @Override public void loadData(Long instanceId, String data, String mimeType, String encoding) { final WebView webView = (WebView) instanceManager.getInstance(instanceId); - webView.loadData( - data, parseNullStringIdentifier(mimeType), parseNullStringIdentifier(encoding)); + webView.loadData(data, mimeType, encoding); } @Override @@ -368,12 +362,7 @@ public void loadDataWithBaseUrl( String encoding, String historyUrl) { final WebView webView = (WebView) instanceManager.getInstance(instanceId); - webView.loadDataWithBaseURL( - parseNullStringIdentifier(baseUrl), - data, - parseNullStringIdentifier(mimeType), - parseNullStringIdentifier(encoding), - parseNullStringIdentifier(historyUrl)); + webView.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl); } @Override @@ -391,8 +380,7 @@ public void postUrl(Long instanceId, String url, byte[] data) { @Override public String getUrl(Long instanceId) { final WebView webView = (WebView) instanceManager.getInstance(instanceId); - final String result = webView.getUrl(); - return result != null ? result : nullStringIdentifier; + return webView.getUrl(); } @Override @@ -441,8 +429,7 @@ public void evaluateJavascript( @Override public String getTitle(Long instanceId) { final WebView webView = (WebView) instanceManager.getInstance(instanceId); - final String result = webView.getTitle(); - return result != null ? result : nullStringIdentifier; + return webView.getTitle(); } @Override @@ -513,13 +500,4 @@ public void setBackgroundColor(Long instanceId, Long color) { final WebView webView = (WebView) instanceManager.getInstance(instanceId); webView.setBackgroundColor(color.intValue()); } - - @Nullable - private static String parseNullStringIdentifier(String value) { - if (value.equals(nullStringIdentifier)) { - return null; - } - - return value; - } } diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientTest.java index 62d272366a6f..c2abd25c5a66 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientTest.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientTest.java @@ -4,16 +4,22 @@ package io.flutter.plugins.webviewflutter; +import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import android.net.Uri; +import android.webkit.WebResourceRequest; import android.webkit.WebView; import android.webkit.WebViewClient; import io.flutter.plugins.webviewflutter.WebViewClientHostApiImpl.WebViewClientCompatImpl; import io.flutter.plugins.webviewflutter.WebViewClientHostApiImpl.WebViewClientCreator; +import java.util.HashMap; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -96,4 +102,20 @@ public void urlLoading() { webViewClient.shouldOverrideUrlLoading(mockWebView, ""); verify(mockFlutterApi, never()).urlLoading((WebViewClient) any(), any(), any(), any()); } + + @Test + public void convertWebResourceRequestWithNullHeaders() { + final Uri mockUri = mock(Uri.class); + when(mockUri.toString()).thenReturn(""); + + final WebResourceRequest mockRequest = mock(WebResourceRequest.class); + when(mockRequest.getMethod()).thenReturn("method"); + when(mockRequest.getUrl()).thenReturn(mockUri); + when(mockRequest.isForMainFrame()).thenReturn(true); + when(mockRequest.getRequestHeaders()).thenReturn(null); + + final GeneratedAndroidWebView.WebResourceRequestData data = + WebViewClientFlutterApiImpl.createWebResourceRequestData(mockRequest); + assertEquals(data.getRequestHeaders(), new HashMap()); + } } 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 2312b764342f..5be39ab963a3 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 @@ -166,8 +166,7 @@ public void loadData() { @Test public void loadDataWithNullValues() { - testHostApiImpl.loadData( - 0L, "VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==", "", ""); + testHostApiImpl.loadData(0L, "VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==", null, null); verify(mockWebView).loadData("VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==", null, null); } @@ -192,12 +191,7 @@ public void loadDataWithBaseUrl() { @Test public void loadDataWithBaseUrlAndNullValues() { testHostApiImpl.loadDataWithBaseUrl( - 0L, - "", - "VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==", - "", - "", - ""); + 0L, null, "VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==", null, null, null); verify(mockWebView) .loadDataWithBaseURL(null, "VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==", null, null, null); } diff --git a/packages/webview_flutter/webview_flutter_android/generatePigeons.sh b/packages/webview_flutter/webview_flutter_android/generatePigeons.sh deleted file mode 100755 index 30a6918fc922..000000000000 --- a/packages/webview_flutter/webview_flutter_android/generatePigeons.sh +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright 2013 The Flutter Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -flutter pub run pigeon \ ---input pigeons/android_webview.dart \ ---dart_out lib/src/android_webview.pigeon.dart \ ---dart_test_out test/android_webview.pigeon.dart \ ---java_out android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.Java \ ---java_package io.flutter.plugins.webviewflutter diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart index bd50640919f9..f858fb39a943 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart @@ -11,11 +11,6 @@ import 'package:flutter/widgets.dart' show AndroidViewSurface; import 'android_webview.pigeon.dart'; import 'android_webview_api_impls.dart'; -// TODO(bparrishMines): This can be removed once pigeon supports null values: https://github.com/flutter/flutter/issues/59118 -// Workaround to represent null Strings since pigeon doesn't support null -// values. -const String _nullStringIdentifier = ''; - /// An Android View that displays web pages. /// /// **Basic usage** @@ -102,8 +97,8 @@ class WebView { return api.loadDataFromInstance( this, data, - mimeType ?? _nullStringIdentifier, - encoding ?? _nullStringIdentifier, + mimeType, + encoding, ); } @@ -151,11 +146,11 @@ class WebView { }) { return api.loadDataWithBaseUrlFromInstance( this, - baseUrl ?? _nullStringIdentifier, + baseUrl, data, - mimeType ?? _nullStringIdentifier, - encoding ?? _nullStringIdentifier, - historyUrl ?? _nullStringIdentifier, + mimeType, + encoding, + historyUrl, ); } @@ -184,12 +179,8 @@ class WebView { /// begun, the current page may not have changed. /// /// Returns null if no page has been loaded. - Future getUrl() async { - final String result = await api.getUrlFromInstance(this); - if (result == _nullStringIdentifier) { - return null; - } - return result; + Future getUrl() { + return api.getUrlFromInstance(this); } /// Whether this WebView has a back history item. @@ -235,27 +226,19 @@ class WebView { /// JavaScript state from an empty WebView is no longer persisted across /// navigations like [loadUrl]. For example, global variables and functions /// defined before calling [loadUrl]) will not exist in the loaded page. - Future evaluateJavascript(String javascriptString) async { - final String result = await api.evaluateJavascriptFromInstance( + Future evaluateJavascript(String javascriptString) { + return api.evaluateJavascriptFromInstance( this, javascriptString, ); - if (result == _nullStringIdentifier) { - return null; - } - return result; } // TODO(bparrishMines): Update documentation when WebViewClient.onReceivedTitle is added. /// Gets the title for the current page. /// /// Returns null if no page has been loaded. - Future getTitle() async { - final String result = await api.getTitleFromInstance(this); - if (result == _nullStringIdentifier) { - return null; - } - return result; + Future getTitle() { + return api.getTitleFromInstance(this); } // TODO(bparrishMines): Update documentation when onScrollChanged is added. @@ -337,9 +320,11 @@ class WebView { /// Registers the interface to be used when content can not be handled by the rendering engine, and should be downloaded instead. /// /// This will replace the current handler. - Future setDownloadListener(DownloadListener listener) { - DownloadListener.api.createFromInstance(listener); - return api.setDownloadListenerFromInstance(this, listener); + Future setDownloadListener(DownloadListener? listener) async { + await Future.wait(>[ + if (listener != null) DownloadListener.api.createFromInstance(listener), + api.setDownloadListenerFromInstance(this, listener) + ]); } /// Sets the chrome handler. @@ -347,7 +332,7 @@ class WebView { /// This is an implementation of [WebChromeClient] for use in handling /// JavaScript dialogs, favicons, titles, and the progress. This will replace /// the current handler. - Future setWebChromeClient(WebChromeClient client) { + Future setWebChromeClient(WebChromeClient? client) async { // WebView requires a WebViewClient because of a bug fix that makes // calls to WebViewClient.requestLoading/WebViewClient.urlLoading when a new // window is opened. This is to make sure a url opened by `Window.open` has @@ -356,8 +341,11 @@ class WebView { _currentWebViewClient != null, "Can't set a WebChromeClient without setting a WebViewClient first.", ); - WebChromeClient.api.createFromInstance(client, _currentWebViewClient!); - return api.setWebChromeClientFromInstance(this, client); + await Future.wait(>[ + if (client != null) + WebChromeClient.api.createFromInstance(client, _currentWebViewClient!), + api.setWebChromeClientFromInstance(this, client), + ]); } /// Sets the background color of this WebView. @@ -477,7 +465,7 @@ class WebSettings { /// If the string is empty, the system default value will be used. Note that /// starting from KITKAT Android version, changing the user-agent while /// loading a web page causes WebView to initiate loading once again. - Future setUserAgentString(String userAgentString) { + Future setUserAgentString(String? userAgentString) { return api.setUserAgentStringFromInstance(this, userAgentString); } diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart index 4a0965eaeac0..4491e162ce9c 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart @@ -1,8 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - -// Autogenerated from Pigeon (v1.0.9), do not edit directly. +// Autogenerated from Pigeon (v3.0.3), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name // @dart = 2.12 @@ -13,12 +12,21 @@ import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; import 'package:flutter/services.dart'; class WebResourceRequestData { - String? url; - bool? isForMainFrame; + WebResourceRequestData({ + required this.url, + required this.isForMainFrame, + this.isRedirect, + required this.hasGesture, + required this.method, + required this.requestHeaders, + }); + + String url; + bool isForMainFrame; bool? isRedirect; - bool? hasGesture; - String? method; - Map? requestHeaders; + bool hasGesture; + String method; + Map requestHeaders; Object encode() { final Map pigeonMap = {}; @@ -33,20 +41,26 @@ class WebResourceRequestData { static WebResourceRequestData decode(Object message) { final Map pigeonMap = message as Map; - return WebResourceRequestData() - ..url = pigeonMap['url'] as String? - ..isForMainFrame = pigeonMap['isForMainFrame'] as bool? - ..isRedirect = pigeonMap['isRedirect'] as bool? - ..hasGesture = pigeonMap['hasGesture'] as bool? - ..method = pigeonMap['method'] as String? - ..requestHeaders = (pigeonMap['requestHeaders'] as Map?) - ?.cast(); + return WebResourceRequestData( + url: pigeonMap['url']! as String, + isForMainFrame: pigeonMap['isForMainFrame']! as bool, + isRedirect: pigeonMap['isRedirect'] as bool?, + hasGesture: pigeonMap['hasGesture']! as bool, + method: pigeonMap['method']! as String, + requestHeaders: (pigeonMap['requestHeaders'] as Map?)! + .cast(), + ); } } class WebResourceErrorData { - int? errorCode; - String? description; + WebResourceErrorData({ + required this.errorCode, + required this.description, + }); + + int errorCode; + String description; Object encode() { final Map pigeonMap = {}; @@ -57,9 +71,10 @@ class WebResourceErrorData { static WebResourceErrorData decode(Object message) { final Map pigeonMap = message as Map; - return WebResourceErrorData() - ..errorCode = pigeonMap['errorCode'] as int? - ..description = pigeonMap['description'] as String?; + return WebResourceErrorData( + errorCode: pigeonMap['errorCode']! as int, + description: pigeonMap['description']! as String, + ); } } @@ -88,7 +103,6 @@ class CookieManagerHostApi { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -98,6 +112,11 @@ class CookieManagerHostApi { message: error['message'] as String?, details: error['details'], ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { return (replyMap['result'] as bool?)!; } @@ -108,12 +127,11 @@ class CookieManagerHostApi { 'dev.flutter.pigeon.CookieManagerHostApi.setCookie', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_url, arg_value]) as Map?; + .send([arg_url, arg_value]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -149,13 +167,12 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.create', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_useHybridComposition]) + await channel.send([arg_instanceId, arg_useHybridComposition]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -175,12 +192,11 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.dispose', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -196,18 +212,17 @@ class WebViewHostApi { } Future loadData(int arg_instanceId, String arg_data, - String arg_mimeType, String arg_encoding) async { + String? arg_mimeType, String? arg_encoding) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.loadData', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send( - [arg_instanceId, arg_data, arg_mimeType, arg_encoding]) + [arg_instanceId, arg_data, arg_mimeType, arg_encoding]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -224,15 +239,15 @@ class WebViewHostApi { Future loadDataWithBaseUrl( int arg_instanceId, - String arg_baseUrl, + String? arg_baseUrl, String arg_data, - String arg_mimeType, - String arg_encoding, - String arg_historyUrl) async { + String? arg_mimeType, + String? arg_encoding, + String? arg_historyUrl) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel.send([ + final Map? replyMap = await channel.send([ arg_instanceId, arg_baseUrl, arg_data, @@ -244,7 +259,6 @@ class WebViewHostApi { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -265,13 +279,12 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.loadUrl', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_url, arg_headers]) + await channel.send([arg_instanceId, arg_url, arg_headers]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -292,13 +305,12 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.postUrl', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_url, arg_data]) + await channel.send([arg_instanceId, arg_url, arg_data]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -313,17 +325,16 @@ class WebViewHostApi { } } - Future getUrl(int arg_instanceId) async { + Future getUrl(int arg_instanceId) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.getUrl', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -334,7 +345,7 @@ class WebViewHostApi { details: error['details'], ); } else { - return (replyMap['result'] as String?)!; + return (replyMap['result'] as String?); } } @@ -343,12 +354,11 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.canGoBack', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -358,6 +368,11 @@ class WebViewHostApi { message: error['message'] as String?, details: error['details'], ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { return (replyMap['result'] as bool?)!; } @@ -368,12 +383,11 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.canGoForward', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -383,6 +397,11 @@ class WebViewHostApi { message: error['message'] as String?, details: error['details'], ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { return (replyMap['result'] as bool?)!; } @@ -393,12 +412,11 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.goBack', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -418,12 +436,11 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.goForward', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -443,12 +460,11 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.reload', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -468,13 +484,12 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.clearCache', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_includeDiskFiles]) + await channel.send([arg_instanceId, arg_includeDiskFiles]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -489,19 +504,18 @@ class WebViewHostApi { } } - Future evaluateJavascript( + Future evaluateJavascript( int arg_instanceId, String arg_javascriptString) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.evaluateJavascript', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_javascriptString]) + await channel.send([arg_instanceId, arg_javascriptString]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -512,21 +526,20 @@ class WebViewHostApi { details: error['details'], ); } else { - return (replyMap['result'] as String?)!; + return (replyMap['result'] as String?); } } - Future getTitle(int arg_instanceId) async { + Future getTitle(int arg_instanceId) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.getTitle', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -537,7 +550,7 @@ class WebViewHostApi { details: error['details'], ); } else { - return (replyMap['result'] as String?)!; + return (replyMap['result'] as String?); } } @@ -545,13 +558,13 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.scrollTo', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_x, arg_y]) as Map?; + final Map? replyMap = + await channel.send([arg_instanceId, arg_x, arg_y]) + as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -570,13 +583,13 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.scrollBy', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_x, arg_y]) as Map?; + final Map? replyMap = + await channel.send([arg_instanceId, arg_x, arg_y]) + as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -596,12 +609,11 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.getScrollX', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -611,6 +623,11 @@ class WebViewHostApi { message: error['message'] as String?, details: error['details'], ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { return (replyMap['result'] as int?)!; } @@ -621,12 +638,11 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.getScrollY', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -636,6 +652,11 @@ class WebViewHostApi { message: error['message'] as String?, details: error['details'], ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { return (replyMap['result'] as int?)!; } @@ -647,12 +668,11 @@ class WebViewHostApi { codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_enabled]) as Map?; + await channel.send([arg_enabled]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -673,13 +693,12 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.setWebViewClient', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_webViewClientInstanceId]) + .send([arg_instanceId, arg_webViewClientInstanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -700,13 +719,12 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.addJavaScriptChannel', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_javaScriptChannelInstanceId]) + .send([arg_instanceId, arg_javaScriptChannelInstanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -727,13 +745,12 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.removeJavaScriptChannel', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_javaScriptChannelInstanceId]) + .send([arg_instanceId, arg_javaScriptChannelInstanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -749,18 +766,17 @@ class WebViewHostApi { } Future setDownloadListener( - int arg_instanceId, int arg_listenerInstanceId) async { + int arg_instanceId, int? arg_listenerInstanceId) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.setDownloadListener', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_listenerInstanceId]) + await channel.send([arg_instanceId, arg_listenerInstanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -776,18 +792,17 @@ class WebViewHostApi { } Future setWebChromeClient( - int arg_instanceId, int arg_clientInstanceId) async { + int arg_instanceId, int? arg_clientInstanceId) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.setWebChromeClient', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_clientInstanceId]) + await channel.send([arg_instanceId, arg_clientInstanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -807,12 +822,11 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.setBackgroundColor', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_color]) as Map?; + .send([arg_instanceId, arg_color]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -848,13 +862,12 @@ class WebSettingsHostApi { 'dev.flutter.pigeon.WebSettingsHostApi.create', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_webViewInstanceId]) + await channel.send([arg_instanceId, arg_webViewInstanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -874,12 +887,11 @@ class WebSettingsHostApi { 'dev.flutter.pigeon.WebSettingsHostApi.dispose', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -899,12 +911,11 @@ class WebSettingsHostApi { 'dev.flutter.pigeon.WebSettingsHostApi.setDomStorageEnabled', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_flag]) as Map?; + .send([arg_instanceId, arg_flag]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -926,12 +937,11 @@ class WebSettingsHostApi { codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_flag]) as Map?; + .send([arg_instanceId, arg_flag]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -953,12 +963,11 @@ class WebSettingsHostApi { codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_support]) as Map?; + .send([arg_instanceId, arg_support]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -978,12 +987,11 @@ class WebSettingsHostApi { 'dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptEnabled', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_flag]) as Map?; + .send([arg_instanceId, arg_flag]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -999,18 +1007,17 @@ class WebSettingsHostApi { } Future setUserAgentString( - int arg_instanceId, String arg_userAgentString) async { + int arg_instanceId, String? arg_userAgentString) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebSettingsHostApi.setUserAgentString', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_userAgentString]) + await channel.send([arg_instanceId, arg_userAgentString]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1032,12 +1039,11 @@ class WebSettingsHostApi { codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_require]) as Map?; + .send([arg_instanceId, arg_require]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1057,12 +1063,11 @@ class WebSettingsHostApi { 'dev.flutter.pigeon.WebSettingsHostApi.setSupportZoom', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_support]) as Map?; + .send([arg_instanceId, arg_support]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1082,13 +1087,13 @@ class WebSettingsHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebSettingsHostApi.setLoadWithOverviewMode', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_overview]) as Map?; + final Map? replyMap = + await channel.send([arg_instanceId, arg_overview]) + as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1108,12 +1113,11 @@ class WebSettingsHostApi { 'dev.flutter.pigeon.WebSettingsHostApi.setUseWideViewPort', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_use]) as Map?; + .send([arg_instanceId, arg_use]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1134,12 +1138,11 @@ class WebSettingsHostApi { 'dev.flutter.pigeon.WebSettingsHostApi.setDisplayZoomControls', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_enabled]) as Map?; + .send([arg_instanceId, arg_enabled]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1160,12 +1163,11 @@ class WebSettingsHostApi { 'dev.flutter.pigeon.WebSettingsHostApi.setBuiltInZoomControls', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_enabled]) as Map?; + .send([arg_instanceId, arg_enabled]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1185,12 +1187,11 @@ class WebSettingsHostApi { 'dev.flutter.pigeon.WebSettingsHostApi.setAllowFileAccess', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_enabled]) as Map?; + .send([arg_instanceId, arg_enabled]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1226,13 +1227,12 @@ class JavaScriptChannelHostApi { 'dev.flutter.pigeon.JavaScriptChannelHostApi.create', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_channelName]) + await channel.send([arg_instanceId, arg_channelName]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1325,13 +1325,12 @@ class WebViewClientHostApi { 'dev.flutter.pigeon.WebViewClientHostApi.create', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_shouldOverrideUrlLoading]) + .send([arg_instanceId, arg_shouldOverrideUrlLoading]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1600,12 +1599,11 @@ class DownloadListenerHostApi { 'dev.flutter.pigeon.DownloadListenerHostApi.create', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1712,13 +1710,12 @@ class WebChromeClientHostApi { 'dev.flutter.pigeon.WebChromeClientHostApi.create', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_webViewClientInstanceId]) + .send([arg_instanceId, arg_webViewClientInstanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1754,12 +1751,11 @@ class FlutterAssetManagerHostApi { 'dev.flutter.pigeon.FlutterAssetManagerHostApi.list', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_path]) as Map?; + await channel.send([arg_path]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1769,6 +1765,11 @@ class FlutterAssetManagerHostApi { message: error['message'] as String?, details: error['details'], ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { return (replyMap['result'] as List?)!.cast(); } @@ -1780,12 +1781,11 @@ class FlutterAssetManagerHostApi { codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_name]) as Map?; + await channel.send([arg_name]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1795,6 +1795,11 @@ class FlutterAssetManagerHostApi { message: error['message'] as String?, details: error['details'], ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { return (replyMap['result'] as String?)!; } @@ -1881,12 +1886,11 @@ class WebStorageHostApi { 'dev.flutter.pigeon.WebStorageHostApi.create', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1906,12 +1910,11 @@ class WebStorageHostApi { 'dev.flutter.pigeon.WebStorageHostApi.deleteAllData', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart index 9c980c80d58d..b40a0518dca0 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart @@ -13,21 +13,20 @@ import 'instance_manager.dart'; /// Converts [WebResourceRequestData] to [WebResourceRequest] WebResourceRequest _toWebResourceRequest(WebResourceRequestData data) { return WebResourceRequest( - url: data.url!, - isForMainFrame: data.isForMainFrame!, + url: data.url, + isForMainFrame: data.isForMainFrame, isRedirect: data.isRedirect, - hasGesture: data.hasGesture!, - method: data.method!, - requestHeaders: - data.requestHeaders?.cast() ?? {}, + hasGesture: data.hasGesture, + method: data.method, + requestHeaders: data.requestHeaders.cast(), ); } /// Converts [WebResourceErrorData] to [WebResourceError]. WebResourceError _toWebResourceError(WebResourceErrorData data) { return WebResourceError( - errorCode: data.errorCode!, - description: data.description!, + errorCode: data.errorCode, + description: data.description, ); } @@ -115,8 +114,8 @@ class WebViewHostApiImpl extends WebViewHostApi { Future loadDataFromInstance( WebView instance, String data, - String mimeType, - String encoding, + String? mimeType, + String? encoding, ) { return loadData( instanceManager.getInstanceId(instance)!, @@ -129,11 +128,11 @@ class WebViewHostApiImpl extends WebViewHostApi { /// Helper method to convert instances ids to objects. Future loadDataWithBaseUrlFromInstance( WebView instance, - String baseUrl, + String? baseUrl, String data, - String mimeType, - String encoding, - String historyUrl, + String? mimeType, + String? encoding, + String? historyUrl, ) { return loadDataWithBaseUrl( instanceManager.getInstanceId(instance)!, @@ -164,7 +163,7 @@ class WebViewHostApiImpl extends WebViewHostApi { } /// Helper method to convert instances ids to objects. - Future getUrlFromInstance(WebView instance) { + Future getUrlFromInstance(WebView instance) { return getUrl(instanceManager.getInstanceId(instance)!); } @@ -202,16 +201,18 @@ class WebViewHostApiImpl extends WebViewHostApi { } /// Helper method to convert instances ids to objects. - Future evaluateJavascriptFromInstance( + Future evaluateJavascriptFromInstance( WebView instance, String javascriptString, ) { return evaluateJavascript( - instanceManager.getInstanceId(instance)!, javascriptString); + instanceManager.getInstanceId(instance)!, + javascriptString, + ); } /// Helper method to convert instances ids to objects. - Future getTitleFromInstance(WebView instance) { + Future getTitleFromInstance(WebView instance) { return getTitle(instanceManager.getInstanceId(instance)!); } @@ -271,22 +272,22 @@ class WebViewHostApiImpl extends WebViewHostApi { /// Helper method to convert instances ids to objects. Future setDownloadListenerFromInstance( WebView instance, - DownloadListener listener, + DownloadListener? listener, ) { return setDownloadListener( instanceManager.getInstanceId(instance)!, - instanceManager.getInstanceId(listener)!, + listener != null ? instanceManager.getInstanceId(listener) : null, ); } /// Helper method to convert instances ids to objects. Future setWebChromeClientFromInstance( WebView instance, - WebChromeClient client, + WebChromeClient? client, ) { return setWebChromeClient( instanceManager.getInstanceId(instance)!, - instanceManager.getInstanceId(client)!, + client != null ? instanceManager.getInstanceId(client) : null, ); } @@ -370,7 +371,7 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { /// Helper method to convert instances ids to objects. Future setUserAgentStringFromInstance( WebSettings instance, - String userAgentString, + String? userAgentString, ) { return setUserAgentString( instanceManager.getInstanceId(instance)!, diff --git a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart index d3d18f64f97d..70ecd99d3638 100644 --- a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart +++ b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart @@ -4,18 +4,51 @@ import 'package:pigeon/pigeon.dart'; +@ConfigurePigeon( + PigeonOptions( + dartOut: 'lib/src/android_webview.pigeon.dart', + dartTestOut: 'test/test_android_webview.pigeon.dart', + dartOptions: DartOptions(copyrightHeader: [ + 'Copyright 2013 The Flutter Authors. All rights reserved.', + 'Use of this source code is governed by a BSD-style license that can be', + 'found in the LICENSE file.', + ]), + javaOut: + 'android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java', + javaOptions: JavaOptions( + package: 'io.flutter.plugins.webviewflutter', + className: 'GeneratedAndroidWebView', + copyrightHeader: [ + 'Copyright 2013 The Flutter Authors. All rights reserved.', + 'Use of this source code is governed by a BSD-style license that can be', + 'found in the LICENSE file.', + ], + ), + ), +) class WebResourceRequestData { - String? url; - bool? isForMainFrame; + WebResourceRequestData( + this.url, + this.isForMainFrame, + this.isRedirect, + this.hasGesture, + this.method, + this.requestHeaders, + ); + + String url; + bool isForMainFrame; bool? isRedirect; - bool? hasGesture; - String? method; - Map? requestHeaders; + bool hasGesture; + String method; + Map requestHeaders; } class WebResourceErrorData { - int? errorCode; - String? description; + WebResourceErrorData(this.errorCode, this.description); + + int errorCode; + String description; } @HostApi() @@ -35,17 +68,17 @@ abstract class WebViewHostApi { void loadData( int instanceId, String data, - String mimeType, - String encoding, + String? mimeType, + String? encoding, ); void loadDataWithBaseUrl( int instanceId, - String baseUrl, + String? baseUrl, String data, - String mimeType, - String encoding, - String historyUrl, + String? mimeType, + String? encoding, + String? historyUrl, ); void loadUrl( @@ -60,7 +93,7 @@ abstract class WebViewHostApi { Uint8List data, ); - String getUrl(int instanceId); + String? getUrl(int instanceId); bool canGoBack(int instanceId); @@ -75,12 +108,12 @@ abstract class WebViewHostApi { void clearCache(int instanceId, bool includeDiskFiles); @async - String evaluateJavascript( + String? evaluateJavascript( int instanceId, String javascriptString, ); - String getTitle(int instanceId); + String? getTitle(int instanceId); void scrollTo(int instanceId, int x, int y); @@ -98,9 +131,9 @@ abstract class WebViewHostApi { void removeJavaScriptChannel(int instanceId, int javaScriptChannelInstanceId); - void setDownloadListener(int instanceId, int listenerInstanceId); + void setDownloadListener(int instanceId, int? listenerInstanceId); - void setWebChromeClient(int instanceId, int clientInstanceId); + void setWebChromeClient(int instanceId, int? clientInstanceId); void setBackgroundColor(int instanceId, int color); } @@ -119,7 +152,7 @@ abstract class WebSettingsHostApi { void setJavaScriptEnabled(int instanceId, bool flag); - void setUserAgentString(int instanceId, String userAgentString); + void setUserAgentString(int instanceId, String? userAgentString); void setMediaPlaybackRequiresUserGesture(int instanceId, bool require); diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 3fae698d1481..9a7c48a4dcd8 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_android description: A Flutter plugin that provides a WebView widget on Android. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.8.5 +version: 2.8.6 environment: sdk: ">=2.14.0 <3.0.0" @@ -29,4 +29,4 @@ dev_dependencies: sdk: flutter mockito: ^5.1.0 pedantic: ^1.10.0 - pigeon: 1.0.9 + pigeon: ^3.0.3 diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart index e2e6513dd841..4c63ab025702 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart @@ -10,8 +10,8 @@ import 'package:webview_flutter_android/src/android_webview.pigeon.dart'; import 'package:webview_flutter_android/src/android_webview_api_impls.dart'; import 'package:webview_flutter_android/src/instance_manager.dart'; -import 'android_webview.pigeon.dart'; import 'android_webview_test.mocks.dart'; +import 'test_android_webview.pigeon.dart'; @GenerateMocks([ CookieManagerHostApi, @@ -85,8 +85,8 @@ void main() { verify(mockPlatformHostApi.loadData( webViewInstanceId, 'hello', - '', - '', + null, + null, )); }); @@ -113,11 +113,11 @@ void main() { webView.loadDataWithBaseUrl(data: 'hello'); verify(mockPlatformHostApi.loadDataWithBaseUrl( webViewInstanceId, - '', + null, 'hello', - '', - '', - '', + null, + null, + null, )); }); @@ -527,14 +527,15 @@ void main() { flutterApi.onReceivedRequestError( mockWebViewClientInstanceId, mockWebViewInstanceId, - WebResourceRequestData() - ..url = 'https://www.google.com' - ..isForMainFrame = true - ..hasGesture = true - ..method = 'POST', - WebResourceErrorData() - ..errorCode = 34 - ..description = 'error description', + WebResourceRequestData( + url: 'https://www.google.com', + isForMainFrame: true, + hasGesture: true, + method: 'POST', + isRedirect: false, + requestHeaders: {}, + ), + WebResourceErrorData(errorCode: 34, description: 'error description'), ); verify(mockWebViewClient.onReceivedRequestError( @@ -565,11 +566,14 @@ void main() { flutterApi.requestLoading( mockWebViewClientInstanceId, mockWebViewInstanceId, - WebResourceRequestData() - ..url = 'https://www.google.com' - ..isForMainFrame = true - ..hasGesture = true - ..method = 'POST', + WebResourceRequestData( + url: 'https://www.google.com', + isForMainFrame: true, + hasGesture: true, + method: 'POST', + isRedirect: true, + requestHeaders: {}, + ), ); verify(mockWebViewClient.requestLoading( diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart index e8859c9b74c5..85ab6685ca34 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart @@ -10,7 +10,7 @@ import 'package:mockito/mockito.dart' as _i1; import 'package:webview_flutter_android/src/android_webview.dart' as _i2; import 'package:webview_flutter_android/src/android_webview.pigeon.dart' as _i3; -import 'android_webview.pigeon.dart' as _i5; +import 'test_android_webview.pigeon.dart' as _i5; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -277,9 +277,8 @@ class MockTestWebViewHostApi extends _i1.Mock super.noSuchMethod(Invocation.method(#postUrl, [instanceId, url, data]), returnValueForMissingStub: null); @override - String getUrl(int? instanceId) => - (super.noSuchMethod(Invocation.method(#getUrl, [instanceId]), - returnValue: '') as String); + String? getUrl(int? instanceId) => + (super.noSuchMethod(Invocation.method(#getUrl, [instanceId])) as String?); @override bool canGoBack(int? instanceId) => (super.noSuchMethod(Invocation.method(#canGoBack, [instanceId]), @@ -306,16 +305,16 @@ class MockTestWebViewHostApi extends _i1.Mock Invocation.method(#clearCache, [instanceId, includeDiskFiles]), returnValueForMissingStub: null); @override - _i4.Future evaluateJavascript( + _i4.Future evaluateJavascript( int? instanceId, String? javascriptString) => (super.noSuchMethod( Invocation.method( #evaluateJavascript, [instanceId, javascriptString]), - returnValue: Future.value('')) as _i4.Future); + returnValue: Future.value()) as _i4.Future); @override - String getTitle(int? instanceId) => - (super.noSuchMethod(Invocation.method(#getTitle, [instanceId]), - returnValue: '') as String); + String? getTitle(int? instanceId) => + (super.noSuchMethod(Invocation.method(#getTitle, [instanceId])) + as String?); @override void scrollTo(int? instanceId, int? x, int? y) => super.noSuchMethod(Invocation.method(#scrollTo, [instanceId, x, y]), diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart similarity index 95% rename from packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart rename to packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart index 4ee4b1ddc0b7..e3c5909f3207 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart @@ -1,10 +1,10 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - -// Autogenerated from Pigeon (v1.0.9), do not edit directly. +// Autogenerated from Pigeon (v3.0.3), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis +// ignore_for_file: avoid_relative_lib_imports // @dart = 2.12 import 'dart:async'; import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; @@ -23,20 +23,21 @@ abstract class TestWebViewHostApi { void create(int instanceId, bool useHybridComposition); void dispose(int instanceId); - void loadData(int instanceId, String data, String mimeType, String encoding); - void loadDataWithBaseUrl(int instanceId, String baseUrl, String data, - String mimeType, String encoding, String historyUrl); + void loadData( + int instanceId, String data, String? mimeType, String? encoding); + void loadDataWithBaseUrl(int instanceId, String? baseUrl, String data, + String? mimeType, String? encoding, String? historyUrl); void loadUrl(int instanceId, String url, Map headers); void postUrl(int instanceId, String url, Uint8List data); - String getUrl(int instanceId); + String? getUrl(int instanceId); bool canGoBack(int instanceId); bool canGoForward(int instanceId); void goBack(int instanceId); void goForward(int instanceId); void reload(int instanceId); void clearCache(int instanceId, bool includeDiskFiles); - Future evaluateJavascript(int instanceId, String javascriptString); - String getTitle(int instanceId); + Future evaluateJavascript(int instanceId, String javascriptString); + String? getTitle(int instanceId); void scrollTo(int instanceId, int x, int y); void scrollBy(int instanceId, int x, int y); int getScrollX(int instanceId); @@ -45,8 +46,8 @@ abstract class TestWebViewHostApi { void setWebViewClient(int instanceId, int webViewClientInstanceId); void addJavaScriptChannel(int instanceId, int javaScriptChannelInstanceId); void removeJavaScriptChannel(int instanceId, int javaScriptChannelInstanceId); - void setDownloadListener(int instanceId, int listenerInstanceId); - void setWebChromeClient(int instanceId, int clientInstanceId); + void setDownloadListener(int instanceId, int? listenerInstanceId); + void setWebChromeClient(int instanceId, int? clientInstanceId); void setBackgroundColor(int instanceId, int color); static void setup(TestWebViewHostApi? api, {BinaryMessenger? binaryMessenger}) { @@ -109,13 +110,8 @@ abstract class TestWebViewHostApi { assert(arg_data != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.loadData was null, expected non-null String.'); final String? arg_mimeType = (args[2] as String?); - assert(arg_mimeType != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.loadData was null, expected non-null String.'); final String? arg_encoding = (args[3] as String?); - assert(arg_encoding != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.loadData was null, expected non-null String.'); - api.loadData( - arg_instanceId!, arg_data!, arg_mimeType!, arg_encoding!); + api.loadData(arg_instanceId!, arg_data!, arg_mimeType, arg_encoding); return {}; }); } @@ -135,22 +131,14 @@ abstract class TestWebViewHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl was null, expected non-null int.'); final String? arg_baseUrl = (args[1] as String?); - assert(arg_baseUrl != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl was null, expected non-null String.'); final String? arg_data = (args[2] as String?); assert(arg_data != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl was null, expected non-null String.'); final String? arg_mimeType = (args[3] as String?); - assert(arg_mimeType != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl was null, expected non-null String.'); final String? arg_encoding = (args[4] as String?); - assert(arg_encoding != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl was null, expected non-null String.'); final String? arg_historyUrl = (args[5] as String?); - assert(arg_historyUrl != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl was null, expected non-null String.'); - api.loadDataWithBaseUrl(arg_instanceId!, arg_baseUrl!, arg_data!, - arg_mimeType!, arg_encoding!, arg_historyUrl!); + api.loadDataWithBaseUrl(arg_instanceId!, arg_baseUrl, arg_data!, + arg_mimeType, arg_encoding, arg_historyUrl); return {}; }); } @@ -220,7 +208,7 @@ abstract class TestWebViewHostApi { final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.getUrl was null, expected non-null int.'); - final String output = api.getUrl(arg_instanceId!); + final String? output = api.getUrl(arg_instanceId!); return {'result': output}; }); } @@ -359,7 +347,7 @@ abstract class TestWebViewHostApi { final String? arg_javascriptString = (args[1] as String?); assert(arg_javascriptString != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.evaluateJavascript was null, expected non-null String.'); - final String output = await api.evaluateJavascript( + final String? output = await api.evaluateJavascript( arg_instanceId!, arg_javascriptString!); return {'result': output}; }); @@ -379,7 +367,7 @@ abstract class TestWebViewHostApi { final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.getTitle was null, expected non-null int.'); - final String output = api.getTitle(arg_instanceId!); + final String? output = api.getTitle(arg_instanceId!); return {'result': output}; }); } @@ -575,9 +563,7 @@ abstract class TestWebViewHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setDownloadListener was null, expected non-null int.'); final int? arg_listenerInstanceId = (args[1] as int?); - assert(arg_listenerInstanceId != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.setDownloadListener was null, expected non-null int.'); - api.setDownloadListener(arg_instanceId!, arg_listenerInstanceId!); + api.setDownloadListener(arg_instanceId!, arg_listenerInstanceId); return {}; }); } @@ -597,9 +583,7 @@ abstract class TestWebViewHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setWebChromeClient was null, expected non-null int.'); final int? arg_clientInstanceId = (args[1] as int?); - assert(arg_clientInstanceId != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.setWebChromeClient was null, expected non-null int.'); - api.setWebChromeClient(arg_instanceId!, arg_clientInstanceId!); + api.setWebChromeClient(arg_instanceId!, arg_clientInstanceId); return {}; }); } @@ -642,7 +626,7 @@ abstract class TestWebSettingsHostApi { void setJavaScriptCanOpenWindowsAutomatically(int instanceId, bool flag); void setSupportMultipleWindows(int instanceId, bool support); void setJavaScriptEnabled(int instanceId, bool flag); - void setUserAgentString(int instanceId, String userAgentString); + void setUserAgentString(int instanceId, String? userAgentString); void setMediaPlaybackRequiresUserGesture(int instanceId, bool require); void setSupportZoom(int instanceId, bool support); void setLoadWithOverviewMode(int instanceId, bool overview); @@ -799,9 +783,7 @@ abstract class TestWebSettingsHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setUserAgentString was null, expected non-null int.'); final String? arg_userAgentString = (args[1] as String?); - assert(arg_userAgentString != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setUserAgentString was null, expected non-null String.'); - api.setUserAgentString(arg_instanceId!, arg_userAgentString!); + api.setUserAgentString(arg_instanceId!, arg_userAgentString); return {}; }); } diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart index 46d6a29a1107..83662fb81f02 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart @@ -16,8 +16,8 @@ import 'package:webview_flutter_android/src/instance_manager.dart'; import 'package:webview_flutter_android/webview_android_widget.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; -import 'android_webview.pigeon.dart'; import 'android_webview_test.mocks.dart' show MockTestWebViewHostApi; +import 'test_android_webview.pigeon.dart'; import 'webview_android_widget_test.mocks.dart'; @GenerateMocks([ From 4b687c9fa75e97b219a7017720378fc7bb0f5b12 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Thu, 28 Apr 2022 14:16:04 -0700 Subject: [PATCH 457/600] [video_player] Fix XCUITest based on the new tooltip accessibility label (#5426) The accessibility label for tool tip seems change in one of the recent flutter updates(flutter/flutter#87684), which results failure in this particular XCUITest. This is blocking the flutter to plugins roll. Fixes: flutter/flutter#102698 --- .../video_player_avfoundation/CHANGELOG.md | 5 +++++ .../example/ios/RunnerUITests/VideoPlayerUITests.m | 12 ++++++++---- .../video_player_avfoundation/pubspec.yaml | 2 +- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index a1f23eb670fc..3916503ce3d9 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.3.3 + +* Fix XCUITest based on the new voice over announcement for tooltips. + See: https://github.com/flutter/flutter/pull/87684 + ## 2.3.2 * Applies the standardized transform for videos with different orientations. diff --git a/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m b/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m index 2933cf36feae..b9f0f16bb27b 100644 --- a/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m +++ b/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m @@ -30,16 +30,20 @@ - (void)testPlayVideo { XCTAssertTrue([playButton waitForExistenceWithTimeout:30.0]); [playButton tap]; - XCUIElement *playbackSpeed1x = app.staticTexts[@"Playback speed\n1.0x"]; - XCTAssertTrue([playbackSpeed1x waitForExistenceWithTimeout:30.0]); + NSPredicate *find1xButton = [NSPredicate predicateWithFormat:@"label CONTAINS '1.0x'"]; + XCUIElement *playbackSpeed1x = [app.staticTexts elementMatchingPredicate:find1xButton]; + BOOL foundPlaybackSpeed1x = [playbackSpeed1x waitForExistenceWithTimeout:30.0]; + XCTAssertTrue(foundPlaybackSpeed1x); [playbackSpeed1x tap]; XCUIElement *playbackSpeed5xButton = app.buttons[@"5.0x"]; XCTAssertTrue([playbackSpeed5xButton waitForExistenceWithTimeout:30.0]); [playbackSpeed5xButton tap]; - XCUIElement *playbackSpeed5x = app.staticTexts[@"Playback speed\n5.0x"]; - XCTAssertTrue([playbackSpeed5x waitForExistenceWithTimeout:30.0]); + NSPredicate *find5xButton = [NSPredicate predicateWithFormat:@"label CONTAINS '5.0x'"]; + XCUIElement *playbackSpeed5x = [app.staticTexts elementMatchingPredicate:find5xButton]; + BOOL foundPlaybackSpeed5x = [playbackSpeed5x waitForExistenceWithTimeout:30.0]; + XCTAssertTrue(foundPlaybackSpeed5x); // Cycle through tabs. for (NSString *tabName in @[ @"Asset", @"Remote" ]) { diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index 5874b52cba6f..b3cc69eca958 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_avfoundation description: iOS implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.2 +version: 2.3.3 environment: sdk: ">=2.14.0 <3.0.0" From e777e515b5f09f13015f5bec9c1d909339bfa65b Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 28 Apr 2022 18:24:16 -0400 Subject: [PATCH 458/600] Roll Flutter from 1b58a593deac to 4cea9afc1224 (147 revisions) (#5437) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 23335129b903..d7dca8a86441 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -1b58a593deac48f1c3a6f579d53a0401372dc59f +4cea9afc12243850d4776003349fb9cff4c031fc From 656e8c44346f920fe2455c6e4d76058c45e2c606 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 28 Apr 2022 21:34:11 -0400 Subject: [PATCH 459/600] Roll Flutter from 4cea9afc1224 to 5b713147404d (4 revisions) (#5441) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index d7dca8a86441..6aede9cabd01 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -4cea9afc12243850d4776003349fb9cff4c031fc +5b713147404d076f1a955bdb0becdd5fca816ed1 From f689280bf756d65cb9c9f17fb50f3be4c78af4a3 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 28 Apr 2022 22:29:09 -0400 Subject: [PATCH 460/600] [flutter_plugin_tools] Validate code blocks in readme-check (#5436) --- .cirrus.yml | 8 +- packages/camera/camera/README.md | 2 +- packages/espresso/README.md | 4 +- .../file_selector/file_selector/README.md | 4 +- .../file_selector_macos/README.md | 4 +- .../google_sign_in_web/README.md | 2 +- packages/url_launcher/url_launcher/README.md | 2 +- script/configs/temp_exclude_excerpt.yaml | 27 ++++ script/tool/CHANGELOG.md | 7 + script/tool/lib/src/readme_check_command.dart | 93 +++++++++++-- script/tool/pubspec.yaml | 2 +- .../tool/test/readme_check_command_test.dart | 131 ++++++++++++++++++ 12 files changed, 266 insertions(+), 20 deletions(-) create mode 100644 script/configs/temp_exclude_excerpt.yaml diff --git a/.cirrus.yml b/.cirrus.yml index c256ab19426e..fe5a18097c80 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -101,7 +101,13 @@ task: always: format_script: ./script/tool_runner.sh format --fail-on-change pubspec_script: ./script/tool_runner.sh pubspec-check - readme_script: ./script/tool_runner.sh readme-check + 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 license_script: dart $PLUGIN_TOOL license-check - name: federated_safety # This check is only meaningful for PRs, as it validates changes diff --git a/packages/camera/camera/README.md b/packages/camera/camera/README.md index a1c60a08950d..97b16d20f48a 100644 --- a/packages/camera/camera/README.md +++ b/packages/camera/camera/README.md @@ -46,7 +46,7 @@ If editing `Info.plist` as text, add: Change the minimum Android sdk version to 21 (or higher) in your `android/app/build.gradle` file. -``` +```groovy minSdkVersion 21 ``` diff --git a/packages/espresso/README.md b/packages/espresso/README.md index 68c3b55089ca..6d66bfbe85b5 100644 --- a/packages/espresso/README.md +++ b/packages/espresso/README.md @@ -85,13 +85,13 @@ void main() { The following command line command runs the test locally: -``` +```sh ./gradlew app:connectedAndroidTest -Ptarget=`pwd`/../test_driver/example.dart ``` Espresso tests can also be run on [Firebase Test Lab](https://firebase.google.com/docs/test-lab): -``` +```sh ./gradlew app:assembleAndroidTest ./gradlew app:assembleDebug -Ptarget=.dart gcloud auth activate-service-account --key-file= diff --git a/packages/file_selector/file_selector/README.md b/packages/file_selector/file_selector/README.md index 544cde8218bd..89cac1e6fd5f 100644 --- a/packages/file_selector/file_selector/README.md +++ b/packages/file_selector/file_selector/README.md @@ -14,12 +14,12 @@ To use this plugin, add `file_selector` as a [dependency in your pubspec.yaml fi ### macOS You will need to [add an entitlement][entitlement] for either read-only access: -``` +```xml com.apple.security.files.user-selected.read-only ``` or read/write access: -``` +```xml com.apple.security.files.user-selected.read-write ``` diff --git a/packages/file_selector/file_selector_macos/README.md b/packages/file_selector/file_selector_macos/README.md index efa5272149be..3241b21d1e18 100644 --- a/packages/file_selector/file_selector_macos/README.md +++ b/packages/file_selector/file_selector_macos/README.md @@ -17,12 +17,12 @@ APIs directly. ### Entitlements You will need to [add an entitlement][4] for either read-only access: -``` +```xml com.apple.security.files.user-selected.read-only ``` or read/write access: -``` +```xml com.apple.security.files.user-selected.read-write ``` diff --git a/packages/google_sign_in/google_sign_in_web/README.md b/packages/google_sign_in/google_sign_in_web/README.md index 4ee1a2956b45..463603e73e54 100644 --- a/packages/google_sign_in/google_sign_in_web/README.md +++ b/packages/google_sign_in/google_sign_in_web/README.md @@ -37,7 +37,7 @@ Normally `flutter run` starts in a random port. In the case where you need to de You can tell `flutter run` to listen for requests in a specific host and port with the following: -``` +```sh flutter run -d chrome --web-hostname localhost --web-port 7357 ``` diff --git a/packages/url_launcher/url_launcher/README.md b/packages/url_launcher/url_launcher/README.md index 0cdbe1b9859e..9c9f0b57e667 100644 --- a/packages/url_launcher/url_launcher/README.md +++ b/packages/url_launcher/url_launcher/README.md @@ -46,7 +46,7 @@ See the example app for more complex examples. Add any URL schemes passed to `canLaunchUrl` as `LSApplicationQueriesSchemes` entries in your Info.plist file. Example: -``` +```xml LSApplicationQueriesSchemes https diff --git a/script/configs/temp_exclude_excerpt.yaml b/script/configs/temp_exclude_excerpt.yaml new file mode 100644 index 000000000000..fc8454d75a0c --- /dev/null +++ b/script/configs/temp_exclude_excerpt.yaml @@ -0,0 +1,27 @@ +# Packages that have not yet adopted code-excerpt. +# +# This only exists to allow incrementally adopting the new requirement. +# Packages shoud never be added to this list. + +# TODO(ecosystem): Remove everything from this list. See +# https://github.com/flutter/flutter/issues/102679 +- camera_web +- espresso +- file_selector/file_selector +- google_maps_flutter/google_maps_flutter +- google_sign_in/google_sign_in +- google_sign_in_web +- image_picker/image_picker +- image_picker_for_web +- in_app_purchase/in_app_purchase +- ios_platform_images +- local_auth/local_auth +- path_provider/path_provider +- plugin_platform_interface +- quick_actions/quick_actions +- shared_preferences/shared_preferences +- url_launcher/url_launcher +- video_player/video_player +- webview_flutter/webview_flutter +- webview_flutter_android +- webview_flutter_web diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 7587ff33f027..1bce029a559f 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,10 @@ +## 0.8.4 + +- `readme-check` now validates that there's a info tag on code blocks to + identify (and for supported languages, syntax highlight) the language. +- `readme-check` now has a `--require-excerpts` flag to require that any Dart + code blocks be managed by `code_excerpter`. + ## 0.8.3 - Adds a new `update-excerpts` command to maintain README files using the diff --git a/script/tool/lib/src/readme_check_command.dart b/script/tool/lib/src/readme_check_command.dart index 99e271c73388..9432c4b7fa40 100644 --- a/script/tool/lib/src/readme_check_command.dart +++ b/script/tool/lib/src/readme_check_command.dart @@ -26,7 +26,12 @@ class ReadmeCheckCommand extends PackageLoopingCommand { processRunner: processRunner, platform: platform, gitDir: gitDir, - ); + ) { + argParser.addFlag(_requireExcerptsArg, + help: 'Require that Dart code blocks be managed by code-excerpt.'); + } + + static const String _requireExcerptsArg = 'require-excerpts'; // Standardized capitalizations for platforms that a plugin can support. static const Map _standardPlatformNames = { @@ -61,8 +66,15 @@ class ReadmeCheckCommand extends PackageLoopingCommand { final Pubspec pubspec = package.parsePubspec(); final bool isPlugin = pubspec.flutter?['plugin'] != null; + final List readmeLines = package.readmeFile.readAsLinesSync(); + + final String? blockValidationError = _validateCodeBlocks(readmeLines); + if (blockValidationError != null) { + errors.add(blockValidationError); + } + if (isPlugin && (!package.isFederated || package.isAppFacing)) { - final String? error = _validateSupportedPlatforms(package, pubspec); + final String? error = _validateSupportedPlatforms(readmeLines, pubspec); if (error != null) { errors.add(error); } @@ -73,23 +85,86 @@ class ReadmeCheckCommand extends PackageLoopingCommand { : PackageResult.fail(errors); } + /// Validates that code blocks (``` ... ```) follow repository standards. + String? _validateCodeBlocks(List readmeLines) { + final RegExp codeBlockDelimiterPattern = RegExp(r'^\s*```\s*([^ ]*)\s*'); + final List missingLanguageLines = []; + final List missingExcerptLines = []; + bool inBlock = false; + for (int i = 0; i < readmeLines.length; ++i) { + final RegExpMatch? match = + codeBlockDelimiterPattern.firstMatch(readmeLines[i]); + if (match == null) { + continue; + } + if (inBlock) { + inBlock = false; + continue; + } + inBlock = true; + + final int humanReadableLineNumber = i + 1; + + // Ensure that there's a language tag. + final String infoString = match[1] ?? ''; + if (infoString.isEmpty) { + missingLanguageLines.add(humanReadableLineNumber); + continue; + } + + // Check for code-excerpt usage if requested. + if (getBoolArg(_requireExcerptsArg) && infoString == 'dart') { + const String excerptTagStart = ' ' + 'tag on the previous line, and ensure that a build.excerpt.yaml is ' + 'configured for the source example.\n'); + errorSummary ??= 'Missing code-excerpt management for code block'; + } + + return errorSummary; + } + /// Validates that the plugin has a supported platforms table following the /// expected format, returning an error string if any issues are found. String? _validateSupportedPlatforms( - RepositoryPackage package, Pubspec pubspec) { - final List contents = package.readmeFile.readAsLinesSync(); - + List readmeLines, Pubspec pubspec) { // Example table following expected format: // | | Android | iOS | Web | // |----------------|---------|----------|------------------------| // | **Support** | SDK 21+ | iOS 10+* | [See `camera_web `][1] | - final int detailsLineNumber = - contents.indexWhere((String line) => line.startsWith('| **Support**')); + final int detailsLineNumber = readmeLines + .indexWhere((String line) => line.startsWith('| **Support**')); if (detailsLineNumber == -1) { return 'No OS support table found'; } final int osLineNumber = detailsLineNumber - 2; - if (osLineNumber < 0 || !contents[osLineNumber].startsWith('|')) { + if (osLineNumber < 0 || !readmeLines[osLineNumber].startsWith('|')) { return 'OS support table does not have the expected header format'; } @@ -111,7 +186,7 @@ class ReadmeCheckCommand extends PackageLoopingCommand { final YamlMap platformSupportMaps = platformsEntry as YamlMap; final Set actuallySupportedPlatform = platformSupportMaps.keys.toSet().cast(); - final Iterable documentedPlatforms = contents[osLineNumber] + final Iterable documentedPlatforms = readmeLines[osLineNumber] .split('|') .map((String entry) => entry.trim()) .where((String entry) => entry.isNotEmpty); diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index 9f9910f934f7..af38193294a5 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.8.3 +version: 0.8.4 dependencies: args: ^2.1.0 diff --git a/script/tool/test/readme_check_command_test.dart b/script/tool/test/readme_check_command_test.dart index aec2fa078454..b6e016dccab4 100644 --- a/script/tool/test/readme_check_command_test.dart +++ b/script/tool/test/readme_check_command_test.dart @@ -275,4 +275,135 @@ A very useful plugin. ); }); }); + + group('code blocks', () { + test('fails on missing info string', () async { + final Directory packageDir = createFakePackage('a_package', packagesDir); + + packageDir.childFile('README.md').writeAsStringSync(''' +Example: + +``` +void main() { + // ... +} +``` +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('Code block at line 3 is missing a language identifier.'), + contains('Missing language identifier for code block'), + ]), + ); + }); + + test('allows unknown info strings', () async { + final Directory packageDir = createFakePackage('a_package', packagesDir); + + packageDir.childFile('README.md').writeAsStringSync(''' +Example: + +```someunknowninfotag +A B C +``` +'''); + + final List output = await runCapturingPrint(runner, [ + 'readme-check', + ]); + + expect( + output, + containsAll([ + contains('Running for a_package...'), + contains('No issues found!'), + ]), + ); + }); + + test('allows space around info strings', () async { + final Directory packageDir = createFakePackage('a_package', packagesDir); + + packageDir.childFile('README.md').writeAsStringSync(''' +Example: + +``` dart +A B C +``` +'''); + + final List output = await runCapturingPrint(runner, [ + 'readme-check', + ]); + + expect( + output, + containsAll([ + contains('Running for a_package...'), + contains('No issues found!'), + ]), + ); + }); + + test('passes when excerpt requirement is met', () async { + final Directory packageDir = createFakePackage('a_package', packagesDir); + + packageDir.childFile('README.md').writeAsStringSync(''' +Example: + + +```dart +A B C +``` +'''); + + final List output = await runCapturingPrint( + runner, ['readme-check', '--require-excerpts']); + + expect( + output, + containsAll([ + contains('Running for a_package...'), + contains('No issues found!'), + ]), + ); + }); + + test('fails on missing excerpt tag when requested', () async { + final Directory packageDir = createFakePackage('a_package', packagesDir); + + packageDir.childFile('README.md').writeAsStringSync(''' +Example: + +```dart +A B C +``` +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check', '--require-excerpts'], + errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('Dart code block at line 3 is not managed by code-excerpt.'), + contains('Missing code-excerpt management for code block'), + ]), + ); + }); + }); } From 9c41c6895803a6174eb79b96b9f94e1b09854d39 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 28 Apr 2022 23:09:08 -0400 Subject: [PATCH 461/600] Roll Flutter from 5b713147404d to 7a74222832f8 (1 revision) (#5442) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 6aede9cabd01..10fc1313c8eb 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -5b713147404d076f1a955bdb0becdd5fca816ed1 +7a74222832f8da20cd7e2d202bc83089e8a7d123 From 160c714e75f3933f4c883ecd0e79ada6f729c27c Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 29 Apr 2022 00:14:09 -0400 Subject: [PATCH 462/600] Roll Flutter from 7a74222832f8 to 2eed8cbf9370 (1 revision) (#5443) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 10fc1313c8eb..2340a040f39e 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -7a74222832f8da20cd7e2d202bc83089e8a7d123 +2eed8cbf937029f140c5bfbba920ff838e7d4871 From b247855a3b3e71886eba865cabc31becb013221e Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 29 Apr 2022 14:05:13 -0400 Subject: [PATCH 463/600] [ci] Don't run Cirrus tests on `master` (#5444) Addresses https://github.com/flutter/flutter/issues/102801 for this repository. --- .cirrus.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index fe5a18097c80..c26118d1443f 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -1,8 +1,9 @@ gcp_credentials: ENCRYPTED[!2c88dee9c9d9805b214c9f7ad8f3bc8fae936cdb0f881d562101151c408c7e024a41222677d5831df90c60d2dd6cd80a!] # Don't run on release tags since it creates O(n^2) tasks where n is the -# number of plugins -only_if: $CIRRUS_TAG == '' +# number of plugins. +# Don't run on 'master' since it's a mirror of 'main'. +only_if: $CIRRUS_TAG == '' && $CIRRUS_BRANCH != 'master' env: CHANNEL: "master" # Default to master when not explicitly set by a task. PLUGIN_TOOL: "./script/tool/bin/flutter_plugin_tools.dart" From 48cfd2a15b5b8c80ae7db7a9cdab1fb330b79e6a Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 29 Apr 2022 14:49:09 -0400 Subject: [PATCH 464/600] Roll Flutter from 2eed8cbf9370 to ad1d30017e69 (1 revision) (#5445) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 2340a040f39e..a7787d42ee77 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2eed8cbf937029f140c5bfbba920ff838e7d4871 +ad1d30017e69f91983edaf9bd6dae2a9f1c7b698 From 7a741bf0b51ecc30f8f3a9b03829d4bbcb661bd2 Mon Sep 17 00:00:00 2001 From: Nikolas Mayr Date: Fri, 29 Apr 2022 21:34:10 +0200 Subject: [PATCH 465/600] [webview_flutter] fix warning (#5131) --- .../webview_flutter_wkwebview/CHANGELOG.md | 4 +++ .../ios/Classes/FlutterWebView.m | 35 ++++++------------- .../webview_flutter_wkwebview/pubspec.yaml | 2 +- 3 files changed, 16 insertions(+), 25 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index bad900de46b4..058e8645dd7b 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.7.3 + +* Removes two occurrences of the compiler warning: "'RequiresUserActionForMediaPlayback' is deprecated: first deprecated in ios 10.0". + ## 2.7.2 * Fixes an integration test race condition. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m index 61f112019686..5bb81fce89db 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m @@ -449,19 +449,14 @@ - (void)onRemoveJavaScriptChannels:(FlutterMethodCall *)call result:(FlutterResu } - (void)clearCache:(FlutterResult)result { - if (@available(iOS 9.0, *)) { - NSSet *cacheDataTypes = [WKWebsiteDataStore allWebsiteDataTypes]; - WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore]; - NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0]; - [dataStore removeDataOfTypes:cacheDataTypes - modifiedSince:dateFrom - completionHandler:^{ - result(nil); - }]; - } else { - // support for iOS8 tracked in https://github.com/flutter/flutter/issues/27624. - NSLog(@"Clearing cache is not supported for Flutter WebViews prior to iOS 9."); - } + NSSet *cacheDataTypes = [WKWebsiteDataStore allWebsiteDataTypes]; + WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore]; + NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0]; + [dataStore removeDataOfTypes:cacheDataTypes + modifiedSince:dateFrom + completionHandler:^{ + result(nil); + }]; } - (void)onGetTitle:(FlutterResult)result { @@ -571,24 +566,20 @@ - (void)updateAutoMediaPlaybackPolicy:(NSNumber *)policy case 0: // require_user_action_for_all_media_types if (@available(iOS 10.0, *)) { configuration.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeAll; - } else if (@available(iOS 9.0, *)) { - configuration.requiresUserActionForMediaPlayback = true; } else { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" - configuration.mediaPlaybackRequiresUserAction = true; + configuration.requiresUserActionForMediaPlayback = true; #pragma clang diagnostic pop } break; case 1: // always_allow if (@available(iOS 10.0, *)) { configuration.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone; - } else if (@available(iOS 9.0, *)) { - configuration.requiresUserActionForMediaPlayback = false; } else { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" - configuration.mediaPlaybackRequiresUserAction = false; + configuration.requiresUserActionForMediaPlayback = false; #pragma clang diagnostic pop } break; @@ -658,11 +649,7 @@ - (void)registerJavaScriptChannels:(NSSet *)channelNames } - (void)updateUserAgent:(NSString *)userAgent { - if (@available(iOS 9.0, *)) { - [_webView setCustomUserAgent:userAgent]; - } else { - NSLog(@"Updating UserAgent is not supported for Flutter WebViews prior to iOS 9."); - } + [_webView setCustomUserAgent:userAgent]; } /** diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 2a4a7a4f674d..e52d1df9bbb6 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_wkwebview description: A Flutter plugin that provides a WebView widget based on Apple's WKWebView control. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_wkwebview issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.7.2 +version: 2.7.3 environment: sdk: ">=2.14.0 <3.0.0" From a868c33370d305fb101b54a9e8f8ee769d5ef47a Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 29 Apr 2022 16:14:14 -0400 Subject: [PATCH 466/600] Roll Flutter from ad1d30017e69 to 3fe2cbabc421 (2 revisions) (#5446) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index a7787d42ee77..db063fd7b523 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ad1d30017e69f91983edaf9bd6dae2a9f1c7b698 +3fe2cbabc4216262e4635fe4f9fe6f53aee89fde From f66cda1a0318c54fa4b1fc3b5940032b3dcb196f Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 30 Apr 2022 07:34:08 -0400 Subject: [PATCH 467/600] Roll Flutter from 3fe2cbabc421 to 0b3c5d1228f1 (7 revisions) (#5449) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index db063fd7b523..2bedcd7d5c3c 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -3fe2cbabc4216262e4635fe4f9fe6f53aee89fde +0b3c5d1228f1cbd6099ba789ab7d8190cdb1a2ac From fc0905fa97e020d0ff383f55c385af2c06c73301 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 30 Apr 2022 08:39:08 -0400 Subject: [PATCH 468/600] Roll Flutter from 0b3c5d1228f1 to fa519eb22fe3 (15 revisions) (#5451) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 2bedcd7d5c3c..11ab7f571fb5 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -0b3c5d1228f1cbd6099ba789ab7d8190cdb1a2ac +fa519eb22fe3fd1bc36f8a6c48b35efd99ec01eb From 3c841994ac5080a3afb9fc5810d087ef08a1dc64 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 30 Apr 2022 12:19:05 -0400 Subject: [PATCH 469/600] Roll Flutter from fa519eb22fe3 to 84e099d5f467 (1 revision) (#5452) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 11ab7f571fb5..bddc8a28699d 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -fa519eb22fe3fd1bc36f8a6c48b35efd99ec01eb +84e099d5f46791d1304ca9bbb361390bb6521a8e From 7762d6f34cf0e6b262ba5475650694c229e38cab Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 30 Apr 2022 14:59:08 -0400 Subject: [PATCH 470/600] Roll Flutter from 84e099d5f467 to b0d5d8ab2930 (1 revision) (#5454) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index bddc8a28699d..21a1d2109bee 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -84e099d5f46791d1304ca9bbb361390bb6521a8e +b0d5d8ab2930f94b10b600ba7893e3573a10a6fd From 355b6d7f21fcea6b70d09d1c3071d980ddda7469 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 30 Apr 2022 17:39:09 -0400 Subject: [PATCH 471/600] Roll Flutter from b0d5d8ab2930 to 2a24ee494db4 (2 revisions) (#5456) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 21a1d2109bee..81dd56edbc55 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b0d5d8ab2930f94b10b600ba7893e3573a10a6fd +2a24ee494db49d2240692ef191e568e735518cb9 From 5c9ffc71e345d30ae5abbc8c5360d0807daa9d80 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Sat, 30 Apr 2022 15:17:33 -0700 Subject: [PATCH 472/600] [webview_flutter_wkwebview] Implementation of Objective-C WKWebView Host Apis (#5341) --- .../ios/Runner.xcodeproj/project.pbxproj | 10 +- .../ios/RunnerTests/FWFDataConvertersTests.m | 25 + .../ios/RunnerTests/FWFWebViewHostApiTests.m | 340 +++ .../ios/Classes/FLTWebViewFlutterPlugin.m | 1 + .../ios/Classes/FWFDataConverters.h | 19 + .../ios/Classes/FWFDataConverters.m | 25 + .../ios/Classes/FWFGeneratedWebKitApis.h | 437 ++++ .../ios/Classes/FWFGeneratedWebKitApis.m | 2126 +++++++++++++++++ .../ios/Classes/FWFInstanceManager.m | 6 +- .../ios/Classes/FWFWebViewHostApi.h | 40 + .../ios/Classes/FWFWebViewHostApi.m | 249 ++ .../ios/Classes/webview-umbrella.h | 3 + .../lib/src/common/web_kit.pigeon.dart | 76 +- .../pigeons/web_kit.dart | 88 +- .../webview_flutter_wkwebview/pubspec.yaml | 2 +- .../test/src/common/test_web_kit.pigeon.dart | 80 +- .../test/src/ui_kit/ui_kit_test.mocks.dart | 8 +- .../test/src/web_kit/web_kit_test.mocks.dart | 10 +- .../web_kit_webview_widget_test.mocks.dart | 29 +- 19 files changed, 3511 insertions(+), 63 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFDataConvertersTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj index a444be330949..c48b313daec2 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 46; objects = { /* Begin PBXBuildFile section */ @@ -12,6 +12,8 @@ 334734022669319400DCC49E /* FLTWKNavigationDelegateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 686B4BF82548DBC7000AEA36 /* FLTWKNavigationDelegateTests.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 8FA6A87928062CD000A4B183 /* FWFInstanceManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */; }; + 8FB79B5328134C3100C101D3 /* FWFWebViewHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B5228134C3100C101D3 /* FWFWebViewHostApiTests.m */; }; + 8FB79B55281B24F600C101D3 /* FWFDataConvertersTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B54281B24F600C101D3 /* FWFDataConvertersTests.m */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -69,6 +71,8 @@ 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFInstanceManagerTests.m; sourceTree = ""; }; + 8FB79B5228134C3100C101D3 /* FWFWebViewHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFWebViewHostApiTests.m; sourceTree = ""; }; + 8FB79B54281B24F600C101D3 /* FWFDataConvertersTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFDataConvertersTests.m; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -129,6 +133,8 @@ 68BDCAED23C3F7CB00D9C032 /* Info.plist */, E43693B427512C0F00382F85 /* FLTCookieManagerTests.m */, 8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */, + 8FB79B5228134C3100C101D3 /* FWFWebViewHostApiTests.m */, + 8FB79B54281B24F600C101D3 /* FWFDataConvertersTests.m */, ); path = RunnerTests; sourceTree = ""; @@ -427,8 +433,10 @@ buildActionMask = 2147483647; files = ( 8FA6A87928062CD000A4B183 /* FWFInstanceManagerTests.m in Sources */, + 8FB79B5328134C3100C101D3 /* FWFWebViewHostApiTests.m in Sources */, 334734012669319100DCC49E /* FLTWebViewTests.m in Sources */, 334734022669319400DCC49E /* FLTWKNavigationDelegateTests.m in Sources */, + 8FB79B55281B24F600C101D3 /* FWFDataConvertersTests.m in Sources */, E43693B527512C0F00382F85 /* FLTCookieManagerTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFDataConvertersTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFDataConvertersTests.m new file mode 100644 index 000000000000..55952373998d --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFDataConvertersTests.m @@ -0,0 +1,25 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import Flutter; +@import XCTest; +@import webview_flutter_wkwebview; + +@interface FWFDataConvertersTests : XCTestCase +@end + +@implementation FWFDataConvertersTests +- (void)testFNSURLRequestFromRequestData { + NSURLRequest *request = FWFNSURLRequestFromRequestData([FWFNSUrlRequestData + makeWithUrl:@"https://flutter.dev" + httpMethod:@"post" + httpBody:[FlutterStandardTypedData typedDataWithBytes:[NSData data]] + allHttpHeaderFields:@{@"a" : @"header"}]); + + XCTAssertEqualObjects(request.URL, [NSURL URLWithString:@"https://flutter.dev"]); + XCTAssertEqualObjects(request.HTTPMethod, @"POST"); + XCTAssertEqualObjects(request.HTTPBody, [NSData data]); + XCTAssertEqualObjects(request.allHTTPHeaderFields, @{@"a" : @"header"}); +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m new file mode 100644 index 000000000000..dc0c1ce593d2 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m @@ -0,0 +1,340 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import Flutter; +@import XCTest; +@import webview_flutter_wkwebview; + +#import + +@interface FWFWebViewHostApiTests : XCTestCase +@end + +@implementation FWFWebViewHostApiTests +- (void)testLoadRequest { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + FWFNSUrlRequestData *requestData = [FWFNSUrlRequestData makeWithUrl:@"https://www.flutter.dev" + httpMethod:@"get" + httpBody:nil + allHttpHeaderFields:@{@"a" : @"header"}]; + [hostApi loadRequestForWebViewWithIdentifier:@0 request:requestData error:&error]; + + NSURL *url = [NSURL URLWithString:@"https://www.flutter.dev"]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; + request.HTTPMethod = @"get"; + request.allHTTPHeaderFields = @{@"a" : @"header"}; + OCMVerify([mockWebView loadRequest:request]); + XCTAssertNil(error); +} + +- (void)testLoadRequestWithInvalidUrl { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + OCMReject([mockWebView loadRequest:OCMOCK_ANY]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + FWFNSUrlRequestData *requestData = [FWFNSUrlRequestData makeWithUrl:@"%invalidUrl%" + httpMethod:nil + httpBody:nil + allHttpHeaderFields:@{}]; + [hostApi loadRequestForWebViewWithIdentifier:@0 request:requestData error:&error]; + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.code, @"FWFURLRequestParsingError"); + XCTAssertEqualObjects(error.message, @"Failed instantiating an NSURLRequest."); + XCTAssertEqualObjects(error.details, @"URL was: '%invalidUrl%'"); +} + +- (void)testSetCustomUserAgent { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi setUserAgentForWebViewWithIdentifier:@0 userAgent:@"userA" error:&error]; + OCMVerify([mockWebView setCustomUserAgent:@"userA"]); + XCTAssertNil(error); +} + +- (void)testURL { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + OCMStub([mockWebView URL]).andReturn([NSURL URLWithString:@"https://www.flutter.dev/"]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + XCTAssertEqualObjects([hostApi URLForWebViewWithIdentifier:@0 error:&error], + @"https://www.flutter.dev/"); + XCTAssertNil(error); +} + +- (void)testCanGoBack { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + OCMStub([mockWebView canGoBack]).andReturn(YES); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + XCTAssertEqualObjects([hostApi canGoBackForWebViewWithIdentifier:@0 error:&error], @YES); + XCTAssertNil(error); +} + +- (void)testSetUIDelegate { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + id mockDelegate = OCMProtocolMock(@protocol(WKUIDelegate)); + [instanceManager addInstance:mockDelegate withIdentifier:1]; + + FlutterError *error; + [hostApi setUIDelegateForWebViewWithIdentifier:@0 delegateIdentifier:@1 error:&error]; + OCMVerify([mockWebView setUIDelegate:mockDelegate]); + XCTAssertNil(error); +} + +- (void)testSetNavigationDelegate { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + id mockDelegate = OCMProtocolMock(@protocol(WKNavigationDelegate)); + [instanceManager addInstance:mockDelegate withIdentifier:1]; + FlutterError *error; + + [hostApi setNavigationDelegateForWebViewWithIdentifier:@0 delegateIdentifier:@1 error:&error]; + OCMVerify([mockWebView setNavigationDelegate:mockDelegate]); + XCTAssertNil(error); +} + +- (void)testEstimatedProgress { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + OCMStub([mockWebView estimatedProgress]).andReturn(34.0); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + XCTAssertEqualObjects([hostApi estimatedProgressForWebViewWithIdentifier:@0 error:&error], @34.0); + XCTAssertNil(error); +} + +- (void)testloadHTMLString { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi loadHTMLForWebViewWithIdentifier:@0 + HTMLString:@"myString" + baseURL:@"myBaseUrl" + error:&error]; + OCMVerify([mockWebView loadHTMLString:@"myString" baseURL:[NSURL URLWithString:@"myBaseUrl"]]); + XCTAssertNil(error); +} + +- (void)testLoadFileURL { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi loadFileForWebViewWithIdentifier:@0 + fileURL:@"myFolder/apple.txt" + readAccessURL:@"myFolder" + error:&error]; + XCTAssertNil(error); + OCMVerify([mockWebView loadFileURL:[NSURL fileURLWithPath:@"myFolder/apple.txt" isDirectory:NO] + allowingReadAccessToURL:[NSURL fileURLWithPath:@"myFolder/" isDirectory:YES] + + ]); +} + +- (void)testLoadFlutterAsset { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFAssetManager *mockAssetManager = OCMClassMock([FWFAssetManager class]); + OCMStub([mockAssetManager lookupKeyForAsset:@"assets/index.html"]) + .andReturn(@"myFolder/assets/index.html"); + + NSBundle *mockBundle = OCMClassMock([NSBundle class]); + OCMStub([mockBundle URLForResource:@"myFolder/assets/index" withExtension:@"html"]) + .andReturn([NSURL URLWithString:@"webview_flutter/myFolder/assets/index.html"]); + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager + bundle:mockBundle + assetManager:mockAssetManager]; + + FlutterError *error; + [hostApi loadAssetForWebViewWithIdentifier:@0 assetKey:@"assets/index.html" error:&error]; + + XCTAssertNil(error); + OCMVerify([mockWebView + loadFileURL:[NSURL URLWithString:@"webview_flutter/myFolder/assets/index.html"] + allowingReadAccessToURL:[NSURL URLWithString:@"webview_flutter/myFolder/assets/"]]); +} + +- (void)testCanGoForward { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + OCMStub([mockWebView canGoForward]).andReturn(NO); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + XCTAssertEqualObjects([hostApi canGoForwardForWebViewWithIdentifier:@0 error:&error], @NO); + XCTAssertNil(error); +} + +- (void)testGoBack { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi goBackForWebViewWithIdentifier:@0 error:&error]; + OCMVerify([mockWebView goBack]); + XCTAssertNil(error); +} + +- (void)testGoForward { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi goForwardForWebViewWithIdentifier:@0 error:&error]; + OCMVerify([mockWebView goForward]); + XCTAssertNil(error); +} + +- (void)testReload { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi reloadWebViewWithIdentifier:@0 error:&error]; + OCMVerify([mockWebView reload]); + XCTAssertNil(error); +} + +- (void)testTitle { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + OCMStub([mockWebView title]).andReturn(@"myTitle"); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + XCTAssertEqualObjects([hostApi titleForWebViewWithIdentifier:@0 error:&error], @"myTitle"); + XCTAssertNil(error); +} + +- (void)testSetAllowsBackForwardNavigationGestures { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi setAllowsBackForwardForWebViewWithIdentifier:@0 isAllowed:@YES error:&error]; + OCMVerify([mockWebView setAllowsBackForwardNavigationGestures:YES]); + XCTAssertNil(error); +} + +- (void)testEvaluateJavaScript { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + + OCMStub([mockWebView + evaluateJavaScript:@"runJavaScript" + completionHandler:([OCMArg invokeBlockWithArgs:@"result", [NSNull null], nil])]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + NSString __block *returnValue; + FlutterError __block *returnError; + [hostApi evaluateJavaScriptForWebViewWithIdentifier:@0 + javaScriptString:@"runJavaScript" + completion:^(id result, FlutterError *error) { + returnValue = result; + returnError = error; + }]; + + XCTAssertEqualObjects(returnValue, @"result"); + XCTAssertNil(returnError); +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWebViewFlutterPlugin.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWebViewFlutterPlugin.m index a4e87ba5177a..a63d6a60b114 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWebViewFlutterPlugin.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWebViewFlutterPlugin.m @@ -4,6 +4,7 @@ #import "FLTWebViewFlutterPlugin.h" #import "FLTCookieManager.h" +#import "FWFWebViewHostApi.h" #import "FlutterWebView.h" @implementation FLTWebViewFlutterPlugin diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.h new file mode 100644 index 000000000000..e9dbf2d9efa7 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.h @@ -0,0 +1,19 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FWFGeneratedWebKitApis.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Converts an FWFNSUrlRequestData to an NSURLRequest. + * + * @param data The data object containing information to create an NSURLRequest. + * + * @return An NSURLRequest or nil if data could not be converted. + */ +extern NSURLRequest* _Nullable FWFNSURLRequestFromRequestData( + FWFNSUrlRequestData* data); + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m new file mode 100644 index 000000000000..1945bc3354f3 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m @@ -0,0 +1,25 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FWFDataConverters.h" + +#import + +NSURLRequest *_Nullable FWFNSURLRequestFromRequestData(FWFNSUrlRequestData *data) { + NSURL *url = [NSURL URLWithString:data.url]; + if (!url) { + return nil; + } + + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; + if (!request) { + return nil; + } + + [request setHTTPMethod:data.httpMethod]; + [request setHTTPBody:data.httpBody.data]; + [request setAllHTTPHeaderFields:data.allHttpHeaderFields]; + + return request; +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h new file mode 100644 index 000000000000..5306b300fc03 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h @@ -0,0 +1,437 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v3.0.3), do not edit directly. +// See also: https://pub.dev/packages/pigeon +#import +@protocol FlutterBinaryMessenger; +@protocol FlutterMessageCodec; +@class FlutterError; +@class FlutterStandardTypedData; + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSUInteger, FWFNSKeyValueObservingOptionsEnum) { + FWFNSKeyValueObservingOptionsEnumNewValue = 0, + FWFNSKeyValueObservingOptionsEnumOldValue = 1, + FWFNSKeyValueObservingOptionsEnumInitialValue = 2, + FWFNSKeyValueObservingOptionsEnumPriorNotification = 3, +}; + +typedef NS_ENUM(NSUInteger, FWFNSKeyValueChangeEnum) { + FWFNSKeyValueChangeEnumSetting = 0, + FWFNSKeyValueChangeEnumInsertion = 1, + FWFNSKeyValueChangeEnumRemoval = 2, + FWFNSKeyValueChangeEnumReplacement = 3, +}; + +typedef NS_ENUM(NSUInteger, FWFNSKeyValueChangeKeyEnum) { + FWFNSKeyValueChangeKeyEnumIndexes = 0, + FWFNSKeyValueChangeKeyEnumKind = 1, + FWFNSKeyValueChangeKeyEnumNewValue = 2, + FWFNSKeyValueChangeKeyEnumNotificationIsPrior = 3, + FWFNSKeyValueChangeKeyEnumOldValue = 4, +}; + +typedef NS_ENUM(NSUInteger, FWFWKUserScriptInjectionTimeEnum) { + FWFWKUserScriptInjectionTimeEnumAtDocumentStart = 0, + FWFWKUserScriptInjectionTimeEnumAtDocumentEnd = 1, +}; + +typedef NS_ENUM(NSUInteger, FWFWKAudiovisualMediaTypeEnum) { + FWFWKAudiovisualMediaTypeEnumNone = 0, + FWFWKAudiovisualMediaTypeEnumAudio = 1, + FWFWKAudiovisualMediaTypeEnumVideo = 2, + FWFWKAudiovisualMediaTypeEnumAll = 3, +}; + +typedef NS_ENUM(NSUInteger, FWFWKWebsiteDataTypesEnum) { + FWFWKWebsiteDataTypesEnumCookies = 0, + FWFWKWebsiteDataTypesEnumMemoryCache = 1, + FWFWKWebsiteDataTypesEnumDiskCache = 2, + FWFWKWebsiteDataTypesEnumOfflineWebApplicationCache = 3, + FWFWKWebsiteDataTypesEnumLocalStroage = 4, + FWFWKWebsiteDataTypesEnumSessionStorage = 5, + FWFWKWebsiteDataTypesEnumSqlDatabases = 6, + FWFWKWebsiteDataTypesEnumIndexedDBDatabases = 7, +}; + +typedef NS_ENUM(NSUInteger, FWFWKNavigationActionPolicyEnum) { + FWFWKNavigationActionPolicyEnumAllow = 0, + FWFWKNavigationActionPolicyEnumCancel = 1, +}; + +typedef NS_ENUM(NSUInteger, FWFNSHttpCookiePropertyKeyEnum) { + FWFNSHttpCookiePropertyKeyEnumComment = 0, + FWFNSHttpCookiePropertyKeyEnumCommentUrl = 1, + FWFNSHttpCookiePropertyKeyEnumDiscard = 2, + FWFNSHttpCookiePropertyKeyEnumDomain = 3, + FWFNSHttpCookiePropertyKeyEnumExpires = 4, + FWFNSHttpCookiePropertyKeyEnumMaximumAge = 5, + FWFNSHttpCookiePropertyKeyEnumName = 6, + FWFNSHttpCookiePropertyKeyEnumOriginUrl = 7, + FWFNSHttpCookiePropertyKeyEnumPath = 8, + FWFNSHttpCookiePropertyKeyEnumPort = 9, + FWFNSHttpCookiePropertyKeyEnumSameSitePolicy = 10, + FWFNSHttpCookiePropertyKeyEnumSecure = 11, + FWFNSHttpCookiePropertyKeyEnumValue = 12, + FWFNSHttpCookiePropertyKeyEnumVersion = 13, +}; + +@class FWFNSKeyValueObservingOptionsEnumData; +@class FWFWKUserScriptInjectionTimeEnumData; +@class FWFWKAudiovisualMediaTypeEnumData; +@class FWFWKWebsiteDataTypesEnumData; +@class FWFNSHttpCookiePropertyKeyEnumData; +@class FWFNSUrlRequestData; +@class FWFWKUserScriptData; +@class FWFNSHttpCookieData; + +@interface FWFNSKeyValueObservingOptionsEnumData : NSObject ++ (instancetype)makeWithValue:(FWFNSKeyValueObservingOptionsEnum)value; +@property(nonatomic, assign) FWFNSKeyValueObservingOptionsEnum value; +@end + +@interface FWFWKUserScriptInjectionTimeEnumData : NSObject ++ (instancetype)makeWithValue:(FWFWKUserScriptInjectionTimeEnum)value; +@property(nonatomic, assign) FWFWKUserScriptInjectionTimeEnum value; +@end + +@interface FWFWKAudiovisualMediaTypeEnumData : NSObject ++ (instancetype)makeWithValue:(FWFWKAudiovisualMediaTypeEnum)value; +@property(nonatomic, assign) FWFWKAudiovisualMediaTypeEnum value; +@end + +@interface FWFWKWebsiteDataTypesEnumData : NSObject ++ (instancetype)makeWithValue:(FWFWKWebsiteDataTypesEnum)value; +@property(nonatomic, assign) FWFWKWebsiteDataTypesEnum value; +@end + +@interface FWFNSHttpCookiePropertyKeyEnumData : NSObject ++ (instancetype)makeWithValue:(FWFNSHttpCookiePropertyKeyEnum)value; +@property(nonatomic, assign) FWFNSHttpCookiePropertyKeyEnum value; +@end + +@interface FWFNSUrlRequestData : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithUrl:(NSString *)url + httpMethod:(nullable NSString *)httpMethod + httpBody:(nullable FlutterStandardTypedData *)httpBody + allHttpHeaderFields:(NSDictionary *)allHttpHeaderFields; +@property(nonatomic, copy) NSString *url; +@property(nonatomic, copy, nullable) NSString *httpMethod; +@property(nonatomic, strong, nullable) FlutterStandardTypedData *httpBody; +@property(nonatomic, strong) NSDictionary *allHttpHeaderFields; +@end + +@interface FWFWKUserScriptData : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithSource:(NSString *)source + injectionTime:(nullable FWFWKUserScriptInjectionTimeEnumData *)injectionTime + isMainFrameOnly:(NSNumber *)isMainFrameOnly; +@property(nonatomic, copy) NSString *source; +@property(nonatomic, strong, nullable) FWFWKUserScriptInjectionTimeEnumData *injectionTime; +@property(nonatomic, strong) NSNumber *isMainFrameOnly; +@end + +@interface FWFNSHttpCookieData : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithProperties: + (NSDictionary *)properties; +@property(nonatomic, strong) + NSDictionary *properties; +@end + +/// The codec used by FWFWKWebsiteDataStoreHostApi. +NSObject *FWFWKWebsiteDataStoreHostApiGetCodec(void); + +@protocol FWFWKWebsiteDataStoreHostApi +- (void)createDataStoreFromConfigurationWithIdentifier:(NSNumber *)instanceId + configurationIdentifier:(NSNumber *)configurationInstanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)createDefaultDataStoreWithIdentifier:(NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)removeDataFromDataStoreWithIdentifier:(NSNumber *)instanceId + ofTypes:(NSArray *)dataTypes + secondsModifiedSinceEpoch:(NSNumber *)secondsModifiedSinceEpoch + completion:(void (^)(NSNumber *_Nullable, + FlutterError *_Nullable))completion; +@end + +extern void FWFWKWebsiteDataStoreHostApiSetup( + id binaryMessenger, + NSObject *_Nullable api); + +/// The codec used by FWFUIViewHostApi. +NSObject *FWFUIViewHostApiGetCodec(void); + +@protocol FWFUIViewHostApi +/// @return `nil` only when `error != nil`. +- (nullable NSArray *) + contentOffsetForViewWithIdentifier:(NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setBackgroundColorForViewWithIdentifier:(NSNumber *)instanceId + toValue:(nullable NSNumber *)value + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setOpaqueForViewWithIdentifier:(NSNumber *)instanceId + isOpaque:(NSNumber *)opaque + error:(FlutterError *_Nullable *_Nonnull)error; +@end + +extern void FWFUIViewHostApiSetup(id binaryMessenger, + NSObject *_Nullable api); + +/// The codec used by FWFUIScrollViewHostApi. +NSObject *FWFUIScrollViewHostApiGetCodec(void); + +@protocol FWFUIScrollViewHostApi +- (void)createFromWebViewWithIdentifier:(NSNumber *)instanceId + webViewIdentifier:(NSNumber *)webViewInstanceId + error:(FlutterError *_Nullable *_Nonnull)error; +/// @return `nil` only when `error != nil`. +- (nullable NSArray *) + contentOffsetForScrollViewWithIdentifier:(NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)scrollByForScrollViewWithIdentifier:(NSNumber *)instanceId + toX:(NSNumber *)x + y:(NSNumber *)y + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setContentOffsetForScrollViewWithIdentifier:(NSNumber *)instanceId + toX:(NSNumber *)x + y:(NSNumber *)y + error:(FlutterError *_Nullable *_Nonnull)error; +@end + +extern void FWFUIScrollViewHostApiSetup(id binaryMessenger, + NSObject *_Nullable api); + +/// The codec used by FWFWKWebViewConfigurationHostApi. +NSObject *FWFWKWebViewConfigurationHostApiGetCodec(void); + +@protocol FWFWKWebViewConfigurationHostApi +- (void)createWithIdentifier:(NSNumber *)instanceId error:(FlutterError *_Nullable *_Nonnull)error; +- (void)createFromWebViewWithIdentifier:(NSNumber *)instanceId + webViewIdentifier:(NSNumber *)webViewInstanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:(NSNumber *)instanceId + isAlowed:(NSNumber *)allow + error: + (FlutterError *_Nullable *_Nonnull) + error; +- (void) + setMediaTypesRequiresUserActionForConfigurationWithIdentifier:(NSNumber *)instanceId + forTypes: + (NSArray< + FWFWKAudiovisualMediaTypeEnumData + *> *)types + error: + (FlutterError *_Nullable *_Nonnull) + error; +@end + +extern void FWFWKWebViewConfigurationHostApiSetup( + id binaryMessenger, + NSObject *_Nullable api); + +/// The codec used by FWFWKUserContentControllerHostApi. +NSObject *FWFWKUserContentControllerHostApiGetCodec(void); + +@protocol FWFWKUserContentControllerHostApi +- (void)createFromWebViewConfigurationWithIdentifier:(NSNumber *)instanceId + configurationIdentifier:(NSNumber *)configurationInstanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)addScriptMessageHandlerForControllerWithIdentifier:(NSNumber *)instanceId + handlerIdentifier:(NSNumber *)handlerInstanceid + ofName:(NSString *)name + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)removeScriptMessageHandlerForControllerWithIdentifier:(NSNumber *)instanceId + name:(NSString *)name + error:(FlutterError *_Nullable *_Nonnull) + error; +- (void)removeAllScriptMessageHandlersForControllerWithIdentifier:(NSNumber *)instanceId + error: + (FlutterError *_Nullable *_Nonnull) + error; +- (void)addUserScriptForControllerWithIdentifier:(NSNumber *)instanceId + userScript:(FWFWKUserScriptData *)userScript + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)removeAllUserScriptsForControllerWithIdentifier:(NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error; +@end + +extern void FWFWKUserContentControllerHostApiSetup( + id binaryMessenger, + NSObject *_Nullable api); + +/// The codec used by FWFWKPreferencesHostApi. +NSObject *FWFWKPreferencesHostApiGetCodec(void); + +@protocol FWFWKPreferencesHostApi +- (void)createFromWebViewConfiguration:(NSNumber *)instanceId + configurationIdentifier:(NSNumber *)configurationInstanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setJavaScriptEnabledForPreferencesWithIdentifier:(NSNumber *)instanceId + isEnabled:(NSNumber *)enabled + error:(FlutterError *_Nullable *_Nonnull)error; +@end + +extern void FWFWKPreferencesHostApiSetup(id binaryMessenger, + NSObject *_Nullable api); + +/// The codec used by FWFWKScriptMessageHandlerHostApi. +NSObject *FWFWKScriptMessageHandlerHostApiGetCodec(void); + +@protocol FWFWKScriptMessageHandlerHostApi +- (void)createWithIdentifier:(NSNumber *)instanceId error:(FlutterError *_Nullable *_Nonnull)error; +@end + +extern void FWFWKScriptMessageHandlerHostApiSetup( + id binaryMessenger, + NSObject *_Nullable api); + +/// The codec used by FWFWKNavigationDelegateHostApi. +NSObject *FWFWKNavigationDelegateHostApiGetCodec(void); + +@protocol FWFWKNavigationDelegateHostApi +- (void)createWithIdentifier:(NSNumber *)instanceId error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setDidFinishNavigationForDelegateWithIdentifier:(NSNumber *)instanceId + functionIdentifier:(nullable NSNumber *)functionInstanceId + error:(FlutterError *_Nullable *_Nonnull)error; +@end + +extern void FWFWKNavigationDelegateHostApiSetup( + id binaryMessenger, + NSObject *_Nullable api); + +/// The codec used by FWFWKNavigationDelegateFlutterApi. +NSObject *FWFWKNavigationDelegateFlutterApiGetCodec(void); + +@interface FWFWKNavigationDelegateFlutterApi : NSObject +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger; +- (void)didFinishNavigationForDelegateWithIdentifier:(NSNumber *)functionInstanceId + webViewIdentifier:(NSNumber *)webViewInstanceId + URL:(nullable NSString *)url + completion:(void (^)(NSError *_Nullable))completion; +@end +/// The codec used by FWFNSObjectHostApi. +NSObject *FWFNSObjectHostApiGetCodec(void); + +@protocol FWFNSObjectHostApi +- (void)disposeObjectWithIdentifier:(NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)addObserverForObjectWithIdentifier:(NSNumber *)instanceId + observerIdentifier:(NSNumber *)observerInstanceId + keyPath:(NSString *)keyPath + options: + (NSArray *)options + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)removeObserverForObjectWithIdentifier:(NSNumber *)instanceId + observerIdentifier:(NSNumber *)observerInstanceId + keyPath:(NSString *)keyPath + error:(FlutterError *_Nullable *_Nonnull)error; +@end + +extern void FWFNSObjectHostApiSetup(id binaryMessenger, + NSObject *_Nullable api); + +/// The codec used by FWFFunctionFlutterApi. +NSObject *FWFFunctionFlutterApiGetCodec(void); + +@interface FWFFunctionFlutterApi : NSObject +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger; +- (void)disposeFunctionWithIdentifier:(NSNumber *)instanceId + completion:(void (^)(NSError *_Nullable))completion; +@end +/// The codec used by FWFWKWebViewHostApi. +NSObject *FWFWKWebViewHostApiGetCodec(void); + +@protocol FWFWKWebViewHostApi +- (void)createWithIdentifier:(NSNumber *)instanceId + configurationIdentifier:(NSNumber *)configurationInstanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setUIDelegateForWebViewWithIdentifier:(NSNumber *)instanceId + delegateIdentifier:(nullable NSNumber *)uiDelegateInstanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setNavigationDelegateForWebViewWithIdentifier:(NSNumber *)instanceId + delegateIdentifier: + (nullable NSNumber *)navigationDelegateInstanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (nullable NSString *)URLForWebViewWithIdentifier:(NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error; +/// @return `nil` only when `error != nil`. +- (nullable NSNumber *)estimatedProgressForWebViewWithIdentifier:(NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull) + error; +- (void)loadRequestForWebViewWithIdentifier:(NSNumber *)instanceId + request:(FWFNSUrlRequestData *)request + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)loadHTMLForWebViewWithIdentifier:(NSNumber *)instanceId + HTMLString:(NSString *)string + baseURL:(nullable NSString *)baseUrl + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)loadFileForWebViewWithIdentifier:(NSNumber *)instanceId + fileURL:(NSString *)url + readAccessURL:(NSString *)readAccessUrl + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)loadAssetForWebViewWithIdentifier:(NSNumber *)instanceId + assetKey:(NSString *)key + error:(FlutterError *_Nullable *_Nonnull)error; +/// @return `nil` only when `error != nil`. +- (nullable NSNumber *)canGoBackForWebViewWithIdentifier:(NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error; +/// @return `nil` only when `error != nil`. +- (nullable NSNumber *)canGoForwardForWebViewWithIdentifier:(NSNumber *)instanceId + error: + (FlutterError *_Nullable *_Nonnull)error; +- (void)goBackForWebViewWithIdentifier:(NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)goForwardForWebViewWithIdentifier:(NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)reloadWebViewWithIdentifier:(NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (nullable NSString *)titleForWebViewWithIdentifier:(NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setAllowsBackForwardForWebViewWithIdentifier:(NSNumber *)instanceId + isAllowed:(NSNumber *)allow + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setUserAgentForWebViewWithIdentifier:(NSNumber *)instanceId + userAgent:(nullable NSString *)userAgent + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)evaluateJavaScriptForWebViewWithIdentifier:(NSNumber *)instanceId + javaScriptString:(NSString *)javaScriptString + completion:(void (^)(id _Nullable, + FlutterError *_Nullable))completion; +@end + +extern void FWFWKWebViewHostApiSetup(id binaryMessenger, + NSObject *_Nullable api); + +/// The codec used by FWFWKUIDelegateHostApi. +NSObject *FWFWKUIDelegateHostApiGetCodec(void); + +@protocol FWFWKUIDelegateHostApi +- (void)createWithIdentifier:(NSNumber *)instanceId error:(FlutterError *_Nullable *_Nonnull)error; +@end + +extern void FWFWKUIDelegateHostApiSetup(id binaryMessenger, + NSObject *_Nullable api); + +/// The codec used by FWFWKHttpCookieStoreHostApi. +NSObject *FWFWKHttpCookieStoreHostApiGetCodec(void); + +@protocol FWFWKHttpCookieStoreHostApi +- (void)createFromWebsiteDataStoreWithIdentifier:(NSNumber *)instanceId + dataStoreIdentifier:(NSNumber *)websiteDataStoreInstanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setCookieForStoreWithIdentifier:(NSNumber *)instanceId + cookie:(FWFNSHttpCookieData *)cookie + error:(FlutterError *_Nullable *_Nonnull)error; +@end + +extern void FWFWKHttpCookieStoreHostApiSetup(id binaryMessenger, + NSObject *_Nullable api); + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m new file mode 100644 index 000000000000..070b3c5cc033 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m @@ -0,0 +1,2126 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v3.0.3), do not edit directly. +// See also: https://pub.dev/packages/pigeon +#import "FWFGeneratedWebKitApis.h" +#import + +#if !__has_feature(objc_arc) +#error File requires ARC to be enabled. +#endif + +static NSDictionary *wrapResult(id result, FlutterError *error) { + NSDictionary *errorDict = (NSDictionary *)[NSNull null]; + if (error) { + errorDict = @{ + @"code" : (error.code ? error.code : [NSNull null]), + @"message" : (error.message ? error.message : [NSNull null]), + @"details" : (error.details ? error.details : [NSNull null]), + }; + } + return @{ + @"result" : (result ? result : [NSNull null]), + @"error" : errorDict, + }; +} +static id GetNullableObject(NSDictionary *dict, id key) { + id result = dict[key]; + return (result == [NSNull null]) ? nil : result; +} +static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { + id result = array[key]; + return (result == [NSNull null]) ? nil : result; +} + +@interface FWFNSKeyValueObservingOptionsEnumData () ++ (FWFNSKeyValueObservingOptionsEnumData *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end +@interface FWFWKUserScriptInjectionTimeEnumData () ++ (FWFWKUserScriptInjectionTimeEnumData *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end +@interface FWFWKAudiovisualMediaTypeEnumData () ++ (FWFWKAudiovisualMediaTypeEnumData *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end +@interface FWFWKWebsiteDataTypesEnumData () ++ (FWFWKWebsiteDataTypesEnumData *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end +@interface FWFNSHttpCookiePropertyKeyEnumData () ++ (FWFNSHttpCookiePropertyKeyEnumData *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end +@interface FWFNSUrlRequestData () ++ (FWFNSUrlRequestData *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end +@interface FWFWKUserScriptData () ++ (FWFWKUserScriptData *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end +@interface FWFNSHttpCookieData () ++ (FWFNSHttpCookieData *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end + +@implementation FWFNSKeyValueObservingOptionsEnumData ++ (instancetype)makeWithValue:(FWFNSKeyValueObservingOptionsEnum)value { + FWFNSKeyValueObservingOptionsEnumData *pigeonResult = + [[FWFNSKeyValueObservingOptionsEnumData alloc] init]; + pigeonResult.value = value; + return pigeonResult; +} ++ (FWFNSKeyValueObservingOptionsEnumData *)fromMap:(NSDictionary *)dict { + FWFNSKeyValueObservingOptionsEnumData *pigeonResult = + [[FWFNSKeyValueObservingOptionsEnumData alloc] init]; + pigeonResult.value = [GetNullableObject(dict, @"value") integerValue]; + return pigeonResult; +} +- (NSDictionary *)toMap { + return [NSDictionary dictionaryWithObjectsAndKeys:@(self.value), @"value", nil]; +} +@end + +@implementation FWFWKUserScriptInjectionTimeEnumData ++ (instancetype)makeWithValue:(FWFWKUserScriptInjectionTimeEnum)value { + FWFWKUserScriptInjectionTimeEnumData *pigeonResult = + [[FWFWKUserScriptInjectionTimeEnumData alloc] init]; + pigeonResult.value = value; + return pigeonResult; +} ++ (FWFWKUserScriptInjectionTimeEnumData *)fromMap:(NSDictionary *)dict { + FWFWKUserScriptInjectionTimeEnumData *pigeonResult = + [[FWFWKUserScriptInjectionTimeEnumData alloc] init]; + pigeonResult.value = [GetNullableObject(dict, @"value") integerValue]; + return pigeonResult; +} +- (NSDictionary *)toMap { + return [NSDictionary dictionaryWithObjectsAndKeys:@(self.value), @"value", nil]; +} +@end + +@implementation FWFWKAudiovisualMediaTypeEnumData ++ (instancetype)makeWithValue:(FWFWKAudiovisualMediaTypeEnum)value { + FWFWKAudiovisualMediaTypeEnumData *pigeonResult = + [[FWFWKAudiovisualMediaTypeEnumData alloc] init]; + pigeonResult.value = value; + return pigeonResult; +} ++ (FWFWKAudiovisualMediaTypeEnumData *)fromMap:(NSDictionary *)dict { + FWFWKAudiovisualMediaTypeEnumData *pigeonResult = + [[FWFWKAudiovisualMediaTypeEnumData alloc] init]; + pigeonResult.value = [GetNullableObject(dict, @"value") integerValue]; + return pigeonResult; +} +- (NSDictionary *)toMap { + return [NSDictionary dictionaryWithObjectsAndKeys:@(self.value), @"value", nil]; +} +@end + +@implementation FWFWKWebsiteDataTypesEnumData ++ (instancetype)makeWithValue:(FWFWKWebsiteDataTypesEnum)value { + FWFWKWebsiteDataTypesEnumData *pigeonResult = [[FWFWKWebsiteDataTypesEnumData alloc] init]; + pigeonResult.value = value; + return pigeonResult; +} ++ (FWFWKWebsiteDataTypesEnumData *)fromMap:(NSDictionary *)dict { + FWFWKWebsiteDataTypesEnumData *pigeonResult = [[FWFWKWebsiteDataTypesEnumData alloc] init]; + pigeonResult.value = [GetNullableObject(dict, @"value") integerValue]; + return pigeonResult; +} +- (NSDictionary *)toMap { + return [NSDictionary dictionaryWithObjectsAndKeys:@(self.value), @"value", nil]; +} +@end + +@implementation FWFNSHttpCookiePropertyKeyEnumData ++ (instancetype)makeWithValue:(FWFNSHttpCookiePropertyKeyEnum)value { + FWFNSHttpCookiePropertyKeyEnumData *pigeonResult = + [[FWFNSHttpCookiePropertyKeyEnumData alloc] init]; + pigeonResult.value = value; + return pigeonResult; +} ++ (FWFNSHttpCookiePropertyKeyEnumData *)fromMap:(NSDictionary *)dict { + FWFNSHttpCookiePropertyKeyEnumData *pigeonResult = + [[FWFNSHttpCookiePropertyKeyEnumData alloc] init]; + pigeonResult.value = [GetNullableObject(dict, @"value") integerValue]; + return pigeonResult; +} +- (NSDictionary *)toMap { + return [NSDictionary dictionaryWithObjectsAndKeys:@(self.value), @"value", nil]; +} +@end + +@implementation FWFNSUrlRequestData ++ (instancetype)makeWithUrl:(NSString *)url + httpMethod:(nullable NSString *)httpMethod + httpBody:(nullable FlutterStandardTypedData *)httpBody + allHttpHeaderFields:(NSDictionary *)allHttpHeaderFields { + FWFNSUrlRequestData *pigeonResult = [[FWFNSUrlRequestData alloc] init]; + pigeonResult.url = url; + pigeonResult.httpMethod = httpMethod; + pigeonResult.httpBody = httpBody; + pigeonResult.allHttpHeaderFields = allHttpHeaderFields; + return pigeonResult; +} ++ (FWFNSUrlRequestData *)fromMap:(NSDictionary *)dict { + FWFNSUrlRequestData *pigeonResult = [[FWFNSUrlRequestData alloc] init]; + pigeonResult.url = GetNullableObject(dict, @"url"); + NSAssert(pigeonResult.url != nil, @""); + pigeonResult.httpMethod = GetNullableObject(dict, @"httpMethod"); + pigeonResult.httpBody = GetNullableObject(dict, @"httpBody"); + pigeonResult.allHttpHeaderFields = GetNullableObject(dict, @"allHttpHeaderFields"); + NSAssert(pigeonResult.allHttpHeaderFields != nil, @""); + return pigeonResult; +} +- (NSDictionary *)toMap { + return [NSDictionary + dictionaryWithObjectsAndKeys:(self.url ? self.url : [NSNull null]), @"url", + (self.httpMethod ? self.httpMethod : [NSNull null]), + @"httpMethod", (self.httpBody ? self.httpBody : [NSNull null]), + @"httpBody", + (self.allHttpHeaderFields ? self.allHttpHeaderFields + : [NSNull null]), + @"allHttpHeaderFields", nil]; +} +@end + +@implementation FWFWKUserScriptData ++ (instancetype)makeWithSource:(NSString *)source + injectionTime:(nullable FWFWKUserScriptInjectionTimeEnumData *)injectionTime + isMainFrameOnly:(NSNumber *)isMainFrameOnly { + FWFWKUserScriptData *pigeonResult = [[FWFWKUserScriptData alloc] init]; + pigeonResult.source = source; + pigeonResult.injectionTime = injectionTime; + pigeonResult.isMainFrameOnly = isMainFrameOnly; + return pigeonResult; +} ++ (FWFWKUserScriptData *)fromMap:(NSDictionary *)dict { + FWFWKUserScriptData *pigeonResult = [[FWFWKUserScriptData alloc] init]; + pigeonResult.source = GetNullableObject(dict, @"source"); + NSAssert(pigeonResult.source != nil, @""); + pigeonResult.injectionTime = + [FWFWKUserScriptInjectionTimeEnumData fromMap:GetNullableObject(dict, @"injectionTime")]; + pigeonResult.isMainFrameOnly = GetNullableObject(dict, @"isMainFrameOnly"); + NSAssert(pigeonResult.isMainFrameOnly != nil, @""); + return pigeonResult; +} +- (NSDictionary *)toMap { + return [NSDictionary + dictionaryWithObjectsAndKeys:(self.source ? self.source : [NSNull null]), @"source", + (self.injectionTime ? [self.injectionTime toMap] + : [NSNull null]), + @"injectionTime", + (self.isMainFrameOnly ? self.isMainFrameOnly : [NSNull null]), + @"isMainFrameOnly", nil]; +} +@end + +@implementation FWFNSHttpCookieData ++ (instancetype)makeWithProperties: + (NSDictionary *)properties { + FWFNSHttpCookieData *pigeonResult = [[FWFNSHttpCookieData alloc] init]; + pigeonResult.properties = properties; + return pigeonResult; +} ++ (FWFNSHttpCookieData *)fromMap:(NSDictionary *)dict { + FWFNSHttpCookieData *pigeonResult = [[FWFNSHttpCookieData alloc] init]; + pigeonResult.properties = GetNullableObject(dict, @"properties"); + NSAssert(pigeonResult.properties != nil, @""); + return pigeonResult; +} +- (NSDictionary *)toMap { + return [NSDictionary + dictionaryWithObjectsAndKeys:(self.properties ? self.properties : [NSNull null]), + @"properties", nil]; +} +@end + +@interface FWFWKWebsiteDataStoreHostApiCodecReader : FlutterStandardReader +@end +@implementation FWFWKWebsiteDataStoreHostApiCodecReader +- (nullable id)readValueOfType:(UInt8)type { + switch (type) { + case 128: + return [FWFWKWebsiteDataTypesEnumData fromMap:[self readValue]]; + + default: + return [super readValueOfType:type]; + } +} +@end + +@interface FWFWKWebsiteDataStoreHostApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFWKWebsiteDataStoreHostApiCodecWriter +- (void)writeValue:(id)value { + if ([value isKindOfClass:[FWFWKWebsiteDataTypesEnumData class]]) { + [self writeByte:128]; + [self writeValue:[value toMap]]; + } else { + [super writeValue:value]; + } +} +@end + +@interface FWFWKWebsiteDataStoreHostApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFWKWebsiteDataStoreHostApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFWKWebsiteDataStoreHostApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFWKWebsiteDataStoreHostApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFWKWebsiteDataStoreHostApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFWKWebsiteDataStoreHostApiCodecReaderWriter *readerWriter = + [[FWFWKWebsiteDataStoreHostApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FWFWKWebsiteDataStoreHostApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + @"dev.flutter.pigeon.WKWebsiteDataStoreHostApi.createFromWebViewConfiguration" + binaryMessenger:binaryMessenger + codec:FWFWKWebsiteDataStoreHostApiGetCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(createDataStoreFromConfigurationWithIdentifier: + configurationIdentifier:error:)], + @"FWFWKWebsiteDataStoreHostApi api (%@) doesn't respond to " + @"@selector(createDataStoreFromConfigurationWithIdentifier:configurationIdentifier:error:" + @")", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_configurationInstanceId = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api createDataStoreFromConfigurationWithIdentifier:arg_instanceId + configurationIdentifier:arg_configurationInstanceId + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebsiteDataStoreHostApi.createDefaultDataStore" + binaryMessenger:binaryMessenger + codec:FWFWKWebsiteDataStoreHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(createDefaultDataStoreWithIdentifier:error:)], + @"FWFWKWebsiteDataStoreHostApi api (%@) doesn't respond to " + @"@selector(createDefaultDataStoreWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api createDefaultDataStoreWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes" + binaryMessenger:binaryMessenger + codec:FWFWKWebsiteDataStoreHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (removeDataFromDataStoreWithIdentifier: + ofTypes:secondsModifiedSinceEpoch:completion:)], + @"FWFWKWebsiteDataStoreHostApi api (%@) doesn't respond to " + @"@selector(removeDataFromDataStoreWithIdentifier:ofTypes:" + @"secondsModifiedSinceEpoch:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSArray *arg_dataTypes = GetNullableObjectAtIndex(args, 1); + NSNumber *arg_secondsModifiedSinceEpoch = GetNullableObjectAtIndex(args, 2); + [api removeDataFromDataStoreWithIdentifier:arg_instanceId + ofTypes:arg_dataTypes + secondsModifiedSinceEpoch:arg_secondsModifiedSinceEpoch + completion:^(NSNumber *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +@interface FWFUIViewHostApiCodecReader : FlutterStandardReader +@end +@implementation FWFUIViewHostApiCodecReader +@end + +@interface FWFUIViewHostApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFUIViewHostApiCodecWriter +@end + +@interface FWFUIViewHostApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFUIViewHostApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFUIViewHostApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFUIViewHostApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFUIViewHostApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFUIViewHostApiCodecReaderWriter *readerWriter = + [[FWFUIViewHostApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FWFUIViewHostApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.UIViewHostApi.getContentOffset" + binaryMessenger:binaryMessenger + codec:FWFUIViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(contentOffsetForViewWithIdentifier:error:)], + @"FWFUIViewHostApi api (%@) doesn't respond to " + @"@selector(contentOffsetForViewWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + NSArray *output = [api contentOffsetForViewWithIdentifier:arg_instanceId + error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.UIViewHostApi.setBackgroundColor" + binaryMessenger:binaryMessenger + codec:FWFUIViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(setBackgroundColorForViewWithIdentifier: + toValue:error:)], + @"FWFUIViewHostApi api (%@) doesn't respond to " + @"@selector(setBackgroundColorForViewWithIdentifier:toValue:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_value = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api setBackgroundColorForViewWithIdentifier:arg_instanceId toValue:arg_value error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.UIViewHostApi.setOpaque" + binaryMessenger:binaryMessenger + codec:FWFUIViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(setOpaqueForViewWithIdentifier:isOpaque:error:)], + @"FWFUIViewHostApi api (%@) doesn't respond to " + @"@selector(setOpaqueForViewWithIdentifier:isOpaque:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_opaque = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api setOpaqueForViewWithIdentifier:arg_instanceId isOpaque:arg_opaque error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +@interface FWFUIScrollViewHostApiCodecReader : FlutterStandardReader +@end +@implementation FWFUIScrollViewHostApiCodecReader +@end + +@interface FWFUIScrollViewHostApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFUIScrollViewHostApiCodecWriter +@end + +@interface FWFUIScrollViewHostApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFUIScrollViewHostApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFUIScrollViewHostApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFUIScrollViewHostApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFUIScrollViewHostApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFUIScrollViewHostApiCodecReaderWriter *readerWriter = + [[FWFUIScrollViewHostApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FWFUIScrollViewHostApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.UIScrollViewHostApi.createFromWebView" + binaryMessenger:binaryMessenger + codec:FWFUIScrollViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(createFromWebViewWithIdentifier: + webViewIdentifier:error:)], + @"FWFUIScrollViewHostApi api (%@) doesn't respond to " + @"@selector(createFromWebViewWithIdentifier:webViewIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_webViewInstanceId = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api createFromWebViewWithIdentifier:arg_instanceId + webViewIdentifier:arg_webViewInstanceId + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.UIScrollViewHostApi.getContentOffset" + binaryMessenger:binaryMessenger + codec:FWFUIScrollViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(contentOffsetForScrollViewWithIdentifier:error:)], + @"FWFUIScrollViewHostApi api (%@) doesn't respond to " + @"@selector(contentOffsetForScrollViewWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + NSArray *output = [api contentOffsetForScrollViewWithIdentifier:arg_instanceId + error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.UIScrollViewHostApi.scrollBy" + binaryMessenger:binaryMessenger + codec:FWFUIScrollViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(scrollByForScrollViewWithIdentifier: + toX:y:error:)], + @"FWFUIScrollViewHostApi api (%@) doesn't respond to " + @"@selector(scrollByForScrollViewWithIdentifier:toX:y:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_x = GetNullableObjectAtIndex(args, 1); + NSNumber *arg_y = GetNullableObjectAtIndex(args, 2); + FlutterError *error; + [api scrollByForScrollViewWithIdentifier:arg_instanceId toX:arg_x y:arg_y error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.UIScrollViewHostApi.setContentOffset" + binaryMessenger:binaryMessenger + codec:FWFUIScrollViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (setContentOffsetForScrollViewWithIdentifier:toX:y:error:)], + @"FWFUIScrollViewHostApi api (%@) doesn't respond to " + @"@selector(setContentOffsetForScrollViewWithIdentifier:toX:y:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_x = GetNullableObjectAtIndex(args, 1); + NSNumber *arg_y = GetNullableObjectAtIndex(args, 2); + FlutterError *error; + [api setContentOffsetForScrollViewWithIdentifier:arg_instanceId + toX:arg_x + y:arg_y + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +@interface FWFWKWebViewConfigurationHostApiCodecReader : FlutterStandardReader +@end +@implementation FWFWKWebViewConfigurationHostApiCodecReader +- (nullable id)readValueOfType:(UInt8)type { + switch (type) { + case 128: + return [FWFWKAudiovisualMediaTypeEnumData fromMap:[self readValue]]; + + default: + return [super readValueOfType:type]; + } +} +@end + +@interface FWFWKWebViewConfigurationHostApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFWKWebViewConfigurationHostApiCodecWriter +- (void)writeValue:(id)value { + if ([value isKindOfClass:[FWFWKAudiovisualMediaTypeEnumData class]]) { + [self writeByte:128]; + [self writeValue:[value toMap]]; + } else { + [super writeValue:value]; + } +} +@end + +@interface FWFWKWebViewConfigurationHostApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFWKWebViewConfigurationHostApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFWKWebViewConfigurationHostApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFWKWebViewConfigurationHostApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFWKWebViewConfigurationHostApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFWKWebViewConfigurationHostApiCodecReaderWriter *readerWriter = + [[FWFWKWebViewConfigurationHostApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FWFWKWebViewConfigurationHostApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewConfigurationHostApi.create" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewConfigurationHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(createWithIdentifier:error:)], + @"FWFWKWebViewConfigurationHostApi api (%@) doesn't respond to " + @"@selector(createWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api createWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewConfigurationHostApi.createFromWebView" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewConfigurationHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(createFromWebViewWithIdentifier: + webViewIdentifier:error:)], + @"FWFWKWebViewConfigurationHostApi api (%@) doesn't respond to " + @"@selector(createFromWebViewWithIdentifier:webViewIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_webViewInstanceId = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api createFromWebViewWithIdentifier:arg_instanceId + webViewIdentifier:arg_webViewInstanceId + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + @"dev.flutter.pigeon.WKWebViewConfigurationHostApi.setAllowsInlineMediaPlayback" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewConfigurationHostApiGetCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector + (setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:isAlowed:error:)], + @"FWFWKWebViewConfigurationHostApi api (%@) doesn't respond to " + @"@selector(setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:isAlowed:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_allow = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:arg_instanceId + isAlowed:arg_allow + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewConfigurationHostApi." + @"setMediaTypesRequiringUserActionForPlayback" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewConfigurationHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (setMediaTypesRequiresUserActionForConfigurationWithIdentifier: + forTypes:error:)], + @"FWFWKWebViewConfigurationHostApi api (%@) doesn't respond to " + @"@selector(setMediaTypesRequiresUserActionForConfigurationWithIdentifier:forTypes:" + @"error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSArray *arg_types = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api setMediaTypesRequiresUserActionForConfigurationWithIdentifier:arg_instanceId + forTypes:arg_types + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +@interface FWFWKUserContentControllerHostApiCodecReader : FlutterStandardReader +@end +@implementation FWFWKUserContentControllerHostApiCodecReader +- (nullable id)readValueOfType:(UInt8)type { + switch (type) { + case 128: + return [FWFWKUserScriptData fromMap:[self readValue]]; + + case 129: + return [FWFWKUserScriptInjectionTimeEnumData fromMap:[self readValue]]; + + default: + return [super readValueOfType:type]; + } +} +@end + +@interface FWFWKUserContentControllerHostApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFWKUserContentControllerHostApiCodecWriter +- (void)writeValue:(id)value { + if ([value isKindOfClass:[FWFWKUserScriptData class]]) { + [self writeByte:128]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKUserScriptInjectionTimeEnumData class]]) { + [self writeByte:129]; + [self writeValue:[value toMap]]; + } else { + [super writeValue:value]; + } +} +@end + +@interface FWFWKUserContentControllerHostApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFWKUserContentControllerHostApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFWKUserContentControllerHostApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFWKUserContentControllerHostApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFWKUserContentControllerHostApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFWKUserContentControllerHostApiCodecReaderWriter *readerWriter = + [[FWFWKUserContentControllerHostApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FWFWKUserContentControllerHostApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + @"dev.flutter.pigeon.WKUserContentControllerHostApi.createFromWebViewConfiguration" + binaryMessenger:binaryMessenger + codec:FWFWKUserContentControllerHostApiGetCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(createFromWebViewConfigurationWithIdentifier: + configurationIdentifier:error:)], + @"FWFWKUserContentControllerHostApi api (%@) doesn't respond to " + @"@selector(createFromWebViewConfigurationWithIdentifier:configurationIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_configurationInstanceId = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api createFromWebViewConfigurationWithIdentifier:arg_instanceId + configurationIdentifier:arg_configurationInstanceId + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKUserContentControllerHostApi.addScriptMessageHandler" + binaryMessenger:binaryMessenger + codec:FWFWKUserContentControllerHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (addScriptMessageHandlerForControllerWithIdentifier: + handlerIdentifier:ofName:error:)], + @"FWFWKUserContentControllerHostApi api (%@) doesn't respond to " + @"@selector(addScriptMessageHandlerForControllerWithIdentifier:handlerIdentifier:" + @"ofName:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_handlerInstanceid = GetNullableObjectAtIndex(args, 1); + NSString *arg_name = GetNullableObjectAtIndex(args, 2); + FlutterError *error; + [api addScriptMessageHandlerForControllerWithIdentifier:arg_instanceId + handlerIdentifier:arg_handlerInstanceid + ofName:arg_name + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + @"dev.flutter.pigeon.WKUserContentControllerHostApi.removeScriptMessageHandler" + binaryMessenger:binaryMessenger + codec:FWFWKUserContentControllerHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (removeScriptMessageHandlerForControllerWithIdentifier:name:error:)], + @"FWFWKUserContentControllerHostApi api (%@) doesn't respond to " + @"@selector(removeScriptMessageHandlerForControllerWithIdentifier:name:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSString *arg_name = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api removeScriptMessageHandlerForControllerWithIdentifier:arg_instanceId + name:arg_name + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + @"dev.flutter.pigeon.WKUserContentControllerHostApi.removeAllScriptMessageHandlers" + binaryMessenger:binaryMessenger + codec:FWFWKUserContentControllerHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (removeAllScriptMessageHandlersForControllerWithIdentifier:error:)], + @"FWFWKUserContentControllerHostApi api (%@) doesn't respond to " + @"@selector(removeAllScriptMessageHandlersForControllerWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api removeAllScriptMessageHandlersForControllerWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKUserContentControllerHostApi.addUserScript" + binaryMessenger:binaryMessenger + codec:FWFWKUserContentControllerHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(addUserScriptForControllerWithIdentifier: + userScript:error:)], + @"FWFWKUserContentControllerHostApi api (%@) doesn't respond to " + @"@selector(addUserScriptForControllerWithIdentifier:userScript:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FWFWKUserScriptData *arg_userScript = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api addUserScriptForControllerWithIdentifier:arg_instanceId + userScript:arg_userScript + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKUserContentControllerHostApi.removeAllUserScripts" + binaryMessenger:binaryMessenger + codec:FWFWKUserContentControllerHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (removeAllUserScriptsForControllerWithIdentifier:error:)], + @"FWFWKUserContentControllerHostApi api (%@) doesn't respond to " + @"@selector(removeAllUserScriptsForControllerWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api removeAllUserScriptsForControllerWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +@interface FWFWKPreferencesHostApiCodecReader : FlutterStandardReader +@end +@implementation FWFWKPreferencesHostApiCodecReader +@end + +@interface FWFWKPreferencesHostApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFWKPreferencesHostApiCodecWriter +@end + +@interface FWFWKPreferencesHostApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFWKPreferencesHostApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFWKPreferencesHostApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFWKPreferencesHostApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFWKPreferencesHostApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFWKPreferencesHostApiCodecReaderWriter *readerWriter = + [[FWFWKPreferencesHostApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FWFWKPreferencesHostApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKPreferencesHostApi.createFromWebViewConfiguration" + binaryMessenger:binaryMessenger + codec:FWFWKPreferencesHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(createFromWebViewConfiguration: + configurationIdentifier:error:)], + @"FWFWKPreferencesHostApi api (%@) doesn't respond to " + @"@selector(createFromWebViewConfiguration:configurationIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_configurationInstanceId = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api createFromWebViewConfiguration:arg_instanceId + configurationIdentifier:arg_configurationInstanceId + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKPreferencesHostApi.setJavaScriptEnabled" + binaryMessenger:binaryMessenger + codec:FWFWKPreferencesHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (setJavaScriptEnabledForPreferencesWithIdentifier:isEnabled:error:)], + @"FWFWKPreferencesHostApi api (%@) doesn't respond to " + @"@selector(setJavaScriptEnabledForPreferencesWithIdentifier:isEnabled:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_enabled = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api setJavaScriptEnabledForPreferencesWithIdentifier:arg_instanceId + isEnabled:arg_enabled + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +@interface FWFWKScriptMessageHandlerHostApiCodecReader : FlutterStandardReader +@end +@implementation FWFWKScriptMessageHandlerHostApiCodecReader +@end + +@interface FWFWKScriptMessageHandlerHostApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFWKScriptMessageHandlerHostApiCodecWriter +@end + +@interface FWFWKScriptMessageHandlerHostApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFWKScriptMessageHandlerHostApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFWKScriptMessageHandlerHostApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFWKScriptMessageHandlerHostApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFWKScriptMessageHandlerHostApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFWKScriptMessageHandlerHostApiCodecReaderWriter *readerWriter = + [[FWFWKScriptMessageHandlerHostApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FWFWKScriptMessageHandlerHostApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKScriptMessageHandlerHostApi.create" + binaryMessenger:binaryMessenger + codec:FWFWKScriptMessageHandlerHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(createWithIdentifier:error:)], + @"FWFWKScriptMessageHandlerHostApi api (%@) doesn't respond to " + @"@selector(createWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api createWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +@interface FWFWKNavigationDelegateHostApiCodecReader : FlutterStandardReader +@end +@implementation FWFWKNavigationDelegateHostApiCodecReader +@end + +@interface FWFWKNavigationDelegateHostApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFWKNavigationDelegateHostApiCodecWriter +@end + +@interface FWFWKNavigationDelegateHostApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFWKNavigationDelegateHostApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFWKNavigationDelegateHostApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFWKNavigationDelegateHostApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFWKNavigationDelegateHostApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFWKNavigationDelegateHostApiCodecReaderWriter *readerWriter = + [[FWFWKNavigationDelegateHostApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FWFWKNavigationDelegateHostApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKNavigationDelegateHostApi.create" + binaryMessenger:binaryMessenger + codec:FWFWKNavigationDelegateHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(createWithIdentifier:error:)], + @"FWFWKNavigationDelegateHostApi api (%@) doesn't respond to " + @"@selector(createWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api createWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKNavigationDelegateHostApi.setDidFinishNavigation" + binaryMessenger:binaryMessenger + codec:FWFWKNavigationDelegateHostApiGetCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector + (setDidFinishNavigationForDelegateWithIdentifier:functionIdentifier:error:)], + @"FWFWKNavigationDelegateHostApi api (%@) doesn't respond to " + @"@selector(setDidFinishNavigationForDelegateWithIdentifier:functionIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_functionInstanceId = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api setDidFinishNavigationForDelegateWithIdentifier:arg_instanceId + functionIdentifier:arg_functionInstanceId + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +@interface FWFWKNavigationDelegateFlutterApiCodecReader : FlutterStandardReader +@end +@implementation FWFWKNavigationDelegateFlutterApiCodecReader +@end + +@interface FWFWKNavigationDelegateFlutterApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFWKNavigationDelegateFlutterApiCodecWriter +@end + +@interface FWFWKNavigationDelegateFlutterApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFWKNavigationDelegateFlutterApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFWKNavigationDelegateFlutterApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFWKNavigationDelegateFlutterApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFWKNavigationDelegateFlutterApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFWKNavigationDelegateFlutterApiCodecReaderWriter *readerWriter = + [[FWFWKNavigationDelegateFlutterApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +@interface FWFWKNavigationDelegateFlutterApi () +@property(nonatomic, strong) NSObject *binaryMessenger; +@end + +@implementation FWFWKNavigationDelegateFlutterApi + +- (instancetype)initWithBinaryMessenger:(NSObject *)binaryMessenger { + self = [super init]; + if (self) { + _binaryMessenger = binaryMessenger; + } + return self; +} +- (void)didFinishNavigationForDelegateWithIdentifier:(NSNumber *)arg_functionInstanceId + webViewIdentifier:(NSNumber *)arg_webViewInstanceId + URL:(nullable NSString *)arg_url + completion:(void (^)(NSError *_Nullable))completion { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName: + @"dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation" + binaryMessenger:self.binaryMessenger + codec:FWFWKNavigationDelegateFlutterApiGetCodec()]; + [channel sendMessage:@[ + (arg_functionInstanceId == nil) ? [NSNull null] : arg_functionInstanceId, + (arg_webViewInstanceId == nil) ? [NSNull null] : arg_webViewInstanceId, + (arg_url == nil) ? [NSNull null] : arg_url + ] + reply:^(id reply) { + completion(nil); + }]; +} +@end +@interface FWFNSObjectHostApiCodecReader : FlutterStandardReader +@end +@implementation FWFNSObjectHostApiCodecReader +- (nullable id)readValueOfType:(UInt8)type { + switch (type) { + case 128: + return [FWFNSKeyValueObservingOptionsEnumData fromMap:[self readValue]]; + + default: + return [super readValueOfType:type]; + } +} +@end + +@interface FWFNSObjectHostApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFNSObjectHostApiCodecWriter +- (void)writeValue:(id)value { + if ([value isKindOfClass:[FWFNSKeyValueObservingOptionsEnumData class]]) { + [self writeByte:128]; + [self writeValue:[value toMap]]; + } else { + [super writeValue:value]; + } +} +@end + +@interface FWFNSObjectHostApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFNSObjectHostApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFNSObjectHostApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFNSObjectHostApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFNSObjectHostApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFNSObjectHostApiCodecReaderWriter *readerWriter = + [[FWFNSObjectHostApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FWFNSObjectHostApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.NSObjectHostApi.dispose" + binaryMessenger:binaryMessenger + codec:FWFNSObjectHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(disposeObjectWithIdentifier:error:)], + @"FWFNSObjectHostApi api (%@) doesn't respond to " + @"@selector(disposeObjectWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api disposeObjectWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.NSObjectHostApi.addObserver" + binaryMessenger:binaryMessenger + codec:FWFNSObjectHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (addObserverForObjectWithIdentifier: + observerIdentifier:keyPath:options:error:)], + @"FWFNSObjectHostApi api (%@) doesn't respond to " + @"@selector(addObserverForObjectWithIdentifier:observerIdentifier:keyPath:options:" + @"error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_observerInstanceId = GetNullableObjectAtIndex(args, 1); + NSString *arg_keyPath = GetNullableObjectAtIndex(args, 2); + NSArray *arg_options = + GetNullableObjectAtIndex(args, 3); + FlutterError *error; + [api addObserverForObjectWithIdentifier:arg_instanceId + observerIdentifier:arg_observerInstanceId + keyPath:arg_keyPath + options:arg_options + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.NSObjectHostApi.removeObserver" + binaryMessenger:binaryMessenger + codec:FWFNSObjectHostApiGetCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(removeObserverForObjectWithIdentifier: + observerIdentifier:keyPath:error:)], + @"FWFNSObjectHostApi api (%@) doesn't respond to " + @"@selector(removeObserverForObjectWithIdentifier:observerIdentifier:keyPath:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_observerInstanceId = GetNullableObjectAtIndex(args, 1); + NSString *arg_keyPath = GetNullableObjectAtIndex(args, 2); + FlutterError *error; + [api removeObserverForObjectWithIdentifier:arg_instanceId + observerIdentifier:arg_observerInstanceId + keyPath:arg_keyPath + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +@interface FWFFunctionFlutterApiCodecReader : FlutterStandardReader +@end +@implementation FWFFunctionFlutterApiCodecReader +@end + +@interface FWFFunctionFlutterApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFFunctionFlutterApiCodecWriter +@end + +@interface FWFFunctionFlutterApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFFunctionFlutterApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFFunctionFlutterApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFFunctionFlutterApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFFunctionFlutterApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFFunctionFlutterApiCodecReaderWriter *readerWriter = + [[FWFFunctionFlutterApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +@interface FWFFunctionFlutterApi () +@property(nonatomic, strong) NSObject *binaryMessenger; +@end + +@implementation FWFFunctionFlutterApi + +- (instancetype)initWithBinaryMessenger:(NSObject *)binaryMessenger { + self = [super init]; + if (self) { + _binaryMessenger = binaryMessenger; + } + return self; +} +- (void)disposeFunctionWithIdentifier:(NSNumber *)arg_instanceId + completion:(void (^)(NSError *_Nullable))completion { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.FunctionFlutterApi.dispose" + binaryMessenger:self.binaryMessenger + codec:FWFFunctionFlutterApiGetCodec()]; + [channel sendMessage:@[ (arg_instanceId == nil) ? [NSNull null] : arg_instanceId ] + reply:^(id reply) { + completion(nil); + }]; +} +@end +@interface FWFWKWebViewHostApiCodecReader : FlutterStandardReader +@end +@implementation FWFWKWebViewHostApiCodecReader +- (nullable id)readValueOfType:(UInt8)type { + switch (type) { + case 128: + return [FWFNSHttpCookieData fromMap:[self readValue]]; + + case 129: + return [FWFNSHttpCookiePropertyKeyEnumData fromMap:[self readValue]]; + + case 130: + return [FWFNSKeyValueObservingOptionsEnumData fromMap:[self readValue]]; + + case 131: + return [FWFNSUrlRequestData fromMap:[self readValue]]; + + case 132: + return [FWFWKAudiovisualMediaTypeEnumData fromMap:[self readValue]]; + + case 133: + return [FWFWKUserScriptData fromMap:[self readValue]]; + + case 134: + return [FWFWKUserScriptInjectionTimeEnumData fromMap:[self readValue]]; + + case 135: + return [FWFWKWebsiteDataTypesEnumData fromMap:[self readValue]]; + + default: + return [super readValueOfType:type]; + } +} +@end + +@interface FWFWKWebViewHostApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFWKWebViewHostApiCodecWriter +- (void)writeValue:(id)value { + if ([value isKindOfClass:[FWFNSHttpCookieData class]]) { + [self writeByte:128]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFNSHttpCookiePropertyKeyEnumData class]]) { + [self writeByte:129]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFNSKeyValueObservingOptionsEnumData class]]) { + [self writeByte:130]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFNSUrlRequestData class]]) { + [self writeByte:131]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKAudiovisualMediaTypeEnumData class]]) { + [self writeByte:132]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKUserScriptData class]]) { + [self writeByte:133]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKUserScriptInjectionTimeEnumData class]]) { + [self writeByte:134]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKWebsiteDataTypesEnumData class]]) { + [self writeByte:135]; + [self writeValue:[value toMap]]; + } else { + [super writeValue:value]; + } +} +@end + +@interface FWFWKWebViewHostApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFWKWebViewHostApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFWKWebViewHostApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFWKWebViewHostApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFWKWebViewHostApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFWKWebViewHostApiCodecReaderWriter *readerWriter = + [[FWFWKWebViewHostApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FWFWKWebViewHostApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.create" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(createWithIdentifier: + configurationIdentifier:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(createWithIdentifier:configurationIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_configurationInstanceId = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api createWithIdentifier:arg_instanceId + configurationIdentifier:arg_configurationInstanceId + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.setUIDelegate" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(setUIDelegateForWebViewWithIdentifier: + delegateIdentifier:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(setUIDelegateForWebViewWithIdentifier:delegateIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_uiDelegateInstanceId = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api setUIDelegateForWebViewWithIdentifier:arg_instanceId + delegateIdentifier:arg_uiDelegateInstanceId + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.setNavigationDelegate" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(setNavigationDelegateForWebViewWithIdentifier: + delegateIdentifier:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(setNavigationDelegateForWebViewWithIdentifier:delegateIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_navigationDelegateInstanceId = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api setNavigationDelegateForWebViewWithIdentifier:arg_instanceId + delegateIdentifier:arg_navigationDelegateInstanceId + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.getUrl" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(URLForWebViewWithIdentifier:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(URLForWebViewWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + NSString *output = [api URLForWebViewWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.getEstimatedProgress" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(estimatedProgressForWebViewWithIdentifier: + error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(estimatedProgressForWebViewWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + NSNumber *output = [api estimatedProgressForWebViewWithIdentifier:arg_instanceId + error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.loadRequest" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(loadRequestForWebViewWithIdentifier: + request:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(loadRequestForWebViewWithIdentifier:request:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FWFNSUrlRequestData *arg_request = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api loadRequestForWebViewWithIdentifier:arg_instanceId request:arg_request error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.loadHtmlString" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(loadHTMLForWebViewWithIdentifier: + HTMLString:baseURL:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(loadHTMLForWebViewWithIdentifier:HTMLString:baseURL:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSString *arg_string = GetNullableObjectAtIndex(args, 1); + NSString *arg_baseUrl = GetNullableObjectAtIndex(args, 2); + FlutterError *error; + [api loadHTMLForWebViewWithIdentifier:arg_instanceId + HTMLString:arg_string + baseURL:arg_baseUrl + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.loadFileUrl" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (loadFileForWebViewWithIdentifier:fileURL:readAccessURL:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(loadFileForWebViewWithIdentifier:fileURL:readAccessURL:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSString *arg_url = GetNullableObjectAtIndex(args, 1); + NSString *arg_readAccessUrl = GetNullableObjectAtIndex(args, 2); + FlutterError *error; + [api loadFileForWebViewWithIdentifier:arg_instanceId + fileURL:arg_url + readAccessURL:arg_readAccessUrl + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.loadFlutterAsset" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(loadAssetForWebViewWithIdentifier: + assetKey:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(loadAssetForWebViewWithIdentifier:assetKey:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSString *arg_key = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api loadAssetForWebViewWithIdentifier:arg_instanceId assetKey:arg_key error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.canGoBack" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(canGoBackForWebViewWithIdentifier:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(canGoBackForWebViewWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + NSNumber *output = [api canGoBackForWebViewWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.canGoForward" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(canGoForwardForWebViewWithIdentifier:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(canGoForwardForWebViewWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + NSNumber *output = [api canGoForwardForWebViewWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.goBack" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(goBackForWebViewWithIdentifier:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(goBackForWebViewWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api goBackForWebViewWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.goForward" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(goForwardForWebViewWithIdentifier:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(goForwardForWebViewWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api goForwardForWebViewWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.reload" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(reloadWebViewWithIdentifier:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(reloadWebViewWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api reloadWebViewWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.getTitle" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(titleForWebViewWithIdentifier:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(titleForWebViewWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + NSString *output = [api titleForWebViewWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + @"dev.flutter.pigeon.WKWebViewHostApi.setAllowsBackForwardNavigationGestures" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (setAllowsBackForwardForWebViewWithIdentifier:isAllowed:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(setAllowsBackForwardForWebViewWithIdentifier:isAllowed:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_allow = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api setAllowsBackForwardForWebViewWithIdentifier:arg_instanceId + isAllowed:arg_allow + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.setCustomUserAgent" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(setUserAgentForWebViewWithIdentifier: + userAgent:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(setUserAgentForWebViewWithIdentifier:userAgent:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSString *arg_userAgent = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api setUserAgentForWebViewWithIdentifier:arg_instanceId + userAgent:arg_userAgent + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.evaluateJavaScript" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector + (evaluateJavaScriptForWebViewWithIdentifier:javaScriptString:completion:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(evaluateJavaScriptForWebViewWithIdentifier:javaScriptString:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSString *arg_javaScriptString = GetNullableObjectAtIndex(args, 1); + [api evaluateJavaScriptForWebViewWithIdentifier:arg_instanceId + javaScriptString:arg_javaScriptString + completion:^(id _Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +@interface FWFWKUIDelegateHostApiCodecReader : FlutterStandardReader +@end +@implementation FWFWKUIDelegateHostApiCodecReader +@end + +@interface FWFWKUIDelegateHostApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFWKUIDelegateHostApiCodecWriter +@end + +@interface FWFWKUIDelegateHostApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFWKUIDelegateHostApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFWKUIDelegateHostApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFWKUIDelegateHostApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFWKUIDelegateHostApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFWKUIDelegateHostApiCodecReaderWriter *readerWriter = + [[FWFWKUIDelegateHostApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FWFWKUIDelegateHostApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKUIDelegateHostApi.create" + binaryMessenger:binaryMessenger + codec:FWFWKUIDelegateHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(createWithIdentifier:error:)], + @"FWFWKUIDelegateHostApi api (%@) doesn't respond to " + @"@selector(createWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api createWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +@interface FWFWKHttpCookieStoreHostApiCodecReader : FlutterStandardReader +@end +@implementation FWFWKHttpCookieStoreHostApiCodecReader +- (nullable id)readValueOfType:(UInt8)type { + switch (type) { + case 128: + return [FWFNSHttpCookieData fromMap:[self readValue]]; + + case 129: + return [FWFNSHttpCookiePropertyKeyEnumData fromMap:[self readValue]]; + + default: + return [super readValueOfType:type]; + } +} +@end + +@interface FWFWKHttpCookieStoreHostApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFWKHttpCookieStoreHostApiCodecWriter +- (void)writeValue:(id)value { + if ([value isKindOfClass:[FWFNSHttpCookieData class]]) { + [self writeByte:128]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFNSHttpCookiePropertyKeyEnumData class]]) { + [self writeByte:129]; + [self writeValue:[value toMap]]; + } else { + [super writeValue:value]; + } +} +@end + +@interface FWFWKHttpCookieStoreHostApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFWKHttpCookieStoreHostApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFWKHttpCookieStoreHostApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFWKHttpCookieStoreHostApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFWKHttpCookieStoreHostApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFWKHttpCookieStoreHostApiCodecReaderWriter *readerWriter = + [[FWFWKHttpCookieStoreHostApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FWFWKHttpCookieStoreHostApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKHttpCookieStoreHostApi.createFromWebsiteDataStore" + binaryMessenger:binaryMessenger + codec:FWFWKHttpCookieStoreHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(createFromWebsiteDataStoreWithIdentifier: + dataStoreIdentifier:error:)], + @"FWFWKHttpCookieStoreHostApi api (%@) doesn't respond to " + @"@selector(createFromWebsiteDataStoreWithIdentifier:dataStoreIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_websiteDataStoreInstanceId = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api createFromWebsiteDataStoreWithIdentifier:arg_instanceId + dataStoreIdentifier:arg_websiteDataStoreInstanceId + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKHttpCookieStoreHostApi.setCookie" + binaryMessenger:binaryMessenger + codec:FWFWKHttpCookieStoreHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(setCookieForStoreWithIdentifier:cookie:error:)], + @"FWFWKHttpCookieStoreHostApi api (%@) doesn't respond to " + @"@selector(setCookieForStoreWithIdentifier:cookie:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FWFNSHttpCookieData *arg_cookie = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api setCookieForStoreWithIdentifier:arg_instanceId cookie:arg_cookie error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.m index 0d88d88ba282..445ecd3e2cf9 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.m @@ -13,9 +13,9 @@ @interface FWFInstanceManager () @implementation FWFInstanceManager - (instancetype)init { if (self) { - self.lockQueue = dispatch_queue_create("FWFInstanceManager", DISPATCH_QUEUE_SERIAL); - self.identifiersToInstances = [NSMapTable strongToStrongObjectsMapTable]; - self.instancesToIdentifiers = [NSMapTable strongToStrongObjectsMapTable]; + _lockQueue = dispatch_queue_create("FWFInstanceManager", DISPATCH_QUEUE_SERIAL); + _identifiersToInstances = [NSMapTable strongToStrongObjectsMapTable]; + _instancesToIdentifiers = [NSMapTable strongToStrongObjectsMapTable]; } return self; } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.h new file mode 100644 index 000000000000..968a9a85b2e7 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.h @@ -0,0 +1,40 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import + +#import "FWFGeneratedWebKitApis.h" +#import "FWFInstanceManager.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * A set of Flutter and Dart assets used by a `FlutterEngine` to initialize execution. + * + * Default implementation delegates methods to FlutterDartProject. + */ +@interface FWFAssetManager : NSObject +- (NSString *)lookupKeyForAsset:(NSString *)asset; +@end + +/** + * Implementation of WKWebView that can be used as a FlutterPlatformView. + */ +@interface FWFWebView : WKWebView +@end + +/** + * Host api implementation for WKWebView. + * + * Handles creating WKWebViews that intercommunicate with a paired Dart object. + */ +@interface FWFWebViewHostApiImpl : NSObject +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager + bundle:(NSBundle *)bundle + assetManager:(FWFAssetManager *)assetManager; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m new file mode 100644 index 000000000000..35677575dddb --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m @@ -0,0 +1,249 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FWFWebViewHostApi.h" +#import "FWFDataConverters.h" + +@implementation FWFAssetManager +- (NSString *)lookupKeyForAsset:(NSString *)asset { + return [FlutterDartProject lookupKeyForAsset:asset]; +} +@end + +@implementation FWFWebView +- (void)setFrame:(CGRect)frame { + [super setFrame:frame]; + // Prevents the contentInsets from being adjusted by iOS and gives control to Flutter. + self.scrollView.contentInset = UIEdgeInsetsZero; + if (@available(iOS 11, *)) { + // Above iOS 11, adjust contentInset to compensate the adjustedContentInset so the sum will + // always be 0. + if (UIEdgeInsetsEqualToEdgeInsets(self.scrollView.adjustedContentInset, UIEdgeInsetsZero)) { + return; + } + UIEdgeInsets insetToAdjust = self.scrollView.adjustedContentInset; + self.scrollView.contentInset = UIEdgeInsetsMake(-insetToAdjust.top, -insetToAdjust.left, + -insetToAdjust.bottom, -insetToAdjust.right); + } +} + +- (nonnull UIView *)view { + return self; +} +@end + +@interface FWFWebViewHostApiImpl () +@property(nonatomic) FWFInstanceManager *instanceManager; +@property NSBundle *bundle; +@property FWFAssetManager *assetManager; +@end + +@implementation FWFWebViewHostApiImpl +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { + return [self initWithInstanceManager:instanceManager + bundle:[NSBundle mainBundle] + assetManager:[[FWFAssetManager alloc] init]]; +} + +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager + bundle:(NSBundle *)bundle + assetManager:(FWFAssetManager *)assetManager { + self = [self init]; + if (self) { + _instanceManager = instanceManager; + _bundle = bundle; + _assetManager = assetManager; + } + return self; +} + +- (FWFWebView *)webViewForIdentifier:(NSNumber *)instanceId { + return (FWFWebView *)[self.instanceManager instanceForIdentifier:instanceId.longValue]; +} + ++ (nonnull FlutterError *)errorForURLString:(nonnull NSString *)string { + NSString *errorDetails = [NSString stringWithFormat:@"Initializing NSURL with the supplied " + @"'%@' path resulted in a nil value.", + string]; + return [FlutterError errorWithCode:@"FWFURLParsingError" + message:@"Failed parsing file path." + details:errorDetails]; +} + +- (void)createWithIdentifier:(nonnull NSNumber *)instanceId + configurationIdentifier:(nonnull NSNumber *)configurationInstanceId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + WKWebViewConfiguration *configuration = (WKWebViewConfiguration *)[self.instanceManager + instanceForIdentifier:configurationInstanceId.longValue]; + FWFWebView *webView = [[FWFWebView alloc] initWithFrame:CGRectMake(0, 0, 0, 0) + configuration:configuration]; + [self.instanceManager addInstance:webView withIdentifier:instanceId.longValue]; +} + +- (void)loadRequestForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + request:(nonnull FWFNSUrlRequestData *)request + error: + (FlutterError *_Nullable __autoreleasing *_Nonnull)error { + NSURLRequest *urlRequest = FWFNSURLRequestFromRequestData(request); + if (!urlRequest) { + *error = [FlutterError errorWithCode:@"FWFURLRequestParsingError" + message:@"Failed instantiating an NSURLRequest." + details:[NSString stringWithFormat:@"URL was: '%@'", request.url]]; + return; + } + [[self webViewForIdentifier:instanceId] loadRequest:urlRequest]; +} + +- (void)setUserAgentForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + userAgent:(nullable NSString *)userAgent + error:(FlutterError *_Nullable __autoreleasing *_Nonnull) + error { + [[self webViewForIdentifier:instanceId] setCustomUserAgent:userAgent]; +} + +- (nullable NSNumber *) + canGoBackForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + return @([self webViewForIdentifier:instanceId].canGoBack); +} + +- (nullable NSString *) + URLForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + return [self webViewForIdentifier:instanceId].URL.absoluteString; +} + +- (nullable NSNumber *) + canGoForwardForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + return @([[self webViewForIdentifier:instanceId] canGoForward]); +} + +- (nullable NSNumber *) + estimatedProgressForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull) + error { + return @([[self webViewForIdentifier:instanceId] estimatedProgress]); +} + +- (void)evaluateJavaScriptForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + javaScriptString:(nonnull NSString *)javaScriptString + completion: + (nonnull void (^)(id _Nullable, + FlutterError *_Nullable))completion { + [[self webViewForIdentifier:instanceId] + evaluateJavaScript:javaScriptString + completionHandler:^(id _Nullable result, NSError *_Nullable error) { + id returnValue = nil; + FlutterError *flutterError = nil; + if (!error) { + if (!result || [result isKindOfClass:[NSString class]] || + [result isKindOfClass:[NSNumber class]]) { + returnValue = result; + } else { + NSString *className = NSStringFromClass([result class]); + NSLog(@"Return type of evaluateJavaScript is not directly supported: %@. Returned " + @"description of value.", + className); + returnValue = [result description]; + } + } else { + flutterError = [FlutterError errorWithCode:@"FWFEvaluateJavaScriptError" + message:@"Failed evaluating JavaScript." + details:error]; + } + + completion(returnValue, flutterError); + }]; +} + +- (void)goBackForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + [[self webViewForIdentifier:instanceId] goBack]; +} + +- (void)goForwardForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + [[self webViewForIdentifier:instanceId] goForward]; +} + +- (void)loadAssetForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + assetKey:(nonnull NSString *)key + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + NSString *assetFilePath = [self.assetManager lookupKeyForAsset:key]; + + NSURL *url = [self.bundle URLForResource:[assetFilePath stringByDeletingPathExtension] + withExtension:assetFilePath.pathExtension]; + if (!url) { + *error = [FWFWebViewHostApiImpl errorForURLString:assetFilePath]; + } else { + [[self webViewForIdentifier:instanceId] loadFileURL:url + allowingReadAccessToURL:[url URLByDeletingLastPathComponent]]; + } +} + +- (void)loadFileForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + fileURL:(nonnull NSString *)url + readAccessURL:(nonnull NSString *)readAccessUrl + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + NSURL *fileURL = [NSURL fileURLWithPath:url isDirectory:NO]; + NSURL *readAccessNSURL = [NSURL fileURLWithPath:readAccessUrl isDirectory:YES]; + + if (!fileURL) { + *error = [FWFWebViewHostApiImpl errorForURLString:url]; + } else if (!readAccessNSURL) { + *error = [FWFWebViewHostApiImpl errorForURLString:readAccessUrl]; + } else { + [[self webViewForIdentifier:instanceId] loadFileURL:fileURL + allowingReadAccessToURL:readAccessNSURL]; + } +} + +- (void)loadHTMLForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + HTMLString:(nonnull NSString *)string + baseURL:(nullable NSString *)baseUrl + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + [[self webViewForIdentifier:instanceId] loadHTMLString:string + baseURL:[NSURL URLWithString:baseUrl]]; +} + +- (void)reloadWebViewWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + [[self webViewForIdentifier:instanceId] reload]; +} + +- (void) + setAllowsBackForwardForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + isAllowed:(nonnull NSNumber *)allow + error:(FlutterError *_Nullable __autoreleasing *_Nonnull) + error { + [[self webViewForIdentifier:instanceId] setAllowsBackForwardNavigationGestures:allow.boolValue]; +} + +- (void) + setNavigationDelegateForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + delegateIdentifier:(nullable NSNumber *)navigationDelegateInstanceId + error: + (FlutterError *_Nullable __autoreleasing *_Nonnull) + error { + id navigationDelegate = (id)[self.instanceManager + instanceForIdentifier:navigationDelegateInstanceId.longValue]; + [[self webViewForIdentifier:instanceId] setNavigationDelegate:navigationDelegate]; +} + +- (void)setUIDelegateForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + delegateIdentifier:(nullable NSNumber *)uiDelegateInstanceId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull) + error { + id navigationDelegate = + (id)[self.instanceManager instanceForIdentifier:uiDelegateInstanceId.longValue]; + [[self webViewForIdentifier:instanceId] setUIDelegate:navigationDelegate]; +} + +- (nullable NSString *) + titleForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + return [[self webViewForIdentifier:instanceId] title]; +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h index 3743f13f4ac7..9a7fd1b5ef3f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h @@ -7,6 +7,9 @@ #import #import #import +#import +#import #import +#import #import #import diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart index 6395f4171f45..fa52b43c396e 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v2.0.3), do not edit directly. +// Autogenerated from Pigeon (v3.0.3), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name // @dart = 2.12 @@ -87,7 +87,7 @@ class NSKeyValueObservingOptionsEnumData { Object encode() { final Map pigeonMap = {}; - pigeonMap['value'] = value == null ? null : value!.index; + pigeonMap['value'] = value?.index; return pigeonMap; } @@ -110,7 +110,7 @@ class WKUserScriptInjectionTimeEnumData { Object encode() { final Map pigeonMap = {}; - pigeonMap['value'] = value == null ? null : value!.index; + pigeonMap['value'] = value?.index; return pigeonMap; } @@ -133,7 +133,7 @@ class WKAudiovisualMediaTypeEnumData { Object encode() { final Map pigeonMap = {}; - pigeonMap['value'] = value == null ? null : value!.index; + pigeonMap['value'] = value?.index; return pigeonMap; } @@ -156,7 +156,7 @@ class WKWebsiteDataTypesEnumData { Object encode() { final Map pigeonMap = {}; - pigeonMap['value'] = value == null ? null : value!.index; + pigeonMap['value'] = value?.index; return pigeonMap; } @@ -179,7 +179,7 @@ class NSHttpCookiePropertyKeyEnumData { Object encode() { final Map pigeonMap = {}; - pigeonMap['value'] = value == null ? null : value!.index; + pigeonMap['value'] = value?.index; return pigeonMap; } @@ -242,8 +242,7 @@ class WKUserScriptData { Object encode() { final Map pigeonMap = {}; pigeonMap['source'] = source; - pigeonMap['injectionTime'] = - injectionTime == null ? null : injectionTime!.encode(); + pigeonMap['injectionTime'] = injectionTime?.encode(); pigeonMap['isMainFrameOnly'] = isMainFrameOnly; return pigeonMap; } @@ -1169,10 +1168,8 @@ abstract class WKNavigationDelegateFlutterApi { assert(arg_webViewInstanceId != null, 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation was null, expected non-null int.'); final String? arg_url = (args[2] as String?); - assert(arg_url != null, - 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation was null, expected non-null String.'); api.didFinishNavigation( - arg_functionInstanceId!, arg_webViewInstanceId!, arg_url!); + arg_functionInstanceId!, arg_webViewInstanceId!, arg_url); return; }); } @@ -1334,9 +1331,30 @@ class _WKWebViewHostApiCodec extends StandardMessageCodec { const _WKWebViewHostApiCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is NSUrlRequestData) { + if (value is NSHttpCookieData) { buffer.putUint8(128); writeValue(buffer, value.encode()); + } else if (value is NSHttpCookiePropertyKeyEnumData) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is NSKeyValueObservingOptionsEnumData) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else if (value is NSUrlRequestData) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); + } else if (value is WKAudiovisualMediaTypeEnumData) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); + } else if (value is WKUserScriptData) { + buffer.putUint8(133); + writeValue(buffer, value.encode()); + } else if (value is WKUserScriptInjectionTimeEnumData) { + buffer.putUint8(134); + writeValue(buffer, value.encode()); + } else if (value is WKWebsiteDataTypesEnumData) { + buffer.putUint8(135); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -1346,8 +1364,29 @@ class _WKWebViewHostApiCodec extends StandardMessageCodec { Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { case 128: + return NSHttpCookieData.decode(readValue(buffer)!); + + case 129: + return NSHttpCookiePropertyKeyEnumData.decode(readValue(buffer)!); + + case 130: + return NSKeyValueObservingOptionsEnumData.decode(readValue(buffer)!); + + case 131: return NSUrlRequestData.decode(readValue(buffer)!); + case 132: + return WKAudiovisualMediaTypeEnumData.decode(readValue(buffer)!); + + case 133: + return WKUserScriptData.decode(readValue(buffer)!); + + case 134: + return WKUserScriptInjectionTimeEnumData.decode(readValue(buffer)!); + + case 135: + return WKWebsiteDataTypesEnumData.decode(readValue(buffer)!); + default: return super.readValueOfType(type, buffer); } @@ -1803,13 +1842,13 @@ class WKWebViewHostApi { } } - Future evaluateJavaScript( - int arg_instanceId, String arg_javascriptString) async { + Future evaluateJavaScript( + int arg_instanceId, String arg_javaScriptString) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebViewHostApi.evaluateJavaScript', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_javascriptString]) + await channel.send([arg_instanceId, arg_javaScriptString]) as Map?; if (replyMap == null) { throw PlatformException( @@ -1824,13 +1863,8 @@ class WKWebViewHostApi { message: error['message'] as String?, details: error['details'], ); - } else if (replyMap['result'] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); } else { - return (replyMap['result'] as String?)!; + return (replyMap['result'] as Object?); } } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart index 23d8712b0486..509fa8e1f7dd 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart @@ -8,11 +8,22 @@ import 'package:pigeon/pigeon.dart'; PigeonOptions( dartOut: 'lib/src/common/web_kit.pigeon.dart', dartTestOut: 'test/src/common/test_web_kit.pigeon.dart', - dartOptions: DartOptions(isNullSafe: true, copyrightHeader: [ + dartOptions: DartOptions(copyrightHeader: [ 'Copyright 2013 The Flutter Authors. All rights reserved.', 'Use of this source code is governed by a BSD-style license that can be', 'found in the LICENSE file.', ]), + objcHeaderOut: 'ios/Classes/FWFGeneratedWebKitApis.h', + objcSourceOut: 'ios/Classes/FWFGeneratedWebKitApis.m', + objcOptions: ObjcOptions( + header: 'ios/Classes/FWFGeneratedWebKitApis.h', + prefix: 'FWF', + copyrightHeader: [ + 'Copyright 2013 The Flutter Authors. All rights reserved.', + 'Use of this source code is governed by a BSD-style license that can be', + 'found in the LICENSE file.', + ], + ), ), ) @@ -204,13 +215,20 @@ class NSHttpCookieData { /// See https://developer.apple.com/documentation/webkit/wkwebsitedatastore?language=objc. @HostApi(dartHostTestHandler: 'TestWKWebsiteDataStoreHostApi') abstract class WKWebsiteDataStoreHostApi { + @ObjCSelector( + 'createDataStoreFromConfigurationWithIdentifier:configurationIdentifier:', + ) void createFromWebViewConfiguration( int instanceId, int configurationInstanceId, ); + @ObjCSelector('createDefaultDataStoreWithIdentifier:') void createDefaultDataStore(int instanceId); + @ObjCSelector( + 'removeDataFromDataStoreWithIdentifier:ofTypes:secondsModifiedSinceEpoch:', + ) @async bool removeDataOfTypes( int instanceId, @@ -224,10 +242,13 @@ abstract class WKWebsiteDataStoreHostApi { /// See https://developer.apple.com/documentation/uikit/uiview?language=objc. @HostApi(dartHostTestHandler: 'TestUIViewHostApi') abstract class UIViewHostApi { + @ObjCSelector('contentOffsetForViewWithIdentifier:') List getContentOffset(int instanceId); + @ObjCSelector('setBackgroundColorForViewWithIdentifier:toValue:') void setBackgroundColor(int instanceId, int? value); + @ObjCSelector('setOpaqueForViewWithIdentifier:isOpaque:') void setOpaque(int instanceId, bool opaque); } @@ -236,12 +257,16 @@ abstract class UIViewHostApi { /// See https://developer.apple.com/documentation/uikit/uiscrollview?language=objc. @HostApi(dartHostTestHandler: 'TestUIScrollViewHostApi') abstract class UIScrollViewHostApi { + @ObjCSelector('createFromWebViewWithIdentifier:webViewIdentifier:') void createFromWebView(int instanceId, int webViewInstanceId); + @ObjCSelector('contentOffsetForScrollViewWithIdentifier:') List getContentOffset(int instanceId); + @ObjCSelector('scrollByForScrollViewWithIdentifier:toX:y:') void scrollBy(int instanceId, double x, double y); + @ObjCSelector('setContentOffsetForScrollViewWithIdentifier:toX:y:') void setContentOffset(int instanceId, double x, double y); } @@ -250,12 +275,20 @@ abstract class UIScrollViewHostApi { /// See https://developer.apple.com/documentation/webkit/wkwebviewconfiguration?language=objc. @HostApi(dartHostTestHandler: 'TestWKWebViewConfigurationHostApi') abstract class WKWebViewConfigurationHostApi { + @ObjCSelector('createWithIdentifier:') void create(int instanceId); + @ObjCSelector('createFromWebViewWithIdentifier:webViewIdentifier:') void createFromWebView(int instanceId, int webViewInstanceId); + @ObjCSelector( + 'setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:isAlowed:', + ) void setAllowsInlineMediaPlayback(int instanceId, bool allow); + @ObjCSelector( + 'setMediaTypesRequiresUserActionForConfigurationWithIdentifier:forTypes:', + ) void setMediaTypesRequiringUserActionForPlayback( int instanceId, List types, @@ -267,23 +300,33 @@ abstract class WKWebViewConfigurationHostApi { /// See https://developer.apple.com/documentation/webkit/wkusercontentcontroller?language=objc. @HostApi(dartHostTestHandler: 'TestWKUserContentControllerHostApi') abstract class WKUserContentControllerHostApi { + @ObjCSelector( + 'createFromWebViewConfigurationWithIdentifier:configurationIdentifier:', + ) void createFromWebViewConfiguration( int instanceId, int configurationInstanceId, ); + @ObjCSelector( + 'addScriptMessageHandlerForControllerWithIdentifier:handlerIdentifier:ofName:', + ) void addScriptMessageHandler( int instanceId, int handlerInstanceid, String name, ); + @ObjCSelector('removeScriptMessageHandlerForControllerWithIdentifier:name:') void removeScriptMessageHandler(int instanceId, String name); + @ObjCSelector('removeAllScriptMessageHandlersForControllerWithIdentifier:') void removeAllScriptMessageHandlers(int instanceId); + @ObjCSelector('addUserScriptForControllerWithIdentifier:userScript:') void addUserScript(int instanceId, WKUserScriptData userScript); + @ObjCSelector('removeAllUserScriptsForControllerWithIdentifier:') void removeAllUserScripts(int instanceId); } @@ -292,11 +335,13 @@ abstract class WKUserContentControllerHostApi { /// See https://developer.apple.com/documentation/webkit/wkpreferences?language=objc. @HostApi(dartHostTestHandler: 'TestWKPreferencesHostApi') abstract class WKPreferencesHostApi { + @ObjCSelector('createFromWebViewConfiguration:configurationIdentifier:') void createFromWebViewConfiguration( int instanceId, int configurationInstanceId, ); + @ObjCSelector('setJavaScriptEnabledForPreferencesWithIdentifier:isEnabled:') void setJavaScriptEnabled(int instanceId, bool enabled); } @@ -305,6 +350,7 @@ abstract class WKPreferencesHostApi { /// See https://developer.apple.com/documentation/webkit/wkscriptmessagehandler?language=objc. @HostApi(dartHostTestHandler: 'TestWKScriptMessageHandlerHostApi') abstract class WKScriptMessageHandlerHostApi { + @ObjCSelector('createWithIdentifier:') void create(int instanceId); } @@ -313,8 +359,12 @@ abstract class WKScriptMessageHandlerHostApi { /// See https://developer.apple.com/documentation/webkit/wknavigationdelegate?language=objc. @HostApi(dartHostTestHandler: 'TestWKNavigationDelegateHostApi') abstract class WKNavigationDelegateHostApi { + @ObjCSelector('createWithIdentifier:') void create(int instanceId); + @ObjCSelector( + 'setDidFinishNavigationForDelegateWithIdentifier:functionIdentifier:', + ) void setDidFinishNavigation(int instanceId, int? functionInstanceId); } @@ -323,6 +373,9 @@ abstract class WKNavigationDelegateHostApi { /// See https://developer.apple.com/documentation/webkit/wknavigationdelegate?language=objc. @FlutterApi() abstract class WKNavigationDelegateFlutterApi { + @ObjCSelector( + 'didFinishNavigationForDelegateWithIdentifier:webViewIdentifier:URL:', + ) void didFinishNavigation( int functionInstanceId, int webViewInstanceId, @@ -335,8 +388,12 @@ abstract class WKNavigationDelegateFlutterApi { /// See https://developer.apple.com/documentation/objectivec/nsobject. @HostApi(dartHostTestHandler: 'TestNSObjectHostApi') abstract class NSObjectHostApi { + @ObjCSelector('disposeObjectWithIdentifier:') void dispose(int instanceId); + @ObjCSelector( + 'addObserverForObjectWithIdentifier:observerIdentifier:keyPath:options:', + ) void addObserver( int instanceId, int observerInstanceId, @@ -344,12 +401,16 @@ abstract class NSObjectHostApi { List options, ); + @ObjCSelector( + 'removeObserverForObjectWithIdentifier:observerIdentifier:keyPath:', + ) void removeObserver(int instanceId, int observerInstanceId, String keyPath); } /// Disposes references to functions. @FlutterApi() abstract class FunctionFlutterApi { + @ObjCSelector('disposeFunctionWithIdentifier:') void dispose(int instanceId); } @@ -358,42 +419,62 @@ abstract class FunctionFlutterApi { /// See https://developer.apple.com/documentation/webkit/wkwebview?language=objc. @HostApi(dartHostTestHandler: 'TestWKWebViewHostApi') abstract class WKWebViewHostApi { + @ObjCSelector('createWithIdentifier:configurationIdentifier:') void create(int instanceId, int configurationInstanceId); + @ObjCSelector('setUIDelegateForWebViewWithIdentifier:delegateIdentifier:') void setUIDelegate(int instanceId, int? uiDelegateInstanceId); + @ObjCSelector( + 'setNavigationDelegateForWebViewWithIdentifier:delegateIdentifier:', + ) void setNavigationDelegate(int instanceId, int? navigationDelegateInstanceId); + @ObjCSelector('URLForWebViewWithIdentifier:') String? getUrl(int instanceId); + @ObjCSelector('estimatedProgressForWebViewWithIdentifier:') double getEstimatedProgress(int instanceId); + @ObjCSelector('loadRequestForWebViewWithIdentifier:request:') void loadRequest(int instanceId, NSUrlRequestData request); + @ObjCSelector('loadHTMLForWebViewWithIdentifier:HTMLString:baseURL:') void loadHtmlString(int instanceId, String string, String? baseUrl); + @ObjCSelector('loadFileForWebViewWithIdentifier:fileURL:readAccessURL:') void loadFileUrl(int instanceId, String url, String readAccessUrl); + @ObjCSelector('loadAssetForWebViewWithIdentifier:assetKey:') void loadFlutterAsset(int instanceId, String key); + @ObjCSelector('canGoBackForWebViewWithIdentifier:') bool canGoBack(int instanceId); + @ObjCSelector('canGoForwardForWebViewWithIdentifier:') bool canGoForward(int instanceId); + @ObjCSelector('goBackForWebViewWithIdentifier:') void goBack(int instanceId); + @ObjCSelector('goForwardForWebViewWithIdentifier:') void goForward(int instanceId); + @ObjCSelector('reloadWebViewWithIdentifier:') void reload(int instanceId); + @ObjCSelector('titleForWebViewWithIdentifier:') String? getTitle(int instanceId); + @ObjCSelector('setAllowsBackForwardForWebViewWithIdentifier:isAllowed:') void setAllowsBackForwardNavigationGestures(int instanceId, bool allow); + @ObjCSelector('setUserAgentForWebViewWithIdentifier:userAgent:') void setCustomUserAgent(int instanceId, String? userAgent); + @ObjCSelector('evaluateJavaScriptForWebViewWithIdentifier:javaScriptString:') @async - String evaluateJavaScript(int instanceId, String javascriptString); + Object? evaluateJavaScript(int instanceId, String javaScriptString); } /// Mirror of WKUIDelegate. @@ -401,6 +482,7 @@ abstract class WKWebViewHostApi { /// See https://developer.apple.com/documentation/webkit/wkuidelegate?language=objc. @HostApi(dartHostTestHandler: 'TestWKUIDelegateHostApi') abstract class WKUIDelegateHostApi { + @ObjCSelector('createWithIdentifier:') void create(int instanceId); } @@ -409,10 +491,12 @@ abstract class WKUIDelegateHostApi { /// See https://developer.apple.com/documentation/webkit/wkhttpcookiestore?language=objc. @HostApi(dartHostTestHandler: 'TestWKHttpCookieStoreHostApi') abstract class WKHttpCookieStoreHostApi { + @ObjCSelector('createFromWebsiteDataStoreWithIdentifier:dataStoreIdentifier:') void createFromWebsiteDataStore( int instanceId, int websiteDataStoreInstanceId, ); + @ObjCSelector('setCookieForStoreWithIdentifier:cookie:') void setCookie(int instanceId, NSHttpCookieData cookie); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index e52d1df9bbb6..f4e72b8f14eb 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -29,4 +29,4 @@ dev_dependencies: sdk: flutter mockito: ^5.1.0 pedantic: ^1.10.0 - pigeon: ^2.0.3 + pigeon: ^3.0.3 diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart index 67ae8882cbd3..042ddedbd769 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v2.0.3), do not edit directly. +// Autogenerated from Pigeon (v3.0.3), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis // ignore_for_file: avoid_relative_lib_imports @@ -172,9 +172,7 @@ abstract class TestUIViewHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.UIViewHostApi.setBackgroundColor was null, expected non-null int.'); final int? arg_value = (args[1] as int?); - assert(arg_value != null, - 'Argument for dev.flutter.pigeon.UIViewHostApi.setBackgroundColor was null, expected non-null int.'); - api.setBackgroundColor(arg_instanceId!, arg_value!); + api.setBackgroundColor(arg_instanceId!, arg_value); return {}; }); } @@ -766,9 +764,7 @@ abstract class TestWKNavigationDelegateHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WKNavigationDelegateHostApi.setDidFinishNavigation was null, expected non-null int.'); final int? arg_functionInstanceId = (args[1] as int?); - assert(arg_functionInstanceId != null, - 'Argument for dev.flutter.pigeon.WKNavigationDelegateHostApi.setDidFinishNavigation was null, expected non-null int.'); - api.setDidFinishNavigation(arg_instanceId!, arg_functionInstanceId!); + api.setDidFinishNavigation(arg_instanceId!, arg_functionInstanceId); return {}; }); } @@ -892,9 +888,30 @@ class _TestWKWebViewHostApiCodec extends StandardMessageCodec { const _TestWKWebViewHostApiCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is NSUrlRequestData) { + if (value is NSHttpCookieData) { buffer.putUint8(128); writeValue(buffer, value.encode()); + } else if (value is NSHttpCookiePropertyKeyEnumData) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is NSKeyValueObservingOptionsEnumData) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else if (value is NSUrlRequestData) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); + } else if (value is WKAudiovisualMediaTypeEnumData) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); + } else if (value is WKUserScriptData) { + buffer.putUint8(133); + writeValue(buffer, value.encode()); + } else if (value is WKUserScriptInjectionTimeEnumData) { + buffer.putUint8(134); + writeValue(buffer, value.encode()); + } else if (value is WKWebsiteDataTypesEnumData) { + buffer.putUint8(135); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -904,8 +921,29 @@ class _TestWKWebViewHostApiCodec extends StandardMessageCodec { Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { case 128: + return NSHttpCookieData.decode(readValue(buffer)!); + + case 129: + return NSHttpCookiePropertyKeyEnumData.decode(readValue(buffer)!); + + case 130: + return NSKeyValueObservingOptionsEnumData.decode(readValue(buffer)!); + + case 131: return NSUrlRequestData.decode(readValue(buffer)!); + case 132: + return WKAudiovisualMediaTypeEnumData.decode(readValue(buffer)!); + + case 133: + return WKUserScriptData.decode(readValue(buffer)!); + + case 134: + return WKUserScriptInjectionTimeEnumData.decode(readValue(buffer)!); + + case 135: + return WKWebsiteDataTypesEnumData.decode(readValue(buffer)!); + default: return super.readValueOfType(type, buffer); } @@ -932,7 +970,7 @@ abstract class TestWKWebViewHostApi { String? getTitle(int instanceId); void setAllowsBackForwardNavigationGestures(int instanceId, bool allow); void setCustomUserAgent(int instanceId, String? userAgent); - Future evaluateJavaScript(int instanceId, String javascriptString); + Future evaluateJavaScript(int instanceId, String javaScriptString); static void setup(TestWKWebViewHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -972,9 +1010,7 @@ abstract class TestWKWebViewHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setUIDelegate was null, expected non-null int.'); final int? arg_uiDelegateInstanceId = (args[1] as int?); - assert(arg_uiDelegateInstanceId != null, - 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setUIDelegate was null, expected non-null int.'); - api.setUIDelegate(arg_instanceId!, arg_uiDelegateInstanceId!); + api.setUIDelegate(arg_instanceId!, arg_uiDelegateInstanceId); return {}; }); } @@ -994,10 +1030,8 @@ abstract class TestWKWebViewHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setNavigationDelegate was null, expected non-null int.'); final int? arg_navigationDelegateInstanceId = (args[1] as int?); - assert(arg_navigationDelegateInstanceId != null, - 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setNavigationDelegate was null, expected non-null int.'); api.setNavigationDelegate( - arg_instanceId!, arg_navigationDelegateInstanceId!); + arg_instanceId!, arg_navigationDelegateInstanceId); return {}; }); } @@ -1080,9 +1114,7 @@ abstract class TestWKWebViewHostApi { assert(arg_string != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadHtmlString was null, expected non-null String.'); final String? arg_baseUrl = (args[2] as String?); - assert(arg_baseUrl != null, - 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadHtmlString was null, expected non-null String.'); - api.loadHtmlString(arg_instanceId!, arg_string!, arg_baseUrl!); + api.loadHtmlString(arg_instanceId!, arg_string!, arg_baseUrl); return {}; }); } @@ -1287,9 +1319,7 @@ abstract class TestWKWebViewHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setCustomUserAgent was null, expected non-null int.'); final String? arg_userAgent = (args[1] as String?); - assert(arg_userAgent != null, - 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setCustomUserAgent was null, expected non-null String.'); - api.setCustomUserAgent(arg_instanceId!, arg_userAgent!); + api.setCustomUserAgent(arg_instanceId!, arg_userAgent); return {}; }); } @@ -1308,11 +1338,11 @@ abstract class TestWKWebViewHostApi { final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.evaluateJavaScript was null, expected non-null int.'); - final String? arg_javascriptString = (args[1] as String?); - assert(arg_javascriptString != null, + final String? arg_javaScriptString = (args[1] as String?); + assert(arg_javaScriptString != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.evaluateJavaScript was null, expected non-null String.'); - final String output = await api.evaluateJavaScript( - arg_instanceId!, arg_javascriptString!); + final Object? output = await api.evaluateJavaScript( + arg_instanceId!, arg_javaScriptString!); return {'result': output}; }); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.mocks.dart index e876aa63cd29..908709c90134 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.mocks.dart @@ -140,12 +140,12 @@ class MockTestWKWebViewHostApi extends _i1.Mock Invocation.method(#setCustomUserAgent, [instanceId, userAgent]), returnValueForMissingStub: null); @override - _i4.Future evaluateJavaScript( - int? instanceId, String? javascriptString) => + _i4.Future evaluateJavaScript( + int? instanceId, String? javaScriptString) => (super.noSuchMethod( Invocation.method( - #evaluateJavaScript, [instanceId, javascriptString]), - returnValue: Future.value('')) as _i4.Future); + #evaluateJavaScript, [instanceId, javaScriptString]), + returnValue: Future.value()) as _i4.Future); } /// A class which mocks [TestUIScrollViewHostApi]. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart index 67c06286185c..d4f1fefc190c 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.1.0 from annotations -// in webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart. +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart. // Do not manually edit this file. import 'dart:async' as _i4; @@ -278,12 +278,12 @@ class MockTestWKWebViewHostApi extends _i1.Mock Invocation.method(#setCustomUserAgent, [instanceId, userAgent]), returnValueForMissingStub: null); @override - _i4.Future evaluateJavaScript( - int? instanceId, String? javascriptString) => + _i4.Future evaluateJavaScript( + int? instanceId, String? javaScriptString) => (super.noSuchMethod( Invocation.method( - #evaluateJavaScript, [instanceId, javascriptString]), - returnValue: Future.value('')) as _i4.Future); + #evaluateJavaScript, [instanceId, javaScriptString]), + returnValue: Future.value()) as _i4.Future); } /// A class which mocks [TestWKWebsiteDataStoreHostApi]. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index bc79f656b6e3..1138d13f0c25 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.1.0 from annotations -// in webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. // Do not manually edit this file. import 'dart:async' as _i5; @@ -179,6 +179,33 @@ class MockWKNavigationDelegate extends _i1.Mock [webViewWebContentProcessDidTerminate]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future addObserver(_i7.NSObject? observer, + {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) => + (super.noSuchMethod( + Invocation.method( + #addObserver, [observer], {#keyPath: keyPath, #options: options}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future removeObserver(_i7.NSObject? observer, {String? keyPath}) => + (super.noSuchMethod( + Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future dispose() => + (super.noSuchMethod(Invocation.method(#dispose, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future setObserveValue( + void Function( + String, _i7.NSObject, Map<_i7.NSKeyValueChangeKey, Object?>)? + observeValue) => + (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [WKPreferences]. From bd6406b99eba3814101e8d40d2ef2e134d22fc82 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 30 Apr 2022 18:59:07 -0400 Subject: [PATCH 473/600] Roll Flutter from 2a24ee494db4 to 7b8d50020338 (1 revision) (#5457) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 81dd56edbc55..8c88219c1b12 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2a24ee494db49d2240692ef191e568e735518cb9 +7b8d500203381fedc9f89e6c6915c5431f310e09 From 60a676ee1e2df32038947270e85ff25abe44b0be Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 30 Apr 2022 20:04:08 -0400 Subject: [PATCH 474/600] Roll Flutter from 7b8d50020338 to 6755ba59f436 (1 revision) (#5458) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 8c88219c1b12..3647d7f9f53e 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -7b8d500203381fedc9f89e6c6915c5431f310e09 +6755ba59f43644cfeb82116b9b5595a8ddf464be From 629d506932d9d25c663ea19bd24912c8fa88b0ed Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 1 May 2022 00:39:09 -0400 Subject: [PATCH 475/600] Roll Flutter from 6755ba59f436 to 43555d05040c (1 revision) (#5459) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 3647d7f9f53e..39c16ba28144 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -6755ba59f43644cfeb82116b9b5595a8ddf464be +43555d05040c7c4d7db2dabc39c407567df4823e From 45bc9e431471dbbbc710fbbc8bde2a97a663743a Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 1 May 2022 03:24:06 -0400 Subject: [PATCH 476/600] Roll Flutter from 43555d05040c to 0f1eb8bad788 (2 revisions) (#5461) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 39c16ba28144..4d7165f03617 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -43555d05040c7c4d7db2dabc39c407567df4823e +0f1eb8bad7885317be8d843d4486c110be3132bf From 3ded4261346e44948cba2da1138c38ba8e1eb6db Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 1 May 2022 10:49:08 -0400 Subject: [PATCH 477/600] Roll Flutter from 0f1eb8bad788 to e3d39f62a26b (1 revision) (#5462) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 4d7165f03617..2d12523bd5ec 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -0f1eb8bad7885317be8d843d4486c110be3132bf +e3d39f62a26bc256f992101c5ae2f95c26a8fd3d From 3a6cab77f1fc0a5b2fc567d2a64fda35fc0512d7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 1 May 2022 13:24:11 -0400 Subject: [PATCH 478/600] Roll Flutter from e3d39f62a26b to e66662f1cb75 (1 revision) (#5463) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 2d12523bd5ec..b2cde3347077 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -e3d39f62a26bc256f992101c5ae2f95c26a8fd3d +e66662f1cb75d513935b5c67eb9678d1b3273187 From 9304c3d1d01131cd786837767a100a109ec54773 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 1 May 2022 14:29:08 -0400 Subject: [PATCH 479/600] Roll Flutter from e66662f1cb75 to a35af5b4659a (1 revision) (#5464) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index b2cde3347077..bc0a82139dcb 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -e66662f1cb75d513935b5c67eb9678d1b3273187 +a35af5b4659a39eeba47cff0b863c9a5883f8686 From 29a954e02c482c890eefb39964d7530ad75c36b3 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 1 May 2022 15:34:06 -0400 Subject: [PATCH 480/600] Roll Flutter from a35af5b4659a to d600a3b48801 (1 revision) (#5465) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index bc0a82139dcb..f5e79613312f 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -a35af5b4659a39eeba47cff0b863c9a5883f8686 +d600a3b48801e856a1345e4d36b705de467093cf From f7d26e94df0b9be2c1ab28b467cd28f23e0e7193 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 1 May 2022 23:44:05 -0400 Subject: [PATCH 481/600] Roll Flutter from d600a3b48801 to f7df22590adf (1 revision) (#5466) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index f5e79613312f..7249b2760418 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -d600a3b48801e856a1345e4d36b705de467093cf +f7df22590adf2fad70f6e83a2df73e9fa866ddfe From bbcb88d3bae4fa1637cc61681e9ab8862e3146c8 Mon Sep 17 00:00:00 2001 From: Jeevan Chandra Joshi Date: Mon, 2 May 2022 23:29:12 +0530 Subject: [PATCH 482/600] remove unnecessary imports (#5410) --- analysis_options.yaml | 3 --- analysis_options_legacy.yaml | 3 --- packages/camera/camera/CHANGELOG.md | 4 ++++ packages/camera/camera/lib/src/camera_image.dart | 1 - packages/camera/camera/test/camera_image_test.dart | 2 -- packages/camera/camera/test/camera_preview_test.dart | 2 -- packages/camera/camera/test/camera_test.dart | 1 - packages/camera/camera/test/camera_value_test.dart | 1 - packages/camera/camera_platform_interface/CHANGELOG.md | 4 ++++ .../lib/src/events/camera_event.dart | 1 - .../lib/src/method_channel/method_channel_camera.dart | 4 ---- .../lib/src/platform_interface/camera_platform.dart | 5 ----- .../test/events/camera_event_test.dart | 2 -- .../test/method_channel/method_channel_camera_test.dart | 3 --- packages/camera/camera_windows/CHANGELOG.md | 4 ++++ packages/camera/camera_windows/lib/camera_windows.dart | 1 - packages/file_selector/file_selector/CHANGELOG.md | 1 + .../file_selector/test/file_selector_test.dart | 1 - packages/file_selector/file_selector_macos/CHANGELOG.md | 4 ++++ .../file_selector_macos/lib/file_selector_macos.dart | 1 - .../file_selector_platform_interface/CHANGELOG.md | 4 ++++ .../src/method_channel/method_channel_file_selector.dart | 1 - .../lib/src/platform_interface/file_selector_interface.dart | 1 - packages/file_selector/file_selector_windows/CHANGELOG.md | 4 ++++ .../file_selector_windows/lib/file_selector_windows.dart | 1 - .../google_maps_flutter/google_maps_flutter/CHANGELOG.md | 4 ++++ .../example/integration_test/google_maps_test.dart | 1 - .../google_maps_flutter/example/lib/lite_mode.dart | 1 - .../google_maps_flutter/example/lib/map_click.dart | 1 - .../google_maps_flutter/example/lib/map_coordinates.dart | 1 - .../google_maps_flutter/example/lib/scrolling_map.dart | 1 - .../google_maps_flutter/lib/google_maps_flutter.dart | 1 - .../google_maps_flutter_platform_interface/CHANGELOG.md | 4 ++++ .../lib/src/events/map_event.dart | 1 - .../method_channel/method_channel_google_maps_flutter.dart | 1 - .../platform_interface/google_maps_flutter_platform.dart | 1 - .../method_channel_google_maps_flutter_test.dart | 3 --- .../google_maps_flutter_platform_test.dart | 1 - .../google_maps_flutter_web/CHANGELOG.md | 4 ++++ .../example/integration_test/projection_test.dart | 1 - .../lib/google_maps_flutter_web.dart | 1 - .../google_sign_in_platform_interface/CHANGELOG.md | 4 ++++ .../lib/src/method_channel_google_sign_in.dart | 1 - .../test/method_channel_google_sign_in_test.dart | 1 - packages/image_picker/image_picker_ios/CHANGELOG.md | 4 ++++ packages/image_picker/image_picker_ios/test/test_api.dart | 1 - packages/image_picker/image_picker_windows/CHANGELOG.md | 6 +++++- .../image_picker/image_picker_windows/example/lib/main.dart | 1 - packages/in_app_purchase/in_app_purchase/CHANGELOG.md | 1 + .../in_app_purchase/lib/in_app_purchase.dart | 1 - .../in_app_purchase/in_app_purchase_android/CHANGELOG.md | 4 ++++ .../src/billing_client_wrappers/billing_client_wrapper.dart | 2 -- .../lib/src/in_app_purchase_android_platform.dart | 1 - .../lib/src/in_app_purchase_android_platform_addition.dart | 1 - .../in_app_purchase_android_platform_addition_test.dart | 1 - .../in_app_purchase_platform_interface/CHANGELOG.md | 4 ++++ .../test/src/types/product_details_test.dart | 1 - .../in_app_purchase/in_app_purchase_storekit/CHANGELOG.md | 4 ++++ .../lib/src/in_app_purchase_storekit_platform.dart | 1 - .../src/store_kit_wrappers/sk_payment_queue_wrapper.dart | 3 --- .../test/fakes/fake_storekit_platform.dart | 1 - .../test/store_kit_wrappers/sk_product_test.dart | 1 - packages/ios_platform_images/CHANGELOG.md | 4 ++++ packages/ios_platform_images/lib/ios_platform_images.dart | 1 - packages/local_auth/local_auth/CHANGELOG.md | 4 ++++ packages/local_auth/local_auth/lib/src/local_auth.dart | 2 -- packages/local_auth/local_auth/test/local_auth_test.dart | 3 --- packages/local_auth/local_auth_android/CHANGELOG.md | 4 ++++ .../local_auth_android/lib/local_auth_android.dart | 3 --- .../local_auth/local_auth_android/test/local_auth_test.dart | 3 --- packages/local_auth/local_auth_ios/CHANGELOG.md | 4 ++++ packages/local_auth/local_auth_ios/example/lib/main.dart | 2 -- packages/local_auth/local_auth_ios/lib/local_auth_ios.dart | 3 --- .../local_auth/local_auth_ios/test/local_auth_test.dart | 3 --- .../local_auth/local_auth_platform_interface/CHANGELOG.md | 4 ++++ .../lib/default_method_channel_platform.dart | 3 --- .../test/default_method_channel_platform_test.dart | 4 ---- packages/path_provider/path_provider/CHANGELOG.md | 1 + .../path_provider/test/path_provider_test.dart | 1 - .../path_provider_platform_interface/CHANGELOG.md | 4 ++++ .../lib/src/method_channel_path_provider.dart | 2 -- packages/quick_actions/quick_actions/CHANGELOG.md | 1 + .../quick_actions/test/quick_actions_test.dart | 2 -- .../shared_preferences_linux/CHANGELOG.md | 4 ++++ .../example/integration_test/shared_preferences_test.dart | 2 -- .../shared_preferences_macos/CHANGELOG.md | 4 ++++ .../example/integration_test/shared_preferences_test.dart | 2 -- packages/url_launcher/url_launcher/CHANGELOG.md | 4 ++++ .../url_launcher/url_launcher/lib/src/url_launcher_uri.dart | 2 -- .../url_launcher/url_launcher/test/src/legacy_api_test.dart | 1 - packages/video_player/video_player/CHANGELOG.md | 4 ++++ packages/video_player/video_player/example/lib/main.dart | 1 - .../video_player/video_player/test/video_player_test.dart | 2 -- packages/video_player/video_player_android/CHANGELOG.md | 4 ++++ .../video_player/video_player_android/example/lib/main.dart | 1 - .../video_player_android/example/lib/mini_controller.dart | 1 - .../video_player_android/lib/src/android_video_player.dart | 1 - .../video_player/video_player_avfoundation/CHANGELOG.md | 4 ++++ .../video_player_avfoundation/example/lib/main.dart | 1 - .../example/lib/mini_controller.dart | 1 - .../lib/src/avfoundation_video_player.dart | 1 - .../video_player_platform_interface/CHANGELOG.md | 4 ++++ .../lib/method_channel_video_player.dart | 1 - packages/video_player/video_player_web/CHANGELOG.md | 4 ++++ .../video_player/video_player_web/lib/src/video_player.dart | 1 - packages/webview_flutter/webview_flutter/CHANGELOG.md | 4 ++++ .../example/integration_test/webview_flutter_test.dart | 2 -- .../webview_flutter/webview_flutter/lib/src/webview.dart | 3 --- .../webview_flutter/webview_flutter_android/CHANGELOG.md | 4 ++++ .../example/integration_test/webview_flutter_test.dart | 1 - .../webview_flutter_android/example/lib/main.dart | 1 - .../test/webview_android_widget_test.dart | 1 - .../webview_flutter_platform_interface/CHANGELOG.md | 4 ++++ .../lib/src/method_channel/webview_method_channel.dart | 1 - .../lib/src/types/creation_params.dart | 3 --- .../src/method_channel/webview_method_channel_test.dart | 1 - .../javascript_channel_registry_test.dart | 1 - packages/webview_flutter/webview_flutter_web/CHANGELOG.md | 1 + .../webview_flutter_web/example/lib/main.dart | 1 - .../webview_flutter_web/example/lib/web_view.dart | 1 - .../webview_flutter/webview_flutter_wkwebview/CHANGELOG.md | 4 ++++ .../example/integration_test/webview_flutter_test.dart | 1 - .../webview_flutter_wkwebview/example/lib/main.dart | 1 - .../lib/src/web_kit_webview_widget.dart | 1 - .../test/src/web_kit_cookie_manager_test.dart | 2 -- .../test/src/web_kit_webview_widget_test.dart | 2 -- 126 files changed, 138 insertions(+), 140 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 60d2c33601eb..f6177cd9939a 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -49,9 +49,6 @@ analyzer: # Allow null checks for as long as mixed mode is officially supported. unnecessary_null_comparison: false always_require_non_null_named_parameters: false # not needed with nnbd - # TODO(https://github.com/flutter/flutter/issues/74381): - # Clean up existing unnecessary imports, and remove line to ignore. - unnecessary_import: ignore exclude: # Ignore generated files - '**/*.g.dart' diff --git a/analysis_options_legacy.yaml b/analysis_options_legacy.yaml index b2a343f220c8..da3c18071650 100644 --- a/analysis_options_legacy.yaml +++ b/analysis_options_legacy.yaml @@ -8,9 +8,6 @@ analyzer: - '**/*.pigeon.dart' # Pigeon generated file errors: always_require_non_null_named_parameters: false # not needed with nnbd - # TODO(https://github.com/flutter/flutter/issues/74381): - # Clean up existing unnecessary imports, and remove line to ignore. - unnecessary_import: ignore unnecessary_null_comparison: false # Turned as long as nnbd mix-mode is supported. linter: rules: diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index a9986697b048..4d7e9bbeb218 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 0.9.4+21 * Fixes README code samples. diff --git a/packages/camera/camera/lib/src/camera_image.dart b/packages/camera/camera/lib/src/camera_image.dart index fd3a3d6233bc..0f2377ed170c 100644 --- a/packages/camera/camera/lib/src/camera_image.dart +++ b/packages/camera/camera/lib/src/camera_image.dart @@ -6,7 +6,6 @@ import 'dart:typed_data'; import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; /// A single color plane of image data. /// diff --git a/packages/camera/camera/test/camera_image_test.dart b/packages/camera/camera/test/camera_image_test.dart index b09a14177121..55bf4a2727e2 100644 --- a/packages/camera/camera/test/camera_image_test.dart +++ b/packages/camera/camera/test/camera_image_test.dart @@ -5,8 +5,6 @@ import 'dart:typed_data'; import 'package:camera/camera.dart'; -import 'package:camera_platform_interface/camera_platform_interface.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; diff --git a/packages/camera/camera/test/camera_preview_test.dart b/packages/camera/camera/test/camera_preview_test.dart index 76bfe40605d7..fe2f4f4e35c7 100644 --- a/packages/camera/camera/test/camera_preview_test.dart +++ b/packages/camera/camera/test/camera_preview_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; - import 'package:camera/camera.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; diff --git a/packages/camera/camera/test/camera_test.dart b/packages/camera/camera/test/camera_test.dart index c4e0c9388231..34a474b2b4f3 100644 --- a/packages/camera/camera/test/camera_test.dart +++ b/packages/camera/camera/test/camera_test.dart @@ -4,7 +4,6 @@ import 'dart:async'; import 'dart:math'; -import 'dart:ui'; import 'package:camera/camera.dart'; import 'package:camera_platform_interface/camera_platform_interface.dart'; diff --git a/packages/camera/camera/test/camera_value_test.dart b/packages/camera/camera/test/camera_value_test.dart index 62df1fd1a2a1..d718d5e48f02 100644 --- a/packages/camera/camera/test/camera_value_test.dart +++ b/packages/camera/camera/test/camera_value_test.dart @@ -5,7 +5,6 @@ import 'dart:ui'; import 'package:camera/camera.dart'; -import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; diff --git a/packages/camera/camera_platform_interface/CHANGELOG.md b/packages/camera/camera_platform_interface/CHANGELOG.md index ef251eab9b93..3cad35d71ae5 100644 --- a/packages/camera/camera_platform_interface/CHANGELOG.md +++ b/packages/camera/camera_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.1.6 * Adopts `Object.hash`. diff --git a/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart b/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart index 755006cab8ab..a6ace8f9ae74 100644 --- a/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart +++ b/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:camera_platform_interface/src/types/focus_mode.dart'; import 'package:flutter/foundation.dart' show immutable; import '../../camera_platform_interface.dart'; diff --git a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart index ec84c204b2c6..c856f3467821 100644 --- a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart +++ b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart @@ -6,11 +6,7 @@ import 'dart:async'; import 'dart:math'; import 'package:camera_platform_interface/camera_platform_interface.dart'; -import 'package:camera_platform_interface/src/events/device_event.dart'; -import 'package:camera_platform_interface/src/types/focus_mode.dart'; -import 'package:camera_platform_interface/src/types/image_format_group.dart'; import 'package:camera_platform_interface/src/utils/utils.dart'; -import 'package:cross_file/cross_file.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; diff --git a/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart b/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart index 0d240496086d..daa19b8b4011 100644 --- a/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart +++ b/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart @@ -6,12 +6,7 @@ import 'dart:async'; import 'dart:math'; import 'package:camera_platform_interface/camera_platform_interface.dart'; -import 'package:camera_platform_interface/src/events/device_event.dart'; import 'package:camera_platform_interface/src/method_channel/method_channel_camera.dart'; -import 'package:camera_platform_interface/src/types/exposure_mode.dart'; -import 'package:camera_platform_interface/src/types/focus_mode.dart'; -import 'package:camera_platform_interface/src/types/image_format_group.dart'; -import 'package:cross_file/cross_file.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; diff --git a/packages/camera/camera_platform_interface/test/events/camera_event_test.dart b/packages/camera/camera_platform_interface/test/events/camera_event_test.dart index 8a428e2fd43a..3914859d44b0 100644 --- a/packages/camera/camera_platform_interface/test/events/camera_event_test.dart +++ b/packages/camera/camera_platform_interface/test/events/camera_event_test.dart @@ -3,8 +3,6 @@ // found in the LICENSE file. import 'package:camera_platform_interface/camera_platform_interface.dart'; -import 'package:camera_platform_interface/src/types/exposure_mode.dart'; -import 'package:camera_platform_interface/src/types/focus_mode.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { diff --git a/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart b/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart index 27fe7c6b7166..7da4262cdf79 100644 --- a/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart +++ b/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart @@ -7,11 +7,8 @@ import 'dart:math'; import 'package:async/async.dart'; import 'package:camera_platform_interface/camera_platform_interface.dart'; -import 'package:camera_platform_interface/src/events/device_event.dart'; import 'package:camera_platform_interface/src/method_channel/method_channel_camera.dart'; -import 'package:camera_platform_interface/src/types/focus_mode.dart'; import 'package:camera_platform_interface/src/utils/utils.dart'; -import 'package:flutter/services.dart' hide DeviceOrientation; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; diff --git a/packages/camera/camera_windows/CHANGELOG.md b/packages/camera/camera_windows/CHANGELOG.md index 1318780830f8..b1383dc54993 100644 --- a/packages/camera/camera_windows/CHANGELOG.md +++ b/packages/camera/camera_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 0.1.0 * Initial release diff --git a/packages/camera/camera_windows/lib/camera_windows.dart b/packages/camera/camera_windows/lib/camera_windows.dart index 33f8bfb68fac..d998863d43a7 100644 --- a/packages/camera/camera_windows/lib/camera_windows.dart +++ b/packages/camera/camera_windows/lib/camera_windows.dart @@ -6,7 +6,6 @@ import 'dart:async'; import 'dart:math'; import 'package:camera_platform_interface/camera_platform_interface.dart'; -import 'package:cross_file/cross_file.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:stream_transform/stream_transform.dart'; diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index 7eeedad37348..c0821fed7446 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Removes unnecessary imports. * Adds OS version support information to README. ## 0.8.4+1 diff --git a/packages/file_selector/file_selector/test/file_selector_test.dart b/packages/file_selector/file_selector/test/file_selector_test.dart index 6ab0bd975036..fc3e668f9d9e 100644 --- a/packages/file_selector/file_selector/test/file_selector_test.dart +++ b/packages/file_selector/file_selector/test/file_selector_test.dart @@ -6,7 +6,6 @@ import 'package:file_selector/file_selector.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -import 'package:test/fake.dart'; void main() { late FakeFileSelector fakePlatformImplementation; diff --git a/packages/file_selector/file_selector_macos/CHANGELOG.md b/packages/file_selector/file_selector_macos/CHANGELOG.md index 794d056811f4..b46a174bd323 100644 --- a/packages/file_selector/file_selector_macos/CHANGELOG.md +++ b/packages/file_selector/file_selector_macos/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 0.8.2 * Moves source to flutter/plugins. diff --git a/packages/file_selector/file_selector_macos/lib/file_selector_macos.dart b/packages/file_selector/file_selector_macos/lib/file_selector_macos.dart index e321d331961b..e50c296b005f 100644 --- a/packages/file_selector/file_selector_macos/lib/file_selector_macos.dart +++ b/packages/file_selector/file_selector_macos/lib/file_selector_macos.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:cross_file/cross_file.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter/services.dart'; diff --git a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md index b633bd35a59e..100b6ad136a7 100644 --- a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md +++ b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.0.4 * Removes dependency on `meta`. diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart index f1fa82b6f3c6..c6d0f4a56155 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:cross_file/cross_file.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter/services.dart'; diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart index f8fa83bd18d2..a23957af9110 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart @@ -4,7 +4,6 @@ import 'dart:async'; -import 'package:cross_file/cross_file.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; diff --git a/packages/file_selector/file_selector_windows/CHANGELOG.md b/packages/file_selector/file_selector_windows/CHANGELOG.md index 63999f245d82..ae3cd13342b1 100644 --- a/packages/file_selector/file_selector_windows/CHANGELOG.md +++ b/packages/file_selector/file_selector_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 0.8.2 * Moves source to flutter/plugins, and restructures to allow for unit testing. diff --git a/packages/file_selector/file_selector_windows/lib/file_selector_windows.dart b/packages/file_selector/file_selector_windows/lib/file_selector_windows.dart index a8b159711e2a..b91a22355572 100644 --- a/packages/file_selector/file_selector_windows/lib/file_selector_windows.dart +++ b/packages/file_selector/file_selector_windows/lib/file_selector_windows.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:cross_file/cross_file.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter/services.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index 61b151427520..b0662b89d9cc 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.1.4 * Updates Android Google maps sdk version to `18.0.2`. diff --git a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart index a007dddd9188..35351505e495 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart @@ -8,7 +8,6 @@ import 'dart:typed_data'; import 'dart:ui' as ui; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:integration_test/integration_test.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart index 0ecc5ed38e87..b1b58fdc91bf 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart @@ -5,7 +5,6 @@ // ignore_for_file: public_member_api_docs import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart index ef1bfe2b11dc..bbe2372dce9a 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart @@ -5,7 +5,6 @@ // ignore_for_file: public_member_api_docs import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart index dc4376a19547..8e4853c040ed 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart @@ -5,7 +5,6 @@ // ignore_for_file: public_member_api_docs import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart index 8d046fc6b387..04769315e685 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart @@ -7,7 +7,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart b/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart index 5b1e67c943dd..9736b8b5e06f 100644 --- a/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart +++ b/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart @@ -7,7 +7,6 @@ library google_maps_flutter; import 'dart:async'; import 'dart:io'; import 'dart:typed_data'; -import 'dart:ui'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md index b955bc033210..b6e15d2ad14c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.1.6 * Migrates from `ui.hash*` to `Object.hash*`. diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/events/map_event.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/events/map_event.dart index 614cbe8e29fb..bb4124612be4 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/events/map_event.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/events/map_event.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; -import 'package:google_maps_flutter_platform_interface/src/method_channel/method_channel_google_maps_flutter.dart'; /// Generic Event coming from the native side of Maps. /// diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/method_channel/method_channel_google_maps_flutter.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/method_channel/method_channel_google_maps_flutter.dart index 99f4fddaccd3..9c5cbf5a54f0 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/method_channel/method_channel_google_maps_flutter.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/method_channel/method_channel_google_maps_flutter.dart @@ -14,7 +14,6 @@ import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platf import 'package:stream_transform/stream_transform.dart'; import '../types/tile_overlay_updates.dart'; -import '../types/utils/tile_overlay.dart'; /// Error thrown when an unknown map ID is provided to a method channel API. class UnknownMapIDError extends Error { diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/platform_interface/google_maps_flutter_platform.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/platform_interface/google_maps_flutter_platform.dart index 6b39973134be..b6c7b8be692d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/platform_interface/google_maps_flutter_platform.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/platform_interface/google_maps_flutter_platform.dart @@ -11,7 +11,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; -import 'package:google_maps_flutter_platform_interface/src/method_channel/method_channel_google_maps_flutter.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/method_channel/method_channel_google_maps_flutter_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/method_channel/method_channel_google_maps_flutter_test.dart index 176f702ff0ff..9ae42ced6c42 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/method_channel/method_channel_google_maps_flutter_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/method_channel/method_channel_google_maps_flutter_test.dart @@ -5,10 +5,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:google_maps_flutter_platform_interface/src/events/map_event.dart'; -import 'package:google_maps_flutter_platform_interface/src/method_channel/method_channel_google_maps_flutter.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; -import 'dart:async'; import 'package:async/async.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/platform_interface/google_maps_flutter_platform_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/platform_interface/google_maps_flutter_platform_test.dart index c381f9e30750..bdbaff7e2599 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/platform_interface/google_maps_flutter_platform_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/platform_interface/google_maps_flutter_platform_test.dart @@ -10,7 +10,6 @@ import 'package:mockito/mockito.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -import 'package:google_maps_flutter_platform_interface/src/method_channel/method_channel_google_maps_flutter.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; void main() { diff --git a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md index 8a3b94151de2..48908b984b0f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 0.3.2+1 * Removes dependency on `meta`. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/projection_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/projection_test.dart index 8a5a62013538..1bf0f10f50c8 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/projection_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/projection_test.dart @@ -10,7 +10,6 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart' show GoogleMap, GoogleMapController; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart index 0355f2923528..c3079dc2492d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart @@ -10,7 +10,6 @@ import 'dart:js_util'; import 'src/shims/dart_ui.dart' as ui; // Conditionally imports dart:ui in web import 'dart:convert'; -import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter/material.dart'; import 'package:flutter/foundation.dart'; diff --git a/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md b/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md index 66fdb3e72a56..da214d3ce6a9 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.1.2 * Internal code cleanup for stricter analysis options. diff --git a/packages/google_sign_in/google_sign_in_platform_interface/lib/src/method_channel_google_sign_in.dart b/packages/google_sign_in/google_sign_in_platform_interface/lib/src/method_channel_google_sign_in.dart index 1abda09fa99f..e56d2028a205 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/lib/src/method_channel_google_sign_in.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/lib/src/method_channel_google_sign_in.dart @@ -8,7 +8,6 @@ import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter/services.dart'; import '../google_sign_in_platform_interface.dart'; -import 'types.dart'; import 'utils.dart'; /// An implementation of [GoogleSignInPlatform] that uses method channels. diff --git a/packages/google_sign_in/google_sign_in_platform_interface/test/method_channel_google_sign_in_test.dart b/packages/google_sign_in/google_sign_in_platform_interface/test/method_channel_google_sign_in_test.dart index a1d83c3f05e6..b6604d1e658e 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/test/method_channel_google_sign_in_test.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/test/method_channel_google_sign_in_test.dart @@ -5,7 +5,6 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; -import 'package:google_sign_in_platform_interface/src/types.dart'; import 'package:google_sign_in_platform_interface/src/utils.dart'; const Map kUserData = { diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index 3380d14418c2..31a0795a4e30 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 0.8.5 * Switches to an in-package method channel based on Pigeon. diff --git a/packages/image_picker/image_picker_ios/test/test_api.dart b/packages/image_picker/image_picker_ios/test/test_api.dart index 1f76e871521d..d22a26b2489b 100644 --- a/packages/image_picker/image_picker_ios/test/test_api.dart +++ b/packages/image_picker/image_picker_ios/test/test_api.dart @@ -6,7 +6,6 @@ // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis // ignore_for_file: avoid_relative_lib_imports // @dart = 2.12 -import 'dart:async'; import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; import 'package:flutter/services.dart'; diff --git a/packages/image_picker/image_picker_windows/CHANGELOG.md b/packages/image_picker/image_picker_windows/CHANGELOG.md index c4a71a0176b5..d98656b849c8 100644 --- a/packages/image_picker/image_picker_windows/CHANGELOG.md +++ b/packages/image_picker/image_picker_windows/CHANGELOG.md @@ -1,3 +1,7 @@ -# 0.1.0 +## NEXT + +* Removes unnecessary imports. + +## 0.1.0 * Initial Windows support. diff --git a/packages/image_picker/image_picker_windows/example/lib/main.dart b/packages/image_picker/image_picker_windows/example/lib/main.dart index af97115676c0..577d6dadf2d9 100644 --- a/packages/image_picker/image_picker_windows/example/lib/main.dart +++ b/packages/image_picker/image_picker_windows/example/lib/main.dart @@ -7,7 +7,6 @@ import 'dart:async'; import 'dart:io'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; import 'package:video_player/video_player.dart'; diff --git a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md index cfe625a0c5b4..24ef9eaffd1d 100644 --- a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Removes unnecessary imports. * Adds OS version support information to README. ## 3.0.2 diff --git a/packages/in_app_purchase/in_app_purchase/lib/in_app_purchase.dart b/packages/in_app_purchase/in_app_purchase/lib/in_app_purchase.dart index df05d8ce86c3..d550e48ebc3a 100644 --- a/packages/in_app_purchase/in_app_purchase/lib/in_app_purchase.dart +++ b/packages/in_app_purchase/in_app_purchase/lib/in_app_purchase.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; import 'package:in_app_purchase_android/in_app_purchase_android.dart'; import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart'; diff --git a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md index 626dba704ad7..2657d504ac91 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 0.2.2+3 * Migrates from `ui.hash*` to `Object.hash*`. diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.dart index 416eb5680770..7378aeb84cfc 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.dart @@ -10,8 +10,6 @@ import 'package:json_annotation/json_annotation.dart'; import '../../billing_client_wrappers.dart'; import '../channel.dart'; -import 'purchase_wrapper.dart'; -import 'sku_details_wrapper.dart'; part 'billing_client_wrapper.g.dart'; diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform.dart index 61af75688a01..14dd69364497 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform.dart @@ -7,7 +7,6 @@ import 'dart:async'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:in_app_purchase_android/in_app_purchase_android.dart'; -import 'package:in_app_purchase_android/src/in_app_purchase_android_platform_addition.dart'; import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; import '../billing_client_wrappers.dart'; diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform_addition.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform_addition.dart index 9bcfc3d1b007..dd629164866f 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform_addition.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform_addition.dart @@ -7,7 +7,6 @@ import 'package:in_app_purchase_android/in_app_purchase_android.dart'; import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; import '../billing_client_wrappers.dart'; -import 'types/types.dart'; /// Contains InApp Purchase features that are only available on PlayStore. class InAppPurchaseAndroidPlatformAddition diff --git a/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_addition_test.dart b/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_addition_test.dart index 9d2045b4c229..c87d0e39f0c2 100644 --- a/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_addition_test.dart +++ b/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_addition_test.dart @@ -8,7 +8,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:in_app_purchase_android/billing_client_wrappers.dart'; import 'package:in_app_purchase_android/in_app_purchase_android.dart'; import 'package:in_app_purchase_android/src/channel.dart'; -import 'package:in_app_purchase_android/src/in_app_purchase_android_platform_addition.dart'; import 'billing_client_wrappers/purchase_wrapper_test.dart'; import 'stub_in_app_purchase_platform.dart'; diff --git a/packages/in_app_purchase/in_app_purchase_platform_interface/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_platform_interface/CHANGELOG.md index a52d8d244f5f..f7d3268d1cae 100644 --- a/packages/in_app_purchase/in_app_purchase_platform_interface/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 1.3.1 * Update to use the `verify` method introduced in plugin_platform_interface 2.1.0. diff --git a/packages/in_app_purchase/in_app_purchase_platform_interface/test/src/types/product_details_test.dart b/packages/in_app_purchase/in_app_purchase_platform_interface/test/src/types/product_details_test.dart index 737f0d00b392..486f38fa850c 100644 --- a/packages/in_app_purchase/in_app_purchase_platform_interface/test/src/types/product_details_test.dart +++ b/packages/in_app_purchase/in_app_purchase_platform_interface/test/src/types/product_details_test.dart @@ -4,7 +4,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; -import 'package:in_app_purchase_platform_interface/src/types/purchase_status.dart'; void main() { group('Constructor Tests', () { diff --git a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md index 4af33f177799..403ee32be2ae 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 0.3.0+5 * Migrates from `ui.hash*` to `Object.hash*`. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart index 6db2e59e1485..629355d12d4c 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart @@ -7,7 +7,6 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; -import 'package:in_app_purchase_storekit/src/in_app_purchase_storekit_platform_addition.dart'; import '../in_app_purchase_storekit.dart'; import '../store_kit_wrappers.dart'; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart index 819c291a1441..70db7da2e275 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart @@ -12,9 +12,6 @@ import 'package:json_annotation/json_annotation.dart'; import '../channel.dart'; import '../in_app_purchase_storekit_platform.dart'; -import 'sk_payment_queue_delegate_wrapper.dart'; -import 'sk_payment_transaction_wrappers.dart'; -import 'sk_product_wrapper.dart'; part 'sk_payment_queue_wrapper.g.dart'; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart index 9667d789f1f7..7c543750c25e 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; import 'dart:io'; import 'package:flutter/services.dart'; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_test.dart index fdf80d68a3ea..41329335dcf4 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:in_app_purchase_storekit/src/store_kit_wrappers/sk_product_wrapper.dart'; import 'package:in_app_purchase_storekit/src/types/app_store_product_details.dart'; import 'package:in_app_purchase_storekit/src/types/app_store_purchase_details.dart'; import 'package:in_app_purchase_storekit/store_kit_wrappers.dart'; diff --git a/packages/ios_platform_images/CHANGELOG.md b/packages/ios_platform_images/CHANGELOG.md index 5e7d997cc711..0a3755afa7e7 100644 --- a/packages/ios_platform_images/CHANGELOG.md +++ b/packages/ios_platform_images/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 0.2.0+5 * Migrates from `ui.hash*` to `Object.hash*`. diff --git a/packages/ios_platform_images/lib/ios_platform_images.dart b/packages/ios_platform_images/lib/ios_platform_images.dart index 9632b3b2c05d..4064fb312506 100644 --- a/packages/ios_platform_images/lib/ios_platform_images.dart +++ b/packages/ios_platform_images/lib/ios_platform_images.dart @@ -8,7 +8,6 @@ import 'dart:ui' as ui; import 'package:flutter/foundation.dart' show SynchronousFuture, describeIdentity, immutable, objectRuntimeType; -import 'package:flutter/painting.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index deac871d935f..c495acae5a81 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.0.0 * Migrates plugin to federated architecture. diff --git a/packages/local_auth/local_auth/lib/src/local_auth.dart b/packages/local_auth/local_auth/lib/src/local_auth.dart index 508e2b14e129..77db4d57f018 100644 --- a/packages/local_auth/local_auth/lib/src/local_auth.dart +++ b/packages/local_auth/local_auth/lib/src/local_auth.dart @@ -15,8 +15,6 @@ import 'package:local_auth/src/types/error_codes.dart'; import 'package:local_auth_android/local_auth_android.dart'; import 'package:local_auth_ios/local_auth_ios.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; -import 'package:local_auth_platform_interface/types/auth_messages.dart'; -import 'package:local_auth_platform_interface/types/biometric_type.dart'; /// A Flutter plugin for authenticating the user identity locally. class LocalAuthentication { diff --git a/packages/local_auth/local_auth/test/local_auth_test.dart b/packages/local_auth/local_auth/test/local_auth_test.dart index d3f92dfe95db..069f9fec2966 100644 --- a/packages/local_auth/local_auth/test/local_auth_test.dart +++ b/packages/local_auth/local_auth/test/local_auth_test.dart @@ -5,12 +5,9 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:local_auth/local_auth.dart'; -import 'package:local_auth/src/local_auth.dart'; import 'package:local_auth_android/local_auth_android.dart'; import 'package:local_auth_ios/local_auth_ios.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; -import 'package:local_auth_platform_interface/types/auth_messages.dart'; -import 'package:local_auth_platform_interface/types/auth_options.dart'; import 'package:mockito/mockito.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index cbf686a2abcb..6afcf1ffed07 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 1.0.2 * Fixes `getEnrolledBiometrics` to match documented behaviour: diff --git a/packages/local_auth/local_auth_android/lib/local_auth_android.dart b/packages/local_auth/local_auth_android/lib/local_auth_android.dart index 2ce0f88477c7..dfe785cc176f 100644 --- a/packages/local_auth/local_auth_android/lib/local_auth_android.dart +++ b/packages/local_auth/local_auth_android/lib/local_auth_android.dart @@ -5,9 +5,6 @@ import 'package:flutter/services.dart'; import 'package:local_auth_android/types/auth_messages_android.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; -import 'package:local_auth_platform_interface/types/auth_messages.dart'; -import 'package:local_auth_platform_interface/types/auth_options.dart'; -import 'package:local_auth_platform_interface/types/biometric_type.dart'; export 'package:local_auth_android/types/auth_messages_android.dart'; export 'package:local_auth_platform_interface/types/auth_messages.dart'; diff --git a/packages/local_auth/local_auth_android/test/local_auth_test.dart b/packages/local_auth/local_auth_android/test/local_auth_test.dart index 55ac92626aea..86e5713f4bd6 100644 --- a/packages/local_auth/local_auth_android/test/local_auth_test.dart +++ b/packages/local_auth/local_auth_android/test/local_auth_test.dart @@ -2,12 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; - import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:local_auth_android/local_auth_android.dart'; -import 'package:local_auth_platform_interface/types/auth_messages.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); diff --git a/packages/local_auth/local_auth_ios/CHANGELOG.md b/packages/local_auth/local_auth_ios/CHANGELOG.md index 0fc716e19f25..ca6ac43eb52f 100644 --- a/packages/local_auth/local_auth_ios/CHANGELOG.md +++ b/packages/local_auth/local_auth_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 1.0.4 * Fixes `deviceSupportsBiometrics` to return true when biometric hardware diff --git a/packages/local_auth/local_auth_ios/example/lib/main.dart b/packages/local_auth/local_auth_ios/example/lib/main.dart index c966d67ec9c6..a8bf23b78a52 100644 --- a/packages/local_auth/local_auth_ios/example/lib/main.dart +++ b/packages/local_auth/local_auth_ios/example/lib/main.dart @@ -10,8 +10,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:local_auth_ios/local_auth_ios.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; -import 'package:local_auth_platform_interface/types/auth_messages.dart'; -import 'package:local_auth_platform_interface/types/biometric_type.dart'; void main() { runApp(MyApp()); diff --git a/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart b/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart index ae25bd50ef5e..d9df89a656a8 100644 --- a/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart +++ b/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart @@ -5,9 +5,6 @@ import 'package:flutter/services.dart'; import 'package:local_auth_ios/types/auth_messages_ios.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; -import 'package:local_auth_platform_interface/types/auth_messages.dart'; -import 'package:local_auth_platform_interface/types/auth_options.dart'; -import 'package:local_auth_platform_interface/types/biometric_type.dart'; export 'package:local_auth_ios/types/auth_messages_ios.dart'; export 'package:local_auth_platform_interface/types/auth_messages.dart'; diff --git a/packages/local_auth/local_auth_ios/test/local_auth_test.dart b/packages/local_auth/local_auth_ios/test/local_auth_test.dart index 192d69ca8ec4..0ad89e52f5ce 100644 --- a/packages/local_auth/local_auth_ios/test/local_auth_test.dart +++ b/packages/local_auth/local_auth_ios/test/local_auth_test.dart @@ -2,12 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; - import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:local_auth_ios/local_auth_ios.dart'; -import 'package:local_auth_platform_interface/types/auth_options.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); diff --git a/packages/local_auth/local_auth_platform_interface/CHANGELOG.md b/packages/local_auth/local_auth_platform_interface/CHANGELOG.md index 9a2ae7aea8e8..10020be3391f 100644 --- a/packages/local_auth/local_auth_platform_interface/CHANGELOG.md +++ b/packages/local_auth/local_auth_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 1.0.3 * Fixes regression in the default method channel implementation of diff --git a/packages/local_auth/local_auth_platform_interface/lib/default_method_channel_platform.dart b/packages/local_auth/local_auth_platform_interface/lib/default_method_channel_platform.dart index 3e695fa41f17..9ded078c3a90 100644 --- a/packages/local_auth/local_auth_platform_interface/lib/default_method_channel_platform.dart +++ b/packages/local_auth/local_auth_platform_interface/lib/default_method_channel_platform.dart @@ -4,9 +4,6 @@ import 'package:flutter/services.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; -import 'package:local_auth_platform_interface/types/auth_messages.dart'; -import 'package:local_auth_platform_interface/types/auth_options.dart'; -import 'package:local_auth_platform_interface/types/biometric_type.dart'; const MethodChannel _channel = MethodChannel('plugins.flutter.io/local_auth'); diff --git a/packages/local_auth/local_auth_platform_interface/test/default_method_channel_platform_test.dart b/packages/local_auth/local_auth_platform_interface/test/default_method_channel_platform_test.dart index 96e78e17cfa8..824597ab2953 100644 --- a/packages/local_auth/local_auth_platform_interface/test/default_method_channel_platform_test.dart +++ b/packages/local_auth/local_auth_platform_interface/test/default_method_channel_platform_test.dart @@ -2,14 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; - import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:local_auth_platform_interface/default_method_channel_platform.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; -import 'package:local_auth_platform_interface/types/auth_messages.dart'; -import 'package:local_auth_platform_interface/types/auth_options.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); diff --git a/packages/path_provider/path_provider/CHANGELOG.md b/packages/path_provider/path_provider/CHANGELOG.md index 4714ad9ac15d..a26a8901d1d7 100644 --- a/packages/path_provider/path_provider/CHANGELOG.md +++ b/packages/path_provider/path_provider/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Removes unnecessary imports. * Adds OS version support information to README. ## 2.0.9 diff --git a/packages/path_provider/path_provider/test/path_provider_test.dart b/packages/path_provider/path_provider/test/path_provider_test.dart index 218861606209..aa6d325574df 100644 --- a/packages/path_provider/path_provider/test/path_provider_test.dart +++ b/packages/path_provider/path_provider/test/path_provider_test.dart @@ -8,7 +8,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:path_provider/path_provider.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -import 'package:test/fake.dart'; const String kTemporaryPath = 'temporaryPath'; const String kApplicationSupportPath = 'applicationSupportPath'; diff --git a/packages/path_provider/path_provider_platform_interface/CHANGELOG.md b/packages/path_provider/path_provider_platform_interface/CHANGELOG.md index 8be2da70c20d..4ed22f09a893 100644 --- a/packages/path_provider/path_provider_platform_interface/CHANGELOG.md +++ b/packages/path_provider/path_provider_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.0.3 * Removes dependency on `meta`. diff --git a/packages/path_provider/path_provider_platform_interface/lib/src/method_channel_path_provider.dart b/packages/path_provider/path_provider_platform_interface/lib/src/method_channel_path_provider.dart index c3e5eccbffba..73e6ab48a585 100644 --- a/packages/path_provider/path_provider_platform_interface/lib/src/method_channel_path_provider.dart +++ b/packages/path_provider/path_provider_platform_interface/lib/src/method_channel_path_provider.dart @@ -7,8 +7,6 @@ import 'package:flutter/services.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; import 'package:platform/platform.dart'; -import 'enums.dart'; - /// An implementation of [PathProviderPlatform] that uses method channels. class MethodChannelPathProvider extends PathProviderPlatform { /// The method channel used to interact with the native platform. diff --git a/packages/quick_actions/quick_actions/CHANGELOG.md b/packages/quick_actions/quick_actions/CHANGELOG.md index 2a764a57e59b..c30d7052320e 100644 --- a/packages/quick_actions/quick_actions/CHANGELOG.md +++ b/packages/quick_actions/quick_actions/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Removes unnecessary imports. * Updates minimum Flutter version to 2.8. * Adds OS version support information to README. diff --git a/packages/quick_actions/quick_actions/test/quick_actions_test.dart b/packages/quick_actions/quick_actions/test/quick_actions_test.dart index 09fcc9799c11..2747818ae302 100644 --- a/packages/quick_actions/quick_actions/test/quick_actions_test.dart +++ b/packages/quick_actions/quick_actions/test/quick_actions_test.dart @@ -6,9 +6,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'package:quick_actions/quick_actions.dart'; -import 'package:quick_actions_platform_interface/platform_interface/quick_actions_platform.dart'; import 'package:quick_actions_platform_interface/quick_actions_platform_interface.dart'; -import 'package:quick_actions_platform_interface/types/shortcut_item.dart'; void main() { group('$QuickActions', () { diff --git a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md index 7c86b3c80dc8..34dd631746bb 100644 --- a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.1.0 * Deprecated `SharedPreferencesWindows.instance` in favor of `SharedPreferencesStorePlatform.instance`. diff --git a/packages/shared_preferences/shared_preferences_linux/example/integration_test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences_linux/example/integration_test/shared_preferences_test.dart index 1d83eead9f25..664048ab98e4 100644 --- a/packages/shared_preferences/shared_preferences_linux/example/integration_test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences_linux/example/integration_test/shared_preferences_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; - import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:shared_preferences_linux/shared_preferences_linux.dart'; diff --git a/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md b/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md index 1f586a2a9581..0f194de44224 100644 --- a/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.0.3 * Switches to an in-package method channel implementation. diff --git a/packages/shared_preferences/shared_preferences_macos/example/integration_test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences_macos/example/integration_test/shared_preferences_test.dart index 66e3be30ee5d..874ceb4c51a7 100644 --- a/packages/shared_preferences/shared_preferences_macos/example/integration_test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences_macos/example/integration_test/shared_preferences_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; - import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index b1ebc4b35a8e..b25956fd5919 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 6.1.0 * Introduces new `launchUrl` and `canLaunchUrl` APIs; `launch` and `canLaunch` diff --git a/packages/url_launcher/url_launcher/lib/src/url_launcher_uri.dart b/packages/url_launcher/url_launcher/lib/src/url_launcher_uri.dart index 1ca787f44180..fc33f05e5afb 100644 --- a/packages/url_launcher/url_launcher/lib/src/url_launcher_uri.dart +++ b/packages/url_launcher/url_launcher/lib/src/url_launcher_uri.dart @@ -7,8 +7,6 @@ import 'dart:async'; import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; -import 'types.dart'; - /// Passes [url] to the underlying platform for handling. /// /// [mode] support varies significantly by platform: diff --git a/packages/url_launcher/url_launcher/test/src/legacy_api_test.dart b/packages/url_launcher/url_launcher/test/src/legacy_api_test.dart index 4594ab21bffc..e94f1847ef51 100644 --- a/packages/url_launcher/url_launcher/test/src/legacy_api_test.dart +++ b/packages/url_launcher/url_launcher/test/src/legacy_api_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; import 'dart:ui'; import 'package:flutter/foundation.dart'; diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 363546c211e3..af01c64fd554 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.4.0 * Updates minimum Flutter version to 2.10. diff --git a/packages/video_player/video_player/example/lib/main.dart b/packages/video_player/video_player/example/lib/main.dart index 5d496a9f5e7d..f5875975cea5 100644 --- a/packages/video_player/video_player/example/lib/main.dart +++ b/packages/video_player/video_player/example/lib/main.dart @@ -7,7 +7,6 @@ /// An example of using the plugin, controlling lifecycle and playback of the /// video. -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:video_player/video_player.dart'; diff --git a/packages/video_player/video_player/test/video_player_test.dart b/packages/video_player/video_player/test/video_player_test.dart index 64113337a860..9eca7f921acb 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -5,10 +5,8 @@ import 'dart:async'; import 'dart:io'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:video_player/video_player.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index 1f0f06f739b2..16dd52ca6da0 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.3.2 * Updates ExoPlayer to 2.17.0. diff --git a/packages/video_player/video_player_android/example/lib/main.dart b/packages/video_player/video_player_android/example/lib/main.dart index cab6eb802ca5..bca4e291efff 100644 --- a/packages/video_player/video_player_android/example/lib/main.dart +++ b/packages/video_player/video_player_android/example/lib/main.dart @@ -4,7 +4,6 @@ // ignore_for_file: public_member_api_docs -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'mini_controller.dart'; diff --git a/packages/video_player/video_player_android/example/lib/mini_controller.dart b/packages/video_player/video_player_android/example/lib/mini_controller.dart index 9bb8e90b65ae..498dbffc9e84 100644 --- a/packages/video_player/video_player_android/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_android/example/lib/mini_controller.dart @@ -8,7 +8,6 @@ import 'dart:async'; import 'dart:io'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; diff --git a/packages/video_player/video_player_android/lib/src/android_video_player.dart b/packages/video_player/video_player_android/lib/src/android_video_player.dart index 31d0744e51dc..5c5fd809c199 100644 --- a/packages/video_player/video_player_android/lib/src/android_video_player.dart +++ b/packages/video_player/video_player_android/lib/src/android_video_player.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:ui'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index 3916503ce3d9..d77c36c915b6 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.3.3 * Fix XCUITest based on the new voice over announcement for tooltips. diff --git a/packages/video_player/video_player_avfoundation/example/lib/main.dart b/packages/video_player/video_player_avfoundation/example/lib/main.dart index cab6eb802ca5..bca4e291efff 100644 --- a/packages/video_player/video_player_avfoundation/example/lib/main.dart +++ b/packages/video_player/video_player_avfoundation/example/lib/main.dart @@ -4,7 +4,6 @@ // ignore_for_file: public_member_api_docs -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'mini_controller.dart'; diff --git a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart index 9bb8e90b65ae..498dbffc9e84 100644 --- a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart @@ -8,7 +8,6 @@ import 'dart:async'; import 'dart:io'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; diff --git a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart index 5dc6862c41df..b5ebedda41e1 100644 --- a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart +++ b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:ui'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; diff --git a/packages/video_player/video_player_platform_interface/CHANGELOG.md b/packages/video_player/video_player_platform_interface/CHANGELOG.md index 9843fd81e82d..4304fd470ba2 100644 --- a/packages/video_player/video_player_platform_interface/CHANGELOG.md +++ b/packages/video_player/video_player_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 5.1.2 * Adopts `Object.hash`. diff --git a/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart b/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart index 097ab29c48a3..be264ca25061 100644 --- a/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart +++ b/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:ui'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md index 3310660137a2..00788c4386fe 100644 --- a/packages/video_player/video_player_web/CHANGELOG.md +++ b/packages/video_player/video_player_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.0.8 * Ensures `buffering` state is only removed when the browser reports enough data diff --git a/packages/video_player/video_player_web/lib/src/video_player.dart b/packages/video_player/video_player_web/lib/src/video_player.dart index eda188cb1b9f..45d90d675b83 100644 --- a/packages/video_player/video_player_web/lib/src/video_player.dart +++ b/packages/video_player/video_player_web/lib/src/video_player.dart @@ -5,7 +5,6 @@ import 'dart:async'; import 'dart:html' as html; -import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index f8a6e2514d7e..7a56f3f176d0 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 3.0.2 * Migrates deprecated `Scaffold.showSnackBar` to `ScaffoldMessenger` in example app. diff --git a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart index 70e8179e1dd0..ba321264ee1e 100644 --- a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart @@ -14,10 +14,8 @@ import 'dart:typed_data'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; -import 'package:webview_flutter/platform_interface.dart'; import 'package:webview_flutter/webview_flutter.dart'; Future main() async { diff --git a/packages/webview_flutter/webview_flutter/lib/src/webview.dart b/packages/webview_flutter/webview_flutter/lib/src/webview.dart index 6a24d3d4cb2d..697eb487b953 100644 --- a/packages/webview_flutter/webview_flutter/lib/src/webview.dart +++ b/packages/webview_flutter/webview_flutter/lib/src/webview.dart @@ -7,15 +7,12 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; -import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; import 'package:webview_flutter_android/webview_android_cookie_manager.dart'; import 'package:webview_flutter_android/webview_surface_android.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart'; -import '../platform_interface.dart'; - /// Optional callback invoked when a web view is first created. [controller] is /// the [WebViewController] for the created web view. typedef WebViewCreatedCallback = void Function(WebViewController controller); diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index c9d023987bb1..edaa0883713f 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.8.6 * Updates pigeon developer dependency to the latest version which adds support for null safety. diff --git a/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart index f1e95288383c..51e09912da23 100644 --- a/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart @@ -14,7 +14,6 @@ import 'dart:typed_data'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:webview_flutter_android/webview_android.dart'; diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart index 3dd107bd557b..5d19ca71ac84 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart @@ -8,7 +8,6 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_driver/driver_extension.dart'; import 'package:path_provider/path_provider.dart'; diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart index 83662fb81f02..a987f1cf548d 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; import 'dart:typed_data'; import 'package:flutter/widgets.dart'; diff --git a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md index f3f612adac35..c7462ddd47d0 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 1.8.2 * Migrates from `ui.hash*` to `Object.hash*`. diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/method_channel/webview_method_channel.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/method_channel/webview_method_channel.dart index bb2a4ac9fb50..f32881701cfb 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/method_channel/webview_method_channel.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/method_channel/webview_method_channel.dart @@ -6,7 +6,6 @@ import 'dart:async'; import 'package:flutter/services.dart'; -import '../platform_interface/javascript_channel_registry.dart'; import '../platform_interface/platform_interface.dart'; import '../types/types.dart'; diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/creation_params.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/creation_params.dart index 6add51ce2d73..c1763cdae501 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/creation_params.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/creation_params.dart @@ -5,9 +5,6 @@ import 'package:flutter/widgets.dart'; import 'package:webview_flutter_platform_interface/src/types/types.dart'; -import 'auto_media_playback_policy.dart'; -import 'web_settings.dart'; - /// Configuration to use when creating a new [WebViewPlatformController]. /// /// The `autoMediaPlaybackPolicy` parameter must not be null. diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/method_channel/webview_method_channel_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/method_channel/webview_method_channel_test.dart index 5d2c717be0e5..a34262fe460a 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/test/src/method_channel/webview_method_channel_test.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/method_channel/webview_method_channel_test.dart @@ -8,7 +8,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; -import 'package:webview_flutter_platform_interface/src/method_channel/webview_method_channel.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; void main() { diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/platform_interface/javascript_channel_registry_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/platform_interface/javascript_channel_registry_test.dart index df1b53090fc8..30795b01c83f 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/test/src/platform_interface/javascript_channel_registry_test.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/platform_interface/javascript_channel_registry_test.dart @@ -4,7 +4,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:webview_flutter_platform_interface/src/platform_interface/javascript_channel_registry.dart'; -import 'package:webview_flutter_platform_interface/src/types/javascript_channel.dart'; import 'package:webview_flutter_platform_interface/src/types/types.dart'; void main() { diff --git a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md index ba8fc0fb01c9..9f7ebe368941 100644 --- a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Removes unnecessary imports. * Fixes unit tests to run on latest `master` version of Flutter. ## 0.1.0+1 diff --git a/packages/webview_flutter/webview_flutter_web/example/lib/main.dart b/packages/webview_flutter/webview_flutter_web/example/lib/main.dart index 9cf412d74e50..c183625be634 100644 --- a/packages/webview_flutter/webview_flutter_web/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_web/example/lib/main.dart @@ -6,7 +6,6 @@ import 'dart:async'; import 'dart:typed_data'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; diff --git a/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart index 787b016d2b77..8cd74f660f58 100644 --- a/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart @@ -4,7 +4,6 @@ import 'dart:async'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; import 'package:webview_flutter_web/webview_flutter_web.dart'; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index 058e8645dd7b..f042dd081475 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.7.3 * Removes two occurrences of the compiler warning: "'RequiresUserActionForMediaPlayback' is deprecated: first deprecated in ios 10.0". diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart index 924a6caa0f85..ceff62e3d5e8 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart @@ -14,7 +14,6 @@ import 'dart:typed_data'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart index e126de8491e5..e8b86d9c5773 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart @@ -9,7 +9,6 @@ import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index d63cbef71f9f..012cd221599b 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -8,7 +8,6 @@ import 'dart:math'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; import 'package:path/path.dart' as path; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart index 5238c0bb2c56..8f23ff6b4a3d 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; - import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index 8c104bdb3ee2..e5042dab78e4 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -2,13 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; import 'dart:math'; import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; From c8a0086b91b700f79fc84f5055b70ba397ee62cd Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 2 May 2022 14:49:10 -0400 Subject: [PATCH 483/600] Roll Flutter from f7df22590adf to ea080e58ba42 (1 revision) (#5467) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 7249b2760418..e0de586dc6ba 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -f7df22590adf2fad70f6e83a2df73e9fa866ddfe +ea080e58ba4245036318b8967e667522bccb930c From 998ade43833f0f377270b14881136dfdba4a3b73 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 2 May 2022 15:54:12 -0400 Subject: [PATCH 484/600] Roll Flutter from ea080e58ba42 to 36be63ba19f5 (5 revisions) (#5469) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index e0de586dc6ba..d37d138094f0 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ea080e58ba4245036318b8967e667522bccb930c +36be63ba19f54e0c82156b7ceb8ee89111bc6731 From 3ff23a2cb4d8023431a49c0f1becf91a35ea7207 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 2 May 2022 16:59:12 -0400 Subject: [PATCH 485/600] Roll Flutter from 36be63ba19f5 to bd2ca58db388 (5 revisions) (#5470) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index d37d138094f0..0fb69462cc90 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -36be63ba19f54e0c82156b7ceb8ee89111bc6731 +bd2ca58db388bcdc0593fef9422f48f9b1ea6445 From 698c82db93cd75e4694c64fa519927ec3c3bf414 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 2 May 2022 17:44:12 -0400 Subject: [PATCH 486/600] [flutter_plugin_tools] Support non-plugin packages for `drive-examples` (#5468) --- script/tool/CHANGELOG.md | 5 + script/tool/lib/src/common/core.dart | 23 +-- .../src/common/package_looping_command.dart | 1 - script/tool/lib/src/common/plugin_utils.dart | 10 +- .../lib/src/common/repository_package.dart | 14 +- .../tool/lib/src/drive_examples_command.dart | 68 ++++++-- .../src/federation_safety_check_command.dart | 1 - .../lib/src/make_deps_path_based_command.dart | 3 +- .../tool/lib/src/publish_check_command.dart | 1 - .../tool/lib/src/publish_plugin_command.dart | 1 - .../tool/lib/src/pubspec_check_command.dart | 1 - script/tool/lib/src/readme_check_command.dart | 1 - script/tool/lib/src/test_command.dart | 2 +- .../tool/lib/src/version_check_command.dart | 1 - .../test/common/repository_package_test.dart | 43 ++++- .../create_all_plugins_app_command_test.dart | 1 - .../test/drive_examples_command_test.dart | 161 ++++++++++++++++++ script/tool/test/list_command_test.dart | 2 +- .../tool/test/pubspec_check_command_test.dart | 115 +++++++++---- script/tool/test/util.dart | 56 +++--- 20 files changed, 398 insertions(+), 112 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 1bce029a559f..2ce644178fa6 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,8 @@ +## NEXT + +- `drive-examples` now supports non-plugin packages. +- Commands that iterate over examples now include non-Flutter example packages. + ## 0.8.4 - `readme-check` now validates that there's a info tag on code blocks to diff --git a/script/tool/lib/src/common/core.dart b/script/tool/lib/src/common/core.dart index de1cefd7225a..13678d720a76 100644 --- a/script/tool/lib/src/common/core.dart +++ b/script/tool/lib/src/common/core.dart @@ -4,7 +4,6 @@ import 'package:colorize/colorize.dart'; import 'package:file/file.dart'; -import 'package:yaml/yaml.dart'; /// The signature for a print handler for commands that allow overriding the /// print destination. @@ -31,26 +30,14 @@ const String platformWindows = 'windows'; /// Key for enable experiment. const String kEnableExperiment = 'enable-experiment'; -/// Returns whether the given directory contains a Flutter package. -bool isFlutterPackage(FileSystemEntity entity) { +/// Returns whether the given directory is a Dart package. +bool isPackage(FileSystemEntity entity) { if (entity is! Directory) { return false; } - - try { - final File pubspecFile = entity.childFile('pubspec.yaml'); - final YamlMap pubspecYaml = - loadYaml(pubspecFile.readAsStringSync()) as YamlMap; - final YamlMap? dependencies = pubspecYaml['dependencies'] as YamlMap?; - if (dependencies == null) { - return false; - } - return dependencies.containsKey('flutter'); - } on FileSystemException { - return false; - } on YamlException { - return false; - } + // Per https://dart.dev/guides/libraries/create-library-packages#what-makes-a-library-package + return entity.childFile('pubspec.yaml').existsSync() && + entity.childDirectory('lib').existsSync(); } /// Prints `successMessage` in green. diff --git a/script/tool/lib/src/common/package_looping_command.dart b/script/tool/lib/src/common/package_looping_command.dart index b75aaa4a4a49..b48743be3170 100644 --- a/script/tool/lib/src/common/package_looping_command.dart +++ b/script/tool/lib/src/common/package_looping_command.dart @@ -10,7 +10,6 @@ import 'package:git/git.dart'; import 'package:path/path.dart' as p; import 'package:platform/platform.dart'; import 'package:pub_semver/pub_semver.dart'; -import 'package:pubspec_parse/pubspec_parse.dart'; import 'core.dart'; import 'plugin_command.dart'; diff --git a/script/tool/lib/src/common/plugin_utils.dart b/script/tool/lib/src/common/plugin_utils.dart index 94f294ebd964..f33d3d73bb75 100644 --- a/script/tool/lib/src/common/plugin_utils.dart +++ b/script/tool/lib/src/common/plugin_utils.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:file/file.dart'; import 'package:flutter_plugin_tools/src/common/repository_package.dart'; import 'package:yaml/yaml.dart'; @@ -111,13 +110,8 @@ YamlMap? _readPlatformPubspecSectionForPlugin( /// section from [plugin]'s pubspec.yaml, or null if either it is not present, /// or the pubspec couldn't be read. YamlMap? _readPluginPubspecSection(RepositoryPackage package) { - final File pubspecFile = package.pubspecFile; - if (!pubspecFile.existsSync()) { - return null; - } - final YamlMap pubspecYaml = - loadYaml(pubspecFile.readAsStringSync()) as YamlMap; - final YamlMap? flutterSection = pubspecYaml['flutter'] as YamlMap?; + final Pubspec pubspec = package.parsePubspec(); + final Map? flutterSection = pubspec.flutter; if (flutterSection == null) { return null; } diff --git a/script/tool/lib/src/common/repository_package.dart b/script/tool/lib/src/common/repository_package.dart index 72e7f948fe9e..76519e040ae1 100644 --- a/script/tool/lib/src/common/repository_package.dart +++ b/script/tool/lib/src/common/repository_package.dart @@ -8,6 +8,8 @@ import 'package:pubspec_parse/pubspec_parse.dart'; import 'core.dart'; +export 'package:pubspec_parse/pubspec_parse.dart' show Pubspec; + /// A package in the repository. // // TODO(stuartmorgan): Add more package-related info here, such as an on-demand @@ -59,6 +61,12 @@ class RepositoryPackage { /// Caches for future use. Pubspec parsePubspec() => _parsedPubspec; + /// Returns true if the package depends on Flutter. + bool requiresFlutter() { + final Pubspec pubspec = parsePubspec(); + return pubspec.dependencies.containsKey('flutter'); + } + /// True if this appears to be a federated plugin package, according to /// repository conventions. bool get isFederated => @@ -91,7 +99,7 @@ class RepositoryPackage { if (!exampleDirectory.existsSync()) { return []; } - if (isFlutterPackage(exampleDirectory)) { + if (isPackage(exampleDirectory)) { return [RepositoryPackage(exampleDirectory)]; } // Only look at the subdirectories of the example directory if the example @@ -99,8 +107,8 @@ class RepositoryPackage { // example directory for other Dart packages. return exampleDirectory .listSync() - .where((FileSystemEntity entity) => isFlutterPackage(entity)) - // isFlutterPackage guarantees that the cast to Directory is safe. + .where((FileSystemEntity entity) => isPackage(entity)) + // isPackage guarantees that the cast to Directory is safe. .map((FileSystemEntity entity) => RepositoryPackage(entity as Directory)); } diff --git a/script/tool/lib/src/drive_examples_command.dart b/script/tool/lib/src/drive_examples_command.dart index 0e1efa842eda..68ad9713a9b7 100644 --- a/script/tool/lib/src/drive_examples_command.dart +++ b/script/tool/lib/src/drive_examples_command.dart @@ -123,6 +123,8 @@ class DriveExamplesCommand extends PackageLoopingCommand { @override Future runForPackage(RepositoryPackage package) async { + final bool isPlugin = isFlutterPlugin(package); + if (package.isPlatformInterface && !package.getSingleExampleDeprecated().directory.existsSync()) { // Platform interface packages generally aren't intended to have @@ -131,23 +133,23 @@ class DriveExamplesCommand extends PackageLoopingCommand { 'Platform interfaces are not expected to have integration tests.'); } - final List deviceFlags = []; - for (final MapEntry> entry - in _targetDeviceFlags.entries) { - final String platform = entry.key; - if (pluginSupportsPlatform(platform, package)) { - deviceFlags.addAll(entry.value); - } else { - print('Skipping unsupported platform ${entry.key}...'); + // For plugin packages, skip if the plugin itself doesn't support any + // requested platform(s). + if (isPlugin) { + final Iterable requestedPlatforms = _targetDeviceFlags.keys; + final Iterable unsupportedPlatforms = requestedPlatforms.where( + (String platform) => !pluginSupportsPlatform(platform, package)); + for (final String platform in unsupportedPlatforms) { + print('Skipping unsupported platform $platform...'); + } + if (unsupportedPlatforms.length == requestedPlatforms.length) { + return PackageResult.skip( + '${package.displayName} does not support any requested platform.'); } - } - // If there is no supported target platform, skip the plugin. - if (deviceFlags.isEmpty) { - return PackageResult.skip( - '${package.displayName} does not support any requested platform.'); } int examplesFound = 0; + int supportedExamplesFound = 0; bool testsRan = false; final List errors = []; for (final RepositoryPackage example in package.getExamples()) { @@ -155,6 +157,15 @@ class DriveExamplesCommand extends PackageLoopingCommand { final String exampleName = getRelativePosixPath(example.directory, from: packagesDir); + // Skip examples that don't support any requested platform(s). + final List deviceFlags = _deviceFlagsForExample(example); + if (deviceFlags.isEmpty) { + print( + 'Skipping $exampleName; does not support any requested platforms.'); + continue; + } + ++supportedExamplesFound; + final List drivers = await _getDrivers(example); if (drivers.isEmpty) { print('No driver tests found for $exampleName'); @@ -195,14 +206,41 @@ class DriveExamplesCommand extends PackageLoopingCommand { } } if (!testsRan) { - printError('No driver tests were run ($examplesFound example(s) found).'); - errors.add('No tests ran (use --exclude if this is intentional).'); + // It is an error for a plugin not to have integration tests, because that + // is the only way to test the method channel communication. + if (isPlugin) { + printError( + 'No driver tests were run ($examplesFound example(s) found).'); + errors.add('No tests ran (use --exclude if this is intentional).'); + } else { + return PackageResult.skip(supportedExamplesFound == 0 + ? 'No example supports requested platform(s).' + : 'No example is configured for driver tests.'); + } } return errors.isEmpty ? PackageResult.success() : PackageResult.fail(errors); } + /// Returns the device flags for the intersection of the requested platforms + /// and the platforms supported by [example]. + List _deviceFlagsForExample(RepositoryPackage example) { + final List deviceFlags = []; + for (final MapEntry> entry + in _targetDeviceFlags.entries) { + final String platform = entry.key; + if (example.directory.childDirectory(platform).existsSync()) { + deviceFlags.addAll(entry.value); + } else { + final String exampleName = + getRelativePosixPath(example.directory, from: packagesDir); + print('Skipping unsupported platform $platform for $exampleName'); + } + } + return deviceFlags; + } + Future> _getDevicesForPlatform(String platform) async { final List deviceIds = []; diff --git a/script/tool/lib/src/federation_safety_check_command.dart b/script/tool/lib/src/federation_safety_check_command.dart index df9d86892e13..383637a9e896 100644 --- a/script/tool/lib/src/federation_safety_check_command.dart +++ b/script/tool/lib/src/federation_safety_check_command.dart @@ -8,7 +8,6 @@ import 'package:git/git.dart'; import 'package:path/path.dart' as p; import 'package:platform/platform.dart'; import 'package:pub_semver/pub_semver.dart'; -import 'package:pubspec_parse/pubspec_parse.dart'; import 'common/core.dart'; import 'common/file_utils.dart'; diff --git a/script/tool/lib/src/make_deps_path_based_command.dart b/script/tool/lib/src/make_deps_path_based_command.dart index 45a4427d3217..370fc3559f71 100644 --- a/script/tool/lib/src/make_deps_path_based_command.dart +++ b/script/tool/lib/src/make_deps_path_based_command.dart @@ -3,15 +3,14 @@ // found in the LICENSE file. import 'package:file/file.dart'; -import 'package:flutter_plugin_tools/src/common/repository_package.dart'; import 'package:git/git.dart'; import 'package:path/path.dart' as p; import 'package:pub_semver/pub_semver.dart'; -import 'package:pubspec_parse/pubspec_parse.dart'; import 'common/core.dart'; import 'common/git_version_finder.dart'; import 'common/plugin_command.dart'; +import 'common/repository_package.dart'; const int _exitPackageNotFound = 3; const int _exitCannotUpdatePubspec = 4; diff --git a/script/tool/lib/src/publish_check_command.dart b/script/tool/lib/src/publish_check_command.dart index 8fd96b818c1d..b6b83dabcb49 100644 --- a/script/tool/lib/src/publish_check_command.dart +++ b/script/tool/lib/src/publish_check_command.dart @@ -10,7 +10,6 @@ import 'package:file/file.dart'; import 'package:http/http.dart' as http; import 'package:platform/platform.dart'; import 'package:pub_semver/pub_semver.dart'; -import 'package:pubspec_parse/pubspec_parse.dart'; import 'common/core.dart'; import 'common/package_looping_command.dart'; diff --git a/script/tool/lib/src/publish_plugin_command.dart b/script/tool/lib/src/publish_plugin_command.dart index 28d17a3a2487..05f0afd0c06f 100644 --- a/script/tool/lib/src/publish_plugin_command.dart +++ b/script/tool/lib/src/publish_plugin_command.dart @@ -13,7 +13,6 @@ import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; import 'package:platform/platform.dart'; import 'package:pub_semver/pub_semver.dart'; -import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:yaml/yaml.dart'; import 'common/core.dart'; diff --git a/script/tool/lib/src/pubspec_check_command.dart b/script/tool/lib/src/pubspec_check_command.dart index 2c27c91e0490..654675ebb858 100644 --- a/script/tool/lib/src/pubspec_check_command.dart +++ b/script/tool/lib/src/pubspec_check_command.dart @@ -5,7 +5,6 @@ import 'package:file/file.dart'; import 'package:git/git.dart'; import 'package:platform/platform.dart'; -import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:yaml/yaml.dart'; import 'common/core.dart'; diff --git a/script/tool/lib/src/readme_check_command.dart b/script/tool/lib/src/readme_check_command.dart index 9432c4b7fa40..0cb64920dea4 100644 --- a/script/tool/lib/src/readme_check_command.dart +++ b/script/tool/lib/src/readme_check_command.dart @@ -5,7 +5,6 @@ import 'package:file/file.dart'; import 'package:git/git.dart'; import 'package:platform/platform.dart'; -import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:yaml/yaml.dart'; import 'common/core.dart'; diff --git a/script/tool/lib/src/test_command.dart b/script/tool/lib/src/test_command.dart index 2c5dd9934b45..d115b6557adc 100644 --- a/script/tool/lib/src/test_command.dart +++ b/script/tool/lib/src/test_command.dart @@ -43,7 +43,7 @@ class TestCommand extends PackageLoopingCommand { } bool passed; - if (isFlutterPackage(package.directory)) { + if (package.requiresFlutter()) { passed = await _runFlutterTests(package); } else { passed = await _runDartTests(package); diff --git a/script/tool/lib/src/version_check_command.dart b/script/tool/lib/src/version_check_command.dart index fcaea335920f..f69611f5a06d 100644 --- a/script/tool/lib/src/version_check_command.dart +++ b/script/tool/lib/src/version_check_command.dart @@ -9,7 +9,6 @@ import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; import 'package:platform/platform.dart'; import 'package:pub_semver/pub_semver.dart'; -import 'package:pubspec_parse/pubspec_parse.dart'; import 'common/core.dart'; import 'common/git_version_finder.dart'; diff --git a/script/tool/test/common/repository_package_test.dart b/script/tool/test/common/repository_package_test.dart index 0a8ea36eb905..2d0e11475c36 100644 --- a/script/tool/test/common/repository_package_test.dart +++ b/script/tool/test/common/repository_package_test.dart @@ -5,7 +5,6 @@ import 'package:file/file.dart'; import 'package:file/memory.dart'; import 'package:flutter_plugin_tools/src/common/repository_package.dart'; -import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:test/test.dart'; import '../util.dart'; @@ -97,7 +96,7 @@ void main() { }); group('getExamples', () { - test('handles a single example', () async { + test('handles a single Flutter example', () async { final Directory plugin = createFakePlugin('a_plugin', packagesDir); final List examples = @@ -107,7 +106,7 @@ void main() { expect(examples[0].path, plugin.childDirectory('example').path); }); - test('handles multiple examples', () async { + test('handles multiple Flutter examples', () async { final Directory plugin = createFakePlugin('a_plugin', packagesDir, examples: ['example1', 'example2']); @@ -120,6 +119,30 @@ void main() { expect(examples[1].path, plugin.childDirectory('example').childDirectory('example2').path); }); + + test('handles a single non-Flutter example', () async { + final Directory package = createFakePackage('a_package', packagesDir); + + final List examples = + RepositoryPackage(package).getExamples().toList(); + + expect(examples.length, 1); + expect(examples[0].path, package.childDirectory('example').path); + }); + + test('handles multiple non-Flutter examples', () async { + final Directory package = createFakePackage('a_package', packagesDir, + examples: ['example1', 'example2']); + + final List examples = + RepositoryPackage(package).getExamples().toList(); + + expect(examples.length, 2); + expect(examples[0].path, + package.childDirectory('example').childDirectory('example1').path); + expect(examples[1].path, + package.childDirectory('example').childDirectory('example2').path); + }); }); group('federated plugin queries', () { @@ -179,4 +202,18 @@ void main() { expect(pubspec.name, 'a_plugin'); }); }); + + group('requiresFlutter', () { + test('returns true for Flutter package', () async { + final Directory package = + createFakePackage('a_package', packagesDir, isFlutter: true); + expect(RepositoryPackage(package).requiresFlutter(), true); + }); + + test('returns false for non-Flutter package', () async { + final Directory package = + createFakePackage('a_package', packagesDir, isFlutter: false); + expect(RepositoryPackage(package).requiresFlutter(), false); + }); + }); } diff --git a/script/tool/test/create_all_plugins_app_command_test.dart b/script/tool/test/create_all_plugins_app_command_test.dart index 917adca020d4..9e2ee29326d7 100644 --- a/script/tool/test/create_all_plugins_app_command_test.dart +++ b/script/tool/test/create_all_plugins_app_command_test.dart @@ -10,7 +10,6 @@ import 'package:file/local.dart'; import 'package:flutter_plugin_tools/src/common/repository_package.dart'; import 'package:flutter_plugin_tools/src/create_all_plugins_app_command.dart'; import 'package:platform/platform.dart'; -import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:test/test.dart'; import 'util.dart'; diff --git a/script/tool/test/drive_examples_command_test.dart b/script/tool/test/drive_examples_command_test.dart index ac57eb7251a2..214efb420227 100644 --- a/script/tool/test/drive_examples_command_test.dart +++ b/script/tool/test/drive_examples_command_test.dart @@ -128,6 +128,7 @@ void main() { extraFiles: [ 'example/test_driver/integration_test.dart', 'example/integration_test/foo_test.dart', + 'example/ios/ios.m', ], platformSupport: { platformIOS: const PlatformDetails(PlatformSupport.inline), @@ -193,6 +194,8 @@ void main() { extraFiles: [ 'example/test_driver/plugin_test.dart', 'example/test_driver/plugin.dart', + 'example/android/android.java', + 'example/ios/ios.m', ], platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline), @@ -243,6 +246,8 @@ void main() { packagesDir, extraFiles: [ 'example/test_driver/plugin_test.dart', + 'example/android/android.java', + 'example/ios/ios.m', ], platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline), @@ -276,6 +281,8 @@ void main() { packagesDir, extraFiles: [ 'example/lib/main.dart', + 'example/android/android.java', + 'example/ios/ios.m', ], platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline), @@ -312,6 +319,8 @@ void main() { 'example/integration_test/bar_test.dart', 'example/integration_test/foo_test.dart', 'example/integration_test/ignore_me.dart', + 'example/android/android.java', + 'example/ios/ios.m', ], platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline), @@ -398,6 +407,7 @@ void main() { extraFiles: [ 'example/test_driver/plugin_test.dart', 'example/test_driver/plugin.dart', + 'example/linux/linux.cc', ], platformSupport: { platformLinux: const PlatformDetails(PlatformSupport.inline), @@ -542,6 +552,7 @@ void main() { extraFiles: [ 'example/test_driver/plugin_test.dart', 'example/test_driver/plugin.dart', + 'example/web/index.html', ], platformSupport: { platformWeb: const PlatformDetails(PlatformSupport.inline), @@ -591,6 +602,7 @@ void main() { extraFiles: [ 'example/test_driver/plugin_test.dart', 'example/test_driver/plugin.dart', + 'example/web/index.html', ], platformSupport: { platformWeb: const PlatformDetails(PlatformSupport.inline), @@ -668,6 +680,7 @@ void main() { extraFiles: [ 'example/test_driver/plugin_test.dart', 'example/test_driver/plugin.dart', + 'example/windows/windows.cpp', ], platformSupport: { platformWindows: const PlatformDetails(PlatformSupport.inline), @@ -715,6 +728,7 @@ void main() { extraFiles: [ 'example/test_driver/plugin_test.dart', 'example/test_driver/plugin.dart', + 'example/android/android.java', ], platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline), @@ -853,6 +867,8 @@ void main() { extraFiles: [ 'example/test_driver/plugin_test.dart', 'example/test_driver/plugin.dart', + 'example/android/android.java', + 'example/ios/ios.m', ], platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline), @@ -927,6 +943,7 @@ void main() { extraFiles: [ 'example/integration_test/bar_test.dart', 'example/integration_test/foo_test.dart', + 'example/web/index.html', ], platformSupport: { platformWeb: const PlatformDetails(PlatformSupport.inline), @@ -959,6 +976,7 @@ void main() { packagesDir, extraFiles: [ 'example/test_driver/integration_test.dart', + 'example/web/index.html', ], platformSupport: { platformWeb: const PlatformDetails(PlatformSupport.inline), @@ -995,6 +1013,7 @@ void main() { 'example/test_driver/integration_test.dart', 'example/integration_test/bar_test.dart', 'example/integration_test/foo_test.dart', + 'example/macos/macos.swift', ], platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), @@ -1060,5 +1079,147 @@ void main() { pluginExampleDirectory.path), ])); }); + + group('packages', () { + test('can be driven', () async { + final Directory package = + createFakePackage('a_package', packagesDir, extraFiles: [ + 'example/integration_test/foo_test.dart', + 'example/test_driver/integration_test.dart', + 'example/web/index.html', + ]); + final Directory exampleDirectory = package.childDirectory('example'); + + final List output = await runCapturingPrint(runner, [ + 'drive-examples', + '--web', + ]); + + expect( + output, + containsAllInOrder([ + contains('Running for a_package'), + contains('No issues found!'), + ]), + ); + + expect( + processRunner.recordedCalls, + orderedEquals([ + ProcessCall( + getFlutterCommand(mockPlatform), + const [ + 'drive', + '-d', + 'web-server', + '--web-port=7357', + '--browser-name=chrome', + '--driver', + 'test_driver/integration_test.dart', + '--target', + 'integration_test/foo_test.dart' + ], + exampleDirectory.path), + ])); + }); + + test('are skipped when example does not support platform', () async { + createFakePackage('a_package', packagesDir, + isFlutter: true, + extraFiles: [ + 'example/integration_test/foo_test.dart', + 'example/test_driver/integration_test.dart', + ]); + + final List output = await runCapturingPrint(runner, [ + 'drive-examples', + '--web', + ]); + + expect( + output, + containsAllInOrder([ + contains('Running for a_package'), + contains('Skipping a_package/example; does not support any ' + 'requested platforms'), + contains('SKIPPING: No example supports requested platform(s).'), + ]), + ); + + expect(processRunner.recordedCalls.isEmpty, true); + }); + + test('drive only supported examples if there is more than one', () async { + final Directory package = createFakePackage('a_package', packagesDir, + isFlutter: true, + examples: [ + 'with_web', + 'without_web' + ], + extraFiles: [ + 'example/with_web/integration_test/foo_test.dart', + 'example/with_web/test_driver/integration_test.dart', + 'example/with_web/web/index.html', + 'example/without_web/integration_test/foo_test.dart', + 'example/without_web/test_driver/integration_test.dart', + ]); + final Directory supportedExampleDirectory = + package.childDirectory('example').childDirectory('with_web'); + + final List output = await runCapturingPrint(runner, [ + 'drive-examples', + '--web', + ]); + + expect( + output, + containsAllInOrder([ + contains('Running for a_package'), + contains( + 'Skipping a_package/example/without_web; does not support any requested platforms.'), + contains('No issues found!'), + ]), + ); + + expect( + processRunner.recordedCalls, + orderedEquals([ + ProcessCall( + getFlutterCommand(mockPlatform), + const [ + 'drive', + '-d', + 'web-server', + '--web-port=7357', + '--browser-name=chrome', + '--driver', + 'test_driver/integration_test.dart', + '--target', + 'integration_test/foo_test.dart' + ], + supportedExampleDirectory.path), + ])); + }); + + test('are skipped when there is no integration testing', () async { + createFakePackage('a_package', packagesDir, + isFlutter: true, extraFiles: ['example/web/index.html']); + + final List output = await runCapturingPrint(runner, [ + 'drive-examples', + '--web', + ]); + + expect( + output, + containsAllInOrder([ + contains('Running for a_package'), + contains('SKIPPING: No example is configured for driver tests.'), + ]), + ); + + expect(processRunner.recordedCalls.isEmpty, true); + }); + }); }); } diff --git a/script/tool/test/list_command_test.dart b/script/tool/test/list_command_test.dart index fcdf9fafdb63..9e70f72e7483 100644 --- a/script/tool/test/list_command_test.dart +++ b/script/tool/test/list_command_test.dart @@ -12,7 +12,7 @@ import 'mocks.dart'; import 'util.dart'; void main() { - group('$ListCommand', () { + group('ListCommand', () { late FileSystem fileSystem; late MockPlatform mockPlatform; late Directory packagesDir; diff --git a/script/tool/test/pubspec_check_command_test.dart b/script/tool/test/pubspec_check_command_test.dart index 42d20240da87..30b6ab6004e7 100644 --- a/script/tool/test/pubspec_check_command_test.dart +++ b/script/tool/test/pubspec_check_command_test.dart @@ -155,6 +155,21 @@ ${_flutterSection(isPlugin: true)} ${_dependenciesSection()} ${_devDependenciesSection()} ${_falseSecretsSection()} +'''); + + pluginDirectory + .childDirectory('example') + .childFile('pubspec.yaml') + .writeAsStringSync(''' +${_headerSection( + 'plugin_example', + publishable: false, + includeRepository: false, + includeIssueTracker: false, + )} +${_environmentSection()} +${_dependenciesSection()} +${_flutterSection()} '''); final List output = await runCapturingPrint(runner, [ @@ -172,15 +187,31 @@ ${_falseSecretsSection()} }); test('passes for a Flutter package following conventions', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final Directory packageDirectory = + createFakePackage('a_package', packagesDir); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' -${_headerSection('plugin')} + packageDirectory.childFile('pubspec.yaml').writeAsStringSync(''' +${_headerSection('a_package')} ${_environmentSection()} ${_dependenciesSection()} ${_devDependenciesSection()} ${_flutterSection()} ${_falseSecretsSection()} +'''); + + packageDirectory + .childDirectory('example') + .childFile('pubspec.yaml') + .writeAsStringSync(''' +${_headerSection( + 'a_package', + publishable: false, + includeRepository: false, + includeIssueTracker: false, + )} +${_environmentSection()} +${_dependenciesSection()} +${_flutterSection()} '''); final List output = await runCapturingPrint(runner, [ @@ -190,8 +221,8 @@ ${_falseSecretsSection()} expect( output, containsAllInOrder([ - contains('Running for plugin...'), - contains('Running for plugin/example...'), + contains('Running for a_package...'), + contains('Running for a_package/example...'), contains('No issues found!'), ]), ); @@ -221,7 +252,8 @@ ${_dependenciesSection()} }); test('fails when homepage is included', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, includeHomepage: true)} @@ -248,7 +280,8 @@ ${_devDependenciesSection()} }); test('fails when repository is missing', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, includeRepository: false)} @@ -274,7 +307,8 @@ ${_devDependenciesSection()} }); test('fails when homepage is given instead of repository', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, includeHomepage: true, includeRepository: false)} @@ -301,7 +335,8 @@ ${_devDependenciesSection()} }); test('fails when repository is incorrect', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, repositoryPackagesDirRelativePath: 'different_plugin')} @@ -327,7 +362,8 @@ ${_devDependenciesSection()} }); test('fails when issue tracker is missing', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, includeIssueTracker: false)} @@ -353,8 +389,9 @@ ${_devDependenciesSection()} }); test('fails when description is too short', () async { - final Directory pluginDirectory = - createFakePlugin('a_plugin', packagesDir.childDirectory('a_plugin')); + final Directory pluginDirectory = createFakePlugin( + 'a_plugin', packagesDir.childDirectory('a_plugin'), + examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, description: 'Too short')} @@ -383,7 +420,8 @@ ${_devDependenciesSection()} test( 'allows short descriptions for non-app-facing parts of federated plugins', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, description: 'Too short')} @@ -410,7 +448,8 @@ ${_devDependenciesSection()} }); test('fails when description is too long', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, examples: []); const String description = 'This description is too long. It just goes ' 'on and on and on and on and on. pub.dev will down-score it because ' @@ -442,7 +481,8 @@ ${_devDependenciesSection()} }); test('fails when environment section is out of order', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true)} @@ -469,7 +509,8 @@ ${_environmentSection()} }); test('fails when flutter section is out of order', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true)} @@ -496,7 +537,8 @@ ${_devDependenciesSection()} }); test('fails when dependencies section is out of order', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true)} @@ -550,7 +592,8 @@ ${_dependenciesSection()} }); test('fails when false_secrets section is out of order', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true)} @@ -580,7 +623,8 @@ ${_devDependenciesSection()} test('fails when an implemenation package is missing "implements"', () async { final Directory pluginDirectory = createFakePlugin( - 'plugin_a_foo', packagesDir.childDirectory('plugin_a')); + 'plugin_a_foo', packagesDir.childDirectory('plugin_a'), + examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection('plugin_a_foo', isPlugin: true)} @@ -608,7 +652,8 @@ ${_devDependenciesSection()} test('fails when an implemenation package has the wrong "implements"', () async { final Directory pluginDirectory = createFakePlugin( - 'plugin_a_foo', packagesDir.childDirectory('plugin_a')); + 'plugin_a_foo', packagesDir.childDirectory('plugin_a'), + examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection('plugin_a_foo', isPlugin: true)} @@ -636,7 +681,8 @@ ${_devDependenciesSection()} test('passes for a correct implemenation package', () async { final Directory pluginDirectory = createFakePlugin( - 'plugin_a_foo', packagesDir.childDirectory('plugin_a')); + 'plugin_a_foo', packagesDir.childDirectory('plugin_a'), + examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection( @@ -663,8 +709,9 @@ ${_devDependenciesSection()} }); test('fails when a "default_package" looks incorrect', () async { - final Directory pluginDirectory = - createFakePlugin('plugin_a', packagesDir.childDirectory('plugin_a')); + final Directory pluginDirectory = createFakePlugin( + 'plugin_a', packagesDir.childDirectory('plugin_a'), + examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection( @@ -702,8 +749,9 @@ ${_devDependenciesSection()} test( 'fails when a "default_package" does not have a corresponding dependency', () async { - final Directory pluginDirectory = - createFakePlugin('plugin_a', packagesDir.childDirectory('plugin_a')); + final Directory pluginDirectory = createFakePlugin( + 'plugin_a', packagesDir.childDirectory('plugin_a'), + examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection( @@ -739,8 +787,9 @@ ${_devDependenciesSection()} }); test('passes for an app-facing package without "implements"', () async { - final Directory pluginDirectory = - createFakePlugin('plugin_a', packagesDir.childDirectory('plugin_a')); + final Directory pluginDirectory = createFakePlugin( + 'plugin_a', packagesDir.childDirectory('plugin_a'), + examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection( @@ -769,8 +818,8 @@ ${_devDependenciesSection()} test('passes for a platform interface package without "implements"', () async { final Directory pluginDirectory = createFakePlugin( - 'plugin_a_platform_interface', - packagesDir.childDirectory('plugin_a')); + 'plugin_a_platform_interface', packagesDir.childDirectory('plugin_a'), + examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection( @@ -799,7 +848,8 @@ ${_devDependenciesSection()} test('validates some properties even for unpublished packages', () async { final Directory pluginDirectory = createFakePlugin( - 'plugin_a_foo', packagesDir.childDirectory('plugin_a')); + 'plugin_a_foo', packagesDir.childDirectory('plugin_a'), + examples: []); // Environment section is in the wrong location. // Missing 'implements'. @@ -829,7 +879,8 @@ ${_environmentSection()} }); test('ignores some checks for unpublished packages', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, examples: []); // Missing metadata that is only useful for published packages, such as // repository and issue tracker. @@ -886,7 +937,7 @@ ${_devDependenciesSection()} test('repository check works', () async { final Directory packageDirectory = - createFakePackage('package', packagesDir); + createFakePackage('package', packagesDir, examples: []); packageDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection('package')} diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart index 2b1719ee8000..8a2bf099cc8a 100644 --- a/script/tool/test/util.dart +++ b/script/tool/test/util.dart @@ -107,6 +107,12 @@ Directory createFakePlugin( /// /// [extraFiles] is an optional list of package-relative paths, using unix-style /// separators, of extra files to create in the package. +/// +/// If [includeCommonFiles] is true, common but non-critical files like +/// CHANGELOG.md and AUTHORS will be included. +/// +/// If non-null, [directoryName] will be used for the directory instead of +/// [name]. // TODO(stuartmorgan): Convert the return to a RepositoryPackage. Directory createFakePackage( String name, @@ -116,37 +122,43 @@ Directory createFakePackage( bool isFlutter = false, String? version = '0.0.1', String flutterConstraint = '>=2.5.0', + bool includeCommonFiles = true, + String? directoryName, + String? publishTo, }) { - final Directory packageDirectory = parentDirectory.childDirectory(name); + final Directory packageDirectory = + parentDirectory.childDirectory(directoryName ?? name); packageDirectory.createSync(recursive: true); + packageDirectory.childDirectory('lib').createSync(); createFakePubspec(packageDirectory, name: name, isFlutter: isFlutter, version: version, flutterConstraint: flutterConstraint); - createFakeCHANGELOG(packageDirectory, ''' + if (includeCommonFiles) { + createFakeCHANGELOG(packageDirectory, ''' ## $version * Some changes. '''); - createFakeAuthors(packageDirectory); + createFakeAuthors(packageDirectory); + } if (examples.length == 1) { - final Directory exampleDir = packageDirectory.childDirectory(examples.first) - ..createSync(); - createFakePubspec(exampleDir, - name: '${name}_example', + createFakePackage('${name}_example', packageDirectory, + directoryName: examples.first, + examples: [], + includeCommonFiles: false, isFlutter: isFlutter, publishTo: 'none', flutterConstraint: flutterConstraint); } else if (examples.isNotEmpty) { - final Directory exampleDir = packageDirectory.childDirectory('example') - ..createSync(); - for (final String example in examples) { - final Directory currentExample = exampleDir.childDirectory(example) - ..createSync(); - createFakePubspec(currentExample, - name: example, + final Directory examplesDirectory = + packageDirectory.childDirectory('example')..createSync(); + for (final String exampleName in examples) { + createFakePackage(exampleName, examplesDirectory, + examples: [], + includeCommonFiles: false, isFlutter: isFlutter, publishTo: 'none', flutterConstraint: flutterConstraint); @@ -179,7 +191,7 @@ void createFakePubspec( bool isPlugin = false, Map platformSupport = const {}, - String publishTo = 'http://no_pub_server.com', + String? publishTo, String? version, String dartConstraint = '>=2.0.0 <3.0.0', String flutterConstraint = '>=2.5.0', @@ -219,9 +231,16 @@ flutter: } } - String yaml = ''' + // Default to a fake server to avoid ever accidentally publishing something + // from a test. Does not use 'none' since that changes the behavior of some + // commands. + final String publishToSection = + 'publish_to: ${publishTo ?? 'http://no_pub_server.com'}'; + + final String yaml = ''' name: $name ${(version != null) ? 'version: $version' : ''} +$publishToSection $environmentSection @@ -230,11 +249,6 @@ $dependenciesSection $pluginSection '''; - if (publishTo.isNotEmpty) { - yaml += ''' -publish_to: $publishTo # Hardcoded safeguard to prevent this from somehow being published by a broken test. -'''; - } parent.childFile('pubspec.yaml').createSync(); parent.childFile('pubspec.yaml').writeAsStringSync(yaml); } From bee239586eea46f3afeeeefa79b7de1b2196c2f4 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 2 May 2022 18:24:08 -0400 Subject: [PATCH 487/600] Roll Flutter from bd2ca58db388 to 7965ee25dd63 (2 revisions) (#5471) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 0fb69462cc90..a750a5ec244f 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -bd2ca58db388bcdc0593fef9422f48f9b1ea6445 +7965ee25dd632d907f69e5ea6f4f81f35c0cf6a7 From cd89420e390ea04108d345566dac8065cf82046b Mon Sep 17 00:00:00 2001 From: Emmanuel Garcia Date: Mon, 2 May 2022 18:04:13 -0700 Subject: [PATCH 488/600] Add dependabot for Gradle dependencies (#5440) --- .github/dependabot.yml | 289 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000000..92e313d21441 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,289 @@ +version: 2 +updates: + - package-ecosystem: "gradle" + directory: "/packages/camera/camera/android" + commit-message: + prefix: "[camera]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/camera/camera/example/android/app" + commit-message: + prefix: "[camera]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/espresso/android" + commit-message: + prefix: "[espresso]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/espresso/example/android/app" + commit-message: + prefix: "[espresso]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/flutter_plugin_android_lifecycle/android" + commit-message: + prefix: "[flutter_plugin_android_lifecycle]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/flutter_plugin_android_lifecycle/example/android/app" + commit-message: + prefix: "[flutter_plugin_android_lifecycle]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/google_maps_flutter/google_maps_flutter/android" + commit-message: + prefix: "[google_maps_flutter]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/google_maps_flutter/google_maps_flutter/example/android/app" + commit-message: + prefix: "[google_maps_flutter]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/google_sign_in/google_sign_in/example/android/app" + commit-message: + prefix: "[google_sign_in]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/google_sign_in/google_sign_in_android/android" + commit-message: + prefix: "[google_sign_in_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/google_sign_in/google_sign_in_android/example/android/app" + commit-message: + prefix: "[google_sign_in_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/in_app_purchase/in_app_purchase_android/android" + commit-message: + prefix: "[in_app_purchase_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/in_app_purchase/in_app_purchase_android/example/android/app" + commit-message: + prefix: "[in_app_purchase_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/in_app_purchase/in_app_purchase/example/android/app" + commit-message: + prefix: "[in_app_purchase]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/image_picker/image_picker/example/android/app" + commit-message: + prefix: "[image_picker]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/image_picker/image_picker_android/android" + commit-message: + prefix: "[image_picker_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/image_picker/image_picker_android/example/android/app" + commit-message: + prefix: "[image_picker_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/local_auth/local_auth_android/android" + commit-message: + prefix: "[local_auth_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/local_auth/local_auth_android/example/android/app" + commit-message: + prefix: "[local_auth_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/local_auth/local_auth/example/android/app" + commit-message: + prefix: "[local_auth]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/path_provider/path_provider/example/android/app" + commit-message: + prefix: "[path_provider]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/path_provider/path_provider_android/android" + commit-message: + prefix: "[path_provider_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/path_provider/path_provider_android/example/android/app" + commit-message: + prefix: "[path_provider_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/quick_actions/quick_actions_android/android" + commit-message: + prefix: "[quick_actions_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/quick_actions/quick_actions_android/example/android/app" + commit-message: + prefix: "[quick_actions_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/quick_actions/quick_actions/example/android/app" + commit-message: + prefix: "[quick_actions]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/shared_preferences/shared_preferences/example/android/app" + commit-message: + prefix: "[shared_preferences]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/shared_preferences/shared_preferences_android/example/android/app" + commit-message: + prefix: "[shared_preferences_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/url_launcher/url_launcher_android/android" + commit-message: + prefix: "[url_launcher_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/url_launcher/url_launcher_android/example/android/app" + commit-message: + prefix: "[url_launcher_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/url_launcher/url_launcher/example/android/app" + commit-message: + prefix: "[url_launcher]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/video_player/video_player_android/android" + commit-message: + prefix: "[video_player_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/video_player/video_player_android/example/android/app" + commit-message: + prefix: "[video_player_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/webview_flutter/webview_flutter/example/android/app" + commit-message: + prefix: "[webview_flutter]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/webview_flutter/webview_flutter_android/android" + commit-message: + prefix: "[webview_flutter_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/webview_flutter/webview_flutter_android/example/android" + commit-message: + prefix: "[webview_flutter_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 From 580de325ffcd1cbf35221751c122744425fe445f Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Mon, 2 May 2022 19:05:05 -0700 Subject: [PATCH 489/600] [ci] Shorten dependabot prefixes to comply with config spec. (#5474) (Landing on red to reopen tree) --- .github/dependabot.yml | 54 +++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 92e313d21441..676ac1da5930 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -35,7 +35,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/flutter_plugin_android_lifecycle/android" commit-message: - prefix: "[flutter_plugin_android_lifecycle]" + prefix: "[lifecycle]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -43,7 +43,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/flutter_plugin_android_lifecycle/example/android/app" commit-message: - prefix: "[flutter_plugin_android_lifecycle]" + prefix: "[lifecycle]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -51,7 +51,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/google_maps_flutter/google_maps_flutter/android" commit-message: - prefix: "[google_maps_flutter]" + prefix: "[google_maps]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -59,7 +59,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/google_maps_flutter/google_maps_flutter/example/android/app" commit-message: - prefix: "[google_maps_flutter]" + prefix: "[google_maps]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -67,7 +67,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/google_sign_in/google_sign_in/example/android/app" commit-message: - prefix: "[google_sign_in]" + prefix: "[sign_in]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -75,7 +75,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/google_sign_in/google_sign_in_android/android" commit-message: - prefix: "[google_sign_in_android]" + prefix: "[sign_in]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -83,7 +83,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/google_sign_in/google_sign_in_android/example/android/app" commit-message: - prefix: "[google_sign_in_android]" + prefix: "[sign_in]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -91,7 +91,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/in_app_purchase/in_app_purchase_android/android" commit-message: - prefix: "[in_app_purchase_android]" + prefix: "[in_app_pur]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -99,7 +99,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/in_app_purchase/in_app_purchase_android/example/android/app" commit-message: - prefix: "[in_app_purchase_android]" + prefix: "[in_app_pur]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -107,7 +107,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/in_app_purchase/in_app_purchase/example/android/app" commit-message: - prefix: "[in_app_purchase]" + prefix: "[in_app_pur]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -123,7 +123,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/image_picker/image_picker_android/android" commit-message: - prefix: "[image_picker_android]" + prefix: "[image_picker]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -131,7 +131,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/image_picker/image_picker_android/example/android/app" commit-message: - prefix: "[image_picker_android]" + prefix: "[image_picker]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -139,7 +139,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/local_auth/local_auth_android/android" commit-message: - prefix: "[local_auth_android]" + prefix: "[local_auth]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -147,7 +147,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/local_auth/local_auth_android/example/android/app" commit-message: - prefix: "[local_auth_android]" + prefix: "[local_auth]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -171,7 +171,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/path_provider/path_provider_android/android" commit-message: - prefix: "[path_provider_android]" + prefix: "[path_provider]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -179,7 +179,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/path_provider/path_provider_android/example/android/app" commit-message: - prefix: "[path_provider_android]" + prefix: "[path_provider]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -187,7 +187,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/quick_actions/quick_actions_android/android" commit-message: - prefix: "[quick_actions_android]" + prefix: "[quick_actions]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -195,7 +195,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/quick_actions/quick_actions_android/example/android/app" commit-message: - prefix: "[quick_actions_android]" + prefix: "[quick_actions]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -211,7 +211,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/shared_preferences/shared_preferences/example/android/app" commit-message: - prefix: "[shared_preferences]" + prefix: "[shared_pref]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -219,7 +219,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/shared_preferences/shared_preferences_android/example/android/app" commit-message: - prefix: "[shared_preferences_android]" + prefix: "[shared_pref]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -227,7 +227,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/url_launcher/url_launcher_android/android" commit-message: - prefix: "[url_launcher_android]" + prefix: "[url_launcher]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -235,7 +235,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/url_launcher/url_launcher_android/example/android/app" commit-message: - prefix: "[url_launcher_android]" + prefix: "[url_launcher]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -251,7 +251,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/video_player/video_player_android/android" commit-message: - prefix: "[video_player_android]" + prefix: "[video_player]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -259,7 +259,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/video_player/video_player_android/example/android/app" commit-message: - prefix: "[video_player_android]" + prefix: "[video_player]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -267,7 +267,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/webview_flutter/webview_flutter/example/android/app" commit-message: - prefix: "[webview_flutter]" + prefix: "[webview]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -275,7 +275,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/webview_flutter/webview_flutter_android/android" commit-message: - prefix: "[webview_flutter_android]" + prefix: "[webview]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -283,7 +283,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/webview_flutter/webview_flutter_android/example/android" commit-message: - prefix: "[webview_flutter_android]" + prefix: "[webview]" schedule: interval: "daily" open-pull-requests-limit: 10 From bce32f6d841aad4cab51a3b7f2bd51e35cfd7f82 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 3 May 2022 00:44:10 -0400 Subject: [PATCH 490/600] [flutter_plugin_tools] Include examples in `test` (#5453) --- script/tool/CHANGELOG.md | 3 +- script/tool/lib/src/test_command.dart | 4 +- script/tool/pubspec.yaml | 2 +- script/tool/test/test_command_test.dart | 50 +++++++++++++++++++++++-- 4 files changed, 53 insertions(+), 6 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 2ce644178fa6..b2319c63dc48 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.8.5 +- Updates `test` to inculde the Dart unit tests of examples, if any. - `drive-examples` now supports non-plugin packages. - Commands that iterate over examples now include non-Flutter example packages. diff --git a/script/tool/lib/src/test_command.dart b/script/tool/lib/src/test_command.dart index d115b6557adc..a1a995dbd88f 100644 --- a/script/tool/lib/src/test_command.dart +++ b/script/tool/lib/src/test_command.dart @@ -36,6 +36,9 @@ class TestCommand extends PackageLoopingCommand { final String description = 'Runs the Dart tests for all packages.\n\n' 'This command requires "flutter" to be in your path.'; + @override + bool get includeSubpackages => true; + @override Future runForPackage(RepositoryPackage package) async { if (!package.directory.childDirectory('test').existsSync()) { @@ -88,7 +91,6 @@ class TestCommand extends PackageLoopingCommand { exitCode = await processRunner.runAndStream( 'dart', [ - 'pub', 'run', if (experiment.isNotEmpty) '--enable-experiment=$experiment', 'test', diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index af38193294a5..32bfc1b62281 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.8.4 +version: 0.8.5 dependencies: args: ^2.1.0 diff --git a/script/tool/test/test_command_test.dart b/script/tool/test/test_command_test.dart index 9bcd8d1ae67a..386eaf0d345b 100644 --- a/script/tool/test/test_command_test.dart +++ b/script/tool/test/test_command_test.dart @@ -58,6 +58,28 @@ void main() { ); }); + test('runs flutter test on Flutter package example tests', () async { + final Directory pluginDir = createFakePlugin('a_plugin', packagesDir, + extraFiles: [ + 'test/empty_test.dart', + 'example/test/an_example_test.dart' + ]); + + await runCapturingPrint(runner, ['test']); + + expect( + processRunner.recordedCalls, + orderedEquals([ + ProcessCall(getFlutterCommand(mockPlatform), + const ['test', '--color'], pluginDir.path), + ProcessCall( + getFlutterCommand(mockPlatform), + const ['test', '--color'], + pluginDir.childDirectory('example').path), + ]), + ); + }); + test('fails when Flutter tests fail', () async { createFakePlugin('plugin1', packagesDir, extraFiles: ['test/empty_test.dart']); @@ -102,7 +124,7 @@ void main() { ); }); - test('runs pub run test on non-Flutter packages', () async { + test('runs dart run test on non-Flutter packages', () async { final Directory pluginDir = createFakePlugin('a', packagesDir, extraFiles: ['test/empty_test.dart']); final Directory packageDir = createFakePackage('b', packagesDir, @@ -121,12 +143,34 @@ void main() { ProcessCall('dart', const ['pub', 'get'], packageDir.path), ProcessCall( 'dart', - const ['pub', 'run', '--enable-experiment=exp1', 'test'], + const ['run', '--enable-experiment=exp1', 'test'], packageDir.path), ]), ); }); + test('runs dart run test on non-Flutter package examples', () async { + final Directory packageDir = createFakePackage('a_package', packagesDir, + extraFiles: [ + 'test/empty_test.dart', + 'example/test/an_example_test.dart' + ]); + + await runCapturingPrint(runner, ['test']); + + expect( + processRunner.recordedCalls, + orderedEquals([ + ProcessCall('dart', const ['pub', 'get'], packageDir.path), + ProcessCall('dart', const ['run', 'test'], packageDir.path), + ProcessCall('dart', const ['pub', 'get'], + packageDir.childDirectory('example').path), + ProcessCall('dart', const ['run', 'test'], + packageDir.childDirectory('example').path), + ]), + ); + }); + test('fails when getting non-Flutter package dependencies fails', () async { createFakePackage('a_package', packagesDir, extraFiles: ['test/empty_test.dart']); @@ -217,7 +261,7 @@ void main() { ProcessCall('dart', const ['pub', 'get'], packageDir.path), ProcessCall( 'dart', - const ['pub', 'run', '--enable-experiment=exp1', 'test'], + const ['run', '--enable-experiment=exp1', 'test'], packageDir.path), ]), ); From aaad0202ab0594ed138c2304ce557ba17ad69e4e Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 3 May 2022 01:29:11 -0400 Subject: [PATCH 491/600] Roll Flutter from 7965ee25dd63 to 597835d6bfc8 (4 revisions) (#5473) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index a750a5ec244f..d0ee9fb91c4d 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -7965ee25dd632d907f69e5ea6f4f81f35c0cf6a7 +597835d6bfc88880d72bbac124bf163b99b45aad From 79bd6024740af6192885a281711178830961c0ae Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 3 May 2022 03:59:09 -0400 Subject: [PATCH 492/600] Roll Flutter from 597835d6bfc8 to 8ad88f162d78 (3 revisions) (#5589) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index d0ee9fb91c4d..2e1abc6dbffd 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -597835d6bfc88880d72bbac124bf163b99b45aad +8ad88f162d78626e67c207fdddc71ae4f0a2e432 From 20251d635acebdf070847dd92cfd97719ad0d7cd Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 3 May 2022 05:04:11 -0400 Subject: [PATCH 493/600] Roll Flutter from 8ad88f162d78 to 0d56b91324f6 (1 revision) (#5590) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 2e1abc6dbffd..c10577854def 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -8ad88f162d78626e67c207fdddc71ae4f0a2e432 +0d56b91324f6446c66dea97ce8b8d9613ac28b1c From d43c5e3ee1afe0326d16d9bedd7712c05bbdfd09 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 3 May 2022 06:09:08 -0400 Subject: [PATCH 494/600] Roll Flutter from 0d56b91324f6 to 2998734bf4ce (1 revision) (#5591) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index c10577854def..4061d0e483ab 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -0d56b91324f6446c66dea97ce8b8d9613ac28b1c +2998734bf4ce652e531be9870ce855c45582dbf6 From 9f440ecac5bf1c3aea92c28b360ae3aebb94cfb9 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 3 May 2022 07:14:08 -0400 Subject: [PATCH 495/600] Roll Flutter from 2998734bf4ce to 912943be79db (1 revision) (#5593) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 4061d0e483ab..3a91d4015f16 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2998734bf4ce652e531be9870ce855c45582dbf6 +912943be79dbaf2fd0ec0528db00e88bdf7a34c9 From 60b0b2d31ee04539af2d2e0c637185e724bea504 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 3 May 2022 08:44:07 -0400 Subject: [PATCH 496/600] Roll Flutter from 912943be79db to 2a102fe5bc7e (1 revision) (#5599) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 3a91d4015f16..440eb8d55be2 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -912943be79dbaf2fd0ec0528db00e88bdf7a34c9 +2a102fe5bc7ebfeadc3825b06b227ed4600f775b From 7e22dfab9a8b5c07991f23098cf78baf9ce2ec6d Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 3 May 2022 13:09:08 -0400 Subject: [PATCH 497/600] Roll Flutter from 2a102fe5bc7e to 2e1ef66665b2 (1 revision) (#5601) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 440eb8d55be2..27b06c9dba1e 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2a102fe5bc7ebfeadc3825b06b227ed4600f775b +2e1ef66665b2fc9c1efd3020d60c1785eed24399 From e40abaa59ea5c551a4300dd879bbf4e058a3426a Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 3 May 2022 13:59:11 -0400 Subject: [PATCH 498/600] [tools] Remove single-example RepositoryPackage method (#5600) --- .../lib/src/common/repository_package.dart | 9 --- .../tool/lib/src/drive_examples_command.dart | 3 +- .../lib/src/firebase_test_lab_command.dart | 42 +++++++--- script/tool/lib/src/lint_android_command.dart | 39 +++++---- .../test/firebase_test_lab_command_test.dart | 81 +++++++++++++++++-- .../tool/test/lint_android_command_test.dart | 64 +++++++++++++-- 6 files changed, 185 insertions(+), 53 deletions(-) diff --git a/script/tool/lib/src/common/repository_package.dart b/script/tool/lib/src/common/repository_package.dart index 76519e040ae1..579ba01868e4 100644 --- a/script/tool/lib/src/common/repository_package.dart +++ b/script/tool/lib/src/common/repository_package.dart @@ -112,13 +112,4 @@ class RepositoryPackage { .map((FileSystemEntity entity) => RepositoryPackage(entity as Directory)); } - - /// Returns the example directory, assuming there is only one. - /// - /// DO NOT USE THIS METHOD. It exists only to easily find code that was - /// written to use a single example and needs to be restructured to handle - /// multiple examples. New code should always use [getExamples]. - // TODO(stuartmorgan): Eliminate all uses of this. - RepositoryPackage getSingleExampleDeprecated() => - RepositoryPackage(directory.childDirectory('example')); } diff --git a/script/tool/lib/src/drive_examples_command.dart b/script/tool/lib/src/drive_examples_command.dart index 68ad9713a9b7..15366e17ae85 100644 --- a/script/tool/lib/src/drive_examples_command.dart +++ b/script/tool/lib/src/drive_examples_command.dart @@ -125,8 +125,7 @@ class DriveExamplesCommand extends PackageLoopingCommand { Future runForPackage(RepositoryPackage package) async { final bool isPlugin = isFlutterPlugin(package); - if (package.isPlatformInterface && - !package.getSingleExampleDeprecated().directory.existsSync()) { + if (package.isPlatformInterface && package.getExamples().isEmpty) { // Platform interface packages generally aren't intended to have // examples, and don't need integration tests, so skip rather than fail. return PackageResult.skip( diff --git a/script/tool/lib/src/firebase_test_lab_command.dart b/script/tool/lib/src/firebase_test_lab_command.dart index e824d8ad1a90..6cc3c129c6bd 100644 --- a/script/tool/lib/src/firebase_test_lab_command.dart +++ b/script/tool/lib/src/firebase_test_lab_command.dart @@ -119,7 +119,32 @@ class FirebaseTestLabCommand extends PackageLoopingCommand { @override Future runForPackage(RepositoryPackage package) async { - final RepositoryPackage example = package.getSingleExampleDeprecated(); + final List results = []; + for (final RepositoryPackage example in package.getExamples()) { + results.add(await _runForExample(example, package: package)); + } + + // If all results skipped, report skip overall. + if (results + .every((PackageResult result) => result.state == RunState.skipped)) { + return PackageResult.skip('No examples support Android.'); + } + // Otherwise, report failure if there were any failures. + final List allErrors = results + .map((PackageResult result) => + result.state == RunState.failed ? result.details : []) + .expand((List list) => list) + .toList(); + return allErrors.isEmpty + ? PackageResult.success() + : PackageResult.fail(allErrors); + } + + /// Runs the test for the given example of [package]. + Future _runForExample( + RepositoryPackage example, { + required RepositoryPackage package, + }) async { final Directory androidDirectory = example.directory.childDirectory('android'); if (!androidDirectory.existsSync()) { @@ -163,7 +188,7 @@ class FirebaseTestLabCommand extends PackageLoopingCommand { // Used within the loop to ensure a unique GCS output location for each // test file's run. int resultsCounter = 0; - for (final File test in _findIntegrationTestFiles(package)) { + for (final File test in _findIntegrationTestFiles(example)) { final String testName = getRelativePosixPath(test, from: package.directory); print('Testing $testName...'); @@ -175,7 +200,8 @@ class FirebaseTestLabCommand extends PackageLoopingCommand { final String buildId = getStringArg('build-id'); final String testRunId = getStringArg('test-run-id'); final String resultsDir = - 'plugins_android_test/${package.displayName}/$buildId/$testRunId/${resultsCounter++}/'; + 'plugins_android_test/${package.displayName}/$buildId/$testRunId/' + '${example.directory.basename}/${resultsCounter++}/'; // Automatically retry failures; there is significant flake with these // tests whose cause isn't yet understood, and having to re-run the @@ -299,12 +325,10 @@ class FirebaseTestLabCommand extends PackageLoopingCommand { return true; } - /// Finds and returns all integration test files for [package]. - Iterable _findIntegrationTestFiles(RepositoryPackage package) sync* { - final Directory integrationTestDir = package - .getSingleExampleDeprecated() - .directory - .childDirectory('integration_test'); + /// Finds and returns all integration test files for [example]. + Iterable _findIntegrationTestFiles(RepositoryPackage example) sync* { + final Directory integrationTestDir = + example.directory.childDirectory('integration_test'); if (!integrationTestDir.existsSync()) { return; diff --git a/script/tool/lib/src/lint_android_command.dart b/script/tool/lib/src/lint_android_command.dart index 8368160c4c95..467dc7942d95 100644 --- a/script/tool/lib/src/lint_android_command.dart +++ b/script/tool/lib/src/lint_android_command.dart @@ -28,7 +28,7 @@ class LintAndroidCommand extends PackageLoopingCommand { @override final String description = 'Runs "gradlew lint" on Android plugins.\n\n' - 'Requires the example to have been build at least once before running.'; + 'Requires the examples to have been build at least once before running.'; @override Future runForPackage(RepositoryPackage package) async { @@ -38,25 +38,30 @@ class LintAndroidCommand extends PackageLoopingCommand { 'Plugin does not have an Android implemenatation.'); } - final RepositoryPackage example = package.getSingleExampleDeprecated(); - final GradleProject project = GradleProject(example.directory, - processRunner: processRunner, platform: platform); + bool failed = false; + for (final RepositoryPackage example in package.getExamples()) { + final GradleProject project = GradleProject(example.directory, + processRunner: processRunner, platform: platform); - if (!project.isConfigured()) { - return PackageResult.fail(['Build example before linting']); - } + if (!project.isConfigured()) { + return PackageResult.fail(['Build examples before linting']); + } - final String packageName = package.directory.basename; + final String packageName = package.directory.basename; - // Only lint one build mode to avoid extra work. - // Only lint the plugin project itself, to avoid failing due to errors in - // dependencies. - // - // TODO(stuartmorgan): Consider adding an XML parser to read and summarize - // all results. Currently, only the first three errors will be shown inline, - // and the rest have to be checked via the CI-uploaded artifact. - final int exitCode = await project.runCommand('$packageName:lintDebug'); + // Only lint one build mode to avoid extra work. + // Only lint the plugin project itself, to avoid failing due to errors in + // dependencies. + // + // TODO(stuartmorgan): Consider adding an XML parser to read and summarize + // all results. Currently, only the first three errors will be shown + // inline, and the rest have to be checked via the CI-uploaded artifact. + final int exitCode = await project.runCommand('$packageName:lintDebug'); + if (exitCode != 0) { + failed = true; + } + } - return exitCode == 0 ? PackageResult.success() : PackageResult.fail(); + return failed ? PackageResult.fail() : PackageResult.success(); } } diff --git a/script/tool/test/firebase_test_lab_command_test.dart b/script/tool/test/firebase_test_lab_command_test.dart index 1dfd8ba66b58..db658e19b28e 100644 --- a/script/tool/test/firebase_test_lab_command_test.dart +++ b/script/tool/test/firebase_test_lab_command_test.dart @@ -17,7 +17,7 @@ import 'mocks.dart'; import 'util.dart'; void main() { - group('$FirebaseTestLabCommand', () { + group('FirebaseTestLabCommand', () { FileSystem fileSystem; late MockPlatform mockPlatform; late Directory packagesDir; @@ -173,7 +173,7 @@ public class MainActivityTest { '/packages/plugin1/example/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin1/buildId/testRunId/0/ --device model=redfin,version=30 --device model=seoul,version=26' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin1/buildId/testRunId/example/0/ --device model=redfin,version=30 --device model=seoul,version=26' .split(' '), '/packages/plugin1/example'), ProcessCall( @@ -187,7 +187,7 @@ public class MainActivityTest { '/packages/plugin2/example/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin2/buildId/testRunId/0/ --device model=redfin,version=30 --device model=seoul,version=26' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin2/buildId/testRunId/example/0/ --device model=redfin,version=30 --device model=seoul,version=26' .split(' '), '/packages/plugin2/example'), ]), @@ -254,7 +254,7 @@ public class MainActivityTest { '/packages/plugin/example/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/0/ --device model=redfin,version=30 --device model=seoul,version=26' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/example/0/ --device model=redfin,version=30 --device model=seoul,version=26' .split(' '), '/packages/plugin/example'), ProcessCall( @@ -264,13 +264,78 @@ public class MainActivityTest { '/packages/plugin/example/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/1/ --device model=redfin,version=30 --device model=seoul,version=26' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/example/1/ --device model=redfin,version=30 --device model=seoul,version=26' .split(' '), '/packages/plugin/example'), ]), ); }); + test('runs for all examples', () async { + const List examples = ['example1', 'example2']; + const String javaTestFileExampleRelativePath = + 'android/app/src/androidTest/MainActivityTest.java'; + final Directory pluginDir = createFakePlugin('plugin', packagesDir, + examples: examples, + extraFiles: [ + for (final String example in examples) ...[ + 'example/$example/integration_test/a_test.dart', + 'example/$example/android/gradlew', + 'example/$example/$javaTestFileExampleRelativePath', + ], + ]); + for (final String example in examples) { + _writeJavaTestFile( + pluginDir, 'example/$example/$javaTestFileExampleRelativePath'); + } + + final List output = await runCapturingPrint(runner, [ + 'firebase-test-lab', + '--device', + 'model=redfin,version=30', + '--device', + 'model=seoul,version=26', + '--test-run-id', + 'testRunId', + '--build-id', + 'buildId', + ]); + + expect( + output, + containsAllInOrder([ + contains('Testing example/example1/integration_test/a_test.dart...'), + contains('Testing example/example2/integration_test/a_test.dart...'), + ]), + ); + + expect( + processRunner.recordedCalls, + containsAll([ + ProcessCall( + '/packages/plugin/example/example1/android/gradlew', + 'app:assembleDebug -Pverbose=true -Ptarget=/packages/plugin/example/example1/integration_test/a_test.dart' + .split(' '), + '/packages/plugin/example/example1/android'), + ProcessCall( + 'gcloud', + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/example1/0/ --device model=redfin,version=30 --device model=seoul,version=26' + .split(' '), + '/packages/plugin/example/example1'), + ProcessCall( + '/packages/plugin/example/example2/android/gradlew', + 'app:assembleDebug -Pverbose=true -Ptarget=/packages/plugin/example/example2/integration_test/a_test.dart' + .split(' '), + '/packages/plugin/example/example2/android'), + ProcessCall( + 'gcloud', + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/example2/0/ --device model=redfin,version=30 --device model=seoul,version=26' + .split(' '), + '/packages/plugin/example/example2'), + ]), + ); + }); + test('fails if a test fails twice', () async { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; @@ -479,7 +544,7 @@ public class MainActivityTest { output, containsAllInOrder([ contains('Running for package'), - contains('package/example does not support Android'), + contains('No examples support Android'), ]), ); expect(output, @@ -547,7 +612,7 @@ public class MainActivityTest { '/packages/plugin/example/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/0/ --device model=redfin,version=30' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/example/0/ --device model=redfin,version=30' .split(' '), '/packages/plugin/example'), ]), @@ -717,7 +782,7 @@ public class MainActivityTest { '/packages/plugin/example/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/0/ --device model=redfin,version=30' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/example/0/ --device model=redfin,version=30' .split(' '), '/packages/plugin/example'), ]), diff --git a/script/tool/test/lint_android_command_test.dart b/script/tool/test/lint_android_command_test.dart index a9ad510f7ee9..91608d3785cc 100644 --- a/script/tool/test/lint_android_command_test.dart +++ b/script/tool/test/lint_android_command_test.dart @@ -16,7 +16,7 @@ import 'mocks.dart'; import 'util.dart'; void main() { - group('$LintAndroidCommand', () { + group('LintAndroidCommand', () { FileSystem fileSystem; late Directory packagesDir; late CommandRunner runner; @@ -72,6 +72,47 @@ void main() { ])); }); + test('runs on all examples', () async { + final List examples = ['example1', 'example2']; + final Directory pluginDir = createFakePlugin('plugin1', packagesDir, + examples: examples, + extraFiles: [ + 'example/example1/android/gradlew', + 'example/example2/android/gradlew', + ], + platformSupport: { + platformAndroid: const PlatformDetails(PlatformSupport.inline) + }); + + final Iterable exampleAndroidDirs = examples.map( + (String example) => pluginDir + .childDirectory('example') + .childDirectory(example) + .childDirectory('android')); + + final List output = + await runCapturingPrint(runner, ['lint-android']); + + expect( + processRunner.recordedCalls, + orderedEquals([ + for (final Directory directory in exampleAndroidDirs) + ProcessCall( + directory.childFile('gradlew').path, + const ['plugin1:lintDebug'], + directory.path, + ), + ]), + ); + + expect( + output, + containsAllInOrder([ + contains('Running for plugin1'), + contains('No issues found!'), + ])); + }); + test('fails if gradlew is missing', () async { createFakePlugin('plugin1', packagesDir, platformSupport: { @@ -89,18 +130,25 @@ void main() { output, containsAllInOrder( [ - contains('Build example before linting'), + contains('Build examples before linting'), ], )); }); test('fails if linting finds issues', () async { - createFakePlugin('plugin1', packagesDir, - platformSupport: { - platformAndroid: const PlatformDetails(PlatformSupport.inline) - }); + final Directory pluginDir = + createFakePlugin('plugin1', packagesDir, extraFiles: [ + 'example/android/gradlew', + ], platformSupport: { + platformAndroid: const PlatformDetails(PlatformSupport.inline) + }); - processRunner.mockProcessesForExecutable['gradlew'] = [ + final String gradlewPath = pluginDir + .childDirectory('example') + .childDirectory('android') + .childFile('gradlew') + .path; + processRunner.mockProcessesForExecutable[gradlewPath] = [ MockProcess(exitCode: 1), ]; @@ -115,7 +163,7 @@ void main() { output, containsAllInOrder( [ - contains('Build example before linting'), + contains('The following packages had errors:'), ], )); }); From 01ba538e910e9cf2dd3f7cb820c8c1739b479609 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 3 May 2022 15:14:12 -0400 Subject: [PATCH 499/600] Roll Flutter from 2e1ef66665b2 to adb8b607e3d2 (2 revisions) (#5602) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 27b06c9dba1e..955d5ddc4f22 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2e1ef66665b2fc9c1efd3020d60c1785eed24399 +adb8b607e3d2b41c68d87eb80c94bdb0776a9363 From cc132b4dcc24b4a6f1fdeaedc3033f8fb7a7c15e Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 3 May 2022 16:19:11 -0400 Subject: [PATCH 500/600] Roll Flutter from adb8b607e3d2 to 1aab2d83d932 (3 revisions) (#5606) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 955d5ddc4f22..368d3f785e14 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -adb8b607e3d2b41c68d87eb80c94bdb0776a9363 +1aab2d83d9324a426c2cfc3468f6d98e5a2d5e43 From 92333f71801f43e1e5143768bc15e9ecc3db3758 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 3 May 2022 17:14:11 -0400 Subject: [PATCH 501/600] [tools] Convert test utils to RepositoryPackage (#5605) --- script/tool/lib/src/common/core.dart | 14 +- script/tool/lib/src/common/gradle.dart | 6 +- .../tool/lib/src/common/plugin_command.dart | 14 +- .../lib/src/common/repository_package.dart | 39 +++ .../src/create_all_plugins_app_command.dart | 22 +- .../lib/src/firebase_test_lab_command.dart | 4 +- script/tool/lib/src/lint_android_command.dart | 2 +- .../lib/src/make_deps_path_based_command.dart | 2 +- script/tool/lib/src/native_test_command.dart | 14 +- .../tool/lib/src/publish_check_command.dart | 4 +- script/tool/lib/src/test_command.dart | 2 +- .../tool/lib/src/version_check_command.dart | 2 +- script/tool/test/analyze_command_test.dart | 84 +++--- .../test/build_examples_command_test.dart | 53 ++-- script/tool/test/common/gradle_test.dart | 39 +-- .../common/package_looping_command_test.dart | 210 +++++++++------ .../tool/test/common/plugin_command_test.dart | 201 ++++++++++----- .../tool/test/common/plugin_utils_test.dart | 40 ++- .../test/common/repository_package_test.dart | 97 +++---- .../create_all_plugins_app_command_test.dart | 14 +- .../tool/test/custom_test_command_test.dart | 24 +- .../test/drive_examples_command_test.dart | 59 ++--- .../federation_safety_check_command_test.dart | 100 ++++---- .../test/firebase_test_lab_command_test.dart | 79 +++--- script/tool/test/format_command_test.dart | 39 +-- .../tool/test/lint_android_command_test.dart | 23 +- .../tool/test/lint_podspecs_command_test.dart | 10 +- script/tool/test/list_command_test.dart | 32 +-- .../make_deps_path_based_command_test.dart | 56 ++-- .../tool/test/native_test_command_test.dart | 242 +++++++++--------- .../tool/test/publish_check_command_test.dart | 25 +- .../test/publish_plugin_command_test.dart | 112 ++++---- .../tool/test/pubspec_check_command_test.dart | 116 ++++----- .../tool/test/readme_check_command_test.dart | 57 +++-- script/tool/test/test_command_test.dart | 64 +++-- .../test/update_excerpts_command_test.dart | 18 +- script/tool/test/util.dart | 71 ++--- .../tool/test/version_check_command_test.dart | 80 +++--- .../tool/test/xcode_analyze_command_test.dart | 40 ++- 39 files changed, 1124 insertions(+), 986 deletions(-) diff --git a/script/tool/lib/src/common/core.dart b/script/tool/lib/src/common/core.dart index 13678d720a76..b91029f1a5c8 100644 --- a/script/tool/lib/src/common/core.dart +++ b/script/tool/lib/src/common/core.dart @@ -30,14 +30,22 @@ const String platformWindows = 'windows'; /// Key for enable experiment. const String kEnableExperiment = 'enable-experiment'; +/// Target platforms supported by Flutter. +// ignore: public_member_api_docs +enum FlutterPlatform { android, ios, linux, macos, web, windows } + /// Returns whether the given directory is a Dart package. bool isPackage(FileSystemEntity entity) { if (entity is! Directory) { return false; } - // Per https://dart.dev/guides/libraries/create-library-packages#what-makes-a-library-package - return entity.childFile('pubspec.yaml').existsSync() && - entity.childDirectory('lib').existsSync(); + // According to + // https://dart.dev/guides/libraries/create-library-packages#what-makes-a-library-package + // a package must also have a `lib/` directory, but in practice that's not + // always true. flutter/plugins has some special cases (espresso, some + // federated implementation packages) that don't have any source, so this + // deliberately doesn't check that there's a lib directory. + return entity.childFile('pubspec.yaml').existsSync(); } /// Prints `successMessage` in green. diff --git a/script/tool/lib/src/common/gradle.dart b/script/tool/lib/src/common/gradle.dart index 9da4e89811e6..746536075014 100644 --- a/script/tool/lib/src/common/gradle.dart +++ b/script/tool/lib/src/common/gradle.dart @@ -6,6 +6,7 @@ import 'package:file/file.dart'; import 'package:platform/platform.dart'; import 'process_runner.dart'; +import 'repository_package.dart'; const String _gradleWrapperWindows = 'gradlew.bat'; const String _gradleWrapperNonWindows = 'gradlew'; @@ -21,7 +22,7 @@ class GradleProject { }); /// The directory of a Flutter project to run Gradle commands in. - final Directory flutterProject; + final RepositoryPackage flutterProject; /// The [ProcessRunner] used to run commands. Overridable for testing. final ProcessRunner processRunner; @@ -30,7 +31,8 @@ class GradleProject { final Platform platform; /// The project's 'android' directory. - Directory get androidDirectory => flutterProject.childDirectory('android'); + Directory get androidDirectory => + flutterProject.platformDirectory(FlutterPlatform.android); /// The path to the Gradle wrapper file for the project. File get gradleWrapper => androidDirectory.childFile( diff --git a/script/tool/lib/src/common/plugin_command.dart b/script/tool/lib/src/common/plugin_command.dart index d21002899669..0ec890368fb5 100644 --- a/script/tool/lib/src/common/plugin_command.dart +++ b/script/tool/lib/src/common/plugin_command.dart @@ -368,7 +368,7 @@ abstract class PluginCommand extends Command { await for (final FileSystemEntity entity in dir.list(followLinks: false)) { // A top-level Dart package is a plugin package. - if (_isDartPackage(entity)) { + if (isPackage(entity)) { if (packages.isEmpty || packages.contains(p.basename(entity.path))) { yield PackageEnumerationEntry( RepositoryPackage(entity as Directory), @@ -378,7 +378,7 @@ abstract class PluginCommand extends Command { // Look for Dart packages under this top-level directory. await for (final FileSystemEntity subdir in entity.list(followLinks: false)) { - if (_isDartPackage(subdir)) { + if (isPackage(subdir)) { // There are three ways for a federated plugin to match: // - package name (path_provider_android) // - fully specified name (path_provider/path_provider_android) @@ -427,9 +427,9 @@ abstract class PluginCommand extends Command { {bool filterExcluded = true}) async* { yield* package.directory .list(recursive: true, followLinks: false) - .where(_isDartPackage) + .where(isPackage) .map((FileSystemEntity directory) => - // _isDartPackage guarantees that this cast is valid. + // isPackage guarantees that this cast is valid. RepositoryPackage(directory as Directory)); } @@ -448,12 +448,6 @@ abstract class PluginCommand extends Command { .cast(); } - /// Returns whether the specified entity is a directory containing a - /// `pubspec.yaml` file. - bool _isDartPackage(FileSystemEntity entity) { - return entity is Directory && entity.childFile('pubspec.yaml').existsSync(); - } - /// Retrieve an instance of [GitVersionFinder] based on `_baseShaArg` and [gitDir]. /// /// Throws tool exit if [gitDir] nor root directory is a git directory. diff --git a/script/tool/lib/src/common/repository_package.dart b/script/tool/lib/src/common/repository_package.dart index 579ba01868e4..5f448d36d7e2 100644 --- a/script/tool/lib/src/common/repository_package.dart +++ b/script/tool/lib/src/common/repository_package.dart @@ -9,6 +9,7 @@ import 'package:pubspec_parse/pubspec_parse.dart'; import 'core.dart'; export 'package:pubspec_parse/pubspec_parse.dart' show Pubspec; +export 'core.dart' show FlutterPlatform; /// A package in the repository. // @@ -53,6 +54,44 @@ class RepositoryPackage { /// The package's top-level README. File get readmeFile => directory.childFile('README.md'); + /// The package's top-level README. + File get changelogFile => directory.childFile('CHANGELOG.md'); + + /// The package's top-level README. + File get authorsFile => directory.childFile('AUTHORS'); + + /// The lib directory containing the package's code. + Directory get libDirectory => directory.childDirectory('lib'); + + /// The test directory containing the package's Dart tests. + Directory get testDirectory => directory.childDirectory('test'); + + /// Returns the directory containing support for [platform]. + Directory platformDirectory(FlutterPlatform platform) { + late final String directoryName; + switch (platform) { + case FlutterPlatform.android: + directoryName = 'android'; + break; + case FlutterPlatform.ios: + directoryName = 'ios'; + break; + case FlutterPlatform.linux: + directoryName = 'linux'; + break; + case FlutterPlatform.macos: + directoryName = 'macos'; + break; + case FlutterPlatform.web: + directoryName = 'web'; + break; + case FlutterPlatform.windows: + directoryName = 'windows'; + break; + } + return directory.childDirectory(directoryName); + } + late final Pubspec _parsedPubspec = Pubspec.parse(pubspecFile.readAsStringSync()); diff --git a/script/tool/lib/src/create_all_plugins_app_command.dart b/script/tool/lib/src/create_all_plugins_app_command.dart index 6b44ab788786..ad836e19d9c6 100644 --- a/script/tool/lib/src/create_all_plugins_app_command.dart +++ b/script/tool/lib/src/create_all_plugins_app_command.dart @@ -30,11 +30,14 @@ class CreateAllPluginsAppCommand extends PluginCommand { 'Defaults to the repository root.'); } - /// The location of the synthesized app project. - Directory get appDirectory => packagesDir.fileSystem + /// The location to create the synthesized app project. + Directory get _appDirectory => packagesDir.fileSystem .directory(getStringArg(_outputDirectoryFlag)) .childDirectory('all_plugins'); + /// The synthesized app project. + RepositoryPackage get app => RepositoryPackage(_appDirectory); + @override String get description => 'Generate Flutter app that includes all plugins in packages.'; @@ -73,7 +76,7 @@ class CreateAllPluginsAppCommand extends PluginCommand { '--template=app', '--project-name=all_plugins', '--android-language=java', - appDirectory.path, + _appDirectory.path, ], ); @@ -83,8 +86,8 @@ class CreateAllPluginsAppCommand extends PluginCommand { } Future _updateAppGradle() async { - final File gradleFile = appDirectory - .childDirectory('android') + final File gradleFile = app + .platformDirectory(FlutterPlatform.android) .childDirectory('app') .childFile('build.gradle'); if (!gradleFile.existsSync()) { @@ -119,8 +122,8 @@ class CreateAllPluginsAppCommand extends PluginCommand { } Future _updateManifest() async { - final File manifestFile = appDirectory - .childDirectory('android') + final File manifestFile = app + .platformDirectory(FlutterPlatform.android) .childDirectory('app') .childDirectory('src') .childDirectory('main') @@ -147,12 +150,11 @@ class CreateAllPluginsAppCommand extends PluginCommand { } Future _genPubspecWithAllPlugins() async { - final RepositoryPackage buildAllApp = RepositoryPackage(appDirectory); // Read the old pubspec file's Dart SDK version, in order to preserve it // in the new file. The template sometimes relies on having opted in to // specific language features via SDK version, so using a different one // can cause compilation failures. - final Pubspec originalPubspec = buildAllApp.parsePubspec(); + final Pubspec originalPubspec = app.parsePubspec(); const String dartSdkKey = 'sdk'; final VersionConstraint dartSdkConstraint = originalPubspec.environment?[dartSdkKey] ?? @@ -177,7 +179,7 @@ class CreateAllPluginsAppCommand extends PluginCommand { }, dependencyOverrides: pluginDeps, ); - buildAllApp.pubspecFile.writeAsStringSync(_pubspecToString(pubspec)); + app.pubspecFile.writeAsStringSync(_pubspecToString(pubspec)); } Future> _getValidPathDependencies() async { diff --git a/script/tool/lib/src/firebase_test_lab_command.dart b/script/tool/lib/src/firebase_test_lab_command.dart index 6cc3c129c6bd..4505259b731f 100644 --- a/script/tool/lib/src/firebase_test_lab_command.dart +++ b/script/tool/lib/src/firebase_test_lab_command.dart @@ -146,7 +146,7 @@ class FirebaseTestLabCommand extends PackageLoopingCommand { required RepositoryPackage package, }) async { final Directory androidDirectory = - example.directory.childDirectory('android'); + example.platformDirectory(FlutterPlatform.android); if (!androidDirectory.existsSync()) { return PackageResult.skip( '${example.displayName} does not support Android.'); @@ -171,7 +171,7 @@ class FirebaseTestLabCommand extends PackageLoopingCommand { } // Ensures that gradle wrapper exists - final GradleProject project = GradleProject(example.directory, + final GradleProject project = GradleProject(example, processRunner: processRunner, platform: platform); if (!await _ensureGradleWrapperExists(project)) { return PackageResult.fail(['Unable to build example apk']); diff --git a/script/tool/lib/src/lint_android_command.dart b/script/tool/lib/src/lint_android_command.dart index 467dc7942d95..8ba1d643a89b 100644 --- a/script/tool/lib/src/lint_android_command.dart +++ b/script/tool/lib/src/lint_android_command.dart @@ -40,7 +40,7 @@ class LintAndroidCommand extends PackageLoopingCommand { bool failed = false; for (final RepositoryPackage example in package.getExamples()) { - final GradleProject project = GradleProject(example.directory, + final GradleProject project = GradleProject(example, processRunner: processRunner, platform: platform); if (!project.isConfigured()) { diff --git a/script/tool/lib/src/make_deps_path_based_command.dart b/script/tool/lib/src/make_deps_path_based_command.dart index 370fc3559f71..9b861c34ec91 100644 --- a/script/tool/lib/src/make_deps_path_based_command.dart +++ b/script/tool/lib/src/make_deps_path_based_command.dart @@ -178,7 +178,7 @@ dependency_overrides: for (final String packageName in packagesToOverride) { // Find the relative path from the common base to the local package. final List repoRelativePathComponents = path.split( - path.relative(localDependencies[packageName]!.directory.path, + path.relative(localDependencies[packageName]!.path, from: commonBasePath)); newPubspecContents += ''' $packageName: diff --git a/script/tool/lib/src/native_test_command.dart b/script/tool/lib/src/native_test_command.dart index a0d2ebd4e23c..81b13cbb75e2 100644 --- a/script/tool/lib/src/native_test_command.dart +++ b/script/tool/lib/src/native_test_command.dart @@ -198,22 +198,22 @@ this command. Future<_PlatformResult> _testAndroid( RepositoryPackage plugin, _TestMode mode) async { bool exampleHasUnitTests(RepositoryPackage example) { - return example.directory - .childDirectory('android') + return example + .platformDirectory(FlutterPlatform.android) .childDirectory('app') .childDirectory('src') .childDirectory('test') .existsSync() || - example.directory.parent - .childDirectory('android') + plugin + .platformDirectory(FlutterPlatform.android) .childDirectory('src') .childDirectory('test') .existsSync(); } bool exampleHasNativeIntegrationTests(RepositoryPackage example) { - final Directory integrationTestDirectory = example.directory - .childDirectory('android') + final Directory integrationTestDirectory = example + .platformDirectory(FlutterPlatform.android) .childDirectory('app') .childDirectory('src') .childDirectory('androidTest'); @@ -269,7 +269,7 @@ this command. _printRunningExampleTestsMessage(example, 'Android'); final GradleProject project = GradleProject( - example.directory, + example, processRunner: processRunner, platform: platform, ); diff --git a/script/tool/lib/src/publish_check_command.dart b/script/tool/lib/src/publish_check_command.dart index b6b83dabcb49..af8eac2257eb 100644 --- a/script/tool/lib/src/publish_check_command.dart +++ b/script/tool/lib/src/publish_check_command.dart @@ -246,12 +246,12 @@ HTTP response: ${pubVersionFinderResponse.httpResponse.body} bool _passesAuthorsCheck(RepositoryPackage package) { final List pathComponents = - package.directory.fileSystem.path.split(package.directory.path); + package.directory.fileSystem.path.split(package.path); if (pathComponents.contains('third_party')) { // Third-party packages aren't required to have an AUTHORS file. return true; } - return package.directory.childFile('AUTHORS').existsSync(); + return package.authorsFile.existsSync(); } void _printImportantStatusMessage(String message, {required bool isError}) { diff --git a/script/tool/lib/src/test_command.dart b/script/tool/lib/src/test_command.dart index a1a995dbd88f..27a01c95e851 100644 --- a/script/tool/lib/src/test_command.dart +++ b/script/tool/lib/src/test_command.dart @@ -41,7 +41,7 @@ class TestCommand extends PackageLoopingCommand { @override Future runForPackage(RepositoryPackage package) async { - if (!package.directory.childDirectory('test').existsSync()) { + if (!package.testDirectory.existsSync()) { return PackageResult.skip('No test/ directory.'); } diff --git a/script/tool/lib/src/version_check_command.dart b/script/tool/lib/src/version_check_command.dart index f69611f5a06d..fb768c19b524 100644 --- a/script/tool/lib/src/version_check_command.dart +++ b/script/tool/lib/src/version_check_command.dart @@ -384,7 +384,7 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body} final Version fromPubspec = pubspec.version!; // get first version from CHANGELOG - final File changelog = package.directory.childFile('CHANGELOG.md'); + final File changelog = package.changelogFile; final List lines = changelog.readAsLinesSync(); String? firstLineWithText; final Iterator iterator = lines.iterator; diff --git a/script/tool/test/analyze_command_test.dart b/script/tool/test/analyze_command_test.dart index 98fb9382a3aa..e293e8b85e98 100644 --- a/script/tool/test/analyze_command_test.dart +++ b/script/tool/test/analyze_command_test.dart @@ -37,45 +37,45 @@ void main() { }); test('analyzes all packages', () async { - final Directory plugin1Dir = createFakePlugin('a', packagesDir); - final Directory plugin2Dir = createFakePlugin('b', packagesDir); + final RepositoryPackage plugin1 = createFakePlugin('a', packagesDir); + final RepositoryPackage plugin2 = createFakePlugin('b', packagesDir); await runCapturingPrint(runner, ['analyze']); expect( processRunner.recordedCalls, orderedEquals([ - ProcessCall('flutter', const ['pub', 'get'], plugin1Dir.path), - ProcessCall('dart', const ['analyze', '--fatal-infos'], - plugin1Dir.path), - ProcessCall('flutter', const ['pub', 'get'], plugin2Dir.path), - ProcessCall('dart', const ['analyze', '--fatal-infos'], - plugin2Dir.path), + ProcessCall('flutter', const ['pub', 'get'], plugin1.path), + ProcessCall( + 'dart', const ['analyze', '--fatal-infos'], plugin1.path), + ProcessCall('flutter', const ['pub', 'get'], plugin2.path), + ProcessCall( + 'dart', const ['analyze', '--fatal-infos'], plugin2.path), ])); }); test('skips flutter pub get for examples', () async { - final Directory plugin1Dir = createFakePlugin('a', packagesDir); + final RepositoryPackage plugin1 = createFakePlugin('a', packagesDir); await runCapturingPrint(runner, ['analyze']); expect( processRunner.recordedCalls, orderedEquals([ - ProcessCall('flutter', const ['pub', 'get'], plugin1Dir.path), - ProcessCall('dart', const ['analyze', '--fatal-infos'], - plugin1Dir.path), + ProcessCall('flutter', const ['pub', 'get'], plugin1.path), + ProcessCall( + 'dart', const ['analyze', '--fatal-infos'], plugin1.path), ])); }); test('runs flutter pub get for non-example subpackages', () async { - final Directory mainPackageDir = createFakePackage('a', packagesDir); - final Directory otherPackages = - mainPackageDir.childDirectory('other_packages'); - final Directory subpackage1 = - createFakePackage('subpackage1', otherPackages); - final Directory subpackage2 = - createFakePackage('subpackage2', otherPackages); + final RepositoryPackage mainPackage = createFakePackage('a', packagesDir); + final Directory otherPackagesDir = + mainPackage.directory.childDirectory('other_packages'); + final RepositoryPackage subpackage1 = + createFakePackage('subpackage1', otherPackagesDir); + final RepositoryPackage subpackage2 = + createFakePackage('subpackage2', otherPackagesDir); await runCapturingPrint(runner, ['analyze']); @@ -83,36 +83,36 @@ void main() { processRunner.recordedCalls, orderedEquals([ ProcessCall( - 'flutter', const ['pub', 'get'], mainPackageDir.path), + 'flutter', const ['pub', 'get'], mainPackage.path), ProcessCall( 'flutter', const ['pub', 'get'], subpackage1.path), ProcessCall( 'flutter', const ['pub', 'get'], subpackage2.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], - mainPackageDir.path), + mainPackage.path), ])); }); test('don\'t elide a non-contained example package', () async { - final Directory plugin1Dir = createFakePlugin('a', packagesDir); - final Directory plugin2Dir = createFakePlugin('example', packagesDir); + final RepositoryPackage plugin1 = createFakePlugin('a', packagesDir); + final RepositoryPackage plugin2 = createFakePlugin('example', packagesDir); await runCapturingPrint(runner, ['analyze']); expect( processRunner.recordedCalls, orderedEquals([ - ProcessCall('flutter', const ['pub', 'get'], plugin1Dir.path), - ProcessCall('dart', const ['analyze', '--fatal-infos'], - plugin1Dir.path), - ProcessCall('flutter', const ['pub', 'get'], plugin2Dir.path), - ProcessCall('dart', const ['analyze', '--fatal-infos'], - plugin2Dir.path), + ProcessCall('flutter', const ['pub', 'get'], plugin1.path), + ProcessCall( + 'dart', const ['analyze', '--fatal-infos'], plugin1.path), + ProcessCall('flutter', const ['pub', 'get'], plugin2.path), + ProcessCall( + 'dart', const ['analyze', '--fatal-infos'], plugin2.path), ])); }); test('uses a separate analysis sdk', () async { - final Directory pluginDir = createFakePlugin('a', packagesDir); + final RepositoryPackage plugin = createFakePlugin('a', packagesDir); await runCapturingPrint( runner, ['analyze', '--analysis-sdk', 'foo/bar/baz']); @@ -123,12 +123,12 @@ void main() { ProcessCall( 'flutter', const ['pub', 'get'], - pluginDir.path, + plugin.path, ), ProcessCall( 'foo/bar/baz/bin/dart', const ['analyze', '--fatal-infos'], - pluginDir.path, + plugin.path, ), ]), ); @@ -180,7 +180,7 @@ void main() { }); test('takes an allow list', () async { - final Directory pluginDir = createFakePlugin('foo', packagesDir, + final RepositoryPackage plugin = createFakePlugin('foo', packagesDir, extraFiles: ['analysis_options.yaml']); await runCapturingPrint( @@ -189,15 +189,14 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - ProcessCall( - 'flutter', const ['pub', 'get'], pluginDir.path), + ProcessCall('flutter', const ['pub', 'get'], plugin.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], - pluginDir.path), + plugin.path), ])); }); test('takes an allow config file', () async { - final Directory pluginDir = createFakePlugin('foo', packagesDir, + final RepositoryPackage plugin = createFakePlugin('foo', packagesDir, extraFiles: ['analysis_options.yaml']); final File allowFile = packagesDir.childFile('custom.yaml'); allowFile.writeAsStringSync('- foo'); @@ -208,10 +207,9 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - ProcessCall( - 'flutter', const ['pub', 'get'], pluginDir.path), + ProcessCall('flutter', const ['pub', 'get'], plugin.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], - pluginDir.path), + plugin.path), ])); }); @@ -280,7 +278,7 @@ void main() { // modify the script above, as it is run from source, but out-of-repo. // Contact stuartmorgan or devoncarew for assistance. test('Dart repo analyze command works', () async { - final Directory pluginDir = createFakePlugin('foo', packagesDir, + final RepositoryPackage plugin = createFakePlugin('foo', packagesDir, extraFiles: ['analysis_options.yaml']); final File allowFile = packagesDir.childFile('custom.yaml'); allowFile.writeAsStringSync('- foo'); @@ -300,12 +298,12 @@ void main() { ProcessCall( 'flutter', const ['pub', 'get'], - pluginDir.path, + plugin.path, ), ProcessCall( 'foo/bar/baz/bin/dart', const ['analyze', '--fatal-infos'], - pluginDir.path, + plugin.path, ), ]), ); diff --git a/script/tool/test/build_examples_command_test.dart b/script/tool/test/build_examples_command_test.dart index 2bdb1bc0c2ba..420b3b1161db 100644 --- a/script/tool/test/build_examples_command_test.dart +++ b/script/tool/test/build_examples_command_test.dart @@ -134,13 +134,12 @@ void main() { test('building for iOS', () async { mockPlatform.isMacOS = true; - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformIOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, ['build-examples', '--ios', '--enable-experiment=exp1']); @@ -191,13 +190,12 @@ void main() { test('building for Linux', () async { mockPlatform.isLinux = true; - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformLinux: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint( runner, ['build-examples', '--linux']); @@ -240,13 +238,12 @@ void main() { test('building for macOS', () async { mockPlatform.isMacOS = true; - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint( runner, ['build-examples', '--macos']); @@ -286,13 +283,12 @@ void main() { }); test('building for web', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformWeb: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, ['build-examples', '--web']); @@ -336,13 +332,12 @@ void main() { test('building for Windows', () async { mockPlatform.isWindows = true; - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformWindows: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint( runner, ['build-examples', '--windows']); @@ -386,13 +381,12 @@ void main() { }); test('building for Android', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, [ 'build-examples', @@ -415,13 +409,12 @@ void main() { }); test('enable-experiment flag for Android', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); await runCapturingPrint(runner, ['build-examples', '--apk', '--enable-experiment=exp1']); @@ -437,13 +430,12 @@ void main() { }); test('enable-experiment flag for ios', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformIOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); await runCapturingPrint(runner, ['build-examples', '--ios', '--enable-experiment=exp1']); @@ -481,7 +473,7 @@ void main() { group('packages', () { test('builds when requested platform is supported by example', () async { - final Directory packageDirectory = createFakePackage( + final RepositoryPackage package = createFakePackage( 'package', packagesDir, isFlutter: true, extraFiles: [ 'example/ios/Runner.xcodeproj/project.pbxproj' ]); @@ -507,7 +499,7 @@ void main() { 'ios', '--no-codesign', ], - packageDirectory.childDirectory('example').path), + getExampleDir(package).path), ])); }); @@ -567,7 +559,7 @@ void main() { }); test('logs skipped platforms when only some are supported', () async { - final Directory packageDirectory = createFakePackage( + final RepositoryPackage package = createFakePackage( 'package', packagesDir, isFlutter: true, extraFiles: ['example/linux/CMakeLists.txt']); @@ -590,21 +582,20 @@ void main() { ProcessCall( getFlutterCommand(mockPlatform), const ['build', 'linux'], - packageDirectory.childDirectory('example').path), + getExampleDir(package).path), ])); }); }); test('The .pluginToolsConfig.yaml file', () async { mockPlatform.isLinux = true; - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformLinux: const PlatformDetails(PlatformSupport.inline), platformMacOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final File pluginExampleConfigFile = pluginExampleDirectory.childFile('.pluginToolsConfig.yaml'); diff --git a/script/tool/test/common/gradle_test.dart b/script/tool/test/common/gradle_test.dart index 3eac60baf3c3..8df4a65b93a5 100644 --- a/script/tool/test/common/gradle_test.dart +++ b/script/tool/test/common/gradle_test.dart @@ -23,7 +23,7 @@ void main() { group('isConfigured', () { test('reports true when configured on Windows', () async { - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', fileSystem.directory('/'), extraFiles: ['android/gradlew.bat']); final GradleProject project = GradleProject( @@ -36,7 +36,7 @@ void main() { }); test('reports true when configured on non-Windows', () async { - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', fileSystem.directory('/'), extraFiles: ['android/gradlew']); final GradleProject project = GradleProject( @@ -49,7 +49,7 @@ void main() { }); test('reports false when not configured on Windows', () async { - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', fileSystem.directory('/'), extraFiles: ['android/foo']); final GradleProject project = GradleProject( @@ -62,7 +62,7 @@ void main() { }); test('reports true when configured on non-Windows', () async { - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', fileSystem.directory('/'), extraFiles: ['android/foo']); final GradleProject project = GradleProject( @@ -75,9 +75,9 @@ void main() { }); }); - group('runXcodeBuild', () { + group('runCommand', () { test('runs without arguments', () async { - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', fileSystem.directory('/'), extraFiles: ['android/gradlew']); final GradleProject project = GradleProject( @@ -93,16 +93,19 @@ void main() { processRunner.recordedCalls, orderedEquals([ ProcessCall( - plugin.childDirectory('android').childFile('gradlew').path, + plugin + .platformDirectory(FlutterPlatform.android) + .childFile('gradlew') + .path, const [ 'foo', ], - plugin.childDirectory('android').path), + plugin.platformDirectory(FlutterPlatform.android).path), ])); }); test('runs with arguments', () async { - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', fileSystem.directory('/'), extraFiles: ['android/gradlew']); final GradleProject project = GradleProject( @@ -121,18 +124,21 @@ void main() { processRunner.recordedCalls, orderedEquals([ ProcessCall( - plugin.childDirectory('android').childFile('gradlew').path, + plugin + .platformDirectory(FlutterPlatform.android) + .childFile('gradlew') + .path, const [ 'foo', '--bar', '--baz', ], - plugin.childDirectory('android').path), + plugin.platformDirectory(FlutterPlatform.android).path), ])); }); test('runs with the correct wrapper on Windows', () async { - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', fileSystem.directory('/'), extraFiles: ['android/gradlew.bat']); final GradleProject project = GradleProject( @@ -148,16 +154,19 @@ void main() { processRunner.recordedCalls, orderedEquals([ ProcessCall( - plugin.childDirectory('android').childFile('gradlew.bat').path, + plugin + .platformDirectory(FlutterPlatform.android) + .childFile('gradlew.bat') + .path, const [ 'foo', ], - plugin.childDirectory('android').path), + plugin.platformDirectory(FlutterPlatform.android).path), ])); }); test('returns error codes', () async { - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', fileSystem.directory('/'), extraFiles: ['android/gradlew.bat']); final GradleProject project = GradleProject( diff --git a/script/tool/test/common/package_looping_command_test.dart b/script/tool/test/common/package_looping_command_test.dart index ea02bd4ea387..3dd6a47ae2fb 100644 --- a/script/tool/test/common/package_looping_command_test.dart +++ b/script/tool/test/common/package_looping_command_test.dart @@ -11,7 +11,6 @@ import 'package:file/memory.dart'; import 'package:flutter_plugin_tools/src/common/core.dart'; import 'package:flutter_plugin_tools/src/common/package_looping_command.dart'; import 'package:flutter_plugin_tools/src/common/process_runner.dart'; -import 'package:flutter_plugin_tools/src/common/repository_package.dart'; import 'package:git/git.dart'; import 'package:mockito/mockito.dart'; import 'package:platform/platform.dart'; @@ -31,6 +30,21 @@ const String _startSuccessColor = '\x1B[32m'; const String _startWarningColor = '\x1B[33m'; const String _endColor = '\x1B[0m'; +// The filename within a package containing warnings to log during runForPackage. +enum _ResultFileType { + /// A file containing errors to return. + errors, + + /// A file containing warnings that should be logged. + warns, + + /// A file indicating that the package should be skipped, and why. + skips, + + /// A file indicating that the package should throw. + throws, +} + // The filename within a package containing errors to return from runForPackage. const String _errorFile = 'errors'; // The filename within a package indicating that it should be skipped. @@ -40,6 +54,30 @@ const String _warningFile = 'warnings'; // The filename within a package indicating that it should throw. const String _throwFile = 'throw'; +/// Writes a file to [package] to control the behavior of +/// [TestPackageLoopingCommand] for that package. +void _addResultFile(RepositoryPackage package, _ResultFileType type, + {String? contents}) { + final File file = package.directory.childFile(_filenameForType(type)); + file.createSync(); + if (contents != null) { + file.writeAsStringSync(contents); + } +} + +String _filenameForType(_ResultFileType type) { + switch (type) { + case _ResultFileType.errors: + return _errorFile; + case _ResultFileType.warns: + return _warningFile; + case _ResultFileType.skips: + return _skipFile; + case _ResultFileType.throws: + return _throwFile; + } +} + void main() { late FileSystem fileSystem; late MockPlatform mockPlatform; @@ -122,10 +160,10 @@ void main() { test('does not stop looping on error', () async { createFakePackage('package_a', packagesDir); - final Directory failingPackage = + final RepositoryPackage failingPackage = createFakePlugin('package_b', packagesDir); createFakePackage('package_c', packagesDir); - failingPackage.childFile(_errorFile).createSync(); + _addResultFile(failingPackage, _ResultFileType.errors); final TestPackageLoopingCommand command = createTestCommand(hasLongOutput: false); @@ -147,10 +185,10 @@ void main() { test('does not stop looping on exceptions', () async { createFakePackage('package_a', packagesDir); - final Directory failingPackage = + final RepositoryPackage failingPackage = createFakePlugin('package_b', packagesDir); createFakePackage('package_c', packagesDir); - failingPackage.childFile(_throwFile).createSync(); + _addResultFile(failingPackage, _ResultFileType.throws); final TestPackageLoopingCommand command = createTestCommand(hasLongOutput: false); @@ -173,8 +211,10 @@ void main() { group('package iteration', () { test('includes plugins and packages', () async { - final Directory plugin = createFakePlugin('a_plugin', packagesDir); - final Directory package = createFakePackage('a_package', packagesDir); + final RepositoryPackage plugin = + createFakePlugin('a_plugin', packagesDir); + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); final TestPackageLoopingCommand command = createTestCommand(); await runCommand(command); @@ -184,8 +224,9 @@ void main() { }); test('includes third_party/packages', () async { - final Directory package1 = createFakePackage('a_package', packagesDir); - final Directory package2 = + final RepositoryPackage package1 = + createFakePackage('a_package', packagesDir); + final RepositoryPackage package2 = createFakePackage('another_package', thirdPartyPackagesDir); final TestPackageLoopingCommand command = createTestCommand(); @@ -196,9 +237,10 @@ void main() { }); test('includes subpackages when requested', () async { - final Directory plugin = createFakePlugin('a_plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('a_plugin', packagesDir, examples: ['example1', 'example2']); - final Directory package = createFakePackage('a_package', packagesDir); + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); final TestPackageLoopingCommand command = createTestCommand(includeSubpackages: true); @@ -208,39 +250,45 @@ void main() { command.checkedPackages, unorderedEquals([ plugin.path, - plugin.childDirectory('example').childDirectory('example1').path, - plugin.childDirectory('example').childDirectory('example2').path, + getExampleDir(plugin).childDirectory('example1').path, + getExampleDir(plugin).childDirectory('example2').path, package.path, - package.childDirectory('example').path, + getExampleDir(package).path, ])); }); test('excludes subpackages when main package is excluded', () async { - final Directory excluded = createFakePlugin('a_plugin', packagesDir, + final RepositoryPackage excluded = createFakePlugin( + 'a_plugin', packagesDir, examples: ['example1', 'example2']); - final Directory included = createFakePackage('a_package', packagesDir); + final RepositoryPackage included = + createFakePackage('a_package', packagesDir); final TestPackageLoopingCommand command = createTestCommand(includeSubpackages: true); await runCommand(command, arguments: ['--exclude=a_plugin']); + final Iterable examples = excluded.getExamples(); + expect( command.checkedPackages, unorderedEquals([ included.path, - included.childDirectory('example').path, + getExampleDir(included).path, ])); expect(command.checkedPackages, isNot(contains(excluded.path))); - expect(command.checkedPackages, - isNot(contains(excluded.childDirectory('example1').path))); - expect(command.checkedPackages, - isNot(contains(excluded.childDirectory('example2').path))); + expect(examples.length, 2); + for (final RepositoryPackage example in examples) { + expect(command.checkedPackages, isNot(contains(example.path))); + } }); test('skips unsupported versions when requested', () async { - final Directory excluded = createFakePlugin('a_plugin', packagesDir, + final RepositoryPackage excluded = createFakePlugin( + 'a_plugin', packagesDir, flutterConstraint: '>=2.10.0'); - final Directory included = createFakePackage('a_package', packagesDir); + final RepositoryPackage included = + createFakePackage('a_package', packagesDir); final TestPackageLoopingCommand command = createTestCommand(includeSubpackages: true, hasLongOutput: false); @@ -252,7 +300,7 @@ void main() { command.checkedPackages, unorderedEquals([ included.path, - included.childDirectory('example').path, + getExampleDir(included).path, ])); expect(command.checkedPackages, isNot(contains(excluded.path))); @@ -360,13 +408,13 @@ void main() { test('shows failure summaries when something fails without extra details', () async { createFakePackage('package_a', packagesDir); - final Directory failingPackage1 = + final RepositoryPackage failingPackage1 = createFakePlugin('package_b', packagesDir); createFakePackage('package_c', packagesDir); - final Directory failingPackage2 = + final RepositoryPackage failingPackage2 = createFakePlugin('package_d', packagesDir); - failingPackage1.childFile(_errorFile).createSync(); - failingPackage2.childFile(_errorFile).createSync(); + _addResultFile(failingPackage1, _ResultFileType.errors); + _addResultFile(failingPackage2, _ResultFileType.errors); final TestPackageLoopingCommand command = createTestCommand(hasLongOutput: false); @@ -390,13 +438,13 @@ void main() { test('uses custom summary header and footer if provided', () async { createFakePackage('package_a', packagesDir); - final Directory failingPackage1 = + final RepositoryPackage failingPackage1 = createFakePlugin('package_b', packagesDir); createFakePackage('package_c', packagesDir); - final Directory failingPackage2 = + final RepositoryPackage failingPackage2 = createFakePlugin('package_d', packagesDir); - failingPackage1.childFile(_errorFile).createSync(); - failingPackage2.childFile(_errorFile).createSync(); + _addResultFile(failingPackage1, _ResultFileType.errors); + _addResultFile(failingPackage2, _ResultFileType.errors); final TestPackageLoopingCommand command = createTestCommand( hasLongOutput: false, @@ -423,17 +471,15 @@ void main() { test('shows failure summaries when something fails with extra details', () async { createFakePackage('package_a', packagesDir); - final Directory failingPackage1 = + final RepositoryPackage failingPackage1 = createFakePlugin('package_b', packagesDir); createFakePackage('package_c', packagesDir); - final Directory failingPackage2 = + final RepositoryPackage failingPackage2 = createFakePlugin('package_d', packagesDir); - final File errorFile1 = failingPackage1.childFile(_errorFile); - errorFile1.createSync(); - errorFile1.writeAsStringSync('just one detail'); - final File errorFile2 = failingPackage2.childFile(_errorFile); - errorFile2.createSync(); - errorFile2.writeAsStringSync('first detail\nsecond detail'); + _addResultFile(failingPackage1, _ResultFileType.errors, + contents: 'just one detail'); + _addResultFile(failingPackage2, _ResultFileType.errors, + contents: 'first detail\nsecond detail'); final TestPackageLoopingCommand command = createTestCommand(hasLongOutput: false); @@ -479,8 +525,10 @@ void main() { test('logs skips', () async { createFakePackage('package_a', packagesDir); - final Directory skipPackage = createFakePackage('package_b', packagesDir); - skipPackage.childFile(_skipFile).writeAsStringSync('For a reason'); + final RepositoryPackage skipPackage = + createFakePackage('package_b', packagesDir); + _addResultFile(skipPackage, _ResultFileType.skips, + contents: 'For a reason'); final TestPackageLoopingCommand command = createTestCommand(hasLongOutput: false); @@ -513,10 +561,10 @@ void main() { }); test('logs warnings', () async { - final Directory warnPackage = createFakePackage('package_a', packagesDir); - warnPackage - .childFile(_warningFile) - .writeAsStringSync('Warning 1\nWarning 2'); + final RepositoryPackage warnPackage = + createFakePackage('package_a', packagesDir); + _addResultFile(warnPackage, _ResultFileType.warns, + contents: 'Warning 1\nWarning 2'); createFakePackage('package_b', packagesDir); final TestPackageLoopingCommand command = @@ -535,10 +583,10 @@ void main() { test('logs unhandled exceptions as errors', () async { createFakePackage('package_a', packagesDir); - final Directory failingPackage = + final RepositoryPackage failingPackage = createFakePlugin('package_b', packagesDir); createFakePackage('package_c', packagesDir); - failingPackage.childFile(_throwFile).createSync(); + _addResultFile(failingPackage, _ResultFileType.throws); final TestPackageLoopingCommand command = createTestCommand(hasLongOutput: false); @@ -559,23 +607,30 @@ void main() { }); test('prints run summary on success', () async { - final Directory warnPackage1 = + final RepositoryPackage warnPackage1 = createFakePackage('package_a', packagesDir); - warnPackage1 - .childFile(_warningFile) - .writeAsStringSync('Warning 1\nWarning 2'); + _addResultFile(warnPackage1, _ResultFileType.warns, + contents: 'Warning 1\nWarning 2'); + createFakePackage('package_b', packagesDir); - final Directory skipPackage = createFakePackage('package_c', packagesDir); - skipPackage.childFile(_skipFile).writeAsStringSync('For a reason'); - final Directory skipAndWarnPackage = + + final RepositoryPackage skipPackage = + createFakePackage('package_c', packagesDir); + _addResultFile(skipPackage, _ResultFileType.skips, + contents: 'For a reason'); + + final RepositoryPackage skipAndWarnPackage = createFakePackage('package_d', packagesDir); - skipAndWarnPackage.childFile(_warningFile).writeAsStringSync('Warning'); - skipAndWarnPackage.childFile(_skipFile).writeAsStringSync('See warning'); - final Directory warnPackage2 = + _addResultFile(skipAndWarnPackage, _ResultFileType.warns, + contents: 'Warning'); + _addResultFile(skipAndWarnPackage, _ResultFileType.skips, + contents: 'See warning'); + + final RepositoryPackage warnPackage2 = createFakePackage('package_e', packagesDir); - warnPackage2 - .childFile(_warningFile) - .writeAsStringSync('Warning 1\nWarning 2'); + _addResultFile(warnPackage2, _ResultFileType.warns, + contents: 'Warning 1\nWarning 2'); + createFakePackage('package_f', packagesDir); final TestPackageLoopingCommand command = @@ -615,23 +670,30 @@ void main() { }); test('prints long-form run summary for long-output commands', () async { - final Directory warnPackage1 = + final RepositoryPackage warnPackage1 = createFakePackage('package_a', packagesDir); - warnPackage1 - .childFile(_warningFile) - .writeAsStringSync('Warning 1\nWarning 2'); + _addResultFile(warnPackage1, _ResultFileType.warns, + contents: 'Warning 1\nWarning 2'); + createFakePackage('package_b', packagesDir); - final Directory skipPackage = createFakePackage('package_c', packagesDir); - skipPackage.childFile(_skipFile).writeAsStringSync('For a reason'); - final Directory skipAndWarnPackage = + + final RepositoryPackage skipPackage = + createFakePackage('package_c', packagesDir); + _addResultFile(skipPackage, _ResultFileType.skips, + contents: 'For a reason'); + + final RepositoryPackage skipAndWarnPackage = createFakePackage('package_d', packagesDir); - skipAndWarnPackage.childFile(_warningFile).writeAsStringSync('Warning'); - skipAndWarnPackage.childFile(_skipFile).writeAsStringSync('See warning'); - final Directory warnPackage2 = + _addResultFile(skipAndWarnPackage, _ResultFileType.warns, + contents: 'Warning'); + _addResultFile(skipAndWarnPackage, _ResultFileType.skips, + contents: 'See warning'); + + final RepositoryPackage warnPackage2 = createFakePackage('package_e', packagesDir); - warnPackage2 - .childFile(_warningFile) - .writeAsStringSync('Warning 1\nWarning 2'); + _addResultFile(warnPackage2, _ResultFileType.warns, + contents: 'Warning 1\nWarning 2'); + createFakePackage('package_f', packagesDir); final TestPackageLoopingCommand command = diff --git a/script/tool/test/common/plugin_command_test.dart b/script/tool/test/common/plugin_command_test.dart index 782ea7267ae7..7ed3d239b2ad 100644 --- a/script/tool/test/common/plugin_command_test.dart +++ b/script/tool/test/common/plugin_command_test.dart @@ -62,18 +62,24 @@ void main() { group('plugin iteration', () { test('all plugins from file system', () async { - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); await runCapturingPrint(runner, ['sample']); expect(command.plugins, unorderedEquals([plugin1.path, plugin2.path])); }); test('includes both plugins and packages', () async { - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); - final Directory package3 = createFakePackage('package3', packagesDir); - final Directory package4 = createFakePackage('package4', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); + final RepositoryPackage package3 = + createFakePackage('package3', packagesDir); + final RepositoryPackage package4 = + createFakePackage('package4', packagesDir); await runCapturingPrint(runner, ['sample']); expect( command.plugins, @@ -85,10 +91,25 @@ void main() { ])); }); + test('includes packages without source', () async { + final RepositoryPackage package = + createFakePackage('package', packagesDir); + package.libDirectory.deleteSync(recursive: true); + + await runCapturingPrint(runner, ['sample']); + expect( + command.plugins, + unorderedEquals([ + package.path, + ])); + }); + test('all plugins includes third_party/packages', () async { - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); - final Directory plugin3 = + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin3 = createFakePlugin('plugin3', thirdPartyPackagesDir); await runCapturingPrint(runner, ['sample']); expect(command.plugins, @@ -96,10 +117,12 @@ void main() { }); test('--packages limits packages', () async { - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); createFakePlugin('plugin2', packagesDir); createFakePackage('package3', packagesDir); - final Directory package4 = createFakePackage('package4', packagesDir); + final RepositoryPackage package4 = + createFakePackage('package4', packagesDir); await runCapturingPrint( runner, ['sample', '--packages=plugin1,package4']); expect( @@ -111,10 +134,12 @@ void main() { }); test('--plugins acts as an alias to --packages', () async { - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); createFakePlugin('plugin2', packagesDir); createFakePackage('package3', packagesDir); - final Directory package4 = createFakePackage('package4', packagesDir); + final RepositoryPackage package4 = + createFakePackage('package4', packagesDir); await runCapturingPrint( runner, ['sample', '--plugins=plugin1,package4']); expect( @@ -127,7 +152,8 @@ void main() { test('exclude packages when packages flag is specified', () async { createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); await runCapturingPrint(runner, [ 'sample', '--packages=plugin1,plugin2', @@ -146,7 +172,8 @@ void main() { test('exclude federated plugins when packages flag is specified', () async { createFakePlugin('plugin1', packagesDir.childDirectory('federated')); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); await runCapturingPrint(runner, [ 'sample', '--packages=federated/plugin1,plugin2', @@ -158,7 +185,8 @@ void main() { test('exclude entire federated plugins when packages flag is specified', () async { createFakePlugin('plugin1', packagesDir.childDirectory('federated')); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); await runCapturingPrint(runner, [ 'sample', '--packages=federated/plugin1,plugin2', @@ -189,11 +217,11 @@ packages/plugin1/plugin1/plugin1.dart '''), ]; final Directory pluginGroup = packagesDir.childDirectory('plugin1'); - final Directory appFacingPackage = + final RepositoryPackage appFacingPackage = createFakePlugin('plugin1', pluginGroup); - final Directory platformInterfacePackage = + final RepositoryPackage platformInterfacePackage = createFakePlugin('plugin1_platform_interface', pluginGroup); - final Directory implementationPackage = + final RepositoryPackage implementationPackage = createFakePlugin('plugin1_web', pluginGroup); await runCapturingPrint( @@ -217,7 +245,7 @@ packages/plugin1/plugin1/plugin1.dart '''), ]; final Directory pluginGroup = packagesDir.childDirectory('plugin1'); - final Directory appFacingPackage = + final RepositoryPackage appFacingPackage = createFakePlugin('plugin1', pluginGroup); createFakePlugin('plugin1_platform_interface', pluginGroup); createFakePlugin('plugin1_web', pluginGroup); @@ -239,7 +267,7 @@ packages/plugin1/plugin1/plugin1.dart final Directory pluginGroup = packagesDir.childDirectory('plugin1'); createFakePlugin('plugin1', pluginGroup); - final Directory platformInterfacePackage = + final RepositoryPackage platformInterfacePackage = createFakePlugin('plugin1_platform_interface', pluginGroup); createFakePlugin('plugin1_web', pluginGroup); @@ -265,14 +293,15 @@ packages/plugin1/plugin1/plugin1.dart CommandRunner('common_command', 'subpackage testing'); localRunner.addCommand(localCommand); - final Directory package = createFakePackage('apackage', packagesDir); + final RepositoryPackage package = + createFakePackage('apackage', packagesDir); await runCapturingPrint(localRunner, ['sample']); expect( localCommand.plugins, containsAllInOrder([ package.path, - package.childDirectory('example').path, + getExampleDir(package).path, ])); }); @@ -340,8 +369,10 @@ packages/plugin1/plugin1/plugin1.dart group('test run-on-changed-packages', () { test('all plugins should be tested if there are no changes.', () async { - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); @@ -355,8 +386,10 @@ packages/plugin1/plugin1/plugin1.dart processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: 'AUTHORS'), ]; - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); @@ -371,8 +404,10 @@ packages/plugin1/plugin1/plugin1.dart packages/plugin1/CHANGELOG '''), ]; - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); @@ -387,8 +422,10 @@ packages/plugin1/CHANGELOG packages/plugin1/CHANGELOG '''), ]; - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); @@ -404,8 +441,10 @@ packages/plugin1/CHANGELOG packages/plugin1/CHANGELOG '''), ]; - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); @@ -421,8 +460,10 @@ script/tool_runner.sh packages/plugin1/CHANGELOG '''), ]; - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); @@ -438,8 +479,10 @@ analysis_options.yaml packages/plugin1/CHANGELOG '''), ]; - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); @@ -455,8 +498,10 @@ packages/plugin1/CHANGELOG packages/plugin1/CHANGELOG '''), ]; - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); @@ -468,7 +513,8 @@ packages/plugin1/CHANGELOG processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: 'packages/plugin1/plugin1.dart'), ]; - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); createFakePlugin('plugin2', packagesDir); final List output = await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); @@ -491,7 +537,8 @@ packages/plugin1/plugin1.dart packages/plugin1/ios/plugin1.m '''), ]; - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); createFakePlugin('plugin2', packagesDir); await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); @@ -507,8 +554,10 @@ packages/plugin1/plugin1.dart packages/plugin2/ios/plugin2.m '''), ]; - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); createFakePlugin('plugin3', packagesDir); await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); @@ -527,7 +576,7 @@ packages/plugin1/plugin1_platform_interface/plugin1_platform_interface.dart packages/plugin1/plugin1_web/plugin1_web.dart '''), ]; - final Directory plugin1 = + final RepositoryPackage plugin1 = createFakePlugin('plugin1', packagesDir.childDirectory('plugin1')); createFakePlugin('plugin2', packagesDir); createFakePlugin('plugin3', packagesDir); @@ -545,7 +594,7 @@ packages/plugin1/plugin1_web/plugin1_web.dart packages/plugin1/plugin1/plugin1.dart '''), ]; - final Directory plugin1 = + final RepositoryPackage plugin1 = createFakePlugin('plugin1', packagesDir.childDirectory('plugin1')); createFakePlugin('plugin1_platform_interface', packagesDir.childDirectory('plugin1')); @@ -564,7 +613,7 @@ packages/plugin2/ios/plugin2.m packages/plugin3/plugin3.dart '''), ]; - final Directory plugin1 = + final RepositoryPackage plugin1 = createFakePlugin('plugin1', packagesDir.childDirectory('plugin1')); createFakePlugin('plugin2', packagesDir); createFakePlugin('plugin3', packagesDir); @@ -624,7 +673,8 @@ script/tool_runner.sh processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: 'packages/a_package/lib/a_package.dart'), ]; - final Directory packageA = createFakePackage('a_package', packagesDir); + final RepositoryPackage packageA = + createFakePackage('a_package', packagesDir); createFakePlugin('b_package', packagesDir); final List output = await runCapturingPrint( runner, ['sample', '--run-on-dirty-packages']); @@ -647,8 +697,10 @@ packages/a_package/lib/a_package.dart packages/b_package/lib/src/foo.dart '''), ]; - final Directory packageA = createFakePackage('a_package', packagesDir); - final Directory packageB = createFakePackage('b_package', packagesDir); + final RepositoryPackage packageA = + createFakePackage('a_package', packagesDir); + final RepositoryPackage packageB = + createFakePackage('b_package', packagesDir); createFakePackage('c_package', packagesDir); await runCapturingPrint( runner, ['sample', '--run-on-dirty-packages']); @@ -664,7 +716,8 @@ packages/a_package/lib/a_package.dart packages/b_package/lib/src/foo.dart '''), ]; - final Directory packageA = createFakePackage('a_package', packagesDir); + final RepositoryPackage packageA = + createFakePackage('a_package', packagesDir); createFakePackage('b_package', packagesDir); createFakePackage('c_package', packagesDir); await runCapturingPrint(runner, [ @@ -686,7 +739,8 @@ packages/b_package/lib/src/foo.dart processRunner.mockProcessesForExecutable['git-rev-parse'] = [ MockProcess(stdout: 'a-branch'), ]; - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); createFakePlugin('plugin2', packagesDir); final List output = await runCapturingPrint( @@ -707,8 +761,10 @@ packages/b_package/lib/src/foo.dart processRunner.mockProcessesForExecutable['git-rev-parse'] = [ MockProcess(stdout: 'main'), ]; - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); final List output = await runCapturingPrint( runner, ['sample', '--packages-for-branch']); @@ -729,8 +785,10 @@ packages/b_package/lib/src/foo.dart processRunner.mockProcessesForExecutable['git-rev-parse'] = [ MockProcess(stdout: 'master'), ]; - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); final List output = await runCapturingPrint( runner, ['sample', '--packages-for-branch']); @@ -770,18 +828,19 @@ packages/b_package/lib/src/foo.dart group('sharding', () { test('distributes evenly when evenly divisible', () async { - final List> expectedShards = >[ - [ + final List> expectedShards = + >[ + [ createFakePackage('package1', packagesDir), createFakePackage('package2', packagesDir), createFakePackage('package3', packagesDir), ], - [ + [ createFakePackage('package4', packagesDir), createFakePackage('package5', packagesDir), createFakePackage('package6', packagesDir), ], - [ + [ createFakePackage('package7', packagesDir), createFakePackage('package8', packagesDir), createFakePackage('package9', packagesDir), @@ -807,25 +866,26 @@ packages/b_package/lib/src/foo.dart expect( localCommand.plugins, unorderedEquals(expectedShards[i] - .map((Directory packageDir) => packageDir.path) + .map((RepositoryPackage package) => package.path) .toList())); } }); test('distributes as evenly as possible when not evenly divisible', () async { - final List> expectedShards = >[ - [ + final List> expectedShards = + >[ + [ createFakePackage('package1', packagesDir), createFakePackage('package2', packagesDir), createFakePackage('package3', packagesDir), ], - [ + [ createFakePackage('package4', packagesDir), createFakePackage('package5', packagesDir), createFakePackage('package6', packagesDir), ], - [ + [ createFakePackage('package7', packagesDir), createFakePackage('package8', packagesDir), ], @@ -850,7 +910,7 @@ packages/b_package/lib/src/foo.dart expect( localCommand.plugins, unorderedEquals(expectedShards[i] - .map((Directory packageDir) => packageDir.path) + .map((RepositoryPackage package) => package.path) .toList())); } }); @@ -864,18 +924,19 @@ packages/b_package/lib/src/foo.dart // excluding some plugins from the later step shouldn't change what's tested // in each shard, as it may no longer align with what was built. test('counts excluded plugins when sharding', () async { - final List> expectedShards = >[ - [ + final List> expectedShards = + >[ + [ createFakePackage('package1', packagesDir), createFakePackage('package2', packagesDir), createFakePackage('package3', packagesDir), ], - [ + [ createFakePackage('package4', packagesDir), createFakePackage('package5', packagesDir), createFakePackage('package6', packagesDir), ], - [ + [ createFakePackage('package7', packagesDir), ], ]; @@ -903,7 +964,7 @@ packages/b_package/lib/src/foo.dart expect( localCommand.plugins, unorderedEquals(expectedShards[i] - .map((Directory packageDir) => packageDir.path) + .map((RepositoryPackage package) => package.path) .toList())); } }); diff --git a/script/tool/test/common/plugin_utils_test.dart b/script/tool/test/common/plugin_utils_test.dart index af5cb7dfe4c6..9c5ddc3f85b9 100644 --- a/script/tool/test/common/plugin_utils_test.dart +++ b/script/tool/test/common/plugin_utils_test.dart @@ -6,7 +6,6 @@ import 'package:file/file.dart'; import 'package:file/memory.dart'; import 'package:flutter_plugin_tools/src/common/core.dart'; import 'package:flutter_plugin_tools/src/common/plugin_utils.dart'; -import 'package:flutter_plugin_tools/src/common/repository_package.dart'; import 'package:test/test.dart'; import '../util.dart'; @@ -22,8 +21,7 @@ void main() { group('pluginSupportsPlatform', () { test('no platforms', () async { - final RepositoryPackage plugin = - RepositoryPackage(createFakePlugin('plugin', packagesDir)); + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir); expect(pluginSupportsPlatform(platformAndroid, plugin), isFalse); expect(pluginSupportsPlatform(platformIOS, plugin), isFalse); @@ -34,8 +32,7 @@ void main() { }); test('all platforms', () async { - final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( - 'plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline), platformIOS: const PlatformDetails(PlatformSupport.inline), @@ -43,7 +40,7 @@ void main() { platformMacOS: const PlatformDetails(PlatformSupport.inline), platformWeb: const PlatformDetails(PlatformSupport.inline), platformWindows: const PlatformDetails(PlatformSupport.inline), - })); + }); expect(pluginSupportsPlatform(platformAndroid, plugin), isTrue); expect(pluginSupportsPlatform(platformIOS, plugin), isTrue); @@ -54,13 +51,12 @@ void main() { }); test('some platforms', () async { - final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( - 'plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline), platformLinux: const PlatformDetails(PlatformSupport.inline), platformWeb: const PlatformDetails(PlatformSupport.inline), - })); + }); expect(pluginSupportsPlatform(platformAndroid, plugin), isTrue); expect(pluginSupportsPlatform(platformIOS, plugin), isFalse); @@ -71,8 +67,7 @@ void main() { }); test('inline plugins are only detected as inline', () async { - final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( - 'plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline), platformIOS: const PlatformDetails(PlatformSupport.inline), @@ -80,7 +75,7 @@ void main() { platformMacOS: const PlatformDetails(PlatformSupport.inline), platformWeb: const PlatformDetails(PlatformSupport.inline), platformWindows: const PlatformDetails(PlatformSupport.inline), - })); + }); expect( pluginSupportsPlatform(platformAndroid, plugin, @@ -133,8 +128,7 @@ void main() { }); test('federated plugins are only detected as federated', () async { - final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( - 'plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.federated), platformIOS: const PlatformDetails(PlatformSupport.federated), @@ -142,7 +136,7 @@ void main() { platformMacOS: const PlatformDetails(PlatformSupport.federated), platformWeb: const PlatformDetails(PlatformSupport.federated), platformWindows: const PlatformDetails(PlatformSupport.federated), - })); + }); expect( pluginSupportsPlatform(platformAndroid, plugin, @@ -197,19 +191,19 @@ void main() { group('pluginHasNativeCodeForPlatform', () { test('returns false for web', () async { - final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, platformSupport: { platformWeb: const PlatformDetails(PlatformSupport.inline), }, - )); + ); expect(pluginHasNativeCodeForPlatform(platformWeb, plugin), isFalse); }); test('returns false for a native-only plugin', () async { - final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, platformSupport: { @@ -217,7 +211,7 @@ void main() { platformMacOS: const PlatformDetails(PlatformSupport.inline), platformWindows: const PlatformDetails(PlatformSupport.inline), }, - )); + ); expect(pluginHasNativeCodeForPlatform(platformLinux, plugin), isTrue); expect(pluginHasNativeCodeForPlatform(platformMacOS, plugin), isTrue); @@ -225,7 +219,7 @@ void main() { }); test('returns true for a native+Dart plugin', () async { - final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, platformSupport: { @@ -236,7 +230,7 @@ void main() { platformWindows: const PlatformDetails(PlatformSupport.inline, hasNativeCode: true, hasDartCode: true), }, - )); + ); expect(pluginHasNativeCodeForPlatform(platformLinux, plugin), isTrue); expect(pluginHasNativeCodeForPlatform(platformMacOS, plugin), isTrue); @@ -244,7 +238,7 @@ void main() { }); test('returns false for a Dart-only plugin', () async { - final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, platformSupport: { @@ -255,7 +249,7 @@ void main() { platformWindows: const PlatformDetails(PlatformSupport.inline, hasNativeCode: false, hasDartCode: true), }, - )); + ); expect(pluginHasNativeCodeForPlatform(platformLinux, plugin), isFalse); expect(pluginHasNativeCodeForPlatform(platformMacOS, plugin), isFalse); diff --git a/script/tool/test/common/repository_package_test.dart b/script/tool/test/common/repository_package_test.dart index 2d0e11475c36..dadfc8832997 100644 --- a/script/tool/test/common/repository_package_test.dart +++ b/script/tool/test/common/repository_package_test.dart @@ -4,7 +4,6 @@ import 'package:file/file.dart'; import 'package:file/memory.dart'; -import 'package:flutter_plugin_tools/src/common/repository_package.dart'; import 'package:test/test.dart'; import '../util.dart'; @@ -97,107 +96,109 @@ void main() { group('getExamples', () { test('handles a single Flutter example', () async { - final Directory plugin = createFakePlugin('a_plugin', packagesDir); + final RepositoryPackage plugin = + createFakePlugin('a_plugin', packagesDir); - final List examples = - RepositoryPackage(plugin).getExamples().toList(); + final List examples = plugin.getExamples().toList(); expect(examples.length, 1); - expect(examples[0].path, plugin.childDirectory('example').path); + expect(examples[0].path, getExampleDir(plugin).path); }); test('handles multiple Flutter examples', () async { - final Directory plugin = createFakePlugin('a_plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('a_plugin', packagesDir, examples: ['example1', 'example2']); - final List examples = - RepositoryPackage(plugin).getExamples().toList(); + final List examples = plugin.getExamples().toList(); expect(examples.length, 2); expect(examples[0].path, - plugin.childDirectory('example').childDirectory('example1').path); + getExampleDir(plugin).childDirectory('example1').path); expect(examples[1].path, - plugin.childDirectory('example').childDirectory('example2').path); + getExampleDir(plugin).childDirectory('example2').path); }); test('handles a single non-Flutter example', () async { - final Directory package = createFakePackage('a_package', packagesDir); + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); - final List examples = - RepositoryPackage(package).getExamples().toList(); + final List examples = package.getExamples().toList(); expect(examples.length, 1); - expect(examples[0].path, package.childDirectory('example').path); + expect(examples[0].path, getExampleDir(package).path); }); test('handles multiple non-Flutter examples', () async { - final Directory package = createFakePackage('a_package', packagesDir, + final RepositoryPackage package = createFakePackage( + 'a_package', packagesDir, examples: ['example1', 'example2']); - final List examples = - RepositoryPackage(package).getExamples().toList(); + final List examples = package.getExamples().toList(); expect(examples.length, 2); expect(examples[0].path, - package.childDirectory('example').childDirectory('example1').path); + getExampleDir(package).childDirectory('example1').path); expect(examples[1].path, - package.childDirectory('example').childDirectory('example2').path); + getExampleDir(package).childDirectory('example2').path); }); }); group('federated plugin queries', () { test('all return false for a simple plugin', () { - final Directory plugin = createFakePlugin('a_plugin', packagesDir); - expect(RepositoryPackage(plugin).isFederated, false); - expect(RepositoryPackage(plugin).isAppFacing, false); - expect(RepositoryPackage(plugin).isPlatformInterface, false); - expect(RepositoryPackage(plugin).isFederated, false); + final RepositoryPackage plugin = + createFakePlugin('a_plugin', packagesDir); + expect(plugin.isFederated, false); + expect(plugin.isAppFacing, false); + expect(plugin.isPlatformInterface, false); + expect(plugin.isFederated, false); }); test('handle app-facing packages', () { - final Directory plugin = + final RepositoryPackage plugin = createFakePlugin('a_plugin', packagesDir.childDirectory('a_plugin')); - expect(RepositoryPackage(plugin).isFederated, true); - expect(RepositoryPackage(plugin).isAppFacing, true); - expect(RepositoryPackage(plugin).isPlatformInterface, false); - expect(RepositoryPackage(plugin).isPlatformImplementation, false); + expect(plugin.isFederated, true); + expect(plugin.isAppFacing, true); + expect(plugin.isPlatformInterface, false); + expect(plugin.isPlatformImplementation, false); }); test('handle platform interface packages', () { - final Directory plugin = createFakePlugin('a_plugin_platform_interface', + final RepositoryPackage plugin = createFakePlugin( + 'a_plugin_platform_interface', packagesDir.childDirectory('a_plugin')); - expect(RepositoryPackage(plugin).isFederated, true); - expect(RepositoryPackage(plugin).isAppFacing, false); - expect(RepositoryPackage(plugin).isPlatformInterface, true); - expect(RepositoryPackage(plugin).isPlatformImplementation, false); + expect(plugin.isFederated, true); + expect(plugin.isAppFacing, false); + expect(plugin.isPlatformInterface, true); + expect(plugin.isPlatformImplementation, false); }); test('handle platform implementation packages', () { // A platform interface can end with anything, not just one of the known // platform names, because of cases like webview_flutter_wkwebview. - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'a_plugin_foo', packagesDir.childDirectory('a_plugin')); - expect(RepositoryPackage(plugin).isFederated, true); - expect(RepositoryPackage(plugin).isAppFacing, false); - expect(RepositoryPackage(plugin).isPlatformInterface, false); - expect(RepositoryPackage(plugin).isPlatformImplementation, true); + expect(plugin.isFederated, true); + expect(plugin.isAppFacing, false); + expect(plugin.isPlatformInterface, false); + expect(plugin.isPlatformImplementation, true); }); }); group('pubspec', () { test('file', () async { - final Directory plugin = createFakePlugin('a_plugin', packagesDir); + final RepositoryPackage plugin = + createFakePlugin('a_plugin', packagesDir); - final File pubspecFile = RepositoryPackage(plugin).pubspecFile; + final File pubspecFile = plugin.pubspecFile; - expect(pubspecFile.path, plugin.childFile('pubspec.yaml').path); + expect(pubspecFile.path, plugin.directory.childFile('pubspec.yaml').path); }); test('parsing', () async { - final Directory plugin = createFakePlugin('a_plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('a_plugin', packagesDir, examples: ['example1', 'example2']); - final Pubspec pubspec = RepositoryPackage(plugin).parsePubspec(); + final Pubspec pubspec = plugin.parsePubspec(); expect(pubspec.name, 'a_plugin'); }); @@ -205,15 +206,15 @@ void main() { group('requiresFlutter', () { test('returns true for Flutter package', () async { - final Directory package = + final RepositoryPackage package = createFakePackage('a_package', packagesDir, isFlutter: true); - expect(RepositoryPackage(package).requiresFlutter(), true); + expect(package.requiresFlutter(), true); }); test('returns false for non-Flutter package', () async { - final Directory package = + final RepositoryPackage package = createFakePackage('a_package', packagesDir, isFlutter: false); - expect(RepositoryPackage(package).requiresFlutter(), false); + expect(package.requiresFlutter(), false); }); }); } diff --git a/script/tool/test/create_all_plugins_app_command_test.dart b/script/tool/test/create_all_plugins_app_command_test.dart index 9e2ee29326d7..830dd59a8d42 100644 --- a/script/tool/test/create_all_plugins_app_command_test.dart +++ b/script/tool/test/create_all_plugins_app_command_test.dart @@ -7,7 +7,6 @@ import 'dart:io' as io; import 'package:args/command_runner.dart'; import 'package:file/file.dart'; import 'package:file/local.dart'; -import 'package:flutter_plugin_tools/src/common/repository_package.dart'; import 'package:flutter_plugin_tools/src/create_all_plugins_app_command.dart'; import 'package:platform/platform.dart'; import 'package:test/test.dart'; @@ -49,8 +48,7 @@ void main() { createFakePlugin('pluginc', packagesDir); await runCapturingPrint(runner, ['all-plugins-app']); - final List pubspec = - command.appDirectory.childFile('pubspec.yaml').readAsLinesSync(); + final List pubspec = command.app.pubspecFile.readAsLinesSync(); expect( pubspec, @@ -67,8 +65,7 @@ void main() { createFakePlugin('pluginc', packagesDir); await runCapturingPrint(runner, ['all-plugins-app']); - final List pubspec = - command.appDirectory.childFile('pubspec.yaml').readAsLinesSync(); + final List pubspec = command.app.pubspecFile.readAsLinesSync(); expect( pubspec, @@ -99,8 +96,7 @@ void main() { createFakePlugin('plugina', packagesDir); await runCapturingPrint(runner, ['all-plugins-app']); - final Pubspec generatedPubspec = - RepositoryPackage(command.appDirectory).parsePubspec(); + final Pubspec generatedPubspec = command.app.parsePubspec(); const String dartSdkKey = 'sdk'; expect(generatedPubspec.environment?[dartSdkKey], @@ -115,8 +111,8 @@ void main() { await runCapturingPrint(runner, ['all-plugins-app', '--output-dir=${customOutputDir.path}']); - expect(command.appDirectory.path, - customOutputDir.childDirectory('all_plugins').path); + expect( + command.app.path, customOutputDir.childDirectory('all_plugins').path); }); test('logs exclusions', () async { diff --git a/script/tool/test/custom_test_command_test.dart b/script/tool/test/custom_test_command_test.dart index bc30d9a1d2e3..54a1acf8b82b 100644 --- a/script/tool/test/custom_test_command_test.dart +++ b/script/tool/test/custom_test_command_test.dart @@ -39,7 +39,7 @@ void main() { }); test('runs both new and legacy when both are present', () async { - final Directory package = + final RepositoryPackage package = createFakePlugin('a_package', packagesDir, extraFiles: [ 'tool/run_tests.dart', 'run_tests.sh', @@ -51,7 +51,7 @@ void main() { expect( processRunner.recordedCalls, containsAll([ - ProcessCall(package.childFile('run_tests.sh').path, + ProcessCall(package.directory.childFile('run_tests.sh').path, const [], package.path), ProcessCall('dart', const ['run', 'tool/run_tests.dart'], package.path), @@ -65,7 +65,8 @@ void main() { }); test('runs when only new is present', () async { - final Directory package = createFakePlugin('a_package', packagesDir, + final RepositoryPackage package = createFakePlugin( + 'a_package', packagesDir, extraFiles: ['tool/run_tests.dart']); final List output = @@ -86,7 +87,8 @@ void main() { }); test('runs pub get before running Dart test script', () async { - final Directory package = createFakePlugin('a_package', packagesDir, + final RepositoryPackage package = createFakePlugin( + 'a_package', packagesDir, extraFiles: ['tool/run_tests.dart']); await runCapturingPrint(runner, ['custom-test']); @@ -101,7 +103,8 @@ void main() { }); test('runs when only legacy is present', () async { - final Directory package = createFakePlugin('a_package', packagesDir, + final RepositoryPackage package = createFakePlugin( + 'a_package', packagesDir, extraFiles: ['run_tests.sh']); final List output = @@ -110,7 +113,7 @@ void main() { expect( processRunner.recordedCalls, containsAll([ - ProcessCall(package.childFile('run_tests.sh').path, + ProcessCall(package.directory.childFile('run_tests.sh').path, const [], package.path), ])); @@ -189,14 +192,14 @@ void main() { }); test('fails if legacy fails', () async { - final Directory package = + final RepositoryPackage package = createFakePlugin('a_package', packagesDir, extraFiles: [ 'tool/run_tests.dart', 'run_tests.sh', ]); processRunner.mockProcessesForExecutable[ - package.childFile('run_tests.sh').path] = [ + package.directory.childFile('run_tests.sh').path] = [ MockProcess(exitCode: 1), ]; @@ -234,7 +237,7 @@ void main() { }); test('runs new and skips old when both are present', () async { - final Directory package = + final RepositoryPackage package = createFakePlugin('a_package', packagesDir, extraFiles: [ 'tool/run_tests.dart', 'run_tests.sh', @@ -258,7 +261,8 @@ void main() { }); test('runs when only new is present', () async { - final Directory package = createFakePlugin('a_package', packagesDir, + final RepositoryPackage package = createFakePlugin( + 'a_package', packagesDir, extraFiles: ['tool/run_tests.dart']); final List output = diff --git a/script/tool/test/drive_examples_command_test.dart b/script/tool/test/drive_examples_command_test.dart index 214efb420227..23318f7cd604 100644 --- a/script/tool/test/drive_examples_command_test.dart +++ b/script/tool/test/drive_examples_command_test.dart @@ -188,7 +188,7 @@ void main() { }); test('driving under folder "test_driver"', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, extraFiles: [ @@ -203,8 +203,7 @@ void main() { }, ); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); setMockFlutterDevicesOutput(); final List output = @@ -311,7 +310,7 @@ void main() { test( 'driving under folder "test_driver" when targets are under "integration_test"', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, extraFiles: [ @@ -328,8 +327,7 @@ void main() { }, ); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); setMockFlutterDevicesOutput(); final List output = @@ -401,7 +399,7 @@ void main() { }); test('driving on a Linux plugin', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, extraFiles: [ @@ -414,8 +412,7 @@ void main() { }, ); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, [ 'drive-examples', @@ -474,7 +471,7 @@ void main() { }); test('driving on a macOS plugin', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, extraFiles: [ @@ -487,8 +484,7 @@ void main() { }, ); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, [ 'drive-examples', @@ -546,7 +542,7 @@ void main() { }); test('driving a web plugin', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, extraFiles: [ @@ -559,8 +555,7 @@ void main() { }, ); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, [ 'drive-examples', @@ -596,7 +591,7 @@ void main() { }); test('driving a web plugin with CHROME_EXECUTABLE', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, extraFiles: [ @@ -609,8 +604,7 @@ void main() { }, ); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); mockPlatform.environment['CHROME_EXECUTABLE'] = '/path/to/chrome'; @@ -674,7 +668,7 @@ void main() { }); test('driving on a Windows plugin', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, extraFiles: [ @@ -687,8 +681,7 @@ void main() { }, ); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, [ 'drive-examples', @@ -722,7 +715,7 @@ void main() { }); test('driving on an Android plugin', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, extraFiles: [ @@ -735,8 +728,7 @@ void main() { }, ); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); setMockFlutterDevicesOutput(); final List output = await runCapturingPrint(runner, [ @@ -861,7 +853,7 @@ void main() { }); test('enable-experiment flag', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, extraFiles: [ @@ -876,8 +868,7 @@ void main() { }, ); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); setMockFlutterDevicesOutput(); await runCapturingPrint(runner, [ @@ -1006,7 +997,7 @@ void main() { }); test('reports test failures', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, extraFiles: [ @@ -1048,8 +1039,7 @@ void main() { ]), ); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); expect( processRunner.recordedCalls, orderedEquals([ @@ -1082,13 +1072,13 @@ void main() { group('packages', () { test('can be driven', () async { - final Directory package = + final RepositoryPackage package = createFakePackage('a_package', packagesDir, extraFiles: [ 'example/integration_test/foo_test.dart', 'example/test_driver/integration_test.dart', 'example/web/index.html', ]); - final Directory exampleDirectory = package.childDirectory('example'); + final Directory exampleDirectory = getExampleDir(package); final List output = await runCapturingPrint(runner, [ 'drive-examples', @@ -1150,7 +1140,8 @@ void main() { }); test('drive only supported examples if there is more than one', () async { - final Directory package = createFakePackage('a_package', packagesDir, + final RepositoryPackage package = createFakePackage( + 'a_package', packagesDir, isFlutter: true, examples: [ 'with_web', @@ -1164,7 +1155,7 @@ void main() { 'example/without_web/test_driver/integration_test.dart', ]); final Directory supportedExampleDirectory = - package.childDirectory('example').childDirectory('with_web'); + getExampleDir(package).childDirectory('with_web'); final List output = await runCapturingPrint(runner, [ 'drive-examples', diff --git a/script/tool/test/federation_safety_check_command_test.dart b/script/tool/test/federation_safety_check_command_test.dart index 126aa8b41c83..015a0eb634d9 100644 --- a/script/tool/test/federation_safety_check_command_test.dart +++ b/script/tool/test/federation_safety_check_command_test.dart @@ -8,7 +8,6 @@ import 'package:args/command_runner.dart'; import 'package:file/file.dart'; import 'package:file/memory.dart'; import 'package:flutter_plugin_tools/src/common/core.dart'; -import 'package:flutter_plugin_tools/src/common/repository_package.dart'; import 'package:flutter_plugin_tools/src/federation_safety_check_command.dart'; import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; @@ -55,10 +54,10 @@ void main() { }); test('skips non-plugin packages', () async { - final Directory package = createFakePackage('foo', packagesDir); + final RepositoryPackage package = createFakePackage('foo', packagesDir); final String changedFileOutput = [ - package.childDirectory('lib').childFile('foo.dart'), + package.libDirectory.childFile('foo.dart'), ].map((File file) => file.path).join('\n'); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: changedFileOutput), @@ -78,10 +77,10 @@ void main() { }); test('skips unfederated plugins', () async { - final Directory package = createFakePlugin('foo', packagesDir); + final RepositoryPackage package = createFakePlugin('foo', packagesDir); final String changedFileOutput = [ - package.childDirectory('lib').childFile('foo.dart'), + package.libDirectory.childFile('foo.dart'), ].map((File file) => file.path).join('\n'); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: changedFileOutput), @@ -102,11 +101,11 @@ void main() { test('skips interface packages', () async { final Directory pluginGroupDir = packagesDir.childDirectory('foo'); - final Directory platformInterface = + final RepositoryPackage platformInterface = createFakePlugin('foo_platform_interface', pluginGroupDir); final String changedFileOutput = [ - platformInterface.childDirectory('lib').childFile('foo.dart'), + platformInterface.libDirectory.childFile('foo.dart'), ].map((File file) => file.path).join('\n'); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: changedFileOutput), @@ -127,15 +126,15 @@ void main() { test('allows changes to just an interface package', () async { final Directory pluginGroupDir = packagesDir.childDirectory('foo'); - final Directory platformInterface = + final RepositoryPackage platformInterface = createFakePlugin('foo_platform_interface', pluginGroupDir); createFakePlugin('foo', pluginGroupDir); createFakePlugin('foo_ios', pluginGroupDir); createFakePlugin('foo_android', pluginGroupDir); final String changedFileOutput = [ - platformInterface.childDirectory('lib').childFile('foo.dart'), - platformInterface.childFile('pubspec.yaml'), + platformInterface.libDirectory.childFile('foo.dart'), + platformInterface.pubspecFile, ].map((File file) => file.path).join('\n'); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: changedFileOutput), @@ -168,14 +167,14 @@ void main() { test('allows changes to multiple non-interface packages', () async { final Directory pluginGroupDir = packagesDir.childDirectory('foo'); - final Directory appFacing = createFakePlugin('foo', pluginGroupDir); - final Directory implementation = + final RepositoryPackage appFacing = createFakePlugin('foo', pluginGroupDir); + final RepositoryPackage implementation = createFakePlugin('foo_bar', pluginGroupDir); createFakePlugin('foo_platform_interface', pluginGroupDir); final String changedFileOutput = [ - appFacing.childFile('foo.dart'), - implementation.childFile('foo.dart'), + appFacing.libDirectory.childFile('foo.dart'), + implementation.libDirectory.childFile('foo.dart'), ].map((File file) => file.path).join('\n'); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: changedFileOutput), @@ -199,17 +198,17 @@ void main() { 'fails on changes to interface and non-interface packages in the same plugin', () async { final Directory pluginGroupDir = packagesDir.childDirectory('foo'); - final Directory appFacing = createFakePlugin('foo', pluginGroupDir); - final Directory implementation = + final RepositoryPackage appFacing = createFakePlugin('foo', pluginGroupDir); + final RepositoryPackage implementation = createFakePlugin('foo_bar', pluginGroupDir); - final Directory platformInterface = + final RepositoryPackage platformInterface = createFakePlugin('foo_platform_interface', pluginGroupDir); final String changedFileOutput = [ - appFacing.childFile('foo.dart'), - implementation.childFile('foo.dart'), - platformInterface.childFile('pubspec.yaml'), - platformInterface.childDirectory('lib').childFile('foo.dart'), + appFacing.libDirectory.childFile('foo.dart'), + implementation.libDirectory.childFile('foo.dart'), + platformInterface.pubspecFile, + platformInterface.libDirectory.childFile('foo.dart'), ].map((File file) => file.path).join('\n'); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: changedFileOutput), @@ -244,17 +243,17 @@ void main() { test('ignores test-only changes to interface packages', () async { final Directory pluginGroupDir = packagesDir.childDirectory('foo'); - final Directory appFacing = createFakePlugin('foo', pluginGroupDir); - final Directory implementation = + final RepositoryPackage appFacing = createFakePlugin('foo', pluginGroupDir); + final RepositoryPackage implementation = createFakePlugin('foo_bar', pluginGroupDir); - final Directory platformInterface = + final RepositoryPackage platformInterface = createFakePlugin('foo_platform_interface', pluginGroupDir); final String changedFileOutput = [ - appFacing.childFile('foo.dart'), - implementation.childFile('foo.dart'), - platformInterface.childFile('pubspec.yaml'), - platformInterface.childDirectory('test').childFile('foo.dart'), + appFacing.libDirectory.childFile('foo.dart'), + implementation.libDirectory.childFile('foo.dart'), + platformInterface.pubspecFile, + platformInterface.testDirectory.childFile('foo.dart'), ].map((File file) => file.path).join('\n'); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: changedFileOutput), @@ -276,27 +275,24 @@ void main() { test('ignores unpublished changes to interface packages', () async { final Directory pluginGroupDir = packagesDir.childDirectory('foo'); - final Directory appFacing = createFakePlugin('foo', pluginGroupDir); - final Directory implementation = + final RepositoryPackage appFacing = createFakePlugin('foo', pluginGroupDir); + final RepositoryPackage implementation = createFakePlugin('foo_bar', pluginGroupDir); - final Directory platformInterface = + final RepositoryPackage platformInterface = createFakePlugin('foo_platform_interface', pluginGroupDir); final String changedFileOutput = [ - appFacing.childFile('foo.dart'), - implementation.childFile('foo.dart'), - platformInterface.childFile('pubspec.yaml'), - platformInterface.childDirectory('lib').childFile('foo.dart'), + appFacing.libDirectory.childFile('foo.dart'), + implementation.libDirectory.childFile('foo.dart'), + platformInterface.pubspecFile, + platformInterface.libDirectory.childFile('foo.dart'), ].map((File file) => file.path).join('\n'); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: changedFileOutput), ]; // Simulate no change to the version in the interface's pubspec.yaml. processRunner.mockProcessesForExecutable['git-show'] = [ - MockProcess( - stdout: RepositoryPackage(platformInterface) - .pubspecFile - .readAsStringSync()), + MockProcess(stdout: platformInterface.pubspecFile.readAsStringSync()), ]; final List output = @@ -315,22 +311,22 @@ void main() { test('allows things that look like mass changes, with warning', () async { final Directory pluginGroupDir = packagesDir.childDirectory('foo'); - final Directory appFacing = createFakePlugin('foo', pluginGroupDir); - final Directory implementation = + final RepositoryPackage appFacing = createFakePlugin('foo', pluginGroupDir); + final RepositoryPackage implementation = createFakePlugin('foo_bar', pluginGroupDir); - final Directory platformInterface = + final RepositoryPackage platformInterface = createFakePlugin('foo_platform_interface', pluginGroupDir); - final Directory otherPlugin1 = createFakePlugin('bar', packagesDir); - final Directory otherPlugin2 = createFakePlugin('baz', packagesDir); + final RepositoryPackage otherPlugin1 = createFakePlugin('bar', packagesDir); + final RepositoryPackage otherPlugin2 = createFakePlugin('baz', packagesDir); final String changedFileOutput = [ - appFacing.childFile('foo.dart'), - implementation.childFile('foo.dart'), - platformInterface.childFile('pubspec.yaml'), - platformInterface.childDirectory('lib').childFile('foo.dart'), - otherPlugin1.childFile('bar.dart'), - otherPlugin2.childFile('baz.dart'), + appFacing.libDirectory.childFile('foo.dart'), + implementation.libDirectory.childFile('foo.dart'), + platformInterface.pubspecFile, + platformInterface.libDirectory.childFile('foo.dart'), + otherPlugin1.libDirectory.childFile('bar.dart'), + otherPlugin2.libDirectory.childFile('baz.dart'), ].map((File file) => file.path).join('\n'); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: changedFileOutput), @@ -355,11 +351,11 @@ void main() { test('handles top-level files that match federated package heuristics', () async { - final Directory plugin = createFakePlugin('foo', packagesDir); + final RepositoryPackage plugin = createFakePlugin('foo', packagesDir); final String changedFileOutput = [ // This should be picked up as a change to 'foo', and not crash. - plugin.childFile('foo_bar.baz'), + plugin.directory.childFile('foo_bar.baz'), ].map((File file) => file.path).join('\n'); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: changedFileOutput), diff --git a/script/tool/test/firebase_test_lab_command_test.dart b/script/tool/test/firebase_test_lab_command_test.dart index db658e19b28e..a41409394728 100644 --- a/script/tool/test/firebase_test_lab_command_test.dart +++ b/script/tool/test/firebase_test_lab_command_test.dart @@ -40,9 +40,10 @@ void main() { runner.addCommand(command); }); - void _writeJavaTestFile(Directory pluginDir, String relativeFilePath, + void _writeJavaTestFile(RepositoryPackage plugin, String relativeFilePath, {String runnerClass = 'FlutterTestRunner'}) { - childFileWithSubcomponents(pluginDir, p.posix.split(relativeFilePath)) + childFileWithSubcomponents( + plugin.directory, p.posix.split(relativeFilePath)) .writeAsStringSync(''' @DartIntegrationTest @RunWith($runnerClass.class) @@ -60,13 +61,13 @@ public class MainActivityTest { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/integration_test/foo_test.dart', 'example/android/gradlew', javaTestFileRelativePath, ]); - _writeJavaTestFile(pluginDir, javaTestFileRelativePath); + _writeJavaTestFile(plugin, javaTestFileRelativePath); Error? commandError; final List output = await runCapturingPrint( @@ -90,13 +91,13 @@ public class MainActivityTest { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/integration_test/foo_test.dart', 'example/android/gradlew', javaTestFileRelativePath, ]); - _writeJavaTestFile(pluginDir, javaTestFileRelativePath); + _writeJavaTestFile(plugin, javaTestFileRelativePath); final List output = await runCapturingPrint(runner, ['firebase-test-lab']); @@ -112,22 +113,22 @@ public class MainActivityTest { test('only runs gcloud configuration once', () async { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; - final Directory plugin1Dir = + final RepositoryPackage plugin1 = createFakePlugin('plugin1', packagesDir, extraFiles: [ 'test/plugin_test.dart', 'example/integration_test/foo_test.dart', 'example/android/gradlew', javaTestFileRelativePath, ]); - _writeJavaTestFile(plugin1Dir, javaTestFileRelativePath); - final Directory plugin2Dir = + _writeJavaTestFile(plugin1, javaTestFileRelativePath); + final RepositoryPackage plugin2 = createFakePlugin('plugin2', packagesDir, extraFiles: [ 'test/plugin_test.dart', 'example/integration_test/bar_test.dart', 'example/android/gradlew', javaTestFileRelativePath, ]); - _writeJavaTestFile(plugin2Dir, javaTestFileRelativePath); + _writeJavaTestFile(plugin2, javaTestFileRelativePath); final List output = await runCapturingPrint(runner, [ 'firebase-test-lab', @@ -197,7 +198,7 @@ public class MainActivityTest { test('runs integration tests', () async { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'test/plugin_test.dart', 'example/integration_test/bar_test.dart', @@ -206,7 +207,7 @@ public class MainActivityTest { 'example/android/gradlew', javaTestFileRelativePath, ]); - _writeJavaTestFile(pluginDir, javaTestFileRelativePath); + _writeJavaTestFile(plugin, javaTestFileRelativePath); final List output = await runCapturingPrint(runner, [ 'firebase-test-lab', @@ -275,7 +276,7 @@ public class MainActivityTest { const List examples = ['example1', 'example2']; const String javaTestFileExampleRelativePath = 'android/app/src/androidTest/MainActivityTest.java'; - final Directory pluginDir = createFakePlugin('plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, examples: examples, extraFiles: [ for (final String example in examples) ...[ @@ -286,7 +287,7 @@ public class MainActivityTest { ]); for (final String example in examples) { _writeJavaTestFile( - pluginDir, 'example/$example/$javaTestFileExampleRelativePath'); + plugin, 'example/$example/$javaTestFileExampleRelativePath'); } final List output = await runCapturingPrint(runner, [ @@ -339,14 +340,14 @@ public class MainActivityTest { test('fails if a test fails twice', () async { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/integration_test/bar_test.dart', 'example/integration_test/foo_test.dart', 'example/android/gradlew', javaTestFileRelativePath, ]); - _writeJavaTestFile(pluginDir, javaTestFileRelativePath); + _writeJavaTestFile(plugin, javaTestFileRelativePath); processRunner.mockProcessesForExecutable['gcloud'] = [ MockProcess(), // auth @@ -385,14 +386,14 @@ public class MainActivityTest { () async { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/integration_test/bar_test.dart', 'example/integration_test/foo_test.dart', 'example/android/gradlew', javaTestFileRelativePath, ]); - _writeJavaTestFile(pluginDir, javaTestFileRelativePath); + _writeJavaTestFile(plugin, javaTestFileRelativePath); processRunner.mockProcessesForExecutable['gcloud'] = [ MockProcess(), // auth @@ -454,12 +455,12 @@ public class MainActivityTest { test('fails for packages with no integration test files', () async { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/android/gradlew', javaTestFileRelativePath, ]); - _writeJavaTestFile(pluginDir, javaTestFileRelativePath); + _writeJavaTestFile(plugin, javaTestFileRelativePath); Error? commandError; final List output = await runCapturingPrint( @@ -490,7 +491,7 @@ public class MainActivityTest { test('fails for packages with no integration_test runner', () async { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'test/plugin_test.dart', 'example/integration_test/bar_test.dart', @@ -500,7 +501,7 @@ public class MainActivityTest { javaTestFileRelativePath, ]); // Use the wrong @RunWith annotation. - _writeJavaTestFile(pluginDir, javaTestFileRelativePath, + _writeJavaTestFile(plugin, javaTestFileRelativePath, runnerClass: 'AndroidJUnit4.class'); Error? commandError; @@ -559,12 +560,12 @@ public class MainActivityTest { test('builds if gradlew is missing', () async { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/integration_test/foo_test.dart', javaTestFileRelativePath, ]); - _writeJavaTestFile(pluginDir, javaTestFileRelativePath); + _writeJavaTestFile(plugin, javaTestFileRelativePath); final List output = await runCapturingPrint(runner, [ 'firebase-test-lab', @@ -622,12 +623,12 @@ public class MainActivityTest { test('fails if building to generate gradlew fails', () async { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/integration_test/foo_test.dart', javaTestFileRelativePath, ]); - _writeJavaTestFile(pluginDir, javaTestFileRelativePath); + _writeJavaTestFile(plugin, javaTestFileRelativePath); processRunner.mockProcessesForExecutable['flutter'] = [ MockProcess(exitCode: 1) // flutter build @@ -657,16 +658,17 @@ public class MainActivityTest { test('fails if assembleAndroidTest fails', () async { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/integration_test/foo_test.dart', javaTestFileRelativePath, ]); - _writeJavaTestFile(pluginDir, javaTestFileRelativePath); + _writeJavaTestFile(plugin, javaTestFileRelativePath); - final String gradlewPath = pluginDir - .childDirectory('example') - .childDirectory('android') + final String gradlewPath = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android) .childFile('gradlew') .path; processRunner.mockProcessesForExecutable[gradlewPath] = [ @@ -697,16 +699,17 @@ public class MainActivityTest { test('fails if assembleDebug fails', () async { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/integration_test/foo_test.dart', javaTestFileRelativePath, ]); - _writeJavaTestFile(pluginDir, javaTestFileRelativePath); + _writeJavaTestFile(plugin, javaTestFileRelativePath); - final String gradlewPath = pluginDir - .childDirectory('example') - .childDirectory('android') + final String gradlewPath = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android) .childFile('gradlew') .path; processRunner.mockProcessesForExecutable[gradlewPath] = [ @@ -741,13 +744,13 @@ public class MainActivityTest { test('experimental flag', () async { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/integration_test/foo_test.dart', 'example/android/gradlew', javaTestFileRelativePath, ]); - _writeJavaTestFile(pluginDir, javaTestFileRelativePath); + _writeJavaTestFile(plugin, javaTestFileRelativePath); await runCapturingPrint(runner, [ 'firebase-test-lab', diff --git a/script/tool/test/format_command_test.dart b/script/tool/test/format_command_test.dart index 6c10a7dc3209..3fa7782245a7 100644 --- a/script/tool/test/format_command_test.dart +++ b/script/tool/test/format_command_test.dart @@ -50,10 +50,10 @@ void main() { /// Returns a modified version of a list of [relativePaths] that are relative /// to [package] to instead be relative to [packagesDir]. List _getPackagesDirRelativePaths( - Directory packageDir, List relativePaths) { + RepositoryPackage package, List relativePaths) { final p.Context path = analyzeCommand.path; final String relativeBase = - path.relative(packageDir.path, from: packagesDir.path); + path.relative(package.path, from: packagesDir.path); return relativePaths .map((String relativePath) => path.join(relativeBase, relativePath)) .toList(); @@ -86,7 +86,7 @@ void main() { 'lib/src/b.dart', 'lib/src/c.dart', ]; - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'a_plugin', packagesDir, extraFiles: files, @@ -101,7 +101,7 @@ void main() { getFlutterCommand(mockPlatform), [ 'format', - ..._getPackagesDirRelativePaths(pluginDir, files) + ..._getPackagesDirRelativePaths(plugin, files) ], packagesDir.path), ])); @@ -114,7 +114,7 @@ void main() { 'lib/src/c.dart', ]; const String unformattedFile = 'lib/src/d.dart'; - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'a_plugin', packagesDir, extraFiles: [ @@ -124,7 +124,8 @@ void main() { ); final p.Context posixContext = p.posix; - childFileWithSubcomponents(pluginDir, posixContext.split(unformattedFile)) + childFileWithSubcomponents( + plugin.directory, posixContext.split(unformattedFile)) .writeAsStringSync( '// copyright bla bla\n// This file is hand-formatted.\ncode...'); @@ -137,7 +138,7 @@ void main() { getFlutterCommand(mockPlatform), [ 'format', - ..._getPackagesDirRelativePaths(pluginDir, formattedFiles) + ..._getPackagesDirRelativePaths(plugin, formattedFiles) ], packagesDir.path), ])); @@ -172,7 +173,7 @@ void main() { 'android/src/main/java/io/flutter/plugins/a_plugin/a.java', 'android/src/main/java/io/flutter/plugins/a_plugin/b.java', ]; - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'a_plugin', packagesDir, extraFiles: files, @@ -190,7 +191,7 @@ void main() { '-jar', javaFormatPath, '--replace', - ..._getPackagesDirRelativePaths(pluginDir, files) + ..._getPackagesDirRelativePaths(plugin, files) ], packagesDir.path), ])); @@ -252,7 +253,7 @@ void main() { 'android/src/main/java/io/flutter/plugins/a_plugin/a.java', 'android/src/main/java/io/flutter/plugins/a_plugin/b.java', ]; - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'a_plugin', packagesDir, extraFiles: files, @@ -270,7 +271,7 @@ void main() { '-jar', javaFormatPath, '--replace', - ..._getPackagesDirRelativePaths(pluginDir, files) + ..._getPackagesDirRelativePaths(plugin, files) ], packagesDir.path), ])); @@ -285,7 +286,7 @@ void main() { 'macos/Classes/Foo.mm', 'windows/foo_plugin.cpp', ]; - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'a_plugin', packagesDir, extraFiles: files, @@ -302,7 +303,7 @@ void main() { [ '-i', '--style=file', - ..._getPackagesDirRelativePaths(pluginDir, files) + ..._getPackagesDirRelativePaths(plugin, files) ], packagesDir.path), ])); @@ -339,7 +340,7 @@ void main() { const List files = [ 'windows/foo_plugin.cpp', ]; - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'a_plugin', packagesDir, extraFiles: files, @@ -358,7 +359,7 @@ void main() { [ '-i', '--style=file', - ..._getPackagesDirRelativePaths(pluginDir, files) + ..._getPackagesDirRelativePaths(plugin, files) ], packagesDir.path), ])); @@ -403,7 +404,7 @@ void main() { const List javaFiles = [ 'android/src/main/java/io/flutter/plugins/a_plugin/a.java' ]; - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'a_plugin', packagesDir, extraFiles: [ @@ -426,14 +427,14 @@ void main() { [ '-i', '--style=file', - ..._getPackagesDirRelativePaths(pluginDir, clangFiles) + ..._getPackagesDirRelativePaths(plugin, clangFiles) ], packagesDir.path), ProcessCall( getFlutterCommand(mockPlatform), [ 'format', - ..._getPackagesDirRelativePaths(pluginDir, dartFiles) + ..._getPackagesDirRelativePaths(plugin, dartFiles) ], packagesDir.path), ProcessCall( @@ -442,7 +443,7 @@ void main() { '-jar', javaFormatPath, '--replace', - ..._getPackagesDirRelativePaths(pluginDir, javaFiles) + ..._getPackagesDirRelativePaths(plugin, javaFiles) ], packagesDir.path), ])); diff --git a/script/tool/test/lint_android_command_test.dart b/script/tool/test/lint_android_command_test.dart index 91608d3785cc..b072946ff959 100644 --- a/script/tool/test/lint_android_command_test.dart +++ b/script/tool/test/lint_android_command_test.dart @@ -40,7 +40,7 @@ void main() { }); test('runs gradle lint', () async { - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('plugin1', packagesDir, extraFiles: [ 'example/android/gradlew', ], platformSupport: { @@ -48,7 +48,7 @@ void main() { }); final Directory androidDir = - pluginDir.childDirectory('example').childDirectory('android'); + plugin.getExamples().first.platformDirectory(FlutterPlatform.android); final List output = await runCapturingPrint(runner, ['lint-android']); @@ -74,7 +74,7 @@ void main() { test('runs on all examples', () async { final List examples = ['example1', 'example2']; - final Directory pluginDir = createFakePlugin('plugin1', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin1', packagesDir, examples: examples, extraFiles: [ 'example/example1/android/gradlew', @@ -84,11 +84,9 @@ void main() { platformAndroid: const PlatformDetails(PlatformSupport.inline) }); - final Iterable exampleAndroidDirs = examples.map( - (String example) => pluginDir - .childDirectory('example') - .childDirectory(example) - .childDirectory('android')); + final Iterable exampleAndroidDirs = plugin.getExamples().map( + (RepositoryPackage example) => + example.platformDirectory(FlutterPlatform.android)); final List output = await runCapturingPrint(runner, ['lint-android']); @@ -136,16 +134,17 @@ void main() { }); test('fails if linting finds issues', () async { - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('plugin1', packagesDir, extraFiles: [ 'example/android/gradlew', ], platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline) }); - final String gradlewPath = pluginDir - .childDirectory('example') - .childDirectory('android') + final String gradlewPath = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android) .childFile('gradlew') .path; processRunner.mockProcessesForExecutable[gradlewPath] = [ diff --git a/script/tool/test/lint_podspecs_command_test.dart b/script/tool/test/lint_podspecs_command_test.dart index bccbec678666..516a32fa6925 100644 --- a/script/tool/test/lint_podspecs_command_test.dart +++ b/script/tool/test/lint_podspecs_command_test.dart @@ -65,7 +65,7 @@ void main() { }); test('runs pod lib lint on a podspec', () async { - final Directory plugin1Dir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin1', packagesDir, extraFiles: [ @@ -91,8 +91,8 @@ void main() { [ 'lib', 'lint', - plugin1Dir - .childDirectory('ios') + plugin + .platformDirectory(FlutterPlatform.ios) .childFile('plugin1.podspec') .path, '--configuration=Debug', @@ -106,8 +106,8 @@ void main() { [ 'lib', 'lint', - plugin1Dir - .childDirectory('ios') + plugin + .platformDirectory(FlutterPlatform.ios) .childFile('plugin1.podspec') .path, '--configuration=Debug', diff --git a/script/tool/test/list_command_test.dart b/script/tool/test/list_command_test.dart index 9e70f72e7483..c2042c26638c 100644 --- a/script/tool/test/list_command_test.dart +++ b/script/tool/test/list_command_test.dart @@ -119,17 +119,11 @@ void main() { // Create a federated plugin by creating a directory under the packages // directory with several packages underneath. - final Directory federatedPlugin = packagesDir.childDirectory('my_plugin') - ..createSync(); - final Directory clientLibrary = - federatedPlugin.childDirectory('my_plugin')..createSync(); - createFakePubspec(clientLibrary); - final Directory webLibrary = - federatedPlugin.childDirectory('my_plugin_web')..createSync(); - createFakePubspec(webLibrary); - final Directory macLibrary = - federatedPlugin.childDirectory('my_plugin_macos')..createSync(); - createFakePubspec(macLibrary); + final Directory federatedPluginDir = + packagesDir.childDirectory('my_plugin')..createSync(); + createFakePlugin('my_plugin', federatedPluginDir); + createFakePlugin('my_plugin_web', federatedPluginDir); + createFakePlugin('my_plugin_macos', federatedPluginDir); // Test without specifying `--type`. final List plugins = @@ -151,17 +145,11 @@ void main() { // Create a federated plugin by creating a directory under the packages // directory with several packages underneath. - final Directory federatedPlugin = packagesDir.childDirectory('my_plugin') - ..createSync(); - final Directory clientLibrary = - federatedPlugin.childDirectory('my_plugin')..createSync(); - createFakePubspec(clientLibrary); - final Directory webLibrary = - federatedPlugin.childDirectory('my_plugin_web')..createSync(); - createFakePubspec(webLibrary); - final Directory macLibrary = - federatedPlugin.childDirectory('my_plugin_macos')..createSync(); - createFakePubspec(macLibrary); + final Directory federatedPluginDir = + packagesDir.childDirectory('my_plugin')..createSync(); + createFakePlugin('my_plugin', federatedPluginDir); + createFakePlugin('my_plugin_web', federatedPluginDir); + createFakePlugin('my_plugin_macos', federatedPluginDir); List plugins = await runCapturingPrint( runner, ['list', '--packages=plugin1']); diff --git a/script/tool/test/make_deps_path_based_command_test.dart b/script/tool/test/make_deps_path_based_command_test.dart index da241c3d83f7..2644e814f578 100644 --- a/script/tool/test/make_deps_path_based_command_test.dart +++ b/script/tool/test/make_deps_path_based_command_test.dart @@ -7,7 +7,6 @@ import 'dart:io' as io; import 'package:args/command_runner.dart'; import 'package:file/file.dart'; import 'package:file/memory.dart'; -import 'package:flutter_plugin_tools/src/common/repository_package.dart'; import 'package:flutter_plugin_tools/src/make_deps_path_based_command.dart'; import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; @@ -62,9 +61,9 @@ void main() { } test('no-ops for no plugins', () async { - RepositoryPackage(createFakePackage('foo', packagesDir, isFlutter: true)); - final RepositoryPackage packageBar = RepositoryPackage( - createFakePackage('bar', packagesDir, isFlutter: true)); + createFakePackage('foo', packagesDir, isFlutter: true); + final RepositoryPackage packageBar = + createFakePackage('bar', packagesDir, isFlutter: true); _addDependencies(packageBar, ['foo']); final String originalPubspecContents = packageBar.pubspecFile.readAsStringSync(); @@ -83,16 +82,15 @@ void main() { }); test('rewrites references', () async { - final RepositoryPackage simplePackage = RepositoryPackage( - createFakePackage('foo', packagesDir, isFlutter: true)); + final RepositoryPackage simplePackage = + createFakePackage('foo', packagesDir, isFlutter: true); final Directory pluginGroup = packagesDir.childDirectory('bar'); - RepositoryPackage(createFakePackage('bar_platform_interface', pluginGroup, - isFlutter: true)); + createFakePackage('bar_platform_interface', pluginGroup, isFlutter: true); final RepositoryPackage pluginImplementation = - RepositoryPackage(createFakePlugin('bar_android', pluginGroup)); + createFakePlugin('bar_android', pluginGroup); final RepositoryPackage pluginAppFacing = - RepositoryPackage(createFakePlugin('bar', pluginGroup)); + createFakePlugin('bar', pluginGroup); _addDependencies(simplePackage, [ 'bar', @@ -147,16 +145,15 @@ void main() { // This test case ensures that running CI using this command on an interim // PR that itself used this command won't fail on the rewrite step. test('running a second time no-ops without failing', () async { - final RepositoryPackage simplePackage = RepositoryPackage( - createFakePackage('foo', packagesDir, isFlutter: true)); + final RepositoryPackage simplePackage = + createFakePackage('foo', packagesDir, isFlutter: true); final Directory pluginGroup = packagesDir.childDirectory('bar'); - RepositoryPackage(createFakePackage('bar_platform_interface', pluginGroup, - isFlutter: true)); + createFakePackage('bar_platform_interface', pluginGroup, isFlutter: true); final RepositoryPackage pluginImplementation = - RepositoryPackage(createFakePlugin('bar_android', pluginGroup)); + createFakePlugin('bar_android', pluginGroup); final RepositoryPackage pluginAppFacing = - RepositoryPackage(createFakePlugin('bar', pluginGroup)); + createFakePlugin('bar', pluginGroup); _addDependencies(simplePackage, [ 'bar', @@ -192,18 +189,17 @@ void main() { group('target-dependencies-with-non-breaking-updates', () { test('no-ops for no published changes', () async { - final Directory package = createFakePackage('foo', packagesDir); + final RepositoryPackage package = createFakePackage('foo', packagesDir); final String changedFileOutput = [ - package.childFile('pubspec.yaml'), + package.pubspecFile, ].map((File file) => file.path).join('\n'); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: changedFileOutput), ]; // Simulate no change to the version in the interface's pubspec.yaml. processRunner.mockProcessesForExecutable['git-show'] = [ - MockProcess( - stdout: RepositoryPackage(package).pubspecFile.readAsStringSync()), + MockProcess(stdout: package.pubspecFile.readAsStringSync()), ]; final List output = await runCapturingPrint(runner, [ @@ -244,10 +240,10 @@ void main() { test('includes bugfix version changes as targets', () async { const String newVersion = '1.0.1'; - final Directory package = + final RepositoryPackage package = createFakePackage('foo', packagesDir, version: newVersion); - final File pubspecFile = RepositoryPackage(package).pubspecFile; + final File pubspecFile = package.pubspecFile; final String changedFileOutput = [ pubspecFile, ].map((File file) => file.path).join('\n'); @@ -276,10 +272,10 @@ void main() { test('includes minor version changes to 1.0+ as targets', () async { const String newVersion = '1.1.0'; - final Directory package = + final RepositoryPackage package = createFakePackage('foo', packagesDir, version: newVersion); - final File pubspecFile = RepositoryPackage(package).pubspecFile; + final File pubspecFile = package.pubspecFile; final String changedFileOutput = [ pubspecFile, ].map((File file) => file.path).join('\n'); @@ -308,10 +304,10 @@ void main() { test('does not include major version changes as targets', () async { const String newVersion = '2.0.0'; - final Directory package = + final RepositoryPackage package = createFakePackage('foo', packagesDir, version: newVersion); - final File pubspecFile = RepositoryPackage(package).pubspecFile; + final File pubspecFile = package.pubspecFile; final String changedFileOutput = [ pubspecFile, ].map((File file) => file.path).join('\n'); @@ -340,10 +336,10 @@ void main() { test('does not include minor version changes to 0.x as targets', () async { const String newVersion = '0.8.0'; - final Directory package = + final RepositoryPackage package = createFakePackage('foo', packagesDir, version: newVersion); - final File pubspecFile = RepositoryPackage(package).pubspecFile; + final File pubspecFile = package.pubspecFile; final String changedFileOutput = [ pubspecFile, ].map((File file) => file.path).join('\n'); @@ -373,12 +369,12 @@ void main() { test('skips anything outside of the packages directory', () async { final Directory toolDir = packagesDir.parent.childDirectory('tool'); const String newVersion = '1.1.0'; - final Directory package = createFakePackage( + final RepositoryPackage package = createFakePackage( 'flutter_plugin_tools', toolDir, version: newVersion); // Simulate a minor version change so it would be a target. - final File pubspecFile = RepositoryPackage(package).pubspecFile; + final File pubspecFile = package.pubspecFile; final String changedFileOutput = [ pubspecFile, ].map((File file) => file.path).join('\n'); diff --git a/script/tool/test/native_test_command_test.dart b/script/tool/test/native_test_command_test.dart index 1069a68107c5..d420184b6125 100644 --- a/script/tool/test/native_test_command_test.dart +++ b/script/tool/test/native_test_command_test.dart @@ -57,8 +57,8 @@ final Map _kDeviceListMap = { const String _fakeCmakeCommand = 'path/to/cmake'; -void _createFakeCMakeCache(Directory pluginDir, Platform platform) { - final CMakeProject project = CMakeProject(pluginDir.childDirectory('example'), +void _createFakeCMakeCache(RepositoryPackage plugin, Platform platform) { + final CMakeProject project = CMakeProject(getExampleDir(plugin), platform: platform, buildMode: 'Release'); final File cache = project.buildDirectory.childFile('CMakeCache.txt'); cache.createSync(recursive: true); @@ -150,13 +150,12 @@ void main() { // Returns the ProcessCall to expect for build the Linux unit tests for the // given plugin. - ProcessCall _getLinuxBuildCall(Directory pluginDir) { + ProcessCall _getLinuxBuildCall(RepositoryPackage plugin) { return ProcessCall( 'cmake', [ '--build', - pluginDir - .childDirectory('example') + getExampleDir(plugin) .childDirectory('build') .childDirectory('linux') .childDirectory('x64') @@ -205,13 +204,12 @@ void main() { }); test('reports skips with no tests', () async { - final Directory pluginDirectory1 = createFakePlugin('plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory1.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); processRunner.mockProcessesForExecutable['xcrun'] = [ _getMockXcodebuildListProcess(['RunnerTests', 'RunnerUITests']), @@ -273,13 +271,12 @@ void main() { }); test('running with correct destination', () async { - final Directory pluginDirectory = createFakePlugin( - 'plugin', packagesDir, platformSupport: { - platformIOS: const PlatformDetails(PlatformSupport.inline) - }); + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, + platformSupport: { + platformIOS: const PlatformDetails(PlatformSupport.inline) + }); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); processRunner.mockProcessesForExecutable['xcrun'] = [ _getMockXcodebuildListProcess( @@ -311,12 +308,11 @@ void main() { test('Not specifying --ios-destination assigns an available simulator', () async { - final Directory pluginDirectory = createFakePlugin( - 'plugin', packagesDir, platformSupport: { - platformIOS: const PlatformDetails(PlatformSupport.inline) - }); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, + platformSupport: { + platformIOS: const PlatformDetails(PlatformSupport.inline) + }); + final Directory pluginExampleDirectory = getExampleDir(plugin); processRunner.mockProcessesForExecutable['xcrun'] = [ MockProcess(stdout: jsonEncode(_kDeviceListMap)), // simctl @@ -382,14 +378,12 @@ void main() { }); test('runs for macOS plugin', () async { - final Directory pluginDirectory1 = createFakePlugin( - 'plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory1.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); processRunner.mockProcessesForExecutable['xcrun'] = [ _getMockXcodebuildListProcess( @@ -417,7 +411,7 @@ void main() { group('Android', () { test('runs Java unit tests in Android implementation folder', () async { - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, platformSupport: { @@ -431,8 +425,10 @@ void main() { await runCapturingPrint(runner, ['native-test', '--android']); - final Directory androidFolder = - plugin.childDirectory('example').childDirectory('android'); + final Directory androidFolder = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android); expect( processRunner.recordedCalls, @@ -447,7 +443,7 @@ void main() { }); test('runs Java unit tests in example folder', () async { - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, platformSupport: { @@ -461,8 +457,10 @@ void main() { await runCapturingPrint(runner, ['native-test', '--android']); - final Directory androidFolder = - plugin.childDirectory('example').childDirectory('android'); + final Directory androidFolder = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android); expect( processRunner.recordedCalls, @@ -477,7 +475,7 @@ void main() { }); test('runs Java integration tests', () async { - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, platformSupport: { @@ -492,8 +490,10 @@ void main() { await runCapturingPrint( runner, ['native-test', '--android', '--no-unit']); - final Directory androidFolder = - plugin.childDirectory('example').childDirectory('android'); + final Directory androidFolder = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android); expect( processRunner.recordedCalls, @@ -539,7 +539,7 @@ void main() { }); test('runs all tests when present', () async { - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, platformSupport: { @@ -554,8 +554,10 @@ void main() { await runCapturingPrint(runner, ['native-test', '--android']); - final Directory androidFolder = - plugin.childDirectory('example').childDirectory('android'); + final Directory androidFolder = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android); expect( processRunner.recordedCalls, @@ -578,7 +580,7 @@ void main() { }); test('honors --no-unit', () async { - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, platformSupport: { @@ -594,8 +596,10 @@ void main() { await runCapturingPrint( runner, ['native-test', '--android', '--no-unit']); - final Directory androidFolder = - plugin.childDirectory('example').childDirectory('android'); + final Directory androidFolder = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android); expect( processRunner.recordedCalls, @@ -613,7 +617,7 @@ void main() { }); test('honors --no-integration', () async { - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, platformSupport: { @@ -629,8 +633,10 @@ void main() { await runCapturingPrint( runner, ['native-test', '--android', '--no-integration']); - final Directory androidFolder = - plugin.childDirectory('example').childDirectory('android'); + final Directory androidFolder = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android); expect( processRunner.recordedCalls, @@ -720,7 +726,7 @@ void main() { }); test('fails when a unit test fails', () async { - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, platformSupport: { @@ -732,9 +738,10 @@ void main() { ], ); - final String gradlewPath = pluginDir - .childDirectory('example') - .childDirectory('android') + final String gradlewPath = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android) .childFile('gradlew') .path; processRunner.mockProcessesForExecutable[gradlewPath] = [ @@ -761,7 +768,7 @@ void main() { }); test('fails when an integration test fails', () async { - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, platformSupport: { @@ -774,9 +781,10 @@ void main() { ], ); - final String gradlewPath = pluginDir - .childDirectory('example') - .childDirectory('android') + final String gradlewPath = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android) .childFile('gradlew') .path; processRunner.mockProcessesForExecutable[gradlewPath] = [ @@ -882,15 +890,15 @@ void main() { test('builds and runs unit tests', () async { const String testBinaryRelativePath = 'build/linux/x64/release/bar/plugin_test'; - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/$testBinaryRelativePath' ], platformSupport: { platformLinux: const PlatformDetails(PlatformSupport.inline), }); - _createFakeCMakeCache(pluginDirectory, mockPlatform); + _createFakeCMakeCache(plugin, mockPlatform); - final File testBinary = childFileWithSubcomponents(pluginDirectory, + final File testBinary = childFileWithSubcomponents(plugin.directory, ['example', ...testBinaryRelativePath.split('/')]); final List output = await runCapturingPrint(runner, [ @@ -910,7 +918,7 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getLinuxBuildCall(pluginDirectory), + _getLinuxBuildCall(plugin), ProcessCall(testBinary.path, const [], null), ])); }); @@ -920,17 +928,17 @@ void main() { 'build/linux/x64/debug/bar/plugin_test'; const String releaseTestBinaryRelativePath = 'build/linux/x64/release/bar/plugin_test'; - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/$debugTestBinaryRelativePath', 'example/$releaseTestBinaryRelativePath' ], platformSupport: { platformLinux: const PlatformDetails(PlatformSupport.inline), }); - _createFakeCMakeCache(pluginDirectory, mockPlatform); + _createFakeCMakeCache(plugin, mockPlatform); final File releaseTestBinary = childFileWithSubcomponents( - pluginDirectory, + plugin.directory, ['example', ...releaseTestBinaryRelativePath.split('/')]); final List output = await runCapturingPrint(runner, [ @@ -950,7 +958,7 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getLinuxBuildCall(pluginDirectory), + _getLinuxBuildCall(plugin), ProcessCall(releaseTestBinary.path, const [], null), ])); }); @@ -983,12 +991,11 @@ void main() { }); test('fails if there are no unit tests', () async { - final Directory pluginDirectory = createFakePlugin( - 'plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformLinux: const PlatformDetails(PlatformSupport.inline), }); - _createFakeCMakeCache(pluginDirectory, mockPlatform); + _createFakeCMakeCache(plugin, mockPlatform); Error? commandError; final List output = await runCapturingPrint(runner, [ @@ -1010,22 +1017,22 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getLinuxBuildCall(pluginDirectory), + _getLinuxBuildCall(plugin), ])); }); test('fails if a unit test fails', () async { const String testBinaryRelativePath = 'build/linux/x64/release/bar/plugin_test'; - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/$testBinaryRelativePath' ], platformSupport: { platformLinux: const PlatformDetails(PlatformSupport.inline), }); - _createFakeCMakeCache(pluginDirectory, mockPlatform); + _createFakeCMakeCache(plugin, mockPlatform); - final File testBinary = childFileWithSubcomponents(pluginDirectory, + final File testBinary = childFileWithSubcomponents(plugin.directory, ['example', ...testBinaryRelativePath.split('/')]); processRunner.mockProcessesForExecutable[testBinary.path] = @@ -1051,7 +1058,7 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getLinuxBuildCall(pluginDirectory), + _getLinuxBuildCall(plugin), ProcessCall(testBinary.path, const [], null), ])); }); @@ -1087,14 +1094,12 @@ void main() { }); test('honors unit-only', () async { - final Directory pluginDirectory1 = createFakePlugin( - 'plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory1.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); processRunner.mockProcessesForExecutable['xcrun'] = [ _getMockXcodebuildListProcess( @@ -1123,14 +1128,13 @@ void main() { }); test('honors integration-only', () async { - final Directory pluginDirectory1 = createFakePlugin( + final RepositoryPackage plugin1 = createFakePlugin( 'plugin', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory1.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin1); processRunner.mockProcessesForExecutable['xcrun'] = [ _getMockXcodebuildListProcess( @@ -1159,14 +1163,13 @@ void main() { }); test('skips when the requested target is not present', () async { - final Directory pluginDirectory1 = createFakePlugin( + final RepositoryPackage plugin1 = createFakePlugin( 'plugin', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory1.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin1); // Simulate a project with unit tests but no integration tests... processRunner.mockProcessesForExecutable['xcrun'] = [ @@ -1195,14 +1198,13 @@ void main() { }); test('fails if there are no unit tests', () async { - final Directory pluginDirectory1 = createFakePlugin( + final RepositoryPackage plugin1 = createFakePlugin( 'plugin', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory1.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin1); processRunner.mockProcessesForExecutable['xcrun'] = [ _getMockXcodebuildListProcess(['RunnerUITests']), @@ -1235,14 +1237,13 @@ void main() { }); test('fails if unable to check for requested target', () async { - final Directory pluginDirectory1 = createFakePlugin( + final RepositoryPackage plugin1 = createFakePlugin( 'plugin', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory1.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin1); processRunner.mockProcessesForExecutable['xcrun'] = [ MockProcess(exitCode: 1), // xcodebuild -list @@ -1275,7 +1276,7 @@ void main() { group('multiplatform', () { test('runs all platfroms when supported', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, extraFiles: [ @@ -1289,8 +1290,7 @@ void main() { }, ); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final Directory androidFolder = pluginExampleDirectory.childDirectory('android'); @@ -1334,14 +1334,12 @@ void main() { }); test('runs only macOS for a macOS plugin', () async { - final Directory pluginDirectory1 = createFakePlugin( - 'plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory1.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); processRunner.mockProcessesForExecutable['xcrun'] = [ _getMockXcodebuildListProcess( @@ -1372,13 +1370,12 @@ void main() { }); test('runs only iOS for a iOS plugin', () async { - final Directory pluginDirectory = createFakePlugin( - 'plugin', packagesDir, platformSupport: { - platformIOS: const PlatformDetails(PlatformSupport.inline) - }); + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, + platformSupport: { + platformIOS: const PlatformDetails(PlatformSupport.inline) + }); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); processRunner.mockProcessesForExecutable['xcrun'] = [ _getMockXcodebuildListProcess( @@ -1466,7 +1463,7 @@ void main() { }); test('failing one platform does not stop the tests', () async { - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, platformSupport: { @@ -1485,9 +1482,10 @@ void main() { ]; // Simulate failing Android, but not iOS. - final String gradlewPath = pluginDir - .childDirectory('example') - .childDirectory('android') + final String gradlewPath = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android) .childFile('gradlew') .path; processRunner.mockProcessesForExecutable[gradlewPath] = [ @@ -1522,7 +1520,7 @@ void main() { }); test('failing multiple platforms reports multiple failures', () async { - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, platformSupport: { @@ -1536,9 +1534,10 @@ void main() { ); // Simulate failing Android. - final String gradlewPath = pluginDir - .childDirectory('example') - .childDirectory('android') + final String gradlewPath = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android) .childFile('gradlew') .path; processRunner.mockProcessesForExecutable[gradlewPath] = [ @@ -1599,13 +1598,12 @@ void main() { // Returns the ProcessCall to expect for build the Windows unit tests for // the given plugin. - ProcessCall _getWindowsBuildCall(Directory pluginDir) { + ProcessCall _getWindowsBuildCall(RepositoryPackage plugin) { return ProcessCall( _fakeCmakeCommand, [ '--build', - pluginDir - .childDirectory('example') + getExampleDir(plugin) .childDirectory('build') .childDirectory('windows') .path, @@ -1621,15 +1619,15 @@ void main() { test('runs unit tests', () async { const String testBinaryRelativePath = 'build/windows/Debug/bar/plugin_test.exe'; - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/$testBinaryRelativePath' ], platformSupport: { platformWindows: const PlatformDetails(PlatformSupport.inline), }); - _createFakeCMakeCache(pluginDirectory, mockPlatform); + _createFakeCMakeCache(plugin, mockPlatform); - final File testBinary = childFileWithSubcomponents(pluginDirectory, + final File testBinary = childFileWithSubcomponents(plugin.directory, ['example', ...testBinaryRelativePath.split('/')]); final List output = await runCapturingPrint(runner, [ @@ -1649,7 +1647,7 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getWindowsBuildCall(pluginDirectory), + _getWindowsBuildCall(plugin), ProcessCall(testBinary.path, const [], null), ])); }); @@ -1659,16 +1657,17 @@ void main() { 'build/windows/Debug/bar/plugin_test.exe'; const String releaseTestBinaryRelativePath = 'build/windows/Release/bar/plugin_test.exe'; - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/$debugTestBinaryRelativePath', 'example/$releaseTestBinaryRelativePath' ], platformSupport: { platformWindows: const PlatformDetails(PlatformSupport.inline), }); - _createFakeCMakeCache(pluginDirectory, mockPlatform); + _createFakeCMakeCache(plugin, mockPlatform); - final File debugTestBinary = childFileWithSubcomponents(pluginDirectory, + final File debugTestBinary = childFileWithSubcomponents( + plugin.directory, ['example', ...debugTestBinaryRelativePath.split('/')]); final List output = await runCapturingPrint(runner, [ @@ -1688,7 +1687,7 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getWindowsBuildCall(pluginDirectory), + _getWindowsBuildCall(plugin), ProcessCall(debugTestBinary.path, const [], null), ])); }); @@ -1721,12 +1720,11 @@ void main() { }); test('fails if there are no unit tests', () async { - final Directory pluginDirectory = createFakePlugin( - 'plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformWindows: const PlatformDetails(PlatformSupport.inline), }); - _createFakeCMakeCache(pluginDirectory, mockPlatform); + _createFakeCMakeCache(plugin, mockPlatform); Error? commandError; final List output = await runCapturingPrint(runner, [ @@ -1748,22 +1746,22 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getWindowsBuildCall(pluginDirectory), + _getWindowsBuildCall(plugin), ])); }); test('fails if a unit test fails', () async { const String testBinaryRelativePath = 'build/windows/Debug/bar/plugin_test.exe'; - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/$testBinaryRelativePath' ], platformSupport: { platformWindows: const PlatformDetails(PlatformSupport.inline), }); - _createFakeCMakeCache(pluginDirectory, mockPlatform); + _createFakeCMakeCache(plugin, mockPlatform); - final File testBinary = childFileWithSubcomponents(pluginDirectory, + final File testBinary = childFileWithSubcomponents(plugin.directory, ['example', ...testBinaryRelativePath.split('/')]); processRunner.mockProcessesForExecutable[testBinary.path] = @@ -1789,7 +1787,7 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getWindowsBuildCall(pluginDirectory), + _getWindowsBuildCall(plugin), ProcessCall(testBinary.path, const [], null), ])); }); diff --git a/script/tool/test/publish_check_command_test.dart b/script/tool/test/publish_check_command_test.dart index c5527af21736..e6c5b9cdebc5 100644 --- a/script/tool/test/publish_check_command_test.dart +++ b/script/tool/test/publish_check_command_test.dart @@ -44,9 +44,9 @@ void main() { }); test('publish check all packages', () async { - final Directory plugin1Dir = + final RepositoryPackage plugin1 = createFakePlugin('plugin_tools_test_package_a', packagesDir); - final Directory plugin2Dir = + final RepositoryPackage plugin2 = createFakePlugin('plugin_tools_test_package_b', packagesDir); await runCapturingPrint(runner, ['publish-check']); @@ -57,11 +57,11 @@ void main() { ProcessCall( 'flutter', const ['pub', 'publish', '--', '--dry-run'], - plugin1Dir.path), + plugin1.path), ProcessCall( 'flutter', const ['pub', 'publish', '--', '--dry-run'], - plugin2Dir.path), + plugin2.path), ])); }); @@ -89,8 +89,8 @@ void main() { }); test('fail on bad pubspec', () async { - final Directory dir = createFakePlugin('c', packagesDir); - await dir.childFile('pubspec.yaml').writeAsString('bad-yaml'); + final RepositoryPackage package = createFakePlugin('c', packagesDir); + await package.pubspecFile.writeAsString('bad-yaml'); Error? commandError; final List output = await runCapturingPrint( @@ -108,8 +108,9 @@ void main() { }); test('fails if AUTHORS is missing', () async { - final Directory package = createFakePackage('a_package', packagesDir); - package.childFile('AUTHORS').delete(); + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); + package.authorsFile.delete(); Error? commandError; final List output = await runCapturingPrint( @@ -128,12 +129,12 @@ void main() { }); test('does not require AUTHORS for third-party', () async { - final Directory package = createFakePackage( + final RepositoryPackage package = createFakePackage( 'a_package', packagesDir.parent .childDirectory('third_party') .childDirectory('packages')); - package.childFile('AUTHORS').delete(); + package.authorsFile.delete(); final List output = await runCapturingPrint(runner, ['publish-check']); @@ -372,11 +373,11 @@ void main() { ); runner.addCommand(command); - final Directory plugin1Dir = + final RepositoryPackage plugin = createFakePlugin('no_publish_a', packagesDir, version: '0.1.0'); createFakePlugin('no_publish_b', packagesDir, version: '0.2.0'); - await plugin1Dir.childFile('pubspec.yaml').writeAsString('bad-yaml'); + await plugin.pubspecFile.writeAsString('bad-yaml'); bool hasError = false; final List output = await runCapturingPrint( diff --git a/script/tool/test/publish_plugin_command_test.dart b/script/tool/test/publish_plugin_command_test.dart index 2cb3fc25af2e..857828ab9306 100644 --- a/script/tool/test/publish_plugin_command_test.dart +++ b/script/tool/test/publish_plugin_command_test.dart @@ -83,11 +83,11 @@ void main() { group('Initial validation', () { test('refuses to proceed with dirty files', () async { - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('foo', packagesDir, examples: []); processRunner.mockProcessesForExecutable['git-status'] = [ - MockProcess(stdout: '?? ${pluginDir.childFile('tmp').path}\n') + MockProcess(stdout: '?? ${plugin.directory.childFile('tmp').path}\n') ]; Error? commandError; @@ -183,7 +183,7 @@ void main() { }); test('forwards --pub-publish-flags to pub publish', () async { - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('foo', packagesDir, examples: []); await runCapturingPrint(commandRunner, [ @@ -198,14 +198,14 @@ void main() { contains(ProcessCall( flutterCommand, const ['pub', 'publish', '--dry-run', '--server=bar'], - pluginDir.path))); + plugin.path))); }); test( '--skip-confirmation flag automatically adds --force to --pub-publish-flags', () async { _createMockCredentialFile(); - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('foo', packagesDir, examples: []); await runCapturingPrint(commandRunner, [ @@ -221,7 +221,7 @@ void main() { contains(ProcessCall( flutterCommand, const ['pub', 'publish', '--server=bar', '--force'], - pluginDir.path))); + plugin.path))); }); test('throws if pub publish fails', () async { @@ -249,7 +249,7 @@ void main() { }); test('publish, dry run', () async { - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('foo', packagesDir, examples: []); final List output = @@ -268,7 +268,7 @@ void main() { containsAllInOrder([ contains('=============== DRY RUN ==============='), contains('Running for foo'), - contains('Running `pub publish ` in ${pluginDir.path}...'), + contains('Running `pub publish ` in ${plugin.path}...'), contains('Tagging release foo-v0.0.1...'), contains('Pushing tag to upstream...'), contains('Published foo successfully!'), @@ -386,7 +386,7 @@ void main() { }); test('to upstream by default, dry run', () async { - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('foo', packagesDir, examples: []); mockStdin.readLineOutput = 'y'; @@ -402,7 +402,7 @@ void main() { output, containsAllInOrder([ contains('=============== DRY RUN ==============='), - contains('Running `pub publish ` in ${pluginDir.path}...'), + contains('Running `pub publish ` in ${plugin.path}...'), contains('Tagging release foo-v0.0.1...'), contains('Pushing tag to upstream...'), contains('Published foo successfully!'), @@ -447,16 +447,17 @@ void main() { }; // Non-federated - final Directory pluginDir1 = createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); // federated - final Directory pluginDir2 = createFakePlugin( + final RepositoryPackage plugin2 = createFakePlugin( 'plugin2', packagesDir.childDirectory('plugin2'), ); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess( - stdout: '${pluginDir1.childFile('pubspec.yaml').path}\n' - '${pluginDir2.childFile('pubspec.yaml').path}\n') + stdout: '${plugin1.pubspecFile.path}\n' + '${plugin2.pubspecFile.path}\n') ]; mockStdin.readLineOutput = 'y'; @@ -468,8 +469,8 @@ void main() { containsAllInOrder([ contains( 'Publishing all packages that have changed relative to "HEAD~"'), - contains('Running `pub publish ` in ${pluginDir1.path}...'), - contains('Running `pub publish ` in ${pluginDir2.path}...'), + contains('Running `pub publish ` in ${plugin1.path}...'), + contains('Running `pub publish ` in ${plugin2.path}...'), contains('plugin1 - \x1B[32mpublished\x1B[0m'), contains('plugin2/plugin2 - \x1B[32mpublished\x1B[0m'), ])); @@ -503,9 +504,10 @@ void main() { // The existing plugin. createFakePlugin('plugin0', packagesDir); // Non-federated - final Directory pluginDir1 = createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); // federated - final Directory pluginDir2 = + final RepositoryPackage plugin2 = createFakePlugin('plugin2', packagesDir.childDirectory('plugin2')); // Git results for plugin0 having been released already, and plugin1 and @@ -515,8 +517,8 @@ void main() { ]; processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess( - stdout: '${pluginDir1.childFile('pubspec.yaml').path}\n' - '${pluginDir2.childFile('pubspec.yaml').path}\n') + stdout: '${plugin1.pubspecFile.path}\n' + '${plugin2.pubspecFile.path}\n') ]; mockStdin.readLineOutput = 'y'; @@ -527,8 +529,8 @@ void main() { expect( output, containsAllInOrder([ - 'Running `pub publish ` in ${pluginDir1.path}...\n', - 'Running `pub publish ` in ${pluginDir2.path}...\n', + 'Running `pub publish ` in ${plugin1.path}...\n', + 'Running `pub publish ` in ${plugin2.path}...\n', ])); expect( processRunner.recordedCalls, @@ -552,15 +554,16 @@ void main() { }; // Non-federated - final Directory pluginDir1 = createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); // federated - final Directory pluginDir2 = + final RepositoryPackage plugin2 = createFakePlugin('plugin2', packagesDir.childDirectory('plugin2')); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess( - stdout: '${pluginDir1.childFile('pubspec.yaml').path}\n' - '${pluginDir2.childFile('pubspec.yaml').path}\n') + stdout: '${plugin1.pubspecFile.path}\n' + '${plugin2.pubspecFile.path}\n') ]; mockStdin.readLineOutput = 'y'; @@ -576,11 +579,11 @@ void main() { output, containsAllInOrder([ contains('=============== DRY RUN ==============='), - contains('Running `pub publish ` in ${pluginDir1.path}...'), + contains('Running `pub publish ` in ${plugin1.path}...'), contains('Tagging release plugin1-v0.0.1...'), contains('Pushing tag to upstream...'), contains('Published plugin1 successfully!'), - contains('Running `pub publish ` in ${pluginDir2.path}...'), + contains('Running `pub publish ` in ${plugin2.path}...'), contains('Tagging release plugin2-v0.0.1...'), contains('Pushing tag to upstream...'), contains('Published plugin2 successfully!'), @@ -603,17 +606,17 @@ void main() { }; // Non-federated - final Directory pluginDir1 = + final RepositoryPackage plugin1 = createFakePlugin('plugin1', packagesDir, version: '0.0.2'); // federated - final Directory pluginDir2 = createFakePlugin( + final RepositoryPackage plugin2 = createFakePlugin( 'plugin2', packagesDir.childDirectory('plugin2'), version: '0.0.2'); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess( - stdout: '${pluginDir1.childFile('pubspec.yaml').path}\n' - '${pluginDir2.childFile('pubspec.yaml').path}\n') + stdout: '${plugin1.pubspecFile.path}\n' + '${plugin2.pubspecFile.path}\n') ]; mockStdin.readLineOutput = 'y'; @@ -623,9 +626,9 @@ void main() { expect( output2, containsAllInOrder([ - contains('Running `pub publish ` in ${pluginDir1.path}...'), + contains('Running `pub publish ` in ${plugin1.path}...'), contains('Published plugin1 successfully!'), - contains('Running `pub publish ` in ${pluginDir2.path}...'), + contains('Running `pub publish ` in ${plugin2.path}...'), contains('Published plugin2 successfully!'), ])); expect( @@ -652,17 +655,17 @@ void main() { }; // Non-federated - final Directory pluginDir1 = + final RepositoryPackage plugin1 = createFakePlugin('plugin1', packagesDir, version: '0.0.2'); // federated - final Directory pluginDir2 = + final RepositoryPackage plugin2 = createFakePlugin('plugin2', packagesDir.childDirectory('plugin2')); - pluginDir2.deleteSync(recursive: true); + plugin2.directory.deleteSync(recursive: true); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess( - stdout: '${pluginDir1.childFile('pubspec.yaml').path}\n' - '${pluginDir2.childFile('pubspec.yaml').path}\n') + stdout: '${plugin1.pubspecFile.path}\n' + '${plugin2.pubspecFile.path}\n') ]; mockStdin.readLineOutput = 'y'; @@ -672,7 +675,7 @@ void main() { expect( output2, containsAllInOrder([ - contains('Running `pub publish ` in ${pluginDir1.path}...'), + contains('Running `pub publish ` in ${plugin1.path}...'), contains('Published plugin1 successfully!'), contains( 'The pubspec file for plugin2/plugin2 does not exist, so no publishing will happen.\nSafe to ignore if the package is deleted in this commit.\n'), @@ -698,17 +701,17 @@ void main() { }; // Non-federated - final Directory pluginDir1 = + final RepositoryPackage plugin1 = createFakePlugin('plugin1', packagesDir, version: '0.0.2'); // federated - final Directory pluginDir2 = createFakePlugin( + final RepositoryPackage plugin2 = createFakePlugin( 'plugin2', packagesDir.childDirectory('plugin2'), version: '0.0.2'); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess( - stdout: '${pluginDir1.childFile('pubspec.yaml').path}\n' - '${pluginDir2.childFile('pubspec.yaml').path}\n') + stdout: '${plugin1.pubspecFile.path}\n' + '${plugin2.pubspecFile.path}\n') ]; processRunner.mockProcessesForExecutable['git-tag'] = [ MockProcess( @@ -748,17 +751,17 @@ void main() { }; // Non-federated - final Directory pluginDir1 = + final RepositoryPackage plugin1 = createFakePlugin('plugin1', packagesDir, version: '0.0.2'); // federated - final Directory pluginDir2 = createFakePlugin( + final RepositoryPackage plugin2 = createFakePlugin( 'plugin2', packagesDir.childDirectory('plugin2'), version: '0.0.2'); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess( - stdout: '${pluginDir1.childFile('pubspec.yaml').path}\n' - '${pluginDir2.childFile('pubspec.yaml').path}\n') + stdout: '${plugin1.pubspecFile.path}\n' + '${plugin2.pubspecFile.path}\n') ]; Error? commandError; @@ -785,15 +788,16 @@ void main() { test('No version change does not release any plugins', () async { // Non-federated - final Directory pluginDir1 = createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); // federated - final Directory pluginDir2 = + final RepositoryPackage plugin2 = createFakePlugin('plugin2', packagesDir.childDirectory('plugin2')); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess( - stdout: '${pluginDir1.childFile('plugin1.dart').path}\n' - '${pluginDir2.childFile('plugin2.dart').path}\n') + stdout: '${plugin1.libDirectory.childFile('plugin1.dart').path}\n' + '${plugin2.libDirectory.childFile('plugin2.dart').path}\n') ]; final List output = await runCapturingPrint(commandRunner, @@ -812,10 +816,10 @@ void main() { 'versions': [], }; - final Directory flutterPluginTools = + final RepositoryPackage flutterPluginTools = createFakePlugin('flutter_plugin_tools', packagesDir); processRunner.mockProcessesForExecutable['git-diff'] = [ - MockProcess(stdout: flutterPluginTools.childFile('pubspec.yaml').path) + MockProcess(stdout: flutterPluginTools.pubspecFile.path) ]; final List output = await runCapturingPrint(commandRunner, diff --git a/script/tool/test/pubspec_check_command_test.dart b/script/tool/test/pubspec_check_command_test.dart index 30b6ab6004e7..89bb98abd80c 100644 --- a/script/tool/test/pubspec_check_command_test.dart +++ b/script/tool/test/pubspec_check_command_test.dart @@ -146,9 +146,9 @@ void main() { }); test('passes for a plugin following conventions', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true)} ${_environmentSection()} ${_flutterSection(isPlugin: true)} @@ -157,10 +157,7 @@ ${_devDependenciesSection()} ${_falseSecretsSection()} '''); - pluginDirectory - .childDirectory('example') - .childFile('pubspec.yaml') - .writeAsStringSync(''' + plugin.getExamples().first.pubspecFile.writeAsStringSync(''' ${_headerSection( 'plugin_example', publishable: false, @@ -187,10 +184,10 @@ ${_flutterSection()} }); test('passes for a Flutter package following conventions', () async { - final Directory packageDirectory = + final RepositoryPackage package = createFakePackage('a_package', packagesDir); - packageDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + package.pubspecFile.writeAsStringSync(''' ${_headerSection('a_package')} ${_environmentSection()} ${_dependenciesSection()} @@ -199,10 +196,7 @@ ${_flutterSection()} ${_falseSecretsSection()} '''); - packageDirectory - .childDirectory('example') - .childFile('pubspec.yaml') - .writeAsStringSync(''' + package.getExamples().first.pubspecFile.writeAsStringSync(''' ${_headerSection( 'a_package', publishable: false, @@ -229,10 +223,10 @@ ${_flutterSection()} }); test('passes for a minimal package following conventions', () async { - final Directory packageDirectory = packagesDir.childDirectory('package'); - packageDirectory.createSync(recursive: true); + final RepositoryPackage package = + createFakePackage('package', packagesDir, examples: []); - packageDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + package.pubspecFile.writeAsStringSync(''' ${_headerSection('package')} ${_environmentSection()} ${_dependenciesSection()} @@ -252,10 +246,10 @@ ${_dependenciesSection()} }); test('fails when homepage is included', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, includeHomepage: true)} ${_environmentSection()} ${_flutterSection(isPlugin: true)} @@ -280,10 +274,10 @@ ${_devDependenciesSection()} }); test('fails when repository is missing', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, includeRepository: false)} ${_environmentSection()} ${_flutterSection(isPlugin: true)} @@ -307,10 +301,10 @@ ${_devDependenciesSection()} }); test('fails when homepage is given instead of repository', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, includeHomepage: true, includeRepository: false)} ${_environmentSection()} ${_flutterSection(isPlugin: true)} @@ -335,10 +329,10 @@ ${_devDependenciesSection()} }); test('fails when repository is incorrect', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, repositoryPackagesDirRelativePath: 'different_plugin')} ${_environmentSection()} ${_flutterSection(isPlugin: true)} @@ -362,10 +356,10 @@ ${_devDependenciesSection()} }); test('fails when issue tracker is missing', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, includeIssueTracker: false)} ${_environmentSection()} ${_flutterSection(isPlugin: true)} @@ -389,11 +383,11 @@ ${_devDependenciesSection()} }); test('fails when description is too short', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'a_plugin', packagesDir.childDirectory('a_plugin'), examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, description: 'Too short')} ${_environmentSection()} ${_flutterSection(isPlugin: true)} @@ -420,10 +414,10 @@ ${_devDependenciesSection()} test( 'allows short descriptions for non-app-facing parts of federated plugins', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, description: 'Too short')} ${_environmentSection()} ${_flutterSection(isPlugin: true)} @@ -448,7 +442,7 @@ ${_devDependenciesSection()} }); test('fails when description is too long', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, examples: []); const String description = 'This description is too long. It just goes ' @@ -456,7 +450,7 @@ ${_devDependenciesSection()} 'there is just too much here. Someone shoul really cut this down to just ' 'the core description so that search results are more useful and the ' 'package does not lose pub points.'; - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, description: description)} ${_environmentSection()} ${_flutterSection(isPlugin: true)} @@ -481,10 +475,10 @@ ${_devDependenciesSection()} }); test('fails when environment section is out of order', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true)} ${_flutterSection(isPlugin: true)} ${_dependenciesSection()} @@ -509,10 +503,10 @@ ${_environmentSection()} }); test('fails when flutter section is out of order', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true)} ${_flutterSection(isPlugin: true)} ${_environmentSection()} @@ -537,10 +531,10 @@ ${_devDependenciesSection()} }); test('fails when dependencies section is out of order', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true)} ${_environmentSection()} ${_flutterSection(isPlugin: true)} @@ -565,9 +559,9 @@ ${_dependenciesSection()} }); test('fails when dev_dependencies section is out of order', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true)} ${_environmentSection()} ${_devDependenciesSection()} @@ -592,10 +586,10 @@ ${_dependenciesSection()} }); test('fails when false_secrets section is out of order', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true)} ${_environmentSection()} ${_flutterSection(isPlugin: true)} @@ -622,11 +616,11 @@ ${_devDependenciesSection()} test('fails when an implemenation package is missing "implements"', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin_a_foo', packagesDir.childDirectory('plugin_a'), examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin_a_foo', isPlugin: true)} ${_environmentSection()} ${_flutterSection(isPlugin: true)} @@ -651,11 +645,11 @@ ${_devDependenciesSection()} test('fails when an implemenation package has the wrong "implements"', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin_a_foo', packagesDir.childDirectory('plugin_a'), examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin_a_foo', isPlugin: true)} ${_environmentSection()} ${_flutterSection(isPlugin: true, implementedPackage: 'plugin_a_foo')} @@ -680,11 +674,11 @@ ${_devDependenciesSection()} }); test('passes for a correct implemenation package', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin_a_foo', packagesDir.childDirectory('plugin_a'), examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection( 'plugin_a_foo', isPlugin: true, @@ -709,11 +703,11 @@ ${_devDependenciesSection()} }); test('fails when a "default_package" looks incorrect', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin_a', packagesDir.childDirectory('plugin_a'), examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection( 'plugin_a', isPlugin: true, @@ -749,11 +743,11 @@ ${_devDependenciesSection()} test( 'fails when a "default_package" does not have a corresponding dependency', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin_a', packagesDir.childDirectory('plugin_a'), examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection( 'plugin_a', isPlugin: true, @@ -787,11 +781,11 @@ ${_devDependenciesSection()} }); test('passes for an app-facing package without "implements"', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin_a', packagesDir.childDirectory('plugin_a'), examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection( 'plugin_a', isPlugin: true, @@ -817,11 +811,11 @@ ${_devDependenciesSection()} test('passes for a platform interface package without "implements"', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin_a_platform_interface', packagesDir.childDirectory('plugin_a'), examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection( 'plugin_a_platform_interface', isPlugin: true, @@ -847,13 +841,13 @@ ${_devDependenciesSection()} }); test('validates some properties even for unpublished packages', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin_a_foo', packagesDir.childDirectory('plugin_a'), examples: []); // Environment section is in the wrong location. // Missing 'implements'. - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin_a_foo', isPlugin: true, publishable: false)} ${_flutterSection(isPlugin: true)} ${_dependenciesSection()} @@ -879,12 +873,12 @@ ${_environmentSection()} }); test('ignores some checks for unpublished packages', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, examples: []); // Missing metadata that is only useful for published packages, such as // repository and issue tracker. - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection( 'plugin', isPlugin: true, @@ -936,10 +930,10 @@ ${_devDependenciesSection()} }); test('repository check works', () async { - final Directory packageDirectory = + final RepositoryPackage package = createFakePackage('package', packagesDir, examples: []); - packageDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + package.pubspecFile.writeAsStringSync(''' ${_headerSection('package')} ${_environmentSection()} ${_dependenciesSection()} diff --git a/script/tool/test/readme_check_command_test.dart b/script/tool/test/readme_check_command_test.dart index b6e016dccab4..f53fa06a8133 100644 --- a/script/tool/test/readme_check_command_test.dart +++ b/script/tool/test/readme_check_command_test.dart @@ -62,7 +62,7 @@ void main() { const String federatedPluginName = 'a_federated_plugin'; final Directory federatedDir = packagesDir.childDirectory(federatedPluginName); - final List packageDirectories = [ + final List packages = [ // A non-plugin package. createFakePackage('a_package', packagesDir), // Non-app-facing parts of a federated plugin. @@ -71,8 +71,8 @@ void main() { createFakePlugin('${federatedPluginName}_android', federatedDir), ]; - for (final Directory package in packageDirectories) { - package.childFile('README.md').writeAsStringSync(''' + for (final RepositoryPackage package in packages) { + package.readmeFile.writeAsStringSync(''' A very useful package. '''); } @@ -94,9 +94,10 @@ A very useful package. test('fails when non-federated plugin is missing an OS support table', () async { - final Directory pluginDir = createFakePlugin('a_plugin', packagesDir); + final RepositoryPackage plugin = + createFakePlugin('a_plugin', packagesDir); - pluginDir.childFile('README.md').writeAsStringSync(''' + plugin.readmeFile.writeAsStringSync(''' A very useful plugin. '''); @@ -118,10 +119,10 @@ A very useful plugin. test( 'fails when app-facing part of a federated plugin is missing an OS support table', () async { - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('a_plugin', packagesDir.childDirectory('a_plugin')); - pluginDir.childFile('README.md').writeAsStringSync(''' + plugin.readmeFile.writeAsStringSync(''' A very useful plugin. '''); @@ -141,9 +142,10 @@ A very useful plugin. }); test('fails the OS support table is missing the header', () async { - final Directory pluginDir = createFakePlugin('a_plugin', packagesDir); + final RepositoryPackage plugin = + createFakePlugin('a_plugin', packagesDir); - pluginDir.childFile('README.md').writeAsStringSync(''' + plugin.readmeFile.writeAsStringSync(''' A very useful plugin. | **Support** | SDK 21+ | iOS 10+* | [See `camera_web `][1] | @@ -165,7 +167,7 @@ A very useful plugin. }); test('fails if the OS support table is missing a supported OS', () async { - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'a_plugin', packagesDir, platformSupport: { @@ -175,7 +177,7 @@ A very useful plugin. }, ); - pluginDir.childFile('README.md').writeAsStringSync(''' + plugin.readmeFile.writeAsStringSync(''' A very useful plugin. | | Android | iOS | @@ -202,7 +204,7 @@ A very useful plugin. }); test('fails if the OS support table lists an extra OS', () async { - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'a_plugin', packagesDir, platformSupport: { @@ -211,7 +213,7 @@ A very useful plugin. }, ); - pluginDir.childFile('README.md').writeAsStringSync(''' + plugin.readmeFile.writeAsStringSync(''' A very useful plugin. | | Android | iOS | Web | @@ -239,7 +241,7 @@ A very useful plugin. test('fails if the OS support table has unexpected OS formatting', () async { - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'a_plugin', packagesDir, platformSupport: { @@ -250,7 +252,7 @@ A very useful plugin. }, ); - pluginDir.childFile('README.md').writeAsStringSync(''' + plugin.readmeFile.writeAsStringSync(''' A very useful plugin. | | android | ios | MacOS | web | @@ -278,9 +280,10 @@ A very useful plugin. group('code blocks', () { test('fails on missing info string', () async { - final Directory packageDir = createFakePackage('a_package', packagesDir); + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); - packageDir.childFile('README.md').writeAsStringSync(''' + package.readmeFile.writeAsStringSync(''' Example: ``` @@ -307,9 +310,10 @@ void main() { }); test('allows unknown info strings', () async { - final Directory packageDir = createFakePackage('a_package', packagesDir); + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); - packageDir.childFile('README.md').writeAsStringSync(''' + package.readmeFile.writeAsStringSync(''' Example: ```someunknowninfotag @@ -331,9 +335,10 @@ A B C }); test('allows space around info strings', () async { - final Directory packageDir = createFakePackage('a_package', packagesDir); + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); - packageDir.childFile('README.md').writeAsStringSync(''' + package.readmeFile.writeAsStringSync(''' Example: ``` dart @@ -355,9 +360,10 @@ A B C }); test('passes when excerpt requirement is met', () async { - final Directory packageDir = createFakePackage('a_package', packagesDir); + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); - packageDir.childFile('README.md').writeAsStringSync(''' + package.readmeFile.writeAsStringSync(''' Example: @@ -379,9 +385,10 @@ A B C }); test('fails on missing excerpt tag when requested', () async { - final Directory packageDir = createFakePackage('a_package', packagesDir); + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); - packageDir.childFile('README.md').writeAsStringSync(''' + package.readmeFile.writeAsStringSync(''' Example: ```dart diff --git a/script/tool/test/test_command_test.dart b/script/tool/test/test_command_test.dart index 386eaf0d345b..14a1e4a67c1f 100644 --- a/script/tool/test/test_command_test.dart +++ b/script/tool/test/test_command_test.dart @@ -40,9 +40,9 @@ void main() { }); test('runs flutter test on each plugin', () async { - final Directory plugin1Dir = createFakePlugin('plugin1', packagesDir, + final RepositoryPackage plugin1 = createFakePlugin('plugin1', packagesDir, extraFiles: ['test/empty_test.dart']); - final Directory plugin2Dir = createFakePlugin('plugin2', packagesDir, + final RepositoryPackage plugin2 = createFakePlugin('plugin2', packagesDir, extraFiles: ['test/empty_test.dart']); await runCapturingPrint(runner, ['test']); @@ -51,15 +51,15 @@ void main() { processRunner.recordedCalls, orderedEquals([ ProcessCall(getFlutterCommand(mockPlatform), - const ['test', '--color'], plugin1Dir.path), + const ['test', '--color'], plugin1.path), ProcessCall(getFlutterCommand(mockPlatform), - const ['test', '--color'], plugin2Dir.path), + const ['test', '--color'], plugin2.path), ]), ); }); test('runs flutter test on Flutter package example tests', () async { - final Directory pluginDir = createFakePlugin('a_plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('a_plugin', packagesDir, extraFiles: [ 'test/empty_test.dart', 'example/test/an_example_test.dart' @@ -71,11 +71,9 @@ void main() { processRunner.recordedCalls, orderedEquals([ ProcessCall(getFlutterCommand(mockPlatform), - const ['test', '--color'], pluginDir.path), - ProcessCall( - getFlutterCommand(mockPlatform), - const ['test', '--color'], - pluginDir.childDirectory('example').path), + const ['test', '--color'], plugin.path), + ProcessCall(getFlutterCommand(mockPlatform), + const ['test', '--color'], getExampleDir(plugin).path), ]), ); }); @@ -110,7 +108,7 @@ void main() { test('skips testing plugins without test directory', () async { createFakePlugin('plugin1', packagesDir); - final Directory plugin2Dir = createFakePlugin('plugin2', packagesDir, + final RepositoryPackage plugin2 = createFakePlugin('plugin2', packagesDir, extraFiles: ['test/empty_test.dart']); await runCapturingPrint(runner, ['test']); @@ -119,15 +117,15 @@ void main() { processRunner.recordedCalls, orderedEquals([ ProcessCall(getFlutterCommand(mockPlatform), - const ['test', '--color'], plugin2Dir.path), + const ['test', '--color'], plugin2.path), ]), ); }); test('runs dart run test on non-Flutter packages', () async { - final Directory pluginDir = createFakePlugin('a', packagesDir, + final RepositoryPackage plugin = createFakePlugin('a', packagesDir, extraFiles: ['test/empty_test.dart']); - final Directory packageDir = createFakePackage('b', packagesDir, + final RepositoryPackage package = createFakePackage('b', packagesDir, extraFiles: ['test/empty_test.dart']); await runCapturingPrint( @@ -139,34 +137,34 @@ void main() { ProcessCall( getFlutterCommand(mockPlatform), const ['test', '--color', '--enable-experiment=exp1'], - pluginDir.path), - ProcessCall('dart', const ['pub', 'get'], packageDir.path), + plugin.path), + ProcessCall('dart', const ['pub', 'get'], package.path), ProcessCall( 'dart', const ['run', '--enable-experiment=exp1', 'test'], - packageDir.path), + package.path), ]), ); }); test('runs dart run test on non-Flutter package examples', () async { - final Directory packageDir = createFakePackage('a_package', packagesDir, - extraFiles: [ - 'test/empty_test.dart', - 'example/test/an_example_test.dart' - ]); + final RepositoryPackage package = createFakePackage( + 'a_package', packagesDir, extraFiles: [ + 'test/empty_test.dart', + 'example/test/an_example_test.dart' + ]); await runCapturingPrint(runner, ['test']); expect( processRunner.recordedCalls, orderedEquals([ - ProcessCall('dart', const ['pub', 'get'], packageDir.path), - ProcessCall('dart', const ['run', 'test'], packageDir.path), + ProcessCall('dart', const ['pub', 'get'], package.path), + ProcessCall('dart', const ['run', 'test'], package.path), ProcessCall('dart', const ['pub', 'get'], - packageDir.childDirectory('example').path), + getExampleDir(package).path), ProcessCall('dart', const ['run', 'test'], - packageDir.childDirectory('example').path), + getExampleDir(package).path), ]), ); }); @@ -220,7 +218,7 @@ void main() { }); test('runs on Chrome for web plugins', () async { - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, extraFiles: ['test/empty_test.dart'], @@ -237,15 +235,15 @@ void main() { ProcessCall( getFlutterCommand(mockPlatform), const ['test', '--color', '--platform=chrome'], - pluginDir.path), + plugin.path), ]), ); }); test('enable-experiment flag', () async { - final Directory pluginDir = createFakePlugin('a', packagesDir, + final RepositoryPackage plugin = createFakePlugin('a', packagesDir, extraFiles: ['test/empty_test.dart']); - final Directory packageDir = createFakePackage('b', packagesDir, + final RepositoryPackage package = createFakePackage('b', packagesDir, extraFiles: ['test/empty_test.dart']); await runCapturingPrint( @@ -257,12 +255,12 @@ void main() { ProcessCall( getFlutterCommand(mockPlatform), const ['test', '--color', '--enable-experiment=exp1'], - pluginDir.path), - ProcessCall('dart', const ['pub', 'get'], packageDir.path), + plugin.path), + ProcessCall('dart', const ['pub', 'get'], package.path), ProcessCall( 'dart', const ['run', '--enable-experiment=exp1', 'test'], - packageDir.path), + package.path), ]), ); }); diff --git a/script/tool/test/update_excerpts_command_test.dart b/script/tool/test/update_excerpts_command_test.dart index 30189cf23a00..5c1d74444eea 100644 --- a/script/tool/test/update_excerpts_command_test.dart +++ b/script/tool/test/update_excerpts_command_test.dart @@ -8,7 +8,6 @@ import 'package:args/command_runner.dart'; import 'package:file/file.dart'; import 'package:file/memory.dart'; import 'package:flutter_plugin_tools/src/common/core.dart'; -import 'package:flutter_plugin_tools/src/common/repository_package.dart'; import 'package:flutter_plugin_tools/src/update_excerpts_command.dart'; import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; @@ -42,9 +41,9 @@ void main() { }); test('runs pub get before running scripts', () async { - final Directory package = createFakePlugin('a_package', packagesDir, + final RepositoryPackage package = createFakePlugin('a_package', packagesDir, extraFiles: ['example/build.excerpt.yaml']); - final Directory example = package.childDirectory('example'); + final Directory example = getExampleDir(package); await runCapturingPrint(runner, ['update-excerpts']); @@ -69,9 +68,9 @@ void main() { }); test('runs when config is present', () async { - final Directory package = createFakePlugin('a_package', packagesDir, + final RepositoryPackage package = createFakePlugin('a_package', packagesDir, extraFiles: ['example/build.excerpt.yaml']); - final Directory example = package.childDirectory('example'); + final Directory example = getExampleDir(package); final List output = await runCapturingPrint(runner, ['update-excerpts']); @@ -128,7 +127,7 @@ void main() { }); test('restores pubspec even if running the script fails', () async { - final Directory package = createFakePlugin('a_package', packagesDir, + final RepositoryPackage package = createFakePlugin('a_package', packagesDir, extraFiles: ['example/build.excerpt.yaml']); processRunner.mockProcessesForExecutable['dart'] = [ @@ -152,11 +151,8 @@ void main() { ' Unable to get script dependencies') ])); - final String examplePubspecContent = RepositoryPackage(package) - .getExamples() - .first - .pubspecFile - .readAsStringSync(); + final String examplePubspecContent = + package.getExamples().first.pubspecFile.readAsStringSync(); expect(examplePubspecContent, isNot(contains('code_excerpter'))); expect(examplePubspecContent, isNot(contains('code_excerpt_updater'))); }); diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart index 8a2bf099cc8a..5c38bd5f7033 100644 --- a/script/tool/test/util.dart +++ b/script/tool/test/util.dart @@ -13,6 +13,7 @@ import 'package:flutter_plugin_tools/src/common/core.dart'; import 'package:flutter_plugin_tools/src/common/file_utils.dart'; import 'package:flutter_plugin_tools/src/common/plugin_utils.dart'; import 'package:flutter_plugin_tools/src/common/process_runner.dart'; +import 'package:flutter_plugin_tools/src/common/repository_package.dart'; import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; import 'package:platform/platform.dart'; @@ -20,6 +21,8 @@ import 'package:quiver/collection.dart'; import 'mocks.dart'; +export 'package:flutter_plugin_tools/src/common/repository_package.dart'; + /// Returns the exe name that command will use when running Flutter on /// [platform]. String getFlutterCommand(Platform platform) => @@ -65,6 +68,20 @@ class PlatformDetails { final bool hasDartCode; } +/// Returns the 'example' directory for [package]. +/// +/// This is deliberately not a method on [RepositoryPackage] since actual tool +/// code should essentially never need this, and instead be using +/// [RepositoryPackage.getExamples] to avoid assuming there's a single example +/// directory. However, needing to construct paths with the example directory +/// is very common in test code. +/// +/// This returns a Directory rather than a RepositoryPackage because there is no +/// guarantee that the returned directory is a package. +Directory getExampleDir(RepositoryPackage package) { + return package.directory.childDirectory('example'); +} + /// Creates a plugin package with the given [name] in [packagesDirectory]. /// /// [platformSupport] is a map of platform string to the support details for @@ -72,8 +89,7 @@ class PlatformDetails { /// /// [extraFiles] is an optional list of plugin-relative paths, using Posix /// separators, of extra files to create in the plugin. -// TODO(stuartmorgan): Convert the return to a RepositoryPackage. -Directory createFakePlugin( +RepositoryPackage createFakePlugin( String name, Directory parentDirectory, { List examples = const ['example'], @@ -83,7 +99,7 @@ Directory createFakePlugin( String? version = '0.0.1', String flutterConstraint = '>=2.5.0', }) { - final Directory pluginDirectory = createFakePackage(name, parentDirectory, + final RepositoryPackage package = createFakePackage(name, parentDirectory, isFlutter: true, examples: examples, extraFiles: extraFiles, @@ -91,7 +107,7 @@ Directory createFakePlugin( flutterConstraint: flutterConstraint); createFakePubspec( - pluginDirectory, + package, name: name, isFlutter: true, isPlugin: true, @@ -100,7 +116,7 @@ Directory createFakePlugin( flutterConstraint: flutterConstraint, ); - return pluginDirectory; + return package; } /// Creates a plugin package with the given [name] in [packagesDirectory]. @@ -113,8 +129,7 @@ Directory createFakePlugin( /// /// If non-null, [directoryName] will be used for the directory instead of /// [name]. -// TODO(stuartmorgan): Convert the return to a RepositoryPackage. -Directory createFakePackage( +RepositoryPackage createFakePackage( String name, Directory parentDirectory, { List examples = const ['example'], @@ -126,26 +141,26 @@ Directory createFakePackage( String? directoryName, String? publishTo, }) { - final Directory packageDirectory = - parentDirectory.childDirectory(directoryName ?? name); - packageDirectory.createSync(recursive: true); + final RepositoryPackage package = + RepositoryPackage(parentDirectory.childDirectory(directoryName ?? name)); + package.directory.createSync(recursive: true); - packageDirectory.childDirectory('lib').createSync(); - createFakePubspec(packageDirectory, + package.libDirectory.createSync(); + createFakePubspec(package, name: name, isFlutter: isFlutter, version: version, flutterConstraint: flutterConstraint); if (includeCommonFiles) { - createFakeCHANGELOG(packageDirectory, ''' + createFakeCHANGELOG(package, ''' ## $version * Some changes. '''); - createFakeAuthors(packageDirectory); + createFakeAuthors(package); } if (examples.length == 1) { - createFakePackage('${name}_example', packageDirectory, + createFakePackage('${name}_example', package.directory, directoryName: examples.first, examples: [], includeCommonFiles: false, @@ -153,8 +168,7 @@ Directory createFakePackage( publishTo: 'none', flutterConstraint: flutterConstraint); } else if (examples.isNotEmpty) { - final Directory examplesDirectory = - packageDirectory.childDirectory('example')..createSync(); + final Directory examplesDirectory = getExampleDir(package)..createSync(); for (final String exampleName in examples) { createFakePackage(exampleName, examplesDirectory, examples: [], @@ -167,16 +181,16 @@ Directory createFakePackage( final p.Context posixContext = p.posix; for (final String file in extraFiles) { - childFileWithSubcomponents(packageDirectory, posixContext.split(file)) + childFileWithSubcomponents(package.directory, posixContext.split(file)) .createSync(recursive: true); } - return packageDirectory; + return package; } -void createFakeCHANGELOG(Directory parent, String texts) { - parent.childFile('CHANGELOG.md').createSync(); - parent.childFile('CHANGELOG.md').writeAsStringSync(texts); +void createFakeCHANGELOG(RepositoryPackage package, String texts) { + package.changelogFile.createSync(); + package.changelogFile.writeAsStringSync(texts); } /// Creates a `pubspec.yaml` file with a flutter dependency. @@ -185,7 +199,7 @@ void createFakeCHANGELOG(Directory parent, String texts) { /// that platform. If empty, no `plugin` entry will be created unless `isPlugin` /// is set to `true`. void createFakePubspec( - Directory parent, { + RepositoryPackage package, { String name = 'fake_package', bool isFlutter = true, bool isPlugin = false, @@ -249,14 +263,13 @@ $dependenciesSection $pluginSection '''; - parent.childFile('pubspec.yaml').createSync(); - parent.childFile('pubspec.yaml').writeAsStringSync(yaml); + package.pubspecFile.createSync(); + package.pubspecFile.writeAsStringSync(yaml); } -void createFakeAuthors(Directory parent) { - final File authorsFile = parent.childFile('AUTHORS'); - authorsFile.createSync(); - authorsFile.writeAsStringSync('Google Inc.'); +void createFakeAuthors(RepositoryPackage package) { + package.authorsFile.createSync(); + package.authorsFile.writeAsStringSync('Google Inc.'); } String _pluginPlatformSection( diff --git a/script/tool/test/version_check_command_test.dart b/script/tool/test/version_check_command_test.dart index 5a5a0a108a32..aeacd77635df 100644 --- a/script/tool/test/version_check_command_test.dart +++ b/script/tool/test/version_check_command_test.dart @@ -414,14 +414,14 @@ This is necessary because of X, Y, and Z test('Allow empty lines in front of the first version in CHANGELOG', () async { const String version = '1.0.1'; - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: version); const String changelog = ''' ## $version * Some changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); final List output = await runCapturingPrint( runner, ['version-check', '--base-sha=main']); expect( @@ -433,13 +433,13 @@ This is necessary because of X, Y, and Z }); test('Throws if versions in changelog and pubspec do not match', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.1'); const String changelog = ''' ## 1.0.2 * Some changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); Error? commandError; final List output = await runCapturingPrint( runner, ['version-check', '--base-sha=main', '--against-pub'], @@ -458,14 +458,14 @@ This is necessary because of X, Y, and Z test('Success if CHANGELOG and pubspec versions match', () async { const String version = '1.0.1'; - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: version); const String changelog = ''' ## $version * Some changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); final List output = await runCapturingPrint( runner, ['version-check', '--base-sha=main']); expect( @@ -479,7 +479,7 @@ This is necessary because of X, Y, and Z test( 'Fail if pubspec version only matches an older version listed in CHANGELOG', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.0'); const String changelog = ''' @@ -488,7 +488,7 @@ This is necessary because of X, Y, and Z ## 1.0.0 * Some other changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); bool hasError = false; final List output = await runCapturingPrint( runner, ['version-check', '--base-sha=main', '--against-pub'], @@ -509,7 +509,7 @@ This is necessary because of X, Y, and Z test('Allow NEXT as a placeholder for gathering CHANGELOG entries', () async { const String version = '1.0.0'; - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: version); const String changelog = ''' @@ -518,7 +518,7 @@ This is necessary because of X, Y, and Z ## $version * Some other changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -536,7 +536,7 @@ This is necessary because of X, Y, and Z test('Fail if NEXT appears after a version', () async { const String version = '1.0.1'; - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: version); const String changelog = ''' @@ -547,7 +547,7 @@ This is necessary because of X, Y, and Z ## 1.0.0 * Some other changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); bool hasError = false; final List output = await runCapturingPrint( runner, ['version-check', '--base-sha=main', '--against-pub'], @@ -569,7 +569,7 @@ This is necessary because of X, Y, and Z test('Fail if NEXT is left in the CHANGELOG when adding a version bump', () async { const String version = '1.0.1'; - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: version); const String changelog = ''' @@ -580,7 +580,7 @@ This is necessary because of X, Y, and Z ## 1.0.0 * Some other changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); bool hasError = false; final List output = await runCapturingPrint( @@ -603,7 +603,7 @@ This is necessary because of X, Y, and Z }); test('Fail if the version changes without replacing NEXT', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.1'); const String changelog = ''' @@ -612,7 +612,7 @@ This is necessary because of X, Y, and Z ## 1.0.0 * Some other changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); bool hasError = false; final List output = await runCapturingPrint( @@ -635,7 +635,7 @@ This is necessary because of X, Y, and Z test( 'fails gracefully if the version headers are not found due to using the wrong style', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.0'); const String changelog = ''' @@ -644,7 +644,7 @@ This is necessary because of X, Y, and Z # 1.0.0 * Some other changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -670,14 +670,14 @@ This is necessary because of X, Y, and Z }); test('fails gracefully if the version is unparseable', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.0'); const String changelog = ''' ## Alpha * Some changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -715,14 +715,14 @@ This is necessary because of X, Y, and Z } test('passes for unchanged packages', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.0'); const String changelog = ''' ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -744,14 +744,14 @@ This is necessary because of X, Y, and Z test( 'fails if a version change is missing from a change that does not ' 'pass the exemption check', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.0'); const String changelog = ''' ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -779,14 +779,14 @@ packages/plugin/lib/plugin.dart }); test('passes version change requirement when version changes', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.1'); const String changelog = ''' ## 1.0.1 * Some changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -810,14 +810,14 @@ packages/plugin/pubspec.yaml }); test('version change check ignores files outside the package', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.0'); const String changelog = ''' ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -840,14 +840,14 @@ tool/plugin/lib/plugin.dart }); test('allows missing version change for exempt changes', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.0'); const String changelog = ''' ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -873,14 +873,14 @@ packages/plugin/CHANGELOG.md }); test('allows missing version change with justification', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.0'); const String changelog = ''' ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -914,14 +914,14 @@ No version change: Code change is only to implementation comments. }); test('fails if a CHANGELOG change is missing', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.0'); const String changelog = ''' ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -949,14 +949,14 @@ packages/plugin/example/lib/foo.dart }); test('passes CHANGELOG check when the CHANGELOG is changed', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.0'); const String changelog = ''' ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -980,14 +980,14 @@ packages/plugin/CHANGELOG.md test('fails CHANGELOG check if only another package CHANGELOG chages', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.0'); const String changelog = ''' ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -1014,14 +1014,14 @@ packages/another_plugin/CHANGELOG.md }); test('allows missing CHANGELOG change with justification', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.0'); const String changelog = ''' ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; diff --git a/script/tool/test/xcode_analyze_command_test.dart b/script/tool/test/xcode_analyze_command_test.dart index 097d4d21cb19..51e8e3283295 100644 --- a/script/tool/test/xcode_analyze_command_test.dart +++ b/script/tool/test/xcode_analyze_command_test.dart @@ -82,13 +82,12 @@ void main() { }); test('runs for iOS plugin', () async { - final Directory pluginDirectory = createFakePlugin( - 'plugin', packagesDir, platformSupport: { - platformIOS: const PlatformDetails(PlatformSupport.inline) - }); + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, + platformSupport: { + platformIOS: const PlatformDetails(PlatformSupport.inline) + }); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, [ 'xcode-analyze', @@ -184,14 +183,12 @@ void main() { }); test('runs for macOS plugin', () async { - final Directory pluginDirectory1 = createFakePlugin( - 'plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory1.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, [ 'xcode-analyze', @@ -251,15 +248,13 @@ void main() { group('combined', () { test('runs both iOS and macOS when supported', () async { - final Directory pluginDirectory1 = createFakePlugin( - 'plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformIOS: const PlatformDetails(PlatformSupport.inline), platformMacOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory1.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, [ 'xcode-analyze', @@ -311,14 +306,12 @@ void main() { }); test('runs only macOS for a macOS plugin', () async { - final Directory pluginDirectory1 = createFakePlugin( - 'plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory1.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, [ 'xcode-analyze', @@ -353,13 +346,12 @@ void main() { }); test('runs only iOS for a iOS plugin', () async { - final Directory pluginDirectory = createFakePlugin( - 'plugin', packagesDir, platformSupport: { - platformIOS: const PlatformDetails(PlatformSupport.inline) - }); + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, + platformSupport: { + platformIOS: const PlatformDetails(PlatformSupport.inline) + }); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, [ 'xcode-analyze', From bb6fc5e05165ab316fb2ed2a0485614b564c9397 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 3 May 2022 18:29:08 -0400 Subject: [PATCH 502/600] Roll Flutter from 1aab2d83d932 to 8b395090c9c1 (3 revisions) (#5608) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 368d3f785e14..b91311ac768b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -1aab2d83d9324a426c2cfc3468f6d98e5a2d5e43 +8b395090c9c1e20c5df24945fb8f0d272ea42aac From 7a31f83289e8c6c08fda0bdfa4affbb50e527729 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 3 May 2022 19:34:10 -0400 Subject: [PATCH 503/600] Roll Flutter from 8b395090c9c1 to b4f8d7c79296 (1 revision) (#5610) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index b91311ac768b..0dedab86f82a 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -8b395090c9c1e20c5df24945fb8f0d272ea42aac +b4f8d7c7929648cf2e4f51fb4ab746473ff09142 From 9b8659aaf672d6c5b332f36fc4644c56db8dd7fb Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 3 May 2022 20:39:07 -0400 Subject: [PATCH 504/600] Roll Flutter from b4f8d7c79296 to 896e5b332d36 (1 revision) (#5611) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 0dedab86f82a..c9f3e69b4be6 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b4f8d7c7929648cf2e4f51fb4ab746473ff09142 +896e5b332d364cbe407966787115ce6cc4e6dde4 From 8c261a223285e69702d96ef006001e623f0ee300 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 3 May 2022 22:49:09 -0400 Subject: [PATCH 505/600] Roll Flutter from 896e5b332d36 to 6f8ffecc7263 (2 revisions) (#5613) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index c9f3e69b4be6..fdccafa80965 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -896e5b332d364cbe407966787115ce6cc4e6dde4 +6f8ffecc726305bf050dfa9fe16dcee39e18a5d8 From 21e2f99e1ae06e014033171cd4640d5870a9ff20 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 4 May 2022 00:29:09 -0400 Subject: [PATCH 506/600] Roll Flutter from 6f8ffecc7263 to e291b58813f7 (1 revision) (#5615) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index fdccafa80965..5fb7f2fb5a18 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -6f8ffecc726305bf050dfa9fe16dcee39e18a5d8 +e291b58813f78177e959117bcdf86c298ad4da60 From 182071df2bf86faaeb4d2af2ed62f8b3ff833f62 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 4 May 2022 01:34:08 -0400 Subject: [PATCH 507/600] Roll Flutter from e291b58813f7 to e9c0ee1e08d6 (1 revision) (#5616) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 5fb7f2fb5a18..31ebd1f2f443 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -e291b58813f78177e959117bcdf86c298ad4da60 +e9c0ee1e08d64cdda1cc05e40f7688e2aae7041e From cafc154de894293ec032fe5c9247775ae363a7c5 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 4 May 2022 04:29:11 -0400 Subject: [PATCH 508/600] Roll Flutter from e9c0ee1e08d6 to b3838eb3de6d (1 revision) (#5617) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 31ebd1f2f443..e3f0de9a31e4 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -e9c0ee1e08d64cdda1cc05e40f7688e2aae7041e +b3838eb3de6d10bfcd91ccca2d44b1939d625bb8 From 0f0f6d70ba36ded848359562eebc4b994076f109 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 4 May 2022 13:49:13 -0400 Subject: [PATCH 509/600] Roll Flutter from b3838eb3de6d to b5321d182a6b (1 revision) (#5618) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index e3f0de9a31e4..b28761746824 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b3838eb3de6d10bfcd91ccca2d44b1939d625bb8 +b5321d182a6bf89fd3d86ceee52179867ffa7c89 From 8a62a57c5d0c32ae88d8de346899f8f25fd09108 Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Wed, 4 May 2022 11:44:19 -0700 Subject: [PATCH 510/600] [camera] Update mocktail to latest. (#5614) --- .../camera_web/example/integration_test/camera_test.dart | 2 +- .../example/integration_test/camera_web_test.dart | 6 +++--- packages/camera/camera_web/example/pubspec.yaml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/camera/camera_web/example/integration_test/camera_test.dart b/packages/camera/camera_web/example/integration_test/camera_test.dart index 4e8050eef6a8..20d1cbaf8e36 100644 --- a/packages/camera/camera_web/example/integration_test/camera_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_test.dart @@ -53,7 +53,7 @@ void main() { }); setUpAll(() { - registerFallbackValue(MockCameraOptions()); + registerFallbackValue(MockCameraOptions()); }); group('initialize', () { diff --git a/packages/camera/camera_web/example/integration_test/camera_web_test.dart b/packages/camera/camera_web/example/integration_test/camera_web_test.dart index 9f21eb43fb34..5a564dd66021 100644 --- a/packages/camera/camera_web/example/integration_test/camera_web_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_web_test.dart @@ -76,9 +76,9 @@ void main() { }); setUpAll(() { - registerFallbackValue(MockMediaStreamTrack()); - registerFallbackValue(MockCameraOptions()); - registerFallbackValue(FlashMode.off); + registerFallbackValue(MockMediaStreamTrack()); + registerFallbackValue(MockCameraOptions()); + registerFallbackValue(FlashMode.off); }); testWidgets('CameraPlugin is the live instance', diff --git a/packages/camera/camera_web/example/pubspec.yaml b/packages/camera/camera_web/example/pubspec.yaml index 0457e8fdfcf2..911648ef030d 100644 --- a/packages/camera/camera_web/example/pubspec.yaml +++ b/packages/camera/camera_web/example/pubspec.yaml @@ -18,4 +18,4 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - mocktail: ^0.1.4 + mocktail: ^0.3.0 From e5afe6d49b016327307e98403b6748dff8ccdfb7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 4 May 2022 15:49:14 -0400 Subject: [PATCH 511/600] Roll Flutter from b5321d182a6b to 82b91b0b45e5 (8 revisions) (#5626) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index b28761746824..d178b7bc2506 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b5321d182a6bf89fd3d86ceee52179867ffa7c89 +82b91b0b45e53f57c9a48fd43906f77ad73003f6 From 24b8e3bbbc659871b84b58293e57f56feda8acd8 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 4 May 2022 16:54:14 -0400 Subject: [PATCH 512/600] Roll Flutter from 82b91b0b45e5 to 3c9f417326fa (2 revisions) (#5629) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index d178b7bc2506..d440c987fb9e 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -82b91b0b45e53f57c9a48fd43906f77ad73003f6 +3c9f417326fa0d9ce296465fb0b213958927cee3 From 9a12b66452f98a485db152294c806cc60756197c Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 4 May 2022 17:59:12 -0400 Subject: [PATCH 513/600] Roll Flutter from 3c9f417326fa to 2e1c146eec61 (4 revisions) (#5630) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index d440c987fb9e..bb3f35211141 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -3c9f417326fa0d9ce296465fb0b213958927cee3 +2e1c146eec61aa534890a49d219e55a56da039c0 From d648000d1366edd4b85ce479c584b08542e82213 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 4 May 2022 19:39:15 -0400 Subject: [PATCH 514/600] Roll Flutter from 2e1c146eec61 to 122ab838f48b (1 revision) (#5631) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index bb3f35211141..508a93dc7479 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2e1c146eec61aa534890a49d219e55a56da039c0 +122ab838f48b728784f088b1560ee52f44ea7054 From 665628766a2b8783c772de2dc00d00438b876b86 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 4 May 2022 20:44:10 -0400 Subject: [PATCH 515/600] Roll Flutter from 122ab838f48b to 051f1b027f0d (1 revision) (#5632) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 508a93dc7479..6766638ed22b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -122ab838f48b728784f088b1560ee52f44ea7054 +051f1b027f0dada4eb09930f9b08061d7bfb3ce4 From 1dbf987a25d8ac559f100efa15e4a07a8d7e2aff Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 4 May 2022 21:49:09 -0400 Subject: [PATCH 516/600] Roll Flutter from 051f1b027f0d to 35513c265c94 (1 revision) (#5633) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 6766638ed22b..c99bf3cbe23c 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -051f1b027f0dada4eb09930f9b08061d7bfb3ce4 +35513c265c948260421ffdcc5ef9520349a91289 From 2d5121f4b77f5111afb6db219389e959e4dd2698 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 5 May 2022 09:39:09 -0400 Subject: [PATCH 517/600] Roll Flutter from 35513c265c94 to f7db4893667e (1 revision) (#5634) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index c99bf3cbe23c..3d1bf9d0a4d2 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -35513c265c948260421ffdcc5ef9520349a91289 +f7db4893667ee8190b2b52f82509bd84577292d7 From 1761e10db058c3a80879c4c8ed0b7db22890a517 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 5 May 2022 10:44:09 -0400 Subject: [PATCH 518/600] Roll Flutter from f7db4893667e to 0ea21bc5b397 (12 revisions) (#5637) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 3d1bf9d0a4d2..a77459034db0 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -f7db4893667ee8190b2b52f82509bd84577292d7 +0ea21bc5b397d9814bb8f2803a4533fae1fd24d6 From 810d8344aa9e8d99acdcfc8e6a8ede16763180bf Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 5 May 2022 13:54:14 -0400 Subject: [PATCH 519/600] Roll Flutter from 0ea21bc5b397 to f771c9fee734 (1 revision) (#5638) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index a77459034db0..4a584cd7c024 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -0ea21bc5b397d9814bb8f2803a4533fae1fd24d6 +f771c9fee734efb4912ecc2615990318b91a8c15 From 8907fe9f6e28392963cfdb129d20357657d9bfad Mon Sep 17 00:00:00 2001 From: Jesse Seales <103135467+sealesj@users.noreply.github.com> Date: Thu, 5 May 2022 15:49:11 -0400 Subject: [PATCH 520/600] [github workflow] Resolve token-permissions security alerts (#5627) --- .github/workflows/mirror.yml | 3 +++ .github/workflows/pull_request_label.yml | 3 +++ .github/workflows/release.yml | 3 +++ 3 files changed, 9 insertions(+) diff --git a/.github/workflows/mirror.yml b/.github/workflows/mirror.yml index 9026643d7b2e..9cfaf20d6cf0 100644 --- a/.github/workflows/mirror.yml +++ b/.github/workflows/mirror.yml @@ -7,6 +7,9 @@ on: push: branches: - 'main' + +# Declare default permissions as read only. +permissions: read-all jobs: mirror_job: diff --git a/.github/workflows/pull_request_label.yml b/.github/workflows/pull_request_label.yml index 825a3afd8508..54cae2deca98 100644 --- a/.github/workflows/pull_request_label.yml +++ b/.github/workflows/pull_request_label.yml @@ -12,6 +12,9 @@ on: pull_request_target: types: [opened, synchronize, reopened, closed] +# Declare default permissions as read only. +permissions: read-all + jobs: label: permissions: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1bf50a0d1dfc..a67f8f1575e4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,9 @@ on: branches: - main +# Declare default permissions as read only. +permissions: read-all + jobs: release: if: github.repository_owner == 'flutter' From ced642f92b900c127f7edc027558b568f186b74b Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 5 May 2022 16:54:13 -0400 Subject: [PATCH 521/600] Roll Flutter from f771c9fee734 to 0a26277b58c4 (3 revisions) (#5641) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 4a584cd7c024..b1dba7fa567b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -f771c9fee734efb4912ecc2615990318b91a8c15 +0a26277b58c401936e7cada2d17fb9278a1dfb40 From 6706418e2baa771eecd1345091c856f0cfb7b4cb Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 5 May 2022 17:59:13 -0400 Subject: [PATCH 522/600] Roll Flutter from 0a26277b58c4 to 603eb82734ab (1 revision) (#5644) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index b1dba7fa567b..8913c71b316c 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -0a26277b58c401936e7cada2d17fb9278a1dfb40 +603eb82734ab09e94c34c70324b915521d409991 From 49b06b36c9986b6d78babcf48c82dc4626a30fd3 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 5 May 2022 21:39:13 -0400 Subject: [PATCH 523/600] Roll Flutter from 603eb82734ab to 758ebda16dc1 (6 revisions) (#5646) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 8913c71b316c..9d16b7153de8 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -603eb82734ab09e94c34c70324b915521d409991 +758ebda16dc16994edac84257ffef92324eaf1bb From b5f505ee0afc144933551d36b73578a3000ab8d7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 6 May 2022 13:20:47 -0400 Subject: [PATCH 524/600] Roll Flutter from 758ebda16dc1 to 2f3f053c0dfb (5 revisions) (#5648) * 543cc6032 Roll Engine from 387a9b9e300f to b1b0a5adffe9 (1 revision) (flutter/flutter#103159) * 55881f7ae Reland "Fix crash from alt-tab'ing just after startup" (flutter/flutter#103093) * 82afe3ea4 Clear the cached data of `RenderBox` if its parent re-layout (flutter/flutter#101493) * b20e27e77 Does not replace the root layer unnecessarily (flutter/flutter#101748) * 2f3f053c0 Roll Engine from b1b0a5adffe9 to 47c8a6acf9f3 (1 revision) (flutter/flutter#103164) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 9d16b7153de8..8fe5ef2b1e32 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -758ebda16dc16994edac84257ffef92324eaf1bb +2f3f053c0dfb2101d2e3466144ef50f7836b47b6 From c5f1c6bbf3bf281735d29f9a25ab4443dfc79690 Mon Sep 17 00:00:00 2001 From: moko256 Date: Sat, 7 May 2022 03:09:11 +0900 Subject: [PATCH 525/600] Add issues/PRs badges to README (#5649) --- README.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 30c6a2f00e66..cc6543d4ecee 100644 --- a/README.md +++ b/README.md @@ -40,22 +40,22 @@ and send a [pull request](https://github.com/flutter/plugins/pulls). ## Plugins These are the available plugins in this repository. -| Plugin | Pub | Points | Popularity | Likes | -|--------|-----|--------|------------|-------| -| [camera](./packages/camera/) | [![pub package](https://img.shields.io/pub/v/camera.svg)](https://pub.dev/packages/camera) | [![pub points](https://badges.bar/camera/pub%20points)](https://pub.dev/packages/camera/score) | [![popularity](https://badges.bar/camera/popularity)](https://pub.dev/packages/camera/score) | [![likes](https://badges.bar/camera/likes)](https://pub.dev/packages/camera/score) | -| [espresso](./packages/espresso/) | [![pub package](https://img.shields.io/pub/v/espresso.svg)](https://pub.dev/packages/espresso) | [![pub points](https://badges.bar/espresso/pub%20points)](https://pub.dev/packages/espresso/score) | [![popularity](https://badges.bar/espresso/popularity)](https://pub.dev/packages/espresso/score) | [![likes](https://badges.bar/espresso/likes)](https://pub.dev/packages/espresso/score) | -| [file_selector](./packages/file_selector/) | [![pub package](https://img.shields.io/pub/v/file_selector.svg)](https://pub.dev/packages/file_selector) | [![pub points](https://badges.bar/file_selector/pub%20points)](https://pub.dev/packages/file_selector/score) | [![popularity](https://badges.bar/file_selector/popularity)](https://pub.dev/packages/file_selector/score) | [![likes](https://badges.bar/file_selector/likes)](https://pub.dev/packages/file_selector/score) | -| [flutter_plugin_android_lifecycle](./packages/flutter_plugin_android_lifecycle/) | [![pub package](https://img.shields.io/pub/v/flutter_plugin_android_lifecycle.svg)](https://pub.dev/packages/flutter_plugin_android_lifecycle) | [![pub points](https://badges.bar/flutter_plugin_android_lifecycle/pub%20points)](https://pub.dev/packages/flutter_plugin_android_lifecycle/score) | [![popularity](https://badges.bar/flutter_plugin_android_lifecycle/popularity)](https://pub.dev/packages/flutter_plugin_android_lifecycle/score) | [![likes](https://badges.bar/flutter_plugin_android_lifecycle/likes)](https://pub.dev/packages/flutter_plugin_android_lifecycle/score) | -| [google_maps_flutter](./packages/google_maps_flutter) | [![pub package](https://img.shields.io/pub/v/google_maps_flutter.svg)](https://pub.dev/packages/google_maps_flutter) | [![pub points](https://badges.bar/google_maps_flutter/pub%20points)](https://pub.dev/packages/google_maps_flutter/score) | [![popularity](https://badges.bar/google_maps_flutter/popularity)](https://pub.dev/packages/google_maps_flutter/score) | [![likes](https://badges.bar/google_maps_flutter/likes)](https://pub.dev/packages/google_maps_flutter/score) | -| [google_sign_in](./packages/google_sign_in/) | [![pub package](https://img.shields.io/pub/v/google_sign_in.svg)](https://pub.dev/packages/google_sign_in) | [![pub points](https://badges.bar/google_sign_in/pub%20points)](https://pub.dev/packages/google_sign_in/score) | [![popularity](https://badges.bar/google_sign_in/popularity)](https://pub.dev/packages/google_sign_in/score) | [![likes](https://badges.bar/google_sign_in/likes)](https://pub.dev/packages/google_sign_in/score) | -| [image_picker](./packages/image_picker/) | [![pub package](https://img.shields.io/pub/v/image_picker.svg)](https://pub.dev/packages/image_picker) | [![pub points](https://badges.bar/image_picker/pub%20points)](https://pub.dev/packages/image_picker/score) | [![popularity](https://badges.bar/image_picker/popularity)](https://pub.dev/packages/image_picker/score) | [![likes](https://badges.bar/image_picker/likes)](https://pub.dev/packages/image_picker/score) | -| [in_app_purchase](./packages/in_app_purchase/) | [![pub package](https://img.shields.io/pub/v/in_app_purchase.svg)](https://pub.dev/packages/in_app_purchase) | [![pub points](https://badges.bar/in_app_purchase/pub%20points)](https://pub.dev/packages/in_app_purchase/score) | [![popularity](https://badges.bar/in_app_purchase/popularity)](https://pub.dev/packages/in_app_purchase/score) | [![likes](https://badges.bar/in_app_purchase/likes)](https://pub.dev/packages/in_app_purchase/score) | -| [ios_platform_images](./packages/ios_platform_images/) | [![pub package](https://img.shields.io/pub/v/ios_platform_images.svg)](https://pub.dev/packages/ios_platform_images) | [![pub points](https://badges.bar/ios_platform_images/pub%20points)](https://pub.dev/packages/ios_platform_images/score) | [![popularity](https://badges.bar/ios_platform_images/popularity)](https://pub.dev/packages/ios_platform_images/score) | [![likes](https://badges.bar/ios_platform_images/likes)](https://pub.dev/packages/ios_platform_images/score) | -| [local_auth](./packages/local_auth/) | [![pub package](https://img.shields.io/pub/v/local_auth.svg)](https://pub.dev/packages/local_auth) | [![pub points](https://badges.bar/local_auth/pub%20points)](https://pub.dev/packages/local_auth/score) | [![popularity](https://badges.bar/local_auth/popularity)](https://pub.dev/packages/local_auth/score) | [![likes](https://badges.bar/local_auth/likes)](https://pub.dev/packages/local_auth/score) | -| [path_provider](./packages/path_provider/) | [![pub package](https://img.shields.io/pub/v/path_provider.svg)](https://pub.dev/packages/path_provider) | [![pub points](https://badges.bar/path_provider/pub%20points)](https://pub.dev/packages/path_provider/score) | [![popularity](https://badges.bar/path_provider/popularity)](https://pub.dev/packages/path_provider/score) | [![likes](https://badges.bar/path_provider/likes)](https://pub.dev/packages/path_provider/score) | -| [plugin_platform_interface](./packages/plugin_platform_interface/) | [![pub package](https://img.shields.io/pub/v/plugin_platform_interface.svg)](https://pub.dev/packages/plugin_platform_interface) | [![pub points](https://badges.bar/plugin_platform_interface/pub%20points)](https://pub.dev/packages/plugin_platform_interface/score) | [![popularity](https://badges.bar/plugin_platform_interface/popularity)](https://pub.dev/packages/plugin_platform_interface/score) | [![likes](https://badges.bar/plugin_platform_interface/likes)](https://pub.dev/packages/plugin_platform_interface/score) | -| [quick_actions](./packages/quick_actions/) | [![pub package](https://img.shields.io/pub/v/quick_actions.svg)](https://pub.dev/packages/quick_actions) | [![pub points](https://badges.bar/quick_actions/pub%20points)](https://pub.dev/packages/quick_actions/score) | [![popularity](https://badges.bar/quick_actions/popularity)](https://pub.dev/packages/quick_actions/score) | [![likes](https://badges.bar/quick_actions/likes)](https://pub.dev/packages/quick_actions/score) | -| [shared_preferences](./packages/shared_preferences/) | [![pub package](https://img.shields.io/pub/v/shared_preferences.svg)](https://pub.dev/packages/shared_preferences) | [![pub points](https://badges.bar/shared_preferences/pub%20points)](https://pub.dev/packages/shared_preferences/score) | [![popularity](https://badges.bar/shared_preferences/popularity)](https://pub.dev/packages/shared_preferences/score) | [![likes](https://badges.bar/shared_preferences/likes)](https://pub.dev/packages/shared_preferences/score) | -| [url_launcher](./packages/url_launcher/) | [![pub package](https://img.shields.io/pub/v/url_launcher.svg)](https://pub.dev/packages/url_launcher) | [![pub points](https://badges.bar/url_launcher/pub%20points)](https://pub.dev/packages/url_launcher/score) | [![popularity](https://badges.bar/url_launcher/popularity)](https://pub.dev/packages/url_launcher/score) | [![likes](https://badges.bar/url_launcher/likes)](https://pub.dev/packages/url_launcher/score) | -| [video_player](./packages/video_player/) | [![pub package](https://img.shields.io/pub/v/video_player.svg)](https://pub.dev/packages/video_player) | [![pub points](https://badges.bar/video_player/pub%20points)](https://pub.dev/packages/video_player/score) | [![popularity](https://badges.bar/video_player/popularity)](https://pub.dev/packages/video_player/score) | [![likes](https://badges.bar/video_player/likes)](https://pub.dev/packages/video_player/score) | -| [webview_flutter](./packages/webview_flutter/) | [![pub package](https://img.shields.io/pub/v/webview_flutter.svg)](https://pub.dev/packages/webview_flutter) | [![pub points](https://badges.bar/webview_flutter/pub%20points)](https://pub.dev/packages/webview_flutter/score) | [![popularity](https://badges.bar/webview_flutter/popularity)](https://pub.dev/packages/webview_flutter/score) | [![likes](https://badges.bar/webview_flutter/likes)](https://pub.dev/packages/webview_flutter/score) | +| Plugin | Pub | Points | Popularity | Likes | Issues | Pull requests | +|--------|-----|--------|------------|-------|--------|---------------| +| [camera](./packages/camera/) | [![pub package](https://img.shields.io/pub/v/camera.svg)](https://pub.dev/packages/camera) | [![pub points](https://badges.bar/camera/pub%20points)](https://pub.dev/packages/camera/score) | [![popularity](https://badges.bar/camera/popularity)](https://pub.dev/packages/camera/score) | [![likes](https://badges.bar/camera/likes)](https://pub.dev/packages/camera/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20camera?label=)](https://github.com/flutter/flutter/labels/p%3A%20camera) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20camera?label=)](https://github.com/flutter/plugins/labels/p%3A%20camera) | +| [espresso](./packages/espresso/) | [![pub package](https://img.shields.io/pub/v/espresso.svg)](https://pub.dev/packages/espresso) | [![pub points](https://badges.bar/espresso/pub%20points)](https://pub.dev/packages/espresso/score) | [![popularity](https://badges.bar/espresso/popularity)](https://pub.dev/packages/espresso/score) | [![likes](https://badges.bar/espresso/likes)](https://pub.dev/packages/espresso/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20espresso?label=)](https://github.com/flutter/flutter/labels/p%3A%20espresso) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20espresso?label=)](https://github.com/flutter/plugins/labels/p%3A%20espresso) | +| [file_selector](./packages/file_selector/) | [![pub package](https://img.shields.io/pub/v/file_selector.svg)](https://pub.dev/packages/file_selector) | [![pub points](https://badges.bar/file_selector/pub%20points)](https://pub.dev/packages/file_selector/score) | [![popularity](https://badges.bar/file_selector/popularity)](https://pub.dev/packages/file_selector/score) | [![likes](https://badges.bar/file_selector/likes)](https://pub.dev/packages/file_selector/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20file_selector?label=)](https://github.com/flutter/flutter/labels/p%3A%20file_selector) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20file_selector?label=)](https://github.com/flutter/plugins/labels/p%3A%20file_selector) | +| [flutter_plugin_android_lifecycle](./packages/flutter_plugin_android_lifecycle/) | [![pub package](https://img.shields.io/pub/v/flutter_plugin_android_lifecycle.svg)](https://pub.dev/packages/flutter_plugin_android_lifecycle) | [![pub points](https://badges.bar/flutter_plugin_android_lifecycle/pub%20points)](https://pub.dev/packages/flutter_plugin_android_lifecycle/score) | [![popularity](https://badges.bar/flutter_plugin_android_lifecycle/popularity)](https://pub.dev/packages/flutter_plugin_android_lifecycle/score) | [![likes](https://badges.bar/flutter_plugin_android_lifecycle/likes)](https://pub.dev/packages/flutter_plugin_android_lifecycle/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20flutter_plugin_android_lifecycle?label=)](https://github.com/flutter/flutter/labels/p%3A%20flutter_plugin_android_lifecycle) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20flutter_plugin_android_lifecycle?label=)](https://github.com/flutter/plugins/labels/p%3A%20flutter_plugin_android_lifecycle) | +| [google_maps_flutter](./packages/google_maps_flutter) | [![pub package](https://img.shields.io/pub/v/google_maps_flutter.svg)](https://pub.dev/packages/google_maps_flutter) | [![pub points](https://badges.bar/google_maps_flutter/pub%20points)](https://pub.dev/packages/google_maps_flutter/score) | [![popularity](https://badges.bar/google_maps_flutter/popularity)](https://pub.dev/packages/google_maps_flutter/score) | [![likes](https://badges.bar/google_maps_flutter/likes)](https://pub.dev/packages/google_maps_flutter/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20maps?label=)](https://github.com/flutter/flutter/labels/p%3A%20maps) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20google_maps_flutter?label=)](https://github.com/flutter/plugins/labels/p%3A%20google_maps_flutter) | +| [google_sign_in](./packages/google_sign_in/) | [![pub package](https://img.shields.io/pub/v/google_sign_in.svg)](https://pub.dev/packages/google_sign_in) | [![pub points](https://badges.bar/google_sign_in/pub%20points)](https://pub.dev/packages/google_sign_in/score) | [![popularity](https://badges.bar/google_sign_in/popularity)](https://pub.dev/packages/google_sign_in/score) | [![likes](https://badges.bar/google_sign_in/likes)](https://pub.dev/packages/google_sign_in/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20google_sign_in?label=)](https://github.com/flutter/flutter/labels/p%3A%20google_sign_in) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20google_sign_in?label=)](https://github.com/flutter/plugins/labels/p%3A%20google_sign_in) | +| [image_picker](./packages/image_picker/) | [![pub package](https://img.shields.io/pub/v/image_picker.svg)](https://pub.dev/packages/image_picker) | [![pub points](https://badges.bar/image_picker/pub%20points)](https://pub.dev/packages/image_picker/score) | [![popularity](https://badges.bar/image_picker/popularity)](https://pub.dev/packages/image_picker/score) | [![likes](https://badges.bar/image_picker/likes)](https://pub.dev/packages/image_picker/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20image_picker?label=)](https://github.com/flutter/flutter/labels/p%3A%20image_picker) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20image_picker?label=)](https://github.com/flutter/plugins/labels/p%3A%20image_picker) | +| [in_app_purchase](./packages/in_app_purchase/) | [![pub package](https://img.shields.io/pub/v/in_app_purchase.svg)](https://pub.dev/packages/in_app_purchase) | [![pub points](https://badges.bar/in_app_purchase/pub%20points)](https://pub.dev/packages/in_app_purchase/score) | [![popularity](https://badges.bar/in_app_purchase/popularity)](https://pub.dev/packages/in_app_purchase/score) | [![likes](https://badges.bar/in_app_purchase/likes)](https://pub.dev/packages/in_app_purchase/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20in_app_purchase?label=)](https://github.com/flutter/flutter/labels/p%3A%20in_app_purchase) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20in_app_purchase?label=)](https://github.com/flutter/plugins/labels/p%3A%20in_app_purchase) | +| [ios_platform_images](./packages/ios_platform_images/) | [![pub package](https://img.shields.io/pub/v/ios_platform_images.svg)](https://pub.dev/packages/ios_platform_images) | [![pub points](https://badges.bar/ios_platform_images/pub%20points)](https://pub.dev/packages/ios_platform_images/score) | [![popularity](https://badges.bar/ios_platform_images/popularity)](https://pub.dev/packages/ios_platform_images/score) | [![likes](https://badges.bar/ios_platform_images/likes)](https://pub.dev/packages/ios_platform_images/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20ios_platform_images?label=)](https://github.com/flutter/flutter/labels/p%3A%20ios_platform_images) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20ios_platform_images?label=)](https://github.com/flutter/plugins/labels/p%3A%20ios_platform_images) | +| [local_auth](./packages/local_auth/) | [![pub package](https://img.shields.io/pub/v/local_auth.svg)](https://pub.dev/packages/local_auth) | [![pub points](https://badges.bar/local_auth/pub%20points)](https://pub.dev/packages/local_auth/score) | [![popularity](https://badges.bar/local_auth/popularity)](https://pub.dev/packages/local_auth/score) | [![likes](https://badges.bar/local_auth/likes)](https://pub.dev/packages/local_auth/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20local_auth?label=)](https://github.com/flutter/flutter/labels/p%3A%20local_auth) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20local_auth?label=)](https://github.com/flutter/plugins/labels/p%3A%20local_auth) | +| [path_provider](./packages/path_provider/) | [![pub package](https://img.shields.io/pub/v/path_provider.svg)](https://pub.dev/packages/path_provider) | [![pub points](https://badges.bar/path_provider/pub%20points)](https://pub.dev/packages/path_provider/score) | [![popularity](https://badges.bar/path_provider/popularity)](https://pub.dev/packages/path_provider/score) | [![likes](https://badges.bar/path_provider/likes)](https://pub.dev/packages/path_provider/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20path_provider?label=)](https://github.com/flutter/flutter/labels/p%3A%20path_provider) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20path_provider?label=)](https://github.com/flutter/plugins/labels/p%3A%20path_provider) | +| [plugin_platform_interface](./packages/plugin_platform_interface/) | [![pub package](https://img.shields.io/pub/v/plugin_platform_interface.svg)](https://pub.dev/packages/plugin_platform_interface) | [![pub points](https://badges.bar/plugin_platform_interface/pub%20points)](https://pub.dev/packages/plugin_platform_interface/score) | [![popularity](https://badges.bar/plugin_platform_interface/popularity)](https://pub.dev/packages/plugin_platform_interface/score) | [![likes](https://badges.bar/plugin_platform_interface/likes)](https://pub.dev/packages/plugin_platform_interface/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20plugin_platform_interface?label=)](https://github.com/flutter/flutter/labels/p%3A%20plugin_platform_interface) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20plugin_platform_interface?label=)](https://github.com/flutter/plugins/labels/p%3A%20plugin_platform_interface) | +| [quick_actions](./packages/quick_actions/) | [![pub package](https://img.shields.io/pub/v/quick_actions.svg)](https://pub.dev/packages/quick_actions) | [![pub points](https://badges.bar/quick_actions/pub%20points)](https://pub.dev/packages/quick_actions/score) | [![popularity](https://badges.bar/quick_actions/popularity)](https://pub.dev/packages/quick_actions/score) | [![likes](https://badges.bar/quick_actions/likes)](https://pub.dev/packages/quick_actions/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20quick_actions?label=)](https://github.com/flutter/flutter/labels/p%3A%20quick_actions) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20quick_actions?label=)](https://github.com/flutter/plugins/labels/p%3A%20quick_actions) | +| [shared_preferences](./packages/shared_preferences/) | [![pub package](https://img.shields.io/pub/v/shared_preferences.svg)](https://pub.dev/packages/shared_preferences) | [![pub points](https://badges.bar/shared_preferences/pub%20points)](https://pub.dev/packages/shared_preferences/score) | [![popularity](https://badges.bar/shared_preferences/popularity)](https://pub.dev/packages/shared_preferences/score) | [![likes](https://badges.bar/shared_preferences/likes)](https://pub.dev/packages/shared_preferences/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20shared_preferences?label=)](https://github.com/flutter/flutter/labels/p%3A%20shared_preferences) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20shared_preferences?label=)](https://github.com/flutter/plugins/labels/p%3A%20shared_preferences) | +| [url_launcher](./packages/url_launcher/) | [![pub package](https://img.shields.io/pub/v/url_launcher.svg)](https://pub.dev/packages/url_launcher) | [![pub points](https://badges.bar/url_launcher/pub%20points)](https://pub.dev/packages/url_launcher/score) | [![popularity](https://badges.bar/url_launcher/popularity)](https://pub.dev/packages/url_launcher/score) | [![likes](https://badges.bar/url_launcher/likes)](https://pub.dev/packages/url_launcher/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20url_launcher?label=)](https://github.com/flutter/flutter/labels/p%3A%20url_launcher) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20url_launcher?label=)](https://github.com/flutter/plugins/labels/p%3A%20url_launcher) | +| [video_player](./packages/video_player/) | [![pub package](https://img.shields.io/pub/v/video_player.svg)](https://pub.dev/packages/video_player) | [![pub points](https://badges.bar/video_player/pub%20points)](https://pub.dev/packages/video_player/score) | [![popularity](https://badges.bar/video_player/popularity)](https://pub.dev/packages/video_player/score) | [![likes](https://badges.bar/video_player/likes)](https://pub.dev/packages/video_player/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20video_player?label=)](https://github.com/flutter/flutter/labels/p%3A%20video_player) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20video_player?label=)](https://github.com/flutter/plugins/labels/p%3A%20video_player) | +| [webview_flutter](./packages/webview_flutter/) | [![pub package](https://img.shields.io/pub/v/webview_flutter.svg)](https://pub.dev/packages/webview_flutter) | [![pub points](https://badges.bar/webview_flutter/pub%20points)](https://pub.dev/packages/webview_flutter/score) | [![popularity](https://badges.bar/webview_flutter/popularity)](https://pub.dev/packages/webview_flutter/score) | [![likes](https://badges.bar/webview_flutter/likes)](https://pub.dev/packages/webview_flutter/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20webview?label=)](https://github.com/flutter/flutter/labels/p%3A%20webview) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20webview_flutter?label=)](https://github.com/flutter/plugins/labels/p%3A%20webview_flutter) | From 7960ff2d6707f885ce6b4737b65e83ceea121a9d Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 6 May 2022 14:44:12 -0400 Subject: [PATCH 526/600] Roll Flutter from 2f3f053c0dfb to ac30842c8259 (13 revisions) (#5650) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 8fe5ef2b1e32..cfb6754642fb 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2f3f053c0dfb2101d2e3466144ef50f7836b47b6 +ac30842c82596539e6f04c5aac86dfedb3904e3a From 3eba6615700c88cd5846a0a76da06a04ac3cd9bc Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 6 May 2022 20:19:13 -0400 Subject: [PATCH 527/600] Roll Flutter from ac30842c8259 to e5718354020e (4 revisions) (#5652) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index cfb6754642fb..8a369244f133 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ac30842c82596539e6f04c5aac86dfedb3904e3a +e5718354020e9245e1a7b2cfd2486bfb424a826e From bc5acb9a25a970a9e72a31fc4e923fa9ba522c9b Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 6 May 2022 21:14:13 -0400 Subject: [PATCH 528/600] [local_auth] Overhaul README, and fix `error_codes.dart` visibility (#5653) --- packages/local_auth/local_auth/CHANGELOG.md | 11 +- packages/local_auth/local_auth/README.md | 290 ++++++++++-------- .../local_auth/example/build.excerpt.yaml | 15 + .../example/lib/readme_excerpts.dart | 164 ++++++++++ .../local_auth/example/pubspec.yaml | 1 + .../lib/{src/types => }/error_codes.dart | 8 +- .../local_auth/lib/src/local_auth.dart | 1 - packages/local_auth/local_auth/pubspec.yaml | 2 +- script/configs/temp_exclude_excerpt.yaml | 1 - 9 files changed, 351 insertions(+), 142 deletions(-) create mode 100644 packages/local_auth/local_auth/example/build.excerpt.yaml create mode 100644 packages/local_auth/local_auth/example/lib/readme_excerpts.dart rename packages/local_auth/local_auth/lib/{src/types => }/error_codes.dart (73%) diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index c495acae5a81..570d8eef7b9b 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -1,5 +1,8 @@ -## NEXT +## 2.0.1 +* Restores the ability to import `error_codes.dart`. +* Updates README to match API changes in 2.0, and to improve clarity in + general. * Removes unnecessary imports. ## 2.0.0 @@ -9,11 +12,11 @@ * BREAKING CHANGE: Deprecated method `authenticateWithBiometrics` has been removed. Use `authenticate` instead. * BREAKING CHANGE: Enum `BiometricType` has been expanded with options for `strong` and `weak`, - and applications should be updated to handle these accordingly. + and applications should be updated to handle these accordingly. * BREAKING CHANGE: Parameters of `authenticate` have been changed. - + Example: - ```dart + ```dart // Old way of calling `authenticate`. Future authenticate( localizedReason: 'localized reason', diff --git a/packages/local_auth/local_auth/README.md b/packages/local_auth/local_auth/README.md index 3a60e45a57eb..7ec4829ba521 100644 --- a/packages/local_auth/local_auth/README.md +++ b/packages/local_auth/local_auth/README.md @@ -1,10 +1,12 @@ # local_auth + + This Flutter plugin provides means to perform local, on-device authentication of the user. -This means referring to biometric authentication on iOS (Touch ID or lock code) -and the fingerprint APIs on Android (introduced in Android 6.0). +On supported devices, this includes authentication with biometrics such as +fingerprint or facial recognition. | | Android | iOS | |-------------|-----------|------| @@ -12,137 +14,175 @@ and the fingerprint APIs on Android (introduced in Android 6.0). ## Usage -Import the relevant file: - -```dart -import 'package:local_auth/local_auth.dart'; -``` +### Device Capabilities -To check whether there is local authentication available on this device or not, call canCheckBiometrics: +To check whether there is local authentication available on this device or not, +call `canCheckBiometrics` (if you need biometrics support) and/or +`isDeviceSupported()` (if you just need some device-level authentication): + ```dart -bool canCheckBiometrics = - await localAuth.canCheckBiometrics; +import 'package:local_auth/local_auth.dart'; +// ··· + final LocalAuthentication auth = LocalAuthentication(); + // ··· + final bool canAuthenticateWithBiometrics = await auth.canCheckBiometrics; + final bool canAuthenticate = + canAuthenticateWithBiometrics || await auth.isDeviceSupported(); ``` Currently the following biometric types are implemented: - BiometricType.face - BiometricType.fingerprint +- BiometricType.weak +- BiometricType.strong + +### Enrolled Biometrics + +`canCheckBiometrics` only indicates whether hardware support is available, not +whether the device has any biometrics enrolled. To get a list of enrolled +biometrics, call `getAvailableBiometrics()`. -To get a list of enrolled biometrics, call getAvailableBiometrics: +The types are device-specific and platform-specific, and other types may be +added in the future, so when possible you should not rely on specific biometric +types and only check that some biometric is enrolled: + ```dart -List availableBiometrics = +final List availableBiometrics = await auth.getAvailableBiometrics(); -if (Platform.isIOS) { - if (availableBiometrics.contains(BiometricType.face)) { - // Face ID. - } else if (availableBiometrics.contains(BiometricType.fingerprint)) { - // Touch ID. - } +if (availableBiometrics.isNotEmpty) { + // Some biometrics are enrolled. } -``` - -We have default dialogs with an 'OK' button to show authentication error -messages for the following 2 cases: - -1. Passcode/PIN/Pattern Not Set. The user has not yet configured a passcode on - iOS or PIN/pattern on Android. -2. Touch ID/Fingerprint Not Enrolled. The user has not enrolled any - fingerprints on the device. -Which means, if there's no fingerprint on the user's device, a dialog with -instructions will pop up to let the user set up fingerprint. If the user clicks -'OK' button, it will return 'false'. +if (availableBiometrics.contains(BiometricType.strong) || + availableBiometrics.contains(BiometricType.face)) { + // Specific types of biometrics are available. + // Use checks like this with caution! +} +``` -Use the exported APIs to trigger local authentication with default dialogs: +### Options -The `authenticate()` method uses biometric authentication, but also allows -users to use pin, pattern, or passcode. +The `authenticate()` method uses biometric authentication when possible, but +also allows fallback to pin, pattern, or passcode. + ```dart -var localAuth = LocalAuthentication(); -bool didAuthenticate = - await localAuth.authenticate( - localizedReason: 'Please authenticate to show account balance'); +try { + final bool didAuthenticate = await auth.authenticate( + localizedReason: 'Please authenticate to show account balance'); + // ··· +} on PlatformException { + // ... +} ``` -To authenticate using biometric authentication only, set `biometricOnly` to `true`. +To require biometric authentication, pass `AuthenticationOptions` with +`biometricOnly` set to `true`. + ```dart -var localAuth = LocalAuthentication(); -bool didAuthenticate = - await localAuth.authenticate( - localizedReason: 'Please authenticate to show account balance', - biometricOnly: true); +final bool didAuthenticate = await auth.authenticate( + localizedReason: 'Please authenticate to show account balance', + options: const AuthenticationOptions(biometricOnly: true)); ``` -If you don't want to use the default dialogs, call this API with -'useErrorDialogs = false'. In this case, it will throw the error message back -and you need to handle them in your dart code: - -```dart -bool didAuthenticate = - await localAuth.authenticate( - localizedReason: 'Please authenticate to show account balance', - useErrorDialogs: false); -``` +#### Dialogs -You can use our default dialog messages, or you can use your own messages by -passing in IOSAuthMessages and AndroidAuthMessages: +The plugin provides default dialogs for the following cases: -```dart -import 'package:local_auth/auth_strings.dart'; - -const iosStrings = const IOSAuthMessages( - cancelButton: 'cancel', - goToSettingsButton: 'settings', - goToSettingsDescription: 'Please set up your Touch ID.', - lockOut: 'Please reenable your Touch ID'); -await localAuth.authenticate( - localizedReason: 'Please authenticate to show account balance', - useErrorDialogs: false, - iOSAuthStrings: iosStrings); +1. Passcode/PIN/Pattern Not Set: The user has not yet configured a passcode on + iOS or PIN/pattern on Android. +2. Biometrics Not Enrolled: The user has not enrolled any biometrics on the + device. -``` +If a user does not have the necessary authentication enrolled when +`authenticate` is called, they will be given the option to enroll at that point, +or cancel authentication. -If needed, you can manually stop authentication for android: +If you don't want to use the default dialogs, set the `useErrorDialogs` option +to `false` to have `authenticate` immediately return an error in those cases. + ```dart +import 'package:local_auth/error_codes.dart' as auth_error; +// ··· + try { + final bool didAuthenticate = await auth.authenticate( + localizedReason: 'Please authenticate to show account balance', + options: const AuthenticationOptions(useErrorDialogs: false)); + // ··· + } on PlatformException catch (e) { + if (e.code == auth_error.notAvailable) { + // Add handling of no hardware here. + } else if (e.code == auth_error.notEnrolled) { + // ... + } else { + // ... + } + } +``` -void _cancelAuthentication() { - localAuth.stopAuthentication(); -} +If you want to customize the messages in the dialogs, you can pass +`AuthMessages` for each platform you support. These are platform-specific, so +you will need to import the platform-specific implementation packages. For +instance, to customize Android and iOS: + +```dart +import 'package:local_auth_android/local_auth_android.dart'; +import 'package:local_auth_ios/local_auth_ios.dart'; +// ··· + final bool didAuthenticate = await auth.authenticate( + localizedReason: 'Please authenticate to show account balance', + authMessages: const [ + AndroidAuthMessages( + signInTitle: 'Oops! Biometric authentication required!', + cancelButton: 'No thanks', + ), + IOSAuthMessages( + cancelButton: 'No thanks', + ), + ]); ``` +See the platform-specific classes for details about what can be customized on +each platform. + ### Exceptions -There are 6 types of exceptions: PasscodeNotSet, NotEnrolled, NotAvailable, OtherOperatingSystem, LockedOut and PermanentlyLockedOut. -They are wrapped in LocalAuthenticationError class. You can -catch the exception and handle them by different types. For example: +`authenticate` throws `PlatformException`s in many error cases. See +`error_codes.dart` for known error codes that you may want to have specific +handling for. For example: + ```dart import 'package:flutter/services.dart'; import 'package:local_auth/error_codes.dart' as auth_error; - -try { - bool didAuthenticate = await local_auth.authenticate( - localizedReason: 'Please authenticate to show account balance'); -} on PlatformException catch (e) { - if (e.code == auth_error.notAvailable) { - // Handle this exception here. - } -} +import 'package:local_auth/local_auth.dart'; +// ··· + final LocalAuthentication auth = LocalAuthentication(); + // ··· + try { + final bool didAuthenticate = await auth.authenticate( + localizedReason: 'Please authenticate to show account balance', + options: const AuthenticationOptions(useErrorDialogs: false)); + // ··· + } on PlatformException catch (e) { + if (e.code == auth_error.notEnrolled) { + // Add handling of no hardware here. + } else if (e.code == auth_error.lockedOut || + e.code == auth_error.permanentlyLockedOut) { + // ... + } else { + // ... + } + } ``` -### Android - -\* The plugin will build and run on SDK 16+, but `isDeviceSupported()` will -always return false before SDK 23 (Android 6.0). - ## iOS Integration Note that this plugin works with both Touch ID and Face ID. However, to use the latter, @@ -158,46 +198,39 @@ app has not been updated to use Face ID. ## Android Integration -Note that local_auth plugin requires the use of a FragmentActivity as -opposed to Activity. This can be easily done by switching to use -`FlutterFragmentActivity` as opposed to `FlutterActivity` in your -manifest (or your own Activity class if you are extending the base class). - -Update your MainActivity.java: - -```java -import android.os.Bundle; -import io.flutter.app.FlutterFragmentActivity; -import io.flutter.plugins.flutter_plugin_android_lifecycle.FlutterAndroidLifecyclePlugin; -import io.flutter.plugins.localauth.LocalAuthPlugin; - -public class MainActivity extends FlutterFragmentActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - FlutterAndroidLifecyclePlugin.registerWith( - registrarFor( - "io.flutter.plugins.flutter_plugin_android_lifecycle.FlutterAndroidLifecyclePlugin")); - LocalAuthPlugin.registerWith(registrarFor("io.flutter.plugins.localauth.LocalAuthPlugin")); - } -} -``` +\* The plugin will build and run on SDK 16+, but `isDeviceSupported()` will +always return false before SDK 23 (Android 6.0). -OR +### Activity Changes -Update your MainActivity.kt: +Note that `local_auth` requires the use of a `FragmentActivity` instead of an +`Activity`. To update your application: -```kotlin -import io.flutter.embedding.android.FlutterFragmentActivity -import io.flutter.embedding.engine.FlutterEngine -import io.flutter.plugins.GeneratedPluginRegistrant +* If you are using `FlutterActivity` directly, change it to +`FlutterFragmentActivity` in your `AndroidManifest.xml`. +* If you are using a custom activity, update your `MainActivity.java`: -class MainActivity: FlutterFragmentActivity() { - override fun configureFlutterEngine(flutterEngine: FlutterEngine) { - GeneratedPluginRegistrant.registerWith(flutterEngine) + ```java + import io.flutter.embedding.android.FlutterFragmentActivity; + + public class MainActivity extends FlutterFragmentActivity { + // ... } -} -``` + ``` + + or MainActivity.kt: + + ```kotlin + import io.flutter.embedding.android.FlutterFragmentActivity + + class MainActivity: FlutterFragmentActivity() { + // ... + } + ``` + + to inherit from `FlutterFragmentActivity`. + +### Permissions Update your project's `AndroidManifest.xml` file to include the `USE_FINGERPRINT` permissions: @@ -209,6 +242,8 @@ Update your project's `AndroidManifest.xml` file to include the ``` +### Compatibility + On Android, you can check only for existence of fingerprint hardware prior to API 29 (Android Q). Therefore, if you would like to support other biometrics types (such as face scanning) and you want to support SDKs lower than Q, @@ -223,10 +258,3 @@ if the user receives a phone call before they get a chance to authenticate. With `stickyAuth` set to false, this would result in plugin returning failure result to the Dart app. If set to true, the plugin will retry authenticating when the app resumes. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). - -For help on editing plugin code, view the [documentation](https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin). diff --git a/packages/local_auth/local_auth/example/build.excerpt.yaml b/packages/local_auth/local_auth/example/build.excerpt.yaml new file mode 100644 index 000000000000..e317efa11cb3 --- /dev/null +++ b/packages/local_auth/local_auth/example/build.excerpt.yaml @@ -0,0 +1,15 @@ +targets: + $default: + sources: + include: + - lib/** + # Some default includes that aren't really used here but will prevent + # false-negative warnings: + - $package$ + - lib/$lib$ + exclude: + - '**/.*/**' + - '**/build/**' + builders: + code_excerpter|code_excerpter: + enabled: true diff --git a/packages/local_auth/local_auth/example/lib/readme_excerpts.dart b/packages/local_auth/local_auth/example/lib/readme_excerpts.dart new file mode 100644 index 000000000000..25ddfe0fa62f --- /dev/null +++ b/packages/local_auth/local_auth/example/lib/readme_excerpts.dart @@ -0,0 +1,164 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file exists solely to host compiled excerpts for README.md, and is not +// intended for use as an actual example application. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; +// #docregion ErrorHandling +import 'package:flutter/services.dart'; +// #docregion NoErrorDialogs +import 'package:local_auth/error_codes.dart' as auth_error; +// #enddocregion NoErrorDialogs +// #docregion CanCheck +import 'package:local_auth/local_auth.dart'; +// #enddocregion CanCheck +// #enddocregion ErrorHandling + +// #docregion CustomMessages +import 'package:local_auth_android/local_auth_android.dart'; +import 'package:local_auth_ios/local_auth_ios.dart'; +// #enddocregion CustomMessages + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatefulWidget { + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + // #docregion CanCheck + // #docregion ErrorHandling + final LocalAuthentication auth = LocalAuthentication(); + // #enddocregion CanCheck + // #enddocregion ErrorHandling + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('README example app'), + ), + body: const Text('See example in main.dart'), + ), + ); + } + + Future checkSupport() async { + // #docregion CanCheck + final bool canAuthenticateWithBiometrics = await auth.canCheckBiometrics; + final bool canAuthenticate = + canAuthenticateWithBiometrics || await auth.isDeviceSupported(); + // #enddocregion CanCheck + + print('Can authenticate: $canAuthenticate'); + print('Can authenticate with biometrics: $canAuthenticateWithBiometrics'); + } + + Future getEnrolledBiometrics() async { + // #docregion Enrolled + final List availableBiometrics = + await auth.getAvailableBiometrics(); + + if (availableBiometrics.isNotEmpty) { + // Some biometrics are enrolled. + } + + if (availableBiometrics.contains(BiometricType.strong) || + availableBiometrics.contains(BiometricType.face)) { + // Specific types of biometrics are available. + // Use checks like this with caution! + } + // #enddocregion Enrolled + } + + Future authenticate() async { + // #docregion AuthAny + try { + final bool didAuthenticate = await auth.authenticate( + localizedReason: 'Please authenticate to show account balance'); + // #enddocregion AuthAny + print(didAuthenticate); + // #docregion AuthAny + } on PlatformException { + // ... + } + // #enddocregion AuthAny + } + + Future authenticateWithBiometrics() async { + // #docregion AuthBioOnly + final bool didAuthenticate = await auth.authenticate( + localizedReason: 'Please authenticate to show account balance', + options: const AuthenticationOptions(biometricOnly: true)); + // #enddocregion AuthBioOnly + print(didAuthenticate); + } + + Future authenticateWithoutDialogs() async { + // #docregion NoErrorDialogs + try { + final bool didAuthenticate = await auth.authenticate( + localizedReason: 'Please authenticate to show account balance', + options: const AuthenticationOptions(useErrorDialogs: false)); + // #enddocregion NoErrorDialogs + print(didAuthenticate ? 'Success!' : 'Failure'); + // #docregion NoErrorDialogs + } on PlatformException catch (e) { + if (e.code == auth_error.notAvailable) { + // Add handling of no hardware here. + } else if (e.code == auth_error.notEnrolled) { + // ... + } else { + // ... + } + } + // #enddocregion NoErrorDialogs + } + + Future authenticateWithErrorHandling() async { + // #docregion ErrorHandling + try { + final bool didAuthenticate = await auth.authenticate( + localizedReason: 'Please authenticate to show account balance', + options: const AuthenticationOptions(useErrorDialogs: false)); + // #enddocregion ErrorHandling + print(didAuthenticate ? 'Success!' : 'Failure'); + // #docregion ErrorHandling + } on PlatformException catch (e) { + if (e.code == auth_error.notEnrolled) { + // Add handling of no hardware here. + } else if (e.code == auth_error.lockedOut || + e.code == auth_error.permanentlyLockedOut) { + // ... + } else { + // ... + } + } + // #enddocregion ErrorHandling + } + + Future authenticateWithCustomDialogMessages() async { + // #docregion CustomMessages + final bool didAuthenticate = await auth.authenticate( + localizedReason: 'Please authenticate to show account balance', + authMessages: const [ + AndroidAuthMessages( + signInTitle: 'Oops! Biometric authentication required!', + cancelButton: 'No thanks', + ), + IOSAuthMessages( + cancelButton: 'No thanks', + ), + ]); + // #enddocregion CustomMessages + print(didAuthenticate ? 'Success!' : 'Failure'); + } +} diff --git a/packages/local_auth/local_auth/example/pubspec.yaml b/packages/local_auth/local_auth/example/pubspec.yaml index ad0de2002a4c..c8496fcc0da7 100644 --- a/packages/local_auth/local_auth/example/pubspec.yaml +++ b/packages/local_auth/local_auth/example/pubspec.yaml @@ -18,6 +18,7 @@ dependencies: path: ../ dev_dependencies: + build_runner: ^2.1.10 flutter_driver: sdk: flutter integration_test: diff --git a/packages/local_auth/local_auth/lib/src/types/error_codes.dart b/packages/local_auth/local_auth/lib/error_codes.dart similarity index 73% rename from packages/local_auth/local_auth/lib/src/types/error_codes.dart rename to packages/local_auth/local_auth/lib/error_codes.dart index 3426099bacbd..15660ee948df 100644 --- a/packages/local_auth/local_auth/lib/src/types/error_codes.dart +++ b/packages/local_auth/local_auth/lib/error_codes.dart @@ -9,18 +9,18 @@ /// PIN/pattern/password (Android) on the device. const String passcodeNotSet = 'PasscodeNotSet'; -/// Indicates the user has not enrolled any fingerprints on the device. +/// Indicates the user has not enrolled any biometrics on the device. const String notEnrolled = 'NotEnrolled'; -/// Indicates the device does not have a Touch ID/fingerprint scanner. +/// Indicates the device does not have hardware support for biometrics. const String notAvailable = 'NotAvailable'; /// Indicates the device operating system is unsupported. const String otherOperatingSystem = 'OtherOperatingSystem'; -/// Indicates the API lock out due to too many attempts. +/// Indicates the API is temporarily locked out due to too many attempts. const String lockedOut = 'LockedOut'; -/// Indicates the API being disabled due to too many lock outs. +/// Indicates the API is locked out more persistently than [lockedOut]. /// Strong authentication like PIN/Pattern/Password is required to unlock. const String permanentlyLockedOut = 'PermanentlyLockedOut'; diff --git a/packages/local_auth/local_auth/lib/src/local_auth.dart b/packages/local_auth/local_auth/lib/src/local_auth.dart index 77db4d57f018..206bd04f7b32 100644 --- a/packages/local_auth/local_auth/lib/src/local_auth.dart +++ b/packages/local_auth/local_auth/lib/src/local_auth.dart @@ -11,7 +11,6 @@ import 'dart:async'; import 'package:flutter/services.dart'; -import 'package:local_auth/src/types/error_codes.dart'; import 'package:local_auth_android/local_auth_android.dart'; import 'package:local_auth_ios/local_auth_ios.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; diff --git a/packages/local_auth/local_auth/pubspec.yaml b/packages/local_auth/local_auth/pubspec.yaml index fa055fab17f8..087b84920bb9 100644 --- a/packages/local_auth/local_auth/pubspec.yaml +++ b/packages/local_auth/local_auth/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Android and iOS devices to allow local authentication via fingerprint, touch ID, face ID, passcode, pin, or pattern. repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 2.0.0 +version: 2.0.1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/script/configs/temp_exclude_excerpt.yaml b/script/configs/temp_exclude_excerpt.yaml index fc8454d75a0c..bd73880ee5ee 100644 --- a/script/configs/temp_exclude_excerpt.yaml +++ b/script/configs/temp_exclude_excerpt.yaml @@ -15,7 +15,6 @@ - image_picker_for_web - in_app_purchase/in_app_purchase - ios_platform_images -- local_auth/local_auth - path_provider/path_provider - plugin_platform_interface - quick_actions/quick_actions From b5b88c949dafd36c669d3e6c02a3e768f0874a1c Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 6 May 2022 21:59:12 -0400 Subject: [PATCH 529/600] Roll Flutter from e5718354020e to 47f48e4888e4 (5 revisions) (#5654) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 8a369244f133..5eca3dc9d3fb 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -e5718354020e9245e1a7b2cfd2486bfb424a826e +47f48e4888e406436f1c9732e5ce7147e6401ea2 From 166ae43cae65a07d81d27d3497cf6ae1b271f398 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 6 May 2022 23:04:12 -0400 Subject: [PATCH 530/600] Roll Flutter from 47f48e4888e4 to 8e532db7b6ff (1 revision) (#5655) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 5eca3dc9d3fb..2ed7c491b124 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -47f48e4888e406436f1c9732e5ce7147e6401ea2 +8e532db7b6ff7758f4fb0d1be6060d26222a1fca From c46bb0e6d73b40fe1b2d1bf62f9d28697c54e5bc Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 7 May 2022 00:09:14 -0400 Subject: [PATCH 531/600] Roll Flutter from 8e532db7b6ff to cc9ac0787871 (1 revision) (#5656) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 2ed7c491b124..20ac900f049f 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -8e532db7b6ff7758f4fb0d1be6060d26222a1fca +cc9ac078787187b35cba5d3f65223ca682528271 From dbd0c99aa46e67949ab4fe53a9bd29bb5c9e127d Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 7 May 2022 01:14:10 -0400 Subject: [PATCH 532/600] Roll Flutter from cc9ac0787871 to f56c0b3bbe69 (1 revision) (#5657) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 20ac900f049f..c76300c1d811 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -cc9ac078787187b35cba5d3f65223ca682528271 +f56c0b3bbe6974982d2602a6329f5c316ef94f37 From caa5475ec359721654b1fd543a69b30e0f1559b6 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 7 May 2022 02:19:12 -0400 Subject: [PATCH 533/600] Roll Flutter from f56c0b3bbe69 to d01b0f5d23f9 (1 revision) (#5658) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index c76300c1d811..6e9590186026 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -f56c0b3bbe6974982d2602a6329f5c316ef94f37 +d01b0f5d23f982da3d79452e281dad24818196f6 From 9db48b8cef95397bcdbd92c748a1988461b3a79a Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 7 May 2022 04:29:12 -0400 Subject: [PATCH 534/600] Roll Flutter from d01b0f5d23f9 to b05b44e88ad9 (3 revisions) (#5660) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 6e9590186026..383e106d95c2 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -d01b0f5d23f982da3d79452e281dad24818196f6 +b05b44e88ad9cf4c0b383fed554561718bae4c3a From 1c646b6193c5fa682537db7af7a789e4aa037cb6 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 7 May 2022 06:39:09 -0400 Subject: [PATCH 535/600] Roll Flutter from b05b44e88ad9 to 90868d3ba33a (3 revisions) (#5662) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 383e106d95c2..3d826fc7993b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b05b44e88ad9cf4c0b383fed554561718bae4c3a +90868d3ba33a3571fb3a8918472dfa736ba5bea7 From d8e8bb5a2ab91afe884791c66ed31bba434aa947 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 7 May 2022 10:59:09 -0400 Subject: [PATCH 536/600] Roll Flutter from 90868d3ba33a to ae7fcc7e51f6 (2 revisions) (#5663) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 3d826fc7993b..c370f81e1635 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -90868d3ba33a3571fb3a8918472dfa736ba5bea7 +ae7fcc7e51f604ce615bc4b2b0930091f720910e From 32f2476aca4c08d0c615a8f21fdc81afe9301832 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 7 May 2022 12:04:07 -0400 Subject: [PATCH 537/600] Roll Flutter from ae7fcc7e51f6 to c18097178c91 (1 revision) (#5664) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index c370f81e1635..c459b8fc5cc7 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ae7fcc7e51f604ce615bc4b2b0930091f720910e +c18097178c91e8440db2daf9ff56a0b48ca0cee8 From c5c319aa2524583fca09a6c4ea151bf295979e67 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 7 May 2022 13:09:07 -0400 Subject: [PATCH 538/600] Roll Flutter from c18097178c91 to c6ced845e389 (2 revisions) (#5665) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index c459b8fc5cc7..0b4741df25f1 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c18097178c91e8440db2daf9ff56a0b48ca0cee8 +c6ced845e389d4ba2778007642ef439d7e6654c9 From fc0dd3352b0569753a2e279a40d525dc77eb2ff7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 9 May 2022 12:49:07 -0400 Subject: [PATCH 539/600] Roll Flutter from c6ced845e389 to 4bed76757db7 (1 revision) (#5666) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 0b4741df25f1..40188c21bd43 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c6ced845e389d4ba2778007642ef439d7e6654c9 +4bed76757db7a4eea74d77d5a63804ef52198f37 From 4b7b67916242a9c82283a642f169fbdb7f9045be Mon Sep 17 00:00:00 2001 From: Ahmed Ashour Date: Mon, 9 May 2022 20:54:11 +0200 Subject: [PATCH 540/600] Enable lints `library_private_types_in_public_api`, `sort_child_properties_last` and `use_key_in_widget_constructors` (#5428) --- analysis_options.yaml | 5 +- packages/camera/camera/CHANGELOG.md | 4 +- packages/camera/camera/README.md | 14 ++- packages/camera/camera/example/lib/main.dart | 60 +++++----- .../example/lib/readme_full_example.dart | 16 +-- .../camera/camera/example/test/main_test.dart | 2 +- .../camera/camera/lib/src/camera_preview.dart | 3 +- packages/camera/camera/pubspec.yaml | 2 +- packages/camera/camera_web/CHANGELOG.md | 5 + .../camera/camera_web/example/lib/main.dart | 5 +- packages/camera/camera_web/pubspec.yaml | 2 +- packages/camera/camera_windows/CHANGELOG.md | 4 +- .../camera_windows/example/lib/main.dart | 5 +- packages/camera/camera_windows/pubspec.yaml | 2 +- packages/espresso/CHANGELOG.md | 7 +- packages/espresso/example/lib/main.dart | 7 +- packages/espresso/pubspec.yaml | 2 +- .../file_selector/file_selector/CHANGELOG.md | 4 +- .../example/lib/get_directory_page.dart | 5 +- .../file_selector/example/lib/home_page.dart | 3 + .../file_selector/example/lib/main.dart | 16 ++- .../example/lib/open_image_page.dart | 6 +- .../lib/open_multiple_images_page.dart | 5 +- .../example/lib/open_text_page.dart | 6 +- .../example/lib/save_text_page.dart | 3 + .../file_selector/file_selector/pubspec.yaml | 2 +- .../file_selector_macos/CHANGELOG.md | 4 +- .../example/lib/get_directory_page.dart | 5 +- .../example/lib/home_page.dart | 3 + .../file_selector_macos/example/lib/main.dart | 16 ++- .../example/lib/open_image_page.dart | 6 +- .../lib/open_multiple_images_page.dart | 5 +- .../example/lib/open_text_page.dart | 6 +- .../example/lib/save_text_page.dart | 5 +- .../file_selector_macos/pubspec.yaml | 2 +- .../CHANGELOG.md | 4 +- .../pubspec.yaml | 2 +- .../file_selector_web/CHANGELOG.md | 5 + .../file_selector_web/example/lib/main.dart | 7 +- .../file_selector_web/pubspec.yaml | 2 +- .../file_selector_windows/CHANGELOG.md | 4 +- .../example/lib/get_directory_page.dart | 5 +- .../example/lib/home_page.dart | 3 + .../example/lib/main.dart | 16 ++- .../example/lib/open_image_page.dart | 6 +- .../lib/open_multiple_images_page.dart | 5 +- .../example/lib/open_text_page.dart | 6 +- .../example/lib/save_text_page.dart | 5 +- .../file_selector_windows/pubspec.yaml | 2 +- .../CHANGELOG.md | 4 +- ...flutter_plugin_android_lifecycle_test.dart | 2 +- .../example/lib/main.dart | 8 +- .../pubspec.yaml | 2 +- .../google_maps_flutter/CHANGELOG.md | 6 +- .../example/lib/animate_camera.dart | 6 +- .../example/lib/lite_mode.dart | 3 +- .../google_maps_flutter/example/lib/main.dart | 8 +- .../example/lib/map_click.dart | 3 +- .../example/lib/map_coordinates.dart | 3 +- .../example/lib/map_ui.dart | 5 +- .../example/lib/marker_icons.dart | 5 +- .../example/lib/move_camera.dart | 5 +- .../example/lib/padding.dart | 5 +- .../google_maps_flutter/example/lib/page.dart | 3 +- .../example/lib/place_circle.dart | 18 +-- .../example/lib/place_marker.dart | 31 ++--- .../example/lib/place_polygon.dart | 24 ++-- .../example/lib/place_polyline.dart | 26 ++-- .../example/lib/scrolling_map.dart | 5 +- .../example/lib/snapshot.dart | 5 +- .../example/lib/tile_overlay.dart | 11 +- .../lib/src/controller.dart | 2 + .../google_maps_flutter/pubspec.yaml | 2 +- .../google_maps_flutter_web/CHANGELOG.md | 4 +- .../example/lib/main.dart | 2 +- .../google_maps_flutter_web/pubspec.yaml | 2 +- .../google_sign_in/CHANGELOG.md | 5 + .../google_sign_in/example/lib/main.dart | 8 +- .../google_sign_in/lib/widgets.dart | 4 +- .../google_sign_in/pubspec.yaml | 2 +- .../google_sign_in_android/CHANGELOG.md | 5 + .../example/lib/main.dart | 8 +- .../google_sign_in_android/pubspec.yaml | 2 +- .../google_sign_in_ios/CHANGELOG.md | 5 + .../google_sign_in_ios/example/lib/main.dart | 8 +- .../google_sign_in_ios/pubspec.yaml | 2 +- .../google_sign_in_web/CHANGELOG.md | 5 + .../google_sign_in_web/example/lib/main.dart | 7 +- .../google_sign_in_web/pubspec.yaml | 2 +- .../image_picker/image_picker/CHANGELOG.md | 5 + .../image_picker/example/lib/main.dart | 39 +++--- .../image_picker/image_picker/pubspec.yaml | 2 +- .../image_picker_android/CHANGELOG.md | 5 + .../example/lib/main.dart | 39 +++--- .../image_picker_android/pubspec.yaml | 2 +- .../image_picker_for_web/CHANGELOG.md | 5 + .../example/lib/main.dart | 7 +- .../image_picker_for_web/pubspec.yaml | 2 +- .../image_picker_ios/CHANGELOG.md | 4 +- .../image_picker_ios/example/lib/main.dart | 39 +++--- .../image_picker_ios/pubspec.yaml | 2 +- .../image_picker_windows/CHANGELOG.md | 4 +- .../example/lib/main.dart | 31 ++--- .../image_picker_windows/pubspec.yaml | 2 +- .../in_app_purchase/CHANGELOG.md | 4 +- .../in_app_purchase/example/lib/main.dart | 111 +++++++++--------- .../in_app_purchase/pubspec.yaml | 2 +- .../in_app_purchase_android/CHANGELOG.md | 4 +- .../example/lib/main.dart | 6 +- .../in_app_purchase_android/pubspec.yaml | 2 +- .../in_app_purchase_storekit/CHANGELOG.md | 4 +- .../example/lib/main.dart | 8 +- .../in_app_purchase_storekit/pubspec.yaml | 2 +- packages/ios_platform_images/CHANGELOG.md | 4 +- .../ios_platform_images/example/lib/main.dart | 7 +- .../example/test/widget_test.dart | 2 +- packages/ios_platform_images/pubspec.yaml | 2 +- packages/local_auth/local_auth/CHANGELOG.md | 2 + .../local_auth/example/lib/main.dart | 14 ++- .../local_auth_android/CHANGELOG.md | 4 +- .../local_auth_android/example/lib/main.dart | 14 ++- .../local_auth_android/pubspec.yaml | 2 +- .../local_auth/local_auth_ios/CHANGELOG.md | 4 +- .../local_auth_ios/example/lib/main.dart | 14 ++- .../local_auth/local_auth_ios/pubspec.yaml | 2 +- .../path_provider/path_provider/CHANGELOG.md | 4 +- .../path_provider/example/lib/main.dart | 44 +++---- .../path_provider/path_provider/pubspec.yaml | 2 +- .../path_provider_android/CHANGELOG.md | 5 + .../example/lib/main.dart | 16 +-- .../path_provider_android/pubspec.yaml | 2 +- .../path_provider_ios/CHANGELOG.md | 5 + .../path_provider_ios/example/lib/main.dart | 14 ++- .../path_provider_ios/pubspec.yaml | 2 +- .../path_provider_linux/CHANGELOG.md | 5 + .../path_provider_linux/example/lib/main.dart | 7 +- .../path_provider_linux/pubspec.yaml | 2 +- .../path_provider_macos/CHANGELOG.md | 5 + .../path_provider_macos/example/lib/main.dart | 6 +- .../path_provider_macos/pubspec.yaml | 2 +- .../path_provider_windows/CHANGELOG.md | 5 + .../example/lib/main.dart | 6 +- .../path_provider_windows/pubspec.yaml | 2 +- .../quick_actions/quick_actions/CHANGELOG.md | 4 +- .../quick_actions/example/lib/main.dart | 6 +- .../quick_actions/quick_actions/pubspec.yaml | 2 +- .../quick_actions_android/CHANGELOG.md | 5 + .../example/lib/main.dart | 6 +- .../quick_actions_android/pubspec.yaml | 2 +- .../quick_actions_ios/CHANGELOG.md | 4 + .../quick_actions_ios/example/lib/main.dart | 6 +- .../quick_actions_ios/pubspec.yaml | 2 +- .../shared_preferences/CHANGELOG.md | 4 +- .../shared_preferences/example/lib/main.dart | 4 +- .../shared_preferences/pubspec.yaml | 2 +- .../shared_preferences_android/CHANGELOG.md | 5 + .../example/lib/main.dart | 4 +- .../shared_preferences_android/pubspec.yaml | 2 +- .../shared_preferences_ios/CHANGELOG.md | 5 + .../example/lib/main.dart | 4 +- .../shared_preferences_ios/pubspec.yaml | 2 +- .../shared_preferences_linux/CHANGELOG.md | 4 +- .../example/lib/main.dart | 4 +- .../shared_preferences_linux/pubspec.yaml | 2 +- .../shared_preferences_macos/CHANGELOG.md | 4 +- .../example/lib/main.dart | 4 +- .../shared_preferences_macos/pubspec.yaml | 2 +- .../shared_preferences_web/CHANGELOG.md | 5 + .../example/lib/main.dart | 7 +- .../shared_preferences_web/pubspec.yaml | 2 +- .../shared_preferences_windows/CHANGELOG.md | 5 + .../example/lib/main.dart | 4 +- .../shared_preferences_windows/pubspec.yaml | 2 +- .../url_launcher/url_launcher/CHANGELOG.md | 4 +- .../url_launcher/example/lib/main.dart | 6 +- .../url_launcher/lib/src/link.dart | 2 +- .../url_launcher/url_launcher/pubspec.yaml | 2 +- .../url_launcher_android/CHANGELOG.md | 5 + .../example/lib/main.dart | 6 +- .../url_launcher_android/pubspec.yaml | 2 +- .../url_launcher_ios/CHANGELOG.md | 5 + .../url_launcher_ios/example/lib/main.dart | 6 +- .../url_launcher_ios/pubspec.yaml | 2 +- .../url_launcher_linux/CHANGELOG.md | 5 + .../url_launcher_linux/example/lib/main.dart | 6 +- .../url_launcher_linux/pubspec.yaml | 2 +- .../url_launcher_macos/CHANGELOG.md | 5 + .../url_launcher_macos/example/lib/main.dart | 6 +- .../url_launcher_macos/pubspec.yaml | 2 +- .../url_launcher_web/CHANGELOG.md | 5 + .../url_launcher_web/example/lib/main.dart | 7 +- .../url_launcher_web/lib/src/link.dart | 2 +- .../url_launcher_web/pubspec.yaml | 2 +- .../url_launcher_windows/CHANGELOG.md | 5 + .../example/lib/main.dart | 6 +- .../url_launcher_windows/pubspec.yaml | 2 +- .../video_player/video_player/CHANGELOG.md | 4 +- .../video_player/lib/video_player.dart | 11 +- .../video_player/video_player/pubspec.yaml | 2 +- .../video_player_android/CHANGELOG.md | 4 +- .../example/lib/mini_controller.dart | 10 +- .../video_player_android/pubspec.yaml | 2 +- .../video_player_avfoundation/CHANGELOG.md | 4 +- .../example/lib/mini_controller.dart | 10 +- .../video_player_avfoundation/pubspec.yaml | 2 +- .../video_player_web/CHANGELOG.md | 4 +- .../video_player_web/example/lib/main.dart | 7 +- .../video_player_web/pubspec.yaml | 2 +- .../webview_flutter/CHANGELOG.md | 6 +- .../webview_flutter_test.dart | 3 +- .../webview_flutter/example/lib/main.dart | 16 +-- .../webview_flutter/pubspec.yaml | 2 +- .../webview_flutter_android/CHANGELOG.md | 4 +- .../webview_flutter_test.dart | 3 +- .../example/lib/main.dart | 2 +- .../example/lib/web_view.dart | 2 +- .../lib/webview_android_widget.dart | 3 +- .../webview_flutter_android/pubspec.yaml | 2 +- .../webview_flutter_web/CHANGELOG.md | 4 +- .../example/lib/web_view.dart | 2 +- .../webview_flutter_web/pubspec.yaml | 2 +- .../webview_flutter_wkwebview/CHANGELOG.md | 4 +- .../webview_flutter_test.dart | 3 +- .../example/lib/main.dart | 2 +- .../example/lib/web_view.dart | 2 +- .../lib/src/web_kit_webview_widget.dart | 3 +- .../webview_flutter_wkwebview/pubspec.yaml | 2 +- script/tool/CHANGELOG.md | 2 +- script/tool/test/util.dart | 4 +- 229 files changed, 936 insertions(+), 526 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index f6177cd9939a..ba5e0a9c4ced 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -134,6 +134,7 @@ linter: - leading_newlines_in_multiline_strings - library_names - library_prefixes + - library_private_types_in_public_api # - lines_longer_than_80_chars # not required by flutter style - list_remove_unrelated_type # - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/sdk/issues/34181 @@ -197,7 +198,7 @@ linter: - recursive_getters # - sized_box_for_whitespace # not yet tested - slash_for_doc_comments - # - sort_child_properties_last # not yet tested + - sort_child_properties_last - sort_constructors_first - sort_unnamed_constructors_first - test_types_in_equals @@ -229,7 +230,7 @@ linter: - use_full_hex_values_for_flutter_colors # - use_function_type_syntax_for_parameters # not yet tested - use_is_even_rather_than_modulo - # - use_key_in_widget_constructors # not yet tested + - use_key_in_widget_constructors - use_late_for_private_fields_and_variables - use_raw_strings - use_rethrow_when_possible diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 4d7e9bbeb218..bf0ccf86a82e 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.9.4+22 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.9.4+21 diff --git a/packages/camera/camera/README.md b/packages/camera/camera/README.md index 97b16d20f48a..0bcaeaeb3b7c 100644 --- a/packages/camera/camera/README.md +++ b/packages/camera/camera/README.md @@ -89,18 +89,22 @@ Here is a small example flutter app displaying a full screen camera preview. import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; -late List cameras; +late List _cameras; Future main() async { WidgetsFlutterBinding.ensureInitialized(); - cameras = await availableCameras(); - runApp(CameraApp()); + _cameras = await availableCameras(); + runApp(const CameraApp()); } +/// CameraApp is the Main Application. class CameraApp extends StatefulWidget { + /// Default Constructor + const CameraApp({Key? key}) : super(key: key); + @override - _CameraAppState createState() => _CameraAppState(); + State createState() => _CameraAppState(); } class _CameraAppState extends State { @@ -109,7 +113,7 @@ class _CameraAppState extends State { @override void initState() { super.initState(); - controller = CameraController(cameras[0], ResolutionPreset.max); + controller = CameraController(_cameras[0], ResolutionPreset.max); controller.initialize().then((_) { if (!mounted) { return; diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index aabbe249313d..a645326f2803 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore_for_file: public_member_api_docs - import 'dart:async'; import 'dart:io'; @@ -13,9 +11,13 @@ import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:video_player/video_player.dart'; +/// Camera example home widget. class CameraExampleHome extends StatefulWidget { + /// Default Constructor + const CameraExampleHome({Key? key}) : super(key: key); + @override - _CameraExampleHomeState createState() { + State createState() { return _CameraExampleHomeState(); } } @@ -34,7 +36,7 @@ IconData getCameraLensIcon(CameraLensDirection direction) { } } -void logError(String code, String? message) { +void _logError(String code, String? message) { if (message != null) { print('Error: $code\nError Message: $message'); } else { @@ -134,12 +136,6 @@ class _CameraExampleHomeState extends State children: [ Expanded( child: Container( - child: Padding( - padding: const EdgeInsets.all(1.0), - child: Center( - child: _cameraPreviewWidget(), - ), - ), decoration: BoxDecoration( color: Colors.black, border: Border.all( @@ -150,6 +146,12 @@ class _CameraExampleHomeState extends State width: 3.0, ), ), + child: Padding( + padding: const EdgeInsets.all(1.0), + child: Center( + child: _cameraPreviewWidget(), + ), + ), ), ), _captureControlRowWidget(), @@ -233,6 +235,8 @@ class _CameraExampleHomeState extends State Container() else SizedBox( + width: 64.0, + height: 64.0, child: (localVideoController == null) ? ( // The captured image on the web contains a network-accessible URL @@ -243,6 +247,8 @@ class _CameraExampleHomeState extends State ? Image.network(imageFile!.path) : Image.file(File(imageFile!.path))) : Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.pink)), child: Center( child: AspectRatio( aspectRatio: @@ -251,11 +257,7 @@ class _CameraExampleHomeState extends State : 1.0, child: VideoPlayer(localVideoController)), ), - decoration: BoxDecoration( - border: Border.all(color: Colors.pink)), ), - width: 64.0, - height: 64.0, ), ], ), @@ -394,7 +396,6 @@ class _CameraExampleHomeState extends State mainAxisSize: MainAxisSize.max, children: [ TextButton( - child: const Text('AUTO'), style: styleAuto, onPressed: controller != null ? () => @@ -406,21 +407,22 @@ class _CameraExampleHomeState extends State showInSnackBar('Resetting exposure point'); } }, + child: const Text('AUTO'), ), TextButton( - child: const Text('LOCKED'), style: styleLocked, onPressed: controller != null ? () => onSetExposureModeButtonPressed(ExposureMode.locked) : null, + child: const Text('LOCKED'), ), TextButton( - child: const Text('RESET OFFSET'), style: styleLocked, onPressed: controller != null ? () => controller!.setExposureOffset(0.0) : null, + child: const Text('RESET OFFSET'), ), ], ), @@ -479,7 +481,6 @@ class _CameraExampleHomeState extends State mainAxisSize: MainAxisSize.max, children: [ TextButton( - child: const Text('AUTO'), style: styleAuto, onPressed: controller != null ? () => onSetFocusModeButtonPressed(FocusMode.auto) @@ -490,13 +491,14 @@ class _CameraExampleHomeState extends State } showInSnackBar('Resetting focus point'); }, + child: const Text('AUTO'), ), TextButton( - child: const Text('LOCKED'), style: styleLocked, onPressed: controller != null ? () => onSetFocusModeButtonPressed(FocusMode.locked) : null, + child: const Text('LOCKED'), ), ], ), @@ -582,13 +584,13 @@ class _CameraExampleHomeState extends State onNewCameraSelected(description); }; - if (cameras.isEmpty) { + if (_cameras.isEmpty) { _ambiguate(SchedulerBinding.instance)?.addPostFrameCallback((_) async { showInSnackBar('No camera found.'); }); return const Text('None'); } else { - for (final CameraDescription cameraDescription in cameras) { + for (final CameraDescription cameraDescription in _cameras) { toggles.add( SizedBox( width: 90.0, @@ -1014,31 +1016,35 @@ class _CameraExampleHomeState extends State } void _showCameraException(CameraException e) { - logError(e.code, e.description); + _logError(e.code, e.description); showInSnackBar('Error: ${e.code}\n${e.description}'); } } +/// CameraApp is the Main Application. class CameraApp extends StatelessWidget { + /// Default Constructor + const CameraApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { - return MaterialApp( + return const MaterialApp( home: CameraExampleHome(), ); } } -List cameras = []; +List _cameras = []; Future main() async { // Fetch the available cameras before initializing the app. try { WidgetsFlutterBinding.ensureInitialized(); - cameras = await availableCameras(); + _cameras = await availableCameras(); } on CameraException catch (e) { - logError(e.code, e.description); + _logError(e.code, e.description); } - runApp(CameraApp()); + runApp(const CameraApp()); } /// This allows a value of type T or T? to be treated as a value of type T?. diff --git a/packages/camera/camera/example/lib/readme_full_example.dart b/packages/camera/camera/example/lib/readme_full_example.dart index b25e637a0c95..a310fd9daeb0 100644 --- a/packages/camera/camera/example/lib/readme_full_example.dart +++ b/packages/camera/camera/example/lib/readme_full_example.dart @@ -2,24 +2,26 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore_for_file: public_member_api_docs - // #docregion FullAppExample import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; -late List cameras; +late List _cameras; Future main() async { WidgetsFlutterBinding.ensureInitialized(); - cameras = await availableCameras(); - runApp(CameraApp()); + _cameras = await availableCameras(); + runApp(const CameraApp()); } +/// CameraApp is the Main Application. class CameraApp extends StatefulWidget { + /// Default Constructor + const CameraApp({Key? key}) : super(key: key); + @override - _CameraAppState createState() => _CameraAppState(); + State createState() => _CameraAppState(); } class _CameraAppState extends State { @@ -28,7 +30,7 @@ class _CameraAppState extends State { @override void initState() { super.initState(); - controller = CameraController(cameras[0], ResolutionPreset.max); + controller = CameraController(_cameras[0], ResolutionPreset.max); controller.initialize().then((_) { if (!mounted) { return; diff --git a/packages/camera/camera/example/test/main_test.dart b/packages/camera/camera/example/test/main_test.dart index 9a5fcdf2d5ea..6e909efcfc62 100644 --- a/packages/camera/camera/example/test/main_test.dart +++ b/packages/camera/camera/example/test/main_test.dart @@ -9,7 +9,7 @@ import 'package:flutter_test/flutter_test.dart'; void main() { testWidgets('Test snackbar', (WidgetTester tester) async { WidgetsFlutterBinding.ensureInitialized(); - await tester.pumpWidget(CameraApp()); + await tester.pumpWidget(const CameraApp()); await tester.pumpAndSettle(); expect(find.byType(SnackBar), findsOneWidget); }); diff --git a/packages/camera/camera/lib/src/camera_preview.dart b/packages/camera/camera/lib/src/camera_preview.dart index a9b3f2143b49..94ffca649fa6 100644 --- a/packages/camera/camera/lib/src/camera_preview.dart +++ b/packages/camera/camera/lib/src/camera_preview.dart @@ -10,7 +10,8 @@ import 'package:flutter/services.dart'; /// A widget showing a live camera preview. class CameraPreview extends StatelessWidget { /// Creates a preview widget for the given camera controller. - const CameraPreview(this.controller, {this.child}); + const CameraPreview(this.controller, {Key? key, this.child}) + : super(key: key); /// The controller for the camera that the preview is shown for. final CameraController controller; diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index f62777044617..fde6663844c2 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+21 +version: 0.9.4+22 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index 852e4a03fd43..7a24e12e5029 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.2.1+5 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 0.2.1+4 * Migrates from `ui.hash*` to `Object.hash*`. diff --git a/packages/camera/camera_web/example/lib/main.dart b/packages/camera/camera_web/example/lib/main.dart index ab04ce2ca2c7..670891fa5009 100644 --- a/packages/camera/camera_web/example/lib/main.dart +++ b/packages/camera/camera_web/example/lib/main.dart @@ -4,10 +4,13 @@ import 'package:flutter/material.dart'; -void main() => runApp(MyApp()); +void main() => runApp(const MyApp()); /// App for testing class MyApp extends StatelessWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const Directionality( diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index 2d1a4508eb73..8bef974190b2 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_web description: A Flutter plugin for getting information about and controlling the camera on Web. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.2.1+4 +version: 0.2.1+5 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/camera/camera_windows/CHANGELOG.md b/packages/camera/camera_windows/CHANGELOG.md index b1383dc54993..0f3bf441b05a 100644 --- a/packages/camera/camera_windows/CHANGELOG.md +++ b/packages/camera/camera_windows/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.1.0+1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.1.0 diff --git a/packages/camera/camera_windows/example/lib/main.dart b/packages/camera/camera_windows/example/lib/main.dart index b73e00cac52b..5758b0f1e397 100644 --- a/packages/camera/camera_windows/example/lib/main.dart +++ b/packages/camera/camera_windows/example/lib/main.dart @@ -9,11 +9,14 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// Example app for Camera Windows plugin. class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override State createState() => _MyAppState(); } diff --git a/packages/camera/camera_windows/pubspec.yaml b/packages/camera/camera_windows/pubspec.yaml index 1081c3dfc01f..fe655b04e8c8 100644 --- a/packages/camera/camera_windows/pubspec.yaml +++ b/packages/camera/camera_windows/pubspec.yaml @@ -1,8 +1,8 @@ name: camera_windows description: A Flutter plugin for getting information about and controlling the camera on Windows. -version: 0.1.0 repository: https://github.com/flutter/plugins/tree/master/packages/camera/camera_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 +version: 0.1.0+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/espresso/CHANGELOG.md b/packages/espresso/CHANGELOG.md index eb1f267ca1d3..dad0a912e174 100644 --- a/packages/espresso/CHANGELOG.md +++ b/packages/espresso/CHANGELOG.md @@ -1,8 +1,13 @@ +## 0.2.0+2 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 0.2.0+1 * Adds OS version support information to README. * Updates `androidx.test.ext:junit` and `androidx.test.ext:truth` for - compatibilty with updated Flutter template. + compatibility with updated Flutter template. ## 0.2.0 diff --git a/packages/espresso/example/lib/main.dart b/packages/espresso/example/lib/main.dart index 14f94abb28c8..741cd9cf9fa2 100644 --- a/packages/espresso/example/lib/main.dart +++ b/packages/espresso/example/lib/main.dart @@ -4,10 +4,13 @@ import 'package:flutter/material.dart'; -void main() => runApp(MyApp()); +void main() => runApp(const MyApp()); /// Example app for Espresso plugin. class MyApp extends StatelessWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + // This widget is the root of your application. @override Widget build(BuildContext context) { @@ -45,7 +48,7 @@ class _MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State<_MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<_MyHomePage> { diff --git a/packages/espresso/pubspec.yaml b/packages/espresso/pubspec.yaml index 7737fc46d4b6..ac0199cf045f 100644 --- a/packages/espresso/pubspec.yaml +++ b/packages/espresso/pubspec.yaml @@ -3,7 +3,7 @@ description: Java classes for testing Flutter apps using Espresso. Allows driving Flutter widgets from a native Espresso test. repository: https://github.com/flutter/plugins/tree/main/packages/espresso issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+espresso%22 -version: 0.2.0+1 +version: 0.2.0+2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index c0821fed7446..17baf9f12469 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,7 +1,9 @@ -## NEXT +## 0.8.4+2 * Removes unnecessary imports. * Adds OS version support information to README. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.8.4+1 diff --git a/packages/file_selector/file_selector/example/lib/get_directory_page.dart b/packages/file_selector/file_selector/example/lib/get_directory_page.dart index b3ed9d0eeaca..506134d66bd8 100644 --- a/packages/file_selector/file_selector/example/lib/get_directory_page.dart +++ b/packages/file_selector/file_selector/example/lib/get_directory_page.dart @@ -7,6 +7,9 @@ import 'package:flutter/material.dart'; /// Screen that shows an example of getDirectoryPath class GetDirectoryPage extends StatelessWidget { + /// Default Constructor + const GetDirectoryPage({Key? key}) : super(key: key); + Future _getDirectoryPath(BuildContext context) async { const String confirmButtonText = 'Choose'; final String? directoryPath = await getDirectoryPath( @@ -50,7 +53,7 @@ class GetDirectoryPage extends StatelessWidget { /// Widget that displays a text file in a dialog class TextDisplay extends StatelessWidget { /// Default Constructor - const TextDisplay(this.directoryPath); + const TextDisplay(this.directoryPath, {Key? key}) : super(key: key); /// Directory path final String directoryPath; diff --git a/packages/file_selector/file_selector/example/lib/home_page.dart b/packages/file_selector/file_selector/example/lib/home_page.dart index c598cbdf2611..9a0733b56b93 100644 --- a/packages/file_selector/file_selector/example/lib/home_page.dart +++ b/packages/file_selector/file_selector/example/lib/home_page.dart @@ -6,6 +6,9 @@ import 'package:flutter/material.dart'; /// Home Page of the application class HomePage extends StatelessWidget { + /// Default Constructor + const HomePage({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { final ButtonStyle style = ElevatedButton.styleFrom( diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index 14ce3f593f33..34f5857ab0bc 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -11,11 +11,14 @@ import 'package:example/save_text_page.dart'; import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// MyApp is the Main Application class MyApp extends StatelessWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -24,13 +27,14 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: HomePage(), + home: const HomePage(), routes: { - '/open/image': (BuildContext context) => OpenImagePage(), - '/open/images': (BuildContext context) => OpenMultipleImagesPage(), - '/open/text': (BuildContext context) => OpenTextPage(), + '/open/image': (BuildContext context) => const OpenImagePage(), + '/open/images': (BuildContext context) => + const OpenMultipleImagesPage(), + '/open/text': (BuildContext context) => const OpenTextPage(), '/save/text': (BuildContext context) => SaveTextPage(), - '/directory': (BuildContext context) => GetDirectoryPage(), + '/directory': (BuildContext context) => const GetDirectoryPage(), }, ); } diff --git a/packages/file_selector/file_selector/example/lib/open_image_page.dart b/packages/file_selector/file_selector/example/lib/open_image_page.dart index 0abdba6eb72d..e520ffb402aa 100644 --- a/packages/file_selector/file_selector/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_image_page.dart @@ -10,6 +10,9 @@ import 'package:flutter/material.dart'; /// Screen that shows an example of openFiles class OpenImagePage extends StatelessWidget { + /// Default Constructor + const OpenImagePage({Key? key}) : super(key: key); + Future _openImageFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'images', @@ -59,7 +62,8 @@ class OpenImagePage extends StatelessWidget { /// Widget that displays a text file in a dialog class ImageDisplay extends StatelessWidget { /// Default Constructor - const ImageDisplay(this.fileName, this.filePath); + const ImageDisplay(this.fileName, this.filePath, {Key? key}) + : super(key: key); /// Image's name final String fileName; diff --git a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart index 9a1101214aaa..e2d21c7f04d1 100644 --- a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart @@ -10,6 +10,9 @@ import 'package:flutter/material.dart'; /// Screen that shows an example of openFiles class OpenMultipleImagesPage extends StatelessWidget { + /// Default Constructor + const OpenMultipleImagesPage({Key? key}) : super(key: key); + Future _openImageFile(BuildContext context) async { final XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', @@ -61,7 +64,7 @@ class OpenMultipleImagesPage extends StatelessWidget { /// Widget that displays a text file in a dialog class MultipleImagesDisplay extends StatelessWidget { /// Default Constructor - const MultipleImagesDisplay(this.files); + const MultipleImagesDisplay(this.files, {Key? key}) : super(key: key); /// The files containing the images final List files; diff --git a/packages/file_selector/file_selector/example/lib/open_text_page.dart b/packages/file_selector/file_selector/example/lib/open_text_page.dart index 652e8596cf81..be48a434282b 100644 --- a/packages/file_selector/file_selector/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_text_page.dart @@ -7,6 +7,9 @@ import 'package:flutter/material.dart'; /// Screen that shows an example of openFile class OpenTextPage extends StatelessWidget { + /// Default Constructor + const OpenTextPage({Key? key}) : super(key: key); + Future _openTextFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'text', @@ -55,7 +58,8 @@ class OpenTextPage extends StatelessWidget { /// Widget that displays a text file in a dialog class TextDisplay extends StatelessWidget { /// Default Constructor - const TextDisplay(this.fileName, this.fileContent); + const TextDisplay(this.fileName, this.fileContent, {Key? key}) + : super(key: key); /// File's name final String fileName; diff --git a/packages/file_selector/file_selector/example/lib/save_text_page.dart b/packages/file_selector/file_selector/example/lib/save_text_page.dart index 108ef89b0248..b0317844ec36 100644 --- a/packages/file_selector/file_selector/example/lib/save_text_page.dart +++ b/packages/file_selector/file_selector/example/lib/save_text_page.dart @@ -8,6 +8,9 @@ import 'package:flutter/material.dart'; /// Page for showing an example of saving with file_selector class SaveTextPage extends StatelessWidget { + /// Default Constructor + SaveTextPage({Key? key}) : super(key: key); + final TextEditingController _nameController = TextEditingController(); final TextEditingController _contentController = TextEditingController(); diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index c05900f650cf..7026f7f32287 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for opening and saving files, or selecting directories, using native file selection UI. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.4+1 +version: 0.8.4+2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_macos/CHANGELOG.md b/packages/file_selector/file_selector_macos/CHANGELOG.md index b46a174bd323..19724a513a9e 100644 --- a/packages/file_selector/file_selector_macos/CHANGELOG.md +++ b/packages/file_selector/file_selector_macos/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.8.2+1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.8.2 diff --git a/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart b/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart index 0e55df8ce622..a27ab2b40aba 100644 --- a/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart @@ -8,6 +8,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a directory using `getDirectoryPath`, /// then displays the selected directory in a dialog. class GetDirectoryPage extends StatelessWidget { + /// Default Constructor + const GetDirectoryPage({Key? key}) : super(key: key); + Future _getDirectoryPath(BuildContext context) async { const String confirmButtonText = 'Choose'; final String? directoryPath = @@ -52,7 +55,7 @@ class GetDirectoryPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class TextDisplay extends StatelessWidget { /// Creates a `TextDisplay`. - const TextDisplay(this.directoryPath); + const TextDisplay(this.directoryPath, {Key? key}) : super(key: key); /// The path selected in the dialog. final String directoryPath; diff --git a/packages/file_selector/file_selector_macos/example/lib/home_page.dart b/packages/file_selector/file_selector_macos/example/lib/home_page.dart index 958680be0e3b..4d6ca7e6e6e3 100644 --- a/packages/file_selector/file_selector_macos/example/lib/home_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/home_page.dart @@ -6,6 +6,9 @@ import 'package:flutter/material.dart'; /// Home Page of the application. class HomePage extends StatelessWidget { + /// Default Constructor + const HomePage({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { final ButtonStyle style = ElevatedButton.styleFrom( diff --git a/packages/file_selector/file_selector_macos/example/lib/main.dart b/packages/file_selector/file_selector_macos/example/lib/main.dart index a49ebac1aea5..cbe268e1c7ab 100644 --- a/packages/file_selector/file_selector_macos/example/lib/main.dart +++ b/packages/file_selector/file_selector_macos/example/lib/main.dart @@ -11,11 +11,14 @@ import 'package:example/save_text_page.dart'; import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// MyApp is the Main Application. class MyApp extends StatelessWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -24,13 +27,14 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: HomePage(), + home: const HomePage(), routes: { - '/open/image': (BuildContext context) => OpenImagePage(), - '/open/images': (BuildContext context) => OpenMultipleImagesPage(), - '/open/text': (BuildContext context) => OpenTextPage(), + '/open/image': (BuildContext context) => const OpenImagePage(), + '/open/images': (BuildContext context) => + const OpenMultipleImagesPage(), + '/open/text': (BuildContext context) => const OpenTextPage(), '/save/text': (BuildContext context) => SaveTextPage(), - '/directory': (BuildContext context) => GetDirectoryPage(), + '/directory': (BuildContext context) => const GetDirectoryPage(), }, ); } diff --git a/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart index aaf083603e72..1a05343b27ab 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart @@ -11,6 +11,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select an image file using /// `openFiles`, then displays the selected images in a gallery dialog. class OpenImagePage extends StatelessWidget { + /// Default Constructor + const OpenImagePage({Key? key}) : super(key: key); + Future _openImageFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'images', @@ -59,7 +62,8 @@ class OpenImagePage extends StatelessWidget { /// Widget that displays an image in a dialog. class ImageDisplay extends StatelessWidget { /// Default Constructor. - const ImageDisplay(this.fileName, this.filePath); + const ImageDisplay(this.fileName, this.filePath, {Key? key}) + : super(key: key); /// The name of the selected file. final String fileName; diff --git a/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart index a030b8b4b10b..9c3c8e369f5b 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart @@ -11,6 +11,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select multiple image files using /// `openFiles`, then displays the selected images in a gallery dialog. class OpenMultipleImagesPage extends StatelessWidget { + /// Default Constructor + const OpenMultipleImagesPage({Key? key}) : super(key: key); + Future _openImageFile(BuildContext context) async { final XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', @@ -63,7 +66,7 @@ class OpenMultipleImagesPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class MultipleImagesDisplay extends StatelessWidget { /// Default Constructor. - const MultipleImagesDisplay(this.files); + const MultipleImagesDisplay(this.files, {Key? key}) : super(key: key); /// The files containing the images. final List files; diff --git a/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart index fa281a0020d5..9adde400b5e1 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart @@ -8,6 +8,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a text file using `openFile`, then /// displays its contents in a dialog. class OpenTextPage extends StatelessWidget { + /// Default Constructor + const OpenTextPage({Key? key}) : super(key: key); + Future _openTextFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'text', @@ -56,7 +59,8 @@ class OpenTextPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class TextDisplay extends StatelessWidget { /// Default Constructor. - const TextDisplay(this.fileName, this.fileContent); + const TextDisplay(this.fileName, this.fileContent, {Key? key}) + : super(key: key); /// The name of the selected file. final String fileName; diff --git a/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart b/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart index 3989c62b7442..a44a387c019b 100644 --- a/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart @@ -9,6 +9,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a save location using `getSavePath`, /// then writes text to a file at that location. class SaveTextPage extends StatelessWidget { + /// Default Constructor + SaveTextPage({Key? key}) : super(key: key); + final TextEditingController _nameController = TextEditingController(); final TextEditingController _contentController = TextEditingController(); @@ -67,8 +70,8 @@ class SaveTextPage extends StatelessWidget { primary: Colors.blue, onPrimary: Colors.white, ), - child: const Text('Press to save a text file'), onPressed: _saveFile, + child: const Text('Press to save a text file'), ), ], ), diff --git a/packages/file_selector/file_selector_macos/pubspec.yaml b/packages/file_selector/file_selector_macos/pubspec.yaml index 071d261c4bf8..41077c1c04e6 100644 --- a/packages/file_selector/file_selector_macos/pubspec.yaml +++ b/packages/file_selector/file_selector_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_macos description: macOS implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.2 +version: 0.8.2+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md index 100b6ad136a7..934d38827ddb 100644 --- a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md +++ b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.0.5 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.0.4 diff --git a/packages/file_selector/file_selector_platform_interface/pubspec.yaml b/packages/file_selector/file_selector_platform_interface/pubspec.yaml index 42917751ed58..d280b0f5c71c 100644 --- a/packages/file_selector/file_selector_platform_interface/pubspec.yaml +++ b/packages/file_selector/file_selector_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.0.4 +version: 2.0.5 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_web/CHANGELOG.md b/packages/file_selector/file_selector_web/CHANGELOG.md index 5927239ef9e3..ce9d5590f9a9 100644 --- a/packages/file_selector/file_selector_web/CHANGELOG.md +++ b/packages/file_selector/file_selector_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.8.1+4 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 0.8.1+3 * Minor code cleanup for new analysis rules. diff --git a/packages/file_selector/file_selector_web/example/lib/main.dart b/packages/file_selector/file_selector_web/example/lib/main.dart index 341913a18490..87422953de6a 100644 --- a/packages/file_selector/file_selector_web/example/lib/main.dart +++ b/packages/file_selector/file_selector_web/example/lib/main.dart @@ -5,13 +5,16 @@ import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// App for testing class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/file_selector/file_selector_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml index 74d0412b440f..2e12b6d175a3 100644 --- a/packages/file_selector/file_selector_web/pubspec.yaml +++ b/packages/file_selector/file_selector_web/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_web description: Web platform implementation of file_selector repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.1+3 +version: 0.8.1+4 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_windows/CHANGELOG.md b/packages/file_selector/file_selector_windows/CHANGELOG.md index ae3cd13342b1..c242717c3267 100644 --- a/packages/file_selector/file_selector_windows/CHANGELOG.md +++ b/packages/file_selector/file_selector_windows/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.8.2+1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.8.2 diff --git a/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart b/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart index b282b9030d67..8fc1a9001465 100644 --- a/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart @@ -8,6 +8,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a directory using `getDirectoryPath`, /// then displays the selected directory in a dialog. class GetDirectoryPage extends StatelessWidget { + /// Default Constructor + const GetDirectoryPage({Key? key}) : super(key: key); + Future _getDirectoryPath(BuildContext context) async { const String confirmButtonText = 'Choose'; final String? directoryPath = @@ -52,7 +55,7 @@ class GetDirectoryPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class TextDisplay extends StatelessWidget { /// Creates a `TextDisplay`. - const TextDisplay(this.directoryPath); + const TextDisplay(this.directoryPath, {Key? key}) : super(key: key); /// The path selected in the dialog. final String directoryPath; diff --git a/packages/file_selector/file_selector_windows/example/lib/home_page.dart b/packages/file_selector/file_selector_windows/example/lib/home_page.dart index 958680be0e3b..4d6ca7e6e6e3 100644 --- a/packages/file_selector/file_selector_windows/example/lib/home_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/home_page.dart @@ -6,6 +6,9 @@ import 'package:flutter/material.dart'; /// Home Page of the application. class HomePage extends StatelessWidget { + /// Default Constructor + const HomePage({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { final ButtonStyle style = ElevatedButton.styleFrom( diff --git a/packages/file_selector/file_selector_windows/example/lib/main.dart b/packages/file_selector/file_selector_windows/example/lib/main.dart index a49ebac1aea5..cbe268e1c7ab 100644 --- a/packages/file_selector/file_selector_windows/example/lib/main.dart +++ b/packages/file_selector/file_selector_windows/example/lib/main.dart @@ -11,11 +11,14 @@ import 'package:example/save_text_page.dart'; import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// MyApp is the Main Application. class MyApp extends StatelessWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -24,13 +27,14 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: HomePage(), + home: const HomePage(), routes: { - '/open/image': (BuildContext context) => OpenImagePage(), - '/open/images': (BuildContext context) => OpenMultipleImagesPage(), - '/open/text': (BuildContext context) => OpenTextPage(), + '/open/image': (BuildContext context) => const OpenImagePage(), + '/open/images': (BuildContext context) => + const OpenMultipleImagesPage(), + '/open/text': (BuildContext context) => const OpenTextPage(), '/save/text': (BuildContext context) => SaveTextPage(), - '/directory': (BuildContext context) => GetDirectoryPage(), + '/directory': (BuildContext context) => const GetDirectoryPage(), }, ); } diff --git a/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart index aaf083603e72..1a05343b27ab 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart @@ -11,6 +11,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select an image file using /// `openFiles`, then displays the selected images in a gallery dialog. class OpenImagePage extends StatelessWidget { + /// Default Constructor + const OpenImagePage({Key? key}) : super(key: key); + Future _openImageFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'images', @@ -59,7 +62,8 @@ class OpenImagePage extends StatelessWidget { /// Widget that displays an image in a dialog. class ImageDisplay extends StatelessWidget { /// Default Constructor. - const ImageDisplay(this.fileName, this.filePath); + const ImageDisplay(this.fileName, this.filePath, {Key? key}) + : super(key: key); /// The name of the selected file. final String fileName; diff --git a/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart index a030b8b4b10b..9c3c8e369f5b 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart @@ -11,6 +11,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select multiple image files using /// `openFiles`, then displays the selected images in a gallery dialog. class OpenMultipleImagesPage extends StatelessWidget { + /// Default Constructor + const OpenMultipleImagesPage({Key? key}) : super(key: key); + Future _openImageFile(BuildContext context) async { final XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', @@ -63,7 +66,7 @@ class OpenMultipleImagesPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class MultipleImagesDisplay extends StatelessWidget { /// Default Constructor. - const MultipleImagesDisplay(this.files); + const MultipleImagesDisplay(this.files, {Key? key}) : super(key: key); /// The files containing the images. final List files; diff --git a/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart index fa281a0020d5..9adde400b5e1 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart @@ -8,6 +8,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a text file using `openFile`, then /// displays its contents in a dialog. class OpenTextPage extends StatelessWidget { + /// Default Constructor + const OpenTextPage({Key? key}) : super(key: key); + Future _openTextFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'text', @@ -56,7 +59,8 @@ class OpenTextPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class TextDisplay extends StatelessWidget { /// Default Constructor. - const TextDisplay(this.fileName, this.fileContent); + const TextDisplay(this.fileName, this.fileContent, {Key? key}) + : super(key: key); /// The name of the selected file. final String fileName; diff --git a/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart b/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart index b87a51c3877d..961e0fb2fbc3 100644 --- a/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart @@ -9,6 +9,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a save location using `getSavePath`, /// then writes text to a file at that location. class SaveTextPage extends StatelessWidget { + /// Default Constructor + SaveTextPage({Key? key}) : super(key: key); + final TextEditingController _nameController = TextEditingController(); final TextEditingController _contentController = TextEditingController(); @@ -67,8 +70,8 @@ class SaveTextPage extends StatelessWidget { primary: Colors.blue, onPrimary: Colors.white, ), - child: const Text('Press to save a text file'), onPressed: _saveFile, + child: const Text('Press to save a text file'), ), ], ), diff --git a/packages/file_selector/file_selector_windows/pubspec.yaml b/packages/file_selector/file_selector_windows/pubspec.yaml index 7b035e974293..152b63ef4a3f 100644 --- a/packages/file_selector/file_selector_windows/pubspec.yaml +++ b/packages/file_selector/file_selector_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_windows description: Windows implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.2 +version: 0.8.2+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/flutter_plugin_android_lifecycle/CHANGELOG.md b/packages/flutter_plugin_android_lifecycle/CHANGELOG.md index 8fdfc39f3bf9..a0a3f67bed46 100644 --- a/packages/flutter_plugin_android_lifecycle/CHANGELOG.md +++ b/packages/flutter_plugin_android_lifecycle/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.0.6 * Adds OS version support information to README. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.0.5 diff --git a/packages/flutter_plugin_android_lifecycle/example/integration_test/flutter_plugin_android_lifecycle_test.dart b/packages/flutter_plugin_android_lifecycle/example/integration_test/flutter_plugin_android_lifecycle_test.dart index 1d329a02f93b..1198c6f01806 100644 --- a/packages/flutter_plugin_android_lifecycle/example/integration_test/flutter_plugin_android_lifecycle_test.dart +++ b/packages/flutter_plugin_android_lifecycle/example/integration_test/flutter_plugin_android_lifecycle_test.dart @@ -10,6 +10,6 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('loads', (WidgetTester tester) async { - await tester.pumpWidget(MyApp()); + await tester.pumpWidget(const MyApp()); }); } diff --git a/packages/flutter_plugin_android_lifecycle/example/lib/main.dart b/packages/flutter_plugin_android_lifecycle/example/lib/main.dart index 3ef6794dfad2..c465b3b687f2 100644 --- a/packages/flutter_plugin_android_lifecycle/example/lib/main.dart +++ b/packages/flutter_plugin_android_lifecycle/example/lib/main.dart @@ -2,13 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore_for_file: public_member_api_docs - import 'package:flutter/material.dart'; -void main() => runApp(MyApp()); +void main() => runApp(const MyApp()); +/// MyApp is the Main Application. class MyApp extends StatelessWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( diff --git a/packages/flutter_plugin_android_lifecycle/pubspec.yaml b/packages/flutter_plugin_android_lifecycle/pubspec.yaml index b359cc55c351..c109d0936589 100644 --- a/packages/flutter_plugin_android_lifecycle/pubspec.yaml +++ b/packages/flutter_plugin_android_lifecycle/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_plugin_android_lifecycle description: Flutter plugin for accessing an Android Lifecycle within other plugins. repository: https://github.com/flutter/plugins/tree/main/packages/flutter_plugin_android_lifecycle issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_plugin_android_lifecycle%22 -version: 2.0.5 +version: 2.0.6 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index b0662b89d9cc..d1f2f926e2ab 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.1.5 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.1.4 @@ -223,7 +225,7 @@ GoogleMapController is now uniformly driven by implementing `DefaultLifecycleObs ## 0.5.26+1 -* Removes a errorneously added method from the GoogleMapController.h header file. +* Removes an erroneously added method from the GoogleMapController.h header file. ## 0.5.26 diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart index f8072eee7c85..09df2b98b146 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart @@ -10,8 +10,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class AnimateCameraPage extends GoogleMapExampleAppPage { - const AnimateCameraPage() - : super(const Icon(Icons.map), 'Camera control, animated'); + const AnimateCameraPage({Key? key}) + : super(const Icon(Icons.map), 'Camera control, animated', key: key); @override Widget build(BuildContext context) { @@ -20,7 +20,7 @@ class AnimateCameraPage extends GoogleMapExampleAppPage { } class AnimateCamera extends StatefulWidget { - const AnimateCamera(); + const AnimateCamera({Key? key}) : super(key: key); @override State createState() => AnimateCameraState(); } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart index b1b58fdc91bf..fd95cf864a7c 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart @@ -12,7 +12,8 @@ const CameraPosition _kInitialPosition = CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); class LiteModePage extends GoogleMapExampleAppPage { - const LiteModePage() : super(const Icon(Icons.map), 'Lite mode'); + const LiteModePage({Key? key}) + : super(const Icon(Icons.map), 'Lite mode', key: key); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart index f4d420a72f7c..8932705bc6d5 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore_for_file: public_member_api_docs - import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -43,7 +41,11 @@ final List _allPages = [ const TileOverlayPage(), ]; +/// MapsDemo is the Main Application. class MapsDemo extends StatelessWidget { + /// Default Constructor + const MapsDemo({Key? key}) : super(key: key); + void _pushPage(BuildContext context, GoogleMapExampleAppPage page) { Navigator.of(context).push(MaterialPageRoute( builder: (_) => Scaffold( @@ -72,5 +74,5 @@ void main() { if (defaultTargetPlatform == TargetPlatform.android) { AndroidGoogleMapsFlutter.useAndroidViewSurface = true; } - runApp(MaterialApp(home: MapsDemo())); + runApp(const MaterialApp(home: MapsDemo())); } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart index bbe2372dce9a..9c96f25d5fa7 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart @@ -12,7 +12,8 @@ const CameraPosition _kInitialPosition = CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); class MapClickPage extends GoogleMapExampleAppPage { - const MapClickPage() : super(const Icon(Icons.mouse), 'Map click'); + const MapClickPage({Key? key}) + : super(const Icon(Icons.mouse), 'Map click', key: key); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart index 8e4853c040ed..1179acd46ffa 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart @@ -12,7 +12,8 @@ const CameraPosition _kInitialPosition = CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); class MapCoordinatesPage extends GoogleMapExampleAppPage { - const MapCoordinatesPage() : super(const Icon(Icons.map), 'Map coordinates'); + const MapCoordinatesPage({Key? key}) + : super(const Icon(Icons.map), 'Map coordinates', key: key); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_ui.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_ui.dart index 48ef1f570e02..fbfeda56a968 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_ui.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_ui.dart @@ -16,7 +16,8 @@ final LatLngBounds sydneyBounds = LatLngBounds( ); class MapUiPage extends GoogleMapExampleAppPage { - const MapUiPage() : super(const Icon(Icons.map), 'User interface'); + const MapUiPage({Key? key}) + : super(const Icon(Icons.map), 'User interface', key: key); @override Widget build(BuildContext context) { @@ -25,7 +26,7 @@ class MapUiPage extends GoogleMapExampleAppPage { } class MapUiBody extends StatefulWidget { - const MapUiBody(); + const MapUiBody({Key? key}) : super(key: key); @override State createState() => MapUiBodyState(); diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart index 95ace9d7c482..58d266c95d1d 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart @@ -11,7 +11,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class MarkerIconsPage extends GoogleMapExampleAppPage { - const MarkerIconsPage() : super(const Icon(Icons.image), 'Marker icons'); + const MarkerIconsPage({Key? key}) + : super(const Icon(Icons.image), 'Marker icons', key: key); @override Widget build(BuildContext context) { @@ -20,7 +21,7 @@ class MarkerIconsPage extends GoogleMapExampleAppPage { } class MarkerIconsBody extends StatefulWidget { - const MarkerIconsBody(); + const MarkerIconsBody({Key? key}) : super(key: key); @override State createState() => MarkerIconsBodyState(); diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart index 33da90f32c1b..a6bae3009f0b 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart @@ -10,7 +10,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class MoveCameraPage extends GoogleMapExampleAppPage { - const MoveCameraPage() : super(const Icon(Icons.map), 'Camera control'); + const MoveCameraPage({Key? key}) + : super(const Icon(Icons.map), 'Camera control', key: key); @override Widget build(BuildContext context) { @@ -19,7 +20,7 @@ class MoveCameraPage extends GoogleMapExampleAppPage { } class MoveCamera extends StatefulWidget { - const MoveCamera(); + const MoveCamera({Key? key}) : super(key: key); @override State createState() => MoveCameraState(); } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart index 77091909e970..a4bfa88d559f 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart @@ -9,7 +9,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PaddingPage extends GoogleMapExampleAppPage { - const PaddingPage() : super(const Icon(Icons.map), 'Add padding to the map'); + const PaddingPage({Key? key}) + : super(const Icon(Icons.map), 'Add padding to the map', key: key); @override Widget build(BuildContext context) { @@ -18,7 +19,7 @@ class PaddingPage extends GoogleMapExampleAppPage { } class MarkerIconsBody extends StatefulWidget { - const MarkerIconsBody(); + const MarkerIconsBody({Key? key}) : super(key: key); @override State createState() => MarkerIconsBodyState(); diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/page.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/page.dart index fb6eb3260f6d..eb01ab07a6f3 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/page.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/page.dart @@ -7,7 +7,8 @@ import 'package:flutter/material.dart'; abstract class GoogleMapExampleAppPage extends StatelessWidget { - const GoogleMapExampleAppPage(this.leading, this.title); + const GoogleMapExampleAppPage(this.leading, this.title, {Key? key}) + : super(key: key); final Widget leading; final String title; diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart index c6f1509af69f..ef5033cfa1ee 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart @@ -10,8 +10,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PlaceCirclePage extends GoogleMapExampleAppPage { - const PlaceCirclePage() - : super(const Icon(Icons.linear_scale), 'Place circle'); + const PlaceCirclePage({Key? key}) + : super(const Icon(Icons.linear_scale), 'Place circle', key: key); @override Widget build(BuildContext context) { @@ -20,7 +20,7 @@ class PlaceCirclePage extends GoogleMapExampleAppPage { } class PlaceCircleBody extends StatefulWidget { - const PlaceCircleBody(); + const PlaceCircleBody({Key? key}) : super(key: key); @override State createState() => PlaceCircleBodyState(); @@ -170,42 +170,42 @@ class PlaceCircleBodyState extends State { Column( children: [ TextButton( - child: const Text('add'), onPressed: _add, + child: const Text('add'), ), TextButton( - child: const Text('remove'), onPressed: (selectedId == null) ? null : () => _remove(selectedId), + child: const Text('remove'), ), TextButton( - child: const Text('toggle visible'), onPressed: (selectedId == null) ? null : () => _toggleVisible(selectedId), + child: const Text('toggle visible'), ), ], ), Column( children: [ TextButton( - child: const Text('change stroke width'), onPressed: (selectedId == null) ? null : () => _changeStrokeWidth(selectedId), + child: const Text('change stroke width'), ), TextButton( - child: const Text('change stroke color'), onPressed: (selectedId == null) ? null : () => _changeStrokeColor(selectedId), + child: const Text('change stroke color'), ), TextButton( - child: const Text('change fill color'), onPressed: (selectedId == null) ? null : () => _changeFillColor(selectedId), + child: const Text('change fill color'), ), ], ) diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart index 4291cac6841e..1238d61547b8 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart @@ -15,7 +15,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PlaceMarkerPage extends GoogleMapExampleAppPage { - const PlaceMarkerPage() : super(const Icon(Icons.place), 'Place marker'); + const PlaceMarkerPage({Key? key}) + : super(const Icon(Icons.place), 'Place marker', key: key); @override Widget build(BuildContext context) { @@ -24,7 +25,7 @@ class PlaceMarkerPage extends GoogleMapExampleAppPage { } class PlaceMarkerBody extends StatefulWidget { - const PlaceMarkerBody(); + const PlaceMarkerBody({Key? key}) : super(key: key); @override State createState() => PlaceMarkerBodyState(); @@ -308,13 +309,13 @@ class PlaceMarkerBodyState extends State { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ TextButton( - child: const Text('Add'), onPressed: _add, + child: const Text('Add'), ), TextButton( - child: const Text('Remove'), onPressed: selectedId == null ? null : () => _remove(selectedId), + child: const Text('Remove'), ), ], ), @@ -322,62 +323,61 @@ class PlaceMarkerBodyState extends State { alignment: WrapAlignment.spaceEvenly, children: [ TextButton( - child: const Text('change info'), onPressed: selectedId == null ? null : () => _changeInfo(selectedId), + child: const Text('change info'), ), TextButton( - child: const Text('change info anchor'), onPressed: selectedId == null ? null : () => _changeInfoAnchor(selectedId), + child: const Text('change info anchor'), ), TextButton( - child: const Text('change alpha'), onPressed: selectedId == null ? null : () => _changeAlpha(selectedId), + child: const Text('change alpha'), ), TextButton( - child: const Text('change anchor'), onPressed: selectedId == null ? null : () => _changeAnchor(selectedId), + child: const Text('change anchor'), ), TextButton( - child: const Text('toggle draggable'), onPressed: selectedId == null ? null : () => _toggleDraggable(selectedId), + child: const Text('toggle draggable'), ), TextButton( - child: const Text('toggle flat'), onPressed: selectedId == null ? null : () => _toggleFlat(selectedId), + child: const Text('toggle flat'), ), TextButton( - child: const Text('change position'), onPressed: selectedId == null ? null : () => _changePosition(selectedId), + child: const Text('change position'), ), TextButton( - child: const Text('change rotation'), onPressed: selectedId == null ? null : () => _changeRotation(selectedId), + child: const Text('change rotation'), ), TextButton( - child: const Text('toggle visible'), onPressed: selectedId == null ? null : () => _toggleVisible(selectedId), + child: const Text('toggle visible'), ), TextButton( - child: const Text('change zIndex'), onPressed: selectedId == null ? null : () => _changeZIndex(selectedId), + child: const Text('change zIndex'), ), TextButton( - child: const Text('set marker icon'), onPressed: selectedId == null ? null : () { @@ -387,6 +387,7 @@ class PlaceMarkerBodyState extends State { }, ); }, + child: const Text('set marker icon'), ), ], ), diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart index 8d4fa3e07c36..f1932141b8ab 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart @@ -10,8 +10,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PlacePolygonPage extends GoogleMapExampleAppPage { - const PlacePolygonPage() - : super(const Icon(Icons.linear_scale), 'Place polygon'); + const PlacePolygonPage({Key? key}) + : super(const Icon(Icons.linear_scale), 'Place polygon', key: key); @override Widget build(BuildContext context) { @@ -20,7 +20,7 @@ class PlacePolygonPage extends GoogleMapExampleAppPage { } class PlacePolygonBody extends StatefulWidget { - const PlacePolygonBody(); + const PlacePolygonBody({Key? key}) : super(key: key); @override State createState() => PlacePolygonBodyState(); @@ -196,64 +196,64 @@ class PlacePolygonBodyState extends State { Column( children: [ TextButton( - child: const Text('add'), onPressed: _add, + child: const Text('add'), ), TextButton( - child: const Text('remove'), onPressed: (selectedId == null) ? null : () => _remove(selectedId), + child: const Text('remove'), ), TextButton( - child: const Text('toggle visible'), onPressed: (selectedId == null) ? null : () => _toggleVisible(selectedId), + child: const Text('toggle visible'), ), TextButton( - child: const Text('toggle geodesic'), onPressed: (selectedId == null) ? null : () => _toggleGeodesic(selectedId), + child: const Text('toggle geodesic'), ), ], ), Column( children: [ TextButton( - child: const Text('add holes'), onPressed: (selectedId == null) ? null : ((polygons[selectedId]!.holes.isNotEmpty) ? null : () => _addHoles(selectedId)), + child: const Text('add holes'), ), TextButton( - child: const Text('remove holes'), onPressed: (selectedId == null) ? null : ((polygons[selectedId]!.holes.isEmpty) ? null : () => _removeHoles(selectedId)), + child: const Text('remove holes'), ), TextButton( - child: const Text('change stroke width'), onPressed: (selectedId == null) ? null : () => _changeWidth(selectedId), + child: const Text('change stroke width'), ), TextButton( - child: const Text('change stroke color'), onPressed: (selectedId == null) ? null : () => _changeStrokeColor(selectedId), + child: const Text('change stroke color'), ), TextButton( - child: const Text('change fill color'), onPressed: (selectedId == null) ? null : () => _changeFillColor(selectedId), + child: const Text('change fill color'), ), ], ) diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart index 434920d293be..b3a637ce7d15 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart @@ -11,8 +11,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PlacePolylinePage extends GoogleMapExampleAppPage { - const PlacePolylinePage() - : super(const Icon(Icons.linear_scale), 'Place polyline'); + const PlacePolylinePage({Key? key}) + : super(const Icon(Icons.linear_scale), 'Place polyline', key: key); @override Widget build(BuildContext context) { @@ -21,7 +21,7 @@ class PlacePolylinePage extends GoogleMapExampleAppPage { } class PlacePolylineBody extends StatefulWidget { - const PlacePolylineBody(); + const PlacePolylineBody({Key? key}) : super(key: key); @override State createState() => PlacePolylineBodyState(); @@ -234,66 +234,66 @@ class PlacePolylineBodyState extends State { Column( children: [ TextButton( - child: const Text('add'), onPressed: _add, + child: const Text('add'), ), TextButton( - child: const Text('remove'), onPressed: (selectedId == null) ? null : () => _remove(selectedId), + child: const Text('remove'), ), TextButton( - child: const Text('toggle visible'), onPressed: (selectedId == null) ? null : () => _toggleVisible(selectedId), + child: const Text('toggle visible'), ), TextButton( - child: const Text('toggle geodesic'), onPressed: (selectedId == null) ? null : () => _toggleGeodesic(selectedId), + child: const Text('toggle geodesic'), ), ], ), Column( children: [ TextButton( - child: const Text('change width'), onPressed: (selectedId == null) ? null : () => _changeWidth(selectedId), + child: const Text('change width'), ), TextButton( - child: const Text('change color'), onPressed: (selectedId == null) ? null : () => _changeColor(selectedId), + child: const Text('change color'), ), TextButton( - child: const Text('change start cap [Android only]'), onPressed: isIOS || (selectedId == null) ? null : () => _changeStartCap(selectedId), + child: const Text('change start cap [Android only]'), ), TextButton( - child: const Text('change end cap [Android only]'), onPressed: isIOS || (selectedId == null) ? null : () => _changeEndCap(selectedId), + child: const Text('change end cap [Android only]'), ), TextButton( - child: const Text('change joint type [Android only]'), onPressed: isIOS || (selectedId == null) ? null : () => _changeJointType(selectedId), + child: const Text('change joint type [Android only]'), ), TextButton( - child: const Text('change pattern [Android only]'), onPressed: isIOS || (selectedId == null) ? null : () => _changePattern(selectedId), + child: const Text('change pattern [Android only]'), ), ], ) diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart index 04769315e685..ca9d3962ddd7 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart @@ -14,7 +14,8 @@ import 'page.dart'; const LatLng _center = LatLng(32.080664, 34.9563837); class ScrollingMapPage extends GoogleMapExampleAppPage { - const ScrollingMapPage() : super(const Icon(Icons.map), 'Scrolling map'); + const ScrollingMapPage({Key? key}) + : super(const Icon(Icons.map), 'Scrolling map', key: key); @override Widget build(BuildContext context) { @@ -23,7 +24,7 @@ class ScrollingMapPage extends GoogleMapExampleAppPage { } class ScrollingMapBody extends StatelessWidget { - const ScrollingMapBody(); + const ScrollingMapBody({Key? key}) : super(key: key); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart index 9afc28869490..849a9f469938 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart @@ -15,8 +15,9 @@ const CameraPosition _kInitialPosition = CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); class SnapshotPage extends GoogleMapExampleAppPage { - const SnapshotPage() - : super(const Icon(Icons.camera_alt), 'Take a snapshot of the map'); + const SnapshotPage({Key? key}) + : super(const Icon(Icons.camera_alt), 'Take a snapshot of the map', + key: key); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart index dc7f7d1d6f33..81dfc2815866 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart @@ -13,7 +13,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class TileOverlayPage extends GoogleMapExampleAppPage { - const TileOverlayPage() : super(const Icon(Icons.map), 'Tile overlay'); + const TileOverlayPage({Key? key}) + : super(const Icon(Icons.map), 'Tile overlay', key: key); @override Widget build(BuildContext context) { @@ -22,7 +23,7 @@ class TileOverlayPage extends GoogleMapExampleAppPage { } class TileOverlayBody extends StatefulWidget { - const TileOverlayBody(); + const TileOverlayBody({Key? key}) : super(key: key); @override State createState() => TileOverlayBodyState(); @@ -90,16 +91,16 @@ class TileOverlayBodyState extends State { ), ), TextButton( - child: const Text('Add tile overlay'), onPressed: _addTileOverlay, + child: const Text('Add tile overlay'), ), TextButton( - child: const Text('Remove tile overlay'), onPressed: _removeTileOverlay, + child: const Text('Remove tile overlay'), ), TextButton( - child: const Text('Clear tile cache'), onPressed: _clearTileCache, + child: const Text('Clear tile cache'), ), ], ); diff --git a/packages/google_maps_flutter/google_maps_flutter/lib/src/controller.dart b/packages/google_maps_flutter/google_maps_flutter/lib/src/controller.dart index 088589e4a2ce..dfc6e579d560 100644 --- a/packages/google_maps_flutter/google_maps_flutter/lib/src/controller.dart +++ b/packages/google_maps_flutter/google_maps_flutter/lib/src/controller.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: library_private_types_in_public_api + part of google_maps_flutter; /// Controller for a single GoogleMap instance running on the host platform. diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index c10f9c679a17..a294dd09981f 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter description: A Flutter plugin for integrating Google Maps in iOS and Android applications. repository: https://github.com/flutter/plugins/tree/main/packages/google_maps_flutter/google_maps_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 2.1.4 +version: 2.1.5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md index 48908b984b0f..f2fe971f4591 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.3.2+2 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.3.2+1 diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/lib/main.dart index 10415204570c..d1ba571b5bd0 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/lib/main.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/lib/main.dart @@ -11,7 +11,7 @@ void main() { /// App for testing class MyApp extends StatefulWidget { @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml index 2780175d29e2..271d87d21092 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter_web description: Web platform implementation of google_maps_flutter repository: https://github.com/flutter/plugins/tree/main/packages/google_maps_flutter/google_maps_flutter_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 0.3.2+1 +version: 0.3.2+2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index caab46de7c5b..5b47536cd2e2 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,3 +1,8 @@ +## 5.3.1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 5.3.0 * Moves Android and iOS implementations to federated packages. diff --git a/packages/google_sign_in/google_sign_in/example/lib/main.dart b/packages/google_sign_in/google_sign_in/example/lib/main.dart index 9840a1e0a9f6..4c27543f5b18 100644 --- a/packages/google_sign_in/google_sign_in/example/lib/main.dart +++ b/packages/google_sign_in/google_sign_in/example/lib/main.dart @@ -22,7 +22,7 @@ GoogleSignIn _googleSignIn = GoogleSignIn( void main() { runApp( - MaterialApp( + const MaterialApp( title: 'Google Sign In', home: SignInDemo(), ), @@ -30,6 +30,8 @@ void main() { } class SignInDemo extends StatefulWidget { + const SignInDemo({Key? key}) : super(key: key); + @override State createState() => SignInDemoState(); } @@ -125,8 +127,8 @@ class SignInDemoState extends State { const Text('Signed in successfully.'), Text(_contactText), ElevatedButton( - child: const Text('SIGN OUT'), onPressed: _handleSignOut, + child: const Text('SIGN OUT'), ), ElevatedButton( child: const Text('REFRESH'), @@ -140,8 +142,8 @@ class SignInDemoState extends State { children: [ const Text('You are not currently signed in.'), ElevatedButton( - child: const Text('SIGN IN'), onPressed: _handleSignIn, + child: const Text('SIGN IN'), ), ], ); diff --git a/packages/google_sign_in/google_sign_in/lib/widgets.dart b/packages/google_sign_in/google_sign_in/lib/widgets.dart index 61f89133fba4..f7ae5f9a6e5f 100644 --- a/packages/google_sign_in/google_sign_in/lib/widgets.dart +++ b/packages/google_sign_in/google_sign_in/lib/widgets.dart @@ -22,11 +22,13 @@ class GoogleUserCircleAvatar extends StatelessWidget { /// in place of a profile photo, or a default profile photo if the user's /// identity does not specify a `displayName`. const GoogleUserCircleAvatar({ + Key? key, required this.identity, this.placeholderPhotoUrl, this.foregroundColor, this.backgroundColor, - }) : assert(identity != null); + }) : assert(identity != null), + super(key: key); /// A regular expression that matches against the "size directive" path /// segment of Google profile image URLs. diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index 760706f2e7bc..e58b27af08b7 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Google Sign-In, a secure authentication system for signing in with a Google account on Android and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.3.0 +version: 5.3.1 environment: diff --git a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md index 3ffa6b5b7d6b..90069d05f442 100644 --- a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 5.2.7 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 5.2.6 * Switches to an internal method channel, rather than the default. diff --git a/packages/google_sign_in/google_sign_in_android/example/lib/main.dart b/packages/google_sign_in/google_sign_in_android/example/lib/main.dart index a750c330001d..526cf8b69ccf 100644 --- a/packages/google_sign_in/google_sign_in_android/example/lib/main.dart +++ b/packages/google_sign_in/google_sign_in_android/example/lib/main.dart @@ -14,7 +14,7 @@ import 'package:http/http.dart' as http; void main() { runApp( - MaterialApp( + const MaterialApp( title: 'Google Sign In', home: SignInDemo(), ), @@ -22,6 +22,8 @@ void main() { } class SignInDemo extends StatefulWidget { + const SignInDemo({Key? key}) : super(key: key); + @override State createState() => SignInDemoState(); } @@ -142,8 +144,8 @@ class SignInDemoState extends State { const Text('Signed in successfully.'), Text(_contactText), ElevatedButton( - child: const Text('SIGN OUT'), onPressed: _handleSignOut, + child: const Text('SIGN OUT'), ), ElevatedButton( child: const Text('REFRESH'), @@ -157,8 +159,8 @@ class SignInDemoState extends State { children: [ const Text('You are not currently signed in.'), ElevatedButton( - child: const Text('SIGN IN'), onPressed: _handleSignIn, + child: const Text('SIGN IN'), ), ], ); diff --git a/packages/google_sign_in/google_sign_in_android/pubspec.yaml b/packages/google_sign_in/google_sign_in_android/pubspec.yaml index fa3dc1489b26..d9b272320694 100644 --- a/packages/google_sign_in/google_sign_in_android/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_android/pubspec.yaml @@ -2,7 +2,7 @@ name: google_sign_in_android description: Android implementation of the google_sign_in plugin. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.2.6 +version: 5.2.7 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md b/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md index 3ffa6b5b7d6b..90069d05f442 100644 --- a/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md @@ -1,3 +1,8 @@ +## 5.2.7 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 5.2.6 * Switches to an internal method channel, rather than the default. diff --git a/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart b/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart index a750c330001d..526cf8b69ccf 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart +++ b/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart @@ -14,7 +14,7 @@ import 'package:http/http.dart' as http; void main() { runApp( - MaterialApp( + const MaterialApp( title: 'Google Sign In', home: SignInDemo(), ), @@ -22,6 +22,8 @@ void main() { } class SignInDemo extends StatefulWidget { + const SignInDemo({Key? key}) : super(key: key); + @override State createState() => SignInDemoState(); } @@ -142,8 +144,8 @@ class SignInDemoState extends State { const Text('Signed in successfully.'), Text(_contactText), ElevatedButton( - child: const Text('SIGN OUT'), onPressed: _handleSignOut, + child: const Text('SIGN OUT'), ), ElevatedButton( child: const Text('REFRESH'), @@ -157,8 +159,8 @@ class SignInDemoState extends State { children: [ const Text('You are not currently signed in.'), ElevatedButton( - child: const Text('SIGN IN'), onPressed: _handleSignIn, + child: const Text('SIGN IN'), ), ], ); diff --git a/packages/google_sign_in/google_sign_in_ios/pubspec.yaml b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml index e5ef3832ff0e..b6f541b22ce1 100644 --- a/packages/google_sign_in/google_sign_in_ios/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: google_sign_in_ios description: Android implementation of the google_sign_in plugin. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.2.6 +version: 5.2.7 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md index aab4acae0376..6e87bebf5d02 100644 --- a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.10.1+1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 0.10.1 * Updates minimum Flutter version to 2.8. diff --git a/packages/google_sign_in/google_sign_in_web/example/lib/main.dart b/packages/google_sign_in/google_sign_in_web/example/lib/main.dart index d381fb4af3ab..b23015c811e8 100644 --- a/packages/google_sign_in/google_sign_in_web/example/lib/main.dart +++ b/packages/google_sign_in/google_sign_in_web/example/lib/main.dart @@ -5,13 +5,16 @@ import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// App for testing class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/google_sign_in/google_sign_in_web/pubspec.yaml b/packages/google_sign_in/google_sign_in_web/pubspec.yaml index 5a09453b8e86..3dcd0e8eef29 100644 --- a/packages/google_sign_in/google_sign_in_web/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_web/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Google Sign-In, a secure authentication system for signing in with a Google account on Android, iOS and Web. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 0.10.1 +version: 0.10.1+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index f1bf54c5cd35..a384a5272e05 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.8.5+1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 0.8.5 * Moves Android and iOS implementations to federated packages. diff --git a/packages/image_picker/image_picker/example/lib/main.dart b/packages/image_picker/image_picker/example/lib/main.dart index f3ad2375b8f2..a6f0e83c3abb 100755 --- a/packages/image_picker/image_picker/example/lib/main.dart +++ b/packages/image_picker/image_picker/example/lib/main.dart @@ -13,10 +13,12 @@ import 'package:image_picker/image_picker.dart'; import 'package:video_player/video_player.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( @@ -32,7 +34,7 @@ class MyHomePage extends StatefulWidget { final String? title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -177,21 +179,22 @@ class _MyHomePageState extends State { } if (_imageFileList != null) { return Semantics( - child: ListView.builder( - key: UniqueKey(), - itemBuilder: (BuildContext context, int index) { - // Why network for web? - // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform - return Semantics( - label: 'image_picker_example_picked_image', - child: kIsWeb - ? Image.network(_imageFileList![index].path) - : Image.file(File(_imageFileList![index].path)), - ); - }, - itemCount: _imageFileList!.length, - ), - label: 'image_picker_example_picked_images'); + label: 'image_picker_example_picked_images', + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + // Why network for web? + // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform + return Semantics( + label: 'image_picker_example_picked_image', + child: kIsWeb + ? Image.network(_imageFileList![index].path) + : Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + ); } else if (_pickImageError != null) { return Text( 'Pick image error: $_pickImageError', @@ -417,7 +420,7 @@ typedef OnPickImageCallback = void Function( double? maxWidth, double? maxHeight, int? quality); class AspectRatioVideo extends StatefulWidget { - const AspectRatioVideo(this.controller); + const AspectRatioVideo(this.controller, {Key? key}) : super(key: key); final VideoPlayerController? controller; diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 77a50916283e..818486d8e145 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.5 +version: 0.8.5+1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/image_picker/image_picker_android/CHANGELOG.md b/packages/image_picker/image_picker_android/CHANGELOG.md index 3472ade28d5b..0514fc33d420 100644 --- a/packages/image_picker/image_picker_android/CHANGELOG.md +++ b/packages/image_picker/image_picker_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.8.4+12 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 0.8.4+11 * Splits from `image_picker` as a federated implementation. diff --git a/packages/image_picker/image_picker_android/example/lib/main.dart b/packages/image_picker/image_picker_android/example/lib/main.dart index 48eee35445da..d56aeb866195 100755 --- a/packages/image_picker/image_picker_android/example/lib/main.dart +++ b/packages/image_picker/image_picker_android/example/lib/main.dart @@ -13,10 +13,12 @@ import 'package:image_picker_platform_interface/image_picker_platform_interface. import 'package:video_player/video_player.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( @@ -32,7 +34,7 @@ class MyHomePage extends StatefulWidget { final String? title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -177,21 +179,22 @@ class _MyHomePageState extends State { } if (_imageFileList != null) { return Semantics( - child: ListView.builder( - key: UniqueKey(), - itemBuilder: (BuildContext context, int index) { - // Why network for web? - // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform - return Semantics( - label: 'image_picker_example_picked_image', - child: kIsWeb - ? Image.network(_imageFileList![index].path) - : Image.file(File(_imageFileList![index].path)), - ); - }, - itemCount: _imageFileList!.length, - ), - label: 'image_picker_example_picked_images'); + label: 'image_picker_example_picked_images', + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + // Why network for web? + // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform + return Semantics( + label: 'image_picker_example_picked_image', + child: kIsWeb + ? Image.network(_imageFileList![index].path) + : Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + ); } else if (_pickImageError != null) { return Text( 'Pick image error: $_pickImageError', @@ -417,7 +420,7 @@ typedef OnPickImageCallback = void Function( double? maxWidth, double? maxHeight, int? quality); class AspectRatioVideo extends StatefulWidget { - const AspectRatioVideo(this.controller); + const AspectRatioVideo(this.controller, {Key? key}) : super(key: key); final VideoPlayerController? controller; diff --git a/packages/image_picker/image_picker_android/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml index dbeef9bed193..90d136c2c89b 100755 --- a/packages/image_picker/image_picker_android/pubspec.yaml +++ b/packages/image_picker/image_picker_android/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_android description: Android implementation of the image_picker plugin. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.4+11 +version: 0.8.4+12 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/image_picker/image_picker_for_web/CHANGELOG.md b/packages/image_picker/image_picker_for_web/CHANGELOG.md index dcf353fe19b1..c33b3b9981de 100644 --- a/packages/image_picker/image_picker_for_web/CHANGELOG.md +++ b/packages/image_picker/image_picker_for_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.1.7 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.1.6 * Internal code cleanup for stricter analysis options. diff --git a/packages/image_picker/image_picker_for_web/example/lib/main.dart b/packages/image_picker/image_picker_for_web/example/lib/main.dart index 341913a18490..87422953de6a 100644 --- a/packages/image_picker/image_picker_for_web/example/lib/main.dart +++ b/packages/image_picker/image_picker_for_web/example/lib/main.dart @@ -5,13 +5,16 @@ import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// App for testing class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/image_picker/image_picker_for_web/pubspec.yaml b/packages/image_picker/image_picker_for_web/pubspec.yaml index deccd2b50a1f..b0c5deb0da7a 100644 --- a/packages/image_picker/image_picker_for_web/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_for_web description: Web platform implementation of image_picker repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_for_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 2.1.6 +version: 2.1.7 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index 31a0795a4e30..af391db02689 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.8.5+1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.8.5 diff --git a/packages/image_picker/image_picker_ios/example/lib/main.dart b/packages/image_picker/image_picker_ios/example/lib/main.dart index 48eee35445da..d56aeb866195 100755 --- a/packages/image_picker/image_picker_ios/example/lib/main.dart +++ b/packages/image_picker/image_picker_ios/example/lib/main.dart @@ -13,10 +13,12 @@ import 'package:image_picker_platform_interface/image_picker_platform_interface. import 'package:video_player/video_player.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( @@ -32,7 +34,7 @@ class MyHomePage extends StatefulWidget { final String? title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -177,21 +179,22 @@ class _MyHomePageState extends State { } if (_imageFileList != null) { return Semantics( - child: ListView.builder( - key: UniqueKey(), - itemBuilder: (BuildContext context, int index) { - // Why network for web? - // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform - return Semantics( - label: 'image_picker_example_picked_image', - child: kIsWeb - ? Image.network(_imageFileList![index].path) - : Image.file(File(_imageFileList![index].path)), - ); - }, - itemCount: _imageFileList!.length, - ), - label: 'image_picker_example_picked_images'); + label: 'image_picker_example_picked_images', + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + // Why network for web? + // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform + return Semantics( + label: 'image_picker_example_picked_image', + child: kIsWeb + ? Image.network(_imageFileList![index].path) + : Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + ); } else if (_pickImageError != null) { return Text( 'Pick image error: $_pickImageError', @@ -417,7 +420,7 @@ typedef OnPickImageCallback = void Function( double? maxWidth, double? maxHeight, int? quality); class AspectRatioVideo extends StatefulWidget { - const AspectRatioVideo(this.controller); + const AspectRatioVideo(this.controller, {Key? key}) : super(key: key); final VideoPlayerController? controller; diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index a9cd052be56a..76ca20614f18 100755 --- a/packages/image_picker/image_picker_ios/pubspec.yaml +++ b/packages/image_picker/image_picker_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_ios description: iOS implementation of the video_picker plugin. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.5 +version: 0.8.5+1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/image_picker/image_picker_windows/CHANGELOG.md b/packages/image_picker/image_picker_windows/CHANGELOG.md index d98656b849c8..e72ab244068f 100644 --- a/packages/image_picker/image_picker_windows/CHANGELOG.md +++ b/packages/image_picker/image_picker_windows/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.1.0+1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.1.0 diff --git a/packages/image_picker/image_picker_windows/example/lib/main.dart b/packages/image_picker/image_picker_windows/example/lib/main.dart index 577d6dadf2d9..b3ba3574522a 100644 --- a/packages/image_picker/image_picker_windows/example/lib/main.dart +++ b/packages/image_picker/image_picker_windows/example/lib/main.dart @@ -12,10 +12,12 @@ import 'package:image_picker_platform_interface/image_picker_platform_interface. import 'package:video_player/video_player.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( @@ -31,7 +33,7 @@ class MyHomePage extends StatefulWidget { final String? title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -176,17 +178,18 @@ class _MyHomePageState extends State { } if (_imageFileList != null) { return Semantics( - child: ListView.builder( - key: UniqueKey(), - itemBuilder: (BuildContext context, int index) { - return Semantics( - label: 'image_picker_example_picked_image', - child: Image.file(File(_imageFileList![index].path)), - ); - }, - itemCount: _imageFileList!.length, - ), - label: 'image_picker_example_picked_images'); + label: 'image_picker_example_picked_images', + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + return Semantics( + label: 'image_picker_example_picked_image', + child: Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + ); } else if (_pickImageError != null) { return Text( 'Pick image error: $_pickImageError', @@ -363,7 +366,7 @@ typedef OnPickImageCallback = void Function( double? maxWidth, double? maxHeight, int? quality); class AspectRatioVideo extends StatefulWidget { - const AspectRatioVideo(this.controller); + const AspectRatioVideo(this.controller, {Key? key}) : super(key: key); final VideoPlayerController? controller; diff --git a/packages/image_picker/image_picker_windows/pubspec.yaml b/packages/image_picker/image_picker_windows/pubspec.yaml index eec41f7bfa0d..afadf3e39148 100644 --- a/packages/image_picker/image_picker_windows/pubspec.yaml +++ b/packages/image_picker/image_picker_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_windows description: Windows platform implementation of image_picker repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.1.0 +version: 0.1.0+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md index 24ef9eaffd1d..8412c23ee8e8 100644 --- a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md @@ -1,7 +1,9 @@ -## NEXT +## 3.0.3 * Removes unnecessary imports. * Adds OS version support information to README. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 3.0.2 diff --git a/packages/in_app_purchase/in_app_purchase/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase/example/lib/main.dart index 651652b40c3a..34346a0bd339 100644 --- a/packages/in_app_purchase/in_app_purchase/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase/example/lib/main.dart @@ -33,7 +33,7 @@ const List _kProductIds = [ class _MyApp extends StatefulWidget { @override - _MyAppState createState() => _MyAppState(); + State<_MyApp> createState() => _MyAppState(); } class _MyAppState extends State<_MyApp> { @@ -247,60 +247,61 @@ class _MyAppState extends State<_MyApp> { (ProductDetails productDetails) { final PurchaseDetails? previousPurchase = purchases[productDetails.id]; return ListTile( - title: Text( - productDetails.title, - ), - subtitle: Text( - productDetails.description, - ), - trailing: previousPurchase != null - ? IconButton( - onPressed: () => confirmPriceChange(context), - icon: const Icon(Icons.upgrade)) - : TextButton( - child: Text(productDetails.price), - style: TextButton.styleFrom( - backgroundColor: Colors.green[800], - primary: Colors.white, - ), - onPressed: () { - late PurchaseParam purchaseParam; - - if (Platform.isAndroid) { - // NOTE: If you are making a subscription purchase/upgrade/downgrade, we recommend you to - // verify the latest status of you your subscription by using server side receipt validation - // and update the UI accordingly. The subscription purchase status shown - // inside the app may not be accurate. - final GooglePlayPurchaseDetails? oldSubscription = - _getOldSubscription(productDetails, purchases); - - purchaseParam = GooglePlayPurchaseParam( - productDetails: productDetails, - applicationUserName: null, - changeSubscriptionParam: (oldSubscription != null) - ? ChangeSubscriptionParam( - oldPurchaseDetails: oldSubscription, - prorationMode: ProrationMode - .immediateWithTimeProration, - ) - : null); - } else { - purchaseParam = PurchaseParam( + title: Text( + productDetails.title, + ), + subtitle: Text( + productDetails.description, + ), + trailing: previousPurchase != null + ? IconButton( + onPressed: () => confirmPriceChange(context), + icon: const Icon(Icons.upgrade)) + : TextButton( + style: TextButton.styleFrom( + backgroundColor: Colors.green[800], + primary: Colors.white, + ), + onPressed: () { + late PurchaseParam purchaseParam; + + if (Platform.isAndroid) { + // NOTE: If you are making a subscription purchase/upgrade/downgrade, we recommend you to + // verify the latest status of you your subscription by using server side receipt validation + // and update the UI accordingly. The subscription purchase status shown + // inside the app may not be accurate. + final GooglePlayPurchaseDetails? oldSubscription = + _getOldSubscription(productDetails, purchases); + + purchaseParam = GooglePlayPurchaseParam( productDetails: productDetails, applicationUserName: null, - ); - } - - if (productDetails.id == _kConsumableId) { - _inAppPurchase.buyConsumable( - purchaseParam: purchaseParam, - autoConsume: _kAutoConsume || Platform.isIOS); - } else { - _inAppPurchase.buyNonConsumable( - purchaseParam: purchaseParam); - } - }, - )); + changeSubscriptionParam: (oldSubscription != null) + ? ChangeSubscriptionParam( + oldPurchaseDetails: oldSubscription, + prorationMode: + ProrationMode.immediateWithTimeProration, + ) + : null); + } else { + purchaseParam = PurchaseParam( + productDetails: productDetails, + applicationUserName: null, + ); + } + + if (productDetails.id == _kConsumableId) { + _inAppPurchase.buyConsumable( + purchaseParam: purchaseParam, + autoConsume: _kAutoConsume || Platform.isIOS); + } else { + _inAppPurchase.buyNonConsumable( + purchaseParam: purchaseParam); + } + }, + child: Text(productDetails.price), + ), + ); }, )); @@ -340,9 +341,9 @@ class _MyAppState extends State<_MyApp> { const Divider(), GridView.count( crossAxisCount: 5, - children: tokens, shrinkWrap: true, padding: const EdgeInsets.all(16.0), + children: tokens, ) ])); } @@ -359,12 +360,12 @@ class _MyAppState extends State<_MyApp> { mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( - child: const Text('Restore purchases'), style: TextButton.styleFrom( backgroundColor: Theme.of(context).primaryColor, primary: Colors.white, ), onPressed: () => _inAppPurchase.restorePurchases(), + child: const Text('Restore purchases'), ), ], ), diff --git a/packages/in_app_purchase/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/pubspec.yaml index af8f5f3c2773..d2f875293876 100644 --- a/packages/in_app_purchase/in_app_purchase/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase description: A Flutter plugin for in-app purchases. Exposes APIs for making in-app purchases through the App Store and Google Play. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 3.0.2 +version: 3.0.3 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md index 2657d504ac91..4b9e58c08d06 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.2.2+4 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.2.2+3 diff --git a/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart index 939bc43bea63..1da943535f70 100644 --- a/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart @@ -37,7 +37,7 @@ const List _kProductIds = [ class _MyApp extends StatefulWidget { @override - _MyAppState createState() => _MyAppState(); + State<_MyApp> createState() => _MyAppState(); } class _MyAppState extends State<_MyApp> { @@ -264,7 +264,6 @@ class _MyAppState extends State<_MyApp> { }, icon: const Icon(Icons.upgrade)) : TextButton( - child: Text(productDetails.price), style: TextButton.styleFrom( backgroundColor: Colors.green[800], primary: Colors.white, @@ -297,6 +296,7 @@ class _MyAppState extends State<_MyApp> { purchaseParam: purchaseParam); } }, + child: Text(productDetails.price), )); }, )); @@ -337,9 +337,9 @@ class _MyAppState extends State<_MyApp> { const Divider(), GridView.count( crossAxisCount: 5, - children: tokens, shrinkWrap: true, padding: const EdgeInsets.all(16.0), + children: tokens, ) ])); } diff --git a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml index 62888e6dfb73..7de778177c31 100644 --- a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_android description: An implementation for the Android platform of the Flutter `in_app_purchase` plugin. This uses the Android BillingClient APIs. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.2.2+3 +version: 0.2.2+4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md index 403ee32be2ae..7342077ab176 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.3.0+6 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.3.0+5 diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart index 2ee2deb7fe35..f45a8c7f8741 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart @@ -37,7 +37,7 @@ const List _kProductIds = [ class _MyApp extends StatefulWidget { @override - _MyAppState createState() => _MyAppState(); + State<_MyApp> createState() => _MyAppState(); } class _MyAppState extends State<_MyApp> { @@ -259,7 +259,6 @@ class _MyAppState extends State<_MyApp> { }, icon: const Icon(Icons.upgrade)) : TextButton( - child: Text(productDetails.price), style: TextButton.styleFrom( backgroundColor: Colors.green[800], primary: Colors.white, @@ -278,6 +277,7 @@ class _MyAppState extends State<_MyApp> { purchaseParam: purchaseParam); } }, + child: Text(productDetails.price), )); }, )); @@ -318,9 +318,9 @@ class _MyAppState extends State<_MyApp> { const Divider(), GridView.count( crossAxisCount: 5, - children: tokens, shrinkWrap: true, padding: const EdgeInsets.all(16.0), + children: tokens, ) ])); } @@ -337,12 +337,12 @@ class _MyAppState extends State<_MyApp> { mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( - child: const Text('Restore purchases'), style: TextButton.styleFrom( backgroundColor: Theme.of(context).primaryColor, primary: Colors.white, ), onPressed: () => _iapStoreKitPlatform.restorePurchases(), + child: const Text('Restore purchases'), ), ], ), diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml index 9693c186119c..24b693c98c36 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_storekit description: An implementation for the iOS platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase_storekit issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.3.0+5 +version: 0.3.0+6 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/ios_platform_images/CHANGELOG.md b/packages/ios_platform_images/CHANGELOG.md index 0a3755afa7e7..cf2632feaac7 100644 --- a/packages/ios_platform_images/CHANGELOG.md +++ b/packages/ios_platform_images/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.2.0+6 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.2.0+5 diff --git a/packages/ios_platform_images/example/lib/main.dart b/packages/ios_platform_images/example/lib/main.dart index ecdfeb2cba01..929814ecce00 100644 --- a/packages/ios_platform_images/example/lib/main.dart +++ b/packages/ios_platform_images/example/lib/main.dart @@ -5,12 +5,15 @@ import 'package:flutter/material.dart'; import 'package:ios_platform_images/ios_platform_images.dart'; -void main() => runApp(MyApp()); +void main() => runApp(const MyApp()); /// Main widget for the example app. class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/ios_platform_images/example/test/widget_test.dart b/packages/ios_platform_images/example/test/widget_test.dart index 18e9e657ddb9..f3cd4c68b65b 100644 --- a/packages/ios_platform_images/example/test/widget_test.dart +++ b/packages/ios_platform_images/example/test/widget_test.dart @@ -12,7 +12,7 @@ import 'package:ios_platform_images_example/main.dart'; void main() { testWidgets('Verify loads image', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(MyApp()); + await tester.pumpWidget(const MyApp()); expect( find.byWidgetPredicate( diff --git a/packages/ios_platform_images/pubspec.yaml b/packages/ios_platform_images/pubspec.yaml index 7f80714e4c1c..41a177560299 100644 --- a/packages/ios_platform_images/pubspec.yaml +++ b/packages/ios_platform_images/pubspec.yaml @@ -2,7 +2,7 @@ name: ios_platform_images description: A plugin to share images between Flutter and iOS in add-to-app setups. repository: https://github.com/flutter/plugins/tree/main/packages/ios_platform_images issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+ios_platform_images%22 -version: 0.2.0+5 +version: 0.2.0+6 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index 570d8eef7b9b..6765b497e506 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -4,6 +4,8 @@ * Updates README to match API changes in 2.0, and to improve clarity in general. * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.0.0 diff --git a/packages/local_auth/local_auth/example/lib/main.dart b/packages/local_auth/local_auth/example/lib/main.dart index 92ad7cf4fb3f..cc687f562402 100644 --- a/packages/local_auth/local_auth/example/lib/main.dart +++ b/packages/local_auth/local_auth/example/lib/main.dart @@ -11,12 +11,14 @@ import 'package:flutter/services.dart'; import 'package:local_auth/local_auth.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { @@ -169,14 +171,14 @@ class _MyAppState extends State { const Divider(height: 100), Text('Can check biometrics: $_canCheckBiometrics\n'), ElevatedButton( - child: const Text('Check biometrics'), onPressed: _checkBiometrics, + child: const Text('Check biometrics'), ), const Divider(height: 100), Text('Available biometrics: $_availableBiometrics\n'), ElevatedButton( - child: const Text('Get available biometrics'), onPressed: _getAvailableBiometrics, + child: const Text('Get available biometrics'), ), const Divider(height: 100), Text('Current State: $_authorized\n'), @@ -195,6 +197,7 @@ class _MyAppState extends State { Column( children: [ ElevatedButton( + onPressed: _authenticate, child: Row( mainAxisSize: MainAxisSize.min, children: const [ @@ -202,9 +205,9 @@ class _MyAppState extends State { Icon(Icons.perm_device_information), ], ), - onPressed: _authenticate, ), ElevatedButton( + onPressed: _authenticateWithBiometrics, child: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -214,7 +217,6 @@ class _MyAppState extends State { const Icon(Icons.fingerprint), ], ), - onPressed: _authenticateWithBiometrics, ), ], ), diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index 6afcf1ffed07..9f9bd7b535a9 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 1.0.3 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 1.0.2 diff --git a/packages/local_auth/local_auth_android/example/lib/main.dart b/packages/local_auth/local_auth_android/example/lib/main.dart index 29b1d66440eb..016d955f0a3f 100644 --- a/packages/local_auth/local_auth_android/example/lib/main.dart +++ b/packages/local_auth/local_auth_android/example/lib/main.dart @@ -12,12 +12,14 @@ import 'package:local_auth_android/local_auth_android.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { @@ -174,14 +176,14 @@ class _MyAppState extends State { Text( 'Device supports biometrics: $_deviceSupportsBiometrics\n'), ElevatedButton( - child: const Text('Check biometrics'), onPressed: _checkBiometrics, + child: const Text('Check biometrics'), ), const Divider(height: 100), Text('Enrolled biometrics: $_enrolledBiometrics\n'), ElevatedButton( - child: const Text('Get enrolled biometrics'), onPressed: _getEnrolledBiometrics, + child: const Text('Get enrolled biometrics'), ), const Divider(height: 100), Text('Current State: $_authorized\n'), @@ -200,6 +202,7 @@ class _MyAppState extends State { Column( children: [ ElevatedButton( + onPressed: _authenticate, child: Row( mainAxisSize: MainAxisSize.min, children: const [ @@ -207,9 +210,9 @@ class _MyAppState extends State { Icon(Icons.perm_device_information), ], ), - onPressed: _authenticate, ), ElevatedButton( + onPressed: _authenticateWithBiometrics, child: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -219,7 +222,6 @@ class _MyAppState extends State { const Icon(Icons.fingerprint), ], ), - onPressed: _authenticateWithBiometrics, ), ], ), diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml index aad0b9b92e70..cdd4e8225504 100644 --- a/packages/local_auth/local_auth_android/pubspec.yaml +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_android description: Android implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.2 +version: 1.0.3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_ios/CHANGELOG.md b/packages/local_auth/local_auth_ios/CHANGELOG.md index ca6ac43eb52f..2237cbe216f0 100644 --- a/packages/local_auth/local_auth_ios/CHANGELOG.md +++ b/packages/local_auth/local_auth_ios/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 1.0.5 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 1.0.4 diff --git a/packages/local_auth/local_auth_ios/example/lib/main.dart b/packages/local_auth/local_auth_ios/example/lib/main.dart index a8bf23b78a52..479a96ba809c 100644 --- a/packages/local_auth/local_auth_ios/example/lib/main.dart +++ b/packages/local_auth/local_auth_ios/example/lib/main.dart @@ -12,12 +12,14 @@ import 'package:local_auth_ios/local_auth_ios.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { @@ -173,14 +175,14 @@ class _MyAppState extends State { const Divider(height: 100), Text('Device supports biometrics: $_canCheckBiometrics\n'), ElevatedButton( - child: const Text('Check biometrics'), onPressed: _checkBiometrics, + child: const Text('Check biometrics'), ), const Divider(height: 100), Text('Enrolled biometrics: $_enrolledBiometrics\n'), ElevatedButton( - child: const Text('Get enrolled biometrics'), onPressed: _getEnrolledBiometrics, + child: const Text('Get enrolled biometrics'), ), const Divider(height: 100), Text('Current State: $_authorized\n'), @@ -199,6 +201,7 @@ class _MyAppState extends State { Column( children: [ ElevatedButton( + onPressed: _authenticate, child: Row( mainAxisSize: MainAxisSize.min, children: const [ @@ -206,9 +209,9 @@ class _MyAppState extends State { Icon(Icons.perm_device_information), ], ), - onPressed: _authenticate, ), ElevatedButton( + onPressed: _authenticateWithBiometrics, child: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -218,7 +221,6 @@ class _MyAppState extends State { const Icon(Icons.fingerprint), ], ), - onPressed: _authenticateWithBiometrics, ), ], ), diff --git a/packages/local_auth/local_auth_ios/pubspec.yaml b/packages/local_auth/local_auth_ios/pubspec.yaml index 77ab74d383c8..dded42b673f4 100644 --- a/packages/local_auth/local_auth_ios/pubspec.yaml +++ b/packages/local_auth/local_auth_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_ios description: iOS implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.4 +version: 1.0.5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider/CHANGELOG.md b/packages/path_provider/path_provider/CHANGELOG.md index a26a8901d1d7..990021671801 100644 --- a/packages/path_provider/path_provider/CHANGELOG.md +++ b/packages/path_provider/path_provider/CHANGELOG.md @@ -1,7 +1,9 @@ -## NEXT +## 2.0.10 * Removes unnecessary imports. * Adds OS version support information to README. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.0.9 diff --git a/packages/path_provider/path_provider/example/lib/main.dart b/packages/path_provider/path_provider/example/lib/main.dart index 90c2ccb93154..cb9c2eb1798d 100644 --- a/packages/path_provider/path_provider/example/lib/main.dart +++ b/packages/path_provider/path_provider/example/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -31,7 +33,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -138,10 +140,10 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + onPressed: _requestTempDirectory, child: const Text( 'Get Temporary Directory', ), - onPressed: _requestTempDirectory, ), ), FutureBuilder( @@ -155,10 +157,10 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + onPressed: _requestAppDocumentsDirectory, child: const Text( 'Get Application Documents Directory', ), - onPressed: _requestAppDocumentsDirectory, ), ), FutureBuilder( @@ -172,10 +174,10 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + onPressed: _requestAppSupportDirectory, child: const Text( 'Get Application Support Directory', ), - onPressed: _requestAppSupportDirectory, ), ), FutureBuilder( @@ -189,13 +191,13 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + onPressed: + Platform.isAndroid ? null : _requestAppLibraryDirectory, child: Text( Platform.isAndroid ? 'Application Library Directory unavailable' : 'Get Application Library Directory', ), - onPressed: - Platform.isAndroid ? null : _requestAppLibraryDirectory, ), ), FutureBuilder( @@ -209,14 +211,14 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + onPressed: !Platform.isAndroid + ? null + : _requestExternalStorageDirectory, child: Text( !Platform.isAndroid ? 'External storage is unavailable' : 'Get External Storage Directory', ), - onPressed: !Platform.isAndroid - ? null - : _requestExternalStorageDirectory, ), ), FutureBuilder( @@ -230,11 +232,6 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: Text( - !Platform.isAndroid - ? 'External directories are unavailable' - : 'Get External Storage Directories', - ), onPressed: !Platform.isAndroid ? null : () { @@ -242,6 +239,11 @@ class _MyHomePageState extends State { StorageDirectory.music, ); }, + child: Text( + !Platform.isAndroid + ? 'External directories are unavailable' + : 'Get External Storage Directories', + ), ), ), FutureBuilder?>( @@ -255,14 +257,14 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + onPressed: !Platform.isAndroid + ? null + : _requestExternalCacheDirectories, child: Text( !Platform.isAndroid ? 'External directories are unavailable' : 'Get External Cache Directories', ), - onPressed: !Platform.isAndroid - ? null - : _requestExternalCacheDirectories, ), ), FutureBuilder?>( @@ -276,14 +278,14 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + onPressed: Platform.isAndroid || Platform.isIOS + ? null + : _requestDownloadsDirectory, child: Text( Platform.isAndroid || Platform.isIOS ? 'Downloads directory is unavailable' : 'Get Downloads Directory', ), - onPressed: Platform.isAndroid || Platform.isIOS - ? null - : _requestDownloadsDirectory, ), ), FutureBuilder( diff --git a/packages/path_provider/path_provider/pubspec.yaml b/packages/path_provider/path_provider/pubspec.yaml index 30ddfdcfb119..6ca8325843e4 100644 --- a/packages/path_provider/path_provider/pubspec.yaml +++ b/packages/path_provider/path_provider/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider description: Flutter plugin for getting commonly used locations on host platform file systems, such as the temp and app data directories. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.9 +version: 2.0.10 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider_android/CHANGELOG.md b/packages/path_provider/path_provider_android/CHANGELOG.md index 31f8c81f8a65..4b15e2605038 100644 --- a/packages/path_provider/path_provider_android/CHANGELOG.md +++ b/packages/path_provider/path_provider_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.14 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.13 * Fixes typing build warning. diff --git a/packages/path_provider/path_provider_android/example/lib/main.dart b/packages/path_provider/path_provider_android/example/lib/main.dart index 6e04f865bfcf..fc9424a33542 100644 --- a/packages/path_provider/path_provider_android/example/lib/main.dart +++ b/packages/path_provider/path_provider_android/example/lib/main.dart @@ -8,10 +8,12 @@ import 'package:flutter/material.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -29,7 +31,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -121,8 +123,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get Temporary Directory'), onPressed: _requestTempDirectory, + child: const Text('Get Temporary Directory'), ), ), FutureBuilder( @@ -130,8 +132,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get Application Documents Directory'), onPressed: _requestAppDocumentsDirectory, + child: const Text('Get Application Documents Directory'), ), ), FutureBuilder( @@ -139,8 +141,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get Application Support Directory'), onPressed: _requestAppSupportDirectory, + child: const Text('Get Application Support Directory'), ), ), FutureBuilder( @@ -148,8 +150,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get External Storage Directory'), onPressed: _requestExternalStorageDirectory, + child: const Text('Get External Storage Directory'), ), ), FutureBuilder( @@ -174,8 +176,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get External Cache Directories'), onPressed: _requestExternalCacheDirectories, + child: const Text('Get External Cache Directories'), ), ), ]), diff --git a/packages/path_provider/path_provider_android/pubspec.yaml b/packages/path_provider/path_provider_android/pubspec.yaml index 93ed9848f75b..f1dc92abdefc 100644 --- a/packages/path_provider/path_provider_android/pubspec.yaml +++ b/packages/path_provider/path_provider_android/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_android description: Android implementation of the path_provider plugin. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.13 +version: 2.0.14 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider_ios/CHANGELOG.md b/packages/path_provider/path_provider_ios/CHANGELOG.md index 543af778d2e2..1940f5c7888e 100644 --- a/packages/path_provider/path_provider_ios/CHANGELOG.md +++ b/packages/path_provider/path_provider_ios/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.9 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.8 * Switches to a package-internal implementation of the platform interface. diff --git a/packages/path_provider/path_provider_ios/example/lib/main.dart b/packages/path_provider/path_provider_ios/example/lib/main.dart index 8c8d5410f923..d7140b76a06b 100644 --- a/packages/path_provider/path_provider_ios/example/lib/main.dart +++ b/packages/path_provider/path_provider_ios/example/lib/main.dart @@ -8,10 +8,12 @@ import 'package:flutter/material.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -29,7 +31,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -90,8 +92,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get Temporary Directory'), onPressed: _requestTempDirectory, + child: const Text('Get Temporary Directory'), ), ), FutureBuilder( @@ -99,8 +101,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get Application Documents Directory'), onPressed: _requestAppDocumentsDirectory, + child: const Text('Get Application Documents Directory'), ), ), FutureBuilder( @@ -108,8 +110,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get Application Support Directory'), onPressed: _requestAppSupportDirectory, + child: const Text('Get Application Support Directory'), ), ), FutureBuilder( @@ -117,8 +119,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get Application Library Directory'), onPressed: _requestAppLibraryDirectory, + child: const Text('Get Application Library Directory'), ), ), FutureBuilder( diff --git a/packages/path_provider/path_provider_ios/pubspec.yaml b/packages/path_provider/path_provider_ios/pubspec.yaml index 282f8e4f0ebd..d6c7de108c97 100644 --- a/packages/path_provider/path_provider_ios/pubspec.yaml +++ b/packages/path_provider/path_provider_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_ios description: iOS implementation of the path_provider plugin. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.8 +version: 2.0.9 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider_linux/CHANGELOG.md b/packages/path_provider/path_provider_linux/CHANGELOG.md index 55235c3542f9..c9c4bb3cc906 100644 --- a/packages/path_provider/path_provider_linux/CHANGELOG.md +++ b/packages/path_provider/path_provider_linux/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.1.6 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.1.5 * Removes dependency on `meta`. diff --git a/packages/path_provider/path_provider_linux/example/lib/main.dart b/packages/path_provider/path_provider_linux/example/lib/main.dart index d365e6bdeab4..1c7c7e87397a 100644 --- a/packages/path_provider/path_provider_linux/example/lib/main.dart +++ b/packages/path_provider/path_provider_linux/example/lib/main.dart @@ -7,13 +7,16 @@ import 'package:flutter/services.dart'; import 'package:path_provider_linux/path_provider_linux.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// Sample app class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/path_provider/path_provider_linux/pubspec.yaml b/packages/path_provider/path_provider_linux/pubspec.yaml index 91304fc0b268..16438a3870d1 100644 --- a/packages/path_provider/path_provider_linux/pubspec.yaml +++ b/packages/path_provider/path_provider_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_linux description: Linux implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.1.5 +version: 2.1.6 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/path_provider/path_provider_macos/CHANGELOG.md b/packages/path_provider/path_provider_macos/CHANGELOG.md index 047792f8bcc4..c59ba971d461 100644 --- a/packages/path_provider/path_provider_macos/CHANGELOG.md +++ b/packages/path_provider/path_provider_macos/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.6 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.5 * Removes dependency on `meta`. diff --git a/packages/path_provider/path_provider_macos/example/lib/main.dart b/packages/path_provider/path_provider_macos/example/lib/main.dart index 67a0eb32eeda..13a6fada5fef 100644 --- a/packages/path_provider/path_provider_macos/example/lib/main.dart +++ b/packages/path_provider/path_provider_macos/example/lib/main.dart @@ -8,13 +8,15 @@ import 'package:flutter/material.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// Sample app class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/path_provider/path_provider_macos/pubspec.yaml b/packages/path_provider/path_provider_macos/pubspec.yaml index 2451b6dedf80..444165b86c3f 100644 --- a/packages/path_provider/path_provider_macos/pubspec.yaml +++ b/packages/path_provider/path_provider_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_macos description: macOS implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.5 +version: 2.0.6 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/path_provider/path_provider_windows/CHANGELOG.md b/packages/path_provider/path_provider_windows/CHANGELOG.md index cd33e85a851d..014b6b36da2b 100644 --- a/packages/path_provider/path_provider_windows/CHANGELOG.md +++ b/packages/path_provider/path_provider_windows/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.6 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.5 * Removes dependency on `meta`. diff --git a/packages/path_provider/path_provider_windows/example/lib/main.dart b/packages/path_provider/path_provider_windows/example/lib/main.dart index 509292bf7405..4c63d245a16a 100644 --- a/packages/path_provider/path_provider_windows/example/lib/main.dart +++ b/packages/path_provider/path_provider_windows/example/lib/main.dart @@ -8,13 +8,15 @@ import 'package:flutter/material.dart'; import 'package:path_provider_windows/path_provider_windows.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// Sample app class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/path_provider/path_provider_windows/pubspec.yaml b/packages/path_provider/path_provider_windows/pubspec.yaml index 873fa0a6861b..49afdd6293e7 100644 --- a/packages/path_provider/path_provider_windows/pubspec.yaml +++ b/packages/path_provider/path_provider_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_windows description: Windows implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.5 +version: 2.0.6 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/quick_actions/quick_actions/CHANGELOG.md b/packages/quick_actions/quick_actions/CHANGELOG.md index c30d7052320e..73540a863364 100644 --- a/packages/quick_actions/quick_actions/CHANGELOG.md +++ b/packages/quick_actions/quick_actions/CHANGELOG.md @@ -1,8 +1,10 @@ -## NEXT +## 0.6.0+11 * Removes unnecessary imports. * Updates minimum Flutter version to 2.8. * Adds OS version support information to README. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.6.0+10 diff --git a/packages/quick_actions/quick_actions/example/lib/main.dart b/packages/quick_actions/quick_actions/example/lib/main.dart index 1ce6f51d71de..cafbf0c351d9 100644 --- a/packages/quick_actions/quick_actions/example/lib/main.dart +++ b/packages/quick_actions/quick_actions/example/lib/main.dart @@ -8,10 +8,12 @@ import 'package:flutter/material.dart'; import 'package:quick_actions/quick_actions.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -28,7 +30,7 @@ class MyHomePage extends StatefulWidget { const MyHomePage({Key? key}) : super(key: key); @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/quick_actions/quick_actions/pubspec.yaml b/packages/quick_actions/quick_actions/pubspec.yaml index 8ef2d3ab4e02..37e8dbe5f3e3 100644 --- a/packages/quick_actions/quick_actions/pubspec.yaml +++ b/packages/quick_actions/quick_actions/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for creating shortcuts on home screen, also known as Quick Actions on iOS and App Shortcuts on Android. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+quick_actions%22 -version: 0.6.0+10 +version: 0.6.0+11 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/quick_actions/quick_actions_android/CHANGELOG.md b/packages/quick_actions/quick_actions_android/CHANGELOG.md index 98e8cf5e333b..56accc9d044c 100644 --- a/packages/quick_actions/quick_actions_android/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.6.0+10 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 0.6.0+9 * Switches to a package-internal implementation of the platform interface. \ No newline at end of file diff --git a/packages/quick_actions/quick_actions_android/example/lib/main.dart b/packages/quick_actions/quick_actions_android/example/lib/main.dart index 06f141073b33..d8b7832bf9dc 100644 --- a/packages/quick_actions/quick_actions_android/example/lib/main.dart +++ b/packages/quick_actions/quick_actions_android/example/lib/main.dart @@ -8,10 +8,12 @@ import 'package:flutter/material.dart'; import 'package:quick_actions_android/quick_actions_android.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -28,7 +30,7 @@ class MyHomePage extends StatefulWidget { const MyHomePage({Key? key}) : super(key: key); @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/quick_actions/quick_actions_android/pubspec.yaml b/packages/quick_actions/quick_actions_android/pubspec.yaml index cf9971dca945..4ddbc79ee5e9 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/plugins/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: 0.6.0+9 +version: 0.6.0+10 environment: sdk: ">=2.15.0 <3.0.0" diff --git a/packages/quick_actions/quick_actions_ios/CHANGELOG.md b/packages/quick_actions/quick_actions_ios/CHANGELOG.md index d48afbd8d13a..56accc9d044c 100644 --- a/packages/quick_actions/quick_actions_ios/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.0+10 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.6.0+9 diff --git a/packages/quick_actions/quick_actions_ios/example/lib/main.dart b/packages/quick_actions/quick_actions_ios/example/lib/main.dart index 5173d952d623..008917b724e0 100644 --- a/packages/quick_actions/quick_actions_ios/example/lib/main.dart +++ b/packages/quick_actions/quick_actions_ios/example/lib/main.dart @@ -8,10 +8,12 @@ import 'package:flutter/material.dart'; import 'package:quick_actions_ios/quick_actions_ios.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -28,7 +30,7 @@ class MyHomePage extends StatefulWidget { const MyHomePage({Key? key}) : super(key: key); @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/quick_actions/quick_actions_ios/pubspec.yaml b/packages/quick_actions/quick_actions_ios/pubspec.yaml index 26644ba12fde..47748b9789ad 100644 --- a/packages/quick_actions/quick_actions_ios/pubspec.yaml +++ b/packages/quick_actions/quick_actions_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: quick_actions_ios description: An implementation for the iOS platform of the Flutter `quick_actions` plugin. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.6.0+9 +version: 0.6.0+10 environment: sdk: ">=2.15.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences/CHANGELOG.md b/packages/shared_preferences/shared_preferences/CHANGELOG.md index 84566e26e2c0..22c39aad98fd 100644 --- a/packages/shared_preferences/shared_preferences/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.0.14 * Adds OS version support information to README. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.0.13 diff --git a/packages/shared_preferences/shared_preferences/example/lib/main.dart b/packages/shared_preferences/shared_preferences/example/lib/main.dart index 43f3c78ad920..a2e72b446925 100644 --- a/packages/shared_preferences/shared_preferences/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences/example/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences/pubspec.yaml b/packages/shared_preferences/shared_preferences/pubspec.yaml index 39b48ef51ed2..4218095c0efe 100644 --- a/packages/shared_preferences/shared_preferences/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for reading and writing simple key-value pairs. Wraps NSUserDefaults on iOS and SharedPreferences on Android. repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.13 +version: 2.0.14 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md index 5321e869c497..51e99ec6d3d5 100644 --- a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.12 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.11 * Switches to an in-package method channel implementation. diff --git a/packages/shared_preferences/shared_preferences_android/example/lib/main.dart b/packages/shared_preferences/shared_preferences_android/example/lib/main.dart index 06ee9434ba84..bb513b09f6d5 100644 --- a/packages/shared_preferences/shared_preferences_android/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_android/example/lib/main.dart @@ -8,10 +8,12 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_android/pubspec.yaml b/packages/shared_preferences/shared_preferences_android/pubspec.yaml index 7eb180f3ab48..2d8cc88d3703 100644 --- a/packages/shared_preferences/shared_preferences_android/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_android/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_android description: Android implementation of the shared_preferences plugin repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.11 +version: 2.0.12 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md b/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md index a5cc1d34e034..29ade8d496a1 100644 --- a/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.1.1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.1.0 * Upgrades to using Pigeon. diff --git a/packages/shared_preferences/shared_preferences_ios/example/lib/main.dart b/packages/shared_preferences/shared_preferences_ios/example/lib/main.dart index 06ee9434ba84..bb513b09f6d5 100644 --- a/packages/shared_preferences/shared_preferences_ios/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_ios/example/lib/main.dart @@ -8,10 +8,12 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_ios/pubspec.yaml b/packages/shared_preferences/shared_preferences_ios/pubspec.yaml index 33bf5baffd18..a8bde2e9f87f 100644 --- a/packages/shared_preferences/shared_preferences_ios/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_ios description: iOS implementation of the shared_preferences plugin repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.1.0 +version: 2.1.1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md index 34dd631746bb..f0cb8322f385 100644 --- a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.1.1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.1.0 diff --git a/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart b/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart index 4b71c7ea3beb..d51be33baeed 100644 --- a/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_linux/shared_preferences_linux.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml index 8ab692a613e2..8f3ce1723bc9 100644 --- a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_linux description: Linux implementation of the shared_preferences plugin repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.1.0 +version: 2.1.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md b/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md index 0f194de44224..8ba116a74fe0 100644 --- a/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.0.4 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.0.3 diff --git a/packages/shared_preferences/shared_preferences_macos/example/lib/main.dart b/packages/shared_preferences/shared_preferences_macos/example/lib/main.dart index 349e9c45405a..e6bbe5931471 100644 --- a/packages/shared_preferences/shared_preferences_macos/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_macos/example/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_macos/pubspec.yaml b/packages/shared_preferences/shared_preferences_macos/pubspec.yaml index 0873696e6d4f..615d0b05ba99 100644 --- a/packages/shared_preferences/shared_preferences_macos/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_macos description: macOS implementation of the shared_preferences plugin. repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.3 +version: 2.0.4 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_web/CHANGELOG.md b/packages/shared_preferences/shared_preferences_web/CHANGELOG.md index 2a6ffa20e37b..9ea249034105 100644 --- a/packages/shared_preferences/shared_preferences_web/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.4 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.3 * Fixes newly enabled analyzer options. diff --git a/packages/shared_preferences/shared_preferences_web/example/lib/main.dart b/packages/shared_preferences/shared_preferences_web/example/lib/main.dart index 341913a18490..87422953de6a 100644 --- a/packages/shared_preferences/shared_preferences_web/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_web/example/lib/main.dart @@ -5,13 +5,16 @@ import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// App for testing class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/shared_preferences/shared_preferences_web/pubspec.yaml b/packages/shared_preferences/shared_preferences_web/pubspec.yaml index 232c87c426fc..9ff76d27714c 100644 --- a/packages/shared_preferences/shared_preferences_web/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_web/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_web description: Web platform implementation of shared_preferences repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.3 +version: 2.0.4 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md b/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md index 6c96681ce5b7..f79f9e3d5d39 100644 --- a/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.1.1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.1.0 * Deprecated `SharedPreferencesWindows.instance` in favor of `SharedPreferencesStorePlatform.instance`. diff --git a/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart b/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart index 40a9159cee70..74d5e4c68772 100644 --- a/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_windows/shared_preferences_windows.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_windows/pubspec.yaml b/packages/shared_preferences/shared_preferences_windows/pubspec.yaml index 6dcb5997f131..99326cb24f18 100644 --- a/packages/shared_preferences/shared_preferences_windows/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_windows description: Windows implementation of shared_preferences repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.1.0 +version: 2.1.1 environment: sdk: '>=2.12.0 <3.0.0' diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index b25956fd5919..493412c3e006 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 6.1.1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 6.1.0 diff --git a/packages/url_launcher/url_launcher/example/lib/main.dart b/packages/url_launcher/url_launcher/example/lib/main.dart index 898e80661296..a538940f1a68 100644 --- a/packages/url_launcher/url_launcher/example/lib/main.dart +++ b/packages/url_launcher/url_launcher/example/lib/main.dart @@ -11,10 +11,12 @@ import 'package:url_launcher/link.dart'; import 'package:url_launcher/url_launcher.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -32,7 +34,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher/lib/src/link.dart b/packages/url_launcher/url_launcher/lib/src/link.dart index 76cb97748003..8c0c18e820e3 100644 --- a/packages/url_launcher/url_launcher/lib/src/link.dart +++ b/packages/url_launcher/url_launcher/lib/src/link.dart @@ -86,7 +86,7 @@ class Link extends StatelessWidget implements LinkInfo { /// event channel messages to instruct the framework to push the route name. class DefaultLinkDelegate extends StatelessWidget { /// Creates a delegate for the given [link]. - const DefaultLinkDelegate(this.link); + const DefaultLinkDelegate(this.link, {Key? key}) : super(key: key); /// Given a [link], creates an instance of [DefaultLinkDelegate]. /// diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index 6803d71032cb..c14b62a1e70a 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.1.0 +version: 6.1.1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_android/CHANGELOG.md b/packages/url_launcher/url_launcher_android/CHANGELOG.md index 69b96156d849..887178c479e4 100644 --- a/packages/url_launcher/url_launcher_android/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 6.0.17 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 6.0.16 * Adds fallback querying for `canLaunch` with web URLs, to avoid false negatives diff --git a/packages/url_launcher/url_launcher_android/example/lib/main.dart b/packages/url_launcher/url_launcher_android/example/lib/main.dart index 7abc73430e8b..672ae4a27665 100644 --- a/packages/url_launcher/url_launcher_android/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_android/example/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -31,7 +33,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_android/pubspec.yaml b/packages/url_launcher/url_launcher_android/pubspec.yaml index 3230dfeffd2e..3c80170f1422 100644 --- a/packages/url_launcher/url_launcher_android/pubspec.yaml +++ b/packages/url_launcher/url_launcher_android/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_android description: Android implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.0.16 +version: 6.0.17 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_ios/CHANGELOG.md b/packages/url_launcher/url_launcher_ios/CHANGELOG.md index 6e0c8d6a20d7..5f6dd37142bb 100644 --- a/packages/url_launcher/url_launcher_ios/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_ios/CHANGELOG.md @@ -1,3 +1,8 @@ +## 6.0.16 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 6.0.15 * Switches to an in-package method channel implementation. diff --git a/packages/url_launcher/url_launcher_ios/example/lib/main.dart b/packages/url_launcher/url_launcher_ios/example/lib/main.dart index 2f73622ebb41..7aa3a4b74e83 100644 --- a/packages/url_launcher/url_launcher_ios/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_ios/example/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -31,7 +33,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_ios/pubspec.yaml b/packages/url_launcher/url_launcher_ios/pubspec.yaml index 8a5bfd20c8f4..0b21bad35204 100644 --- a/packages/url_launcher/url_launcher_ios/pubspec.yaml +++ b/packages/url_launcher/url_launcher_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_ios description: iOS implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.0.15 +version: 6.0.16 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_linux/CHANGELOG.md b/packages/url_launcher/url_launcher_linux/CHANGELOG.md index 0fc373f2ebb1..27c18a66805b 100644 --- a/packages/url_launcher/url_launcher_linux/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_linux/CHANGELOG.md @@ -1,3 +1,8 @@ +## 3.0.1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 3.0.0 * Changes the major version since, due to a typo in `default_package` in diff --git a/packages/url_launcher/url_launcher_linux/example/lib/main.dart b/packages/url_launcher/url_launcher_linux/example/lib/main.dart index a9a5d22dadd5..0b985e78ac0d 100644 --- a/packages/url_launcher/url_launcher_linux/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_linux/example/lib/main.dart @@ -9,10 +9,12 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -30,7 +32,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_linux/pubspec.yaml b/packages/url_launcher/url_launcher_linux/pubspec.yaml index cb9a0be0aa41..c9472045e499 100644 --- a/packages/url_launcher/url_launcher_linux/pubspec.yaml +++ b/packages/url_launcher/url_launcher_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_linux description: Linux implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 3.0.0 +version: 3.0.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_macos/CHANGELOG.md b/packages/url_launcher/url_launcher_macos/CHANGELOG.md index 082bc45fc2e8..2fa5e918eadd 100644 --- a/packages/url_launcher/url_launcher_macos/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_macos/CHANGELOG.md @@ -1,3 +1,8 @@ +## 3.0.1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 3.0.0 * Changes the major version since, due to a typo in `default_package` in diff --git a/packages/url_launcher/url_launcher_macos/example/lib/main.dart b/packages/url_launcher/url_launcher_macos/example/lib/main.dart index a9a5d22dadd5..0b985e78ac0d 100644 --- a/packages/url_launcher/url_launcher_macos/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_macos/example/lib/main.dart @@ -9,10 +9,12 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -30,7 +32,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_macos/pubspec.yaml b/packages/url_launcher/url_launcher_macos/pubspec.yaml index 8b5183b1914d..edda6b67cfb3 100644 --- a/packages/url_launcher/url_launcher_macos/pubspec.yaml +++ b/packages/url_launcher/url_launcher_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_macos description: macOS implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 3.0.0 +version: 3.0.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_web/CHANGELOG.md b/packages/url_launcher/url_launcher_web/CHANGELOG.md index a434b7af70c2..b53a92cee707 100644 --- a/packages/url_launcher/url_launcher_web/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.10 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.9 - Fixes invalid routes when opening a `Link` in a new tab diff --git a/packages/url_launcher/url_launcher_web/example/lib/main.dart b/packages/url_launcher/url_launcher_web/example/lib/main.dart index 341913a18490..87422953de6a 100644 --- a/packages/url_launcher/url_launcher_web/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_web/example/lib/main.dart @@ -5,13 +5,16 @@ import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// App for testing class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/url_launcher/url_launcher_web/lib/src/link.dart b/packages/url_launcher/url_launcher_web/lib/src/link.dart index 4498e74ea9ce..eccd9aef80e3 100644 --- a/packages/url_launcher/url_launcher_web/lib/src/link.dart +++ b/packages/url_launcher/url_launcher_web/lib/src/link.dart @@ -31,7 +31,7 @@ HtmlViewFactory get linkViewFactory => LinkViewController._viewFactory; /// It uses a platform view to render an anchor element in the DOM. class WebLinkDelegate extends StatefulWidget { /// Creates a delegate for the given [link]. - const WebLinkDelegate(this.link); + const WebLinkDelegate(this.link, {Key? key}) : super(key: key); /// Information about the link built by the app. final LinkInfo link; diff --git a/packages/url_launcher/url_launcher_web/pubspec.yaml b/packages/url_launcher/url_launcher_web/pubspec.yaml index c45c062255ad..cd8ed2d269c8 100644 --- a/packages/url_launcher/url_launcher_web/pubspec.yaml +++ b/packages/url_launcher/url_launcher_web/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_web description: Web platform implementation of url_launcher repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 2.0.9 +version: 2.0.10 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_windows/CHANGELOG.md b/packages/url_launcher/url_launcher_windows/CHANGELOG.md index e02f5a2288e1..3ff14fd2f18a 100644 --- a/packages/url_launcher/url_launcher_windows/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_windows/CHANGELOG.md @@ -1,3 +1,8 @@ +## 3.0.1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 3.0.0 * Changes the major version since, due to a typo in `default_package` in diff --git a/packages/url_launcher/url_launcher_windows/example/lib/main.dart b/packages/url_launcher/url_launcher_windows/example/lib/main.dart index a9a5d22dadd5..0b985e78ac0d 100644 --- a/packages/url_launcher/url_launcher_windows/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_windows/example/lib/main.dart @@ -9,10 +9,12 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -30,7 +32,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_windows/pubspec.yaml b/packages/url_launcher/url_launcher_windows/pubspec.yaml index 95f17ad9e921..c3f224e26adf 100644 --- a/packages/url_launcher/url_launcher_windows/pubspec.yaml +++ b/packages/url_launcher/url_launcher_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_windows description: Windows implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 3.0.0 +version: 3.0.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index af01c64fd554..ede890162f86 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.4.1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.4.0 diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index 39cd415dbb79..a6ec51015c33 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -708,14 +708,14 @@ class _VideoAppLifeCycleObserver extends Object with WidgetsBindingObserver { /// Widget that displays the video controlled by [controller]. class VideoPlayer extends StatefulWidget { /// Uses the given [controller] for all video rendered in this widget. - const VideoPlayer(this.controller); + const VideoPlayer(this.controller, {Key? key}) : super(key: key); /// The [VideoPlayerController] responsible for the video being rendered in /// this widget. final VideoPlayerController controller; @override - _VideoPlayerState createState() => _VideoPlayerState(); + State createState() => _VideoPlayerState(); } class _VideoPlayerState extends State { @@ -883,10 +883,11 @@ class VideoProgressIndicator extends StatefulWidget { /// to `top: 5.0`. const VideoProgressIndicator( this.controller, { + Key? key, this.colors = const VideoProgressColors(), required this.allowScrubbing, this.padding = const EdgeInsets.only(top: 5.0), - }); + }) : super(key: key); /// The [VideoPlayerController] that actually associates a video with this /// widget. @@ -910,7 +911,7 @@ class VideoProgressIndicator extends StatefulWidget { final EdgeInsets padding; @override - _VideoProgressIndicatorState createState() => _VideoProgressIndicatorState(); + State createState() => _VideoProgressIndicatorState(); } class _VideoProgressIndicatorState extends State { @@ -984,8 +985,8 @@ class _VideoProgressIndicatorState extends State { ); if (widget.allowScrubbing) { return _VideoScrubber( - child: paddedProgressIndicator, controller: controller, + child: paddedProgressIndicator, ); } else { return paddedProgressIndicator; diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 0d654a4330a7..b0ca56429271 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for displaying inline video with other Flutter widgets on Android, iOS, and web. repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.4.0 +version: 2.4.1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index 16dd52ca6da0..08acba895eba 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.3.3 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.3.2 diff --git a/packages/video_player/video_player_android/example/lib/mini_controller.dart b/packages/video_player/video_player_android/example/lib/mini_controller.dart index 498dbffc9e84..5bce3117d0d6 100644 --- a/packages/video_player/video_player_android/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_android/example/lib/mini_controller.dart @@ -351,14 +351,14 @@ class MiniController extends ValueNotifier { /// Widget that displays the video controlled by [controller]. class VideoPlayer extends StatefulWidget { /// Uses the given [controller] for all video rendered in this widget. - const VideoPlayer(this.controller); + const VideoPlayer(this.controller, {Key? key}) : super(key: key); /// The [MiniController] responsible for the video being rendered in /// this widget. final MiniController controller; @override - _VideoPlayerState createState() => _VideoPlayerState(); + State createState() => _VideoPlayerState(); } class _VideoPlayerState extends State { @@ -450,14 +450,14 @@ class _VideoScrubberState extends State<_VideoScrubber> { class VideoProgressIndicator extends StatefulWidget { /// Construct an instance that displays the play/buffering status of the video /// controlled by [controller]. - const VideoProgressIndicator(this.controller); + const VideoProgressIndicator(this.controller, {Key? key}) : super(key: key); /// The [MiniController] that actually associates a video with this /// widget. final MiniController controller; @override - _VideoProgressIndicatorState createState() => _VideoProgressIndicatorState(); + State createState() => _VideoProgressIndicatorState(); } class _VideoProgressIndicatorState extends State { @@ -527,11 +527,11 @@ class _VideoProgressIndicatorState extends State { ); } return _VideoScrubber( + controller: controller, child: Padding( padding: const EdgeInsets.only(top: 5.0), child: progressIndicator, ), - controller: controller, ); } } diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index aa288ed71eac..bc69fd41369a 100644 --- a/packages/video_player/video_player_android/pubspec.yaml +++ b/packages/video_player/video_player_android/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_android description: Android implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.2 +version: 2.3.3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index d77c36c915b6..6ab5398f7013 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.3.4 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.3.3 diff --git a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart index 498dbffc9e84..5bce3117d0d6 100644 --- a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart @@ -351,14 +351,14 @@ class MiniController extends ValueNotifier { /// Widget that displays the video controlled by [controller]. class VideoPlayer extends StatefulWidget { /// Uses the given [controller] for all video rendered in this widget. - const VideoPlayer(this.controller); + const VideoPlayer(this.controller, {Key? key}) : super(key: key); /// The [MiniController] responsible for the video being rendered in /// this widget. final MiniController controller; @override - _VideoPlayerState createState() => _VideoPlayerState(); + State createState() => _VideoPlayerState(); } class _VideoPlayerState extends State { @@ -450,14 +450,14 @@ class _VideoScrubberState extends State<_VideoScrubber> { class VideoProgressIndicator extends StatefulWidget { /// Construct an instance that displays the play/buffering status of the video /// controlled by [controller]. - const VideoProgressIndicator(this.controller); + const VideoProgressIndicator(this.controller, {Key? key}) : super(key: key); /// The [MiniController] that actually associates a video with this /// widget. final MiniController controller; @override - _VideoProgressIndicatorState createState() => _VideoProgressIndicatorState(); + State createState() => _VideoProgressIndicatorState(); } class _VideoProgressIndicatorState extends State { @@ -527,11 +527,11 @@ class _VideoProgressIndicatorState extends State { ); } return _VideoScrubber( + controller: controller, child: Padding( padding: const EdgeInsets.only(top: 5.0), child: progressIndicator, ), - controller: controller, ); } } diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index b3cc69eca958..380d8343c024 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_avfoundation description: iOS implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.3 +version: 2.3.4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md index 00788c4386fe..094ffda207c5 100644 --- a/packages/video_player/video_player_web/CHANGELOG.md +++ b/packages/video_player/video_player_web/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.0.9 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.0.8 diff --git a/packages/video_player/video_player_web/example/lib/main.dart b/packages/video_player/video_player_web/example/lib/main.dart index 341913a18490..87422953de6a 100644 --- a/packages/video_player/video_player_web/example/lib/main.dart +++ b/packages/video_player/video_player_web/example/lib/main.dart @@ -5,13 +5,16 @@ import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// App for testing class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml index 064517e1f264..7af0dc46dde8 100644 --- a/packages/video_player/video_player_web/pubspec.yaml +++ b/packages/video_player/video_player_web/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_web description: Web platform implementation of video_player. repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.0.8 +version: 2.0.9 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index 7a56f3f176d0..fa47a6cc3143 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 3.0.3 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 3.0.2 @@ -11,7 +13,7 @@ * Removes a duplicate Android-specific integration test. * Fixes an integration test race condition. -* Fixes comments (accidentially mixed // with ///). +* Fixes comments (accidentally mixed // with ///). ## 3.0.0 diff --git a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart index ba321264ee1e..cc001f336849 100644 --- a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart @@ -1306,7 +1306,8 @@ Future _runJavascriptReturningResult( class ResizableWebView extends StatefulWidget { const ResizableWebView( - {required this.onResize, required this.onPageFinished}); + {Key? key, required this.onResize, required this.onPageFinished}) + : super(key: key); final JavascriptMessageHandler onResize; final VoidCallback onPageFinished; diff --git a/packages/webview_flutter/webview_flutter/example/lib/main.dart b/packages/webview_flutter/webview_flutter/example/lib/main.dart index 51080be01df0..3d8731127970 100644 --- a/packages/webview_flutter/webview_flutter/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter/example/lib/main.dart @@ -71,12 +71,12 @@ const String kTransparentBackgroundPage = ''' '''; class WebViewExample extends StatefulWidget { - const WebViewExample({this.cookieManager}); + const WebViewExample({Key? key, this.cookieManager}) : super(key: key); final CookieManager? cookieManager; @override - _WebViewExampleState createState() => _WebViewExampleState(); + State createState() => _WebViewExampleState(); } class _WebViewExampleState extends State { @@ -190,8 +190,9 @@ enum MenuOptions { } class SampleMenu extends StatelessWidget { - SampleMenu(this.controller, CookieManager? cookieManager) - : cookieManager = cookieManager ?? CookieManager(); + SampleMenu(this.controller, CookieManager? cookieManager, {Key? key}) + : cookieManager = cookieManager ?? CookieManager(), + super(key: key); final Future controller; late final CookieManager cookieManager; @@ -250,8 +251,8 @@ class SampleMenu extends StatelessWidget { itemBuilder: (BuildContext context) => >[ PopupMenuItem( value: MenuOptions.showUserAgent, - child: const Text('Show user agent'), enabled: controller.hasData, + child: const Text('Show user agent'), ), const PopupMenuItem( value: MenuOptions.listCookies, @@ -443,8 +444,9 @@ class SampleMenu extends StatelessWidget { } class NavigationControls extends StatelessWidget { - const NavigationControls(this._webViewControllerFuture) - : assert(_webViewControllerFuture != null); + const NavigationControls(this._webViewControllerFuture, {Key? key}) + : assert(_webViewControllerFuture != null), + super(key: key); final Future _webViewControllerFuture; diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index 10350984ce9a..a48f6f912c2d 100644 --- a/packages/webview_flutter/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter description: A Flutter plugin that provides a WebView widget on Android and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 3.0.2 +version: 3.0.3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index edaa0883713f..4a451442f6cc 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.8.7 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.8.6 diff --git a/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart index 51e09912da23..4c06fa6b3c18 100644 --- a/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart @@ -1446,9 +1446,10 @@ Future _runJavaScriptReturningResult( class ResizableWebView extends StatefulWidget { const ResizableWebView({ + Key? key, required this.onResize, required this.onPageFinished, - }); + }) : super(key: key); final JavascriptMessageHandler onResize; final VoidCallback onPageFinished; diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart index 5d19ca71ac84..349a64916e8b 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart @@ -247,8 +247,8 @@ class _SampleMenu extends StatelessWidget { itemBuilder: (BuildContext context) => >[ PopupMenuItem<_MenuOptions>( value: _MenuOptions.showUserAgent, - child: const Text('Show user agent'), enabled: controller.hasData, + child: const Text('Show user agent'), ), const PopupMenuItem<_MenuOptions>( value: _MenuOptions.listCookies, diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart index 91ea66376904..56745314d92b 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart @@ -250,7 +250,7 @@ class WebView extends StatefulWidget { final Color? backgroundColor; @override - _WebViewState createState() => _WebViewState(); + State createState() => _WebViewState(); } class _WebViewState extends State { diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart index 28d169c9cb94..f1b130c7e365 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart @@ -15,6 +15,7 @@ import 'src/android_webview.dart' as android_webview; class WebViewAndroidWidget extends StatefulWidget { /// Constructs a [WebViewAndroidWidget]. const WebViewAndroidWidget({ + Key? key, required this.creationParams, required this.useHybridComposition, required this.callbacksHandler, @@ -24,7 +25,7 @@ class WebViewAndroidWidget extends StatefulWidget { @visibleForTesting this.flutterAssetManager = const android_webview.FlutterAssetManager(), @visibleForTesting this.webStorage, - }); + }) : super(key: key); /// Initial parameters used to setup the WebView. final CreationParams creationParams; diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 9a7c48a4dcd8..407887f2ba95 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_android description: A Flutter plugin that provides a WebView widget on Android. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.8.6 +version: 2.8.7 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md index 9f7ebe368941..b7254e1a0a7a 100644 --- a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md @@ -1,7 +1,9 @@ -## NEXT +## 0.1.0+2 * Removes unnecessary imports. * Fixes unit tests to run on latest `master` version of Flutter. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.1.0+1 diff --git a/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart index 8cd74f660f58..ffd3367d33f4 100644 --- a/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart @@ -46,7 +46,7 @@ class WebView extends StatefulWidget { final String? initialUrl; @override - _WebViewState createState() => _WebViewState(); + State createState() => _WebViewState(); } class _WebViewState extends State { diff --git a/packages/webview_flutter/webview_flutter_web/pubspec.yaml b/packages/webview_flutter/webview_flutter_web/pubspec.yaml index bd154387097e..35a7b74a764c 100644 --- a/packages/webview_flutter/webview_flutter_web/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_web/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_web description: A Flutter plugin that provides a WebView widget on web. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 0.1.0+1 +version: 0.1.0+2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index f042dd081475..6db769b0d922 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.7.4 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.7.3 diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart index ceff62e3d5e8..aa376f8358e9 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart @@ -1181,7 +1181,8 @@ Future _getUserAgent(WebViewController controller) async { class ResizableWebView extends StatefulWidget { const ResizableWebView( - {required this.onResize, required this.onPageFinished}); + {Key? key, required this.onResize, required this.onPageFinished}) + : super(key: key); final JavascriptMessageHandler onResize; final VoidCallback onPageFinished; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart index e8b86d9c5773..7b30923e1e54 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart @@ -232,8 +232,8 @@ class _SampleMenu extends StatelessWidget { itemBuilder: (BuildContext context) => >[ PopupMenuItem<_MenuOptions>( value: _MenuOptions.showUserAgent, - child: const Text('Show user agent'), enabled: controller.hasData, + child: const Text('Show user agent'), ), const PopupMenuItem<_MenuOptions>( value: _MenuOptions.listCookies, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart index 4d479f943d62..c44c4e743669 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart @@ -240,7 +240,7 @@ class WebView extends StatefulWidget { final Color? backgroundColor; @override - _WebViewState createState() => _WebViewState(); + State createState() => _WebViewState(); } class _WebViewState extends State { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 012cd221599b..8c37112d7a24 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -18,13 +18,14 @@ import 'web_kit/web_kit.dart'; class WebKitWebViewWidget extends StatefulWidget { /// Constructs a [WebKitWebViewWidget]. const WebKitWebViewWidget({ + Key? key, required this.creationParams, required this.callbacksHandler, required this.javascriptChannelRegistry, required this.onBuildWidget, this.configuration, @visibleForTesting this.webViewProxy = const WebViewWidgetProxy(), - }); + }) : super(key: key); /// The initial parameters used to setup the WebView. final CreationParams creationParams; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index f4e72b8f14eb..365e64720d4f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_wkwebview description: A Flutter plugin that provides a WebView widget based on Apple's WKWebView control. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_wkwebview issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.7.3 +version: 2.7.4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index b2319c63dc48..5faa7c8201d9 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -322,7 +322,7 @@ and `firebase-test-lab`. ## v.0.0.36+2 -- Default to showing podspec lint warnings +- Default to showing podspec lint warnings. ## v.0.0.36+1 diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart index 5c38bd5f7033..b0a8990e1300 100644 --- a/script/tool/test/util.dart +++ b/script/tool/test/util.dart @@ -319,14 +319,14 @@ String _pluginPlatformSection( return entry; } -typedef _ErrorHandler = void Function(Error error); +typedef ErrorHandler = void Function(Error error); /// Run the command [runner] with the given [args] and return /// what was printed. /// A custom [errorHandler] can be used to handle the runner error as desired without throwing. Future> runCapturingPrint( CommandRunner runner, List args, - {_ErrorHandler? errorHandler}) async { + {ErrorHandler? errorHandler}) async { final List prints = []; final ZoneSpecification spec = ZoneSpecification( print: (_, __, ___, String message) { From edfd9c0bf2602e3000f9689bc59431cf56fa99bc Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 10 May 2022 13:05:18 -0400 Subject: [PATCH 541/600] Revert "Enable lints `library_private_types_in_public_api`, `sort_child_properties_last` and `use_key_in_widget_constructors`" (#5691) This reverts commit 4b7b67916242a9c82283a642f169fbdb7f9045be. This includes a fix for a latent bug in the version-check repo tooling command that caused it to fail when reverting a package that previously had a NEXT section, so that tests will pass. --- analysis_options.yaml | 5 +- packages/camera/camera/CHANGELOG.md | 4 +- packages/camera/camera/README.md | 14 +-- packages/camera/camera/example/lib/main.dart | 60 +++++----- .../example/lib/readme_full_example.dart | 16 ++- .../camera/camera/example/test/main_test.dart | 2 +- .../camera/camera/lib/src/camera_preview.dart | 3 +- packages/camera/camera/pubspec.yaml | 2 +- packages/camera/camera_web/CHANGELOG.md | 5 - .../camera/camera_web/example/lib/main.dart | 5 +- packages/camera/camera_web/pubspec.yaml | 2 +- packages/camera/camera_windows/CHANGELOG.md | 4 +- .../camera_windows/example/lib/main.dart | 5 +- packages/camera/camera_windows/pubspec.yaml | 2 +- packages/espresso/CHANGELOG.md | 7 +- packages/espresso/example/lib/main.dart | 7 +- packages/espresso/pubspec.yaml | 2 +- .../file_selector/file_selector/CHANGELOG.md | 4 +- .../example/lib/get_directory_page.dart | 5 +- .../file_selector/example/lib/home_page.dart | 3 - .../file_selector/example/lib/main.dart | 16 +-- .../example/lib/open_image_page.dart | 6 +- .../lib/open_multiple_images_page.dart | 5 +- .../example/lib/open_text_page.dart | 6 +- .../example/lib/save_text_page.dart | 3 - .../file_selector/file_selector/pubspec.yaml | 2 +- .../file_selector_macos/CHANGELOG.md | 4 +- .../example/lib/get_directory_page.dart | 5 +- .../example/lib/home_page.dart | 3 - .../file_selector_macos/example/lib/main.dart | 16 +-- .../example/lib/open_image_page.dart | 6 +- .../lib/open_multiple_images_page.dart | 5 +- .../example/lib/open_text_page.dart | 6 +- .../example/lib/save_text_page.dart | 5 +- .../file_selector_macos/pubspec.yaml | 2 +- .../CHANGELOG.md | 4 +- .../pubspec.yaml | 2 +- .../file_selector_web/CHANGELOG.md | 5 - .../file_selector_web/example/lib/main.dart | 7 +- .../file_selector_web/pubspec.yaml | 2 +- .../file_selector_windows/CHANGELOG.md | 4 +- .../example/lib/get_directory_page.dart | 5 +- .../example/lib/home_page.dart | 3 - .../example/lib/main.dart | 16 +-- .../example/lib/open_image_page.dart | 6 +- .../lib/open_multiple_images_page.dart | 5 +- .../example/lib/open_text_page.dart | 6 +- .../example/lib/save_text_page.dart | 5 +- .../file_selector_windows/pubspec.yaml | 2 +- .../CHANGELOG.md | 4 +- ...flutter_plugin_android_lifecycle_test.dart | 2 +- .../example/lib/main.dart | 8 +- .../pubspec.yaml | 2 +- .../google_maps_flutter/CHANGELOG.md | 6 +- .../example/lib/animate_camera.dart | 6 +- .../example/lib/lite_mode.dart | 3 +- .../google_maps_flutter/example/lib/main.dart | 8 +- .../example/lib/map_click.dart | 3 +- .../example/lib/map_coordinates.dart | 3 +- .../example/lib/map_ui.dart | 5 +- .../example/lib/marker_icons.dart | 5 +- .../example/lib/move_camera.dart | 5 +- .../example/lib/padding.dart | 5 +- .../google_maps_flutter/example/lib/page.dart | 3 +- .../example/lib/place_circle.dart | 18 +-- .../example/lib/place_marker.dart | 31 +++-- .../example/lib/place_polygon.dart | 24 ++-- .../example/lib/place_polyline.dart | 26 ++-- .../example/lib/scrolling_map.dart | 5 +- .../example/lib/snapshot.dart | 5 +- .../example/lib/tile_overlay.dart | 11 +- .../lib/src/controller.dart | 2 - .../google_maps_flutter/pubspec.yaml | 2 +- .../google_maps_flutter_web/CHANGELOG.md | 4 +- .../example/lib/main.dart | 2 +- .../google_maps_flutter_web/pubspec.yaml | 2 +- .../google_sign_in/CHANGELOG.md | 5 - .../google_sign_in/example/lib/main.dart | 8 +- .../google_sign_in/lib/widgets.dart | 4 +- .../google_sign_in/pubspec.yaml | 2 +- .../google_sign_in_android/CHANGELOG.md | 5 - .../example/lib/main.dart | 8 +- .../google_sign_in_android/pubspec.yaml | 2 +- .../google_sign_in_ios/CHANGELOG.md | 5 - .../google_sign_in_ios/example/lib/main.dart | 8 +- .../google_sign_in_ios/pubspec.yaml | 2 +- .../google_sign_in_web/CHANGELOG.md | 5 - .../google_sign_in_web/example/lib/main.dart | 7 +- .../google_sign_in_web/pubspec.yaml | 2 +- .../image_picker/image_picker/CHANGELOG.md | 5 - .../image_picker/example/lib/main.dart | 39 +++--- .../image_picker/image_picker/pubspec.yaml | 2 +- .../image_picker_android/CHANGELOG.md | 5 - .../example/lib/main.dart | 39 +++--- .../image_picker_android/pubspec.yaml | 2 +- .../image_picker_for_web/CHANGELOG.md | 5 - .../example/lib/main.dart | 7 +- .../image_picker_for_web/pubspec.yaml | 2 +- .../image_picker_ios/CHANGELOG.md | 4 +- .../image_picker_ios/example/lib/main.dart | 39 +++--- .../image_picker_ios/pubspec.yaml | 2 +- .../image_picker_windows/CHANGELOG.md | 4 +- .../example/lib/main.dart | 31 +++-- .../image_picker_windows/pubspec.yaml | 2 +- .../in_app_purchase/CHANGELOG.md | 4 +- .../in_app_purchase/example/lib/main.dart | 111 +++++++++--------- .../in_app_purchase/pubspec.yaml | 2 +- .../in_app_purchase_android/CHANGELOG.md | 4 +- .../example/lib/main.dart | 6 +- .../in_app_purchase_android/pubspec.yaml | 2 +- .../in_app_purchase_storekit/CHANGELOG.md | 4 +- .../example/lib/main.dart | 8 +- .../in_app_purchase_storekit/pubspec.yaml | 2 +- packages/ios_platform_images/CHANGELOG.md | 4 +- .../ios_platform_images/example/lib/main.dart | 7 +- .../example/test/widget_test.dart | 2 +- packages/ios_platform_images/pubspec.yaml | 2 +- packages/local_auth/local_auth/CHANGELOG.md | 2 - .../local_auth/example/lib/main.dart | 14 +-- .../local_auth_android/CHANGELOG.md | 4 +- .../local_auth_android/example/lib/main.dart | 14 +-- .../local_auth_android/pubspec.yaml | 2 +- .../local_auth/local_auth_ios/CHANGELOG.md | 4 +- .../local_auth_ios/example/lib/main.dart | 14 +-- .../local_auth/local_auth_ios/pubspec.yaml | 2 +- .../path_provider/path_provider/CHANGELOG.md | 4 +- .../path_provider/example/lib/main.dart | 44 ++++--- .../path_provider/path_provider/pubspec.yaml | 2 +- .../path_provider_android/CHANGELOG.md | 5 - .../example/lib/main.dart | 16 ++- .../path_provider_android/pubspec.yaml | 2 +- .../path_provider_ios/CHANGELOG.md | 5 - .../path_provider_ios/example/lib/main.dart | 14 +-- .../path_provider_ios/pubspec.yaml | 2 +- .../path_provider_linux/CHANGELOG.md | 5 - .../path_provider_linux/example/lib/main.dart | 7 +- .../path_provider_linux/pubspec.yaml | 2 +- .../path_provider_macos/CHANGELOG.md | 5 - .../path_provider_macos/example/lib/main.dart | 6 +- .../path_provider_macos/pubspec.yaml | 2 +- .../path_provider_windows/CHANGELOG.md | 5 - .../example/lib/main.dart | 6 +- .../path_provider_windows/pubspec.yaml | 2 +- .../quick_actions/quick_actions/CHANGELOG.md | 4 +- .../quick_actions/example/lib/main.dart | 6 +- .../quick_actions/quick_actions/pubspec.yaml | 2 +- .../quick_actions_android/CHANGELOG.md | 5 - .../example/lib/main.dart | 6 +- .../quick_actions_android/pubspec.yaml | 2 +- .../quick_actions_ios/CHANGELOG.md | 4 - .../quick_actions_ios/example/lib/main.dart | 6 +- .../quick_actions_ios/pubspec.yaml | 2 +- .../shared_preferences/CHANGELOG.md | 4 +- .../shared_preferences/example/lib/main.dart | 4 +- .../shared_preferences/pubspec.yaml | 2 +- .../shared_preferences_android/CHANGELOG.md | 5 - .../example/lib/main.dart | 4 +- .../shared_preferences_android/pubspec.yaml | 2 +- .../shared_preferences_ios/CHANGELOG.md | 5 - .../example/lib/main.dart | 4 +- .../shared_preferences_ios/pubspec.yaml | 2 +- .../shared_preferences_linux/CHANGELOG.md | 4 +- .../example/lib/main.dart | 4 +- .../shared_preferences_linux/pubspec.yaml | 2 +- .../shared_preferences_macos/CHANGELOG.md | 4 +- .../example/lib/main.dart | 4 +- .../shared_preferences_macos/pubspec.yaml | 2 +- .../shared_preferences_web/CHANGELOG.md | 5 - .../example/lib/main.dart | 7 +- .../shared_preferences_web/pubspec.yaml | 2 +- .../shared_preferences_windows/CHANGELOG.md | 5 - .../example/lib/main.dart | 4 +- .../shared_preferences_windows/pubspec.yaml | 2 +- .../url_launcher/url_launcher/CHANGELOG.md | 4 +- .../url_launcher/example/lib/main.dart | 6 +- .../url_launcher/lib/src/link.dart | 2 +- .../url_launcher/url_launcher/pubspec.yaml | 2 +- .../url_launcher_android/CHANGELOG.md | 5 - .../example/lib/main.dart | 6 +- .../url_launcher_android/pubspec.yaml | 2 +- .../url_launcher_ios/CHANGELOG.md | 5 - .../url_launcher_ios/example/lib/main.dart | 6 +- .../url_launcher_ios/pubspec.yaml | 2 +- .../url_launcher_linux/CHANGELOG.md | 5 - .../url_launcher_linux/example/lib/main.dart | 6 +- .../url_launcher_linux/pubspec.yaml | 2 +- .../url_launcher_macos/CHANGELOG.md | 5 - .../url_launcher_macos/example/lib/main.dart | 6 +- .../url_launcher_macos/pubspec.yaml | 2 +- .../url_launcher_web/CHANGELOG.md | 5 - .../url_launcher_web/example/lib/main.dart | 7 +- .../url_launcher_web/lib/src/link.dart | 2 +- .../url_launcher_web/pubspec.yaml | 2 +- .../url_launcher_windows/CHANGELOG.md | 5 - .../example/lib/main.dart | 6 +- .../url_launcher_windows/pubspec.yaml | 2 +- .../video_player/video_player/CHANGELOG.md | 4 +- .../video_player/lib/video_player.dart | 11 +- .../video_player/video_player/pubspec.yaml | 2 +- .../video_player_android/CHANGELOG.md | 4 +- .../example/lib/mini_controller.dart | 10 +- .../video_player_android/pubspec.yaml | 2 +- .../video_player_avfoundation/CHANGELOG.md | 4 +- .../example/lib/mini_controller.dart | 10 +- .../video_player_avfoundation/pubspec.yaml | 2 +- .../video_player_web/CHANGELOG.md | 4 +- .../video_player_web/example/lib/main.dart | 7 +- .../video_player_web/pubspec.yaml | 2 +- .../webview_flutter/CHANGELOG.md | 6 +- .../webview_flutter_test.dart | 3 +- .../webview_flutter/example/lib/main.dart | 16 ++- .../webview_flutter/pubspec.yaml | 2 +- .../webview_flutter_android/CHANGELOG.md | 4 +- .../webview_flutter_test.dart | 3 +- .../example/lib/main.dart | 2 +- .../example/lib/web_view.dart | 2 +- .../lib/webview_android_widget.dart | 3 +- .../webview_flutter_android/pubspec.yaml | 2 +- .../webview_flutter_web/CHANGELOG.md | 4 +- .../example/lib/web_view.dart | 2 +- .../webview_flutter_web/pubspec.yaml | 2 +- .../webview_flutter_wkwebview/CHANGELOG.md | 4 +- .../webview_flutter_test.dart | 3 +- .../example/lib/main.dart | 2 +- .../example/lib/web_view.dart | 2 +- .../lib/src/web_kit_webview_widget.dart | 3 +- .../webview_flutter_wkwebview/pubspec.yaml | 2 +- script/tool/CHANGELOG.md | 6 +- .../tool/lib/src/version_check_command.dart | 25 ++-- script/tool/test/util.dart | 4 +- .../tool/test/version_check_command_test.dart | 31 ++++- 231 files changed, 574 insertions(+), 948 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index ba5e0a9c4ced..f6177cd9939a 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -134,7 +134,6 @@ linter: - leading_newlines_in_multiline_strings - library_names - library_prefixes - - library_private_types_in_public_api # - lines_longer_than_80_chars # not required by flutter style - list_remove_unrelated_type # - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/sdk/issues/34181 @@ -198,7 +197,7 @@ linter: - recursive_getters # - sized_box_for_whitespace # not yet tested - slash_for_doc_comments - - sort_child_properties_last + # - sort_child_properties_last # not yet tested - sort_constructors_first - sort_unnamed_constructors_first - test_types_in_equals @@ -230,7 +229,7 @@ linter: - use_full_hex_values_for_flutter_colors # - use_function_type_syntax_for_parameters # not yet tested - use_is_even_rather_than_modulo - - use_key_in_widget_constructors + # - use_key_in_widget_constructors # not yet tested - use_late_for_private_fields_and_variables - use_raw_strings - use_rethrow_when_possible diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index bf0ccf86a82e..4d7e9bbeb218 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,8 +1,6 @@ -## 0.9.4+22 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 0.9.4+21 diff --git a/packages/camera/camera/README.md b/packages/camera/camera/README.md index 0bcaeaeb3b7c..97b16d20f48a 100644 --- a/packages/camera/camera/README.md +++ b/packages/camera/camera/README.md @@ -89,22 +89,18 @@ Here is a small example flutter app displaying a full screen camera preview. import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; -late List _cameras; +late List cameras; Future main() async { WidgetsFlutterBinding.ensureInitialized(); - _cameras = await availableCameras(); - runApp(const CameraApp()); + cameras = await availableCameras(); + runApp(CameraApp()); } -/// CameraApp is the Main Application. class CameraApp extends StatefulWidget { - /// Default Constructor - const CameraApp({Key? key}) : super(key: key); - @override - State createState() => _CameraAppState(); + _CameraAppState createState() => _CameraAppState(); } class _CameraAppState extends State { @@ -113,7 +109,7 @@ class _CameraAppState extends State { @override void initState() { super.initState(); - controller = CameraController(_cameras[0], ResolutionPreset.max); + controller = CameraController(cameras[0], ResolutionPreset.max); controller.initialize().then((_) { if (!mounted) { return; diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index a645326f2803..aabbe249313d 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: public_member_api_docs + import 'dart:async'; import 'dart:io'; @@ -11,13 +13,9 @@ import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:video_player/video_player.dart'; -/// Camera example home widget. class CameraExampleHome extends StatefulWidget { - /// Default Constructor - const CameraExampleHome({Key? key}) : super(key: key); - @override - State createState() { + _CameraExampleHomeState createState() { return _CameraExampleHomeState(); } } @@ -36,7 +34,7 @@ IconData getCameraLensIcon(CameraLensDirection direction) { } } -void _logError(String code, String? message) { +void logError(String code, String? message) { if (message != null) { print('Error: $code\nError Message: $message'); } else { @@ -136,6 +134,12 @@ class _CameraExampleHomeState extends State children: [ Expanded( child: Container( + child: Padding( + padding: const EdgeInsets.all(1.0), + child: Center( + child: _cameraPreviewWidget(), + ), + ), decoration: BoxDecoration( color: Colors.black, border: Border.all( @@ -146,12 +150,6 @@ class _CameraExampleHomeState extends State width: 3.0, ), ), - child: Padding( - padding: const EdgeInsets.all(1.0), - child: Center( - child: _cameraPreviewWidget(), - ), - ), ), ), _captureControlRowWidget(), @@ -235,8 +233,6 @@ class _CameraExampleHomeState extends State Container() else SizedBox( - width: 64.0, - height: 64.0, child: (localVideoController == null) ? ( // The captured image on the web contains a network-accessible URL @@ -247,8 +243,6 @@ class _CameraExampleHomeState extends State ? Image.network(imageFile!.path) : Image.file(File(imageFile!.path))) : Container( - decoration: BoxDecoration( - border: Border.all(color: Colors.pink)), child: Center( child: AspectRatio( aspectRatio: @@ -257,7 +251,11 @@ class _CameraExampleHomeState extends State : 1.0, child: VideoPlayer(localVideoController)), ), + decoration: BoxDecoration( + border: Border.all(color: Colors.pink)), ), + width: 64.0, + height: 64.0, ), ], ), @@ -396,6 +394,7 @@ class _CameraExampleHomeState extends State mainAxisSize: MainAxisSize.max, children: [ TextButton( + child: const Text('AUTO'), style: styleAuto, onPressed: controller != null ? () => @@ -407,22 +406,21 @@ class _CameraExampleHomeState extends State showInSnackBar('Resetting exposure point'); } }, - child: const Text('AUTO'), ), TextButton( + child: const Text('LOCKED'), style: styleLocked, onPressed: controller != null ? () => onSetExposureModeButtonPressed(ExposureMode.locked) : null, - child: const Text('LOCKED'), ), TextButton( + child: const Text('RESET OFFSET'), style: styleLocked, onPressed: controller != null ? () => controller!.setExposureOffset(0.0) : null, - child: const Text('RESET OFFSET'), ), ], ), @@ -481,6 +479,7 @@ class _CameraExampleHomeState extends State mainAxisSize: MainAxisSize.max, children: [ TextButton( + child: const Text('AUTO'), style: styleAuto, onPressed: controller != null ? () => onSetFocusModeButtonPressed(FocusMode.auto) @@ -491,14 +490,13 @@ class _CameraExampleHomeState extends State } showInSnackBar('Resetting focus point'); }, - child: const Text('AUTO'), ), TextButton( + child: const Text('LOCKED'), style: styleLocked, onPressed: controller != null ? () => onSetFocusModeButtonPressed(FocusMode.locked) : null, - child: const Text('LOCKED'), ), ], ), @@ -584,13 +582,13 @@ class _CameraExampleHomeState extends State onNewCameraSelected(description); }; - if (_cameras.isEmpty) { + if (cameras.isEmpty) { _ambiguate(SchedulerBinding.instance)?.addPostFrameCallback((_) async { showInSnackBar('No camera found.'); }); return const Text('None'); } else { - for (final CameraDescription cameraDescription in _cameras) { + for (final CameraDescription cameraDescription in cameras) { toggles.add( SizedBox( width: 90.0, @@ -1016,35 +1014,31 @@ class _CameraExampleHomeState extends State } void _showCameraException(CameraException e) { - _logError(e.code, e.description); + logError(e.code, e.description); showInSnackBar('Error: ${e.code}\n${e.description}'); } } -/// CameraApp is the Main Application. class CameraApp extends StatelessWidget { - /// Default Constructor - const CameraApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { - return const MaterialApp( + return MaterialApp( home: CameraExampleHome(), ); } } -List _cameras = []; +List cameras = []; Future main() async { // Fetch the available cameras before initializing the app. try { WidgetsFlutterBinding.ensureInitialized(); - _cameras = await availableCameras(); + cameras = await availableCameras(); } on CameraException catch (e) { - _logError(e.code, e.description); + logError(e.code, e.description); } - runApp(const CameraApp()); + runApp(CameraApp()); } /// This allows a value of type T or T? to be treated as a value of type T?. diff --git a/packages/camera/camera/example/lib/readme_full_example.dart b/packages/camera/camera/example/lib/readme_full_example.dart index a310fd9daeb0..b25e637a0c95 100644 --- a/packages/camera/camera/example/lib/readme_full_example.dart +++ b/packages/camera/camera/example/lib/readme_full_example.dart @@ -2,26 +2,24 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: public_member_api_docs + // #docregion FullAppExample import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; -late List _cameras; +late List cameras; Future main() async { WidgetsFlutterBinding.ensureInitialized(); - _cameras = await availableCameras(); - runApp(const CameraApp()); + cameras = await availableCameras(); + runApp(CameraApp()); } -/// CameraApp is the Main Application. class CameraApp extends StatefulWidget { - /// Default Constructor - const CameraApp({Key? key}) : super(key: key); - @override - State createState() => _CameraAppState(); + _CameraAppState createState() => _CameraAppState(); } class _CameraAppState extends State { @@ -30,7 +28,7 @@ class _CameraAppState extends State { @override void initState() { super.initState(); - controller = CameraController(_cameras[0], ResolutionPreset.max); + controller = CameraController(cameras[0], ResolutionPreset.max); controller.initialize().then((_) { if (!mounted) { return; diff --git a/packages/camera/camera/example/test/main_test.dart b/packages/camera/camera/example/test/main_test.dart index 6e909efcfc62..9a5fcdf2d5ea 100644 --- a/packages/camera/camera/example/test/main_test.dart +++ b/packages/camera/camera/example/test/main_test.dart @@ -9,7 +9,7 @@ import 'package:flutter_test/flutter_test.dart'; void main() { testWidgets('Test snackbar', (WidgetTester tester) async { WidgetsFlutterBinding.ensureInitialized(); - await tester.pumpWidget(const CameraApp()); + await tester.pumpWidget(CameraApp()); await tester.pumpAndSettle(); expect(find.byType(SnackBar), findsOneWidget); }); diff --git a/packages/camera/camera/lib/src/camera_preview.dart b/packages/camera/camera/lib/src/camera_preview.dart index 94ffca649fa6..a9b3f2143b49 100644 --- a/packages/camera/camera/lib/src/camera_preview.dart +++ b/packages/camera/camera/lib/src/camera_preview.dart @@ -10,8 +10,7 @@ import 'package:flutter/services.dart'; /// A widget showing a live camera preview. class CameraPreview extends StatelessWidget { /// Creates a preview widget for the given camera controller. - const CameraPreview(this.controller, {Key? key, this.child}) - : super(key: key); + const CameraPreview(this.controller, {this.child}); /// The controller for the camera that the preview is shown for. final CameraController controller; diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index fde6663844c2..f62777044617 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+22 +version: 0.9.4+21 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index 7a24e12e5029..852e4a03fd43 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,8 +1,3 @@ -## 0.2.1+5 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 0.2.1+4 * Migrates from `ui.hash*` to `Object.hash*`. diff --git a/packages/camera/camera_web/example/lib/main.dart b/packages/camera/camera_web/example/lib/main.dart index 670891fa5009..ab04ce2ca2c7 100644 --- a/packages/camera/camera_web/example/lib/main.dart +++ b/packages/camera/camera_web/example/lib/main.dart @@ -4,13 +4,10 @@ import 'package:flutter/material.dart'; -void main() => runApp(const MyApp()); +void main() => runApp(MyApp()); /// App for testing class MyApp extends StatelessWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return const Directionality( diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index 8bef974190b2..2d1a4508eb73 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_web description: A Flutter plugin for getting information about and controlling the camera on Web. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.2.1+5 +version: 0.2.1+4 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/camera/camera_windows/CHANGELOG.md b/packages/camera/camera_windows/CHANGELOG.md index 0f3bf441b05a..b1383dc54993 100644 --- a/packages/camera/camera_windows/CHANGELOG.md +++ b/packages/camera/camera_windows/CHANGELOG.md @@ -1,8 +1,6 @@ -## 0.1.0+1 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 0.1.0 diff --git a/packages/camera/camera_windows/example/lib/main.dart b/packages/camera/camera_windows/example/lib/main.dart index 5758b0f1e397..b73e00cac52b 100644 --- a/packages/camera/camera_windows/example/lib/main.dart +++ b/packages/camera/camera_windows/example/lib/main.dart @@ -9,14 +9,11 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } /// Example app for Camera Windows plugin. class MyApp extends StatefulWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - @override State createState() => _MyAppState(); } diff --git a/packages/camera/camera_windows/pubspec.yaml b/packages/camera/camera_windows/pubspec.yaml index fe655b04e8c8..1081c3dfc01f 100644 --- a/packages/camera/camera_windows/pubspec.yaml +++ b/packages/camera/camera_windows/pubspec.yaml @@ -1,8 +1,8 @@ name: camera_windows description: A Flutter plugin for getting information about and controlling the camera on Windows. +version: 0.1.0 repository: https://github.com/flutter/plugins/tree/master/packages/camera/camera_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.1.0+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/espresso/CHANGELOG.md b/packages/espresso/CHANGELOG.md index dad0a912e174..eb1f267ca1d3 100644 --- a/packages/espresso/CHANGELOG.md +++ b/packages/espresso/CHANGELOG.md @@ -1,13 +1,8 @@ -## 0.2.0+2 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 0.2.0+1 * Adds OS version support information to README. * Updates `androidx.test.ext:junit` and `androidx.test.ext:truth` for - compatibility with updated Flutter template. + compatibilty with updated Flutter template. ## 0.2.0 diff --git a/packages/espresso/example/lib/main.dart b/packages/espresso/example/lib/main.dart index 741cd9cf9fa2..14f94abb28c8 100644 --- a/packages/espresso/example/lib/main.dart +++ b/packages/espresso/example/lib/main.dart @@ -4,13 +4,10 @@ import 'package:flutter/material.dart'; -void main() => runApp(const MyApp()); +void main() => runApp(MyApp()); /// Example app for Espresso plugin. class MyApp extends StatelessWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - // This widget is the root of your application. @override Widget build(BuildContext context) { @@ -48,7 +45,7 @@ class _MyHomePage extends StatefulWidget { final String title; @override - State<_MyHomePage> createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<_MyHomePage> { diff --git a/packages/espresso/pubspec.yaml b/packages/espresso/pubspec.yaml index ac0199cf045f..7737fc46d4b6 100644 --- a/packages/espresso/pubspec.yaml +++ b/packages/espresso/pubspec.yaml @@ -3,7 +3,7 @@ description: Java classes for testing Flutter apps using Espresso. Allows driving Flutter widgets from a native Espresso test. repository: https://github.com/flutter/plugins/tree/main/packages/espresso issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+espresso%22 -version: 0.2.0+2 +version: 0.2.0+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index 17baf9f12469..c0821fed7446 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,9 +1,7 @@ -## 0.8.4+2 +## NEXT * Removes unnecessary imports. * Adds OS version support information to README. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 0.8.4+1 diff --git a/packages/file_selector/file_selector/example/lib/get_directory_page.dart b/packages/file_selector/file_selector/example/lib/get_directory_page.dart index 506134d66bd8..b3ed9d0eeaca 100644 --- a/packages/file_selector/file_selector/example/lib/get_directory_page.dart +++ b/packages/file_selector/file_selector/example/lib/get_directory_page.dart @@ -7,9 +7,6 @@ import 'package:flutter/material.dart'; /// Screen that shows an example of getDirectoryPath class GetDirectoryPage extends StatelessWidget { - /// Default Constructor - const GetDirectoryPage({Key? key}) : super(key: key); - Future _getDirectoryPath(BuildContext context) async { const String confirmButtonText = 'Choose'; final String? directoryPath = await getDirectoryPath( @@ -53,7 +50,7 @@ class GetDirectoryPage extends StatelessWidget { /// Widget that displays a text file in a dialog class TextDisplay extends StatelessWidget { /// Default Constructor - const TextDisplay(this.directoryPath, {Key? key}) : super(key: key); + const TextDisplay(this.directoryPath); /// Directory path final String directoryPath; diff --git a/packages/file_selector/file_selector/example/lib/home_page.dart b/packages/file_selector/file_selector/example/lib/home_page.dart index 9a0733b56b93..c598cbdf2611 100644 --- a/packages/file_selector/file_selector/example/lib/home_page.dart +++ b/packages/file_selector/file_selector/example/lib/home_page.dart @@ -6,9 +6,6 @@ import 'package:flutter/material.dart'; /// Home Page of the application class HomePage extends StatelessWidget { - /// Default Constructor - const HomePage({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { final ButtonStyle style = ElevatedButton.styleFrom( diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index 34f5857ab0bc..14ce3f593f33 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -11,14 +11,11 @@ import 'package:example/save_text_page.dart'; import 'package:flutter/material.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } /// MyApp is the Main Application class MyApp extends StatelessWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -27,14 +24,13 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: const HomePage(), + home: HomePage(), routes: { - '/open/image': (BuildContext context) => const OpenImagePage(), - '/open/images': (BuildContext context) => - const OpenMultipleImagesPage(), - '/open/text': (BuildContext context) => const OpenTextPage(), + '/open/image': (BuildContext context) => OpenImagePage(), + '/open/images': (BuildContext context) => OpenMultipleImagesPage(), + '/open/text': (BuildContext context) => OpenTextPage(), '/save/text': (BuildContext context) => SaveTextPage(), - '/directory': (BuildContext context) => const GetDirectoryPage(), + '/directory': (BuildContext context) => GetDirectoryPage(), }, ); } diff --git a/packages/file_selector/file_selector/example/lib/open_image_page.dart b/packages/file_selector/file_selector/example/lib/open_image_page.dart index e520ffb402aa..0abdba6eb72d 100644 --- a/packages/file_selector/file_selector/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_image_page.dart @@ -10,9 +10,6 @@ import 'package:flutter/material.dart'; /// Screen that shows an example of openFiles class OpenImagePage extends StatelessWidget { - /// Default Constructor - const OpenImagePage({Key? key}) : super(key: key); - Future _openImageFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'images', @@ -62,8 +59,7 @@ class OpenImagePage extends StatelessWidget { /// Widget that displays a text file in a dialog class ImageDisplay extends StatelessWidget { /// Default Constructor - const ImageDisplay(this.fileName, this.filePath, {Key? key}) - : super(key: key); + const ImageDisplay(this.fileName, this.filePath); /// Image's name final String fileName; diff --git a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart index e2d21c7f04d1..9a1101214aaa 100644 --- a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart @@ -10,9 +10,6 @@ import 'package:flutter/material.dart'; /// Screen that shows an example of openFiles class OpenMultipleImagesPage extends StatelessWidget { - /// Default Constructor - const OpenMultipleImagesPage({Key? key}) : super(key: key); - Future _openImageFile(BuildContext context) async { final XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', @@ -64,7 +61,7 @@ class OpenMultipleImagesPage extends StatelessWidget { /// Widget that displays a text file in a dialog class MultipleImagesDisplay extends StatelessWidget { /// Default Constructor - const MultipleImagesDisplay(this.files, {Key? key}) : super(key: key); + const MultipleImagesDisplay(this.files); /// The files containing the images final List files; diff --git a/packages/file_selector/file_selector/example/lib/open_text_page.dart b/packages/file_selector/file_selector/example/lib/open_text_page.dart index be48a434282b..652e8596cf81 100644 --- a/packages/file_selector/file_selector/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_text_page.dart @@ -7,9 +7,6 @@ import 'package:flutter/material.dart'; /// Screen that shows an example of openFile class OpenTextPage extends StatelessWidget { - /// Default Constructor - const OpenTextPage({Key? key}) : super(key: key); - Future _openTextFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'text', @@ -58,8 +55,7 @@ class OpenTextPage extends StatelessWidget { /// Widget that displays a text file in a dialog class TextDisplay extends StatelessWidget { /// Default Constructor - const TextDisplay(this.fileName, this.fileContent, {Key? key}) - : super(key: key); + const TextDisplay(this.fileName, this.fileContent); /// File's name final String fileName; diff --git a/packages/file_selector/file_selector/example/lib/save_text_page.dart b/packages/file_selector/file_selector/example/lib/save_text_page.dart index b0317844ec36..108ef89b0248 100644 --- a/packages/file_selector/file_selector/example/lib/save_text_page.dart +++ b/packages/file_selector/file_selector/example/lib/save_text_page.dart @@ -8,9 +8,6 @@ import 'package:flutter/material.dart'; /// Page for showing an example of saving with file_selector class SaveTextPage extends StatelessWidget { - /// Default Constructor - SaveTextPage({Key? key}) : super(key: key); - final TextEditingController _nameController = TextEditingController(); final TextEditingController _contentController = TextEditingController(); diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index 7026f7f32287..c05900f650cf 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for opening and saving files, or selecting directories, using native file selection UI. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.4+2 +version: 0.8.4+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_macos/CHANGELOG.md b/packages/file_selector/file_selector_macos/CHANGELOG.md index 19724a513a9e..b46a174bd323 100644 --- a/packages/file_selector/file_selector_macos/CHANGELOG.md +++ b/packages/file_selector/file_selector_macos/CHANGELOG.md @@ -1,8 +1,6 @@ -## 0.8.2+1 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 0.8.2 diff --git a/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart b/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart index a27ab2b40aba..0e55df8ce622 100644 --- a/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart @@ -8,9 +8,6 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a directory using `getDirectoryPath`, /// then displays the selected directory in a dialog. class GetDirectoryPage extends StatelessWidget { - /// Default Constructor - const GetDirectoryPage({Key? key}) : super(key: key); - Future _getDirectoryPath(BuildContext context) async { const String confirmButtonText = 'Choose'; final String? directoryPath = @@ -55,7 +52,7 @@ class GetDirectoryPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class TextDisplay extends StatelessWidget { /// Creates a `TextDisplay`. - const TextDisplay(this.directoryPath, {Key? key}) : super(key: key); + const TextDisplay(this.directoryPath); /// The path selected in the dialog. final String directoryPath; diff --git a/packages/file_selector/file_selector_macos/example/lib/home_page.dart b/packages/file_selector/file_selector_macos/example/lib/home_page.dart index 4d6ca7e6e6e3..958680be0e3b 100644 --- a/packages/file_selector/file_selector_macos/example/lib/home_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/home_page.dart @@ -6,9 +6,6 @@ import 'package:flutter/material.dart'; /// Home Page of the application. class HomePage extends StatelessWidget { - /// Default Constructor - const HomePage({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { final ButtonStyle style = ElevatedButton.styleFrom( diff --git a/packages/file_selector/file_selector_macos/example/lib/main.dart b/packages/file_selector/file_selector_macos/example/lib/main.dart index cbe268e1c7ab..a49ebac1aea5 100644 --- a/packages/file_selector/file_selector_macos/example/lib/main.dart +++ b/packages/file_selector/file_selector_macos/example/lib/main.dart @@ -11,14 +11,11 @@ import 'package:example/save_text_page.dart'; import 'package:flutter/material.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } /// MyApp is the Main Application. class MyApp extends StatelessWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -27,14 +24,13 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: const HomePage(), + home: HomePage(), routes: { - '/open/image': (BuildContext context) => const OpenImagePage(), - '/open/images': (BuildContext context) => - const OpenMultipleImagesPage(), - '/open/text': (BuildContext context) => const OpenTextPage(), + '/open/image': (BuildContext context) => OpenImagePage(), + '/open/images': (BuildContext context) => OpenMultipleImagesPage(), + '/open/text': (BuildContext context) => OpenTextPage(), '/save/text': (BuildContext context) => SaveTextPage(), - '/directory': (BuildContext context) => const GetDirectoryPage(), + '/directory': (BuildContext context) => GetDirectoryPage(), }, ); } diff --git a/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart index 1a05343b27ab..aaf083603e72 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart @@ -11,9 +11,6 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select an image file using /// `openFiles`, then displays the selected images in a gallery dialog. class OpenImagePage extends StatelessWidget { - /// Default Constructor - const OpenImagePage({Key? key}) : super(key: key); - Future _openImageFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'images', @@ -62,8 +59,7 @@ class OpenImagePage extends StatelessWidget { /// Widget that displays an image in a dialog. class ImageDisplay extends StatelessWidget { /// Default Constructor. - const ImageDisplay(this.fileName, this.filePath, {Key? key}) - : super(key: key); + const ImageDisplay(this.fileName, this.filePath); /// The name of the selected file. final String fileName; diff --git a/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart index 9c3c8e369f5b..a030b8b4b10b 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart @@ -11,9 +11,6 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select multiple image files using /// `openFiles`, then displays the selected images in a gallery dialog. class OpenMultipleImagesPage extends StatelessWidget { - /// Default Constructor - const OpenMultipleImagesPage({Key? key}) : super(key: key); - Future _openImageFile(BuildContext context) async { final XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', @@ -66,7 +63,7 @@ class OpenMultipleImagesPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class MultipleImagesDisplay extends StatelessWidget { /// Default Constructor. - const MultipleImagesDisplay(this.files, {Key? key}) : super(key: key); + const MultipleImagesDisplay(this.files); /// The files containing the images. final List files; diff --git a/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart index 9adde400b5e1..fa281a0020d5 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart @@ -8,9 +8,6 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a text file using `openFile`, then /// displays its contents in a dialog. class OpenTextPage extends StatelessWidget { - /// Default Constructor - const OpenTextPage({Key? key}) : super(key: key); - Future _openTextFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'text', @@ -59,8 +56,7 @@ class OpenTextPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class TextDisplay extends StatelessWidget { /// Default Constructor. - const TextDisplay(this.fileName, this.fileContent, {Key? key}) - : super(key: key); + const TextDisplay(this.fileName, this.fileContent); /// The name of the selected file. final String fileName; diff --git a/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart b/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart index a44a387c019b..3989c62b7442 100644 --- a/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart @@ -9,9 +9,6 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a save location using `getSavePath`, /// then writes text to a file at that location. class SaveTextPage extends StatelessWidget { - /// Default Constructor - SaveTextPage({Key? key}) : super(key: key); - final TextEditingController _nameController = TextEditingController(); final TextEditingController _contentController = TextEditingController(); @@ -70,8 +67,8 @@ class SaveTextPage extends StatelessWidget { primary: Colors.blue, onPrimary: Colors.white, ), - onPressed: _saveFile, child: const Text('Press to save a text file'), + onPressed: _saveFile, ), ], ), diff --git a/packages/file_selector/file_selector_macos/pubspec.yaml b/packages/file_selector/file_selector_macos/pubspec.yaml index 41077c1c04e6..071d261c4bf8 100644 --- a/packages/file_selector/file_selector_macos/pubspec.yaml +++ b/packages/file_selector/file_selector_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_macos description: macOS implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.2+1 +version: 0.8.2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md index 934d38827ddb..100b6ad136a7 100644 --- a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md +++ b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md @@ -1,8 +1,6 @@ -## 2.0.5 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 2.0.4 diff --git a/packages/file_selector/file_selector_platform_interface/pubspec.yaml b/packages/file_selector/file_selector_platform_interface/pubspec.yaml index d280b0f5c71c..42917751ed58 100644 --- a/packages/file_selector/file_selector_platform_interface/pubspec.yaml +++ b/packages/file_selector/file_selector_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.0.5 +version: 2.0.4 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_web/CHANGELOG.md b/packages/file_selector/file_selector_web/CHANGELOG.md index ce9d5590f9a9..5927239ef9e3 100644 --- a/packages/file_selector/file_selector_web/CHANGELOG.md +++ b/packages/file_selector/file_selector_web/CHANGELOG.md @@ -1,8 +1,3 @@ -## 0.8.1+4 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 0.8.1+3 * Minor code cleanup for new analysis rules. diff --git a/packages/file_selector/file_selector_web/example/lib/main.dart b/packages/file_selector/file_selector_web/example/lib/main.dart index 87422953de6a..341913a18490 100644 --- a/packages/file_selector/file_selector_web/example/lib/main.dart +++ b/packages/file_selector/file_selector_web/example/lib/main.dart @@ -5,16 +5,13 @@ import 'package:flutter/material.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } /// App for testing class MyApp extends StatefulWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - @override - State createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/file_selector/file_selector_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml index 2e12b6d175a3..74d0412b440f 100644 --- a/packages/file_selector/file_selector_web/pubspec.yaml +++ b/packages/file_selector/file_selector_web/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_web description: Web platform implementation of file_selector repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.1+4 +version: 0.8.1+3 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_windows/CHANGELOG.md b/packages/file_selector/file_selector_windows/CHANGELOG.md index c242717c3267..ae3cd13342b1 100644 --- a/packages/file_selector/file_selector_windows/CHANGELOG.md +++ b/packages/file_selector/file_selector_windows/CHANGELOG.md @@ -1,8 +1,6 @@ -## 0.8.2+1 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 0.8.2 diff --git a/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart b/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart index 8fc1a9001465..b282b9030d67 100644 --- a/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart @@ -8,9 +8,6 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a directory using `getDirectoryPath`, /// then displays the selected directory in a dialog. class GetDirectoryPage extends StatelessWidget { - /// Default Constructor - const GetDirectoryPage({Key? key}) : super(key: key); - Future _getDirectoryPath(BuildContext context) async { const String confirmButtonText = 'Choose'; final String? directoryPath = @@ -55,7 +52,7 @@ class GetDirectoryPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class TextDisplay extends StatelessWidget { /// Creates a `TextDisplay`. - const TextDisplay(this.directoryPath, {Key? key}) : super(key: key); + const TextDisplay(this.directoryPath); /// The path selected in the dialog. final String directoryPath; diff --git a/packages/file_selector/file_selector_windows/example/lib/home_page.dart b/packages/file_selector/file_selector_windows/example/lib/home_page.dart index 4d6ca7e6e6e3..958680be0e3b 100644 --- a/packages/file_selector/file_selector_windows/example/lib/home_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/home_page.dart @@ -6,9 +6,6 @@ import 'package:flutter/material.dart'; /// Home Page of the application. class HomePage extends StatelessWidget { - /// Default Constructor - const HomePage({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { final ButtonStyle style = ElevatedButton.styleFrom( diff --git a/packages/file_selector/file_selector_windows/example/lib/main.dart b/packages/file_selector/file_selector_windows/example/lib/main.dart index cbe268e1c7ab..a49ebac1aea5 100644 --- a/packages/file_selector/file_selector_windows/example/lib/main.dart +++ b/packages/file_selector/file_selector_windows/example/lib/main.dart @@ -11,14 +11,11 @@ import 'package:example/save_text_page.dart'; import 'package:flutter/material.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } /// MyApp is the Main Application. class MyApp extends StatelessWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -27,14 +24,13 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: const HomePage(), + home: HomePage(), routes: { - '/open/image': (BuildContext context) => const OpenImagePage(), - '/open/images': (BuildContext context) => - const OpenMultipleImagesPage(), - '/open/text': (BuildContext context) => const OpenTextPage(), + '/open/image': (BuildContext context) => OpenImagePage(), + '/open/images': (BuildContext context) => OpenMultipleImagesPage(), + '/open/text': (BuildContext context) => OpenTextPage(), '/save/text': (BuildContext context) => SaveTextPage(), - '/directory': (BuildContext context) => const GetDirectoryPage(), + '/directory': (BuildContext context) => GetDirectoryPage(), }, ); } diff --git a/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart index 1a05343b27ab..aaf083603e72 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart @@ -11,9 +11,6 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select an image file using /// `openFiles`, then displays the selected images in a gallery dialog. class OpenImagePage extends StatelessWidget { - /// Default Constructor - const OpenImagePage({Key? key}) : super(key: key); - Future _openImageFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'images', @@ -62,8 +59,7 @@ class OpenImagePage extends StatelessWidget { /// Widget that displays an image in a dialog. class ImageDisplay extends StatelessWidget { /// Default Constructor. - const ImageDisplay(this.fileName, this.filePath, {Key? key}) - : super(key: key); + const ImageDisplay(this.fileName, this.filePath); /// The name of the selected file. final String fileName; diff --git a/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart index 9c3c8e369f5b..a030b8b4b10b 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart @@ -11,9 +11,6 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select multiple image files using /// `openFiles`, then displays the selected images in a gallery dialog. class OpenMultipleImagesPage extends StatelessWidget { - /// Default Constructor - const OpenMultipleImagesPage({Key? key}) : super(key: key); - Future _openImageFile(BuildContext context) async { final XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', @@ -66,7 +63,7 @@ class OpenMultipleImagesPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class MultipleImagesDisplay extends StatelessWidget { /// Default Constructor. - const MultipleImagesDisplay(this.files, {Key? key}) : super(key: key); + const MultipleImagesDisplay(this.files); /// The files containing the images. final List files; diff --git a/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart index 9adde400b5e1..fa281a0020d5 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart @@ -8,9 +8,6 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a text file using `openFile`, then /// displays its contents in a dialog. class OpenTextPage extends StatelessWidget { - /// Default Constructor - const OpenTextPage({Key? key}) : super(key: key); - Future _openTextFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'text', @@ -59,8 +56,7 @@ class OpenTextPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class TextDisplay extends StatelessWidget { /// Default Constructor. - const TextDisplay(this.fileName, this.fileContent, {Key? key}) - : super(key: key); + const TextDisplay(this.fileName, this.fileContent); /// The name of the selected file. final String fileName; diff --git a/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart b/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart index 961e0fb2fbc3..b87a51c3877d 100644 --- a/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart @@ -9,9 +9,6 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a save location using `getSavePath`, /// then writes text to a file at that location. class SaveTextPage extends StatelessWidget { - /// Default Constructor - SaveTextPage({Key? key}) : super(key: key); - final TextEditingController _nameController = TextEditingController(); final TextEditingController _contentController = TextEditingController(); @@ -70,8 +67,8 @@ class SaveTextPage extends StatelessWidget { primary: Colors.blue, onPrimary: Colors.white, ), - onPressed: _saveFile, child: const Text('Press to save a text file'), + onPressed: _saveFile, ), ], ), diff --git a/packages/file_selector/file_selector_windows/pubspec.yaml b/packages/file_selector/file_selector_windows/pubspec.yaml index 152b63ef4a3f..7b035e974293 100644 --- a/packages/file_selector/file_selector_windows/pubspec.yaml +++ b/packages/file_selector/file_selector_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_windows description: Windows implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.2+1 +version: 0.8.2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/flutter_plugin_android_lifecycle/CHANGELOG.md b/packages/flutter_plugin_android_lifecycle/CHANGELOG.md index a0a3f67bed46..8fdfc39f3bf9 100644 --- a/packages/flutter_plugin_android_lifecycle/CHANGELOG.md +++ b/packages/flutter_plugin_android_lifecycle/CHANGELOG.md @@ -1,8 +1,6 @@ -## 2.0.6 +## NEXT * Adds OS version support information to README. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 2.0.5 diff --git a/packages/flutter_plugin_android_lifecycle/example/integration_test/flutter_plugin_android_lifecycle_test.dart b/packages/flutter_plugin_android_lifecycle/example/integration_test/flutter_plugin_android_lifecycle_test.dart index 1198c6f01806..1d329a02f93b 100644 --- a/packages/flutter_plugin_android_lifecycle/example/integration_test/flutter_plugin_android_lifecycle_test.dart +++ b/packages/flutter_plugin_android_lifecycle/example/integration_test/flutter_plugin_android_lifecycle_test.dart @@ -10,6 +10,6 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('loads', (WidgetTester tester) async { - await tester.pumpWidget(const MyApp()); + await tester.pumpWidget(MyApp()); }); } diff --git a/packages/flutter_plugin_android_lifecycle/example/lib/main.dart b/packages/flutter_plugin_android_lifecycle/example/lib/main.dart index c465b3b687f2..3ef6794dfad2 100644 --- a/packages/flutter_plugin_android_lifecycle/example/lib/main.dart +++ b/packages/flutter_plugin_android_lifecycle/example/lib/main.dart @@ -2,15 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: public_member_api_docs + import 'package:flutter/material.dart'; -void main() => runApp(const MyApp()); +void main() => runApp(MyApp()); -/// MyApp is the Main Application. class MyApp extends StatelessWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( diff --git a/packages/flutter_plugin_android_lifecycle/pubspec.yaml b/packages/flutter_plugin_android_lifecycle/pubspec.yaml index c109d0936589..b359cc55c351 100644 --- a/packages/flutter_plugin_android_lifecycle/pubspec.yaml +++ b/packages/flutter_plugin_android_lifecycle/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_plugin_android_lifecycle description: Flutter plugin for accessing an Android Lifecycle within other plugins. repository: https://github.com/flutter/plugins/tree/main/packages/flutter_plugin_android_lifecycle issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_plugin_android_lifecycle%22 -version: 2.0.6 +version: 2.0.5 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index d1f2f926e2ab..b0662b89d9cc 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,8 +1,6 @@ -## 2.1.5 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 2.1.4 @@ -225,7 +223,7 @@ GoogleMapController is now uniformly driven by implementing `DefaultLifecycleObs ## 0.5.26+1 -* Removes an erroneously added method from the GoogleMapController.h header file. +* Removes a errorneously added method from the GoogleMapController.h header file. ## 0.5.26 diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart index 09df2b98b146..f8072eee7c85 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart @@ -10,8 +10,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class AnimateCameraPage extends GoogleMapExampleAppPage { - const AnimateCameraPage({Key? key}) - : super(const Icon(Icons.map), 'Camera control, animated', key: key); + const AnimateCameraPage() + : super(const Icon(Icons.map), 'Camera control, animated'); @override Widget build(BuildContext context) { @@ -20,7 +20,7 @@ class AnimateCameraPage extends GoogleMapExampleAppPage { } class AnimateCamera extends StatefulWidget { - const AnimateCamera({Key? key}) : super(key: key); + const AnimateCamera(); @override State createState() => AnimateCameraState(); } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart index fd95cf864a7c..b1b58fdc91bf 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart @@ -12,8 +12,7 @@ const CameraPosition _kInitialPosition = CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); class LiteModePage extends GoogleMapExampleAppPage { - const LiteModePage({Key? key}) - : super(const Icon(Icons.map), 'Lite mode', key: key); + const LiteModePage() : super(const Icon(Icons.map), 'Lite mode'); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart index 8932705bc6d5..f4d420a72f7c 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: public_member_api_docs + import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -41,11 +43,7 @@ final List _allPages = [ const TileOverlayPage(), ]; -/// MapsDemo is the Main Application. class MapsDemo extends StatelessWidget { - /// Default Constructor - const MapsDemo({Key? key}) : super(key: key); - void _pushPage(BuildContext context, GoogleMapExampleAppPage page) { Navigator.of(context).push(MaterialPageRoute( builder: (_) => Scaffold( @@ -74,5 +72,5 @@ void main() { if (defaultTargetPlatform == TargetPlatform.android) { AndroidGoogleMapsFlutter.useAndroidViewSurface = true; } - runApp(const MaterialApp(home: MapsDemo())); + runApp(MaterialApp(home: MapsDemo())); } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart index 9c96f25d5fa7..bbe2372dce9a 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart @@ -12,8 +12,7 @@ const CameraPosition _kInitialPosition = CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); class MapClickPage extends GoogleMapExampleAppPage { - const MapClickPage({Key? key}) - : super(const Icon(Icons.mouse), 'Map click', key: key); + const MapClickPage() : super(const Icon(Icons.mouse), 'Map click'); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart index 1179acd46ffa..8e4853c040ed 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart @@ -12,8 +12,7 @@ const CameraPosition _kInitialPosition = CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); class MapCoordinatesPage extends GoogleMapExampleAppPage { - const MapCoordinatesPage({Key? key}) - : super(const Icon(Icons.map), 'Map coordinates', key: key); + const MapCoordinatesPage() : super(const Icon(Icons.map), 'Map coordinates'); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_ui.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_ui.dart index fbfeda56a968..48ef1f570e02 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_ui.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_ui.dart @@ -16,8 +16,7 @@ final LatLngBounds sydneyBounds = LatLngBounds( ); class MapUiPage extends GoogleMapExampleAppPage { - const MapUiPage({Key? key}) - : super(const Icon(Icons.map), 'User interface', key: key); + const MapUiPage() : super(const Icon(Icons.map), 'User interface'); @override Widget build(BuildContext context) { @@ -26,7 +25,7 @@ class MapUiPage extends GoogleMapExampleAppPage { } class MapUiBody extends StatefulWidget { - const MapUiBody({Key? key}) : super(key: key); + const MapUiBody(); @override State createState() => MapUiBodyState(); diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart index 58d266c95d1d..95ace9d7c482 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart @@ -11,8 +11,7 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class MarkerIconsPage extends GoogleMapExampleAppPage { - const MarkerIconsPage({Key? key}) - : super(const Icon(Icons.image), 'Marker icons', key: key); + const MarkerIconsPage() : super(const Icon(Icons.image), 'Marker icons'); @override Widget build(BuildContext context) { @@ -21,7 +20,7 @@ class MarkerIconsPage extends GoogleMapExampleAppPage { } class MarkerIconsBody extends StatefulWidget { - const MarkerIconsBody({Key? key}) : super(key: key); + const MarkerIconsBody(); @override State createState() => MarkerIconsBodyState(); diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart index a6bae3009f0b..33da90f32c1b 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart @@ -10,8 +10,7 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class MoveCameraPage extends GoogleMapExampleAppPage { - const MoveCameraPage({Key? key}) - : super(const Icon(Icons.map), 'Camera control', key: key); + const MoveCameraPage() : super(const Icon(Icons.map), 'Camera control'); @override Widget build(BuildContext context) { @@ -20,7 +19,7 @@ class MoveCameraPage extends GoogleMapExampleAppPage { } class MoveCamera extends StatefulWidget { - const MoveCamera({Key? key}) : super(key: key); + const MoveCamera(); @override State createState() => MoveCameraState(); } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart index a4bfa88d559f..77091909e970 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart @@ -9,8 +9,7 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PaddingPage extends GoogleMapExampleAppPage { - const PaddingPage({Key? key}) - : super(const Icon(Icons.map), 'Add padding to the map', key: key); + const PaddingPage() : super(const Icon(Icons.map), 'Add padding to the map'); @override Widget build(BuildContext context) { @@ -19,7 +18,7 @@ class PaddingPage extends GoogleMapExampleAppPage { } class MarkerIconsBody extends StatefulWidget { - const MarkerIconsBody({Key? key}) : super(key: key); + const MarkerIconsBody(); @override State createState() => MarkerIconsBodyState(); diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/page.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/page.dart index eb01ab07a6f3..fb6eb3260f6d 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/page.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/page.dart @@ -7,8 +7,7 @@ import 'package:flutter/material.dart'; abstract class GoogleMapExampleAppPage extends StatelessWidget { - const GoogleMapExampleAppPage(this.leading, this.title, {Key? key}) - : super(key: key); + const GoogleMapExampleAppPage(this.leading, this.title); final Widget leading; final String title; diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart index ef5033cfa1ee..c6f1509af69f 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart @@ -10,8 +10,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PlaceCirclePage extends GoogleMapExampleAppPage { - const PlaceCirclePage({Key? key}) - : super(const Icon(Icons.linear_scale), 'Place circle', key: key); + const PlaceCirclePage() + : super(const Icon(Icons.linear_scale), 'Place circle'); @override Widget build(BuildContext context) { @@ -20,7 +20,7 @@ class PlaceCirclePage extends GoogleMapExampleAppPage { } class PlaceCircleBody extends StatefulWidget { - const PlaceCircleBody({Key? key}) : super(key: key); + const PlaceCircleBody(); @override State createState() => PlaceCircleBodyState(); @@ -170,42 +170,42 @@ class PlaceCircleBodyState extends State { Column( children: [ TextButton( - onPressed: _add, child: const Text('add'), + onPressed: _add, ), TextButton( + child: const Text('remove'), onPressed: (selectedId == null) ? null : () => _remove(selectedId), - child: const Text('remove'), ), TextButton( + child: const Text('toggle visible'), onPressed: (selectedId == null) ? null : () => _toggleVisible(selectedId), - child: const Text('toggle visible'), ), ], ), Column( children: [ TextButton( + child: const Text('change stroke width'), onPressed: (selectedId == null) ? null : () => _changeStrokeWidth(selectedId), - child: const Text('change stroke width'), ), TextButton( + child: const Text('change stroke color'), onPressed: (selectedId == null) ? null : () => _changeStrokeColor(selectedId), - child: const Text('change stroke color'), ), TextButton( + child: const Text('change fill color'), onPressed: (selectedId == null) ? null : () => _changeFillColor(selectedId), - child: const Text('change fill color'), ), ], ) diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart index 1238d61547b8..4291cac6841e 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart @@ -15,8 +15,7 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PlaceMarkerPage extends GoogleMapExampleAppPage { - const PlaceMarkerPage({Key? key}) - : super(const Icon(Icons.place), 'Place marker', key: key); + const PlaceMarkerPage() : super(const Icon(Icons.place), 'Place marker'); @override Widget build(BuildContext context) { @@ -25,7 +24,7 @@ class PlaceMarkerPage extends GoogleMapExampleAppPage { } class PlaceMarkerBody extends StatefulWidget { - const PlaceMarkerBody({Key? key}) : super(key: key); + const PlaceMarkerBody(); @override State createState() => PlaceMarkerBodyState(); @@ -309,13 +308,13 @@ class PlaceMarkerBodyState extends State { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ TextButton( - onPressed: _add, child: const Text('Add'), + onPressed: _add, ), TextButton( + child: const Text('Remove'), onPressed: selectedId == null ? null : () => _remove(selectedId), - child: const Text('Remove'), ), ], ), @@ -323,61 +322,62 @@ class PlaceMarkerBodyState extends State { alignment: WrapAlignment.spaceEvenly, children: [ TextButton( + child: const Text('change info'), onPressed: selectedId == null ? null : () => _changeInfo(selectedId), - child: const Text('change info'), ), TextButton( + child: const Text('change info anchor'), onPressed: selectedId == null ? null : () => _changeInfoAnchor(selectedId), - child: const Text('change info anchor'), ), TextButton( + child: const Text('change alpha'), onPressed: selectedId == null ? null : () => _changeAlpha(selectedId), - child: const Text('change alpha'), ), TextButton( + child: const Text('change anchor'), onPressed: selectedId == null ? null : () => _changeAnchor(selectedId), - child: const Text('change anchor'), ), TextButton( + child: const Text('toggle draggable'), onPressed: selectedId == null ? null : () => _toggleDraggable(selectedId), - child: const Text('toggle draggable'), ), TextButton( + child: const Text('toggle flat'), onPressed: selectedId == null ? null : () => _toggleFlat(selectedId), - child: const Text('toggle flat'), ), TextButton( + child: const Text('change position'), onPressed: selectedId == null ? null : () => _changePosition(selectedId), - child: const Text('change position'), ), TextButton( + child: const Text('change rotation'), onPressed: selectedId == null ? null : () => _changeRotation(selectedId), - child: const Text('change rotation'), ), TextButton( + child: const Text('toggle visible'), onPressed: selectedId == null ? null : () => _toggleVisible(selectedId), - child: const Text('toggle visible'), ), TextButton( + child: const Text('change zIndex'), onPressed: selectedId == null ? null : () => _changeZIndex(selectedId), - child: const Text('change zIndex'), ), TextButton( + child: const Text('set marker icon'), onPressed: selectedId == null ? null : () { @@ -387,7 +387,6 @@ class PlaceMarkerBodyState extends State { }, ); }, - child: const Text('set marker icon'), ), ], ), diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart index f1932141b8ab..8d4fa3e07c36 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart @@ -10,8 +10,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PlacePolygonPage extends GoogleMapExampleAppPage { - const PlacePolygonPage({Key? key}) - : super(const Icon(Icons.linear_scale), 'Place polygon', key: key); + const PlacePolygonPage() + : super(const Icon(Icons.linear_scale), 'Place polygon'); @override Widget build(BuildContext context) { @@ -20,7 +20,7 @@ class PlacePolygonPage extends GoogleMapExampleAppPage { } class PlacePolygonBody extends StatefulWidget { - const PlacePolygonBody({Key? key}) : super(key: key); + const PlacePolygonBody(); @override State createState() => PlacePolygonBodyState(); @@ -196,64 +196,64 @@ class PlacePolygonBodyState extends State { Column( children: [ TextButton( - onPressed: _add, child: const Text('add'), + onPressed: _add, ), TextButton( + child: const Text('remove'), onPressed: (selectedId == null) ? null : () => _remove(selectedId), - child: const Text('remove'), ), TextButton( + child: const Text('toggle visible'), onPressed: (selectedId == null) ? null : () => _toggleVisible(selectedId), - child: const Text('toggle visible'), ), TextButton( + child: const Text('toggle geodesic'), onPressed: (selectedId == null) ? null : () => _toggleGeodesic(selectedId), - child: const Text('toggle geodesic'), ), ], ), Column( children: [ TextButton( + child: const Text('add holes'), onPressed: (selectedId == null) ? null : ((polygons[selectedId]!.holes.isNotEmpty) ? null : () => _addHoles(selectedId)), - child: const Text('add holes'), ), TextButton( + child: const Text('remove holes'), onPressed: (selectedId == null) ? null : ((polygons[selectedId]!.holes.isEmpty) ? null : () => _removeHoles(selectedId)), - child: const Text('remove holes'), ), TextButton( + child: const Text('change stroke width'), onPressed: (selectedId == null) ? null : () => _changeWidth(selectedId), - child: const Text('change stroke width'), ), TextButton( + child: const Text('change stroke color'), onPressed: (selectedId == null) ? null : () => _changeStrokeColor(selectedId), - child: const Text('change stroke color'), ), TextButton( + child: const Text('change fill color'), onPressed: (selectedId == null) ? null : () => _changeFillColor(selectedId), - child: const Text('change fill color'), ), ], ) diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart index b3a637ce7d15..434920d293be 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart @@ -11,8 +11,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PlacePolylinePage extends GoogleMapExampleAppPage { - const PlacePolylinePage({Key? key}) - : super(const Icon(Icons.linear_scale), 'Place polyline', key: key); + const PlacePolylinePage() + : super(const Icon(Icons.linear_scale), 'Place polyline'); @override Widget build(BuildContext context) { @@ -21,7 +21,7 @@ class PlacePolylinePage extends GoogleMapExampleAppPage { } class PlacePolylineBody extends StatefulWidget { - const PlacePolylineBody({Key? key}) : super(key: key); + const PlacePolylineBody(); @override State createState() => PlacePolylineBodyState(); @@ -234,66 +234,66 @@ class PlacePolylineBodyState extends State { Column( children: [ TextButton( - onPressed: _add, child: const Text('add'), + onPressed: _add, ), TextButton( + child: const Text('remove'), onPressed: (selectedId == null) ? null : () => _remove(selectedId), - child: const Text('remove'), ), TextButton( + child: const Text('toggle visible'), onPressed: (selectedId == null) ? null : () => _toggleVisible(selectedId), - child: const Text('toggle visible'), ), TextButton( + child: const Text('toggle geodesic'), onPressed: (selectedId == null) ? null : () => _toggleGeodesic(selectedId), - child: const Text('toggle geodesic'), ), ], ), Column( children: [ TextButton( + child: const Text('change width'), onPressed: (selectedId == null) ? null : () => _changeWidth(selectedId), - child: const Text('change width'), ), TextButton( + child: const Text('change color'), onPressed: (selectedId == null) ? null : () => _changeColor(selectedId), - child: const Text('change color'), ), TextButton( + child: const Text('change start cap [Android only]'), onPressed: isIOS || (selectedId == null) ? null : () => _changeStartCap(selectedId), - child: const Text('change start cap [Android only]'), ), TextButton( + child: const Text('change end cap [Android only]'), onPressed: isIOS || (selectedId == null) ? null : () => _changeEndCap(selectedId), - child: const Text('change end cap [Android only]'), ), TextButton( + child: const Text('change joint type [Android only]'), onPressed: isIOS || (selectedId == null) ? null : () => _changeJointType(selectedId), - child: const Text('change joint type [Android only]'), ), TextButton( + child: const Text('change pattern [Android only]'), onPressed: isIOS || (selectedId == null) ? null : () => _changePattern(selectedId), - child: const Text('change pattern [Android only]'), ), ], ) diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart index ca9d3962ddd7..04769315e685 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart @@ -14,8 +14,7 @@ import 'page.dart'; const LatLng _center = LatLng(32.080664, 34.9563837); class ScrollingMapPage extends GoogleMapExampleAppPage { - const ScrollingMapPage({Key? key}) - : super(const Icon(Icons.map), 'Scrolling map', key: key); + const ScrollingMapPage() : super(const Icon(Icons.map), 'Scrolling map'); @override Widget build(BuildContext context) { @@ -24,7 +23,7 @@ class ScrollingMapPage extends GoogleMapExampleAppPage { } class ScrollingMapBody extends StatelessWidget { - const ScrollingMapBody({Key? key}) : super(key: key); + const ScrollingMapBody(); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart index 849a9f469938..9afc28869490 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart @@ -15,9 +15,8 @@ const CameraPosition _kInitialPosition = CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); class SnapshotPage extends GoogleMapExampleAppPage { - const SnapshotPage({Key? key}) - : super(const Icon(Icons.camera_alt), 'Take a snapshot of the map', - key: key); + const SnapshotPage() + : super(const Icon(Icons.camera_alt), 'Take a snapshot of the map'); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart index 81dfc2815866..dc7f7d1d6f33 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart @@ -13,8 +13,7 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class TileOverlayPage extends GoogleMapExampleAppPage { - const TileOverlayPage({Key? key}) - : super(const Icon(Icons.map), 'Tile overlay', key: key); + const TileOverlayPage() : super(const Icon(Icons.map), 'Tile overlay'); @override Widget build(BuildContext context) { @@ -23,7 +22,7 @@ class TileOverlayPage extends GoogleMapExampleAppPage { } class TileOverlayBody extends StatefulWidget { - const TileOverlayBody({Key? key}) : super(key: key); + const TileOverlayBody(); @override State createState() => TileOverlayBodyState(); @@ -91,16 +90,16 @@ class TileOverlayBodyState extends State { ), ), TextButton( - onPressed: _addTileOverlay, child: const Text('Add tile overlay'), + onPressed: _addTileOverlay, ), TextButton( - onPressed: _removeTileOverlay, child: const Text('Remove tile overlay'), + onPressed: _removeTileOverlay, ), TextButton( - onPressed: _clearTileCache, child: const Text('Clear tile cache'), + onPressed: _clearTileCache, ), ], ); diff --git a/packages/google_maps_flutter/google_maps_flutter/lib/src/controller.dart b/packages/google_maps_flutter/google_maps_flutter/lib/src/controller.dart index dfc6e579d560..088589e4a2ce 100644 --- a/packages/google_maps_flutter/google_maps_flutter/lib/src/controller.dart +++ b/packages/google_maps_flutter/google_maps_flutter/lib/src/controller.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore_for_file: library_private_types_in_public_api - part of google_maps_flutter; /// Controller for a single GoogleMap instance running on the host platform. diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index a294dd09981f..c10f9c679a17 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter description: A Flutter plugin for integrating Google Maps in iOS and Android applications. repository: https://github.com/flutter/plugins/tree/main/packages/google_maps_flutter/google_maps_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 2.1.5 +version: 2.1.4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md index f2fe971f4591..48908b984b0f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md @@ -1,8 +1,6 @@ -## 0.3.2+2 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 0.3.2+1 diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/lib/main.dart index d1ba571b5bd0..10415204570c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/lib/main.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/lib/main.dart @@ -11,7 +11,7 @@ void main() { /// App for testing class MyApp extends StatefulWidget { @override - State createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml index 271d87d21092..2780175d29e2 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter_web description: Web platform implementation of google_maps_flutter repository: https://github.com/flutter/plugins/tree/main/packages/google_maps_flutter/google_maps_flutter_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 0.3.2+2 +version: 0.3.2+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index 5b47536cd2e2..caab46de7c5b 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,8 +1,3 @@ -## 5.3.1 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 5.3.0 * Moves Android and iOS implementations to federated packages. diff --git a/packages/google_sign_in/google_sign_in/example/lib/main.dart b/packages/google_sign_in/google_sign_in/example/lib/main.dart index 4c27543f5b18..9840a1e0a9f6 100644 --- a/packages/google_sign_in/google_sign_in/example/lib/main.dart +++ b/packages/google_sign_in/google_sign_in/example/lib/main.dart @@ -22,7 +22,7 @@ GoogleSignIn _googleSignIn = GoogleSignIn( void main() { runApp( - const MaterialApp( + MaterialApp( title: 'Google Sign In', home: SignInDemo(), ), @@ -30,8 +30,6 @@ void main() { } class SignInDemo extends StatefulWidget { - const SignInDemo({Key? key}) : super(key: key); - @override State createState() => SignInDemoState(); } @@ -127,8 +125,8 @@ class SignInDemoState extends State { const Text('Signed in successfully.'), Text(_contactText), ElevatedButton( - onPressed: _handleSignOut, child: const Text('SIGN OUT'), + onPressed: _handleSignOut, ), ElevatedButton( child: const Text('REFRESH'), @@ -142,8 +140,8 @@ class SignInDemoState extends State { children: [ const Text('You are not currently signed in.'), ElevatedButton( - onPressed: _handleSignIn, child: const Text('SIGN IN'), + onPressed: _handleSignIn, ), ], ); diff --git a/packages/google_sign_in/google_sign_in/lib/widgets.dart b/packages/google_sign_in/google_sign_in/lib/widgets.dart index f7ae5f9a6e5f..61f89133fba4 100644 --- a/packages/google_sign_in/google_sign_in/lib/widgets.dart +++ b/packages/google_sign_in/google_sign_in/lib/widgets.dart @@ -22,13 +22,11 @@ class GoogleUserCircleAvatar extends StatelessWidget { /// in place of a profile photo, or a default profile photo if the user's /// identity does not specify a `displayName`. const GoogleUserCircleAvatar({ - Key? key, required this.identity, this.placeholderPhotoUrl, this.foregroundColor, this.backgroundColor, - }) : assert(identity != null), - super(key: key); + }) : assert(identity != null); /// A regular expression that matches against the "size directive" path /// segment of Google profile image URLs. diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index e58b27af08b7..760706f2e7bc 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Google Sign-In, a secure authentication system for signing in with a Google account on Android and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.3.1 +version: 5.3.0 environment: diff --git a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md index 90069d05f442..3ffa6b5b7d6b 100644 --- a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md @@ -1,8 +1,3 @@ -## 5.2.7 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 5.2.6 * Switches to an internal method channel, rather than the default. diff --git a/packages/google_sign_in/google_sign_in_android/example/lib/main.dart b/packages/google_sign_in/google_sign_in_android/example/lib/main.dart index 526cf8b69ccf..a750c330001d 100644 --- a/packages/google_sign_in/google_sign_in_android/example/lib/main.dart +++ b/packages/google_sign_in/google_sign_in_android/example/lib/main.dart @@ -14,7 +14,7 @@ import 'package:http/http.dart' as http; void main() { runApp( - const MaterialApp( + MaterialApp( title: 'Google Sign In', home: SignInDemo(), ), @@ -22,8 +22,6 @@ void main() { } class SignInDemo extends StatefulWidget { - const SignInDemo({Key? key}) : super(key: key); - @override State createState() => SignInDemoState(); } @@ -144,8 +142,8 @@ class SignInDemoState extends State { const Text('Signed in successfully.'), Text(_contactText), ElevatedButton( - onPressed: _handleSignOut, child: const Text('SIGN OUT'), + onPressed: _handleSignOut, ), ElevatedButton( child: const Text('REFRESH'), @@ -159,8 +157,8 @@ class SignInDemoState extends State { children: [ const Text('You are not currently signed in.'), ElevatedButton( - onPressed: _handleSignIn, child: const Text('SIGN IN'), + onPressed: _handleSignIn, ), ], ); diff --git a/packages/google_sign_in/google_sign_in_android/pubspec.yaml b/packages/google_sign_in/google_sign_in_android/pubspec.yaml index d9b272320694..fa3dc1489b26 100644 --- a/packages/google_sign_in/google_sign_in_android/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_android/pubspec.yaml @@ -2,7 +2,7 @@ name: google_sign_in_android description: Android implementation of the google_sign_in plugin. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.2.7 +version: 5.2.6 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md b/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md index 90069d05f442..3ffa6b5b7d6b 100644 --- a/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md @@ -1,8 +1,3 @@ -## 5.2.7 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 5.2.6 * Switches to an internal method channel, rather than the default. diff --git a/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart b/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart index 526cf8b69ccf..a750c330001d 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart +++ b/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart @@ -14,7 +14,7 @@ import 'package:http/http.dart' as http; void main() { runApp( - const MaterialApp( + MaterialApp( title: 'Google Sign In', home: SignInDemo(), ), @@ -22,8 +22,6 @@ void main() { } class SignInDemo extends StatefulWidget { - const SignInDemo({Key? key}) : super(key: key); - @override State createState() => SignInDemoState(); } @@ -144,8 +142,8 @@ class SignInDemoState extends State { const Text('Signed in successfully.'), Text(_contactText), ElevatedButton( - onPressed: _handleSignOut, child: const Text('SIGN OUT'), + onPressed: _handleSignOut, ), ElevatedButton( child: const Text('REFRESH'), @@ -159,8 +157,8 @@ class SignInDemoState extends State { children: [ const Text('You are not currently signed in.'), ElevatedButton( - onPressed: _handleSignIn, child: const Text('SIGN IN'), + onPressed: _handleSignIn, ), ], ); diff --git a/packages/google_sign_in/google_sign_in_ios/pubspec.yaml b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml index b6f541b22ce1..e5ef3832ff0e 100644 --- a/packages/google_sign_in/google_sign_in_ios/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: google_sign_in_ios description: Android implementation of the google_sign_in plugin. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.2.7 +version: 5.2.6 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md index 6e87bebf5d02..aab4acae0376 100644 --- a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md @@ -1,8 +1,3 @@ -## 0.10.1+1 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 0.10.1 * Updates minimum Flutter version to 2.8. diff --git a/packages/google_sign_in/google_sign_in_web/example/lib/main.dart b/packages/google_sign_in/google_sign_in_web/example/lib/main.dart index b23015c811e8..d381fb4af3ab 100644 --- a/packages/google_sign_in/google_sign_in_web/example/lib/main.dart +++ b/packages/google_sign_in/google_sign_in_web/example/lib/main.dart @@ -5,16 +5,13 @@ import 'package:flutter/material.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } /// App for testing class MyApp extends StatefulWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - @override - State createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/google_sign_in/google_sign_in_web/pubspec.yaml b/packages/google_sign_in/google_sign_in_web/pubspec.yaml index 3dcd0e8eef29..5a09453b8e86 100644 --- a/packages/google_sign_in/google_sign_in_web/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_web/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Google Sign-In, a secure authentication system for signing in with a Google account on Android, iOS and Web. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 0.10.1+1 +version: 0.10.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index a384a5272e05..f1bf54c5cd35 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,8 +1,3 @@ -## 0.8.5+1 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 0.8.5 * Moves Android and iOS implementations to federated packages. diff --git a/packages/image_picker/image_picker/example/lib/main.dart b/packages/image_picker/image_picker/example/lib/main.dart index a6f0e83c3abb..f3ad2375b8f2 100755 --- a/packages/image_picker/image_picker/example/lib/main.dart +++ b/packages/image_picker/image_picker/example/lib/main.dart @@ -13,12 +13,10 @@ import 'package:image_picker/image_picker.dart'; import 'package:video_player/video_player.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return const MaterialApp( @@ -34,7 +32,7 @@ class MyHomePage extends StatefulWidget { final String? title; @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -179,22 +177,21 @@ class _MyHomePageState extends State { } if (_imageFileList != null) { return Semantics( - label: 'image_picker_example_picked_images', - child: ListView.builder( - key: UniqueKey(), - itemBuilder: (BuildContext context, int index) { - // Why network for web? - // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform - return Semantics( - label: 'image_picker_example_picked_image', - child: kIsWeb - ? Image.network(_imageFileList![index].path) - : Image.file(File(_imageFileList![index].path)), - ); - }, - itemCount: _imageFileList!.length, - ), - ); + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + // Why network for web? + // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform + return Semantics( + label: 'image_picker_example_picked_image', + child: kIsWeb + ? Image.network(_imageFileList![index].path) + : Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + label: 'image_picker_example_picked_images'); } else if (_pickImageError != null) { return Text( 'Pick image error: $_pickImageError', @@ -420,7 +417,7 @@ typedef OnPickImageCallback = void Function( double? maxWidth, double? maxHeight, int? quality); class AspectRatioVideo extends StatefulWidget { - const AspectRatioVideo(this.controller, {Key? key}) : super(key: key); + const AspectRatioVideo(this.controller); final VideoPlayerController? controller; diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 818486d8e145..77a50916283e 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.5+1 +version: 0.8.5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/image_picker/image_picker_android/CHANGELOG.md b/packages/image_picker/image_picker_android/CHANGELOG.md index 0514fc33d420..3472ade28d5b 100644 --- a/packages/image_picker/image_picker_android/CHANGELOG.md +++ b/packages/image_picker/image_picker_android/CHANGELOG.md @@ -1,8 +1,3 @@ -## 0.8.4+12 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 0.8.4+11 * Splits from `image_picker` as a federated implementation. diff --git a/packages/image_picker/image_picker_android/example/lib/main.dart b/packages/image_picker/image_picker_android/example/lib/main.dart index d56aeb866195..48eee35445da 100755 --- a/packages/image_picker/image_picker_android/example/lib/main.dart +++ b/packages/image_picker/image_picker_android/example/lib/main.dart @@ -13,12 +13,10 @@ import 'package:image_picker_platform_interface/image_picker_platform_interface. import 'package:video_player/video_player.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return const MaterialApp( @@ -34,7 +32,7 @@ class MyHomePage extends StatefulWidget { final String? title; @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -179,22 +177,21 @@ class _MyHomePageState extends State { } if (_imageFileList != null) { return Semantics( - label: 'image_picker_example_picked_images', - child: ListView.builder( - key: UniqueKey(), - itemBuilder: (BuildContext context, int index) { - // Why network for web? - // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform - return Semantics( - label: 'image_picker_example_picked_image', - child: kIsWeb - ? Image.network(_imageFileList![index].path) - : Image.file(File(_imageFileList![index].path)), - ); - }, - itemCount: _imageFileList!.length, - ), - ); + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + // Why network for web? + // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform + return Semantics( + label: 'image_picker_example_picked_image', + child: kIsWeb + ? Image.network(_imageFileList![index].path) + : Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + label: 'image_picker_example_picked_images'); } else if (_pickImageError != null) { return Text( 'Pick image error: $_pickImageError', @@ -420,7 +417,7 @@ typedef OnPickImageCallback = void Function( double? maxWidth, double? maxHeight, int? quality); class AspectRatioVideo extends StatefulWidget { - const AspectRatioVideo(this.controller, {Key? key}) : super(key: key); + const AspectRatioVideo(this.controller); final VideoPlayerController? controller; diff --git a/packages/image_picker/image_picker_android/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml index 90d136c2c89b..dbeef9bed193 100755 --- a/packages/image_picker/image_picker_android/pubspec.yaml +++ b/packages/image_picker/image_picker_android/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_android description: Android implementation of the image_picker plugin. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.4+12 +version: 0.8.4+11 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/image_picker/image_picker_for_web/CHANGELOG.md b/packages/image_picker/image_picker_for_web/CHANGELOG.md index c33b3b9981de..dcf353fe19b1 100644 --- a/packages/image_picker/image_picker_for_web/CHANGELOG.md +++ b/packages/image_picker/image_picker_for_web/CHANGELOG.md @@ -1,8 +1,3 @@ -## 2.1.7 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 2.1.6 * Internal code cleanup for stricter analysis options. diff --git a/packages/image_picker/image_picker_for_web/example/lib/main.dart b/packages/image_picker/image_picker_for_web/example/lib/main.dart index 87422953de6a..341913a18490 100644 --- a/packages/image_picker/image_picker_for_web/example/lib/main.dart +++ b/packages/image_picker/image_picker_for_web/example/lib/main.dart @@ -5,16 +5,13 @@ import 'package:flutter/material.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } /// App for testing class MyApp extends StatefulWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - @override - State createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/image_picker/image_picker_for_web/pubspec.yaml b/packages/image_picker/image_picker_for_web/pubspec.yaml index b0c5deb0da7a..deccd2b50a1f 100644 --- a/packages/image_picker/image_picker_for_web/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_for_web description: Web platform implementation of image_picker repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_for_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 2.1.7 +version: 2.1.6 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index af391db02689..31a0795a4e30 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,8 +1,6 @@ -## 0.8.5+1 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 0.8.5 diff --git a/packages/image_picker/image_picker_ios/example/lib/main.dart b/packages/image_picker/image_picker_ios/example/lib/main.dart index d56aeb866195..48eee35445da 100755 --- a/packages/image_picker/image_picker_ios/example/lib/main.dart +++ b/packages/image_picker/image_picker_ios/example/lib/main.dart @@ -13,12 +13,10 @@ import 'package:image_picker_platform_interface/image_picker_platform_interface. import 'package:video_player/video_player.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return const MaterialApp( @@ -34,7 +32,7 @@ class MyHomePage extends StatefulWidget { final String? title; @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -179,22 +177,21 @@ class _MyHomePageState extends State { } if (_imageFileList != null) { return Semantics( - label: 'image_picker_example_picked_images', - child: ListView.builder( - key: UniqueKey(), - itemBuilder: (BuildContext context, int index) { - // Why network for web? - // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform - return Semantics( - label: 'image_picker_example_picked_image', - child: kIsWeb - ? Image.network(_imageFileList![index].path) - : Image.file(File(_imageFileList![index].path)), - ); - }, - itemCount: _imageFileList!.length, - ), - ); + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + // Why network for web? + // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform + return Semantics( + label: 'image_picker_example_picked_image', + child: kIsWeb + ? Image.network(_imageFileList![index].path) + : Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + label: 'image_picker_example_picked_images'); } else if (_pickImageError != null) { return Text( 'Pick image error: $_pickImageError', @@ -420,7 +417,7 @@ typedef OnPickImageCallback = void Function( double? maxWidth, double? maxHeight, int? quality); class AspectRatioVideo extends StatefulWidget { - const AspectRatioVideo(this.controller, {Key? key}) : super(key: key); + const AspectRatioVideo(this.controller); final VideoPlayerController? controller; diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index 76ca20614f18..a9cd052be56a 100755 --- a/packages/image_picker/image_picker_ios/pubspec.yaml +++ b/packages/image_picker/image_picker_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_ios description: iOS implementation of the video_picker plugin. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.5+1 +version: 0.8.5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/image_picker/image_picker_windows/CHANGELOG.md b/packages/image_picker/image_picker_windows/CHANGELOG.md index e72ab244068f..d98656b849c8 100644 --- a/packages/image_picker/image_picker_windows/CHANGELOG.md +++ b/packages/image_picker/image_picker_windows/CHANGELOG.md @@ -1,8 +1,6 @@ -## 0.1.0+1 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 0.1.0 diff --git a/packages/image_picker/image_picker_windows/example/lib/main.dart b/packages/image_picker/image_picker_windows/example/lib/main.dart index b3ba3574522a..577d6dadf2d9 100644 --- a/packages/image_picker/image_picker_windows/example/lib/main.dart +++ b/packages/image_picker/image_picker_windows/example/lib/main.dart @@ -12,12 +12,10 @@ import 'package:image_picker_platform_interface/image_picker_platform_interface. import 'package:video_player/video_player.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return const MaterialApp( @@ -33,7 +31,7 @@ class MyHomePage extends StatefulWidget { final String? title; @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -178,18 +176,17 @@ class _MyHomePageState extends State { } if (_imageFileList != null) { return Semantics( - label: 'image_picker_example_picked_images', - child: ListView.builder( - key: UniqueKey(), - itemBuilder: (BuildContext context, int index) { - return Semantics( - label: 'image_picker_example_picked_image', - child: Image.file(File(_imageFileList![index].path)), - ); - }, - itemCount: _imageFileList!.length, - ), - ); + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + return Semantics( + label: 'image_picker_example_picked_image', + child: Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + label: 'image_picker_example_picked_images'); } else if (_pickImageError != null) { return Text( 'Pick image error: $_pickImageError', @@ -366,7 +363,7 @@ typedef OnPickImageCallback = void Function( double? maxWidth, double? maxHeight, int? quality); class AspectRatioVideo extends StatefulWidget { - const AspectRatioVideo(this.controller, {Key? key}) : super(key: key); + const AspectRatioVideo(this.controller); final VideoPlayerController? controller; diff --git a/packages/image_picker/image_picker_windows/pubspec.yaml b/packages/image_picker/image_picker_windows/pubspec.yaml index afadf3e39148..eec41f7bfa0d 100644 --- a/packages/image_picker/image_picker_windows/pubspec.yaml +++ b/packages/image_picker/image_picker_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_windows description: Windows platform implementation of image_picker repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.1.0+1 +version: 0.1.0 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md index 8412c23ee8e8..24ef9eaffd1d 100644 --- a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md @@ -1,9 +1,7 @@ -## 3.0.3 +## NEXT * Removes unnecessary imports. * Adds OS version support information to README. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 3.0.2 diff --git a/packages/in_app_purchase/in_app_purchase/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase/example/lib/main.dart index 34346a0bd339..651652b40c3a 100644 --- a/packages/in_app_purchase/in_app_purchase/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase/example/lib/main.dart @@ -33,7 +33,7 @@ const List _kProductIds = [ class _MyApp extends StatefulWidget { @override - State<_MyApp> createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<_MyApp> { @@ -247,61 +247,60 @@ class _MyAppState extends State<_MyApp> { (ProductDetails productDetails) { final PurchaseDetails? previousPurchase = purchases[productDetails.id]; return ListTile( - title: Text( - productDetails.title, - ), - subtitle: Text( - productDetails.description, - ), - trailing: previousPurchase != null - ? IconButton( - onPressed: () => confirmPriceChange(context), - icon: const Icon(Icons.upgrade)) - : TextButton( - style: TextButton.styleFrom( - backgroundColor: Colors.green[800], - primary: Colors.white, - ), - onPressed: () { - late PurchaseParam purchaseParam; - - if (Platform.isAndroid) { - // NOTE: If you are making a subscription purchase/upgrade/downgrade, we recommend you to - // verify the latest status of you your subscription by using server side receipt validation - // and update the UI accordingly. The subscription purchase status shown - // inside the app may not be accurate. - final GooglePlayPurchaseDetails? oldSubscription = - _getOldSubscription(productDetails, purchases); - - purchaseParam = GooglePlayPurchaseParam( + title: Text( + productDetails.title, + ), + subtitle: Text( + productDetails.description, + ), + trailing: previousPurchase != null + ? IconButton( + onPressed: () => confirmPriceChange(context), + icon: const Icon(Icons.upgrade)) + : TextButton( + child: Text(productDetails.price), + style: TextButton.styleFrom( + backgroundColor: Colors.green[800], + primary: Colors.white, + ), + onPressed: () { + late PurchaseParam purchaseParam; + + if (Platform.isAndroid) { + // NOTE: If you are making a subscription purchase/upgrade/downgrade, we recommend you to + // verify the latest status of you your subscription by using server side receipt validation + // and update the UI accordingly. The subscription purchase status shown + // inside the app may not be accurate. + final GooglePlayPurchaseDetails? oldSubscription = + _getOldSubscription(productDetails, purchases); + + purchaseParam = GooglePlayPurchaseParam( + productDetails: productDetails, + applicationUserName: null, + changeSubscriptionParam: (oldSubscription != null) + ? ChangeSubscriptionParam( + oldPurchaseDetails: oldSubscription, + prorationMode: ProrationMode + .immediateWithTimeProration, + ) + : null); + } else { + purchaseParam = PurchaseParam( productDetails: productDetails, applicationUserName: null, - changeSubscriptionParam: (oldSubscription != null) - ? ChangeSubscriptionParam( - oldPurchaseDetails: oldSubscription, - prorationMode: - ProrationMode.immediateWithTimeProration, - ) - : null); - } else { - purchaseParam = PurchaseParam( - productDetails: productDetails, - applicationUserName: null, - ); - } - - if (productDetails.id == _kConsumableId) { - _inAppPurchase.buyConsumable( - purchaseParam: purchaseParam, - autoConsume: _kAutoConsume || Platform.isIOS); - } else { - _inAppPurchase.buyNonConsumable( - purchaseParam: purchaseParam); - } - }, - child: Text(productDetails.price), - ), - ); + ); + } + + if (productDetails.id == _kConsumableId) { + _inAppPurchase.buyConsumable( + purchaseParam: purchaseParam, + autoConsume: _kAutoConsume || Platform.isIOS); + } else { + _inAppPurchase.buyNonConsumable( + purchaseParam: purchaseParam); + } + }, + )); }, )); @@ -341,9 +340,9 @@ class _MyAppState extends State<_MyApp> { const Divider(), GridView.count( crossAxisCount: 5, + children: tokens, shrinkWrap: true, padding: const EdgeInsets.all(16.0), - children: tokens, ) ])); } @@ -360,12 +359,12 @@ class _MyAppState extends State<_MyApp> { mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( + child: const Text('Restore purchases'), style: TextButton.styleFrom( backgroundColor: Theme.of(context).primaryColor, primary: Colors.white, ), onPressed: () => _inAppPurchase.restorePurchases(), - child: const Text('Restore purchases'), ), ], ), diff --git a/packages/in_app_purchase/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/pubspec.yaml index d2f875293876..af8f5f3c2773 100644 --- a/packages/in_app_purchase/in_app_purchase/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase description: A Flutter plugin for in-app purchases. Exposes APIs for making in-app purchases through the App Store and Google Play. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 3.0.3 +version: 3.0.2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md index 4b9e58c08d06..2657d504ac91 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,8 +1,6 @@ -## 0.2.2+4 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 0.2.2+3 diff --git a/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart index 1da943535f70..939bc43bea63 100644 --- a/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart @@ -37,7 +37,7 @@ const List _kProductIds = [ class _MyApp extends StatefulWidget { @override - State<_MyApp> createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<_MyApp> { @@ -264,6 +264,7 @@ class _MyAppState extends State<_MyApp> { }, icon: const Icon(Icons.upgrade)) : TextButton( + child: Text(productDetails.price), style: TextButton.styleFrom( backgroundColor: Colors.green[800], primary: Colors.white, @@ -296,7 +297,6 @@ class _MyAppState extends State<_MyApp> { purchaseParam: purchaseParam); } }, - child: Text(productDetails.price), )); }, )); @@ -337,9 +337,9 @@ class _MyAppState extends State<_MyApp> { const Divider(), GridView.count( crossAxisCount: 5, + children: tokens, shrinkWrap: true, padding: const EdgeInsets.all(16.0), - children: tokens, ) ])); } diff --git a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml index 7de778177c31..62888e6dfb73 100644 --- a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_android description: An implementation for the Android platform of the Flutter `in_app_purchase` plugin. This uses the Android BillingClient APIs. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.2.2+4 +version: 0.2.2+3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md index 7342077ab176..403ee32be2ae 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,8 +1,6 @@ -## 0.3.0+6 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 0.3.0+5 diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart index f45a8c7f8741..2ee2deb7fe35 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart @@ -37,7 +37,7 @@ const List _kProductIds = [ class _MyApp extends StatefulWidget { @override - State<_MyApp> createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<_MyApp> { @@ -259,6 +259,7 @@ class _MyAppState extends State<_MyApp> { }, icon: const Icon(Icons.upgrade)) : TextButton( + child: Text(productDetails.price), style: TextButton.styleFrom( backgroundColor: Colors.green[800], primary: Colors.white, @@ -277,7 +278,6 @@ class _MyAppState extends State<_MyApp> { purchaseParam: purchaseParam); } }, - child: Text(productDetails.price), )); }, )); @@ -318,9 +318,9 @@ class _MyAppState extends State<_MyApp> { const Divider(), GridView.count( crossAxisCount: 5, + children: tokens, shrinkWrap: true, padding: const EdgeInsets.all(16.0), - children: tokens, ) ])); } @@ -337,12 +337,12 @@ class _MyAppState extends State<_MyApp> { mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( + child: const Text('Restore purchases'), style: TextButton.styleFrom( backgroundColor: Theme.of(context).primaryColor, primary: Colors.white, ), onPressed: () => _iapStoreKitPlatform.restorePurchases(), - child: const Text('Restore purchases'), ), ], ), diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml index 24b693c98c36..9693c186119c 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_storekit description: An implementation for the iOS platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase_storekit issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.3.0+6 +version: 0.3.0+5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/ios_platform_images/CHANGELOG.md b/packages/ios_platform_images/CHANGELOG.md index cf2632feaac7..0a3755afa7e7 100644 --- a/packages/ios_platform_images/CHANGELOG.md +++ b/packages/ios_platform_images/CHANGELOG.md @@ -1,8 +1,6 @@ -## 0.2.0+6 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 0.2.0+5 diff --git a/packages/ios_platform_images/example/lib/main.dart b/packages/ios_platform_images/example/lib/main.dart index 929814ecce00..ecdfeb2cba01 100644 --- a/packages/ios_platform_images/example/lib/main.dart +++ b/packages/ios_platform_images/example/lib/main.dart @@ -5,15 +5,12 @@ import 'package:flutter/material.dart'; import 'package:ios_platform_images/ios_platform_images.dart'; -void main() => runApp(const MyApp()); +void main() => runApp(MyApp()); /// Main widget for the example app. class MyApp extends StatefulWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - @override - State createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/ios_platform_images/example/test/widget_test.dart b/packages/ios_platform_images/example/test/widget_test.dart index f3cd4c68b65b..18e9e657ddb9 100644 --- a/packages/ios_platform_images/example/test/widget_test.dart +++ b/packages/ios_platform_images/example/test/widget_test.dart @@ -12,7 +12,7 @@ import 'package:ios_platform_images_example/main.dart'; void main() { testWidgets('Verify loads image', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); + await tester.pumpWidget(MyApp()); expect( find.byWidgetPredicate( diff --git a/packages/ios_platform_images/pubspec.yaml b/packages/ios_platform_images/pubspec.yaml index 41a177560299..7f80714e4c1c 100644 --- a/packages/ios_platform_images/pubspec.yaml +++ b/packages/ios_platform_images/pubspec.yaml @@ -2,7 +2,7 @@ name: ios_platform_images description: A plugin to share images between Flutter and iOS in add-to-app setups. repository: https://github.com/flutter/plugins/tree/main/packages/ios_platform_images issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+ios_platform_images%22 -version: 0.2.0+6 +version: 0.2.0+5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index 6765b497e506..570d8eef7b9b 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -4,8 +4,6 @@ * Updates README to match API changes in 2.0, and to improve clarity in general. * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 2.0.0 diff --git a/packages/local_auth/local_auth/example/lib/main.dart b/packages/local_auth/local_auth/example/lib/main.dart index cc687f562402..92ad7cf4fb3f 100644 --- a/packages/local_auth/local_auth/example/lib/main.dart +++ b/packages/local_auth/local_auth/example/lib/main.dart @@ -11,14 +11,12 @@ import 'package:flutter/services.dart'; import 'package:local_auth/local_auth.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatefulWidget { - const MyApp({Key? key}) : super(key: key); - @override - State createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { @@ -171,14 +169,14 @@ class _MyAppState extends State { const Divider(height: 100), Text('Can check biometrics: $_canCheckBiometrics\n'), ElevatedButton( - onPressed: _checkBiometrics, child: const Text('Check biometrics'), + onPressed: _checkBiometrics, ), const Divider(height: 100), Text('Available biometrics: $_availableBiometrics\n'), ElevatedButton( - onPressed: _getAvailableBiometrics, child: const Text('Get available biometrics'), + onPressed: _getAvailableBiometrics, ), const Divider(height: 100), Text('Current State: $_authorized\n'), @@ -197,7 +195,6 @@ class _MyAppState extends State { Column( children: [ ElevatedButton( - onPressed: _authenticate, child: Row( mainAxisSize: MainAxisSize.min, children: const [ @@ -205,9 +202,9 @@ class _MyAppState extends State { Icon(Icons.perm_device_information), ], ), + onPressed: _authenticate, ), ElevatedButton( - onPressed: _authenticateWithBiometrics, child: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -217,6 +214,7 @@ class _MyAppState extends State { const Icon(Icons.fingerprint), ], ), + onPressed: _authenticateWithBiometrics, ), ], ), diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index 9f9bd7b535a9..6afcf1ffed07 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,8 +1,6 @@ -## 1.0.3 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 1.0.2 diff --git a/packages/local_auth/local_auth_android/example/lib/main.dart b/packages/local_auth/local_auth_android/example/lib/main.dart index 016d955f0a3f..29b1d66440eb 100644 --- a/packages/local_auth/local_auth_android/example/lib/main.dart +++ b/packages/local_auth/local_auth_android/example/lib/main.dart @@ -12,14 +12,12 @@ import 'package:local_auth_android/local_auth_android.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatefulWidget { - const MyApp({Key? key}) : super(key: key); - @override - State createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { @@ -176,14 +174,14 @@ class _MyAppState extends State { Text( 'Device supports biometrics: $_deviceSupportsBiometrics\n'), ElevatedButton( - onPressed: _checkBiometrics, child: const Text('Check biometrics'), + onPressed: _checkBiometrics, ), const Divider(height: 100), Text('Enrolled biometrics: $_enrolledBiometrics\n'), ElevatedButton( - onPressed: _getEnrolledBiometrics, child: const Text('Get enrolled biometrics'), + onPressed: _getEnrolledBiometrics, ), const Divider(height: 100), Text('Current State: $_authorized\n'), @@ -202,7 +200,6 @@ class _MyAppState extends State { Column( children: [ ElevatedButton( - onPressed: _authenticate, child: Row( mainAxisSize: MainAxisSize.min, children: const [ @@ -210,9 +207,9 @@ class _MyAppState extends State { Icon(Icons.perm_device_information), ], ), + onPressed: _authenticate, ), ElevatedButton( - onPressed: _authenticateWithBiometrics, child: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -222,6 +219,7 @@ class _MyAppState extends State { const Icon(Icons.fingerprint), ], ), + onPressed: _authenticateWithBiometrics, ), ], ), diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml index cdd4e8225504..aad0b9b92e70 100644 --- a/packages/local_auth/local_auth_android/pubspec.yaml +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_android description: Android implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.3 +version: 1.0.2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_ios/CHANGELOG.md b/packages/local_auth/local_auth_ios/CHANGELOG.md index 2237cbe216f0..ca6ac43eb52f 100644 --- a/packages/local_auth/local_auth_ios/CHANGELOG.md +++ b/packages/local_auth/local_auth_ios/CHANGELOG.md @@ -1,8 +1,6 @@ -## 1.0.5 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 1.0.4 diff --git a/packages/local_auth/local_auth_ios/example/lib/main.dart b/packages/local_auth/local_auth_ios/example/lib/main.dart index 479a96ba809c..a8bf23b78a52 100644 --- a/packages/local_auth/local_auth_ios/example/lib/main.dart +++ b/packages/local_auth/local_auth_ios/example/lib/main.dart @@ -12,14 +12,12 @@ import 'package:local_auth_ios/local_auth_ios.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatefulWidget { - const MyApp({Key? key}) : super(key: key); - @override - State createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { @@ -175,14 +173,14 @@ class _MyAppState extends State { const Divider(height: 100), Text('Device supports biometrics: $_canCheckBiometrics\n'), ElevatedButton( - onPressed: _checkBiometrics, child: const Text('Check biometrics'), + onPressed: _checkBiometrics, ), const Divider(height: 100), Text('Enrolled biometrics: $_enrolledBiometrics\n'), ElevatedButton( - onPressed: _getEnrolledBiometrics, child: const Text('Get enrolled biometrics'), + onPressed: _getEnrolledBiometrics, ), const Divider(height: 100), Text('Current State: $_authorized\n'), @@ -201,7 +199,6 @@ class _MyAppState extends State { Column( children: [ ElevatedButton( - onPressed: _authenticate, child: Row( mainAxisSize: MainAxisSize.min, children: const [ @@ -209,9 +206,9 @@ class _MyAppState extends State { Icon(Icons.perm_device_information), ], ), + onPressed: _authenticate, ), ElevatedButton( - onPressed: _authenticateWithBiometrics, child: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -221,6 +218,7 @@ class _MyAppState extends State { const Icon(Icons.fingerprint), ], ), + onPressed: _authenticateWithBiometrics, ), ], ), diff --git a/packages/local_auth/local_auth_ios/pubspec.yaml b/packages/local_auth/local_auth_ios/pubspec.yaml index dded42b673f4..77ab74d383c8 100644 --- a/packages/local_auth/local_auth_ios/pubspec.yaml +++ b/packages/local_auth/local_auth_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_ios description: iOS implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.5 +version: 1.0.4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider/CHANGELOG.md b/packages/path_provider/path_provider/CHANGELOG.md index 990021671801..a26a8901d1d7 100644 --- a/packages/path_provider/path_provider/CHANGELOG.md +++ b/packages/path_provider/path_provider/CHANGELOG.md @@ -1,9 +1,7 @@ -## 2.0.10 +## NEXT * Removes unnecessary imports. * Adds OS version support information to README. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 2.0.9 diff --git a/packages/path_provider/path_provider/example/lib/main.dart b/packages/path_provider/path_provider/example/lib/main.dart index cb9c2eb1798d..90c2ccb93154 100644 --- a/packages/path_provider/path_provider/example/lib/main.dart +++ b/packages/path_provider/path_provider/example/lib/main.dart @@ -10,12 +10,10 @@ import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -33,7 +31,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -140,10 +138,10 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: _requestTempDirectory, child: const Text( 'Get Temporary Directory', ), + onPressed: _requestTempDirectory, ), ), FutureBuilder( @@ -157,10 +155,10 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: _requestAppDocumentsDirectory, child: const Text( 'Get Application Documents Directory', ), + onPressed: _requestAppDocumentsDirectory, ), ), FutureBuilder( @@ -174,10 +172,10 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: _requestAppSupportDirectory, child: const Text( 'Get Application Support Directory', ), + onPressed: _requestAppSupportDirectory, ), ), FutureBuilder( @@ -191,13 +189,13 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: - Platform.isAndroid ? null : _requestAppLibraryDirectory, child: Text( Platform.isAndroid ? 'Application Library Directory unavailable' : 'Get Application Library Directory', ), + onPressed: + Platform.isAndroid ? null : _requestAppLibraryDirectory, ), ), FutureBuilder( @@ -211,14 +209,14 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: !Platform.isAndroid - ? null - : _requestExternalStorageDirectory, child: Text( !Platform.isAndroid ? 'External storage is unavailable' : 'Get External Storage Directory', ), + onPressed: !Platform.isAndroid + ? null + : _requestExternalStorageDirectory, ), ), FutureBuilder( @@ -232,6 +230,11 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + child: Text( + !Platform.isAndroid + ? 'External directories are unavailable' + : 'Get External Storage Directories', + ), onPressed: !Platform.isAndroid ? null : () { @@ -239,11 +242,6 @@ class _MyHomePageState extends State { StorageDirectory.music, ); }, - child: Text( - !Platform.isAndroid - ? 'External directories are unavailable' - : 'Get External Storage Directories', - ), ), ), FutureBuilder?>( @@ -257,14 +255,14 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: !Platform.isAndroid - ? null - : _requestExternalCacheDirectories, child: Text( !Platform.isAndroid ? 'External directories are unavailable' : 'Get External Cache Directories', ), + onPressed: !Platform.isAndroid + ? null + : _requestExternalCacheDirectories, ), ), FutureBuilder?>( @@ -278,14 +276,14 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: Platform.isAndroid || Platform.isIOS - ? null - : _requestDownloadsDirectory, child: Text( Platform.isAndroid || Platform.isIOS ? 'Downloads directory is unavailable' : 'Get Downloads Directory', ), + onPressed: Platform.isAndroid || Platform.isIOS + ? null + : _requestDownloadsDirectory, ), ), FutureBuilder( diff --git a/packages/path_provider/path_provider/pubspec.yaml b/packages/path_provider/path_provider/pubspec.yaml index 6ca8325843e4..30ddfdcfb119 100644 --- a/packages/path_provider/path_provider/pubspec.yaml +++ b/packages/path_provider/path_provider/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider description: Flutter plugin for getting commonly used locations on host platform file systems, such as the temp and app data directories. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.10 +version: 2.0.9 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider_android/CHANGELOG.md b/packages/path_provider/path_provider_android/CHANGELOG.md index 4b15e2605038..31f8c81f8a65 100644 --- a/packages/path_provider/path_provider_android/CHANGELOG.md +++ b/packages/path_provider/path_provider_android/CHANGELOG.md @@ -1,8 +1,3 @@ -## 2.0.14 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 2.0.13 * Fixes typing build warning. diff --git a/packages/path_provider/path_provider_android/example/lib/main.dart b/packages/path_provider/path_provider_android/example/lib/main.dart index fc9424a33542..6e04f865bfcf 100644 --- a/packages/path_provider/path_provider_android/example/lib/main.dart +++ b/packages/path_provider/path_provider_android/example/lib/main.dart @@ -8,12 +8,10 @@ import 'package:flutter/material.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -31,7 +29,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -123,8 +121,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: _requestTempDirectory, child: const Text('Get Temporary Directory'), + onPressed: _requestTempDirectory, ), ), FutureBuilder( @@ -132,8 +130,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: _requestAppDocumentsDirectory, child: const Text('Get Application Documents Directory'), + onPressed: _requestAppDocumentsDirectory, ), ), FutureBuilder( @@ -141,8 +139,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: _requestAppSupportDirectory, child: const Text('Get Application Support Directory'), + onPressed: _requestAppSupportDirectory, ), ), FutureBuilder( @@ -150,8 +148,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: _requestExternalStorageDirectory, child: const Text('Get External Storage Directory'), + onPressed: _requestExternalStorageDirectory, ), ), FutureBuilder( @@ -176,8 +174,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: _requestExternalCacheDirectories, child: const Text('Get External Cache Directories'), + onPressed: _requestExternalCacheDirectories, ), ), ]), diff --git a/packages/path_provider/path_provider_android/pubspec.yaml b/packages/path_provider/path_provider_android/pubspec.yaml index f1dc92abdefc..93ed9848f75b 100644 --- a/packages/path_provider/path_provider_android/pubspec.yaml +++ b/packages/path_provider/path_provider_android/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_android description: Android implementation of the path_provider plugin. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.14 +version: 2.0.13 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider_ios/CHANGELOG.md b/packages/path_provider/path_provider_ios/CHANGELOG.md index 1940f5c7888e..543af778d2e2 100644 --- a/packages/path_provider/path_provider_ios/CHANGELOG.md +++ b/packages/path_provider/path_provider_ios/CHANGELOG.md @@ -1,8 +1,3 @@ -## 2.0.9 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 2.0.8 * Switches to a package-internal implementation of the platform interface. diff --git a/packages/path_provider/path_provider_ios/example/lib/main.dart b/packages/path_provider/path_provider_ios/example/lib/main.dart index d7140b76a06b..8c8d5410f923 100644 --- a/packages/path_provider/path_provider_ios/example/lib/main.dart +++ b/packages/path_provider/path_provider_ios/example/lib/main.dart @@ -8,12 +8,10 @@ import 'package:flutter/material.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -31,7 +29,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -92,8 +90,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: _requestTempDirectory, child: const Text('Get Temporary Directory'), + onPressed: _requestTempDirectory, ), ), FutureBuilder( @@ -101,8 +99,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: _requestAppDocumentsDirectory, child: const Text('Get Application Documents Directory'), + onPressed: _requestAppDocumentsDirectory, ), ), FutureBuilder( @@ -110,8 +108,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: _requestAppSupportDirectory, child: const Text('Get Application Support Directory'), + onPressed: _requestAppSupportDirectory, ), ), FutureBuilder( @@ -119,8 +117,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: _requestAppLibraryDirectory, child: const Text('Get Application Library Directory'), + onPressed: _requestAppLibraryDirectory, ), ), FutureBuilder( diff --git a/packages/path_provider/path_provider_ios/pubspec.yaml b/packages/path_provider/path_provider_ios/pubspec.yaml index d6c7de108c97..282f8e4f0ebd 100644 --- a/packages/path_provider/path_provider_ios/pubspec.yaml +++ b/packages/path_provider/path_provider_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_ios description: iOS implementation of the path_provider plugin. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.9 +version: 2.0.8 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider_linux/CHANGELOG.md b/packages/path_provider/path_provider_linux/CHANGELOG.md index c9c4bb3cc906..55235c3542f9 100644 --- a/packages/path_provider/path_provider_linux/CHANGELOG.md +++ b/packages/path_provider/path_provider_linux/CHANGELOG.md @@ -1,8 +1,3 @@ -## 2.1.6 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 2.1.5 * Removes dependency on `meta`. diff --git a/packages/path_provider/path_provider_linux/example/lib/main.dart b/packages/path_provider/path_provider_linux/example/lib/main.dart index 1c7c7e87397a..d365e6bdeab4 100644 --- a/packages/path_provider/path_provider_linux/example/lib/main.dart +++ b/packages/path_provider/path_provider_linux/example/lib/main.dart @@ -7,16 +7,13 @@ import 'package:flutter/services.dart'; import 'package:path_provider_linux/path_provider_linux.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } /// Sample app class MyApp extends StatefulWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - @override - State createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/path_provider/path_provider_linux/pubspec.yaml b/packages/path_provider/path_provider_linux/pubspec.yaml index 16438a3870d1..91304fc0b268 100644 --- a/packages/path_provider/path_provider_linux/pubspec.yaml +++ b/packages/path_provider/path_provider_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_linux description: Linux implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.1.6 +version: 2.1.5 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/path_provider/path_provider_macos/CHANGELOG.md b/packages/path_provider/path_provider_macos/CHANGELOG.md index c59ba971d461..047792f8bcc4 100644 --- a/packages/path_provider/path_provider_macos/CHANGELOG.md +++ b/packages/path_provider/path_provider_macos/CHANGELOG.md @@ -1,8 +1,3 @@ -## 2.0.6 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 2.0.5 * Removes dependency on `meta`. diff --git a/packages/path_provider/path_provider_macos/example/lib/main.dart b/packages/path_provider/path_provider_macos/example/lib/main.dart index 13a6fada5fef..67a0eb32eeda 100644 --- a/packages/path_provider/path_provider_macos/example/lib/main.dart +++ b/packages/path_provider/path_provider_macos/example/lib/main.dart @@ -8,15 +8,13 @@ import 'package:flutter/material.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } /// Sample app class MyApp extends StatefulWidget { - const MyApp({Key? key}) : super(key: key); - @override - State createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/path_provider/path_provider_macos/pubspec.yaml b/packages/path_provider/path_provider_macos/pubspec.yaml index 444165b86c3f..2451b6dedf80 100644 --- a/packages/path_provider/path_provider_macos/pubspec.yaml +++ b/packages/path_provider/path_provider_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_macos description: macOS implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.6 +version: 2.0.5 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/path_provider/path_provider_windows/CHANGELOG.md b/packages/path_provider/path_provider_windows/CHANGELOG.md index 014b6b36da2b..cd33e85a851d 100644 --- a/packages/path_provider/path_provider_windows/CHANGELOG.md +++ b/packages/path_provider/path_provider_windows/CHANGELOG.md @@ -1,8 +1,3 @@ -## 2.0.6 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 2.0.5 * Removes dependency on `meta`. diff --git a/packages/path_provider/path_provider_windows/example/lib/main.dart b/packages/path_provider/path_provider_windows/example/lib/main.dart index 4c63d245a16a..509292bf7405 100644 --- a/packages/path_provider/path_provider_windows/example/lib/main.dart +++ b/packages/path_provider/path_provider_windows/example/lib/main.dart @@ -8,15 +8,13 @@ import 'package:flutter/material.dart'; import 'package:path_provider_windows/path_provider_windows.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } /// Sample app class MyApp extends StatefulWidget { - const MyApp({Key? key}) : super(key: key); - @override - State createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/path_provider/path_provider_windows/pubspec.yaml b/packages/path_provider/path_provider_windows/pubspec.yaml index 49afdd6293e7..873fa0a6861b 100644 --- a/packages/path_provider/path_provider_windows/pubspec.yaml +++ b/packages/path_provider/path_provider_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_windows description: Windows implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.6 +version: 2.0.5 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/quick_actions/quick_actions/CHANGELOG.md b/packages/quick_actions/quick_actions/CHANGELOG.md index 73540a863364..c30d7052320e 100644 --- a/packages/quick_actions/quick_actions/CHANGELOG.md +++ b/packages/quick_actions/quick_actions/CHANGELOG.md @@ -1,10 +1,8 @@ -## 0.6.0+11 +## NEXT * Removes unnecessary imports. * Updates minimum Flutter version to 2.8. * Adds OS version support information to README. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 0.6.0+10 diff --git a/packages/quick_actions/quick_actions/example/lib/main.dart b/packages/quick_actions/quick_actions/example/lib/main.dart index cafbf0c351d9..1ce6f51d71de 100644 --- a/packages/quick_actions/quick_actions/example/lib/main.dart +++ b/packages/quick_actions/quick_actions/example/lib/main.dart @@ -8,12 +8,10 @@ import 'package:flutter/material.dart'; import 'package:quick_actions/quick_actions.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -30,7 +28,7 @@ class MyHomePage extends StatefulWidget { const MyHomePage({Key? key}) : super(key: key); @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/quick_actions/quick_actions/pubspec.yaml b/packages/quick_actions/quick_actions/pubspec.yaml index 37e8dbe5f3e3..8ef2d3ab4e02 100644 --- a/packages/quick_actions/quick_actions/pubspec.yaml +++ b/packages/quick_actions/quick_actions/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for creating shortcuts on home screen, also known as Quick Actions on iOS and App Shortcuts on Android. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+quick_actions%22 -version: 0.6.0+11 +version: 0.6.0+10 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/quick_actions/quick_actions_android/CHANGELOG.md b/packages/quick_actions/quick_actions_android/CHANGELOG.md index 56accc9d044c..98e8cf5e333b 100644 --- a/packages/quick_actions/quick_actions_android/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_android/CHANGELOG.md @@ -1,8 +1,3 @@ -## 0.6.0+10 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 0.6.0+9 * Switches to a package-internal implementation of the platform interface. \ No newline at end of file diff --git a/packages/quick_actions/quick_actions_android/example/lib/main.dart b/packages/quick_actions/quick_actions_android/example/lib/main.dart index d8b7832bf9dc..06f141073b33 100644 --- a/packages/quick_actions/quick_actions_android/example/lib/main.dart +++ b/packages/quick_actions/quick_actions_android/example/lib/main.dart @@ -8,12 +8,10 @@ import 'package:flutter/material.dart'; import 'package:quick_actions_android/quick_actions_android.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -30,7 +28,7 @@ class MyHomePage extends StatefulWidget { const MyHomePage({Key? key}) : super(key: key); @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/quick_actions/quick_actions_android/pubspec.yaml b/packages/quick_actions/quick_actions_android/pubspec.yaml index 4ddbc79ee5e9..cf9971dca945 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/plugins/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: 0.6.0+10 +version: 0.6.0+9 environment: sdk: ">=2.15.0 <3.0.0" diff --git a/packages/quick_actions/quick_actions_ios/CHANGELOG.md b/packages/quick_actions/quick_actions_ios/CHANGELOG.md index 56accc9d044c..d48afbd8d13a 100644 --- a/packages/quick_actions/quick_actions_ios/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_ios/CHANGELOG.md @@ -1,7 +1,3 @@ -## 0.6.0+10 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 0.6.0+9 diff --git a/packages/quick_actions/quick_actions_ios/example/lib/main.dart b/packages/quick_actions/quick_actions_ios/example/lib/main.dart index 008917b724e0..5173d952d623 100644 --- a/packages/quick_actions/quick_actions_ios/example/lib/main.dart +++ b/packages/quick_actions/quick_actions_ios/example/lib/main.dart @@ -8,12 +8,10 @@ import 'package:flutter/material.dart'; import 'package:quick_actions_ios/quick_actions_ios.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -30,7 +28,7 @@ class MyHomePage extends StatefulWidget { const MyHomePage({Key? key}) : super(key: key); @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/quick_actions/quick_actions_ios/pubspec.yaml b/packages/quick_actions/quick_actions_ios/pubspec.yaml index 47748b9789ad..26644ba12fde 100644 --- a/packages/quick_actions/quick_actions_ios/pubspec.yaml +++ b/packages/quick_actions/quick_actions_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: quick_actions_ios description: An implementation for the iOS platform of the Flutter `quick_actions` plugin. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.6.0+10 +version: 0.6.0+9 environment: sdk: ">=2.15.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences/CHANGELOG.md b/packages/shared_preferences/shared_preferences/CHANGELOG.md index 22c39aad98fd..84566e26e2c0 100644 --- a/packages/shared_preferences/shared_preferences/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences/CHANGELOG.md @@ -1,8 +1,6 @@ -## 2.0.14 +## NEXT * Adds OS version support information to README. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 2.0.13 diff --git a/packages/shared_preferences/shared_preferences/example/lib/main.dart b/packages/shared_preferences/shared_preferences/example/lib/main.dart index a2e72b446925..43f3c78ad920 100644 --- a/packages/shared_preferences/shared_preferences/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences/example/lib/main.dart @@ -10,12 +10,10 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences/pubspec.yaml b/packages/shared_preferences/shared_preferences/pubspec.yaml index 4218095c0efe..39b48ef51ed2 100644 --- a/packages/shared_preferences/shared_preferences/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for reading and writing simple key-value pairs. Wraps NSUserDefaults on iOS and SharedPreferences on Android. repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.14 +version: 2.0.13 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md index 51e99ec6d3d5..5321e869c497 100644 --- a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md @@ -1,8 +1,3 @@ -## 2.0.12 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 2.0.11 * Switches to an in-package method channel implementation. diff --git a/packages/shared_preferences/shared_preferences_android/example/lib/main.dart b/packages/shared_preferences/shared_preferences_android/example/lib/main.dart index bb513b09f6d5..06ee9434ba84 100644 --- a/packages/shared_preferences/shared_preferences_android/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_android/example/lib/main.dart @@ -8,12 +8,10 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_android/pubspec.yaml b/packages/shared_preferences/shared_preferences_android/pubspec.yaml index 2d8cc88d3703..7eb180f3ab48 100644 --- a/packages/shared_preferences/shared_preferences_android/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_android/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_android description: Android implementation of the shared_preferences plugin repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.12 +version: 2.0.11 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md b/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md index 29ade8d496a1..a5cc1d34e034 100644 --- a/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md @@ -1,8 +1,3 @@ -## 2.1.1 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 2.1.0 * Upgrades to using Pigeon. diff --git a/packages/shared_preferences/shared_preferences_ios/example/lib/main.dart b/packages/shared_preferences/shared_preferences_ios/example/lib/main.dart index bb513b09f6d5..06ee9434ba84 100644 --- a/packages/shared_preferences/shared_preferences_ios/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_ios/example/lib/main.dart @@ -8,12 +8,10 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_ios/pubspec.yaml b/packages/shared_preferences/shared_preferences_ios/pubspec.yaml index a8bde2e9f87f..33bf5baffd18 100644 --- a/packages/shared_preferences/shared_preferences_ios/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_ios description: iOS implementation of the shared_preferences plugin repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.1.1 +version: 2.1.0 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md index f0cb8322f385..34dd631746bb 100644 --- a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md @@ -1,8 +1,6 @@ -## 2.1.1 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 2.1.0 diff --git a/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart b/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart index d51be33baeed..4b71c7ea3beb 100644 --- a/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart @@ -10,12 +10,10 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_linux/shared_preferences_linux.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml index 8f3ce1723bc9..8ab692a613e2 100644 --- a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_linux description: Linux implementation of the shared_preferences plugin repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.1.1 +version: 2.1.0 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md b/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md index 8ba116a74fe0..0f194de44224 100644 --- a/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md @@ -1,8 +1,6 @@ -## 2.0.4 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 2.0.3 diff --git a/packages/shared_preferences/shared_preferences_macos/example/lib/main.dart b/packages/shared_preferences/shared_preferences_macos/example/lib/main.dart index e6bbe5931471..349e9c45405a 100644 --- a/packages/shared_preferences/shared_preferences_macos/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_macos/example/lib/main.dart @@ -10,12 +10,10 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_macos/pubspec.yaml b/packages/shared_preferences/shared_preferences_macos/pubspec.yaml index 615d0b05ba99..0873696e6d4f 100644 --- a/packages/shared_preferences/shared_preferences_macos/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_macos description: macOS implementation of the shared_preferences plugin. repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.4 +version: 2.0.3 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_web/CHANGELOG.md b/packages/shared_preferences/shared_preferences_web/CHANGELOG.md index 9ea249034105..2a6ffa20e37b 100644 --- a/packages/shared_preferences/shared_preferences_web/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_web/CHANGELOG.md @@ -1,8 +1,3 @@ -## 2.0.4 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 2.0.3 * Fixes newly enabled analyzer options. diff --git a/packages/shared_preferences/shared_preferences_web/example/lib/main.dart b/packages/shared_preferences/shared_preferences_web/example/lib/main.dart index 87422953de6a..341913a18490 100644 --- a/packages/shared_preferences/shared_preferences_web/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_web/example/lib/main.dart @@ -5,16 +5,13 @@ import 'package:flutter/material.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } /// App for testing class MyApp extends StatefulWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - @override - State createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/shared_preferences/shared_preferences_web/pubspec.yaml b/packages/shared_preferences/shared_preferences_web/pubspec.yaml index 9ff76d27714c..232c87c426fc 100644 --- a/packages/shared_preferences/shared_preferences_web/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_web/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_web description: Web platform implementation of shared_preferences repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.4 +version: 2.0.3 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md b/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md index f79f9e3d5d39..6c96681ce5b7 100644 --- a/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md @@ -1,8 +1,3 @@ -## 2.1.1 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 2.1.0 * Deprecated `SharedPreferencesWindows.instance` in favor of `SharedPreferencesStorePlatform.instance`. diff --git a/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart b/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart index 74d5e4c68772..40a9159cee70 100644 --- a/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart @@ -10,12 +10,10 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_windows/shared_preferences_windows.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_windows/pubspec.yaml b/packages/shared_preferences/shared_preferences_windows/pubspec.yaml index 99326cb24f18..6dcb5997f131 100644 --- a/packages/shared_preferences/shared_preferences_windows/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_windows description: Windows implementation of shared_preferences repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.1.1 +version: 2.1.0 environment: sdk: '>=2.12.0 <3.0.0' diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index 493412c3e006..b25956fd5919 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,8 +1,6 @@ -## 6.1.1 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 6.1.0 diff --git a/packages/url_launcher/url_launcher/example/lib/main.dart b/packages/url_launcher/url_launcher/example/lib/main.dart index a538940f1a68..898e80661296 100644 --- a/packages/url_launcher/url_launcher/example/lib/main.dart +++ b/packages/url_launcher/url_launcher/example/lib/main.dart @@ -11,12 +11,10 @@ import 'package:url_launcher/link.dart'; import 'package:url_launcher/url_launcher.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -34,7 +32,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher/lib/src/link.dart b/packages/url_launcher/url_launcher/lib/src/link.dart index 8c0c18e820e3..76cb97748003 100644 --- a/packages/url_launcher/url_launcher/lib/src/link.dart +++ b/packages/url_launcher/url_launcher/lib/src/link.dart @@ -86,7 +86,7 @@ class Link extends StatelessWidget implements LinkInfo { /// event channel messages to instruct the framework to push the route name. class DefaultLinkDelegate extends StatelessWidget { /// Creates a delegate for the given [link]. - const DefaultLinkDelegate(this.link, {Key? key}) : super(key: key); + const DefaultLinkDelegate(this.link); /// Given a [link], creates an instance of [DefaultLinkDelegate]. /// diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index c14b62a1e70a..6803d71032cb 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.1.1 +version: 6.1.0 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_android/CHANGELOG.md b/packages/url_launcher/url_launcher_android/CHANGELOG.md index 887178c479e4..69b96156d849 100644 --- a/packages/url_launcher/url_launcher_android/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_android/CHANGELOG.md @@ -1,8 +1,3 @@ -## 6.0.17 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 6.0.16 * Adds fallback querying for `canLaunch` with web URLs, to avoid false negatives diff --git a/packages/url_launcher/url_launcher_android/example/lib/main.dart b/packages/url_launcher/url_launcher_android/example/lib/main.dart index 672ae4a27665..7abc73430e8b 100644 --- a/packages/url_launcher/url_launcher_android/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_android/example/lib/main.dart @@ -10,12 +10,10 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -33,7 +31,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_android/pubspec.yaml b/packages/url_launcher/url_launcher_android/pubspec.yaml index 3c80170f1422..3230dfeffd2e 100644 --- a/packages/url_launcher/url_launcher_android/pubspec.yaml +++ b/packages/url_launcher/url_launcher_android/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_android description: Android implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.0.17 +version: 6.0.16 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_ios/CHANGELOG.md b/packages/url_launcher/url_launcher_ios/CHANGELOG.md index 5f6dd37142bb..6e0c8d6a20d7 100644 --- a/packages/url_launcher/url_launcher_ios/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_ios/CHANGELOG.md @@ -1,8 +1,3 @@ -## 6.0.16 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 6.0.15 * Switches to an in-package method channel implementation. diff --git a/packages/url_launcher/url_launcher_ios/example/lib/main.dart b/packages/url_launcher/url_launcher_ios/example/lib/main.dart index 7aa3a4b74e83..2f73622ebb41 100644 --- a/packages/url_launcher/url_launcher_ios/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_ios/example/lib/main.dart @@ -10,12 +10,10 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -33,7 +31,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_ios/pubspec.yaml b/packages/url_launcher/url_launcher_ios/pubspec.yaml index 0b21bad35204..8a5bfd20c8f4 100644 --- a/packages/url_launcher/url_launcher_ios/pubspec.yaml +++ b/packages/url_launcher/url_launcher_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_ios description: iOS implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.0.16 +version: 6.0.15 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_linux/CHANGELOG.md b/packages/url_launcher/url_launcher_linux/CHANGELOG.md index 27c18a66805b..0fc373f2ebb1 100644 --- a/packages/url_launcher/url_launcher_linux/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_linux/CHANGELOG.md @@ -1,8 +1,3 @@ -## 3.0.1 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 3.0.0 * Changes the major version since, due to a typo in `default_package` in diff --git a/packages/url_launcher/url_launcher_linux/example/lib/main.dart b/packages/url_launcher/url_launcher_linux/example/lib/main.dart index 0b985e78ac0d..a9a5d22dadd5 100644 --- a/packages/url_launcher/url_launcher_linux/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_linux/example/lib/main.dart @@ -9,12 +9,10 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -32,7 +30,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_linux/pubspec.yaml b/packages/url_launcher/url_launcher_linux/pubspec.yaml index c9472045e499..cb9a0be0aa41 100644 --- a/packages/url_launcher/url_launcher_linux/pubspec.yaml +++ b/packages/url_launcher/url_launcher_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_linux description: Linux implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 3.0.1 +version: 3.0.0 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_macos/CHANGELOG.md b/packages/url_launcher/url_launcher_macos/CHANGELOG.md index 2fa5e918eadd..082bc45fc2e8 100644 --- a/packages/url_launcher/url_launcher_macos/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_macos/CHANGELOG.md @@ -1,8 +1,3 @@ -## 3.0.1 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 3.0.0 * Changes the major version since, due to a typo in `default_package` in diff --git a/packages/url_launcher/url_launcher_macos/example/lib/main.dart b/packages/url_launcher/url_launcher_macos/example/lib/main.dart index 0b985e78ac0d..a9a5d22dadd5 100644 --- a/packages/url_launcher/url_launcher_macos/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_macos/example/lib/main.dart @@ -9,12 +9,10 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -32,7 +30,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_macos/pubspec.yaml b/packages/url_launcher/url_launcher_macos/pubspec.yaml index edda6b67cfb3..8b5183b1914d 100644 --- a/packages/url_launcher/url_launcher_macos/pubspec.yaml +++ b/packages/url_launcher/url_launcher_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_macos description: macOS implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 3.0.1 +version: 3.0.0 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_web/CHANGELOG.md b/packages/url_launcher/url_launcher_web/CHANGELOG.md index b53a92cee707..a434b7af70c2 100644 --- a/packages/url_launcher/url_launcher_web/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_web/CHANGELOG.md @@ -1,8 +1,3 @@ -## 2.0.10 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 2.0.9 - Fixes invalid routes when opening a `Link` in a new tab diff --git a/packages/url_launcher/url_launcher_web/example/lib/main.dart b/packages/url_launcher/url_launcher_web/example/lib/main.dart index 87422953de6a..341913a18490 100644 --- a/packages/url_launcher/url_launcher_web/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_web/example/lib/main.dart @@ -5,16 +5,13 @@ import 'package:flutter/material.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } /// App for testing class MyApp extends StatefulWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - @override - State createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/url_launcher/url_launcher_web/lib/src/link.dart b/packages/url_launcher/url_launcher_web/lib/src/link.dart index eccd9aef80e3..4498e74ea9ce 100644 --- a/packages/url_launcher/url_launcher_web/lib/src/link.dart +++ b/packages/url_launcher/url_launcher_web/lib/src/link.dart @@ -31,7 +31,7 @@ HtmlViewFactory get linkViewFactory => LinkViewController._viewFactory; /// It uses a platform view to render an anchor element in the DOM. class WebLinkDelegate extends StatefulWidget { /// Creates a delegate for the given [link]. - const WebLinkDelegate(this.link, {Key? key}) : super(key: key); + const WebLinkDelegate(this.link); /// Information about the link built by the app. final LinkInfo link; diff --git a/packages/url_launcher/url_launcher_web/pubspec.yaml b/packages/url_launcher/url_launcher_web/pubspec.yaml index cd8ed2d269c8..c45c062255ad 100644 --- a/packages/url_launcher/url_launcher_web/pubspec.yaml +++ b/packages/url_launcher/url_launcher_web/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_web description: Web platform implementation of url_launcher repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 2.0.10 +version: 2.0.9 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_windows/CHANGELOG.md b/packages/url_launcher/url_launcher_windows/CHANGELOG.md index 3ff14fd2f18a..e02f5a2288e1 100644 --- a/packages/url_launcher/url_launcher_windows/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_windows/CHANGELOG.md @@ -1,8 +1,3 @@ -## 3.0.1 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 3.0.0 * Changes the major version since, due to a typo in `default_package` in diff --git a/packages/url_launcher/url_launcher_windows/example/lib/main.dart b/packages/url_launcher/url_launcher_windows/example/lib/main.dart index 0b985e78ac0d..a9a5d22dadd5 100644 --- a/packages/url_launcher/url_launcher_windows/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_windows/example/lib/main.dart @@ -9,12 +9,10 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -32,7 +30,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_windows/pubspec.yaml b/packages/url_launcher/url_launcher_windows/pubspec.yaml index c3f224e26adf..95f17ad9e921 100644 --- a/packages/url_launcher/url_launcher_windows/pubspec.yaml +++ b/packages/url_launcher/url_launcher_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_windows description: Windows implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 3.0.1 +version: 3.0.0 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index ede890162f86..af01c64fd554 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,8 +1,6 @@ -## 2.4.1 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 2.4.0 diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index a6ec51015c33..39cd415dbb79 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -708,14 +708,14 @@ class _VideoAppLifeCycleObserver extends Object with WidgetsBindingObserver { /// Widget that displays the video controlled by [controller]. class VideoPlayer extends StatefulWidget { /// Uses the given [controller] for all video rendered in this widget. - const VideoPlayer(this.controller, {Key? key}) : super(key: key); + const VideoPlayer(this.controller); /// The [VideoPlayerController] responsible for the video being rendered in /// this widget. final VideoPlayerController controller; @override - State createState() => _VideoPlayerState(); + _VideoPlayerState createState() => _VideoPlayerState(); } class _VideoPlayerState extends State { @@ -883,11 +883,10 @@ class VideoProgressIndicator extends StatefulWidget { /// to `top: 5.0`. const VideoProgressIndicator( this.controller, { - Key? key, this.colors = const VideoProgressColors(), required this.allowScrubbing, this.padding = const EdgeInsets.only(top: 5.0), - }) : super(key: key); + }); /// The [VideoPlayerController] that actually associates a video with this /// widget. @@ -911,7 +910,7 @@ class VideoProgressIndicator extends StatefulWidget { final EdgeInsets padding; @override - State createState() => _VideoProgressIndicatorState(); + _VideoProgressIndicatorState createState() => _VideoProgressIndicatorState(); } class _VideoProgressIndicatorState extends State { @@ -985,8 +984,8 @@ class _VideoProgressIndicatorState extends State { ); if (widget.allowScrubbing) { return _VideoScrubber( - controller: controller, child: paddedProgressIndicator, + controller: controller, ); } else { return paddedProgressIndicator; diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index b0ca56429271..0d654a4330a7 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for displaying inline video with other Flutter widgets on Android, iOS, and web. repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.4.1 +version: 2.4.0 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index 08acba895eba..16dd52ca6da0 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,8 +1,6 @@ -## 2.3.3 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 2.3.2 diff --git a/packages/video_player/video_player_android/example/lib/mini_controller.dart b/packages/video_player/video_player_android/example/lib/mini_controller.dart index 5bce3117d0d6..498dbffc9e84 100644 --- a/packages/video_player/video_player_android/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_android/example/lib/mini_controller.dart @@ -351,14 +351,14 @@ class MiniController extends ValueNotifier { /// Widget that displays the video controlled by [controller]. class VideoPlayer extends StatefulWidget { /// Uses the given [controller] for all video rendered in this widget. - const VideoPlayer(this.controller, {Key? key}) : super(key: key); + const VideoPlayer(this.controller); /// The [MiniController] responsible for the video being rendered in /// this widget. final MiniController controller; @override - State createState() => _VideoPlayerState(); + _VideoPlayerState createState() => _VideoPlayerState(); } class _VideoPlayerState extends State { @@ -450,14 +450,14 @@ class _VideoScrubberState extends State<_VideoScrubber> { class VideoProgressIndicator extends StatefulWidget { /// Construct an instance that displays the play/buffering status of the video /// controlled by [controller]. - const VideoProgressIndicator(this.controller, {Key? key}) : super(key: key); + const VideoProgressIndicator(this.controller); /// The [MiniController] that actually associates a video with this /// widget. final MiniController controller; @override - State createState() => _VideoProgressIndicatorState(); + _VideoProgressIndicatorState createState() => _VideoProgressIndicatorState(); } class _VideoProgressIndicatorState extends State { @@ -527,11 +527,11 @@ class _VideoProgressIndicatorState extends State { ); } return _VideoScrubber( - controller: controller, child: Padding( padding: const EdgeInsets.only(top: 5.0), child: progressIndicator, ), + controller: controller, ); } } diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index bc69fd41369a..aa288ed71eac 100644 --- a/packages/video_player/video_player_android/pubspec.yaml +++ b/packages/video_player/video_player_android/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_android description: Android implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.3 +version: 2.3.2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index 6ab5398f7013..d77c36c915b6 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,8 +1,6 @@ -## 2.3.4 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 2.3.3 diff --git a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart index 5bce3117d0d6..498dbffc9e84 100644 --- a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart @@ -351,14 +351,14 @@ class MiniController extends ValueNotifier { /// Widget that displays the video controlled by [controller]. class VideoPlayer extends StatefulWidget { /// Uses the given [controller] for all video rendered in this widget. - const VideoPlayer(this.controller, {Key? key}) : super(key: key); + const VideoPlayer(this.controller); /// The [MiniController] responsible for the video being rendered in /// this widget. final MiniController controller; @override - State createState() => _VideoPlayerState(); + _VideoPlayerState createState() => _VideoPlayerState(); } class _VideoPlayerState extends State { @@ -450,14 +450,14 @@ class _VideoScrubberState extends State<_VideoScrubber> { class VideoProgressIndicator extends StatefulWidget { /// Construct an instance that displays the play/buffering status of the video /// controlled by [controller]. - const VideoProgressIndicator(this.controller, {Key? key}) : super(key: key); + const VideoProgressIndicator(this.controller); /// The [MiniController] that actually associates a video with this /// widget. final MiniController controller; @override - State createState() => _VideoProgressIndicatorState(); + _VideoProgressIndicatorState createState() => _VideoProgressIndicatorState(); } class _VideoProgressIndicatorState extends State { @@ -527,11 +527,11 @@ class _VideoProgressIndicatorState extends State { ); } return _VideoScrubber( - controller: controller, child: Padding( padding: const EdgeInsets.only(top: 5.0), child: progressIndicator, ), + controller: controller, ); } } diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index 380d8343c024..b3cc69eca958 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_avfoundation description: iOS implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.4 +version: 2.3.3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md index 094ffda207c5..00788c4386fe 100644 --- a/packages/video_player/video_player_web/CHANGELOG.md +++ b/packages/video_player/video_player_web/CHANGELOG.md @@ -1,8 +1,6 @@ -## 2.0.9 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 2.0.8 diff --git a/packages/video_player/video_player_web/example/lib/main.dart b/packages/video_player/video_player_web/example/lib/main.dart index 87422953de6a..341913a18490 100644 --- a/packages/video_player/video_player_web/example/lib/main.dart +++ b/packages/video_player/video_player_web/example/lib/main.dart @@ -5,16 +5,13 @@ import 'package:flutter/material.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } /// App for testing class MyApp extends StatefulWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - @override - State createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml index 7af0dc46dde8..064517e1f264 100644 --- a/packages/video_player/video_player_web/pubspec.yaml +++ b/packages/video_player/video_player_web/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_web description: Web platform implementation of video_player. repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.0.9 +version: 2.0.8 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index fa47a6cc3143..7a56f3f176d0 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,8 +1,6 @@ -## 3.0.3 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 3.0.2 @@ -13,7 +11,7 @@ * Removes a duplicate Android-specific integration test. * Fixes an integration test race condition. -* Fixes comments (accidentally mixed // with ///). +* Fixes comments (accidentially mixed // with ///). ## 3.0.0 diff --git a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart index cc001f336849..ba321264ee1e 100644 --- a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart @@ -1306,8 +1306,7 @@ Future _runJavascriptReturningResult( class ResizableWebView extends StatefulWidget { const ResizableWebView( - {Key? key, required this.onResize, required this.onPageFinished}) - : super(key: key); + {required this.onResize, required this.onPageFinished}); final JavascriptMessageHandler onResize; final VoidCallback onPageFinished; diff --git a/packages/webview_flutter/webview_flutter/example/lib/main.dart b/packages/webview_flutter/webview_flutter/example/lib/main.dart index 3d8731127970..51080be01df0 100644 --- a/packages/webview_flutter/webview_flutter/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter/example/lib/main.dart @@ -71,12 +71,12 @@ const String kTransparentBackgroundPage = ''' '''; class WebViewExample extends StatefulWidget { - const WebViewExample({Key? key, this.cookieManager}) : super(key: key); + const WebViewExample({this.cookieManager}); final CookieManager? cookieManager; @override - State createState() => _WebViewExampleState(); + _WebViewExampleState createState() => _WebViewExampleState(); } class _WebViewExampleState extends State { @@ -190,9 +190,8 @@ enum MenuOptions { } class SampleMenu extends StatelessWidget { - SampleMenu(this.controller, CookieManager? cookieManager, {Key? key}) - : cookieManager = cookieManager ?? CookieManager(), - super(key: key); + SampleMenu(this.controller, CookieManager? cookieManager) + : cookieManager = cookieManager ?? CookieManager(); final Future controller; late final CookieManager cookieManager; @@ -251,8 +250,8 @@ class SampleMenu extends StatelessWidget { itemBuilder: (BuildContext context) => >[ PopupMenuItem( value: MenuOptions.showUserAgent, - enabled: controller.hasData, child: const Text('Show user agent'), + enabled: controller.hasData, ), const PopupMenuItem( value: MenuOptions.listCookies, @@ -444,9 +443,8 @@ class SampleMenu extends StatelessWidget { } class NavigationControls extends StatelessWidget { - const NavigationControls(this._webViewControllerFuture, {Key? key}) - : assert(_webViewControllerFuture != null), - super(key: key); + const NavigationControls(this._webViewControllerFuture) + : assert(_webViewControllerFuture != null); final Future _webViewControllerFuture; diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index a48f6f912c2d..10350984ce9a 100644 --- a/packages/webview_flutter/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter description: A Flutter plugin that provides a WebView widget on Android and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 3.0.3 +version: 3.0.2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 4a451442f6cc..edaa0883713f 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,8 +1,6 @@ -## 2.8.7 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 2.8.6 diff --git a/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart index 4c06fa6b3c18..51e09912da23 100644 --- a/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart @@ -1446,10 +1446,9 @@ Future _runJavaScriptReturningResult( class ResizableWebView extends StatefulWidget { const ResizableWebView({ - Key? key, required this.onResize, required this.onPageFinished, - }) : super(key: key); + }); final JavascriptMessageHandler onResize; final VoidCallback onPageFinished; diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart index 349a64916e8b..5d19ca71ac84 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart @@ -247,8 +247,8 @@ class _SampleMenu extends StatelessWidget { itemBuilder: (BuildContext context) => >[ PopupMenuItem<_MenuOptions>( value: _MenuOptions.showUserAgent, - enabled: controller.hasData, child: const Text('Show user agent'), + enabled: controller.hasData, ), const PopupMenuItem<_MenuOptions>( value: _MenuOptions.listCookies, diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart index 56745314d92b..91ea66376904 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart @@ -250,7 +250,7 @@ class WebView extends StatefulWidget { final Color? backgroundColor; @override - State createState() => _WebViewState(); + _WebViewState createState() => _WebViewState(); } class _WebViewState extends State { diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart index f1b130c7e365..28d169c9cb94 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart @@ -15,7 +15,6 @@ import 'src/android_webview.dart' as android_webview; class WebViewAndroidWidget extends StatefulWidget { /// Constructs a [WebViewAndroidWidget]. const WebViewAndroidWidget({ - Key? key, required this.creationParams, required this.useHybridComposition, required this.callbacksHandler, @@ -25,7 +24,7 @@ class WebViewAndroidWidget extends StatefulWidget { @visibleForTesting this.flutterAssetManager = const android_webview.FlutterAssetManager(), @visibleForTesting this.webStorage, - }) : super(key: key); + }); /// Initial parameters used to setup the WebView. final CreationParams creationParams; diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 407887f2ba95..9a7c48a4dcd8 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_android description: A Flutter plugin that provides a WebView widget on Android. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.8.7 +version: 2.8.6 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md index b7254e1a0a7a..9f7ebe368941 100644 --- a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md @@ -1,9 +1,7 @@ -## 0.1.0+2 +## NEXT * Removes unnecessary imports. * Fixes unit tests to run on latest `master` version of Flutter. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 0.1.0+1 diff --git a/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart index ffd3367d33f4..8cd74f660f58 100644 --- a/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart @@ -46,7 +46,7 @@ class WebView extends StatefulWidget { final String? initialUrl; @override - State createState() => _WebViewState(); + _WebViewState createState() => _WebViewState(); } class _WebViewState extends State { diff --git a/packages/webview_flutter/webview_flutter_web/pubspec.yaml b/packages/webview_flutter/webview_flutter_web/pubspec.yaml index 35a7b74a764c..bd154387097e 100644 --- a/packages/webview_flutter/webview_flutter_web/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_web/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_web description: A Flutter plugin that provides a WebView widget on web. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 0.1.0+2 +version: 0.1.0+1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index 6db769b0d922..f042dd081475 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,8 +1,6 @@ -## 2.7.4 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 2.7.3 diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart index aa376f8358e9..ceff62e3d5e8 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart @@ -1181,8 +1181,7 @@ Future _getUserAgent(WebViewController controller) async { class ResizableWebView extends StatefulWidget { const ResizableWebView( - {Key? key, required this.onResize, required this.onPageFinished}) - : super(key: key); + {required this.onResize, required this.onPageFinished}); final JavascriptMessageHandler onResize; final VoidCallback onPageFinished; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart index 7b30923e1e54..e8b86d9c5773 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart @@ -232,8 +232,8 @@ class _SampleMenu extends StatelessWidget { itemBuilder: (BuildContext context) => >[ PopupMenuItem<_MenuOptions>( value: _MenuOptions.showUserAgent, - enabled: controller.hasData, child: const Text('Show user agent'), + enabled: controller.hasData, ), const PopupMenuItem<_MenuOptions>( value: _MenuOptions.listCookies, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart index c44c4e743669..4d479f943d62 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart @@ -240,7 +240,7 @@ class WebView extends StatefulWidget { final Color? backgroundColor; @override - State createState() => _WebViewState(); + _WebViewState createState() => _WebViewState(); } class _WebViewState extends State { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 8c37112d7a24..012cd221599b 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -18,14 +18,13 @@ import 'web_kit/web_kit.dart'; class WebKitWebViewWidget extends StatefulWidget { /// Constructs a [WebKitWebViewWidget]. const WebKitWebViewWidget({ - Key? key, required this.creationParams, required this.callbacksHandler, required this.javascriptChannelRegistry, required this.onBuildWidget, this.configuration, @visibleForTesting this.webViewProxy = const WebViewWidgetProxy(), - }) : super(key: key); + }); /// The initial parameters used to setup the WebView. final CreationParams creationParams; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 365e64720d4f..f4e72b8f14eb 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_wkwebview description: A Flutter plugin that provides a WebView widget based on Apple's WKWebView control. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_wkwebview issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.7.4 +version: 2.7.3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 5faa7c8201d9..9ed2a9278653 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +- Fixes changelog validation when reverting to a `NEXT` state. + ## 0.8.5 - Updates `test` to inculde the Dart unit tests of examples, if any. @@ -322,7 +326,7 @@ and `firebase-test-lab`. ## v.0.0.36+2 -- Default to showing podspec lint warnings. +- Default to showing podspec lint warnings ## v.0.0.36+1 diff --git a/script/tool/lib/src/version_check_command.dart b/script/tool/lib/src/version_check_command.dart index fb768c19b524..c0e67764360e 100644 --- a/script/tool/lib/src/version_check_command.dart +++ b/script/tool/lib/src/version_check_command.dart @@ -39,8 +39,11 @@ enum _CurrentVersionState { /// The version is unchanged. unchanged, - /// The version has changed, and the transition is valid. - validChange, + /// The version has increased, and the transition is valid. + validIncrease, + + /// The version has decrease, and the transition is a valid revert. + validRevert, /// The version has changed, and the transition is invalid. invalidChange, @@ -218,7 +221,8 @@ class VersionCheckCommand extends PackageLoopingCommand { case _CurrentVersionState.unchanged: versionChanged = false; break; - case _CurrentVersionState.validChange: + case _CurrentVersionState.validIncrease: + case _CurrentVersionState.validRevert: versionChanged = true; break; case _CurrentVersionState.invalidChange: @@ -232,7 +236,7 @@ class VersionCheckCommand extends PackageLoopingCommand { } if (!(await _validateChangelogVersion(package, - pubspec: pubspec, pubspecVersionChanged: versionChanged))) { + pubspec: pubspec, pubspecVersionState: versionState))) { errors.add('CHANGELOG.md failed validation.'); } @@ -322,7 +326,7 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body} '${getBoolArg(_againstPubFlag) ? 'on pub server' : 'at git base'}.'); logWarning( '${indentation}If this plugin is not new, something has gone wrong.'); - return _CurrentVersionState.validChange; // Assume new, thus valid. + return _CurrentVersionState.validIncrease; // Assume new, thus valid. } if (previousVersion == currentVersion) { @@ -340,7 +344,7 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body} if (possibleVersionsFromNewVersion.containsKey(previousVersion)) { logWarning('${indentation}New version is lower than previous version. ' 'This is assumed to be a revert.'); - return _CurrentVersionState.validChange; + return _CurrentVersionState.validRevert; } } @@ -367,7 +371,7 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body} return _CurrentVersionState.invalidChange; } - return _CurrentVersionState.validChange; + return _CurrentVersionState.validIncrease; } /// Checks whether or not [package]'s CHANGELOG's versioning is correct, @@ -378,7 +382,7 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body} Future _validateChangelogVersion( RepositoryPackage package, { required Pubspec pubspec, - required bool pubspecVersionChanged, + required _CurrentVersionState pubspecVersionState, }) async { // This method isn't called unless `version` is non-null. final Version fromPubspec = pubspec.version!; @@ -405,8 +409,9 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body} // changes that don't warrant publishing on their own. final bool hasNextSection = versionString == 'NEXT'; if (hasNextSection) { - // NEXT should not be present in a commit that changes the version. - if (pubspecVersionChanged) { + // NEXT should not be present in a commit that increases the version. + if (pubspecVersionState == _CurrentVersionState.validIncrease || + pubspecVersionState == _CurrentVersionState.invalidChange) { printError(badNextErrorMessage); return false; } diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart index b0a8990e1300..5c38bd5f7033 100644 --- a/script/tool/test/util.dart +++ b/script/tool/test/util.dart @@ -319,14 +319,14 @@ String _pluginPlatformSection( return entry; } -typedef ErrorHandler = void Function(Error error); +typedef _ErrorHandler = void Function(Error error); /// Run the command [runner] with the given [args] and return /// what was printed. /// A custom [errorHandler] can be used to handle the runner error as desired without throwing. Future> runCapturingPrint( CommandRunner runner, List args, - {ErrorHandler? errorHandler}) async { + {_ErrorHandler? errorHandler}) async { final List prints = []; final ZoneSpecification spec = ZoneSpecification( print: (_, __, ___, String message) { diff --git a/script/tool/test/version_check_command_test.dart b/script/tool/test/version_check_command_test.dart index aeacd77635df..5b8ed97e20c5 100644 --- a/script/tool/test/version_check_command_test.dart +++ b/script/tool/test/version_check_command_test.dart @@ -44,7 +44,7 @@ class MockProcessResult extends Mock implements io.ProcessResult {} void main() { const String indentation = ' '; - group('$VersionCheckCommand', () { + group('VersionCheckCommand', () { late FileSystem fileSystem; late MockPlatform mockPlatform; late Directory packagesDir; @@ -602,7 +602,7 @@ This is necessary because of X, Y, and Z ); }); - test('Fail if the version changes without replacing NEXT', () async { + test('fails if the version increases without replacing NEXT', () async { final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.1'); @@ -632,6 +632,33 @@ This is necessary because of X, Y, and Z ); }); + test('allows NEXT for a revert', () async { + final RepositoryPackage plugin = + createFakePlugin('plugin', packagesDir, version: '1.0.0'); + + const String changelog = ''' +## NEXT +* Some changes that should be listed as part of 1.0.1. +## 1.0.0 +* Some other changes. +'''; + createFakeCHANGELOG(plugin, changelog); + createFakeCHANGELOG(plugin, changelog); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.1'), + ]; + + final List output = await runCapturingPrint( + runner, ['version-check', '--base-sha=main']); + expect( + output, + containsAllInOrder([ + contains('New version is lower than previous version. ' + 'This is assumed to be a revert.'), + ]), + ); + }); + test( 'fails gracefully if the version headers are not found due to using the wrong style', () async { From 5e50710c4ad2da9ee227cd52580a465364296b8f Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 10 May 2022 14:43:28 -0400 Subject: [PATCH 542/600] Re-land: Enable lints `library_private_types_in_public_api`, `sort_child_properties_last` and `use_key_in_widget_constructors` (#5692) Re-lands https://github.com/flutter/plugins/pull/5428 This is a revert of flutter/plugins#5691 (the revert of the above) with the following changes: - Excludes the repo tooling changes that had to be added to the revert, since we want those - Fixes local_auth: - Updates code for the new analysis failure - Fixes the bad version merge that dropped the version change - Reverts a version change in `file_selector_platform_interface`, which didn't otherwise change --- analysis_options.yaml | 5 +- packages/camera/camera/CHANGELOG.md | 4 +- packages/camera/camera/README.md | 14 ++- packages/camera/camera/example/lib/main.dart | 60 +++++----- .../example/lib/readme_full_example.dart | 16 +-- .../camera/camera/example/test/main_test.dart | 2 +- .../camera/camera/lib/src/camera_preview.dart | 3 +- packages/camera/camera/pubspec.yaml | 2 +- packages/camera/camera_web/CHANGELOG.md | 5 + .../camera/camera_web/example/lib/main.dart | 5 +- packages/camera/camera_web/pubspec.yaml | 2 +- packages/camera/camera_windows/CHANGELOG.md | 4 +- .../camera_windows/example/lib/main.dart | 5 +- packages/camera/camera_windows/pubspec.yaml | 2 +- packages/espresso/CHANGELOG.md | 7 +- packages/espresso/example/lib/main.dart | 7 +- packages/espresso/pubspec.yaml | 2 +- .../file_selector/file_selector/CHANGELOG.md | 4 +- .../example/lib/get_directory_page.dart | 5 +- .../file_selector/example/lib/home_page.dart | 3 + .../file_selector/example/lib/main.dart | 16 ++- .../example/lib/open_image_page.dart | 6 +- .../lib/open_multiple_images_page.dart | 5 +- .../example/lib/open_text_page.dart | 6 +- .../example/lib/save_text_page.dart | 3 + .../file_selector/file_selector/pubspec.yaml | 2 +- .../file_selector_macos/CHANGELOG.md | 4 +- .../example/lib/get_directory_page.dart | 5 +- .../example/lib/home_page.dart | 3 + .../file_selector_macos/example/lib/main.dart | 16 ++- .../example/lib/open_image_page.dart | 6 +- .../lib/open_multiple_images_page.dart | 5 +- .../example/lib/open_text_page.dart | 6 +- .../example/lib/save_text_page.dart | 5 +- .../file_selector_macos/pubspec.yaml | 2 +- .../file_selector_web/CHANGELOG.md | 5 + .../file_selector_web/example/lib/main.dart | 7 +- .../file_selector_web/pubspec.yaml | 2 +- .../file_selector_windows/CHANGELOG.md | 4 +- .../example/lib/get_directory_page.dart | 5 +- .../example/lib/home_page.dart | 3 + .../example/lib/main.dart | 16 ++- .../example/lib/open_image_page.dart | 6 +- .../lib/open_multiple_images_page.dart | 5 +- .../example/lib/open_text_page.dart | 6 +- .../example/lib/save_text_page.dart | 5 +- .../file_selector_windows/pubspec.yaml | 2 +- .../CHANGELOG.md | 4 +- ...flutter_plugin_android_lifecycle_test.dart | 2 +- .../example/lib/main.dart | 8 +- .../pubspec.yaml | 2 +- .../google_maps_flutter/CHANGELOG.md | 6 +- .../example/lib/animate_camera.dart | 6 +- .../example/lib/lite_mode.dart | 3 +- .../google_maps_flutter/example/lib/main.dart | 8 +- .../example/lib/map_click.dart | 3 +- .../example/lib/map_coordinates.dart | 3 +- .../example/lib/map_ui.dart | 5 +- .../example/lib/marker_icons.dart | 5 +- .../example/lib/move_camera.dart | 5 +- .../example/lib/padding.dart | 5 +- .../google_maps_flutter/example/lib/page.dart | 3 +- .../example/lib/place_circle.dart | 18 +-- .../example/lib/place_marker.dart | 31 ++--- .../example/lib/place_polygon.dart | 24 ++-- .../example/lib/place_polyline.dart | 26 ++-- .../example/lib/scrolling_map.dart | 5 +- .../example/lib/snapshot.dart | 5 +- .../example/lib/tile_overlay.dart | 11 +- .../lib/src/controller.dart | 2 + .../google_maps_flutter/pubspec.yaml | 2 +- .../google_maps_flutter_web/CHANGELOG.md | 4 +- .../example/lib/main.dart | 2 +- .../google_maps_flutter_web/pubspec.yaml | 2 +- .../google_sign_in/CHANGELOG.md | 5 + .../google_sign_in/example/lib/main.dart | 8 +- .../google_sign_in/lib/widgets.dart | 4 +- .../google_sign_in/pubspec.yaml | 2 +- .../google_sign_in_android/CHANGELOG.md | 5 + .../example/lib/main.dart | 8 +- .../google_sign_in_android/pubspec.yaml | 2 +- .../google_sign_in_ios/CHANGELOG.md | 5 + .../google_sign_in_ios/example/lib/main.dart | 8 +- .../google_sign_in_ios/pubspec.yaml | 2 +- .../google_sign_in_web/CHANGELOG.md | 5 + .../google_sign_in_web/example/lib/main.dart | 7 +- .../google_sign_in_web/pubspec.yaml | 2 +- .../image_picker/image_picker/CHANGELOG.md | 5 + .../image_picker/example/lib/main.dart | 39 +++--- .../image_picker/image_picker/pubspec.yaml | 2 +- .../image_picker_android/CHANGELOG.md | 5 + .../example/lib/main.dart | 39 +++--- .../image_picker_android/pubspec.yaml | 2 +- .../image_picker_for_web/CHANGELOG.md | 5 + .../example/lib/main.dart | 7 +- .../image_picker_for_web/pubspec.yaml | 2 +- .../image_picker_ios/CHANGELOG.md | 4 +- .../image_picker_ios/example/lib/main.dart | 39 +++--- .../image_picker_ios/pubspec.yaml | 2 +- .../image_picker_windows/CHANGELOG.md | 4 +- .../example/lib/main.dart | 31 ++--- .../image_picker_windows/pubspec.yaml | 2 +- .../in_app_purchase/CHANGELOG.md | 4 +- .../in_app_purchase/example/lib/main.dart | 111 +++++++++--------- .../in_app_purchase/pubspec.yaml | 2 +- .../in_app_purchase_android/CHANGELOG.md | 4 +- .../example/lib/main.dart | 6 +- .../in_app_purchase_android/pubspec.yaml | 2 +- .../in_app_purchase_storekit/CHANGELOG.md | 4 +- .../example/lib/main.dart | 8 +- .../in_app_purchase_storekit/pubspec.yaml | 2 +- packages/ios_platform_images/CHANGELOG.md | 4 +- .../ios_platform_images/example/lib/main.dart | 7 +- .../example/test/widget_test.dart | 2 +- packages/ios_platform_images/pubspec.yaml | 2 +- packages/local_auth/local_auth/CHANGELOG.md | 5 + .../local_auth/example/lib/main.dart | 14 ++- .../example/lib/readme_excerpts.dart | 4 +- packages/local_auth/local_auth/pubspec.yaml | 2 +- .../local_auth_android/CHANGELOG.md | 4 +- .../local_auth_android/example/lib/main.dart | 14 ++- .../local_auth_android/pubspec.yaml | 2 +- .../local_auth/local_auth_ios/CHANGELOG.md | 4 +- .../local_auth_ios/example/lib/main.dart | 14 ++- .../local_auth/local_auth_ios/pubspec.yaml | 2 +- .../path_provider/path_provider/CHANGELOG.md | 4 +- .../path_provider/example/lib/main.dart | 44 +++---- .../path_provider/path_provider/pubspec.yaml | 2 +- .../path_provider_android/CHANGELOG.md | 5 + .../example/lib/main.dart | 16 +-- .../path_provider_android/pubspec.yaml | 2 +- .../path_provider_ios/CHANGELOG.md | 5 + .../path_provider_ios/example/lib/main.dart | 14 ++- .../path_provider_ios/pubspec.yaml | 2 +- .../path_provider_linux/CHANGELOG.md | 5 + .../path_provider_linux/example/lib/main.dart | 7 +- .../path_provider_linux/pubspec.yaml | 2 +- .../path_provider_macos/CHANGELOG.md | 5 + .../path_provider_macos/example/lib/main.dart | 6 +- .../path_provider_macos/pubspec.yaml | 2 +- .../path_provider_windows/CHANGELOG.md | 5 + .../example/lib/main.dart | 6 +- .../path_provider_windows/pubspec.yaml | 2 +- .../quick_actions/quick_actions/CHANGELOG.md | 4 +- .../quick_actions/example/lib/main.dart | 6 +- .../quick_actions/quick_actions/pubspec.yaml | 2 +- .../quick_actions_android/CHANGELOG.md | 5 + .../example/lib/main.dart | 6 +- .../quick_actions_android/pubspec.yaml | 2 +- .../quick_actions_ios/CHANGELOG.md | 4 + .../quick_actions_ios/example/lib/main.dart | 6 +- .../quick_actions_ios/pubspec.yaml | 2 +- .../shared_preferences/CHANGELOG.md | 4 +- .../shared_preferences/example/lib/main.dart | 4 +- .../shared_preferences/pubspec.yaml | 2 +- .../shared_preferences_android/CHANGELOG.md | 5 + .../example/lib/main.dart | 4 +- .../shared_preferences_android/pubspec.yaml | 2 +- .../shared_preferences_ios/CHANGELOG.md | 5 + .../example/lib/main.dart | 4 +- .../shared_preferences_ios/pubspec.yaml | 2 +- .../shared_preferences_linux/CHANGELOG.md | 4 +- .../example/lib/main.dart | 4 +- .../shared_preferences_linux/pubspec.yaml | 2 +- .../shared_preferences_macos/CHANGELOG.md | 4 +- .../example/lib/main.dart | 4 +- .../shared_preferences_macos/pubspec.yaml | 2 +- .../shared_preferences_web/CHANGELOG.md | 5 + .../example/lib/main.dart | 7 +- .../shared_preferences_web/pubspec.yaml | 2 +- .../shared_preferences_windows/CHANGELOG.md | 5 + .../example/lib/main.dart | 4 +- .../shared_preferences_windows/pubspec.yaml | 2 +- .../url_launcher/url_launcher/CHANGELOG.md | 4 +- .../url_launcher/example/lib/main.dart | 6 +- .../url_launcher/lib/src/link.dart | 2 +- .../url_launcher/url_launcher/pubspec.yaml | 2 +- .../url_launcher_android/CHANGELOG.md | 5 + .../example/lib/main.dart | 6 +- .../url_launcher_android/pubspec.yaml | 2 +- .../url_launcher_ios/CHANGELOG.md | 5 + .../url_launcher_ios/example/lib/main.dart | 6 +- .../url_launcher_ios/pubspec.yaml | 2 +- .../url_launcher_linux/CHANGELOG.md | 5 + .../url_launcher_linux/example/lib/main.dart | 6 +- .../url_launcher_linux/pubspec.yaml | 2 +- .../url_launcher_macos/CHANGELOG.md | 5 + .../url_launcher_macos/example/lib/main.dart | 6 +- .../url_launcher_macos/pubspec.yaml | 2 +- .../url_launcher_web/CHANGELOG.md | 5 + .../url_launcher_web/example/lib/main.dart | 7 +- .../url_launcher_web/lib/src/link.dart | 2 +- .../url_launcher_web/pubspec.yaml | 2 +- .../url_launcher_windows/CHANGELOG.md | 5 + .../example/lib/main.dart | 6 +- .../url_launcher_windows/pubspec.yaml | 2 +- .../video_player/video_player/CHANGELOG.md | 4 +- .../video_player/lib/video_player.dart | 11 +- .../video_player/video_player/pubspec.yaml | 2 +- .../video_player_android/CHANGELOG.md | 4 +- .../example/lib/mini_controller.dart | 10 +- .../video_player_android/pubspec.yaml | 2 +- .../video_player_avfoundation/CHANGELOG.md | 4 +- .../example/lib/mini_controller.dart | 10 +- .../video_player_avfoundation/pubspec.yaml | 2 +- .../video_player_web/CHANGELOG.md | 4 +- .../video_player_web/example/lib/main.dart | 7 +- .../video_player_web/pubspec.yaml | 2 +- .../webview_flutter/CHANGELOG.md | 6 +- .../webview_flutter_test.dart | 3 +- .../webview_flutter/example/lib/main.dart | 16 +-- .../webview_flutter/pubspec.yaml | 2 +- .../webview_flutter_android/CHANGELOG.md | 4 +- .../webview_flutter_test.dart | 3 +- .../example/lib/main.dart | 2 +- .../example/lib/web_view.dart | 2 +- .../lib/webview_android_widget.dart | 3 +- .../webview_flutter_android/pubspec.yaml | 2 +- .../webview_flutter_web/CHANGELOG.md | 4 +- .../example/lib/web_view.dart | 2 +- .../webview_flutter_web/pubspec.yaml | 2 +- .../webview_flutter_wkwebview/CHANGELOG.md | 4 +- .../webview_flutter_test.dart | 3 +- .../example/lib/main.dart | 2 +- .../example/lib/web_view.dart | 2 +- .../lib/src/web_kit_webview_widget.dart | 3 +- .../webview_flutter_wkwebview/pubspec.yaml | 2 +- script/tool/test/util.dart | 4 +- 228 files changed, 938 insertions(+), 525 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index f6177cd9939a..ba5e0a9c4ced 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -134,6 +134,7 @@ linter: - leading_newlines_in_multiline_strings - library_names - library_prefixes + - library_private_types_in_public_api # - lines_longer_than_80_chars # not required by flutter style - list_remove_unrelated_type # - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/sdk/issues/34181 @@ -197,7 +198,7 @@ linter: - recursive_getters # - sized_box_for_whitespace # not yet tested - slash_for_doc_comments - # - sort_child_properties_last # not yet tested + - sort_child_properties_last - sort_constructors_first - sort_unnamed_constructors_first - test_types_in_equals @@ -229,7 +230,7 @@ linter: - use_full_hex_values_for_flutter_colors # - use_function_type_syntax_for_parameters # not yet tested - use_is_even_rather_than_modulo - # - use_key_in_widget_constructors # not yet tested + - use_key_in_widget_constructors - use_late_for_private_fields_and_variables - use_raw_strings - use_rethrow_when_possible diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 4d7e9bbeb218..bf0ccf86a82e 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.9.4+22 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.9.4+21 diff --git a/packages/camera/camera/README.md b/packages/camera/camera/README.md index 97b16d20f48a..0bcaeaeb3b7c 100644 --- a/packages/camera/camera/README.md +++ b/packages/camera/camera/README.md @@ -89,18 +89,22 @@ Here is a small example flutter app displaying a full screen camera preview. import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; -late List cameras; +late List _cameras; Future main() async { WidgetsFlutterBinding.ensureInitialized(); - cameras = await availableCameras(); - runApp(CameraApp()); + _cameras = await availableCameras(); + runApp(const CameraApp()); } +/// CameraApp is the Main Application. class CameraApp extends StatefulWidget { + /// Default Constructor + const CameraApp({Key? key}) : super(key: key); + @override - _CameraAppState createState() => _CameraAppState(); + State createState() => _CameraAppState(); } class _CameraAppState extends State { @@ -109,7 +113,7 @@ class _CameraAppState extends State { @override void initState() { super.initState(); - controller = CameraController(cameras[0], ResolutionPreset.max); + controller = CameraController(_cameras[0], ResolutionPreset.max); controller.initialize().then((_) { if (!mounted) { return; diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index aabbe249313d..a645326f2803 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore_for_file: public_member_api_docs - import 'dart:async'; import 'dart:io'; @@ -13,9 +11,13 @@ import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:video_player/video_player.dart'; +/// Camera example home widget. class CameraExampleHome extends StatefulWidget { + /// Default Constructor + const CameraExampleHome({Key? key}) : super(key: key); + @override - _CameraExampleHomeState createState() { + State createState() { return _CameraExampleHomeState(); } } @@ -34,7 +36,7 @@ IconData getCameraLensIcon(CameraLensDirection direction) { } } -void logError(String code, String? message) { +void _logError(String code, String? message) { if (message != null) { print('Error: $code\nError Message: $message'); } else { @@ -134,12 +136,6 @@ class _CameraExampleHomeState extends State children: [ Expanded( child: Container( - child: Padding( - padding: const EdgeInsets.all(1.0), - child: Center( - child: _cameraPreviewWidget(), - ), - ), decoration: BoxDecoration( color: Colors.black, border: Border.all( @@ -150,6 +146,12 @@ class _CameraExampleHomeState extends State width: 3.0, ), ), + child: Padding( + padding: const EdgeInsets.all(1.0), + child: Center( + child: _cameraPreviewWidget(), + ), + ), ), ), _captureControlRowWidget(), @@ -233,6 +235,8 @@ class _CameraExampleHomeState extends State Container() else SizedBox( + width: 64.0, + height: 64.0, child: (localVideoController == null) ? ( // The captured image on the web contains a network-accessible URL @@ -243,6 +247,8 @@ class _CameraExampleHomeState extends State ? Image.network(imageFile!.path) : Image.file(File(imageFile!.path))) : Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.pink)), child: Center( child: AspectRatio( aspectRatio: @@ -251,11 +257,7 @@ class _CameraExampleHomeState extends State : 1.0, child: VideoPlayer(localVideoController)), ), - decoration: BoxDecoration( - border: Border.all(color: Colors.pink)), ), - width: 64.0, - height: 64.0, ), ], ), @@ -394,7 +396,6 @@ class _CameraExampleHomeState extends State mainAxisSize: MainAxisSize.max, children: [ TextButton( - child: const Text('AUTO'), style: styleAuto, onPressed: controller != null ? () => @@ -406,21 +407,22 @@ class _CameraExampleHomeState extends State showInSnackBar('Resetting exposure point'); } }, + child: const Text('AUTO'), ), TextButton( - child: const Text('LOCKED'), style: styleLocked, onPressed: controller != null ? () => onSetExposureModeButtonPressed(ExposureMode.locked) : null, + child: const Text('LOCKED'), ), TextButton( - child: const Text('RESET OFFSET'), style: styleLocked, onPressed: controller != null ? () => controller!.setExposureOffset(0.0) : null, + child: const Text('RESET OFFSET'), ), ], ), @@ -479,7 +481,6 @@ class _CameraExampleHomeState extends State mainAxisSize: MainAxisSize.max, children: [ TextButton( - child: const Text('AUTO'), style: styleAuto, onPressed: controller != null ? () => onSetFocusModeButtonPressed(FocusMode.auto) @@ -490,13 +491,14 @@ class _CameraExampleHomeState extends State } showInSnackBar('Resetting focus point'); }, + child: const Text('AUTO'), ), TextButton( - child: const Text('LOCKED'), style: styleLocked, onPressed: controller != null ? () => onSetFocusModeButtonPressed(FocusMode.locked) : null, + child: const Text('LOCKED'), ), ], ), @@ -582,13 +584,13 @@ class _CameraExampleHomeState extends State onNewCameraSelected(description); }; - if (cameras.isEmpty) { + if (_cameras.isEmpty) { _ambiguate(SchedulerBinding.instance)?.addPostFrameCallback((_) async { showInSnackBar('No camera found.'); }); return const Text('None'); } else { - for (final CameraDescription cameraDescription in cameras) { + for (final CameraDescription cameraDescription in _cameras) { toggles.add( SizedBox( width: 90.0, @@ -1014,31 +1016,35 @@ class _CameraExampleHomeState extends State } void _showCameraException(CameraException e) { - logError(e.code, e.description); + _logError(e.code, e.description); showInSnackBar('Error: ${e.code}\n${e.description}'); } } +/// CameraApp is the Main Application. class CameraApp extends StatelessWidget { + /// Default Constructor + const CameraApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { - return MaterialApp( + return const MaterialApp( home: CameraExampleHome(), ); } } -List cameras = []; +List _cameras = []; Future main() async { // Fetch the available cameras before initializing the app. try { WidgetsFlutterBinding.ensureInitialized(); - cameras = await availableCameras(); + _cameras = await availableCameras(); } on CameraException catch (e) { - logError(e.code, e.description); + _logError(e.code, e.description); } - runApp(CameraApp()); + runApp(const CameraApp()); } /// This allows a value of type T or T? to be treated as a value of type T?. diff --git a/packages/camera/camera/example/lib/readme_full_example.dart b/packages/camera/camera/example/lib/readme_full_example.dart index b25e637a0c95..a310fd9daeb0 100644 --- a/packages/camera/camera/example/lib/readme_full_example.dart +++ b/packages/camera/camera/example/lib/readme_full_example.dart @@ -2,24 +2,26 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore_for_file: public_member_api_docs - // #docregion FullAppExample import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; -late List cameras; +late List _cameras; Future main() async { WidgetsFlutterBinding.ensureInitialized(); - cameras = await availableCameras(); - runApp(CameraApp()); + _cameras = await availableCameras(); + runApp(const CameraApp()); } +/// CameraApp is the Main Application. class CameraApp extends StatefulWidget { + /// Default Constructor + const CameraApp({Key? key}) : super(key: key); + @override - _CameraAppState createState() => _CameraAppState(); + State createState() => _CameraAppState(); } class _CameraAppState extends State { @@ -28,7 +30,7 @@ class _CameraAppState extends State { @override void initState() { super.initState(); - controller = CameraController(cameras[0], ResolutionPreset.max); + controller = CameraController(_cameras[0], ResolutionPreset.max); controller.initialize().then((_) { if (!mounted) { return; diff --git a/packages/camera/camera/example/test/main_test.dart b/packages/camera/camera/example/test/main_test.dart index 9a5fcdf2d5ea..6e909efcfc62 100644 --- a/packages/camera/camera/example/test/main_test.dart +++ b/packages/camera/camera/example/test/main_test.dart @@ -9,7 +9,7 @@ import 'package:flutter_test/flutter_test.dart'; void main() { testWidgets('Test snackbar', (WidgetTester tester) async { WidgetsFlutterBinding.ensureInitialized(); - await tester.pumpWidget(CameraApp()); + await tester.pumpWidget(const CameraApp()); await tester.pumpAndSettle(); expect(find.byType(SnackBar), findsOneWidget); }); diff --git a/packages/camera/camera/lib/src/camera_preview.dart b/packages/camera/camera/lib/src/camera_preview.dart index a9b3f2143b49..94ffca649fa6 100644 --- a/packages/camera/camera/lib/src/camera_preview.dart +++ b/packages/camera/camera/lib/src/camera_preview.dart @@ -10,7 +10,8 @@ import 'package:flutter/services.dart'; /// A widget showing a live camera preview. class CameraPreview extends StatelessWidget { /// Creates a preview widget for the given camera controller. - const CameraPreview(this.controller, {this.child}); + const CameraPreview(this.controller, {Key? key, this.child}) + : super(key: key); /// The controller for the camera that the preview is shown for. final CameraController controller; diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index f62777044617..fde6663844c2 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+21 +version: 0.9.4+22 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index 852e4a03fd43..7a24e12e5029 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.2.1+5 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 0.2.1+4 * Migrates from `ui.hash*` to `Object.hash*`. diff --git a/packages/camera/camera_web/example/lib/main.dart b/packages/camera/camera_web/example/lib/main.dart index ab04ce2ca2c7..670891fa5009 100644 --- a/packages/camera/camera_web/example/lib/main.dart +++ b/packages/camera/camera_web/example/lib/main.dart @@ -4,10 +4,13 @@ import 'package:flutter/material.dart'; -void main() => runApp(MyApp()); +void main() => runApp(const MyApp()); /// App for testing class MyApp extends StatelessWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const Directionality( diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index 2d1a4508eb73..8bef974190b2 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_web description: A Flutter plugin for getting information about and controlling the camera on Web. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.2.1+4 +version: 0.2.1+5 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/camera/camera_windows/CHANGELOG.md b/packages/camera/camera_windows/CHANGELOG.md index b1383dc54993..0f3bf441b05a 100644 --- a/packages/camera/camera_windows/CHANGELOG.md +++ b/packages/camera/camera_windows/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.1.0+1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.1.0 diff --git a/packages/camera/camera_windows/example/lib/main.dart b/packages/camera/camera_windows/example/lib/main.dart index b73e00cac52b..5758b0f1e397 100644 --- a/packages/camera/camera_windows/example/lib/main.dart +++ b/packages/camera/camera_windows/example/lib/main.dart @@ -9,11 +9,14 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// Example app for Camera Windows plugin. class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override State createState() => _MyAppState(); } diff --git a/packages/camera/camera_windows/pubspec.yaml b/packages/camera/camera_windows/pubspec.yaml index 1081c3dfc01f..fe655b04e8c8 100644 --- a/packages/camera/camera_windows/pubspec.yaml +++ b/packages/camera/camera_windows/pubspec.yaml @@ -1,8 +1,8 @@ name: camera_windows description: A Flutter plugin for getting information about and controlling the camera on Windows. -version: 0.1.0 repository: https://github.com/flutter/plugins/tree/master/packages/camera/camera_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 +version: 0.1.0+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/espresso/CHANGELOG.md b/packages/espresso/CHANGELOG.md index eb1f267ca1d3..dad0a912e174 100644 --- a/packages/espresso/CHANGELOG.md +++ b/packages/espresso/CHANGELOG.md @@ -1,8 +1,13 @@ +## 0.2.0+2 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 0.2.0+1 * Adds OS version support information to README. * Updates `androidx.test.ext:junit` and `androidx.test.ext:truth` for - compatibilty with updated Flutter template. + compatibility with updated Flutter template. ## 0.2.0 diff --git a/packages/espresso/example/lib/main.dart b/packages/espresso/example/lib/main.dart index 14f94abb28c8..741cd9cf9fa2 100644 --- a/packages/espresso/example/lib/main.dart +++ b/packages/espresso/example/lib/main.dart @@ -4,10 +4,13 @@ import 'package:flutter/material.dart'; -void main() => runApp(MyApp()); +void main() => runApp(const MyApp()); /// Example app for Espresso plugin. class MyApp extends StatelessWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + // This widget is the root of your application. @override Widget build(BuildContext context) { @@ -45,7 +48,7 @@ class _MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State<_MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<_MyHomePage> { diff --git a/packages/espresso/pubspec.yaml b/packages/espresso/pubspec.yaml index 7737fc46d4b6..ac0199cf045f 100644 --- a/packages/espresso/pubspec.yaml +++ b/packages/espresso/pubspec.yaml @@ -3,7 +3,7 @@ description: Java classes for testing Flutter apps using Espresso. Allows driving Flutter widgets from a native Espresso test. repository: https://github.com/flutter/plugins/tree/main/packages/espresso issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+espresso%22 -version: 0.2.0+1 +version: 0.2.0+2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index c0821fed7446..17baf9f12469 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,7 +1,9 @@ -## NEXT +## 0.8.4+2 * Removes unnecessary imports. * Adds OS version support information to README. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.8.4+1 diff --git a/packages/file_selector/file_selector/example/lib/get_directory_page.dart b/packages/file_selector/file_selector/example/lib/get_directory_page.dart index b3ed9d0eeaca..506134d66bd8 100644 --- a/packages/file_selector/file_selector/example/lib/get_directory_page.dart +++ b/packages/file_selector/file_selector/example/lib/get_directory_page.dart @@ -7,6 +7,9 @@ import 'package:flutter/material.dart'; /// Screen that shows an example of getDirectoryPath class GetDirectoryPage extends StatelessWidget { + /// Default Constructor + const GetDirectoryPage({Key? key}) : super(key: key); + Future _getDirectoryPath(BuildContext context) async { const String confirmButtonText = 'Choose'; final String? directoryPath = await getDirectoryPath( @@ -50,7 +53,7 @@ class GetDirectoryPage extends StatelessWidget { /// Widget that displays a text file in a dialog class TextDisplay extends StatelessWidget { /// Default Constructor - const TextDisplay(this.directoryPath); + const TextDisplay(this.directoryPath, {Key? key}) : super(key: key); /// Directory path final String directoryPath; diff --git a/packages/file_selector/file_selector/example/lib/home_page.dart b/packages/file_selector/file_selector/example/lib/home_page.dart index c598cbdf2611..9a0733b56b93 100644 --- a/packages/file_selector/file_selector/example/lib/home_page.dart +++ b/packages/file_selector/file_selector/example/lib/home_page.dart @@ -6,6 +6,9 @@ import 'package:flutter/material.dart'; /// Home Page of the application class HomePage extends StatelessWidget { + /// Default Constructor + const HomePage({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { final ButtonStyle style = ElevatedButton.styleFrom( diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index 14ce3f593f33..34f5857ab0bc 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -11,11 +11,14 @@ import 'package:example/save_text_page.dart'; import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// MyApp is the Main Application class MyApp extends StatelessWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -24,13 +27,14 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: HomePage(), + home: const HomePage(), routes: { - '/open/image': (BuildContext context) => OpenImagePage(), - '/open/images': (BuildContext context) => OpenMultipleImagesPage(), - '/open/text': (BuildContext context) => OpenTextPage(), + '/open/image': (BuildContext context) => const OpenImagePage(), + '/open/images': (BuildContext context) => + const OpenMultipleImagesPage(), + '/open/text': (BuildContext context) => const OpenTextPage(), '/save/text': (BuildContext context) => SaveTextPage(), - '/directory': (BuildContext context) => GetDirectoryPage(), + '/directory': (BuildContext context) => const GetDirectoryPage(), }, ); } diff --git a/packages/file_selector/file_selector/example/lib/open_image_page.dart b/packages/file_selector/file_selector/example/lib/open_image_page.dart index 0abdba6eb72d..e520ffb402aa 100644 --- a/packages/file_selector/file_selector/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_image_page.dart @@ -10,6 +10,9 @@ import 'package:flutter/material.dart'; /// Screen that shows an example of openFiles class OpenImagePage extends StatelessWidget { + /// Default Constructor + const OpenImagePage({Key? key}) : super(key: key); + Future _openImageFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'images', @@ -59,7 +62,8 @@ class OpenImagePage extends StatelessWidget { /// Widget that displays a text file in a dialog class ImageDisplay extends StatelessWidget { /// Default Constructor - const ImageDisplay(this.fileName, this.filePath); + const ImageDisplay(this.fileName, this.filePath, {Key? key}) + : super(key: key); /// Image's name final String fileName; diff --git a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart index 9a1101214aaa..e2d21c7f04d1 100644 --- a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart @@ -10,6 +10,9 @@ import 'package:flutter/material.dart'; /// Screen that shows an example of openFiles class OpenMultipleImagesPage extends StatelessWidget { + /// Default Constructor + const OpenMultipleImagesPage({Key? key}) : super(key: key); + Future _openImageFile(BuildContext context) async { final XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', @@ -61,7 +64,7 @@ class OpenMultipleImagesPage extends StatelessWidget { /// Widget that displays a text file in a dialog class MultipleImagesDisplay extends StatelessWidget { /// Default Constructor - const MultipleImagesDisplay(this.files); + const MultipleImagesDisplay(this.files, {Key? key}) : super(key: key); /// The files containing the images final List files; diff --git a/packages/file_selector/file_selector/example/lib/open_text_page.dart b/packages/file_selector/file_selector/example/lib/open_text_page.dart index 652e8596cf81..be48a434282b 100644 --- a/packages/file_selector/file_selector/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_text_page.dart @@ -7,6 +7,9 @@ import 'package:flutter/material.dart'; /// Screen that shows an example of openFile class OpenTextPage extends StatelessWidget { + /// Default Constructor + const OpenTextPage({Key? key}) : super(key: key); + Future _openTextFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'text', @@ -55,7 +58,8 @@ class OpenTextPage extends StatelessWidget { /// Widget that displays a text file in a dialog class TextDisplay extends StatelessWidget { /// Default Constructor - const TextDisplay(this.fileName, this.fileContent); + const TextDisplay(this.fileName, this.fileContent, {Key? key}) + : super(key: key); /// File's name final String fileName; diff --git a/packages/file_selector/file_selector/example/lib/save_text_page.dart b/packages/file_selector/file_selector/example/lib/save_text_page.dart index 108ef89b0248..b0317844ec36 100644 --- a/packages/file_selector/file_selector/example/lib/save_text_page.dart +++ b/packages/file_selector/file_selector/example/lib/save_text_page.dart @@ -8,6 +8,9 @@ import 'package:flutter/material.dart'; /// Page for showing an example of saving with file_selector class SaveTextPage extends StatelessWidget { + /// Default Constructor + SaveTextPage({Key? key}) : super(key: key); + final TextEditingController _nameController = TextEditingController(); final TextEditingController _contentController = TextEditingController(); diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index c05900f650cf..7026f7f32287 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for opening and saving files, or selecting directories, using native file selection UI. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.4+1 +version: 0.8.4+2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_macos/CHANGELOG.md b/packages/file_selector/file_selector_macos/CHANGELOG.md index b46a174bd323..19724a513a9e 100644 --- a/packages/file_selector/file_selector_macos/CHANGELOG.md +++ b/packages/file_selector/file_selector_macos/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.8.2+1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.8.2 diff --git a/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart b/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart index 0e55df8ce622..a27ab2b40aba 100644 --- a/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart @@ -8,6 +8,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a directory using `getDirectoryPath`, /// then displays the selected directory in a dialog. class GetDirectoryPage extends StatelessWidget { + /// Default Constructor + const GetDirectoryPage({Key? key}) : super(key: key); + Future _getDirectoryPath(BuildContext context) async { const String confirmButtonText = 'Choose'; final String? directoryPath = @@ -52,7 +55,7 @@ class GetDirectoryPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class TextDisplay extends StatelessWidget { /// Creates a `TextDisplay`. - const TextDisplay(this.directoryPath); + const TextDisplay(this.directoryPath, {Key? key}) : super(key: key); /// The path selected in the dialog. final String directoryPath; diff --git a/packages/file_selector/file_selector_macos/example/lib/home_page.dart b/packages/file_selector/file_selector_macos/example/lib/home_page.dart index 958680be0e3b..4d6ca7e6e6e3 100644 --- a/packages/file_selector/file_selector_macos/example/lib/home_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/home_page.dart @@ -6,6 +6,9 @@ import 'package:flutter/material.dart'; /// Home Page of the application. class HomePage extends StatelessWidget { + /// Default Constructor + const HomePage({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { final ButtonStyle style = ElevatedButton.styleFrom( diff --git a/packages/file_selector/file_selector_macos/example/lib/main.dart b/packages/file_selector/file_selector_macos/example/lib/main.dart index a49ebac1aea5..cbe268e1c7ab 100644 --- a/packages/file_selector/file_selector_macos/example/lib/main.dart +++ b/packages/file_selector/file_selector_macos/example/lib/main.dart @@ -11,11 +11,14 @@ import 'package:example/save_text_page.dart'; import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// MyApp is the Main Application. class MyApp extends StatelessWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -24,13 +27,14 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: HomePage(), + home: const HomePage(), routes: { - '/open/image': (BuildContext context) => OpenImagePage(), - '/open/images': (BuildContext context) => OpenMultipleImagesPage(), - '/open/text': (BuildContext context) => OpenTextPage(), + '/open/image': (BuildContext context) => const OpenImagePage(), + '/open/images': (BuildContext context) => + const OpenMultipleImagesPage(), + '/open/text': (BuildContext context) => const OpenTextPage(), '/save/text': (BuildContext context) => SaveTextPage(), - '/directory': (BuildContext context) => GetDirectoryPage(), + '/directory': (BuildContext context) => const GetDirectoryPage(), }, ); } diff --git a/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart index aaf083603e72..1a05343b27ab 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart @@ -11,6 +11,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select an image file using /// `openFiles`, then displays the selected images in a gallery dialog. class OpenImagePage extends StatelessWidget { + /// Default Constructor + const OpenImagePage({Key? key}) : super(key: key); + Future _openImageFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'images', @@ -59,7 +62,8 @@ class OpenImagePage extends StatelessWidget { /// Widget that displays an image in a dialog. class ImageDisplay extends StatelessWidget { /// Default Constructor. - const ImageDisplay(this.fileName, this.filePath); + const ImageDisplay(this.fileName, this.filePath, {Key? key}) + : super(key: key); /// The name of the selected file. final String fileName; diff --git a/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart index a030b8b4b10b..9c3c8e369f5b 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart @@ -11,6 +11,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select multiple image files using /// `openFiles`, then displays the selected images in a gallery dialog. class OpenMultipleImagesPage extends StatelessWidget { + /// Default Constructor + const OpenMultipleImagesPage({Key? key}) : super(key: key); + Future _openImageFile(BuildContext context) async { final XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', @@ -63,7 +66,7 @@ class OpenMultipleImagesPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class MultipleImagesDisplay extends StatelessWidget { /// Default Constructor. - const MultipleImagesDisplay(this.files); + const MultipleImagesDisplay(this.files, {Key? key}) : super(key: key); /// The files containing the images. final List files; diff --git a/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart index fa281a0020d5..9adde400b5e1 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart @@ -8,6 +8,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a text file using `openFile`, then /// displays its contents in a dialog. class OpenTextPage extends StatelessWidget { + /// Default Constructor + const OpenTextPage({Key? key}) : super(key: key); + Future _openTextFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'text', @@ -56,7 +59,8 @@ class OpenTextPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class TextDisplay extends StatelessWidget { /// Default Constructor. - const TextDisplay(this.fileName, this.fileContent); + const TextDisplay(this.fileName, this.fileContent, {Key? key}) + : super(key: key); /// The name of the selected file. final String fileName; diff --git a/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart b/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart index 3989c62b7442..a44a387c019b 100644 --- a/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart @@ -9,6 +9,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a save location using `getSavePath`, /// then writes text to a file at that location. class SaveTextPage extends StatelessWidget { + /// Default Constructor + SaveTextPage({Key? key}) : super(key: key); + final TextEditingController _nameController = TextEditingController(); final TextEditingController _contentController = TextEditingController(); @@ -67,8 +70,8 @@ class SaveTextPage extends StatelessWidget { primary: Colors.blue, onPrimary: Colors.white, ), - child: const Text('Press to save a text file'), onPressed: _saveFile, + child: const Text('Press to save a text file'), ), ], ), diff --git a/packages/file_selector/file_selector_macos/pubspec.yaml b/packages/file_selector/file_selector_macos/pubspec.yaml index 071d261c4bf8..41077c1c04e6 100644 --- a/packages/file_selector/file_selector_macos/pubspec.yaml +++ b/packages/file_selector/file_selector_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_macos description: macOS implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.2 +version: 0.8.2+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_web/CHANGELOG.md b/packages/file_selector/file_selector_web/CHANGELOG.md index 5927239ef9e3..ce9d5590f9a9 100644 --- a/packages/file_selector/file_selector_web/CHANGELOG.md +++ b/packages/file_selector/file_selector_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.8.1+4 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 0.8.1+3 * Minor code cleanup for new analysis rules. diff --git a/packages/file_selector/file_selector_web/example/lib/main.dart b/packages/file_selector/file_selector_web/example/lib/main.dart index 341913a18490..87422953de6a 100644 --- a/packages/file_selector/file_selector_web/example/lib/main.dart +++ b/packages/file_selector/file_selector_web/example/lib/main.dart @@ -5,13 +5,16 @@ import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// App for testing class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/file_selector/file_selector_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml index 74d0412b440f..2e12b6d175a3 100644 --- a/packages/file_selector/file_selector_web/pubspec.yaml +++ b/packages/file_selector/file_selector_web/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_web description: Web platform implementation of file_selector repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.1+3 +version: 0.8.1+4 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_windows/CHANGELOG.md b/packages/file_selector/file_selector_windows/CHANGELOG.md index ae3cd13342b1..c242717c3267 100644 --- a/packages/file_selector/file_selector_windows/CHANGELOG.md +++ b/packages/file_selector/file_selector_windows/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.8.2+1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.8.2 diff --git a/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart b/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart index b282b9030d67..8fc1a9001465 100644 --- a/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart @@ -8,6 +8,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a directory using `getDirectoryPath`, /// then displays the selected directory in a dialog. class GetDirectoryPage extends StatelessWidget { + /// Default Constructor + const GetDirectoryPage({Key? key}) : super(key: key); + Future _getDirectoryPath(BuildContext context) async { const String confirmButtonText = 'Choose'; final String? directoryPath = @@ -52,7 +55,7 @@ class GetDirectoryPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class TextDisplay extends StatelessWidget { /// Creates a `TextDisplay`. - const TextDisplay(this.directoryPath); + const TextDisplay(this.directoryPath, {Key? key}) : super(key: key); /// The path selected in the dialog. final String directoryPath; diff --git a/packages/file_selector/file_selector_windows/example/lib/home_page.dart b/packages/file_selector/file_selector_windows/example/lib/home_page.dart index 958680be0e3b..4d6ca7e6e6e3 100644 --- a/packages/file_selector/file_selector_windows/example/lib/home_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/home_page.dart @@ -6,6 +6,9 @@ import 'package:flutter/material.dart'; /// Home Page of the application. class HomePage extends StatelessWidget { + /// Default Constructor + const HomePage({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { final ButtonStyle style = ElevatedButton.styleFrom( diff --git a/packages/file_selector/file_selector_windows/example/lib/main.dart b/packages/file_selector/file_selector_windows/example/lib/main.dart index a49ebac1aea5..cbe268e1c7ab 100644 --- a/packages/file_selector/file_selector_windows/example/lib/main.dart +++ b/packages/file_selector/file_selector_windows/example/lib/main.dart @@ -11,11 +11,14 @@ import 'package:example/save_text_page.dart'; import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// MyApp is the Main Application. class MyApp extends StatelessWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -24,13 +27,14 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: HomePage(), + home: const HomePage(), routes: { - '/open/image': (BuildContext context) => OpenImagePage(), - '/open/images': (BuildContext context) => OpenMultipleImagesPage(), - '/open/text': (BuildContext context) => OpenTextPage(), + '/open/image': (BuildContext context) => const OpenImagePage(), + '/open/images': (BuildContext context) => + const OpenMultipleImagesPage(), + '/open/text': (BuildContext context) => const OpenTextPage(), '/save/text': (BuildContext context) => SaveTextPage(), - '/directory': (BuildContext context) => GetDirectoryPage(), + '/directory': (BuildContext context) => const GetDirectoryPage(), }, ); } diff --git a/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart index aaf083603e72..1a05343b27ab 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart @@ -11,6 +11,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select an image file using /// `openFiles`, then displays the selected images in a gallery dialog. class OpenImagePage extends StatelessWidget { + /// Default Constructor + const OpenImagePage({Key? key}) : super(key: key); + Future _openImageFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'images', @@ -59,7 +62,8 @@ class OpenImagePage extends StatelessWidget { /// Widget that displays an image in a dialog. class ImageDisplay extends StatelessWidget { /// Default Constructor. - const ImageDisplay(this.fileName, this.filePath); + const ImageDisplay(this.fileName, this.filePath, {Key? key}) + : super(key: key); /// The name of the selected file. final String fileName; diff --git a/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart index a030b8b4b10b..9c3c8e369f5b 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart @@ -11,6 +11,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select multiple image files using /// `openFiles`, then displays the selected images in a gallery dialog. class OpenMultipleImagesPage extends StatelessWidget { + /// Default Constructor + const OpenMultipleImagesPage({Key? key}) : super(key: key); + Future _openImageFile(BuildContext context) async { final XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', @@ -63,7 +66,7 @@ class OpenMultipleImagesPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class MultipleImagesDisplay extends StatelessWidget { /// Default Constructor. - const MultipleImagesDisplay(this.files); + const MultipleImagesDisplay(this.files, {Key? key}) : super(key: key); /// The files containing the images. final List files; diff --git a/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart index fa281a0020d5..9adde400b5e1 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart @@ -8,6 +8,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a text file using `openFile`, then /// displays its contents in a dialog. class OpenTextPage extends StatelessWidget { + /// Default Constructor + const OpenTextPage({Key? key}) : super(key: key); + Future _openTextFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'text', @@ -56,7 +59,8 @@ class OpenTextPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class TextDisplay extends StatelessWidget { /// Default Constructor. - const TextDisplay(this.fileName, this.fileContent); + const TextDisplay(this.fileName, this.fileContent, {Key? key}) + : super(key: key); /// The name of the selected file. final String fileName; diff --git a/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart b/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart index b87a51c3877d..961e0fb2fbc3 100644 --- a/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart @@ -9,6 +9,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a save location using `getSavePath`, /// then writes text to a file at that location. class SaveTextPage extends StatelessWidget { + /// Default Constructor + SaveTextPage({Key? key}) : super(key: key); + final TextEditingController _nameController = TextEditingController(); final TextEditingController _contentController = TextEditingController(); @@ -67,8 +70,8 @@ class SaveTextPage extends StatelessWidget { primary: Colors.blue, onPrimary: Colors.white, ), - child: const Text('Press to save a text file'), onPressed: _saveFile, + child: const Text('Press to save a text file'), ), ], ), diff --git a/packages/file_selector/file_selector_windows/pubspec.yaml b/packages/file_selector/file_selector_windows/pubspec.yaml index 7b035e974293..152b63ef4a3f 100644 --- a/packages/file_selector/file_selector_windows/pubspec.yaml +++ b/packages/file_selector/file_selector_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_windows description: Windows implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.2 +version: 0.8.2+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/flutter_plugin_android_lifecycle/CHANGELOG.md b/packages/flutter_plugin_android_lifecycle/CHANGELOG.md index 8fdfc39f3bf9..a0a3f67bed46 100644 --- a/packages/flutter_plugin_android_lifecycle/CHANGELOG.md +++ b/packages/flutter_plugin_android_lifecycle/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.0.6 * Adds OS version support information to README. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.0.5 diff --git a/packages/flutter_plugin_android_lifecycle/example/integration_test/flutter_plugin_android_lifecycle_test.dart b/packages/flutter_plugin_android_lifecycle/example/integration_test/flutter_plugin_android_lifecycle_test.dart index 1d329a02f93b..1198c6f01806 100644 --- a/packages/flutter_plugin_android_lifecycle/example/integration_test/flutter_plugin_android_lifecycle_test.dart +++ b/packages/flutter_plugin_android_lifecycle/example/integration_test/flutter_plugin_android_lifecycle_test.dart @@ -10,6 +10,6 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('loads', (WidgetTester tester) async { - await tester.pumpWidget(MyApp()); + await tester.pumpWidget(const MyApp()); }); } diff --git a/packages/flutter_plugin_android_lifecycle/example/lib/main.dart b/packages/flutter_plugin_android_lifecycle/example/lib/main.dart index 3ef6794dfad2..c465b3b687f2 100644 --- a/packages/flutter_plugin_android_lifecycle/example/lib/main.dart +++ b/packages/flutter_plugin_android_lifecycle/example/lib/main.dart @@ -2,13 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore_for_file: public_member_api_docs - import 'package:flutter/material.dart'; -void main() => runApp(MyApp()); +void main() => runApp(const MyApp()); +/// MyApp is the Main Application. class MyApp extends StatelessWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( diff --git a/packages/flutter_plugin_android_lifecycle/pubspec.yaml b/packages/flutter_plugin_android_lifecycle/pubspec.yaml index b359cc55c351..c109d0936589 100644 --- a/packages/flutter_plugin_android_lifecycle/pubspec.yaml +++ b/packages/flutter_plugin_android_lifecycle/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_plugin_android_lifecycle description: Flutter plugin for accessing an Android Lifecycle within other plugins. repository: https://github.com/flutter/plugins/tree/main/packages/flutter_plugin_android_lifecycle issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_plugin_android_lifecycle%22 -version: 2.0.5 +version: 2.0.6 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index b0662b89d9cc..d1f2f926e2ab 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.1.5 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.1.4 @@ -223,7 +225,7 @@ GoogleMapController is now uniformly driven by implementing `DefaultLifecycleObs ## 0.5.26+1 -* Removes a errorneously added method from the GoogleMapController.h header file. +* Removes an erroneously added method from the GoogleMapController.h header file. ## 0.5.26 diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart index f8072eee7c85..09df2b98b146 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart @@ -10,8 +10,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class AnimateCameraPage extends GoogleMapExampleAppPage { - const AnimateCameraPage() - : super(const Icon(Icons.map), 'Camera control, animated'); + const AnimateCameraPage({Key? key}) + : super(const Icon(Icons.map), 'Camera control, animated', key: key); @override Widget build(BuildContext context) { @@ -20,7 +20,7 @@ class AnimateCameraPage extends GoogleMapExampleAppPage { } class AnimateCamera extends StatefulWidget { - const AnimateCamera(); + const AnimateCamera({Key? key}) : super(key: key); @override State createState() => AnimateCameraState(); } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart index b1b58fdc91bf..fd95cf864a7c 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart @@ -12,7 +12,8 @@ const CameraPosition _kInitialPosition = CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); class LiteModePage extends GoogleMapExampleAppPage { - const LiteModePage() : super(const Icon(Icons.map), 'Lite mode'); + const LiteModePage({Key? key}) + : super(const Icon(Icons.map), 'Lite mode', key: key); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart index f4d420a72f7c..8932705bc6d5 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore_for_file: public_member_api_docs - import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -43,7 +41,11 @@ final List _allPages = [ const TileOverlayPage(), ]; +/// MapsDemo is the Main Application. class MapsDemo extends StatelessWidget { + /// Default Constructor + const MapsDemo({Key? key}) : super(key: key); + void _pushPage(BuildContext context, GoogleMapExampleAppPage page) { Navigator.of(context).push(MaterialPageRoute( builder: (_) => Scaffold( @@ -72,5 +74,5 @@ void main() { if (defaultTargetPlatform == TargetPlatform.android) { AndroidGoogleMapsFlutter.useAndroidViewSurface = true; } - runApp(MaterialApp(home: MapsDemo())); + runApp(const MaterialApp(home: MapsDemo())); } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart index bbe2372dce9a..9c96f25d5fa7 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart @@ -12,7 +12,8 @@ const CameraPosition _kInitialPosition = CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); class MapClickPage extends GoogleMapExampleAppPage { - const MapClickPage() : super(const Icon(Icons.mouse), 'Map click'); + const MapClickPage({Key? key}) + : super(const Icon(Icons.mouse), 'Map click', key: key); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart index 8e4853c040ed..1179acd46ffa 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart @@ -12,7 +12,8 @@ const CameraPosition _kInitialPosition = CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); class MapCoordinatesPage extends GoogleMapExampleAppPage { - const MapCoordinatesPage() : super(const Icon(Icons.map), 'Map coordinates'); + const MapCoordinatesPage({Key? key}) + : super(const Icon(Icons.map), 'Map coordinates', key: key); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_ui.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_ui.dart index 48ef1f570e02..fbfeda56a968 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_ui.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_ui.dart @@ -16,7 +16,8 @@ final LatLngBounds sydneyBounds = LatLngBounds( ); class MapUiPage extends GoogleMapExampleAppPage { - const MapUiPage() : super(const Icon(Icons.map), 'User interface'); + const MapUiPage({Key? key}) + : super(const Icon(Icons.map), 'User interface', key: key); @override Widget build(BuildContext context) { @@ -25,7 +26,7 @@ class MapUiPage extends GoogleMapExampleAppPage { } class MapUiBody extends StatefulWidget { - const MapUiBody(); + const MapUiBody({Key? key}) : super(key: key); @override State createState() => MapUiBodyState(); diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart index 95ace9d7c482..58d266c95d1d 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart @@ -11,7 +11,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class MarkerIconsPage extends GoogleMapExampleAppPage { - const MarkerIconsPage() : super(const Icon(Icons.image), 'Marker icons'); + const MarkerIconsPage({Key? key}) + : super(const Icon(Icons.image), 'Marker icons', key: key); @override Widget build(BuildContext context) { @@ -20,7 +21,7 @@ class MarkerIconsPage extends GoogleMapExampleAppPage { } class MarkerIconsBody extends StatefulWidget { - const MarkerIconsBody(); + const MarkerIconsBody({Key? key}) : super(key: key); @override State createState() => MarkerIconsBodyState(); diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart index 33da90f32c1b..a6bae3009f0b 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart @@ -10,7 +10,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class MoveCameraPage extends GoogleMapExampleAppPage { - const MoveCameraPage() : super(const Icon(Icons.map), 'Camera control'); + const MoveCameraPage({Key? key}) + : super(const Icon(Icons.map), 'Camera control', key: key); @override Widget build(BuildContext context) { @@ -19,7 +20,7 @@ class MoveCameraPage extends GoogleMapExampleAppPage { } class MoveCamera extends StatefulWidget { - const MoveCamera(); + const MoveCamera({Key? key}) : super(key: key); @override State createState() => MoveCameraState(); } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart index 77091909e970..a4bfa88d559f 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart @@ -9,7 +9,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PaddingPage extends GoogleMapExampleAppPage { - const PaddingPage() : super(const Icon(Icons.map), 'Add padding to the map'); + const PaddingPage({Key? key}) + : super(const Icon(Icons.map), 'Add padding to the map', key: key); @override Widget build(BuildContext context) { @@ -18,7 +19,7 @@ class PaddingPage extends GoogleMapExampleAppPage { } class MarkerIconsBody extends StatefulWidget { - const MarkerIconsBody(); + const MarkerIconsBody({Key? key}) : super(key: key); @override State createState() => MarkerIconsBodyState(); diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/page.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/page.dart index fb6eb3260f6d..eb01ab07a6f3 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/page.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/page.dart @@ -7,7 +7,8 @@ import 'package:flutter/material.dart'; abstract class GoogleMapExampleAppPage extends StatelessWidget { - const GoogleMapExampleAppPage(this.leading, this.title); + const GoogleMapExampleAppPage(this.leading, this.title, {Key? key}) + : super(key: key); final Widget leading; final String title; diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart index c6f1509af69f..ef5033cfa1ee 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart @@ -10,8 +10,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PlaceCirclePage extends GoogleMapExampleAppPage { - const PlaceCirclePage() - : super(const Icon(Icons.linear_scale), 'Place circle'); + const PlaceCirclePage({Key? key}) + : super(const Icon(Icons.linear_scale), 'Place circle', key: key); @override Widget build(BuildContext context) { @@ -20,7 +20,7 @@ class PlaceCirclePage extends GoogleMapExampleAppPage { } class PlaceCircleBody extends StatefulWidget { - const PlaceCircleBody(); + const PlaceCircleBody({Key? key}) : super(key: key); @override State createState() => PlaceCircleBodyState(); @@ -170,42 +170,42 @@ class PlaceCircleBodyState extends State { Column( children: [ TextButton( - child: const Text('add'), onPressed: _add, + child: const Text('add'), ), TextButton( - child: const Text('remove'), onPressed: (selectedId == null) ? null : () => _remove(selectedId), + child: const Text('remove'), ), TextButton( - child: const Text('toggle visible'), onPressed: (selectedId == null) ? null : () => _toggleVisible(selectedId), + child: const Text('toggle visible'), ), ], ), Column( children: [ TextButton( - child: const Text('change stroke width'), onPressed: (selectedId == null) ? null : () => _changeStrokeWidth(selectedId), + child: const Text('change stroke width'), ), TextButton( - child: const Text('change stroke color'), onPressed: (selectedId == null) ? null : () => _changeStrokeColor(selectedId), + child: const Text('change stroke color'), ), TextButton( - child: const Text('change fill color'), onPressed: (selectedId == null) ? null : () => _changeFillColor(selectedId), + child: const Text('change fill color'), ), ], ) diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart index 4291cac6841e..1238d61547b8 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart @@ -15,7 +15,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PlaceMarkerPage extends GoogleMapExampleAppPage { - const PlaceMarkerPage() : super(const Icon(Icons.place), 'Place marker'); + const PlaceMarkerPage({Key? key}) + : super(const Icon(Icons.place), 'Place marker', key: key); @override Widget build(BuildContext context) { @@ -24,7 +25,7 @@ class PlaceMarkerPage extends GoogleMapExampleAppPage { } class PlaceMarkerBody extends StatefulWidget { - const PlaceMarkerBody(); + const PlaceMarkerBody({Key? key}) : super(key: key); @override State createState() => PlaceMarkerBodyState(); @@ -308,13 +309,13 @@ class PlaceMarkerBodyState extends State { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ TextButton( - child: const Text('Add'), onPressed: _add, + child: const Text('Add'), ), TextButton( - child: const Text('Remove'), onPressed: selectedId == null ? null : () => _remove(selectedId), + child: const Text('Remove'), ), ], ), @@ -322,62 +323,61 @@ class PlaceMarkerBodyState extends State { alignment: WrapAlignment.spaceEvenly, children: [ TextButton( - child: const Text('change info'), onPressed: selectedId == null ? null : () => _changeInfo(selectedId), + child: const Text('change info'), ), TextButton( - child: const Text('change info anchor'), onPressed: selectedId == null ? null : () => _changeInfoAnchor(selectedId), + child: const Text('change info anchor'), ), TextButton( - child: const Text('change alpha'), onPressed: selectedId == null ? null : () => _changeAlpha(selectedId), + child: const Text('change alpha'), ), TextButton( - child: const Text('change anchor'), onPressed: selectedId == null ? null : () => _changeAnchor(selectedId), + child: const Text('change anchor'), ), TextButton( - child: const Text('toggle draggable'), onPressed: selectedId == null ? null : () => _toggleDraggable(selectedId), + child: const Text('toggle draggable'), ), TextButton( - child: const Text('toggle flat'), onPressed: selectedId == null ? null : () => _toggleFlat(selectedId), + child: const Text('toggle flat'), ), TextButton( - child: const Text('change position'), onPressed: selectedId == null ? null : () => _changePosition(selectedId), + child: const Text('change position'), ), TextButton( - child: const Text('change rotation'), onPressed: selectedId == null ? null : () => _changeRotation(selectedId), + child: const Text('change rotation'), ), TextButton( - child: const Text('toggle visible'), onPressed: selectedId == null ? null : () => _toggleVisible(selectedId), + child: const Text('toggle visible'), ), TextButton( - child: const Text('change zIndex'), onPressed: selectedId == null ? null : () => _changeZIndex(selectedId), + child: const Text('change zIndex'), ), TextButton( - child: const Text('set marker icon'), onPressed: selectedId == null ? null : () { @@ -387,6 +387,7 @@ class PlaceMarkerBodyState extends State { }, ); }, + child: const Text('set marker icon'), ), ], ), diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart index 8d4fa3e07c36..f1932141b8ab 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart @@ -10,8 +10,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PlacePolygonPage extends GoogleMapExampleAppPage { - const PlacePolygonPage() - : super(const Icon(Icons.linear_scale), 'Place polygon'); + const PlacePolygonPage({Key? key}) + : super(const Icon(Icons.linear_scale), 'Place polygon', key: key); @override Widget build(BuildContext context) { @@ -20,7 +20,7 @@ class PlacePolygonPage extends GoogleMapExampleAppPage { } class PlacePolygonBody extends StatefulWidget { - const PlacePolygonBody(); + const PlacePolygonBody({Key? key}) : super(key: key); @override State createState() => PlacePolygonBodyState(); @@ -196,64 +196,64 @@ class PlacePolygonBodyState extends State { Column( children: [ TextButton( - child: const Text('add'), onPressed: _add, + child: const Text('add'), ), TextButton( - child: const Text('remove'), onPressed: (selectedId == null) ? null : () => _remove(selectedId), + child: const Text('remove'), ), TextButton( - child: const Text('toggle visible'), onPressed: (selectedId == null) ? null : () => _toggleVisible(selectedId), + child: const Text('toggle visible'), ), TextButton( - child: const Text('toggle geodesic'), onPressed: (selectedId == null) ? null : () => _toggleGeodesic(selectedId), + child: const Text('toggle geodesic'), ), ], ), Column( children: [ TextButton( - child: const Text('add holes'), onPressed: (selectedId == null) ? null : ((polygons[selectedId]!.holes.isNotEmpty) ? null : () => _addHoles(selectedId)), + child: const Text('add holes'), ), TextButton( - child: const Text('remove holes'), onPressed: (selectedId == null) ? null : ((polygons[selectedId]!.holes.isEmpty) ? null : () => _removeHoles(selectedId)), + child: const Text('remove holes'), ), TextButton( - child: const Text('change stroke width'), onPressed: (selectedId == null) ? null : () => _changeWidth(selectedId), + child: const Text('change stroke width'), ), TextButton( - child: const Text('change stroke color'), onPressed: (selectedId == null) ? null : () => _changeStrokeColor(selectedId), + child: const Text('change stroke color'), ), TextButton( - child: const Text('change fill color'), onPressed: (selectedId == null) ? null : () => _changeFillColor(selectedId), + child: const Text('change fill color'), ), ], ) diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart index 434920d293be..b3a637ce7d15 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart @@ -11,8 +11,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PlacePolylinePage extends GoogleMapExampleAppPage { - const PlacePolylinePage() - : super(const Icon(Icons.linear_scale), 'Place polyline'); + const PlacePolylinePage({Key? key}) + : super(const Icon(Icons.linear_scale), 'Place polyline', key: key); @override Widget build(BuildContext context) { @@ -21,7 +21,7 @@ class PlacePolylinePage extends GoogleMapExampleAppPage { } class PlacePolylineBody extends StatefulWidget { - const PlacePolylineBody(); + const PlacePolylineBody({Key? key}) : super(key: key); @override State createState() => PlacePolylineBodyState(); @@ -234,66 +234,66 @@ class PlacePolylineBodyState extends State { Column( children: [ TextButton( - child: const Text('add'), onPressed: _add, + child: const Text('add'), ), TextButton( - child: const Text('remove'), onPressed: (selectedId == null) ? null : () => _remove(selectedId), + child: const Text('remove'), ), TextButton( - child: const Text('toggle visible'), onPressed: (selectedId == null) ? null : () => _toggleVisible(selectedId), + child: const Text('toggle visible'), ), TextButton( - child: const Text('toggle geodesic'), onPressed: (selectedId == null) ? null : () => _toggleGeodesic(selectedId), + child: const Text('toggle geodesic'), ), ], ), Column( children: [ TextButton( - child: const Text('change width'), onPressed: (selectedId == null) ? null : () => _changeWidth(selectedId), + child: const Text('change width'), ), TextButton( - child: const Text('change color'), onPressed: (selectedId == null) ? null : () => _changeColor(selectedId), + child: const Text('change color'), ), TextButton( - child: const Text('change start cap [Android only]'), onPressed: isIOS || (selectedId == null) ? null : () => _changeStartCap(selectedId), + child: const Text('change start cap [Android only]'), ), TextButton( - child: const Text('change end cap [Android only]'), onPressed: isIOS || (selectedId == null) ? null : () => _changeEndCap(selectedId), + child: const Text('change end cap [Android only]'), ), TextButton( - child: const Text('change joint type [Android only]'), onPressed: isIOS || (selectedId == null) ? null : () => _changeJointType(selectedId), + child: const Text('change joint type [Android only]'), ), TextButton( - child: const Text('change pattern [Android only]'), onPressed: isIOS || (selectedId == null) ? null : () => _changePattern(selectedId), + child: const Text('change pattern [Android only]'), ), ], ) diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart index 04769315e685..ca9d3962ddd7 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart @@ -14,7 +14,8 @@ import 'page.dart'; const LatLng _center = LatLng(32.080664, 34.9563837); class ScrollingMapPage extends GoogleMapExampleAppPage { - const ScrollingMapPage() : super(const Icon(Icons.map), 'Scrolling map'); + const ScrollingMapPage({Key? key}) + : super(const Icon(Icons.map), 'Scrolling map', key: key); @override Widget build(BuildContext context) { @@ -23,7 +24,7 @@ class ScrollingMapPage extends GoogleMapExampleAppPage { } class ScrollingMapBody extends StatelessWidget { - const ScrollingMapBody(); + const ScrollingMapBody({Key? key}) : super(key: key); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart index 9afc28869490..849a9f469938 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart @@ -15,8 +15,9 @@ const CameraPosition _kInitialPosition = CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); class SnapshotPage extends GoogleMapExampleAppPage { - const SnapshotPage() - : super(const Icon(Icons.camera_alt), 'Take a snapshot of the map'); + const SnapshotPage({Key? key}) + : super(const Icon(Icons.camera_alt), 'Take a snapshot of the map', + key: key); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart index dc7f7d1d6f33..81dfc2815866 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart @@ -13,7 +13,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class TileOverlayPage extends GoogleMapExampleAppPage { - const TileOverlayPage() : super(const Icon(Icons.map), 'Tile overlay'); + const TileOverlayPage({Key? key}) + : super(const Icon(Icons.map), 'Tile overlay', key: key); @override Widget build(BuildContext context) { @@ -22,7 +23,7 @@ class TileOverlayPage extends GoogleMapExampleAppPage { } class TileOverlayBody extends StatefulWidget { - const TileOverlayBody(); + const TileOverlayBody({Key? key}) : super(key: key); @override State createState() => TileOverlayBodyState(); @@ -90,16 +91,16 @@ class TileOverlayBodyState extends State { ), ), TextButton( - child: const Text('Add tile overlay'), onPressed: _addTileOverlay, + child: const Text('Add tile overlay'), ), TextButton( - child: const Text('Remove tile overlay'), onPressed: _removeTileOverlay, + child: const Text('Remove tile overlay'), ), TextButton( - child: const Text('Clear tile cache'), onPressed: _clearTileCache, + child: const Text('Clear tile cache'), ), ], ); diff --git a/packages/google_maps_flutter/google_maps_flutter/lib/src/controller.dart b/packages/google_maps_flutter/google_maps_flutter/lib/src/controller.dart index 088589e4a2ce..dfc6e579d560 100644 --- a/packages/google_maps_flutter/google_maps_flutter/lib/src/controller.dart +++ b/packages/google_maps_flutter/google_maps_flutter/lib/src/controller.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: library_private_types_in_public_api + part of google_maps_flutter; /// Controller for a single GoogleMap instance running on the host platform. diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index c10f9c679a17..a294dd09981f 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter description: A Flutter plugin for integrating Google Maps in iOS and Android applications. repository: https://github.com/flutter/plugins/tree/main/packages/google_maps_flutter/google_maps_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 2.1.4 +version: 2.1.5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md index 48908b984b0f..f2fe971f4591 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.3.2+2 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.3.2+1 diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/lib/main.dart index 10415204570c..d1ba571b5bd0 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/lib/main.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/lib/main.dart @@ -11,7 +11,7 @@ void main() { /// App for testing class MyApp extends StatefulWidget { @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml index 2780175d29e2..271d87d21092 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter_web description: Web platform implementation of google_maps_flutter repository: https://github.com/flutter/plugins/tree/main/packages/google_maps_flutter/google_maps_flutter_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 0.3.2+1 +version: 0.3.2+2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index caab46de7c5b..5b47536cd2e2 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,3 +1,8 @@ +## 5.3.1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 5.3.0 * Moves Android and iOS implementations to federated packages. diff --git a/packages/google_sign_in/google_sign_in/example/lib/main.dart b/packages/google_sign_in/google_sign_in/example/lib/main.dart index 9840a1e0a9f6..4c27543f5b18 100644 --- a/packages/google_sign_in/google_sign_in/example/lib/main.dart +++ b/packages/google_sign_in/google_sign_in/example/lib/main.dart @@ -22,7 +22,7 @@ GoogleSignIn _googleSignIn = GoogleSignIn( void main() { runApp( - MaterialApp( + const MaterialApp( title: 'Google Sign In', home: SignInDemo(), ), @@ -30,6 +30,8 @@ void main() { } class SignInDemo extends StatefulWidget { + const SignInDemo({Key? key}) : super(key: key); + @override State createState() => SignInDemoState(); } @@ -125,8 +127,8 @@ class SignInDemoState extends State { const Text('Signed in successfully.'), Text(_contactText), ElevatedButton( - child: const Text('SIGN OUT'), onPressed: _handleSignOut, + child: const Text('SIGN OUT'), ), ElevatedButton( child: const Text('REFRESH'), @@ -140,8 +142,8 @@ class SignInDemoState extends State { children: [ const Text('You are not currently signed in.'), ElevatedButton( - child: const Text('SIGN IN'), onPressed: _handleSignIn, + child: const Text('SIGN IN'), ), ], ); diff --git a/packages/google_sign_in/google_sign_in/lib/widgets.dart b/packages/google_sign_in/google_sign_in/lib/widgets.dart index 61f89133fba4..f7ae5f9a6e5f 100644 --- a/packages/google_sign_in/google_sign_in/lib/widgets.dart +++ b/packages/google_sign_in/google_sign_in/lib/widgets.dart @@ -22,11 +22,13 @@ class GoogleUserCircleAvatar extends StatelessWidget { /// in place of a profile photo, or a default profile photo if the user's /// identity does not specify a `displayName`. const GoogleUserCircleAvatar({ + Key? key, required this.identity, this.placeholderPhotoUrl, this.foregroundColor, this.backgroundColor, - }) : assert(identity != null); + }) : assert(identity != null), + super(key: key); /// A regular expression that matches against the "size directive" path /// segment of Google profile image URLs. diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index 760706f2e7bc..e58b27af08b7 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Google Sign-In, a secure authentication system for signing in with a Google account on Android and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.3.0 +version: 5.3.1 environment: diff --git a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md index 3ffa6b5b7d6b..90069d05f442 100644 --- a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 5.2.7 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 5.2.6 * Switches to an internal method channel, rather than the default. diff --git a/packages/google_sign_in/google_sign_in_android/example/lib/main.dart b/packages/google_sign_in/google_sign_in_android/example/lib/main.dart index a750c330001d..526cf8b69ccf 100644 --- a/packages/google_sign_in/google_sign_in_android/example/lib/main.dart +++ b/packages/google_sign_in/google_sign_in_android/example/lib/main.dart @@ -14,7 +14,7 @@ import 'package:http/http.dart' as http; void main() { runApp( - MaterialApp( + const MaterialApp( title: 'Google Sign In', home: SignInDemo(), ), @@ -22,6 +22,8 @@ void main() { } class SignInDemo extends StatefulWidget { + const SignInDemo({Key? key}) : super(key: key); + @override State createState() => SignInDemoState(); } @@ -142,8 +144,8 @@ class SignInDemoState extends State { const Text('Signed in successfully.'), Text(_contactText), ElevatedButton( - child: const Text('SIGN OUT'), onPressed: _handleSignOut, + child: const Text('SIGN OUT'), ), ElevatedButton( child: const Text('REFRESH'), @@ -157,8 +159,8 @@ class SignInDemoState extends State { children: [ const Text('You are not currently signed in.'), ElevatedButton( - child: const Text('SIGN IN'), onPressed: _handleSignIn, + child: const Text('SIGN IN'), ), ], ); diff --git a/packages/google_sign_in/google_sign_in_android/pubspec.yaml b/packages/google_sign_in/google_sign_in_android/pubspec.yaml index fa3dc1489b26..d9b272320694 100644 --- a/packages/google_sign_in/google_sign_in_android/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_android/pubspec.yaml @@ -2,7 +2,7 @@ name: google_sign_in_android description: Android implementation of the google_sign_in plugin. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.2.6 +version: 5.2.7 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md b/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md index 3ffa6b5b7d6b..90069d05f442 100644 --- a/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md @@ -1,3 +1,8 @@ +## 5.2.7 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 5.2.6 * Switches to an internal method channel, rather than the default. diff --git a/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart b/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart index a750c330001d..526cf8b69ccf 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart +++ b/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart @@ -14,7 +14,7 @@ import 'package:http/http.dart' as http; void main() { runApp( - MaterialApp( + const MaterialApp( title: 'Google Sign In', home: SignInDemo(), ), @@ -22,6 +22,8 @@ void main() { } class SignInDemo extends StatefulWidget { + const SignInDemo({Key? key}) : super(key: key); + @override State createState() => SignInDemoState(); } @@ -142,8 +144,8 @@ class SignInDemoState extends State { const Text('Signed in successfully.'), Text(_contactText), ElevatedButton( - child: const Text('SIGN OUT'), onPressed: _handleSignOut, + child: const Text('SIGN OUT'), ), ElevatedButton( child: const Text('REFRESH'), @@ -157,8 +159,8 @@ class SignInDemoState extends State { children: [ const Text('You are not currently signed in.'), ElevatedButton( - child: const Text('SIGN IN'), onPressed: _handleSignIn, + child: const Text('SIGN IN'), ), ], ); diff --git a/packages/google_sign_in/google_sign_in_ios/pubspec.yaml b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml index e5ef3832ff0e..b6f541b22ce1 100644 --- a/packages/google_sign_in/google_sign_in_ios/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: google_sign_in_ios description: Android implementation of the google_sign_in plugin. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.2.6 +version: 5.2.7 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md index aab4acae0376..6e87bebf5d02 100644 --- a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.10.1+1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 0.10.1 * Updates minimum Flutter version to 2.8. diff --git a/packages/google_sign_in/google_sign_in_web/example/lib/main.dart b/packages/google_sign_in/google_sign_in_web/example/lib/main.dart index d381fb4af3ab..b23015c811e8 100644 --- a/packages/google_sign_in/google_sign_in_web/example/lib/main.dart +++ b/packages/google_sign_in/google_sign_in_web/example/lib/main.dart @@ -5,13 +5,16 @@ import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// App for testing class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/google_sign_in/google_sign_in_web/pubspec.yaml b/packages/google_sign_in/google_sign_in_web/pubspec.yaml index 5a09453b8e86..3dcd0e8eef29 100644 --- a/packages/google_sign_in/google_sign_in_web/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_web/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Google Sign-In, a secure authentication system for signing in with a Google account on Android, iOS and Web. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 0.10.1 +version: 0.10.1+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index f1bf54c5cd35..a384a5272e05 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.8.5+1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 0.8.5 * Moves Android and iOS implementations to federated packages. diff --git a/packages/image_picker/image_picker/example/lib/main.dart b/packages/image_picker/image_picker/example/lib/main.dart index f3ad2375b8f2..a6f0e83c3abb 100755 --- a/packages/image_picker/image_picker/example/lib/main.dart +++ b/packages/image_picker/image_picker/example/lib/main.dart @@ -13,10 +13,12 @@ import 'package:image_picker/image_picker.dart'; import 'package:video_player/video_player.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( @@ -32,7 +34,7 @@ class MyHomePage extends StatefulWidget { final String? title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -177,21 +179,22 @@ class _MyHomePageState extends State { } if (_imageFileList != null) { return Semantics( - child: ListView.builder( - key: UniqueKey(), - itemBuilder: (BuildContext context, int index) { - // Why network for web? - // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform - return Semantics( - label: 'image_picker_example_picked_image', - child: kIsWeb - ? Image.network(_imageFileList![index].path) - : Image.file(File(_imageFileList![index].path)), - ); - }, - itemCount: _imageFileList!.length, - ), - label: 'image_picker_example_picked_images'); + label: 'image_picker_example_picked_images', + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + // Why network for web? + // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform + return Semantics( + label: 'image_picker_example_picked_image', + child: kIsWeb + ? Image.network(_imageFileList![index].path) + : Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + ); } else if (_pickImageError != null) { return Text( 'Pick image error: $_pickImageError', @@ -417,7 +420,7 @@ typedef OnPickImageCallback = void Function( double? maxWidth, double? maxHeight, int? quality); class AspectRatioVideo extends StatefulWidget { - const AspectRatioVideo(this.controller); + const AspectRatioVideo(this.controller, {Key? key}) : super(key: key); final VideoPlayerController? controller; diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 77a50916283e..818486d8e145 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.5 +version: 0.8.5+1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/image_picker/image_picker_android/CHANGELOG.md b/packages/image_picker/image_picker_android/CHANGELOG.md index 3472ade28d5b..0514fc33d420 100644 --- a/packages/image_picker/image_picker_android/CHANGELOG.md +++ b/packages/image_picker/image_picker_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.8.4+12 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 0.8.4+11 * Splits from `image_picker` as a federated implementation. diff --git a/packages/image_picker/image_picker_android/example/lib/main.dart b/packages/image_picker/image_picker_android/example/lib/main.dart index 48eee35445da..d56aeb866195 100755 --- a/packages/image_picker/image_picker_android/example/lib/main.dart +++ b/packages/image_picker/image_picker_android/example/lib/main.dart @@ -13,10 +13,12 @@ import 'package:image_picker_platform_interface/image_picker_platform_interface. import 'package:video_player/video_player.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( @@ -32,7 +34,7 @@ class MyHomePage extends StatefulWidget { final String? title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -177,21 +179,22 @@ class _MyHomePageState extends State { } if (_imageFileList != null) { return Semantics( - child: ListView.builder( - key: UniqueKey(), - itemBuilder: (BuildContext context, int index) { - // Why network for web? - // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform - return Semantics( - label: 'image_picker_example_picked_image', - child: kIsWeb - ? Image.network(_imageFileList![index].path) - : Image.file(File(_imageFileList![index].path)), - ); - }, - itemCount: _imageFileList!.length, - ), - label: 'image_picker_example_picked_images'); + label: 'image_picker_example_picked_images', + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + // Why network for web? + // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform + return Semantics( + label: 'image_picker_example_picked_image', + child: kIsWeb + ? Image.network(_imageFileList![index].path) + : Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + ); } else if (_pickImageError != null) { return Text( 'Pick image error: $_pickImageError', @@ -417,7 +420,7 @@ typedef OnPickImageCallback = void Function( double? maxWidth, double? maxHeight, int? quality); class AspectRatioVideo extends StatefulWidget { - const AspectRatioVideo(this.controller); + const AspectRatioVideo(this.controller, {Key? key}) : super(key: key); final VideoPlayerController? controller; diff --git a/packages/image_picker/image_picker_android/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml index dbeef9bed193..90d136c2c89b 100755 --- a/packages/image_picker/image_picker_android/pubspec.yaml +++ b/packages/image_picker/image_picker_android/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_android description: Android implementation of the image_picker plugin. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.4+11 +version: 0.8.4+12 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/image_picker/image_picker_for_web/CHANGELOG.md b/packages/image_picker/image_picker_for_web/CHANGELOG.md index dcf353fe19b1..c33b3b9981de 100644 --- a/packages/image_picker/image_picker_for_web/CHANGELOG.md +++ b/packages/image_picker/image_picker_for_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.1.7 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.1.6 * Internal code cleanup for stricter analysis options. diff --git a/packages/image_picker/image_picker_for_web/example/lib/main.dart b/packages/image_picker/image_picker_for_web/example/lib/main.dart index 341913a18490..87422953de6a 100644 --- a/packages/image_picker/image_picker_for_web/example/lib/main.dart +++ b/packages/image_picker/image_picker_for_web/example/lib/main.dart @@ -5,13 +5,16 @@ import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// App for testing class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/image_picker/image_picker_for_web/pubspec.yaml b/packages/image_picker/image_picker_for_web/pubspec.yaml index deccd2b50a1f..b0c5deb0da7a 100644 --- a/packages/image_picker/image_picker_for_web/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_for_web description: Web platform implementation of image_picker repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_for_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 2.1.6 +version: 2.1.7 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index 31a0795a4e30..af391db02689 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.8.5+1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.8.5 diff --git a/packages/image_picker/image_picker_ios/example/lib/main.dart b/packages/image_picker/image_picker_ios/example/lib/main.dart index 48eee35445da..d56aeb866195 100755 --- a/packages/image_picker/image_picker_ios/example/lib/main.dart +++ b/packages/image_picker/image_picker_ios/example/lib/main.dart @@ -13,10 +13,12 @@ import 'package:image_picker_platform_interface/image_picker_platform_interface. import 'package:video_player/video_player.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( @@ -32,7 +34,7 @@ class MyHomePage extends StatefulWidget { final String? title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -177,21 +179,22 @@ class _MyHomePageState extends State { } if (_imageFileList != null) { return Semantics( - child: ListView.builder( - key: UniqueKey(), - itemBuilder: (BuildContext context, int index) { - // Why network for web? - // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform - return Semantics( - label: 'image_picker_example_picked_image', - child: kIsWeb - ? Image.network(_imageFileList![index].path) - : Image.file(File(_imageFileList![index].path)), - ); - }, - itemCount: _imageFileList!.length, - ), - label: 'image_picker_example_picked_images'); + label: 'image_picker_example_picked_images', + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + // Why network for web? + // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform + return Semantics( + label: 'image_picker_example_picked_image', + child: kIsWeb + ? Image.network(_imageFileList![index].path) + : Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + ); } else if (_pickImageError != null) { return Text( 'Pick image error: $_pickImageError', @@ -417,7 +420,7 @@ typedef OnPickImageCallback = void Function( double? maxWidth, double? maxHeight, int? quality); class AspectRatioVideo extends StatefulWidget { - const AspectRatioVideo(this.controller); + const AspectRatioVideo(this.controller, {Key? key}) : super(key: key); final VideoPlayerController? controller; diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index a9cd052be56a..76ca20614f18 100755 --- a/packages/image_picker/image_picker_ios/pubspec.yaml +++ b/packages/image_picker/image_picker_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_ios description: iOS implementation of the video_picker plugin. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.5 +version: 0.8.5+1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/image_picker/image_picker_windows/CHANGELOG.md b/packages/image_picker/image_picker_windows/CHANGELOG.md index d98656b849c8..e72ab244068f 100644 --- a/packages/image_picker/image_picker_windows/CHANGELOG.md +++ b/packages/image_picker/image_picker_windows/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.1.0+1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.1.0 diff --git a/packages/image_picker/image_picker_windows/example/lib/main.dart b/packages/image_picker/image_picker_windows/example/lib/main.dart index 577d6dadf2d9..b3ba3574522a 100644 --- a/packages/image_picker/image_picker_windows/example/lib/main.dart +++ b/packages/image_picker/image_picker_windows/example/lib/main.dart @@ -12,10 +12,12 @@ import 'package:image_picker_platform_interface/image_picker_platform_interface. import 'package:video_player/video_player.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( @@ -31,7 +33,7 @@ class MyHomePage extends StatefulWidget { final String? title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -176,17 +178,18 @@ class _MyHomePageState extends State { } if (_imageFileList != null) { return Semantics( - child: ListView.builder( - key: UniqueKey(), - itemBuilder: (BuildContext context, int index) { - return Semantics( - label: 'image_picker_example_picked_image', - child: Image.file(File(_imageFileList![index].path)), - ); - }, - itemCount: _imageFileList!.length, - ), - label: 'image_picker_example_picked_images'); + label: 'image_picker_example_picked_images', + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + return Semantics( + label: 'image_picker_example_picked_image', + child: Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + ); } else if (_pickImageError != null) { return Text( 'Pick image error: $_pickImageError', @@ -363,7 +366,7 @@ typedef OnPickImageCallback = void Function( double? maxWidth, double? maxHeight, int? quality); class AspectRatioVideo extends StatefulWidget { - const AspectRatioVideo(this.controller); + const AspectRatioVideo(this.controller, {Key? key}) : super(key: key); final VideoPlayerController? controller; diff --git a/packages/image_picker/image_picker_windows/pubspec.yaml b/packages/image_picker/image_picker_windows/pubspec.yaml index eec41f7bfa0d..afadf3e39148 100644 --- a/packages/image_picker/image_picker_windows/pubspec.yaml +++ b/packages/image_picker/image_picker_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_windows description: Windows platform implementation of image_picker repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.1.0 +version: 0.1.0+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md index 24ef9eaffd1d..8412c23ee8e8 100644 --- a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md @@ -1,7 +1,9 @@ -## NEXT +## 3.0.3 * Removes unnecessary imports. * Adds OS version support information to README. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 3.0.2 diff --git a/packages/in_app_purchase/in_app_purchase/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase/example/lib/main.dart index 651652b40c3a..34346a0bd339 100644 --- a/packages/in_app_purchase/in_app_purchase/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase/example/lib/main.dart @@ -33,7 +33,7 @@ const List _kProductIds = [ class _MyApp extends StatefulWidget { @override - _MyAppState createState() => _MyAppState(); + State<_MyApp> createState() => _MyAppState(); } class _MyAppState extends State<_MyApp> { @@ -247,60 +247,61 @@ class _MyAppState extends State<_MyApp> { (ProductDetails productDetails) { final PurchaseDetails? previousPurchase = purchases[productDetails.id]; return ListTile( - title: Text( - productDetails.title, - ), - subtitle: Text( - productDetails.description, - ), - trailing: previousPurchase != null - ? IconButton( - onPressed: () => confirmPriceChange(context), - icon: const Icon(Icons.upgrade)) - : TextButton( - child: Text(productDetails.price), - style: TextButton.styleFrom( - backgroundColor: Colors.green[800], - primary: Colors.white, - ), - onPressed: () { - late PurchaseParam purchaseParam; - - if (Platform.isAndroid) { - // NOTE: If you are making a subscription purchase/upgrade/downgrade, we recommend you to - // verify the latest status of you your subscription by using server side receipt validation - // and update the UI accordingly. The subscription purchase status shown - // inside the app may not be accurate. - final GooglePlayPurchaseDetails? oldSubscription = - _getOldSubscription(productDetails, purchases); - - purchaseParam = GooglePlayPurchaseParam( - productDetails: productDetails, - applicationUserName: null, - changeSubscriptionParam: (oldSubscription != null) - ? ChangeSubscriptionParam( - oldPurchaseDetails: oldSubscription, - prorationMode: ProrationMode - .immediateWithTimeProration, - ) - : null); - } else { - purchaseParam = PurchaseParam( + title: Text( + productDetails.title, + ), + subtitle: Text( + productDetails.description, + ), + trailing: previousPurchase != null + ? IconButton( + onPressed: () => confirmPriceChange(context), + icon: const Icon(Icons.upgrade)) + : TextButton( + style: TextButton.styleFrom( + backgroundColor: Colors.green[800], + primary: Colors.white, + ), + onPressed: () { + late PurchaseParam purchaseParam; + + if (Platform.isAndroid) { + // NOTE: If you are making a subscription purchase/upgrade/downgrade, we recommend you to + // verify the latest status of you your subscription by using server side receipt validation + // and update the UI accordingly. The subscription purchase status shown + // inside the app may not be accurate. + final GooglePlayPurchaseDetails? oldSubscription = + _getOldSubscription(productDetails, purchases); + + purchaseParam = GooglePlayPurchaseParam( productDetails: productDetails, applicationUserName: null, - ); - } - - if (productDetails.id == _kConsumableId) { - _inAppPurchase.buyConsumable( - purchaseParam: purchaseParam, - autoConsume: _kAutoConsume || Platform.isIOS); - } else { - _inAppPurchase.buyNonConsumable( - purchaseParam: purchaseParam); - } - }, - )); + changeSubscriptionParam: (oldSubscription != null) + ? ChangeSubscriptionParam( + oldPurchaseDetails: oldSubscription, + prorationMode: + ProrationMode.immediateWithTimeProration, + ) + : null); + } else { + purchaseParam = PurchaseParam( + productDetails: productDetails, + applicationUserName: null, + ); + } + + if (productDetails.id == _kConsumableId) { + _inAppPurchase.buyConsumable( + purchaseParam: purchaseParam, + autoConsume: _kAutoConsume || Platform.isIOS); + } else { + _inAppPurchase.buyNonConsumable( + purchaseParam: purchaseParam); + } + }, + child: Text(productDetails.price), + ), + ); }, )); @@ -340,9 +341,9 @@ class _MyAppState extends State<_MyApp> { const Divider(), GridView.count( crossAxisCount: 5, - children: tokens, shrinkWrap: true, padding: const EdgeInsets.all(16.0), + children: tokens, ) ])); } @@ -359,12 +360,12 @@ class _MyAppState extends State<_MyApp> { mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( - child: const Text('Restore purchases'), style: TextButton.styleFrom( backgroundColor: Theme.of(context).primaryColor, primary: Colors.white, ), onPressed: () => _inAppPurchase.restorePurchases(), + child: const Text('Restore purchases'), ), ], ), diff --git a/packages/in_app_purchase/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/pubspec.yaml index af8f5f3c2773..d2f875293876 100644 --- a/packages/in_app_purchase/in_app_purchase/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase description: A Flutter plugin for in-app purchases. Exposes APIs for making in-app purchases through the App Store and Google Play. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 3.0.2 +version: 3.0.3 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md index 2657d504ac91..4b9e58c08d06 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.2.2+4 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.2.2+3 diff --git a/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart index 939bc43bea63..1da943535f70 100644 --- a/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart @@ -37,7 +37,7 @@ const List _kProductIds = [ class _MyApp extends StatefulWidget { @override - _MyAppState createState() => _MyAppState(); + State<_MyApp> createState() => _MyAppState(); } class _MyAppState extends State<_MyApp> { @@ -264,7 +264,6 @@ class _MyAppState extends State<_MyApp> { }, icon: const Icon(Icons.upgrade)) : TextButton( - child: Text(productDetails.price), style: TextButton.styleFrom( backgroundColor: Colors.green[800], primary: Colors.white, @@ -297,6 +296,7 @@ class _MyAppState extends State<_MyApp> { purchaseParam: purchaseParam); } }, + child: Text(productDetails.price), )); }, )); @@ -337,9 +337,9 @@ class _MyAppState extends State<_MyApp> { const Divider(), GridView.count( crossAxisCount: 5, - children: tokens, shrinkWrap: true, padding: const EdgeInsets.all(16.0), + children: tokens, ) ])); } diff --git a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml index 62888e6dfb73..7de778177c31 100644 --- a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_android description: An implementation for the Android platform of the Flutter `in_app_purchase` plugin. This uses the Android BillingClient APIs. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.2.2+3 +version: 0.2.2+4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md index 403ee32be2ae..7342077ab176 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.3.0+6 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.3.0+5 diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart index 2ee2deb7fe35..f45a8c7f8741 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart @@ -37,7 +37,7 @@ const List _kProductIds = [ class _MyApp extends StatefulWidget { @override - _MyAppState createState() => _MyAppState(); + State<_MyApp> createState() => _MyAppState(); } class _MyAppState extends State<_MyApp> { @@ -259,7 +259,6 @@ class _MyAppState extends State<_MyApp> { }, icon: const Icon(Icons.upgrade)) : TextButton( - child: Text(productDetails.price), style: TextButton.styleFrom( backgroundColor: Colors.green[800], primary: Colors.white, @@ -278,6 +277,7 @@ class _MyAppState extends State<_MyApp> { purchaseParam: purchaseParam); } }, + child: Text(productDetails.price), )); }, )); @@ -318,9 +318,9 @@ class _MyAppState extends State<_MyApp> { const Divider(), GridView.count( crossAxisCount: 5, - children: tokens, shrinkWrap: true, padding: const EdgeInsets.all(16.0), + children: tokens, ) ])); } @@ -337,12 +337,12 @@ class _MyAppState extends State<_MyApp> { mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( - child: const Text('Restore purchases'), style: TextButton.styleFrom( backgroundColor: Theme.of(context).primaryColor, primary: Colors.white, ), onPressed: () => _iapStoreKitPlatform.restorePurchases(), + child: const Text('Restore purchases'), ), ], ), diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml index 9693c186119c..24b693c98c36 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_storekit description: An implementation for the iOS platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase_storekit issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.3.0+5 +version: 0.3.0+6 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/ios_platform_images/CHANGELOG.md b/packages/ios_platform_images/CHANGELOG.md index 0a3755afa7e7..cf2632feaac7 100644 --- a/packages/ios_platform_images/CHANGELOG.md +++ b/packages/ios_platform_images/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.2.0+6 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.2.0+5 diff --git a/packages/ios_platform_images/example/lib/main.dart b/packages/ios_platform_images/example/lib/main.dart index ecdfeb2cba01..929814ecce00 100644 --- a/packages/ios_platform_images/example/lib/main.dart +++ b/packages/ios_platform_images/example/lib/main.dart @@ -5,12 +5,15 @@ import 'package:flutter/material.dart'; import 'package:ios_platform_images/ios_platform_images.dart'; -void main() => runApp(MyApp()); +void main() => runApp(const MyApp()); /// Main widget for the example app. class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/ios_platform_images/example/test/widget_test.dart b/packages/ios_platform_images/example/test/widget_test.dart index 18e9e657ddb9..f3cd4c68b65b 100644 --- a/packages/ios_platform_images/example/test/widget_test.dart +++ b/packages/ios_platform_images/example/test/widget_test.dart @@ -12,7 +12,7 @@ import 'package:ios_platform_images_example/main.dart'; void main() { testWidgets('Verify loads image', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(MyApp()); + await tester.pumpWidget(const MyApp()); expect( find.byWidgetPredicate( diff --git a/packages/ios_platform_images/pubspec.yaml b/packages/ios_platform_images/pubspec.yaml index 7f80714e4c1c..41a177560299 100644 --- a/packages/ios_platform_images/pubspec.yaml +++ b/packages/ios_platform_images/pubspec.yaml @@ -2,7 +2,7 @@ name: ios_platform_images description: A plugin to share images between Flutter and iOS in add-to-app setups. repository: https://github.com/flutter/plugins/tree/main/packages/ios_platform_images issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+ios_platform_images%22 -version: 0.2.0+5 +version: 0.2.0+6 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index 570d8eef7b9b..8a2743e6140b 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.2 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.1 * Restores the ability to import `error_codes.dart`. diff --git a/packages/local_auth/local_auth/example/lib/main.dart b/packages/local_auth/local_auth/example/lib/main.dart index 92ad7cf4fb3f..cc687f562402 100644 --- a/packages/local_auth/local_auth/example/lib/main.dart +++ b/packages/local_auth/local_auth/example/lib/main.dart @@ -11,12 +11,14 @@ import 'package:flutter/services.dart'; import 'package:local_auth/local_auth.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { @@ -169,14 +171,14 @@ class _MyAppState extends State { const Divider(height: 100), Text('Can check biometrics: $_canCheckBiometrics\n'), ElevatedButton( - child: const Text('Check biometrics'), onPressed: _checkBiometrics, + child: const Text('Check biometrics'), ), const Divider(height: 100), Text('Available biometrics: $_availableBiometrics\n'), ElevatedButton( - child: const Text('Get available biometrics'), onPressed: _getAvailableBiometrics, + child: const Text('Get available biometrics'), ), const Divider(height: 100), Text('Current State: $_authorized\n'), @@ -195,6 +197,7 @@ class _MyAppState extends State { Column( children: [ ElevatedButton( + onPressed: _authenticate, child: Row( mainAxisSize: MainAxisSize.min, children: const [ @@ -202,9 +205,9 @@ class _MyAppState extends State { Icon(Icons.perm_device_information), ], ), - onPressed: _authenticate, ), ElevatedButton( + onPressed: _authenticateWithBiometrics, child: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -214,7 +217,6 @@ class _MyAppState extends State { const Icon(Icons.fingerprint), ], ), - onPressed: _authenticateWithBiometrics, ), ], ), diff --git a/packages/local_auth/local_auth/example/lib/readme_excerpts.dart b/packages/local_auth/local_auth/example/lib/readme_excerpts.dart index 25ddfe0fa62f..340aaef28f84 100644 --- a/packages/local_auth/local_auth/example/lib/readme_excerpts.dart +++ b/packages/local_auth/local_auth/example/lib/readme_excerpts.dart @@ -24,10 +24,12 @@ import 'package:local_auth_ios/local_auth_ios.dart'; // #enddocregion CustomMessages void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + @override State createState() => _MyAppState(); } diff --git a/packages/local_auth/local_auth/pubspec.yaml b/packages/local_auth/local_auth/pubspec.yaml index 087b84920bb9..119b8d778cbc 100644 --- a/packages/local_auth/local_auth/pubspec.yaml +++ b/packages/local_auth/local_auth/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Android and iOS devices to allow local authentication via fingerprint, touch ID, face ID, passcode, pin, or pattern. repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 2.0.1 +version: 2.0.2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index 6afcf1ffed07..9f9bd7b535a9 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 1.0.3 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 1.0.2 diff --git a/packages/local_auth/local_auth_android/example/lib/main.dart b/packages/local_auth/local_auth_android/example/lib/main.dart index 29b1d66440eb..016d955f0a3f 100644 --- a/packages/local_auth/local_auth_android/example/lib/main.dart +++ b/packages/local_auth/local_auth_android/example/lib/main.dart @@ -12,12 +12,14 @@ import 'package:local_auth_android/local_auth_android.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { @@ -174,14 +176,14 @@ class _MyAppState extends State { Text( 'Device supports biometrics: $_deviceSupportsBiometrics\n'), ElevatedButton( - child: const Text('Check biometrics'), onPressed: _checkBiometrics, + child: const Text('Check biometrics'), ), const Divider(height: 100), Text('Enrolled biometrics: $_enrolledBiometrics\n'), ElevatedButton( - child: const Text('Get enrolled biometrics'), onPressed: _getEnrolledBiometrics, + child: const Text('Get enrolled biometrics'), ), const Divider(height: 100), Text('Current State: $_authorized\n'), @@ -200,6 +202,7 @@ class _MyAppState extends State { Column( children: [ ElevatedButton( + onPressed: _authenticate, child: Row( mainAxisSize: MainAxisSize.min, children: const [ @@ -207,9 +210,9 @@ class _MyAppState extends State { Icon(Icons.perm_device_information), ], ), - onPressed: _authenticate, ), ElevatedButton( + onPressed: _authenticateWithBiometrics, child: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -219,7 +222,6 @@ class _MyAppState extends State { const Icon(Icons.fingerprint), ], ), - onPressed: _authenticateWithBiometrics, ), ], ), diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml index aad0b9b92e70..cdd4e8225504 100644 --- a/packages/local_auth/local_auth_android/pubspec.yaml +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_android description: Android implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.2 +version: 1.0.3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_ios/CHANGELOG.md b/packages/local_auth/local_auth_ios/CHANGELOG.md index ca6ac43eb52f..2237cbe216f0 100644 --- a/packages/local_auth/local_auth_ios/CHANGELOG.md +++ b/packages/local_auth/local_auth_ios/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 1.0.5 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 1.0.4 diff --git a/packages/local_auth/local_auth_ios/example/lib/main.dart b/packages/local_auth/local_auth_ios/example/lib/main.dart index a8bf23b78a52..479a96ba809c 100644 --- a/packages/local_auth/local_auth_ios/example/lib/main.dart +++ b/packages/local_auth/local_auth_ios/example/lib/main.dart @@ -12,12 +12,14 @@ import 'package:local_auth_ios/local_auth_ios.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { @@ -173,14 +175,14 @@ class _MyAppState extends State { const Divider(height: 100), Text('Device supports biometrics: $_canCheckBiometrics\n'), ElevatedButton( - child: const Text('Check biometrics'), onPressed: _checkBiometrics, + child: const Text('Check biometrics'), ), const Divider(height: 100), Text('Enrolled biometrics: $_enrolledBiometrics\n'), ElevatedButton( - child: const Text('Get enrolled biometrics'), onPressed: _getEnrolledBiometrics, + child: const Text('Get enrolled biometrics'), ), const Divider(height: 100), Text('Current State: $_authorized\n'), @@ -199,6 +201,7 @@ class _MyAppState extends State { Column( children: [ ElevatedButton( + onPressed: _authenticate, child: Row( mainAxisSize: MainAxisSize.min, children: const [ @@ -206,9 +209,9 @@ class _MyAppState extends State { Icon(Icons.perm_device_information), ], ), - onPressed: _authenticate, ), ElevatedButton( + onPressed: _authenticateWithBiometrics, child: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -218,7 +221,6 @@ class _MyAppState extends State { const Icon(Icons.fingerprint), ], ), - onPressed: _authenticateWithBiometrics, ), ], ), diff --git a/packages/local_auth/local_auth_ios/pubspec.yaml b/packages/local_auth/local_auth_ios/pubspec.yaml index 77ab74d383c8..dded42b673f4 100644 --- a/packages/local_auth/local_auth_ios/pubspec.yaml +++ b/packages/local_auth/local_auth_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_ios description: iOS implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.4 +version: 1.0.5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider/CHANGELOG.md b/packages/path_provider/path_provider/CHANGELOG.md index a26a8901d1d7..990021671801 100644 --- a/packages/path_provider/path_provider/CHANGELOG.md +++ b/packages/path_provider/path_provider/CHANGELOG.md @@ -1,7 +1,9 @@ -## NEXT +## 2.0.10 * Removes unnecessary imports. * Adds OS version support information to README. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.0.9 diff --git a/packages/path_provider/path_provider/example/lib/main.dart b/packages/path_provider/path_provider/example/lib/main.dart index 90c2ccb93154..cb9c2eb1798d 100644 --- a/packages/path_provider/path_provider/example/lib/main.dart +++ b/packages/path_provider/path_provider/example/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -31,7 +33,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -138,10 +140,10 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + onPressed: _requestTempDirectory, child: const Text( 'Get Temporary Directory', ), - onPressed: _requestTempDirectory, ), ), FutureBuilder( @@ -155,10 +157,10 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + onPressed: _requestAppDocumentsDirectory, child: const Text( 'Get Application Documents Directory', ), - onPressed: _requestAppDocumentsDirectory, ), ), FutureBuilder( @@ -172,10 +174,10 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + onPressed: _requestAppSupportDirectory, child: const Text( 'Get Application Support Directory', ), - onPressed: _requestAppSupportDirectory, ), ), FutureBuilder( @@ -189,13 +191,13 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + onPressed: + Platform.isAndroid ? null : _requestAppLibraryDirectory, child: Text( Platform.isAndroid ? 'Application Library Directory unavailable' : 'Get Application Library Directory', ), - onPressed: - Platform.isAndroid ? null : _requestAppLibraryDirectory, ), ), FutureBuilder( @@ -209,14 +211,14 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + onPressed: !Platform.isAndroid + ? null + : _requestExternalStorageDirectory, child: Text( !Platform.isAndroid ? 'External storage is unavailable' : 'Get External Storage Directory', ), - onPressed: !Platform.isAndroid - ? null - : _requestExternalStorageDirectory, ), ), FutureBuilder( @@ -230,11 +232,6 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: Text( - !Platform.isAndroid - ? 'External directories are unavailable' - : 'Get External Storage Directories', - ), onPressed: !Platform.isAndroid ? null : () { @@ -242,6 +239,11 @@ class _MyHomePageState extends State { StorageDirectory.music, ); }, + child: Text( + !Platform.isAndroid + ? 'External directories are unavailable' + : 'Get External Storage Directories', + ), ), ), FutureBuilder?>( @@ -255,14 +257,14 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + onPressed: !Platform.isAndroid + ? null + : _requestExternalCacheDirectories, child: Text( !Platform.isAndroid ? 'External directories are unavailable' : 'Get External Cache Directories', ), - onPressed: !Platform.isAndroid - ? null - : _requestExternalCacheDirectories, ), ), FutureBuilder?>( @@ -276,14 +278,14 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + onPressed: Platform.isAndroid || Platform.isIOS + ? null + : _requestDownloadsDirectory, child: Text( Platform.isAndroid || Platform.isIOS ? 'Downloads directory is unavailable' : 'Get Downloads Directory', ), - onPressed: Platform.isAndroid || Platform.isIOS - ? null - : _requestDownloadsDirectory, ), ), FutureBuilder( diff --git a/packages/path_provider/path_provider/pubspec.yaml b/packages/path_provider/path_provider/pubspec.yaml index 30ddfdcfb119..6ca8325843e4 100644 --- a/packages/path_provider/path_provider/pubspec.yaml +++ b/packages/path_provider/path_provider/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider description: Flutter plugin for getting commonly used locations on host platform file systems, such as the temp and app data directories. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.9 +version: 2.0.10 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider_android/CHANGELOG.md b/packages/path_provider/path_provider_android/CHANGELOG.md index 31f8c81f8a65..4b15e2605038 100644 --- a/packages/path_provider/path_provider_android/CHANGELOG.md +++ b/packages/path_provider/path_provider_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.14 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.13 * Fixes typing build warning. diff --git a/packages/path_provider/path_provider_android/example/lib/main.dart b/packages/path_provider/path_provider_android/example/lib/main.dart index 6e04f865bfcf..fc9424a33542 100644 --- a/packages/path_provider/path_provider_android/example/lib/main.dart +++ b/packages/path_provider/path_provider_android/example/lib/main.dart @@ -8,10 +8,12 @@ import 'package:flutter/material.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -29,7 +31,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -121,8 +123,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get Temporary Directory'), onPressed: _requestTempDirectory, + child: const Text('Get Temporary Directory'), ), ), FutureBuilder( @@ -130,8 +132,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get Application Documents Directory'), onPressed: _requestAppDocumentsDirectory, + child: const Text('Get Application Documents Directory'), ), ), FutureBuilder( @@ -139,8 +141,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get Application Support Directory'), onPressed: _requestAppSupportDirectory, + child: const Text('Get Application Support Directory'), ), ), FutureBuilder( @@ -148,8 +150,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get External Storage Directory'), onPressed: _requestExternalStorageDirectory, + child: const Text('Get External Storage Directory'), ), ), FutureBuilder( @@ -174,8 +176,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get External Cache Directories'), onPressed: _requestExternalCacheDirectories, + child: const Text('Get External Cache Directories'), ), ), ]), diff --git a/packages/path_provider/path_provider_android/pubspec.yaml b/packages/path_provider/path_provider_android/pubspec.yaml index 93ed9848f75b..f1dc92abdefc 100644 --- a/packages/path_provider/path_provider_android/pubspec.yaml +++ b/packages/path_provider/path_provider_android/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_android description: Android implementation of the path_provider plugin. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.13 +version: 2.0.14 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider_ios/CHANGELOG.md b/packages/path_provider/path_provider_ios/CHANGELOG.md index 543af778d2e2..1940f5c7888e 100644 --- a/packages/path_provider/path_provider_ios/CHANGELOG.md +++ b/packages/path_provider/path_provider_ios/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.9 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.8 * Switches to a package-internal implementation of the platform interface. diff --git a/packages/path_provider/path_provider_ios/example/lib/main.dart b/packages/path_provider/path_provider_ios/example/lib/main.dart index 8c8d5410f923..d7140b76a06b 100644 --- a/packages/path_provider/path_provider_ios/example/lib/main.dart +++ b/packages/path_provider/path_provider_ios/example/lib/main.dart @@ -8,10 +8,12 @@ import 'package:flutter/material.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -29,7 +31,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -90,8 +92,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get Temporary Directory'), onPressed: _requestTempDirectory, + child: const Text('Get Temporary Directory'), ), ), FutureBuilder( @@ -99,8 +101,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get Application Documents Directory'), onPressed: _requestAppDocumentsDirectory, + child: const Text('Get Application Documents Directory'), ), ), FutureBuilder( @@ -108,8 +110,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get Application Support Directory'), onPressed: _requestAppSupportDirectory, + child: const Text('Get Application Support Directory'), ), ), FutureBuilder( @@ -117,8 +119,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get Application Library Directory'), onPressed: _requestAppLibraryDirectory, + child: const Text('Get Application Library Directory'), ), ), FutureBuilder( diff --git a/packages/path_provider/path_provider_ios/pubspec.yaml b/packages/path_provider/path_provider_ios/pubspec.yaml index 282f8e4f0ebd..d6c7de108c97 100644 --- a/packages/path_provider/path_provider_ios/pubspec.yaml +++ b/packages/path_provider/path_provider_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_ios description: iOS implementation of the path_provider plugin. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.8 +version: 2.0.9 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider_linux/CHANGELOG.md b/packages/path_provider/path_provider_linux/CHANGELOG.md index 55235c3542f9..c9c4bb3cc906 100644 --- a/packages/path_provider/path_provider_linux/CHANGELOG.md +++ b/packages/path_provider/path_provider_linux/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.1.6 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.1.5 * Removes dependency on `meta`. diff --git a/packages/path_provider/path_provider_linux/example/lib/main.dart b/packages/path_provider/path_provider_linux/example/lib/main.dart index d365e6bdeab4..1c7c7e87397a 100644 --- a/packages/path_provider/path_provider_linux/example/lib/main.dart +++ b/packages/path_provider/path_provider_linux/example/lib/main.dart @@ -7,13 +7,16 @@ import 'package:flutter/services.dart'; import 'package:path_provider_linux/path_provider_linux.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// Sample app class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/path_provider/path_provider_linux/pubspec.yaml b/packages/path_provider/path_provider_linux/pubspec.yaml index 91304fc0b268..16438a3870d1 100644 --- a/packages/path_provider/path_provider_linux/pubspec.yaml +++ b/packages/path_provider/path_provider_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_linux description: Linux implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.1.5 +version: 2.1.6 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/path_provider/path_provider_macos/CHANGELOG.md b/packages/path_provider/path_provider_macos/CHANGELOG.md index 047792f8bcc4..c59ba971d461 100644 --- a/packages/path_provider/path_provider_macos/CHANGELOG.md +++ b/packages/path_provider/path_provider_macos/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.6 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.5 * Removes dependency on `meta`. diff --git a/packages/path_provider/path_provider_macos/example/lib/main.dart b/packages/path_provider/path_provider_macos/example/lib/main.dart index 67a0eb32eeda..13a6fada5fef 100644 --- a/packages/path_provider/path_provider_macos/example/lib/main.dart +++ b/packages/path_provider/path_provider_macos/example/lib/main.dart @@ -8,13 +8,15 @@ import 'package:flutter/material.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// Sample app class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/path_provider/path_provider_macos/pubspec.yaml b/packages/path_provider/path_provider_macos/pubspec.yaml index 2451b6dedf80..444165b86c3f 100644 --- a/packages/path_provider/path_provider_macos/pubspec.yaml +++ b/packages/path_provider/path_provider_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_macos description: macOS implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.5 +version: 2.0.6 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/path_provider/path_provider_windows/CHANGELOG.md b/packages/path_provider/path_provider_windows/CHANGELOG.md index cd33e85a851d..014b6b36da2b 100644 --- a/packages/path_provider/path_provider_windows/CHANGELOG.md +++ b/packages/path_provider/path_provider_windows/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.6 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.5 * Removes dependency on `meta`. diff --git a/packages/path_provider/path_provider_windows/example/lib/main.dart b/packages/path_provider/path_provider_windows/example/lib/main.dart index 509292bf7405..4c63d245a16a 100644 --- a/packages/path_provider/path_provider_windows/example/lib/main.dart +++ b/packages/path_provider/path_provider_windows/example/lib/main.dart @@ -8,13 +8,15 @@ import 'package:flutter/material.dart'; import 'package:path_provider_windows/path_provider_windows.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// Sample app class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/path_provider/path_provider_windows/pubspec.yaml b/packages/path_provider/path_provider_windows/pubspec.yaml index 873fa0a6861b..49afdd6293e7 100644 --- a/packages/path_provider/path_provider_windows/pubspec.yaml +++ b/packages/path_provider/path_provider_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_windows description: Windows implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.5 +version: 2.0.6 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/quick_actions/quick_actions/CHANGELOG.md b/packages/quick_actions/quick_actions/CHANGELOG.md index c30d7052320e..73540a863364 100644 --- a/packages/quick_actions/quick_actions/CHANGELOG.md +++ b/packages/quick_actions/quick_actions/CHANGELOG.md @@ -1,8 +1,10 @@ -## NEXT +## 0.6.0+11 * Removes unnecessary imports. * Updates minimum Flutter version to 2.8. * Adds OS version support information to README. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.6.0+10 diff --git a/packages/quick_actions/quick_actions/example/lib/main.dart b/packages/quick_actions/quick_actions/example/lib/main.dart index 1ce6f51d71de..cafbf0c351d9 100644 --- a/packages/quick_actions/quick_actions/example/lib/main.dart +++ b/packages/quick_actions/quick_actions/example/lib/main.dart @@ -8,10 +8,12 @@ import 'package:flutter/material.dart'; import 'package:quick_actions/quick_actions.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -28,7 +30,7 @@ class MyHomePage extends StatefulWidget { const MyHomePage({Key? key}) : super(key: key); @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/quick_actions/quick_actions/pubspec.yaml b/packages/quick_actions/quick_actions/pubspec.yaml index 8ef2d3ab4e02..37e8dbe5f3e3 100644 --- a/packages/quick_actions/quick_actions/pubspec.yaml +++ b/packages/quick_actions/quick_actions/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for creating shortcuts on home screen, also known as Quick Actions on iOS and App Shortcuts on Android. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+quick_actions%22 -version: 0.6.0+10 +version: 0.6.0+11 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/quick_actions/quick_actions_android/CHANGELOG.md b/packages/quick_actions/quick_actions_android/CHANGELOG.md index 98e8cf5e333b..56accc9d044c 100644 --- a/packages/quick_actions/quick_actions_android/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.6.0+10 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 0.6.0+9 * Switches to a package-internal implementation of the platform interface. \ No newline at end of file diff --git a/packages/quick_actions/quick_actions_android/example/lib/main.dart b/packages/quick_actions/quick_actions_android/example/lib/main.dart index 06f141073b33..d8b7832bf9dc 100644 --- a/packages/quick_actions/quick_actions_android/example/lib/main.dart +++ b/packages/quick_actions/quick_actions_android/example/lib/main.dart @@ -8,10 +8,12 @@ import 'package:flutter/material.dart'; import 'package:quick_actions_android/quick_actions_android.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -28,7 +30,7 @@ class MyHomePage extends StatefulWidget { const MyHomePage({Key? key}) : super(key: key); @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/quick_actions/quick_actions_android/pubspec.yaml b/packages/quick_actions/quick_actions_android/pubspec.yaml index cf9971dca945..4ddbc79ee5e9 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/plugins/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: 0.6.0+9 +version: 0.6.0+10 environment: sdk: ">=2.15.0 <3.0.0" diff --git a/packages/quick_actions/quick_actions_ios/CHANGELOG.md b/packages/quick_actions/quick_actions_ios/CHANGELOG.md index d48afbd8d13a..56accc9d044c 100644 --- a/packages/quick_actions/quick_actions_ios/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.0+10 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.6.0+9 diff --git a/packages/quick_actions/quick_actions_ios/example/lib/main.dart b/packages/quick_actions/quick_actions_ios/example/lib/main.dart index 5173d952d623..008917b724e0 100644 --- a/packages/quick_actions/quick_actions_ios/example/lib/main.dart +++ b/packages/quick_actions/quick_actions_ios/example/lib/main.dart @@ -8,10 +8,12 @@ import 'package:flutter/material.dart'; import 'package:quick_actions_ios/quick_actions_ios.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -28,7 +30,7 @@ class MyHomePage extends StatefulWidget { const MyHomePage({Key? key}) : super(key: key); @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/quick_actions/quick_actions_ios/pubspec.yaml b/packages/quick_actions/quick_actions_ios/pubspec.yaml index 26644ba12fde..47748b9789ad 100644 --- a/packages/quick_actions/quick_actions_ios/pubspec.yaml +++ b/packages/quick_actions/quick_actions_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: quick_actions_ios description: An implementation for the iOS platform of the Flutter `quick_actions` plugin. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.6.0+9 +version: 0.6.0+10 environment: sdk: ">=2.15.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences/CHANGELOG.md b/packages/shared_preferences/shared_preferences/CHANGELOG.md index 84566e26e2c0..22c39aad98fd 100644 --- a/packages/shared_preferences/shared_preferences/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.0.14 * Adds OS version support information to README. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.0.13 diff --git a/packages/shared_preferences/shared_preferences/example/lib/main.dart b/packages/shared_preferences/shared_preferences/example/lib/main.dart index 43f3c78ad920..a2e72b446925 100644 --- a/packages/shared_preferences/shared_preferences/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences/example/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences/pubspec.yaml b/packages/shared_preferences/shared_preferences/pubspec.yaml index 39b48ef51ed2..4218095c0efe 100644 --- a/packages/shared_preferences/shared_preferences/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for reading and writing simple key-value pairs. Wraps NSUserDefaults on iOS and SharedPreferences on Android. repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.13 +version: 2.0.14 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md index 5321e869c497..51e99ec6d3d5 100644 --- a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.12 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.11 * Switches to an in-package method channel implementation. diff --git a/packages/shared_preferences/shared_preferences_android/example/lib/main.dart b/packages/shared_preferences/shared_preferences_android/example/lib/main.dart index 06ee9434ba84..bb513b09f6d5 100644 --- a/packages/shared_preferences/shared_preferences_android/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_android/example/lib/main.dart @@ -8,10 +8,12 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_android/pubspec.yaml b/packages/shared_preferences/shared_preferences_android/pubspec.yaml index 7eb180f3ab48..2d8cc88d3703 100644 --- a/packages/shared_preferences/shared_preferences_android/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_android/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_android description: Android implementation of the shared_preferences plugin repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.11 +version: 2.0.12 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md b/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md index a5cc1d34e034..29ade8d496a1 100644 --- a/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.1.1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.1.0 * Upgrades to using Pigeon. diff --git a/packages/shared_preferences/shared_preferences_ios/example/lib/main.dart b/packages/shared_preferences/shared_preferences_ios/example/lib/main.dart index 06ee9434ba84..bb513b09f6d5 100644 --- a/packages/shared_preferences/shared_preferences_ios/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_ios/example/lib/main.dart @@ -8,10 +8,12 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_ios/pubspec.yaml b/packages/shared_preferences/shared_preferences_ios/pubspec.yaml index 33bf5baffd18..a8bde2e9f87f 100644 --- a/packages/shared_preferences/shared_preferences_ios/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_ios description: iOS implementation of the shared_preferences plugin repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.1.0 +version: 2.1.1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md index 34dd631746bb..f0cb8322f385 100644 --- a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.1.1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.1.0 diff --git a/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart b/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart index 4b71c7ea3beb..d51be33baeed 100644 --- a/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_linux/shared_preferences_linux.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml index 8ab692a613e2..8f3ce1723bc9 100644 --- a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_linux description: Linux implementation of the shared_preferences plugin repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.1.0 +version: 2.1.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md b/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md index 0f194de44224..8ba116a74fe0 100644 --- a/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.0.4 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.0.3 diff --git a/packages/shared_preferences/shared_preferences_macos/example/lib/main.dart b/packages/shared_preferences/shared_preferences_macos/example/lib/main.dart index 349e9c45405a..e6bbe5931471 100644 --- a/packages/shared_preferences/shared_preferences_macos/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_macos/example/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_macos/pubspec.yaml b/packages/shared_preferences/shared_preferences_macos/pubspec.yaml index 0873696e6d4f..615d0b05ba99 100644 --- a/packages/shared_preferences/shared_preferences_macos/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_macos description: macOS implementation of the shared_preferences plugin. repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.3 +version: 2.0.4 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_web/CHANGELOG.md b/packages/shared_preferences/shared_preferences_web/CHANGELOG.md index 2a6ffa20e37b..9ea249034105 100644 --- a/packages/shared_preferences/shared_preferences_web/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.4 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.3 * Fixes newly enabled analyzer options. diff --git a/packages/shared_preferences/shared_preferences_web/example/lib/main.dart b/packages/shared_preferences/shared_preferences_web/example/lib/main.dart index 341913a18490..87422953de6a 100644 --- a/packages/shared_preferences/shared_preferences_web/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_web/example/lib/main.dart @@ -5,13 +5,16 @@ import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// App for testing class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/shared_preferences/shared_preferences_web/pubspec.yaml b/packages/shared_preferences/shared_preferences_web/pubspec.yaml index 232c87c426fc..9ff76d27714c 100644 --- a/packages/shared_preferences/shared_preferences_web/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_web/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_web description: Web platform implementation of shared_preferences repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.3 +version: 2.0.4 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md b/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md index 6c96681ce5b7..f79f9e3d5d39 100644 --- a/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.1.1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.1.0 * Deprecated `SharedPreferencesWindows.instance` in favor of `SharedPreferencesStorePlatform.instance`. diff --git a/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart b/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart index 40a9159cee70..74d5e4c68772 100644 --- a/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_windows/shared_preferences_windows.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_windows/pubspec.yaml b/packages/shared_preferences/shared_preferences_windows/pubspec.yaml index 6dcb5997f131..99326cb24f18 100644 --- a/packages/shared_preferences/shared_preferences_windows/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_windows description: Windows implementation of shared_preferences repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.1.0 +version: 2.1.1 environment: sdk: '>=2.12.0 <3.0.0' diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index b25956fd5919..493412c3e006 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 6.1.1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 6.1.0 diff --git a/packages/url_launcher/url_launcher/example/lib/main.dart b/packages/url_launcher/url_launcher/example/lib/main.dart index 898e80661296..a538940f1a68 100644 --- a/packages/url_launcher/url_launcher/example/lib/main.dart +++ b/packages/url_launcher/url_launcher/example/lib/main.dart @@ -11,10 +11,12 @@ import 'package:url_launcher/link.dart'; import 'package:url_launcher/url_launcher.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -32,7 +34,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher/lib/src/link.dart b/packages/url_launcher/url_launcher/lib/src/link.dart index 76cb97748003..8c0c18e820e3 100644 --- a/packages/url_launcher/url_launcher/lib/src/link.dart +++ b/packages/url_launcher/url_launcher/lib/src/link.dart @@ -86,7 +86,7 @@ class Link extends StatelessWidget implements LinkInfo { /// event channel messages to instruct the framework to push the route name. class DefaultLinkDelegate extends StatelessWidget { /// Creates a delegate for the given [link]. - const DefaultLinkDelegate(this.link); + const DefaultLinkDelegate(this.link, {Key? key}) : super(key: key); /// Given a [link], creates an instance of [DefaultLinkDelegate]. /// diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index 6803d71032cb..c14b62a1e70a 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.1.0 +version: 6.1.1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_android/CHANGELOG.md b/packages/url_launcher/url_launcher_android/CHANGELOG.md index 69b96156d849..887178c479e4 100644 --- a/packages/url_launcher/url_launcher_android/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 6.0.17 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 6.0.16 * Adds fallback querying for `canLaunch` with web URLs, to avoid false negatives diff --git a/packages/url_launcher/url_launcher_android/example/lib/main.dart b/packages/url_launcher/url_launcher_android/example/lib/main.dart index 7abc73430e8b..672ae4a27665 100644 --- a/packages/url_launcher/url_launcher_android/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_android/example/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -31,7 +33,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_android/pubspec.yaml b/packages/url_launcher/url_launcher_android/pubspec.yaml index 3230dfeffd2e..3c80170f1422 100644 --- a/packages/url_launcher/url_launcher_android/pubspec.yaml +++ b/packages/url_launcher/url_launcher_android/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_android description: Android implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.0.16 +version: 6.0.17 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_ios/CHANGELOG.md b/packages/url_launcher/url_launcher_ios/CHANGELOG.md index 6e0c8d6a20d7..5f6dd37142bb 100644 --- a/packages/url_launcher/url_launcher_ios/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_ios/CHANGELOG.md @@ -1,3 +1,8 @@ +## 6.0.16 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 6.0.15 * Switches to an in-package method channel implementation. diff --git a/packages/url_launcher/url_launcher_ios/example/lib/main.dart b/packages/url_launcher/url_launcher_ios/example/lib/main.dart index 2f73622ebb41..7aa3a4b74e83 100644 --- a/packages/url_launcher/url_launcher_ios/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_ios/example/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -31,7 +33,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_ios/pubspec.yaml b/packages/url_launcher/url_launcher_ios/pubspec.yaml index 8a5bfd20c8f4..0b21bad35204 100644 --- a/packages/url_launcher/url_launcher_ios/pubspec.yaml +++ b/packages/url_launcher/url_launcher_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_ios description: iOS implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.0.15 +version: 6.0.16 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_linux/CHANGELOG.md b/packages/url_launcher/url_launcher_linux/CHANGELOG.md index 0fc373f2ebb1..27c18a66805b 100644 --- a/packages/url_launcher/url_launcher_linux/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_linux/CHANGELOG.md @@ -1,3 +1,8 @@ +## 3.0.1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 3.0.0 * Changes the major version since, due to a typo in `default_package` in diff --git a/packages/url_launcher/url_launcher_linux/example/lib/main.dart b/packages/url_launcher/url_launcher_linux/example/lib/main.dart index a9a5d22dadd5..0b985e78ac0d 100644 --- a/packages/url_launcher/url_launcher_linux/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_linux/example/lib/main.dart @@ -9,10 +9,12 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -30,7 +32,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_linux/pubspec.yaml b/packages/url_launcher/url_launcher_linux/pubspec.yaml index cb9a0be0aa41..c9472045e499 100644 --- a/packages/url_launcher/url_launcher_linux/pubspec.yaml +++ b/packages/url_launcher/url_launcher_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_linux description: Linux implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 3.0.0 +version: 3.0.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_macos/CHANGELOG.md b/packages/url_launcher/url_launcher_macos/CHANGELOG.md index 082bc45fc2e8..2fa5e918eadd 100644 --- a/packages/url_launcher/url_launcher_macos/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_macos/CHANGELOG.md @@ -1,3 +1,8 @@ +## 3.0.1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 3.0.0 * Changes the major version since, due to a typo in `default_package` in diff --git a/packages/url_launcher/url_launcher_macos/example/lib/main.dart b/packages/url_launcher/url_launcher_macos/example/lib/main.dart index a9a5d22dadd5..0b985e78ac0d 100644 --- a/packages/url_launcher/url_launcher_macos/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_macos/example/lib/main.dart @@ -9,10 +9,12 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -30,7 +32,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_macos/pubspec.yaml b/packages/url_launcher/url_launcher_macos/pubspec.yaml index 8b5183b1914d..edda6b67cfb3 100644 --- a/packages/url_launcher/url_launcher_macos/pubspec.yaml +++ b/packages/url_launcher/url_launcher_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_macos description: macOS implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 3.0.0 +version: 3.0.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_web/CHANGELOG.md b/packages/url_launcher/url_launcher_web/CHANGELOG.md index a434b7af70c2..b53a92cee707 100644 --- a/packages/url_launcher/url_launcher_web/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.10 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.9 - Fixes invalid routes when opening a `Link` in a new tab diff --git a/packages/url_launcher/url_launcher_web/example/lib/main.dart b/packages/url_launcher/url_launcher_web/example/lib/main.dart index 341913a18490..87422953de6a 100644 --- a/packages/url_launcher/url_launcher_web/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_web/example/lib/main.dart @@ -5,13 +5,16 @@ import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// App for testing class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/url_launcher/url_launcher_web/lib/src/link.dart b/packages/url_launcher/url_launcher_web/lib/src/link.dart index 4498e74ea9ce..eccd9aef80e3 100644 --- a/packages/url_launcher/url_launcher_web/lib/src/link.dart +++ b/packages/url_launcher/url_launcher_web/lib/src/link.dart @@ -31,7 +31,7 @@ HtmlViewFactory get linkViewFactory => LinkViewController._viewFactory; /// It uses a platform view to render an anchor element in the DOM. class WebLinkDelegate extends StatefulWidget { /// Creates a delegate for the given [link]. - const WebLinkDelegate(this.link); + const WebLinkDelegate(this.link, {Key? key}) : super(key: key); /// Information about the link built by the app. final LinkInfo link; diff --git a/packages/url_launcher/url_launcher_web/pubspec.yaml b/packages/url_launcher/url_launcher_web/pubspec.yaml index c45c062255ad..cd8ed2d269c8 100644 --- a/packages/url_launcher/url_launcher_web/pubspec.yaml +++ b/packages/url_launcher/url_launcher_web/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_web description: Web platform implementation of url_launcher repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 2.0.9 +version: 2.0.10 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_windows/CHANGELOG.md b/packages/url_launcher/url_launcher_windows/CHANGELOG.md index e02f5a2288e1..3ff14fd2f18a 100644 --- a/packages/url_launcher/url_launcher_windows/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_windows/CHANGELOG.md @@ -1,3 +1,8 @@ +## 3.0.1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 3.0.0 * Changes the major version since, due to a typo in `default_package` in diff --git a/packages/url_launcher/url_launcher_windows/example/lib/main.dart b/packages/url_launcher/url_launcher_windows/example/lib/main.dart index a9a5d22dadd5..0b985e78ac0d 100644 --- a/packages/url_launcher/url_launcher_windows/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_windows/example/lib/main.dart @@ -9,10 +9,12 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -30,7 +32,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_windows/pubspec.yaml b/packages/url_launcher/url_launcher_windows/pubspec.yaml index 95f17ad9e921..c3f224e26adf 100644 --- a/packages/url_launcher/url_launcher_windows/pubspec.yaml +++ b/packages/url_launcher/url_launcher_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_windows description: Windows implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 3.0.0 +version: 3.0.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index af01c64fd554..ede890162f86 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.4.1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.4.0 diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index 39cd415dbb79..a6ec51015c33 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -708,14 +708,14 @@ class _VideoAppLifeCycleObserver extends Object with WidgetsBindingObserver { /// Widget that displays the video controlled by [controller]. class VideoPlayer extends StatefulWidget { /// Uses the given [controller] for all video rendered in this widget. - const VideoPlayer(this.controller); + const VideoPlayer(this.controller, {Key? key}) : super(key: key); /// The [VideoPlayerController] responsible for the video being rendered in /// this widget. final VideoPlayerController controller; @override - _VideoPlayerState createState() => _VideoPlayerState(); + State createState() => _VideoPlayerState(); } class _VideoPlayerState extends State { @@ -883,10 +883,11 @@ class VideoProgressIndicator extends StatefulWidget { /// to `top: 5.0`. const VideoProgressIndicator( this.controller, { + Key? key, this.colors = const VideoProgressColors(), required this.allowScrubbing, this.padding = const EdgeInsets.only(top: 5.0), - }); + }) : super(key: key); /// The [VideoPlayerController] that actually associates a video with this /// widget. @@ -910,7 +911,7 @@ class VideoProgressIndicator extends StatefulWidget { final EdgeInsets padding; @override - _VideoProgressIndicatorState createState() => _VideoProgressIndicatorState(); + State createState() => _VideoProgressIndicatorState(); } class _VideoProgressIndicatorState extends State { @@ -984,8 +985,8 @@ class _VideoProgressIndicatorState extends State { ); if (widget.allowScrubbing) { return _VideoScrubber( - child: paddedProgressIndicator, controller: controller, + child: paddedProgressIndicator, ); } else { return paddedProgressIndicator; diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 0d654a4330a7..b0ca56429271 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for displaying inline video with other Flutter widgets on Android, iOS, and web. repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.4.0 +version: 2.4.1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index 16dd52ca6da0..08acba895eba 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.3.3 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.3.2 diff --git a/packages/video_player/video_player_android/example/lib/mini_controller.dart b/packages/video_player/video_player_android/example/lib/mini_controller.dart index 498dbffc9e84..5bce3117d0d6 100644 --- a/packages/video_player/video_player_android/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_android/example/lib/mini_controller.dart @@ -351,14 +351,14 @@ class MiniController extends ValueNotifier { /// Widget that displays the video controlled by [controller]. class VideoPlayer extends StatefulWidget { /// Uses the given [controller] for all video rendered in this widget. - const VideoPlayer(this.controller); + const VideoPlayer(this.controller, {Key? key}) : super(key: key); /// The [MiniController] responsible for the video being rendered in /// this widget. final MiniController controller; @override - _VideoPlayerState createState() => _VideoPlayerState(); + State createState() => _VideoPlayerState(); } class _VideoPlayerState extends State { @@ -450,14 +450,14 @@ class _VideoScrubberState extends State<_VideoScrubber> { class VideoProgressIndicator extends StatefulWidget { /// Construct an instance that displays the play/buffering status of the video /// controlled by [controller]. - const VideoProgressIndicator(this.controller); + const VideoProgressIndicator(this.controller, {Key? key}) : super(key: key); /// The [MiniController] that actually associates a video with this /// widget. final MiniController controller; @override - _VideoProgressIndicatorState createState() => _VideoProgressIndicatorState(); + State createState() => _VideoProgressIndicatorState(); } class _VideoProgressIndicatorState extends State { @@ -527,11 +527,11 @@ class _VideoProgressIndicatorState extends State { ); } return _VideoScrubber( + controller: controller, child: Padding( padding: const EdgeInsets.only(top: 5.0), child: progressIndicator, ), - controller: controller, ); } } diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index aa288ed71eac..bc69fd41369a 100644 --- a/packages/video_player/video_player_android/pubspec.yaml +++ b/packages/video_player/video_player_android/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_android description: Android implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.2 +version: 2.3.3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index d77c36c915b6..6ab5398f7013 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.3.4 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.3.3 diff --git a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart index 498dbffc9e84..5bce3117d0d6 100644 --- a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart @@ -351,14 +351,14 @@ class MiniController extends ValueNotifier { /// Widget that displays the video controlled by [controller]. class VideoPlayer extends StatefulWidget { /// Uses the given [controller] for all video rendered in this widget. - const VideoPlayer(this.controller); + const VideoPlayer(this.controller, {Key? key}) : super(key: key); /// The [MiniController] responsible for the video being rendered in /// this widget. final MiniController controller; @override - _VideoPlayerState createState() => _VideoPlayerState(); + State createState() => _VideoPlayerState(); } class _VideoPlayerState extends State { @@ -450,14 +450,14 @@ class _VideoScrubberState extends State<_VideoScrubber> { class VideoProgressIndicator extends StatefulWidget { /// Construct an instance that displays the play/buffering status of the video /// controlled by [controller]. - const VideoProgressIndicator(this.controller); + const VideoProgressIndicator(this.controller, {Key? key}) : super(key: key); /// The [MiniController] that actually associates a video with this /// widget. final MiniController controller; @override - _VideoProgressIndicatorState createState() => _VideoProgressIndicatorState(); + State createState() => _VideoProgressIndicatorState(); } class _VideoProgressIndicatorState extends State { @@ -527,11 +527,11 @@ class _VideoProgressIndicatorState extends State { ); } return _VideoScrubber( + controller: controller, child: Padding( padding: const EdgeInsets.only(top: 5.0), child: progressIndicator, ), - controller: controller, ); } } diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index b3cc69eca958..380d8343c024 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_avfoundation description: iOS implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.3 +version: 2.3.4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md index 00788c4386fe..094ffda207c5 100644 --- a/packages/video_player/video_player_web/CHANGELOG.md +++ b/packages/video_player/video_player_web/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.0.9 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.0.8 diff --git a/packages/video_player/video_player_web/example/lib/main.dart b/packages/video_player/video_player_web/example/lib/main.dart index 341913a18490..87422953de6a 100644 --- a/packages/video_player/video_player_web/example/lib/main.dart +++ b/packages/video_player/video_player_web/example/lib/main.dart @@ -5,13 +5,16 @@ import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// App for testing class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml index 064517e1f264..7af0dc46dde8 100644 --- a/packages/video_player/video_player_web/pubspec.yaml +++ b/packages/video_player/video_player_web/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_web description: Web platform implementation of video_player. repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.0.8 +version: 2.0.9 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index 7a56f3f176d0..fa47a6cc3143 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 3.0.3 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 3.0.2 @@ -11,7 +13,7 @@ * Removes a duplicate Android-specific integration test. * Fixes an integration test race condition. -* Fixes comments (accidentially mixed // with ///). +* Fixes comments (accidentally mixed // with ///). ## 3.0.0 diff --git a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart index ba321264ee1e..cc001f336849 100644 --- a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart @@ -1306,7 +1306,8 @@ Future _runJavascriptReturningResult( class ResizableWebView extends StatefulWidget { const ResizableWebView( - {required this.onResize, required this.onPageFinished}); + {Key? key, required this.onResize, required this.onPageFinished}) + : super(key: key); final JavascriptMessageHandler onResize; final VoidCallback onPageFinished; diff --git a/packages/webview_flutter/webview_flutter/example/lib/main.dart b/packages/webview_flutter/webview_flutter/example/lib/main.dart index 51080be01df0..3d8731127970 100644 --- a/packages/webview_flutter/webview_flutter/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter/example/lib/main.dart @@ -71,12 +71,12 @@ const String kTransparentBackgroundPage = ''' '''; class WebViewExample extends StatefulWidget { - const WebViewExample({this.cookieManager}); + const WebViewExample({Key? key, this.cookieManager}) : super(key: key); final CookieManager? cookieManager; @override - _WebViewExampleState createState() => _WebViewExampleState(); + State createState() => _WebViewExampleState(); } class _WebViewExampleState extends State { @@ -190,8 +190,9 @@ enum MenuOptions { } class SampleMenu extends StatelessWidget { - SampleMenu(this.controller, CookieManager? cookieManager) - : cookieManager = cookieManager ?? CookieManager(); + SampleMenu(this.controller, CookieManager? cookieManager, {Key? key}) + : cookieManager = cookieManager ?? CookieManager(), + super(key: key); final Future controller; late final CookieManager cookieManager; @@ -250,8 +251,8 @@ class SampleMenu extends StatelessWidget { itemBuilder: (BuildContext context) => >[ PopupMenuItem( value: MenuOptions.showUserAgent, - child: const Text('Show user agent'), enabled: controller.hasData, + child: const Text('Show user agent'), ), const PopupMenuItem( value: MenuOptions.listCookies, @@ -443,8 +444,9 @@ class SampleMenu extends StatelessWidget { } class NavigationControls extends StatelessWidget { - const NavigationControls(this._webViewControllerFuture) - : assert(_webViewControllerFuture != null); + const NavigationControls(this._webViewControllerFuture, {Key? key}) + : assert(_webViewControllerFuture != null), + super(key: key); final Future _webViewControllerFuture; diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index 10350984ce9a..a48f6f912c2d 100644 --- a/packages/webview_flutter/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter description: A Flutter plugin that provides a WebView widget on Android and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 3.0.2 +version: 3.0.3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index edaa0883713f..4a451442f6cc 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.8.7 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.8.6 diff --git a/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart index 51e09912da23..4c06fa6b3c18 100644 --- a/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart @@ -1446,9 +1446,10 @@ Future _runJavaScriptReturningResult( class ResizableWebView extends StatefulWidget { const ResizableWebView({ + Key? key, required this.onResize, required this.onPageFinished, - }); + }) : super(key: key); final JavascriptMessageHandler onResize; final VoidCallback onPageFinished; diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart index 5d19ca71ac84..349a64916e8b 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart @@ -247,8 +247,8 @@ class _SampleMenu extends StatelessWidget { itemBuilder: (BuildContext context) => >[ PopupMenuItem<_MenuOptions>( value: _MenuOptions.showUserAgent, - child: const Text('Show user agent'), enabled: controller.hasData, + child: const Text('Show user agent'), ), const PopupMenuItem<_MenuOptions>( value: _MenuOptions.listCookies, diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart index 91ea66376904..56745314d92b 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart @@ -250,7 +250,7 @@ class WebView extends StatefulWidget { final Color? backgroundColor; @override - _WebViewState createState() => _WebViewState(); + State createState() => _WebViewState(); } class _WebViewState extends State { diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart index 28d169c9cb94..f1b130c7e365 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart @@ -15,6 +15,7 @@ import 'src/android_webview.dart' as android_webview; class WebViewAndroidWidget extends StatefulWidget { /// Constructs a [WebViewAndroidWidget]. const WebViewAndroidWidget({ + Key? key, required this.creationParams, required this.useHybridComposition, required this.callbacksHandler, @@ -24,7 +25,7 @@ class WebViewAndroidWidget extends StatefulWidget { @visibleForTesting this.flutterAssetManager = const android_webview.FlutterAssetManager(), @visibleForTesting this.webStorage, - }); + }) : super(key: key); /// Initial parameters used to setup the WebView. final CreationParams creationParams; diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 9a7c48a4dcd8..407887f2ba95 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_android description: A Flutter plugin that provides a WebView widget on Android. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.8.6 +version: 2.8.7 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md index 9f7ebe368941..b7254e1a0a7a 100644 --- a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md @@ -1,7 +1,9 @@ -## NEXT +## 0.1.0+2 * Removes unnecessary imports. * Fixes unit tests to run on latest `master` version of Flutter. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.1.0+1 diff --git a/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart index 8cd74f660f58..ffd3367d33f4 100644 --- a/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart @@ -46,7 +46,7 @@ class WebView extends StatefulWidget { final String? initialUrl; @override - _WebViewState createState() => _WebViewState(); + State createState() => _WebViewState(); } class _WebViewState extends State { diff --git a/packages/webview_flutter/webview_flutter_web/pubspec.yaml b/packages/webview_flutter/webview_flutter_web/pubspec.yaml index bd154387097e..35a7b74a764c 100644 --- a/packages/webview_flutter/webview_flutter_web/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_web/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_web description: A Flutter plugin that provides a WebView widget on web. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 0.1.0+1 +version: 0.1.0+2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index f042dd081475..6db769b0d922 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.7.4 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.7.3 diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart index ceff62e3d5e8..aa376f8358e9 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart @@ -1181,7 +1181,8 @@ Future _getUserAgent(WebViewController controller) async { class ResizableWebView extends StatefulWidget { const ResizableWebView( - {required this.onResize, required this.onPageFinished}); + {Key? key, required this.onResize, required this.onPageFinished}) + : super(key: key); final JavascriptMessageHandler onResize; final VoidCallback onPageFinished; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart index e8b86d9c5773..7b30923e1e54 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart @@ -232,8 +232,8 @@ class _SampleMenu extends StatelessWidget { itemBuilder: (BuildContext context) => >[ PopupMenuItem<_MenuOptions>( value: _MenuOptions.showUserAgent, - child: const Text('Show user agent'), enabled: controller.hasData, + child: const Text('Show user agent'), ), const PopupMenuItem<_MenuOptions>( value: _MenuOptions.listCookies, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart index 4d479f943d62..c44c4e743669 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart @@ -240,7 +240,7 @@ class WebView extends StatefulWidget { final Color? backgroundColor; @override - _WebViewState createState() => _WebViewState(); + State createState() => _WebViewState(); } class _WebViewState extends State { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 012cd221599b..8c37112d7a24 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -18,13 +18,14 @@ import 'web_kit/web_kit.dart'; class WebKitWebViewWidget extends StatefulWidget { /// Constructs a [WebKitWebViewWidget]. const WebKitWebViewWidget({ + Key? key, required this.creationParams, required this.callbacksHandler, required this.javascriptChannelRegistry, required this.onBuildWidget, this.configuration, @visibleForTesting this.webViewProxy = const WebViewWidgetProxy(), - }); + }) : super(key: key); /// The initial parameters used to setup the WebView. final CreationParams creationParams; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index f4e72b8f14eb..365e64720d4f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_wkwebview description: A Flutter plugin that provides a WebView widget based on Apple's WKWebView control. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_wkwebview issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.7.3 +version: 2.7.4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart index 5c38bd5f7033..b0a8990e1300 100644 --- a/script/tool/test/util.dart +++ b/script/tool/test/util.dart @@ -319,14 +319,14 @@ String _pluginPlatformSection( return entry; } -typedef _ErrorHandler = void Function(Error error); +typedef ErrorHandler = void Function(Error error); /// Run the command [runner] with the given [args] and return /// what was printed. /// A custom [errorHandler] can be used to handle the runner error as desired without throwing. Future> runCapturingPrint( CommandRunner runner, List args, - {_ErrorHandler? errorHandler}) async { + {ErrorHandler? errorHandler}) async { final List prints = []; final ZoneSpecification spec = ZoneSpecification( print: (_, __, ___, String message) { From 329f5efe63bfdd8061f9f0580008d3e79351ad5d Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Tue, 10 May 2022 13:12:14 -0700 Subject: [PATCH 543/600] [webview_flutter_wkwebview] The rest of the Objective-C HostApi methods (#5604) --- .../ios/Runner.xcodeproj/project.pbxproj | 46 +++++- .../ios/RunnerTests/FWFDataConvertersTests.m | 23 ++- .../FWFHTTPCookieStoreHostApiTests.m | 55 +++++++ .../FWFNavigationDelegateHostApiTests.m | 28 ++++ .../ios/RunnerTests/FWFObjectHostApiTests.m | 82 +++++++++++ .../RunnerTests/FWFPreferencesHostApiTests.m | 43 ++++++ .../FWFScriptMessageHandlerHostApiTests.m | 29 ++++ .../RunnerTests/FWFScrollViewHostApiTests.m | 64 ++++++++ .../RunnerTests/FWFUIDelegateHostApiTests.m | 27 ++++ .../ios/RunnerTests/FWFUIViewHostApiTests.m | 49 +++++++ .../FWFUserContentControllerHostApiTests.m | 127 ++++++++++++++++ .../FWFWebViewConfigurationHostApiTests.m | 88 +++++++++++ .../ios/RunnerTests/FWFWebViewHostApiTests.m | 14 ++ .../FWFWebsiteDataStoreHostApiTests.m | 75 ++++++++++ .../ios/Classes/FWFDataConverters.h | 73 ++++++++- .../ios/Classes/FWFDataConverters.m | 130 +++++++++++++++++ .../ios/Classes/FWFGeneratedWebKitApis.h | 70 +++++---- .../ios/Classes/FWFGeneratedWebKitApis.m | 138 ++++++++---------- .../ios/Classes/FWFHTTPCookieStoreHostApi.h | 22 +++ .../ios/Classes/FWFHTTPCookieStoreHostApi.m | 60 ++++++++ .../Classes/FWFNavigationDelegateHostApi.h | 28 ++++ .../Classes/FWFNavigationDelegateHostApi.m | 40 +++++ .../ios/Classes/FWFObjectHostApi.h | 21 +++ .../ios/Classes/FWFObjectHostApi.m | 54 +++++++ .../ios/Classes/FWFPreferencesHostApi.h | 22 +++ .../ios/Classes/FWFPreferencesHostApi.m | 44 ++++++ .../Classes/FWFScriptMessageHandlerHostApi.h | 28 ++++ .../Classes/FWFScriptMessageHandlerHostApi.m | 37 +++++ .../ios/Classes/FWFScrollViewHostApi.h | 22 +++ .../ios/Classes/FWFScrollViewHostApi.m | 57 ++++++++ .../ios/Classes/FWFUIDelegateHostApi.h | 28 ++++ .../ios/Classes/FWFUIDelegateHostApi.m | 33 +++++ .../ios/Classes/FWFUIViewHostApi.h | 21 +++ .../ios/Classes/FWFUIViewHostApi.m | 43 ++++++ .../Classes/FWFUserContentControllerHostApi.h | 22 +++ .../Classes/FWFUserContentControllerHostApi.m | 80 ++++++++++ .../Classes/FWFWebViewConfigurationHostApi.h | 22 +++ .../Classes/FWFWebViewConfigurationHostApi.m | 87 +++++++++++ .../ios/Classes/FWFWebsiteDataStoreHostApi.h | 22 +++ .../ios/Classes/FWFWebsiteDataStoreHostApi.m | 67 +++++++++ .../ios/Classes/webview-umbrella.h | 11 ++ .../lib/src/common/web_kit.pigeon.dart | 122 ++++++---------- .../lib/src/foundation/foundation.dart | 3 + .../lib/src/web_kit/web_kit.dart | 14 +- .../lib/src/web_kit/web_kit_api_impls.dart | 61 ++++---- .../lib/src/web_kit_cookie_manager.dart | 2 +- .../lib/src/web_kit_webview_widget.dart | 18 ++- .../pigeons/web_kit.dart | 70 +++++---- .../test/src/common/test_web_kit.pigeon.dart | 51 ++----- .../test/src/ui_kit/ui_kit_test.mocks.dart | 4 - .../test/src/web_kit/web_kit_test.dart | 14 +- .../test/src/web_kit/web_kit_test.mocks.dart | 27 ++-- .../test/src/web_kit_cookie_manager_test.dart | 4 +- .../web_kit_cookie_manager_test.mocks.dart | 2 +- .../test/src/web_kit_webview_widget_test.dart | 20 +-- .../web_kit_webview_widget_test.mocks.dart | 2 +- 56 files changed, 2115 insertions(+), 331 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFHTTPCookieStoreHostApiTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFNavigationDelegateHostApiTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFObjectHostApiTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFPreferencesHostApiTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScriptMessageHandlerHostApiTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScrollViewHostApiTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIDelegateHostApiTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIViewHostApiTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUserContentControllerHostApiTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewConfigurationHostApiTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebsiteDataStoreHostApiTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFHTTPCookieStoreHostApi.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFHTTPCookieStoreHostApi.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFPreferencesHostApi.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFPreferencesHostApi.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewHostApi.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewHostApi.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIViewHostApi.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIViewHostApi.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUserContentControllerHostApi.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUserContentControllerHostApi.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.m diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj index c48b313daec2..e7519af18e7c 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ @@ -14,6 +14,17 @@ 8FA6A87928062CD000A4B183 /* FWFInstanceManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */; }; 8FB79B5328134C3100C101D3 /* FWFWebViewHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B5228134C3100C101D3 /* FWFWebViewHostApiTests.m */; }; 8FB79B55281B24F600C101D3 /* FWFDataConvertersTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B54281B24F600C101D3 /* FWFDataConvertersTests.m */; }; + 8FB79B672820453400C101D3 /* FWFHTTPCookieStoreHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B662820453400C101D3 /* FWFHTTPCookieStoreHostApiTests.m */; }; + 8FB79B6928204E8700C101D3 /* FWFPreferencesHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B6828204E8700C101D3 /* FWFPreferencesHostApiTests.m */; }; + 8FB79B6B28204EE500C101D3 /* FWFWebsiteDataStoreHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B6A28204EE500C101D3 /* FWFWebsiteDataStoreHostApiTests.m */; }; + 8FB79B6D2820533B00C101D3 /* FWFWebViewConfigurationHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B6C2820533B00C101D3 /* FWFWebViewConfigurationHostApiTests.m */; }; + 8FB79B73282096B500C101D3 /* FWFScriptMessageHandlerHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B72282096B500C101D3 /* FWFScriptMessageHandlerHostApiTests.m */; }; + 8FB79B7928209D1300C101D3 /* FWFUserContentControllerHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B7828209D1300C101D3 /* FWFUserContentControllerHostApiTests.m */; }; + 8FB79B832820A39300C101D3 /* FWFNavigationDelegateHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B822820A39300C101D3 /* FWFNavigationDelegateHostApiTests.m */; }; + 8FB79B852820A3A400C101D3 /* FWFUIDelegateHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B842820A3A400C101D3 /* FWFUIDelegateHostApiTests.m */; }; + 8FB79B8F2820BAB300C101D3 /* FWFScrollViewHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B8E2820BAB300C101D3 /* FWFScrollViewHostApiTests.m */; }; + 8FB79B912820BAC700C101D3 /* FWFUIViewHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B902820BAC700C101D3 /* FWFUIViewHostApiTests.m */; }; + 8FB79B972821985200C101D3 /* FWFObjectHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B962821985200C101D3 /* FWFObjectHostApiTests.m */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -73,6 +84,17 @@ 8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFInstanceManagerTests.m; sourceTree = ""; }; 8FB79B5228134C3100C101D3 /* FWFWebViewHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFWebViewHostApiTests.m; sourceTree = ""; }; 8FB79B54281B24F600C101D3 /* FWFDataConvertersTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFDataConvertersTests.m; sourceTree = ""; }; + 8FB79B662820453400C101D3 /* FWFHTTPCookieStoreHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFHTTPCookieStoreHostApiTests.m; sourceTree = ""; }; + 8FB79B6828204E8700C101D3 /* FWFPreferencesHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFPreferencesHostApiTests.m; sourceTree = ""; }; + 8FB79B6A28204EE500C101D3 /* FWFWebsiteDataStoreHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFWebsiteDataStoreHostApiTests.m; sourceTree = ""; }; + 8FB79B6C2820533B00C101D3 /* FWFWebViewConfigurationHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFWebViewConfigurationHostApiTests.m; sourceTree = ""; }; + 8FB79B72282096B500C101D3 /* FWFScriptMessageHandlerHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFScriptMessageHandlerHostApiTests.m; sourceTree = ""; }; + 8FB79B7828209D1300C101D3 /* FWFUserContentControllerHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFUserContentControllerHostApiTests.m; sourceTree = ""; }; + 8FB79B822820A39300C101D3 /* FWFNavigationDelegateHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFNavigationDelegateHostApiTests.m; sourceTree = ""; }; + 8FB79B842820A3A400C101D3 /* FWFUIDelegateHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFUIDelegateHostApiTests.m; sourceTree = ""; }; + 8FB79B8E2820BAB300C101D3 /* FWFScrollViewHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFScrollViewHostApiTests.m; sourceTree = ""; }; + 8FB79B902820BAC700C101D3 /* FWFUIViewHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFUIViewHostApiTests.m; sourceTree = ""; }; + 8FB79B962821985200C101D3 /* FWFObjectHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFObjectHostApiTests.m; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -135,6 +157,17 @@ 8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */, 8FB79B5228134C3100C101D3 /* FWFWebViewHostApiTests.m */, 8FB79B54281B24F600C101D3 /* FWFDataConvertersTests.m */, + 8FB79B662820453400C101D3 /* FWFHTTPCookieStoreHostApiTests.m */, + 8FB79B6828204E8700C101D3 /* FWFPreferencesHostApiTests.m */, + 8FB79B6A28204EE500C101D3 /* FWFWebsiteDataStoreHostApiTests.m */, + 8FB79B6C2820533B00C101D3 /* FWFWebViewConfigurationHostApiTests.m */, + 8FB79B72282096B500C101D3 /* FWFScriptMessageHandlerHostApiTests.m */, + 8FB79B7828209D1300C101D3 /* FWFUserContentControllerHostApiTests.m */, + 8FB79B822820A39300C101D3 /* FWFNavigationDelegateHostApiTests.m */, + 8FB79B842820A3A400C101D3 /* FWFUIDelegateHostApiTests.m */, + 8FB79B8E2820BAB300C101D3 /* FWFScrollViewHostApiTests.m */, + 8FB79B902820BAC700C101D3 /* FWFUIViewHostApiTests.m */, + 8FB79B962821985200C101D3 /* FWFObjectHostApiTests.m */, ); path = RunnerTests; sourceTree = ""; @@ -433,11 +466,22 @@ buildActionMask = 2147483647; files = ( 8FA6A87928062CD000A4B183 /* FWFInstanceManagerTests.m in Sources */, + 8FB79B852820A3A400C101D3 /* FWFUIDelegateHostApiTests.m in Sources */, + 8FB79B972821985200C101D3 /* FWFObjectHostApiTests.m in Sources */, + 8FB79B672820453400C101D3 /* FWFHTTPCookieStoreHostApiTests.m in Sources */, 8FB79B5328134C3100C101D3 /* FWFWebViewHostApiTests.m in Sources */, + 8FB79B73282096B500C101D3 /* FWFScriptMessageHandlerHostApiTests.m in Sources */, 334734012669319100DCC49E /* FLTWebViewTests.m in Sources */, + 8FB79B7928209D1300C101D3 /* FWFUserContentControllerHostApiTests.m in Sources */, + 8FB79B6B28204EE500C101D3 /* FWFWebsiteDataStoreHostApiTests.m in Sources */, + 8FB79B8F2820BAB300C101D3 /* FWFScrollViewHostApiTests.m in Sources */, + 8FB79B912820BAC700C101D3 /* FWFUIViewHostApiTests.m in Sources */, 334734022669319400DCC49E /* FLTWKNavigationDelegateTests.m in Sources */, 8FB79B55281B24F600C101D3 /* FWFDataConvertersTests.m in Sources */, + 8FB79B6D2820533B00C101D3 /* FWFWebViewConfigurationHostApiTests.m in Sources */, + 8FB79B832820A39300C101D3 /* FWFNavigationDelegateHostApiTests.m in Sources */, E43693B527512C0F00382F85 /* FLTCookieManagerTests.m in Sources */, + 8FB79B6928204E8700C101D3 /* FWFPreferencesHostApiTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFDataConvertersTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFDataConvertersTests.m index 55952373998d..57d90f6c6814 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFDataConvertersTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFDataConvertersTests.m @@ -10,7 +10,7 @@ @interface FWFDataConvertersTests : XCTestCase @end @implementation FWFDataConvertersTests -- (void)testFNSURLRequestFromRequestData { +- (void)testFWFNSURLRequestFromRequestData { NSURLRequest *request = FWFNSURLRequestFromRequestData([FWFNSUrlRequestData makeWithUrl:@"https://flutter.dev" httpMethod:@"post" @@ -22,4 +22,25 @@ - (void)testFNSURLRequestFromRequestData { XCTAssertEqualObjects(request.HTTPBody, [NSData data]); XCTAssertEqualObjects(request.allHTTPHeaderFields, @{@"a" : @"header"}); } + +- (void)testFWFNSHTTPCookieFromCookieData { + NSHTTPCookie *cookie = FWFNSHTTPCookieFromCookieData([FWFNSHttpCookieData + makeWithPropertyKeys:@[ [FWFNSHttpCookiePropertyKeyEnumData + makeWithValue:FWFNSHttpCookiePropertyKeyEnumName] ] + propertyValues:@[ @"cookieName" ]]); + XCTAssertEqualObjects(cookie, + [NSHTTPCookie cookieWithProperties:@{NSHTTPCookieName : @"cookieName"}]); +} + +- (void)testFWFWKUserScriptFromScriptData { + WKUserScript *userScript = FWFWKUserScriptFromScriptData([FWFWKUserScriptData + makeWithSource:@"mySource" + injectionTime:[FWFWKUserScriptInjectionTimeEnumData + makeWithValue:FWFWKUserScriptInjectionTimeEnumAtDocumentStart] + isMainFrameOnly:@NO]); + + XCTAssertEqualObjects(userScript.source, @"mySource"); + XCTAssertEqual(userScript.injectionTime, WKUserScriptInjectionTimeAtDocumentStart); + XCTAssertEqual(userScript.isForMainFrameOnly, NO); +} @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFHTTPCookieStoreHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFHTTPCookieStoreHostApiTests.m new file mode 100644 index 000000000000..315640a99247 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFHTTPCookieStoreHostApiTests.m @@ -0,0 +1,55 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import Flutter; +@import XCTest; +@import webview_flutter_wkwebview; + +#import + +@interface FWFHTTPCookieStoreHostApiTests : XCTestCase +@end + +@implementation FWFHTTPCookieStoreHostApiTests +- (void)testCreateFromWebsiteDataStoreWithIdentifier API_AVAILABLE(ios(11.0)) { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + FWFHTTPCookieStoreHostApiImpl *hostApi = + [[FWFHTTPCookieStoreHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + WKWebsiteDataStore *mockDataStore = OCMClassMock([WKWebsiteDataStore class]); + OCMStub([mockDataStore httpCookieStore]).andReturn(OCMClassMock([WKHTTPCookieStore class])); + [instanceManager addInstance:mockDataStore withIdentifier:0]; + + FlutterError *error; + [hostApi createFromWebsiteDataStoreWithIdentifier:@1 dataStoreIdentifier:@0 error:&error]; + WKHTTPCookieStore *cookieStore = (WKHTTPCookieStore *)[instanceManager instanceForIdentifier:1]; + XCTAssertTrue([cookieStore isKindOfClass:[WKHTTPCookieStore class]]); + XCTAssertNil(error); +} + +- (void)testSetCookie API_AVAILABLE(ios(11.0)) { + WKHTTPCookieStore *mockHttpCookieStore = OCMClassMock([WKHTTPCookieStore class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockHttpCookieStore withIdentifier:0]; + + FWFHTTPCookieStoreHostApiImpl *hostApi = + [[FWFHTTPCookieStoreHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FWFNSHttpCookieData *cookieData = [FWFNSHttpCookieData + makeWithPropertyKeys:@[ [FWFNSHttpCookiePropertyKeyEnumData + makeWithValue:FWFNSHttpCookiePropertyKeyEnumName] ] + propertyValues:@[ @"hello" ]]; + FlutterError *__block blockError; + [hostApi setCookieForStoreWithIdentifier:@0 + cookie:cookieData + completion:^(FlutterError *error) { + blockError = error; + }]; + OCMVerify([mockHttpCookieStore + setCookie:[NSHTTPCookie cookieWithProperties:@{NSHTTPCookieName : @"hello"}] + completionHandler:OCMOCK_ANY]); + XCTAssertNil(blockError); +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFNavigationDelegateHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFNavigationDelegateHostApiTests.m new file mode 100644 index 000000000000..02e473f8b795 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFNavigationDelegateHostApiTests.m @@ -0,0 +1,28 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import Flutter; +@import XCTest; +@import webview_flutter_wkwebview; + +#import + +@interface FWFNavigationDelegateHostApiTests : XCTestCase +@end + +@implementation FWFNavigationDelegateHostApiTests +- (void)testCreateWithIdentifier { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + FWFNavigationDelegateHostApiImpl *hostApi = + [[FWFNavigationDelegateHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi createWithIdentifier:@0 error:&error]; + FWFNavigationDelegate *navigationDelegate = + (FWFNavigationDelegate *)[instanceManager instanceForIdentifier:0]; + + XCTAssertTrue([navigationDelegate conformsToProtocol:@protocol(WKNavigationDelegate)]); + XCTAssertNil(error); +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFObjectHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFObjectHostApiTests.m new file mode 100644 index 000000000000..6886c2600e13 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFObjectHostApiTests.m @@ -0,0 +1,82 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import Flutter; +@import XCTest; +@import webview_flutter_wkwebview; + +#import + +@interface FWFObjectHostApiTests : XCTestCase +@end + +@implementation FWFObjectHostApiTests +- (void)testAddObserver { + NSObject *mockObject = OCMClassMock([NSObject class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockObject withIdentifier:0]; + + FWFObjectHostApiImpl *hostApi = + [[FWFObjectHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + NSObject *observerObject = [[NSObject alloc] init]; + [instanceManager addInstance:observerObject withIdentifier:1]; + + FlutterError *error; + [hostApi + addObserverForObjectWithIdentifier:@0 + observerIdentifier:@1 + keyPath:@"myKey" + options:@[ + [FWFNSKeyValueObservingOptionsEnumData + makeWithValue:FWFNSKeyValueObservingOptionsEnumOldValue], + [FWFNSKeyValueObservingOptionsEnumData + makeWithValue:FWFNSKeyValueObservingOptionsEnumNewValue] + ] + error:&error]; + + OCMVerify([mockObject addObserver:observerObject + forKeyPath:@"myKey" + options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) + context:nil]); + XCTAssertNil(error); +} + +- (void)testRemoveObserver { + NSObject *mockObject = OCMClassMock([NSObject class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockObject withIdentifier:0]; + + FWFObjectHostApiImpl *hostApi = + [[FWFObjectHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + NSObject *observerObject = [[NSObject alloc] init]; + [instanceManager addInstance:observerObject withIdentifier:1]; + + FlutterError *error; + [hostApi removeObserverForObjectWithIdentifier:@0 + observerIdentifier:@1 + keyPath:@"myKey" + error:&error]; + OCMVerify([mockObject removeObserver:observerObject forKeyPath:@"myKey"]); + XCTAssertNil(error); +} + +- (void)testDispose { + NSObject *object = [[NSObject alloc] init]; + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:object withIdentifier:0]; + + FWFObjectHostApiImpl *hostApi = + [[FWFObjectHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi disposeObjectWithIdentifier:@0 error:&error]; + XCTAssertEqual([instanceManager identifierForInstance:object], NSNotFound); + XCTAssertNil(error); +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFPreferencesHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFPreferencesHostApiTests.m new file mode 100644 index 000000000000..1837a9373930 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFPreferencesHostApiTests.m @@ -0,0 +1,43 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import Flutter; +@import XCTest; +@import webview_flutter_wkwebview; + +#import + +@interface FWFPreferencesHostApiTests : XCTestCase +@end + +@implementation FWFPreferencesHostApiTests +- (void)testCreateFromWebViewConfigurationWithIdentifier { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + FWFPreferencesHostApiImpl *hostApi = + [[FWFPreferencesHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + [instanceManager addInstance:[[WKWebViewConfiguration alloc] init] withIdentifier:0]; + + FlutterError *error; + [hostApi createFromWebViewConfigurationWithIdentifier:@1 configurationIdentifier:@0 error:&error]; + WKPreferences *preferences = (WKPreferences *)[instanceManager instanceForIdentifier:1]; + XCTAssertTrue([preferences isKindOfClass:[WKPreferences class]]); + XCTAssertNil(error); +} + +- (void)testSetJavaScriptEnabled { + WKPreferences *mockPreferences = OCMClassMock([WKPreferences class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockPreferences withIdentifier:0]; + + FWFPreferencesHostApiImpl *hostApi = + [[FWFPreferencesHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi setJavaScriptEnabledForPreferencesWithIdentifier:@0 isEnabled:@YES error:&error]; + OCMVerify([mockPreferences setJavaScriptEnabled:YES]); + XCTAssertNil(error); +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScriptMessageHandlerHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScriptMessageHandlerHostApiTests.m new file mode 100644 index 000000000000..cb8348fc4702 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScriptMessageHandlerHostApiTests.m @@ -0,0 +1,29 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import Flutter; +@import XCTest; +@import webview_flutter_wkwebview; + +#import + +@interface FWFScriptMessageHandlerHostApiTests : XCTestCase +@end + +@implementation FWFScriptMessageHandlerHostApiTests +- (void)testCreateWithIdentifier { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + FWFScriptMessageHandlerHostApiImpl *hostApi = + [[FWFScriptMessageHandlerHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi createWithIdentifier:@0 error:&error]; + + FWFScriptMessageHandler *scriptMessageHandler = + (FWFScriptMessageHandler *)[instanceManager instanceForIdentifier:0]; + + XCTAssertTrue([scriptMessageHandler conformsToProtocol:@protocol(WKScriptMessageHandler)]); + XCTAssertNil(error); +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScrollViewHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScrollViewHostApiTests.m new file mode 100644 index 000000000000..87d17119e5e7 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScrollViewHostApiTests.m @@ -0,0 +1,64 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import Flutter; +@import XCTest; +@import webview_flutter_wkwebview; + +#import + +@interface FWFScrollViewHostApiTests : XCTestCase +@end + +@implementation FWFScrollViewHostApiTests +- (void)testGetContentOffset { + UIScrollView *mockScrollView = OCMClassMock([UIScrollView class]); + OCMStub([mockScrollView contentOffset]).andReturn(CGPointMake(1.0, 2.0)); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockScrollView withIdentifier:0]; + + FWFScrollViewHostApiImpl *hostApi = + [[FWFScrollViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + NSArray *expectedValue = @[ @1.0, @2.0 ]; + XCTAssertEqualObjects([hostApi contentOffsetForScrollViewWithIdentifier:@0 error:&error], + expectedValue); + XCTAssertNil(error); +} + +- (void)testScrollBy { + UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + scrollView.contentOffset = CGPointMake(1, 2); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:scrollView withIdentifier:0]; + + FWFScrollViewHostApiImpl *hostApi = + [[FWFScrollViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi scrollByForScrollViewWithIdentifier:@0 x:@1 y:@2 error:&error]; + XCTAssertEqual(scrollView.contentOffset.x, 2); + XCTAssertEqual(scrollView.contentOffset.y, 4); + XCTAssertNil(error); +} + +- (void)testSetContentOffset { + UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:scrollView withIdentifier:0]; + + FWFScrollViewHostApiImpl *hostApi = + [[FWFScrollViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi setContentOffsetForScrollViewWithIdentifier:@0 toX:@1 y:@2 error:&error]; + XCTAssertEqual(scrollView.contentOffset.x, 1); + XCTAssertEqual(scrollView.contentOffset.y, 2); + XCTAssertNil(error); +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIDelegateHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIDelegateHostApiTests.m new file mode 100644 index 000000000000..2f7838be5aa8 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIDelegateHostApiTests.m @@ -0,0 +1,27 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import Flutter; +@import XCTest; +@import webview_flutter_wkwebview; + +#import + +@interface FWFUIDelegateHostApiTests : XCTestCase +@end + +@implementation FWFUIDelegateHostApiTests +- (void)testCreateWithIdentifier { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + FWFUIDelegateHostApiImpl *hostApi = + [[FWFUIDelegateHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi createWithIdentifier:@0 error:&error]; + FWFUIDelegate *delegate = (FWFUIDelegate *)[instanceManager instanceForIdentifier:0]; + + XCTAssertTrue([delegate conformsToProtocol:@protocol(WKUIDelegate)]); + XCTAssertNil(error); +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIViewHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIViewHostApiTests.m new file mode 100644 index 000000000000..8c7c9c9b45b1 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIViewHostApiTests.m @@ -0,0 +1,49 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import Flutter; +@import XCTest; +@import webview_flutter_wkwebview; + +#import + +@interface FWFUIViewHostApiTests : XCTestCase +@end + +@implementation FWFUIViewHostApiTests +- (void)testSetBackgroundColor { + UIView *mockUIView = OCMClassMock([UIView class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockUIView withIdentifier:0]; + + FWFUIViewHostApiImpl *hostApi = + [[FWFUIViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi setBackgroundColorForViewWithIdentifier:@0 toValue:@123 error:&error]; + + OCMVerify([mockUIView setBackgroundColor:[UIColor colorWithRed:(123 >> 16 & 0xff) / 255.0 + green:(123 >> 8 & 0xff) / 255.0 + blue:(123 & 0xff) / 255.0 + alpha:(123 >> 24 & 0xff) / 255.0]]); + XCTAssertNil(error); +} + +- (void)testSetOpaque { + UIView *mockUIView = OCMClassMock([UIView class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockUIView withIdentifier:0]; + + FWFUIViewHostApiImpl *hostApi = + [[FWFUIViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi setOpaqueForViewWithIdentifier:@0 isOpaque:@YES error:&error]; + OCMVerify([mockUIView setOpaque:YES]); + XCTAssertNil(error); +} + +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUserContentControllerHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUserContentControllerHostApiTests.m new file mode 100644 index 000000000000..d70341e87890 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUserContentControllerHostApiTests.m @@ -0,0 +1,127 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import Flutter; +@import XCTest; +@import webview_flutter_wkwebview; + +#import + +@interface FWFUserContentControllerHostApiTests : XCTestCase +@end + +@implementation FWFUserContentControllerHostApiTests +- (void)testCreateFromWebViewConfigurationWithIdentifier { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + FWFUserContentControllerHostApiImpl *hostApi = + [[FWFUserContentControllerHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + [instanceManager addInstance:[[WKWebViewConfiguration alloc] init] withIdentifier:0]; + + FlutterError *error; + [hostApi createFromWebViewConfigurationWithIdentifier:@1 configurationIdentifier:@0 error:&error]; + WKUserContentController *userContentController = + (WKUserContentController *)[instanceManager instanceForIdentifier:1]; + XCTAssertTrue([userContentController isKindOfClass:[WKUserContentController class]]); + XCTAssertNil(error); +} + +- (void)testAddScriptMessageHandler { + WKUserContentController *mockUserContentController = + OCMClassMock([WKUserContentController class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockUserContentController withIdentifier:0]; + + FWFUserContentControllerHostApiImpl *hostApi = + [[FWFUserContentControllerHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + id mockMessageHandler = + OCMProtocolMock(@protocol(WKScriptMessageHandler)); + [instanceManager addInstance:mockMessageHandler withIdentifier:1]; + + FlutterError *error; + [hostApi addScriptMessageHandlerForControllerWithIdentifier:@0 + handlerIdentifier:@1 + ofName:@"apple" + error:&error]; + OCMVerify([mockUserContentController addScriptMessageHandler:mockMessageHandler name:@"apple"]); + XCTAssertNil(error); +} + +- (void)testRemoveScriptMessageHandler { + WKUserContentController *mockUserContentController = + OCMClassMock([WKUserContentController class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockUserContentController withIdentifier:0]; + + FWFUserContentControllerHostApiImpl *hostApi = + [[FWFUserContentControllerHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi removeScriptMessageHandlerForControllerWithIdentifier:@0 name:@"apple" error:&error]; + OCMVerify([mockUserContentController removeScriptMessageHandlerForName:@"apple"]); + XCTAssertNil(error); +} + +- (void)testRemoveAllScriptMessageHandlers API_AVAILABLE(ios(14.0)) { + WKUserContentController *mockUserContentController = + OCMClassMock([WKUserContentController class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockUserContentController withIdentifier:0]; + + FWFUserContentControllerHostApiImpl *hostApi = + [[FWFUserContentControllerHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi removeAllScriptMessageHandlersForControllerWithIdentifier:@0 error:&error]; + OCMVerify([mockUserContentController removeAllScriptMessageHandlers]); + XCTAssertNil(error); +} + +- (void)testAddUserScript { + WKUserContentController *mockUserContentController = + OCMClassMock([WKUserContentController class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockUserContentController withIdentifier:0]; + + FWFUserContentControllerHostApiImpl *hostApi = + [[FWFUserContentControllerHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi + addUserScriptForControllerWithIdentifier:@0 + userScript: + [FWFWKUserScriptData + makeWithSource:@"runAScript" + injectionTime: + [FWFWKUserScriptInjectionTimeEnumData + makeWithValue: + FWFWKUserScriptInjectionTimeEnumAtDocumentEnd] + isMainFrameOnly:@YES] + error:&error]; + + OCMVerify([mockUserContentController addUserScript:[OCMArg isKindOfClass:[WKUserScript class]]]); + XCTAssertNil(error); +} + +- (void)testRemoveAllUserScripts { + WKUserContentController *mockUserContentController = + OCMClassMock([WKUserContentController class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockUserContentController withIdentifier:0]; + + FWFUserContentControllerHostApiImpl *hostApi = + [[FWFUserContentControllerHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi removeAllUserScriptsForControllerWithIdentifier:@0 error:&error]; + OCMVerify([mockUserContentController removeAllUserScripts]); + XCTAssertNil(error); +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewConfigurationHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewConfigurationHostApiTests.m new file mode 100644 index 000000000000..dab10799891b --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewConfigurationHostApiTests.m @@ -0,0 +1,88 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import Flutter; +@import XCTest; +@import webview_flutter_wkwebview; + +#import + +@interface FWFWebViewConfigurationHostApiTests : XCTestCase +@end + +@implementation FWFWebViewConfigurationHostApiTests +- (void)testCreateWithIdentifier { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + FWFWebViewConfigurationHostApiImpl *hostApi = + [[FWFWebViewConfigurationHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi createWithIdentifier:@0 error:&error]; + WKWebViewConfiguration *configuration = + (WKWebViewConfiguration *)[instanceManager instanceForIdentifier:0]; + XCTAssertTrue([configuration isKindOfClass:[WKWebViewConfiguration class]]); + XCTAssertNil(error); +} + +- (void)testCreateFromWebViewWithIdentifier { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + FWFWebViewConfigurationHostApiImpl *hostApi = + [[FWFWebViewConfigurationHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + WKWebView *mockWebView = OCMClassMock([WKWebView class]); + OCMStub([mockWebView configuration]).andReturn(OCMClassMock([WKWebViewConfiguration class])); + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FlutterError *error; + [hostApi createFromWebViewWithIdentifier:@1 webViewIdentifier:@0 error:&error]; + WKWebViewConfiguration *configuration = + (WKWebViewConfiguration *)[instanceManager instanceForIdentifier:1]; + XCTAssertTrue([configuration isKindOfClass:[WKWebViewConfiguration class]]); + XCTAssertNil(error); +} + +- (void)testSetAllowsInlineMediaPlayback { + WKWebViewConfiguration *mockWebViewConfiguration = OCMClassMock([WKWebViewConfiguration class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebViewConfiguration withIdentifier:0]; + + FWFWebViewConfigurationHostApiImpl *hostApi = + [[FWFWebViewConfigurationHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:@0 + isAllowed:@NO + error:&error]; + OCMVerify([mockWebViewConfiguration setAllowsInlineMediaPlayback:NO]); + XCTAssertNil(error); +} + +- (void)testSetMediaTypesRequiringUserActionForPlayback { + WKWebViewConfiguration *mockWebViewConfiguration = OCMClassMock([WKWebViewConfiguration class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebViewConfiguration withIdentifier:0]; + + FWFWebViewConfigurationHostApiImpl *hostApi = + [[FWFWebViewConfigurationHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi + setMediaTypesRequiresUserActionForConfigurationWithIdentifier:@0 + forTypes:@[ + [FWFWKAudiovisualMediaTypeEnumData + makeWithValue: + FWFWKAudiovisualMediaTypeEnumAudio], + [FWFWKAudiovisualMediaTypeEnumData + makeWithValue: + FWFWKAudiovisualMediaTypeEnumVideo] + ] + error:&error]; + OCMVerify([mockWebViewConfiguration + setMediaTypesRequiringUserActionForPlayback:(WKAudiovisualMediaTypeAudio | + WKAudiovisualMediaTypeVideo)]); + XCTAssertNil(error); +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m index dc0c1ce593d2..87960c07ee5e 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m @@ -12,6 +12,20 @@ @interface FWFWebViewHostApiTests : XCTestCase @end @implementation FWFWebViewHostApiTests +- (void)testCreateWithIdentifier { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + [instanceManager addInstance:[[WKWebViewConfiguration alloc] init] withIdentifier:0]; + + FlutterError *error; + [hostApi createWithIdentifier:@1 configurationIdentifier:@0 error:&error]; + WKWebView *webView = (WKWebView *)[instanceManager instanceForIdentifier:1]; + XCTAssertTrue([webView isKindOfClass:[WKWebView class]]); + XCTAssertNil(error); +} + - (void)testLoadRequest { FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebsiteDataStoreHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebsiteDataStoreHostApiTests.m new file mode 100644 index 000000000000..c754f78551b9 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebsiteDataStoreHostApiTests.m @@ -0,0 +1,75 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import Flutter; +@import XCTest; +@import webview_flutter_wkwebview; + +#import + +@interface FWFWebsiteDataStoreHostApiTests : XCTestCase +@end + +@implementation FWFWebsiteDataStoreHostApiTests +- (void)testCreateFromWebViewConfigurationWithIdentifier { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + FWFWebsiteDataStoreHostApiImpl *hostApi = + [[FWFWebsiteDataStoreHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + [instanceManager addInstance:[[WKWebViewConfiguration alloc] init] withIdentifier:0]; + + FlutterError *error; + [hostApi createFromWebViewConfigurationWithIdentifier:@1 configurationIdentifier:@0 error:&error]; + WKWebsiteDataStore *dataStore = (WKWebsiteDataStore *)[instanceManager instanceForIdentifier:1]; + XCTAssertTrue([dataStore isKindOfClass:[WKWebsiteDataStore class]]); + XCTAssertNil(error); +} + +- (void)testCreateDefaultDataStoreWithIdentifier { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + FWFWebsiteDataStoreHostApiImpl *hostApi = + [[FWFWebsiteDataStoreHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi createDefaultDataStoreWithIdentifier:@0 error:&error]; + WKWebsiteDataStore *dataStore = (WKWebsiteDataStore *)[instanceManager instanceForIdentifier:0]; + XCTAssertEqualObjects(dataStore, [WKWebsiteDataStore defaultDataStore]); + XCTAssertNil(error); +} + +- (void)testRemoveDataOfTypes { + WKWebsiteDataStore *mockWebsiteDataStore = OCMClassMock([WKWebsiteDataStore class]); + + WKWebsiteDataRecord *mockDataRecord = OCMClassMock([WKWebsiteDataRecord class]); + OCMStub([mockWebsiteDataStore + fetchDataRecordsOfTypes:[NSSet setWithObject:WKWebsiteDataTypeLocalStorage] + completionHandler:([OCMArg invokeBlockWithArgs:@[ mockDataRecord ], nil])]); + + OCMStub([mockWebsiteDataStore + removeDataOfTypes:[NSSet setWithObject:WKWebsiteDataTypeLocalStorage] + modifiedSince:[NSDate dateWithTimeIntervalSince1970:45.0] + completionHandler:([OCMArg invokeBlock])]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebsiteDataStore withIdentifier:0]; + + FWFWebsiteDataStoreHostApiImpl *hostApi = + [[FWFWebsiteDataStoreHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + NSNumber __block *returnValue; + FlutterError *__block blockError; + [hostApi removeDataFromDataStoreWithIdentifier:@0 + ofTypes:@[ + [FWFWKWebsiteDataTypeEnumData + makeWithValue:FWFWKWebsiteDataTypeEnumLocalStorage] + ] + modifiedSince:@45.0 + completion:^(NSNumber *result, FlutterError *error) { + returnValue = result; + blockError = error; + }]; + XCTAssertEqualObjects(returnValue, @YES); + XCTAssertNil(blockError); +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.h index e9dbf2d9efa7..4cf24c67ac95 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.h @@ -4,6 +4,8 @@ #import "FWFGeneratedWebKitApis.h" +#import + NS_ASSUME_NONNULL_BEGIN /** @@ -13,7 +15,74 @@ NS_ASSUME_NONNULL_BEGIN * * @return An NSURLRequest or nil if data could not be converted. */ -extern NSURLRequest* _Nullable FWFNSURLRequestFromRequestData( - FWFNSUrlRequestData* data); +extern NSURLRequest *_Nullable FWFNSURLRequestFromRequestData(FWFNSUrlRequestData *data); + +/** + * Converts an FWFNSHttpCookieData to an NSHTTPCookie. + * + * @param data The data object containing information to create an NSHTTPCookie. + * + * @return An NSHTTPCookie or nil if data could not be converted. + */ +extern NSHTTPCookie *_Nullable FWFNSHTTPCookieFromCookieData(FWFNSHttpCookieData *data); + +/** + * Converts an FWFNSKeyValueObservingOptionsEnumData to an NSKeyValueObservingOptions. + * + * @param data The data object containing information to create an NSKeyValueObservingOptions. + * + * @return An NSKeyValueObservingOptions or -1 if data could not be converted. + */ +extern NSKeyValueObservingOptions FWFNSKeyValueObservingOptionsFromEnumData( + FWFNSKeyValueObservingOptionsEnumData *data); + +/** + * Converts an FWFNSHTTPCookiePropertyKeyEnumData to an NSHTTPCookiePropertyKey. + * + * @param data The data object containing information to create an NSHTTPCookiePropertyKey. + * + * @return An NSHttpCookiePropertyKey or nil if data could not be converted. + */ +extern NSHTTPCookiePropertyKey _Nullable FWFNSHTTPCookiePropertyKeyFromEnumData( + FWFNSHttpCookiePropertyKeyEnumData *data); + +/** + * Converts a WKUserScriptData to a WKUserScript. + * + * @param data The data object containing information to create a WKUserScript. + * + * @return A WKUserScript or nil if data could not be converted. + */ +extern WKUserScript *FWFWKUserScriptFromScriptData(FWFWKUserScriptData *data); + +/** + * Converts an FWFWKUserScriptInjectionTimeEnumData to a WKUserScriptInjectionTime. + * + * @param data The data object containing information to create a WKUserScriptInjectionTime. + * + * @return A WKUserScriptInjectionTime or -1 if data could not be converted. + */ +extern WKUserScriptInjectionTime FWFWKUserScriptInjectionTimeFromEnumData( + FWFWKUserScriptInjectionTimeEnumData *data); + +/** + * Converts an FWFWKAudiovisualMediaTypeEnumData to a WKAudiovisualMediaTypes. + * + * @param data The data object containing information to create a WKAudiovisualMediaTypes. + * + * @return A WKAudiovisualMediaType or -1 if data could not be converted. + */ +API_AVAILABLE(ios(10.0)) +extern WKAudiovisualMediaTypes FWFWKAudiovisualMediaTypeFromEnumData( + FWFWKAudiovisualMediaTypeEnumData *data); + +/** + * Converts an FWFWKWebsiteDataTypeEnumData to a WKWebsiteDataType. + * + * @param data The data object containing information to create a WKWebsiteDataType. + * + * @return A WKWebsiteDataType or nil if data could not be converted. + */ +extern NSString *_Nullable FWFWKWebsiteDataTypeFromEnumData(FWFWKWebsiteDataTypeEnumData *data); NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m index 1945bc3354f3..a06b3d79b38c 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m @@ -23,3 +23,133 @@ return request; } + +extern NSHTTPCookie *_Nullable FWFNSHTTPCookieFromCookieData(FWFNSHttpCookieData *data) { + NSMutableDictionary *properties = [NSMutableDictionary dictionary]; + for (int i = 0; i < data.propertyKeys.count; i++) { + NSHTTPCookiePropertyKey cookieKey = + FWFNSHTTPCookiePropertyKeyFromEnumData(data.propertyKeys[i]); + if (!cookieKey) { + // Some keys aren't supported on all versions, so this ignores keys + // that require a higher version or are unsupported. + continue; + } + [properties setObject:data.propertyValues[i] forKey:cookieKey]; + } + return [NSHTTPCookie cookieWithProperties:properties]; +} + +NSKeyValueObservingOptions FWFNSKeyValueObservingOptionsFromEnumData( + FWFNSKeyValueObservingOptionsEnumData *data) { + switch (data.value) { + case FWFNSKeyValueObservingOptionsEnumNewValue: + return NSKeyValueObservingOptionNew; + case FWFNSKeyValueObservingOptionsEnumOldValue: + return NSKeyValueObservingOptionOld; + case FWFNSKeyValueObservingOptionsEnumInitialValue: + return NSKeyValueObservingOptionInitial; + case FWFNSKeyValueObservingOptionsEnumPriorNotification: + return NSKeyValueObservingOptionPrior; + } + + return -1; +} + +NSHTTPCookiePropertyKey _Nullable FWFNSHTTPCookiePropertyKeyFromEnumData( + FWFNSHttpCookiePropertyKeyEnumData *data) { + switch (data.value) { + case FWFNSHttpCookiePropertyKeyEnumComment: + return NSHTTPCookieComment; + case FWFNSHttpCookiePropertyKeyEnumCommentUrl: + return NSHTTPCookieCommentURL; + case FWFNSHttpCookiePropertyKeyEnumDiscard: + return NSHTTPCookieDiscard; + case FWFNSHttpCookiePropertyKeyEnumDomain: + return NSHTTPCookieDomain; + case FWFNSHttpCookiePropertyKeyEnumExpires: + return NSHTTPCookieExpires; + case FWFNSHttpCookiePropertyKeyEnumMaximumAge: + return NSHTTPCookieMaximumAge; + case FWFNSHttpCookiePropertyKeyEnumName: + return NSHTTPCookieName; + case FWFNSHttpCookiePropertyKeyEnumOriginUrl: + return NSHTTPCookieOriginURL; + case FWFNSHttpCookiePropertyKeyEnumPath: + return NSHTTPCookiePath; + case FWFNSHttpCookiePropertyKeyEnumPort: + return NSHTTPCookiePort; + case FWFNSHttpCookiePropertyKeyEnumSameSitePolicy: + if (@available(iOS 13.0, *)) { + return NSHTTPCookieSameSitePolicy; + } else { + return nil; + } + case FWFNSHttpCookiePropertyKeyEnumSecure: + return NSHTTPCookieSecure; + case FWFNSHttpCookiePropertyKeyEnumValue: + return NSHTTPCookieValue; + case FWFNSHttpCookiePropertyKeyEnumVersion: + return NSHTTPCookieVersion; + } + + return nil; +} + +extern WKUserScript *FWFWKUserScriptFromScriptData(FWFWKUserScriptData *data) { + return [[WKUserScript alloc] + initWithSource:data.source + injectionTime:FWFWKUserScriptInjectionTimeFromEnumData(data.injectionTime) + forMainFrameOnly:data.isMainFrameOnly.boolValue]; +} + +WKUserScriptInjectionTime FWFWKUserScriptInjectionTimeFromEnumData( + FWFWKUserScriptInjectionTimeEnumData *data) { + switch (data.value) { + case FWFWKUserScriptInjectionTimeEnumAtDocumentStart: + return WKUserScriptInjectionTimeAtDocumentStart; + case FWFWKUserScriptInjectionTimeEnumAtDocumentEnd: + return WKUserScriptInjectionTimeAtDocumentEnd; + } + + return -1; +} + +API_AVAILABLE(ios(10.0)) +WKAudiovisualMediaTypes FWFWKAudiovisualMediaTypeFromEnumData( + FWFWKAudiovisualMediaTypeEnumData *data) { + switch (data.value) { + case FWFWKAudiovisualMediaTypeEnumNone: + return WKAudiovisualMediaTypeNone; + case FWFWKAudiovisualMediaTypeEnumAudio: + return WKAudiovisualMediaTypeAudio; + case FWFWKAudiovisualMediaTypeEnumVideo: + return WKAudiovisualMediaTypeVideo; + case FWFWKAudiovisualMediaTypeEnumAll: + return WKAudiovisualMediaTypeAll; + } + + return -1; +} + +NSString *_Nullable FWFWKWebsiteDataTypeFromEnumData(FWFWKWebsiteDataTypeEnumData *data) { + switch (data.value) { + case FWFWKWebsiteDataTypeEnumCookies: + return WKWebsiteDataTypeCookies; + case FWFWKWebsiteDataTypeEnumMemoryCache: + return WKWebsiteDataTypeMemoryCache; + case FWFWKWebsiteDataTypeEnumDiskCache: + return WKWebsiteDataTypeDiskCache; + case FWFWKWebsiteDataTypeEnumOfflineWebApplicationCache: + return WKWebsiteDataTypeOfflineWebApplicationCache; + case FWFWKWebsiteDataTypeEnumLocalStorage: + return WKWebsiteDataTypeLocalStorage; + case FWFWKWebsiteDataTypeEnumSessionStorage: + return WKWebsiteDataTypeSessionStorage; + case FWFWKWebsiteDataTypeEnumWebSQLDatabases: + return WKWebsiteDataTypeWebSQLDatabases; + case FWFWKWebsiteDataTypeEnumIndexedDBDatabases: + return WKWebsiteDataTypeIndexedDBDatabases; + } + + return nil; +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h index 5306b300fc03..7d2bd44050ea 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h @@ -45,15 +45,15 @@ typedef NS_ENUM(NSUInteger, FWFWKAudiovisualMediaTypeEnum) { FWFWKAudiovisualMediaTypeEnumAll = 3, }; -typedef NS_ENUM(NSUInteger, FWFWKWebsiteDataTypesEnum) { - FWFWKWebsiteDataTypesEnumCookies = 0, - FWFWKWebsiteDataTypesEnumMemoryCache = 1, - FWFWKWebsiteDataTypesEnumDiskCache = 2, - FWFWKWebsiteDataTypesEnumOfflineWebApplicationCache = 3, - FWFWKWebsiteDataTypesEnumLocalStroage = 4, - FWFWKWebsiteDataTypesEnumSessionStorage = 5, - FWFWKWebsiteDataTypesEnumSqlDatabases = 6, - FWFWKWebsiteDataTypesEnumIndexedDBDatabases = 7, +typedef NS_ENUM(NSUInteger, FWFWKWebsiteDataTypeEnum) { + FWFWKWebsiteDataTypeEnumCookies = 0, + FWFWKWebsiteDataTypeEnumMemoryCache = 1, + FWFWKWebsiteDataTypeEnumDiskCache = 2, + FWFWKWebsiteDataTypeEnumOfflineWebApplicationCache = 3, + FWFWKWebsiteDataTypeEnumLocalStorage = 4, + FWFWKWebsiteDataTypeEnumSessionStorage = 5, + FWFWKWebsiteDataTypeEnumWebSQLDatabases = 6, + FWFWKWebsiteDataTypeEnumIndexedDBDatabases = 7, }; typedef NS_ENUM(NSUInteger, FWFWKNavigationActionPolicyEnum) { @@ -81,33 +81,43 @@ typedef NS_ENUM(NSUInteger, FWFNSHttpCookiePropertyKeyEnum) { @class FWFNSKeyValueObservingOptionsEnumData; @class FWFWKUserScriptInjectionTimeEnumData; @class FWFWKAudiovisualMediaTypeEnumData; -@class FWFWKWebsiteDataTypesEnumData; +@class FWFWKWebsiteDataTypeEnumData; @class FWFNSHttpCookiePropertyKeyEnumData; @class FWFNSUrlRequestData; @class FWFWKUserScriptData; @class FWFNSHttpCookieData; @interface FWFNSKeyValueObservingOptionsEnumData : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithValue:(FWFNSKeyValueObservingOptionsEnum)value; @property(nonatomic, assign) FWFNSKeyValueObservingOptionsEnum value; @end @interface FWFWKUserScriptInjectionTimeEnumData : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithValue:(FWFWKUserScriptInjectionTimeEnum)value; @property(nonatomic, assign) FWFWKUserScriptInjectionTimeEnum value; @end @interface FWFWKAudiovisualMediaTypeEnumData : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithValue:(FWFWKAudiovisualMediaTypeEnum)value; @property(nonatomic, assign) FWFWKAudiovisualMediaTypeEnum value; @end -@interface FWFWKWebsiteDataTypesEnumData : NSObject -+ (instancetype)makeWithValue:(FWFWKWebsiteDataTypesEnum)value; -@property(nonatomic, assign) FWFWKWebsiteDataTypesEnum value; +@interface FWFWKWebsiteDataTypeEnumData : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithValue:(FWFWKWebsiteDataTypeEnum)value; +@property(nonatomic, assign) FWFWKWebsiteDataTypeEnum value; @end @interface FWFNSHttpCookiePropertyKeyEnumData : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithValue:(FWFNSHttpCookiePropertyKeyEnum)value; @property(nonatomic, assign) FWFNSHttpCookiePropertyKeyEnum value; @end @@ -139,24 +149,24 @@ typedef NS_ENUM(NSUInteger, FWFNSHttpCookiePropertyKeyEnum) { @interface FWFNSHttpCookieData : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithProperties: - (NSDictionary *)properties; -@property(nonatomic, strong) - NSDictionary *properties; ++ (instancetype)makeWithPropertyKeys:(NSArray *)propertyKeys + propertyValues:(NSArray *)propertyValues; +@property(nonatomic, strong) NSArray *propertyKeys; +@property(nonatomic, strong) NSArray *propertyValues; @end /// The codec used by FWFWKWebsiteDataStoreHostApi. NSObject *FWFWKWebsiteDataStoreHostApiGetCodec(void); @protocol FWFWKWebsiteDataStoreHostApi -- (void)createDataStoreFromConfigurationWithIdentifier:(NSNumber *)instanceId - configurationIdentifier:(NSNumber *)configurationInstanceId - error:(FlutterError *_Nullable *_Nonnull)error; +- (void)createFromWebViewConfigurationWithIdentifier:(NSNumber *)instanceId + configurationIdentifier:(NSNumber *)configurationInstanceId + error:(FlutterError *_Nullable *_Nonnull)error; - (void)createDefaultDataStoreWithIdentifier:(NSNumber *)instanceId error:(FlutterError *_Nullable *_Nonnull)error; - (void)removeDataFromDataStoreWithIdentifier:(NSNumber *)instanceId - ofTypes:(NSArray *)dataTypes - secondsModifiedSinceEpoch:(NSNumber *)secondsModifiedSinceEpoch + ofTypes:(NSArray *)dataTypes + modifiedSince:(NSNumber *)modificationTimeInSecondsSinceEpoch completion:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion; @end @@ -169,10 +179,6 @@ extern void FWFWKWebsiteDataStoreHostApiSetup( NSObject *FWFUIViewHostApiGetCodec(void); @protocol FWFUIViewHostApi -/// @return `nil` only when `error != nil`. -- (nullable NSArray *) - contentOffsetForViewWithIdentifier:(NSNumber *)instanceId - error:(FlutterError *_Nullable *_Nonnull)error; - (void)setBackgroundColorForViewWithIdentifier:(NSNumber *)instanceId toValue:(nullable NSNumber *)value error:(FlutterError *_Nullable *_Nonnull)error; @@ -196,7 +202,7 @@ NSObject *FWFUIScrollViewHostApiGetCodec(void); contentOffsetForScrollViewWithIdentifier:(NSNumber *)instanceId error:(FlutterError *_Nullable *_Nonnull)error; - (void)scrollByForScrollViewWithIdentifier:(NSNumber *)instanceId - toX:(NSNumber *)x + x:(NSNumber *)x y:(NSNumber *)y error:(FlutterError *_Nullable *_Nonnull)error; - (void)setContentOffsetForScrollViewWithIdentifier:(NSNumber *)instanceId @@ -217,7 +223,7 @@ NSObject *FWFWKWebViewConfigurationHostApiGetCodec(void); webViewIdentifier:(NSNumber *)webViewInstanceId error:(FlutterError *_Nullable *_Nonnull)error; - (void)setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:(NSNumber *)instanceId - isAlowed:(NSNumber *)allow + isAllowed:(NSNumber *)allow error: (FlutterError *_Nullable *_Nonnull) error; @@ -270,9 +276,9 @@ extern void FWFWKUserContentControllerHostApiSetup( NSObject *FWFWKPreferencesHostApiGetCodec(void); @protocol FWFWKPreferencesHostApi -- (void)createFromWebViewConfiguration:(NSNumber *)instanceId - configurationIdentifier:(NSNumber *)configurationInstanceId - error:(FlutterError *_Nullable *_Nonnull)error; +- (void)createFromWebViewConfigurationWithIdentifier:(NSNumber *)instanceId + configurationIdentifier:(NSNumber *)configurationInstanceId + error:(FlutterError *_Nullable *_Nonnull)error; - (void)setJavaScriptEnabledForPreferencesWithIdentifier:(NSNumber *)instanceId isEnabled:(NSNumber *)enabled error:(FlutterError *_Nullable *_Nonnull)error; @@ -428,7 +434,7 @@ NSObject *FWFWKHttpCookieStoreHostApiGetCodec(void); error:(FlutterError *_Nullable *_Nonnull)error; - (void)setCookieForStoreWithIdentifier:(NSNumber *)instanceId cookie:(FWFNSHttpCookieData *)cookie - error:(FlutterError *_Nullable *_Nonnull)error; + completion:(void (^)(FlutterError *_Nullable))completion; @end extern void FWFWKHttpCookieStoreHostApiSetup(id binaryMessenger, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m index 070b3c5cc033..aaeccc5cc1b0 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m @@ -45,8 +45,8 @@ @interface FWFWKAudiovisualMediaTypeEnumData () + (FWFWKAudiovisualMediaTypeEnumData *)fromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end -@interface FWFWKWebsiteDataTypesEnumData () -+ (FWFWKWebsiteDataTypesEnumData *)fromMap:(NSDictionary *)dict; +@interface FWFWKWebsiteDataTypeEnumData () ++ (FWFWKWebsiteDataTypeEnumData *)fromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @interface FWFNSHttpCookiePropertyKeyEnumData () @@ -120,14 +120,14 @@ - (NSDictionary *)toMap { } @end -@implementation FWFWKWebsiteDataTypesEnumData -+ (instancetype)makeWithValue:(FWFWKWebsiteDataTypesEnum)value { - FWFWKWebsiteDataTypesEnumData *pigeonResult = [[FWFWKWebsiteDataTypesEnumData alloc] init]; +@implementation FWFWKWebsiteDataTypeEnumData ++ (instancetype)makeWithValue:(FWFWKWebsiteDataTypeEnum)value { + FWFWKWebsiteDataTypeEnumData *pigeonResult = [[FWFWKWebsiteDataTypeEnumData alloc] init]; pigeonResult.value = value; return pigeonResult; } -+ (FWFWKWebsiteDataTypesEnumData *)fromMap:(NSDictionary *)dict { - FWFWKWebsiteDataTypesEnumData *pigeonResult = [[FWFWKWebsiteDataTypesEnumData alloc] init]; ++ (FWFWKWebsiteDataTypeEnumData *)fromMap:(NSDictionary *)dict { + FWFWKWebsiteDataTypeEnumData *pigeonResult = [[FWFWKWebsiteDataTypeEnumData alloc] init]; pigeonResult.value = [GetNullableObject(dict, @"value") integerValue]; return pigeonResult; } @@ -220,22 +220,27 @@ - (NSDictionary *)toMap { @end @implementation FWFNSHttpCookieData -+ (instancetype)makeWithProperties: - (NSDictionary *)properties { ++ (instancetype)makeWithPropertyKeys:(NSArray *)propertyKeys + propertyValues:(NSArray *)propertyValues { FWFNSHttpCookieData *pigeonResult = [[FWFNSHttpCookieData alloc] init]; - pigeonResult.properties = properties; + pigeonResult.propertyKeys = propertyKeys; + pigeonResult.propertyValues = propertyValues; return pigeonResult; } + (FWFNSHttpCookieData *)fromMap:(NSDictionary *)dict { FWFNSHttpCookieData *pigeonResult = [[FWFNSHttpCookieData alloc] init]; - pigeonResult.properties = GetNullableObject(dict, @"properties"); - NSAssert(pigeonResult.properties != nil, @""); + pigeonResult.propertyKeys = GetNullableObject(dict, @"propertyKeys"); + NSAssert(pigeonResult.propertyKeys != nil, @""); + pigeonResult.propertyValues = GetNullableObject(dict, @"propertyValues"); + NSAssert(pigeonResult.propertyValues != nil, @""); return pigeonResult; } - (NSDictionary *)toMap { return [NSDictionary - dictionaryWithObjectsAndKeys:(self.properties ? self.properties : [NSNull null]), - @"properties", nil]; + dictionaryWithObjectsAndKeys:(self.propertyKeys ? self.propertyKeys : [NSNull null]), + @"propertyKeys", + (self.propertyValues ? self.propertyValues : [NSNull null]), + @"propertyValues", nil]; } @end @@ -245,7 +250,7 @@ @implementation FWFWKWebsiteDataStoreHostApiCodecReader - (nullable id)readValueOfType:(UInt8)type { switch (type) { case 128: - return [FWFWKWebsiteDataTypesEnumData fromMap:[self readValue]]; + return [FWFWKWebsiteDataTypeEnumData fromMap:[self readValue]]; default: return [super readValueOfType:type]; @@ -257,7 +262,7 @@ @interface FWFWKWebsiteDataStoreHostApiCodecWriter : FlutterStandardWriter @end @implementation FWFWKWebsiteDataStoreHostApiCodecWriter - (void)writeValue:(id)value { - if ([value isKindOfClass:[FWFWKWebsiteDataTypesEnumData class]]) { + if ([value isKindOfClass:[FWFWKWebsiteDataTypeEnumData class]]) { [self writeByte:128]; [self writeValue:[value toMap]]; } else { @@ -298,20 +303,19 @@ void FWFWKWebsiteDataStoreHostApiSetup(id binaryMessenge codec:FWFWKWebsiteDataStoreHostApiGetCodec()]; if (api) { NSCAssert( - [api respondsToSelector:@selector(createDataStoreFromConfigurationWithIdentifier: - configurationIdentifier:error:)], + [api respondsToSelector:@selector(createFromWebViewConfigurationWithIdentifier: + configurationIdentifier:error:)], @"FWFWKWebsiteDataStoreHostApi api (%@) doesn't respond to " - @"@selector(createDataStoreFromConfigurationWithIdentifier:configurationIdentifier:error:" - @")", + @"@selector(createFromWebViewConfigurationWithIdentifier:configurationIdentifier:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); NSNumber *arg_configurationInstanceId = GetNullableObjectAtIndex(args, 1); FlutterError *error; - [api createDataStoreFromConfigurationWithIdentifier:arg_instanceId - configurationIdentifier:arg_configurationInstanceId - error:&error]; + [api createFromWebViewConfigurationWithIdentifier:arg_instanceId + configurationIdentifier:arg_configurationInstanceId + error:&error]; callback(wrapResult(nil, error)); }]; } else { @@ -345,21 +349,20 @@ void FWFWKWebsiteDataStoreHostApiSetup(id binaryMessenge binaryMessenger:binaryMessenger codec:FWFWKWebsiteDataStoreHostApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector - (removeDataFromDataStoreWithIdentifier: - ofTypes:secondsModifiedSinceEpoch:completion:)], - @"FWFWKWebsiteDataStoreHostApi api (%@) doesn't respond to " - @"@selector(removeDataFromDataStoreWithIdentifier:ofTypes:" - @"secondsModifiedSinceEpoch:completion:)", - api); + NSCAssert( + [api respondsToSelector:@selector + (removeDataFromDataStoreWithIdentifier:ofTypes:modifiedSince:completion:)], + @"FWFWKWebsiteDataStoreHostApi api (%@) doesn't respond to " + @"@selector(removeDataFromDataStoreWithIdentifier:ofTypes:modifiedSince:completion:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); - NSArray *arg_dataTypes = GetNullableObjectAtIndex(args, 1); - NSNumber *arg_secondsModifiedSinceEpoch = GetNullableObjectAtIndex(args, 2); + NSArray *arg_dataTypes = GetNullableObjectAtIndex(args, 1); + NSNumber *arg_modificationTimeInSecondsSinceEpoch = GetNullableObjectAtIndex(args, 2); [api removeDataFromDataStoreWithIdentifier:arg_instanceId ofTypes:arg_dataTypes - secondsModifiedSinceEpoch:arg_secondsModifiedSinceEpoch + modifiedSince:arg_modificationTimeInSecondsSinceEpoch completion:^(NSNumber *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); @@ -404,28 +407,6 @@ - (FlutterStandardReader *)readerWithData:(NSData *)data { void FWFUIViewHostApiSetup(id binaryMessenger, NSObject *api) { - { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.UIViewHostApi.getContentOffset" - binaryMessenger:binaryMessenger - codec:FWFUIViewHostApiGetCodec()]; - if (api) { - NSCAssert([api respondsToSelector:@selector(contentOffsetForViewWithIdentifier:error:)], - @"FWFUIViewHostApi api (%@) doesn't respond to " - @"@selector(contentOffsetForViewWithIdentifier:error:)", - api); - [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); - FlutterError *error; - NSArray *output = [api contentOffsetForViewWithIdentifier:arg_instanceId - error:&error]; - callback(wrapResult(output, error)); - }]; - } else { - [channel setMessageHandler:nil]; - } - } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] initWithName:@"dev.flutter.pigeon.UIViewHostApi.setBackgroundColor" @@ -559,10 +540,9 @@ void FWFUIScrollViewHostApiSetup(id binaryMessenger, binaryMessenger:binaryMessenger codec:FWFUIScrollViewHostApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(scrollByForScrollViewWithIdentifier: - toX:y:error:)], + NSCAssert([api respondsToSelector:@selector(scrollByForScrollViewWithIdentifier:x:y:error:)], @"FWFUIScrollViewHostApi api (%@) doesn't respond to " - @"@selector(scrollByForScrollViewWithIdentifier:toX:y:error:)", + @"@selector(scrollByForScrollViewWithIdentifier:x:y:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; @@ -570,7 +550,7 @@ void FWFUIScrollViewHostApiSetup(id binaryMessenger, NSNumber *arg_x = GetNullableObjectAtIndex(args, 1); NSNumber *arg_y = GetNullableObjectAtIndex(args, 2); FlutterError *error; - [api scrollByForScrollViewWithIdentifier:arg_instanceId toX:arg_x y:arg_y error:&error]; + [api scrollByForScrollViewWithIdentifier:arg_instanceId x:arg_x y:arg_y error:&error]; callback(wrapResult(nil, error)); }]; } else { @@ -711,9 +691,9 @@ void FWFWKWebViewConfigurationHostApiSetup(id binaryMess if (api) { NSCAssert( [api respondsToSelector:@selector - (setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:isAlowed:error:)], + (setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:isAllowed:error:)], @"FWFWKWebViewConfigurationHostApi api (%@) doesn't respond to " - @"@selector(setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:isAlowed:error:)", + @"@selector(setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:isAllowed:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; @@ -721,7 +701,7 @@ void FWFWKWebViewConfigurationHostApiSetup(id binaryMess NSNumber *arg_allow = GetNullableObjectAtIndex(args, 1); FlutterError *error; [api setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:arg_instanceId - isAlowed:arg_allow + isAllowed:arg_allow error:&error]; callback(wrapResult(nil, error)); }]; @@ -1008,19 +988,20 @@ void FWFWKPreferencesHostApiSetup(id binaryMessenger, binaryMessenger:binaryMessenger codec:FWFWKPreferencesHostApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(createFromWebViewConfiguration: - configurationIdentifier:error:)], - @"FWFWKPreferencesHostApi api (%@) doesn't respond to " - @"@selector(createFromWebViewConfiguration:configurationIdentifier:error:)", - api); + NSCAssert( + [api respondsToSelector:@selector(createFromWebViewConfigurationWithIdentifier: + configurationIdentifier:error:)], + @"FWFWKPreferencesHostApi api (%@) doesn't respond to " + @"@selector(createFromWebViewConfigurationWithIdentifier:configurationIdentifier:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); NSNumber *arg_configurationInstanceId = GetNullableObjectAtIndex(args, 1); FlutterError *error; - [api createFromWebViewConfiguration:arg_instanceId - configurationIdentifier:arg_configurationInstanceId - error:&error]; + [api createFromWebViewConfigurationWithIdentifier:arg_instanceId + configurationIdentifier:arg_configurationInstanceId + error:&error]; callback(wrapResult(nil, error)); }]; } else { @@ -1472,7 +1453,7 @@ - (nullable id)readValueOfType:(UInt8)type { return [FWFWKUserScriptInjectionTimeEnumData fromMap:[self readValue]]; case 135: - return [FWFWKWebsiteDataTypesEnumData fromMap:[self readValue]]; + return [FWFWKWebsiteDataTypeEnumData fromMap:[self readValue]]; default: return [super readValueOfType:type]; @@ -1505,7 +1486,7 @@ - (void)writeValue:(id)value { } else if ([value isKindOfClass:[FWFWKUserScriptInjectionTimeEnumData class]]) { [self writeByte:134]; [self writeValue:[value toMap]]; - } else if ([value isKindOfClass:[FWFWKWebsiteDataTypesEnumData class]]) { + } else if ([value isKindOfClass:[FWFWKWebsiteDataTypeEnumData class]]) { [self writeByte:135]; [self writeValue:[value toMap]]; } else { @@ -2107,17 +2088,20 @@ void FWFWKHttpCookieStoreHostApiSetup(id binaryMessenger binaryMessenger:binaryMessenger codec:FWFWKHttpCookieStoreHostApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(setCookieForStoreWithIdentifier:cookie:error:)], + NSCAssert([api respondsToSelector:@selector(setCookieForStoreWithIdentifier: + cookie:completion:)], @"FWFWKHttpCookieStoreHostApi api (%@) doesn't respond to " - @"@selector(setCookieForStoreWithIdentifier:cookie:error:)", + @"@selector(setCookieForStoreWithIdentifier:cookie:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); FWFNSHttpCookieData *arg_cookie = GetNullableObjectAtIndex(args, 1); - FlutterError *error; - [api setCookieForStoreWithIdentifier:arg_instanceId cookie:arg_cookie error:&error]; - callback(wrapResult(nil, error)); + [api setCookieForStoreWithIdentifier:arg_instanceId + cookie:arg_cookie + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; }]; } else { [channel setMessageHandler:nil]; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFHTTPCookieStoreHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFHTTPCookieStoreHostApi.h new file mode 100644 index 000000000000..887c9f1b3d8b --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFHTTPCookieStoreHostApi.h @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import + +#import "FWFGeneratedWebKitApis.h" +#import "FWFInstanceManager.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Host api implementation for WKHTTPCookieStore. + * + * Handles creating WKHTTPCookieStore that intercommunicate with a paired Dart object. + */ +@interface FWFHTTPCookieStoreHostApiImpl : NSObject +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFHTTPCookieStoreHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFHTTPCookieStoreHostApi.m new file mode 100644 index 000000000000..6ebd60a4ac40 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFHTTPCookieStoreHostApi.m @@ -0,0 +1,60 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FWFHTTPCookieStoreHostApi.h" +#import "FWFDataConverters.h" +#import "FWFWebsiteDataStoreHostApi.h" + +@interface FWFHTTPCookieStoreHostApiImpl () +@property(nonatomic) FWFInstanceManager *instanceManager; +@end + +@implementation FWFHTTPCookieStoreHostApiImpl +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { + self = [self init]; + if (self) { + _instanceManager = instanceManager; + } + return self; +} + +- (WKHTTPCookieStore *)HTTPCookieStoreForIdentifier:(NSNumber *)instanceId + API_AVAILABLE(ios(11.0)) { + return (WKHTTPCookieStore *)[self.instanceManager instanceForIdentifier:instanceId.longValue]; +} + +- (void)createFromWebsiteDataStoreWithIdentifier:(nonnull NSNumber *)instanceId + dataStoreIdentifier:(nonnull NSNumber *)websiteDataStoreInstanceId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull) + error { + if (@available(iOS 11.0, *)) { + WKWebsiteDataStore *dataStore = (WKWebsiteDataStore *)[self.instanceManager + instanceForIdentifier:websiteDataStoreInstanceId.longValue]; + [self.instanceManager addInstance:dataStore.httpCookieStore + withIdentifier:instanceId.longValue]; + } else { + *error = [FlutterError + errorWithCode:@"FWFUnsupportedVersionError" + message:@"WKWebsiteDataStore.httpCookieStore is only supported on versions 11+." + details:nil]; + } +} + +- (void)setCookieForStoreWithIdentifier:(nonnull NSNumber *)instanceId + cookie:(nonnull FWFNSHttpCookieData *)cookie + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + NSHTTPCookie *nsCookie = FWFNSHTTPCookieFromCookieData(cookie); + + if (@available(iOS 11.0, *)) { + [[self HTTPCookieStoreForIdentifier:instanceId] setCookie:nsCookie + completionHandler:^{ + completion(nil); + }]; + } else { + completion([FlutterError errorWithCode:@"FWFUnsupportedVersionError" + message:@"setCookie is only supported on versions 11+." + details:nil]); + } +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.h new file mode 100644 index 000000000000..fca1e83c86e0 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.h @@ -0,0 +1,28 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import + +#import "FWFGeneratedWebKitApis.h" +#import "FWFInstanceManager.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Implementation of WKNavigationDelegate for FWFNavigationDelegateHostApiImpl. + */ +@interface FWFNavigationDelegate : NSObject +@end + +/** + * Host api implementation for WKNavigationDelegate. + * + * Handles creating WKNavigationDelegate that intercommunicate with a paired Dart object. + */ +@interface FWFNavigationDelegateHostApiImpl : NSObject +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.m new file mode 100644 index 000000000000..188d83ff81b6 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.m @@ -0,0 +1,40 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FWFNavigationDelegateHostApi.h" +#import "FWFWebViewConfigurationHostApi.h" + +@implementation FWFNavigationDelegate +@end + +@interface FWFNavigationDelegateHostApiImpl () +@property(nonatomic) FWFInstanceManager *instanceManager; +@end + +@implementation FWFNavigationDelegateHostApiImpl +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { + self = [self init]; + if (self) { + _instanceManager = instanceManager; + } + return self; +} + +- (FWFNavigationDelegate *)navigationDelegateForIdentifier:(NSNumber *)instanceId { + return (FWFNavigationDelegate *)[self.instanceManager instanceForIdentifier:instanceId.longValue]; +} + +- (void)createWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error { + FWFNavigationDelegate *navigationDelegate = [[FWFNavigationDelegate alloc] init]; + [self.instanceManager addInstance:navigationDelegate withIdentifier:instanceId.longValue]; +} + +- (void)setDidFinishNavigationForDelegateWithIdentifier:(nonnull NSNumber *)instanceId + functionIdentifier:(nullable NSNumber *)functionInstanceId + error:(FlutterError *_Nullable __autoreleasing + *_Nonnull)error { + // TODO(bparrishMines): Implement when callback method design is finalized. +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.h new file mode 100644 index 000000000000..23c38fbe482d --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.h @@ -0,0 +1,21 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +#import "FWFGeneratedWebKitApis.h" +#import "FWFInstanceManager.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Host api implementation for NSObject. + * + * Handles creating NSObject that intercommunicate with a paired Dart object. + */ +@interface FWFObjectHostApiImpl : NSObject +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.m new file mode 100644 index 000000000000..b229c2e819a9 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.m @@ -0,0 +1,54 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FWFObjectHostApi.h" +#import "FWFDataConverters.h" + +@interface FWFObjectHostApiImpl () +@property(nonatomic) FWFInstanceManager *instanceManager; +@end + +@implementation FWFObjectHostApiImpl +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { + self = [self init]; + if (self) { + _instanceManager = instanceManager; + } + return self; +} + +- (NSObject *)objectForIdentifier:(NSNumber *)instanceId { + return (NSObject *)[self.instanceManager instanceForIdentifier:instanceId.longValue]; +} + +- (void)addObserverForObjectWithIdentifier:(nonnull NSNumber *)instanceId + observerIdentifier:(nonnull NSNumber *)observer + keyPath:(nonnull NSString *)keyPath + options: + (nonnull NSArray *) + options + error:(FlutterError *_Nullable *_Nonnull)error { + NSKeyValueObservingOptions optionsInt = 0; + for (FWFNSKeyValueObservingOptionsEnumData *data in options) { + optionsInt |= FWFNSKeyValueObservingOptionsFromEnumData(data); + } + [[self objectForIdentifier:instanceId] addObserver:[self objectForIdentifier:observer] + forKeyPath:keyPath + options:optionsInt + context:nil]; +} + +- (void)removeObserverForObjectWithIdentifier:(nonnull NSNumber *)instanceId + observerIdentifier:(nonnull NSNumber *)observer + keyPath:(nonnull NSString *)keyPath + error:(FlutterError *_Nullable *_Nonnull)error { + [[self objectForIdentifier:instanceId] removeObserver:[self objectForIdentifier:observer] + forKeyPath:keyPath]; +} + +- (void)disposeObjectWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error { + [self.instanceManager removeInstanceWithIdentifier:instanceId.longValue]; +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFPreferencesHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFPreferencesHostApi.h new file mode 100644 index 000000000000..de2d26491a58 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFPreferencesHostApi.h @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import + +#import "FWFGeneratedWebKitApis.h" +#import "FWFInstanceManager.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Host api implementation for WKPreferences. + * + * Handles creating WKPreferences that intercommunicate with a paired Dart object. + */ +@interface FWFPreferencesHostApiImpl : NSObject +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFPreferencesHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFPreferencesHostApi.m new file mode 100644 index 000000000000..f907d211f08e --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFPreferencesHostApi.m @@ -0,0 +1,44 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FWFPreferencesHostApi.h" +#import "FWFWebViewConfigurationHostApi.h" + +@interface FWFPreferencesHostApiImpl () +@property(nonatomic) FWFInstanceManager *instanceManager; +@end + +@implementation FWFPreferencesHostApiImpl +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { + self = [self init]; + if (self) { + _instanceManager = instanceManager; + } + return self; +} + +- (WKPreferences *)preferencesForIdentifier:(NSNumber *)instanceId { + return (WKPreferences *)[self.instanceManager instanceForIdentifier:instanceId.longValue]; +} + +- (void)createWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error { + WKPreferences *preferences = [[WKPreferences alloc] init]; + [self.instanceManager addInstance:preferences withIdentifier:instanceId.longValue]; +} + +- (void)createFromWebViewConfigurationWithIdentifier:(nonnull NSNumber *)instanceId + configurationIdentifier:(nonnull NSNumber *)configurationInstanceId + error:(FlutterError *_Nullable *_Nonnull)error { + WKWebViewConfiguration *configuration = (WKWebViewConfiguration *)[self.instanceManager + instanceForIdentifier:configurationInstanceId.longValue]; + [self.instanceManager addInstance:configuration.preferences withIdentifier:instanceId.longValue]; +} + +- (void)setJavaScriptEnabledForPreferencesWithIdentifier:(nonnull NSNumber *)instanceId + isEnabled:(nonnull NSNumber *)enabled + error:(FlutterError *_Nullable *_Nonnull)error { + [[self preferencesForIdentifier:instanceId] setJavaScriptEnabled:enabled.boolValue]; +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.h new file mode 100644 index 000000000000..7440862ce4e4 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.h @@ -0,0 +1,28 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import + +#import "FWFGeneratedWebKitApis.h" +#import "FWFInstanceManager.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Implementation of WKScriptMessageHandler for FWFScriptMessageHandlerHostApiImpl. + */ +@interface FWFScriptMessageHandler : NSObject +@end + +/** + * Host api implementation for WKScriptMessageHandler. + * + * Handles creating WKScriptMessageHandler that intercommunicate with a paired Dart object. + */ +@interface FWFScriptMessageHandlerHostApiImpl : NSObject +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.m new file mode 100644 index 000000000000..37662b5fc9f4 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.m @@ -0,0 +1,37 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FWFScriptMessageHandlerHostApi.h" +#import "FWFDataConverters.h" + +@implementation FWFScriptMessageHandler +- (void)userContentController:(nonnull WKUserContentController *)userContentController + didReceiveScriptMessage:(nonnull WKScriptMessage *)message { +} +@end + +@interface FWFScriptMessageHandlerHostApiImpl () +@property(nonatomic) FWFInstanceManager *instanceManager; +@end + +@implementation FWFScriptMessageHandlerHostApiImpl +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { + self = [self init]; + if (self) { + _instanceManager = instanceManager; + } + return self; +} + +- (FWFScriptMessageHandler *)scriptMessageHandlerForIdentifier:(NSNumber *)instanceId { + return (FWFScriptMessageHandler *)[self.instanceManager + instanceForIdentifier:instanceId.longValue]; +} + +- (void)createWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error { + FWFScriptMessageHandler *scriptMessageHandler = [[FWFScriptMessageHandler alloc] init]; + [self.instanceManager addInstance:scriptMessageHandler withIdentifier:instanceId.longValue]; +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewHostApi.h new file mode 100644 index 000000000000..25f373f374e3 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewHostApi.h @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import + +#import "FWFGeneratedWebKitApis.h" +#import "FWFInstanceManager.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Host api implementation for UIScrollView. + * + * Handles creating UIScrollView that intercommunicate with a paired Dart object. + */ +@interface FWFScrollViewHostApiImpl : NSObject +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewHostApi.m new file mode 100644 index 000000000000..a7522995e0e1 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewHostApi.m @@ -0,0 +1,57 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FWFScrollViewHostApi.h" +#import "FWFWebViewHostApi.h" + +@interface FWFScrollViewHostApiImpl () +@property(nonatomic) FWFInstanceManager *instanceManager; +@end + +@implementation FWFScrollViewHostApiImpl +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { + self = [self init]; + if (self) { + _instanceManager = instanceManager; + } + return self; +} + +- (UIScrollView *)scrollViewForIdentifier:(NSNumber *)instanceId { + return (UIScrollView *)[self.instanceManager instanceForIdentifier:instanceId.longValue]; +} + +- (void)createFromWebViewWithIdentifier:(nonnull NSNumber *)instanceId + webViewIdentifier:(nonnull NSNumber *)webViewInstanceId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + WKWebView *webView = + (WKWebView *)[self.instanceManager instanceForIdentifier:webViewInstanceId.longValue]; + [self.instanceManager addInstance:webView.scrollView withIdentifier:instanceId.longValue]; +} + +- (NSArray *) + contentOffsetForScrollViewWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error { + CGPoint point = [[self scrollViewForIdentifier:instanceId] contentOffset]; + return @[ @(point.x), @(point.y) ]; +} + +- (void)scrollByForScrollViewWithIdentifier:(nonnull NSNumber *)instanceId + x:(nonnull NSNumber *)x + y:(nonnull NSNumber *)y + error:(FlutterError *_Nullable *_Nonnull)error { + UIScrollView *scrollView = [self scrollViewForIdentifier:instanceId]; + CGPoint contentOffset = scrollView.contentOffset; + [scrollView setContentOffset:CGPointMake(contentOffset.x + x.doubleValue, + contentOffset.y + y.doubleValue)]; +} + +- (void)setContentOffsetForScrollViewWithIdentifier:(nonnull NSNumber *)instanceId + toX:(nonnull NSNumber *)x + y:(nonnull NSNumber *)y + error:(FlutterError *_Nullable *_Nonnull)error { + [[self scrollViewForIdentifier:instanceId] + setContentOffset:CGPointMake(x.doubleValue, y.doubleValue)]; +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.h new file mode 100644 index 000000000000..8795b00e9001 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.h @@ -0,0 +1,28 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import + +#import "FWFGeneratedWebKitApis.h" +#import "FWFInstanceManager.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Implementation of WKUIDelegate for FWFUIDelegateHostApiImpl. + */ +@interface FWFUIDelegate : NSObject +@end + +/** + * Host api implementation for WKUIDelegate. + * + * Handles creating WKUIDelegate that intercommunicate with a paired Dart object. + */ +@interface FWFUIDelegateHostApiImpl : NSObject +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m new file mode 100644 index 000000000000..2621c5dde287 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m @@ -0,0 +1,33 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FWFUIDelegateHostApi.h" +#import "FWFWebViewConfigurationHostApi.h" + +@implementation FWFUIDelegate +@end + +@interface FWFUIDelegateHostApiImpl () +@property(nonatomic) FWFInstanceManager *instanceManager; +@end + +@implementation FWFUIDelegateHostApiImpl +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { + self = [self init]; + if (self) { + _instanceManager = instanceManager; + } + return self; +} + +- (FWFUIDelegate *)delegateForIdentifier:(NSNumber *)instanceId { + return (FWFUIDelegate *)[self.instanceManager instanceForIdentifier:instanceId.longValue]; +} + +- (void)createWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error { + FWFUIDelegate *uIDelegate = [[FWFUIDelegate alloc] init]; + [self.instanceManager addInstance:uIDelegate withIdentifier:instanceId.longValue]; +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIViewHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIViewHostApi.h new file mode 100644 index 000000000000..82edd6b742ca --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIViewHostApi.h @@ -0,0 +1,21 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +#import "FWFGeneratedWebKitApis.h" +#import "FWFInstanceManager.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Host api implementation for UIView. + * + * Handles creating UIView that intercommunicate with a paired Dart object. + */ +@interface FWFUIViewHostApiImpl : NSObject +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIViewHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIViewHostApi.m new file mode 100644 index 000000000000..b2c1f639d1f6 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIViewHostApi.m @@ -0,0 +1,43 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FWFUIViewHostApi.h" + +@interface FWFUIViewHostApiImpl () +@property(nonatomic) FWFInstanceManager *instanceManager; +@end + +@implementation FWFUIViewHostApiImpl +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { + self = [self init]; + if (self) { + _instanceManager = instanceManager; + } + return self; +} + +- (UIView *)viewForIdentifier:(NSNumber *)instanceId { + return (UIView *)[self.instanceManager instanceForIdentifier:instanceId.longValue]; +} + +- (void)setBackgroundColorForViewWithIdentifier:(nonnull NSNumber *)instanceId + toValue:(nullable NSNumber *)color + error:(FlutterError *_Nullable *_Nonnull)error { + if (!color) { + [[self viewForIdentifier:instanceId] setBackgroundColor:nil]; + } + int colorInt = color.intValue; + UIColor *colorObject = [UIColor colorWithRed:(colorInt >> 16 & 0xff) / 255.0 + green:(colorInt >> 8 & 0xff) / 255.0 + blue:(colorInt & 0xff) / 255.0 + alpha:(colorInt >> 24 & 0xff) / 255.0]; + [[self viewForIdentifier:instanceId] setBackgroundColor:colorObject]; +} + +- (void)setOpaqueForViewWithIdentifier:(nonnull NSNumber *)instanceId + isOpaque:(nonnull NSNumber *)opaque + error:(FlutterError *_Nullable *_Nonnull)error { + [[self viewForIdentifier:instanceId] setOpaque:opaque.boolValue]; +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUserContentControllerHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUserContentControllerHostApi.h new file mode 100644 index 000000000000..f0e5a1383ac3 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUserContentControllerHostApi.h @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import + +#import "FWFGeneratedWebKitApis.h" +#import "FWFInstanceManager.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Host api implementation for WKUserContentController. + * + * Handles creating WKUserContentController that intercommunicate with a paired Dart object. + */ +@interface FWFUserContentControllerHostApiImpl : NSObject +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUserContentControllerHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUserContentControllerHostApi.m new file mode 100644 index 000000000000..2db7cff0b68e --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUserContentControllerHostApi.m @@ -0,0 +1,80 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FWFUserContentControllerHostApi.h" +#import "FWFDataConverters.h" +#import "FWFWebViewConfigurationHostApi.h" + +@interface FWFUserContentControllerHostApiImpl () +@property(nonatomic) FWFInstanceManager *instanceManager; +@end + +@implementation FWFUserContentControllerHostApiImpl +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { + self = [self init]; + if (self) { + _instanceManager = instanceManager; + } + return self; +} + +- (WKUserContentController *)userContentControllerForIdentifier:(NSNumber *)instanceId { + return (WKUserContentController *)[self.instanceManager + instanceForIdentifier:instanceId.longValue]; +} + +- (void)createFromWebViewConfigurationWithIdentifier:(nonnull NSNumber *)instanceId + configurationIdentifier:(nonnull NSNumber *)configurationInstanceId + error:(FlutterError *_Nullable *_Nonnull)error { + WKWebViewConfiguration *configuration = (WKWebViewConfiguration *)[self.instanceManager + instanceForIdentifier:configurationInstanceId.longValue]; + [self.instanceManager addInstance:configuration.userContentController + withIdentifier:instanceId.longValue]; +} + +- (void)addScriptMessageHandlerForControllerWithIdentifier:(nonnull NSNumber *)instanceId + handlerIdentifier:(nonnull NSNumber *)handler + ofName:(nonnull NSString *)name + error: + (FlutterError *_Nullable *_Nonnull)error { + [[self userContentControllerForIdentifier:instanceId] + addScriptMessageHandler:(id)[self.instanceManager + instanceForIdentifier:handler.longValue] + name:name]; +} + +- (void)removeScriptMessageHandlerForControllerWithIdentifier:(nonnull NSNumber *)instanceId + name:(nonnull NSString *)name + error:(FlutterError *_Nullable *_Nonnull) + error { + [[self userContentControllerForIdentifier:instanceId] removeScriptMessageHandlerForName:name]; +} + +- (void)removeAllScriptMessageHandlersForControllerWithIdentifier:(nonnull NSNumber *)instanceId + error: + (FlutterError *_Nullable *_Nonnull) + error { + if (@available(iOS 14.0, *)) { + [[self userContentControllerForIdentifier:instanceId] removeAllScriptMessageHandlers]; + } else { + *error = [FlutterError + errorWithCode:@"FWFUnsupportedVersionError" + message:@"removeAllScriptMessageHandlers is only supported on versions 14+." + details:nil]; + } +} + +- (void)addUserScriptForControllerWithIdentifier:(nonnull NSNumber *)instanceId + userScript:(nonnull FWFWKUserScriptData *)userScript + error:(FlutterError *_Nullable *_Nonnull)error { + [[self userContentControllerForIdentifier:instanceId] + addUserScript:FWFWKUserScriptFromScriptData(userScript)]; +} + +- (void)removeAllUserScriptsForControllerWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error { + [[self userContentControllerForIdentifier:instanceId] removeAllUserScripts]; +} + +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.h new file mode 100644 index 000000000000..4c01d9354107 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.h @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import + +#import "FWFGeneratedWebKitApis.h" +#import "FWFInstanceManager.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Host api implementation for WKWebViewConfiguration. + * + * Handles creating WKWebViewConfiguration that intercommunicate with a paired Dart object. + */ +@interface FWFWebViewConfigurationHostApiImpl : NSObject +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.m new file mode 100644 index 000000000000..0b239daebf99 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.m @@ -0,0 +1,87 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FWFWebViewConfigurationHostApi.h" +#import "FWFDataConverters.h" +#import "FWFWebViewConfigurationHostApi.h" + +@interface FWFWebViewConfigurationHostApiImpl () +@property(nonatomic) FWFInstanceManager *instanceManager; +@end + +@implementation FWFWebViewConfigurationHostApiImpl +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { + self = [self init]; + if (self) { + _instanceManager = instanceManager; + } + return self; +} + +- (WKWebViewConfiguration *)webViewConfigurationForIdentifier:(NSNumber *)instanceId { + return (WKWebViewConfiguration *)[self.instanceManager + instanceForIdentifier:instanceId.longValue]; +} + +- (void)createWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error { + WKWebViewConfiguration *webViewConfiguration = [[WKWebViewConfiguration alloc] init]; + [self.instanceManager addInstance:webViewConfiguration withIdentifier:instanceId.longValue]; +} + +- (void)createFromWebViewWithIdentifier:(nonnull NSNumber *)instanceId + webViewIdentifier:(nonnull NSNumber *)webViewInstanceId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + WKWebView *webView = + (WKWebView *)[self.instanceManager instanceForIdentifier:webViewInstanceId.longValue]; + [self.instanceManager addInstance:webView.configuration withIdentifier:instanceId.longValue]; +} + +- (void)setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:(nonnull NSNumber *)instanceId + isAllowed:(nonnull NSNumber *)allow + error: + (FlutterError *_Nullable *_Nonnull) + error { + [[self webViewConfigurationForIdentifier:instanceId] + setAllowsInlineMediaPlayback:allow.boolValue]; +} + +- (void) + setMediaTypesRequiresUserActionForConfigurationWithIdentifier:(nonnull NSNumber *)instanceId + forTypes: + (nonnull NSArray< + FWFWKAudiovisualMediaTypeEnumData + *> *)types + error: + (FlutterError *_Nullable *_Nonnull) + error { + NSAssert(types.count, @"Types must not be empty."); + + WKWebViewConfiguration *configuration = + (WKWebViewConfiguration *)[self webViewConfigurationForIdentifier:instanceId]; + if (@available(iOS 10.0, *)) { + WKAudiovisualMediaTypes typesInt = 0; + for (FWFWKAudiovisualMediaTypeEnumData *data in types) { + typesInt |= FWFWKAudiovisualMediaTypeFromEnumData(data); + } + [configuration setMediaTypesRequiringUserActionForPlayback:typesInt]; + } else { + for (FWFWKAudiovisualMediaTypeEnumData *data in types) { + switch (data.value) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + case FWFWKAudiovisualMediaTypeEnumNone: + configuration.requiresUserActionForMediaPlayback = false; + break; + case FWFWKAudiovisualMediaTypeEnumAudio: + case FWFWKAudiovisualMediaTypeEnumVideo: + case FWFWKAudiovisualMediaTypeEnumAll: + configuration.requiresUserActionForMediaPlayback = true; + break; +#pragma clang diagnostic pop + } + } + } +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.h new file mode 100644 index 000000000000..72f00e032ee4 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.h @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import + +#import "FWFGeneratedWebKitApis.h" +#import "FWFInstanceManager.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Host api implementation for WKWebsiteDataStore. + * + * Handles creating WKWebsiteDataStore that intercommunicate with a paired Dart object. + */ +@interface FWFWebsiteDataStoreHostApiImpl : NSObject +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.m new file mode 100644 index 000000000000..e052ae03543c --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.m @@ -0,0 +1,67 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FWFWebsiteDataStoreHostApi.h" +#import "FWFDataConverters.h" +#import "FWFWebViewConfigurationHostApi.h" + +@interface FWFWebsiteDataStoreHostApiImpl () +@property(nonatomic) FWFInstanceManager *instanceManager; +@end + +@implementation FWFWebsiteDataStoreHostApiImpl +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { + self = [self init]; + if (self) { + _instanceManager = instanceManager; + } + return self; +} + +- (WKWebsiteDataStore *)websiteDataStoreForIdentifier:(NSNumber *)instanceId { + return (WKWebsiteDataStore *)[self.instanceManager instanceForIdentifier:instanceId.longValue]; +} + +- (void)createFromWebViewConfigurationWithIdentifier:(nonnull NSNumber *)instanceId + configurationIdentifier:(nonnull NSNumber *)configurationInstanceId + error:(FlutterError *_Nullable *_Nonnull)error { + WKWebViewConfiguration *configuration = (WKWebViewConfiguration *)[self.instanceManager + instanceForIdentifier:configurationInstanceId.longValue]; + [self.instanceManager addInstance:configuration.websiteDataStore + withIdentifier:instanceId.longValue]; +} + +- (void)createDefaultDataStoreWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull) + error { + [self.instanceManager addInstance:[WKWebsiteDataStore defaultDataStore] + withIdentifier:instanceId.longValue]; +} + +- (void) + removeDataFromDataStoreWithIdentifier:(nonnull NSNumber *)instanceId + ofTypes: + (nonnull NSArray *)dataTypes + modifiedSince:(nonnull NSNumber *)modificationTimeInSecondsSinceEpoch + completion:(nonnull void (^)(NSNumber *_Nullable, + FlutterError *_Nullable))completion { + NSMutableSet *stringDataTypes = [NSMutableSet set]; + for (FWFWKWebsiteDataTypeEnumData *type in dataTypes) { + [stringDataTypes addObject:FWFWKWebsiteDataTypeFromEnumData(type)]; + } + + WKWebsiteDataStore *dataStore = [self websiteDataStoreForIdentifier:instanceId]; + [dataStore + fetchDataRecordsOfTypes:stringDataTypes + completionHandler:^(NSArray *records) { + [dataStore + removeDataOfTypes:stringDataTypes + modifiedSince:[NSDate dateWithTimeIntervalSince1970: + modificationTimeInSecondsSinceEpoch.doubleValue] + completionHandler:^{ + completion(@(records.count > 0), nil); + }]; + }]; +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h index 9a7fd1b5ef3f..0aa409e4b31f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h @@ -9,7 +9,18 @@ #import #import #import +#import #import +#import +#import +#import +#import +#import +#import +#import +#import +#import #import +#import #import #import diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart index fa52b43c396e..f84ba68bfbc1 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart @@ -45,14 +45,14 @@ enum WKAudiovisualMediaTypeEnum { all, } -enum WKWebsiteDataTypesEnum { +enum WKWebsiteDataTypeEnum { cookies, memoryCache, diskCache, offlineWebApplicationCache, - localStroage, + localStorage, sessionStorage, - sqlDatabases, + webSQLDatabases, indexedDBDatabases, } @@ -80,115 +80,105 @@ enum NSHttpCookiePropertyKeyEnum { class NSKeyValueObservingOptionsEnumData { NSKeyValueObservingOptionsEnumData({ - this.value, + required this.value, }); - NSKeyValueObservingOptionsEnum? value; + NSKeyValueObservingOptionsEnum value; Object encode() { final Map pigeonMap = {}; - pigeonMap['value'] = value?.index; + pigeonMap['value'] = value.index; return pigeonMap; } static NSKeyValueObservingOptionsEnumData decode(Object message) { final Map pigeonMap = message as Map; return NSKeyValueObservingOptionsEnumData( - value: pigeonMap['value'] != null - ? NSKeyValueObservingOptionsEnum.values[pigeonMap['value']! as int] - : null, + value: NSKeyValueObservingOptionsEnum.values[pigeonMap['value']! as int], ); } } class WKUserScriptInjectionTimeEnumData { WKUserScriptInjectionTimeEnumData({ - this.value, + required this.value, }); - WKUserScriptInjectionTimeEnum? value; + WKUserScriptInjectionTimeEnum value; Object encode() { final Map pigeonMap = {}; - pigeonMap['value'] = value?.index; + pigeonMap['value'] = value.index; return pigeonMap; } static WKUserScriptInjectionTimeEnumData decode(Object message) { final Map pigeonMap = message as Map; return WKUserScriptInjectionTimeEnumData( - value: pigeonMap['value'] != null - ? WKUserScriptInjectionTimeEnum.values[pigeonMap['value']! as int] - : null, + value: WKUserScriptInjectionTimeEnum.values[pigeonMap['value']! as int], ); } } class WKAudiovisualMediaTypeEnumData { WKAudiovisualMediaTypeEnumData({ - this.value, + required this.value, }); - WKAudiovisualMediaTypeEnum? value; + WKAudiovisualMediaTypeEnum value; Object encode() { final Map pigeonMap = {}; - pigeonMap['value'] = value?.index; + pigeonMap['value'] = value.index; return pigeonMap; } static WKAudiovisualMediaTypeEnumData decode(Object message) { final Map pigeonMap = message as Map; return WKAudiovisualMediaTypeEnumData( - value: pigeonMap['value'] != null - ? WKAudiovisualMediaTypeEnum.values[pigeonMap['value']! as int] - : null, + value: WKAudiovisualMediaTypeEnum.values[pigeonMap['value']! as int], ); } } -class WKWebsiteDataTypesEnumData { - WKWebsiteDataTypesEnumData({ - this.value, +class WKWebsiteDataTypeEnumData { + WKWebsiteDataTypeEnumData({ + required this.value, }); - WKWebsiteDataTypesEnum? value; + WKWebsiteDataTypeEnum value; Object encode() { final Map pigeonMap = {}; - pigeonMap['value'] = value?.index; + pigeonMap['value'] = value.index; return pigeonMap; } - static WKWebsiteDataTypesEnumData decode(Object message) { + static WKWebsiteDataTypeEnumData decode(Object message) { final Map pigeonMap = message as Map; - return WKWebsiteDataTypesEnumData( - value: pigeonMap['value'] != null - ? WKWebsiteDataTypesEnum.values[pigeonMap['value']! as int] - : null, + return WKWebsiteDataTypeEnumData( + value: WKWebsiteDataTypeEnum.values[pigeonMap['value']! as int], ); } } class NSHttpCookiePropertyKeyEnumData { NSHttpCookiePropertyKeyEnumData({ - this.value, + required this.value, }); - NSHttpCookiePropertyKeyEnum? value; + NSHttpCookiePropertyKeyEnum value; Object encode() { final Map pigeonMap = {}; - pigeonMap['value'] = value?.index; + pigeonMap['value'] = value.index; return pigeonMap; } static NSHttpCookiePropertyKeyEnumData decode(Object message) { final Map pigeonMap = message as Map; return NSHttpCookiePropertyKeyEnumData( - value: pigeonMap['value'] != null - ? NSHttpCookiePropertyKeyEnum.values[pigeonMap['value']! as int] - : null, + value: NSHttpCookiePropertyKeyEnum.values[pigeonMap['value']! as int], ); } } @@ -262,22 +252,27 @@ class WKUserScriptData { class NSHttpCookieData { NSHttpCookieData({ - required this.properties, + required this.propertyKeys, + required this.propertyValues, }); - Map properties; + List propertyKeys; + List propertyValues; Object encode() { final Map pigeonMap = {}; - pigeonMap['properties'] = properties; + pigeonMap['propertyKeys'] = propertyKeys; + pigeonMap['propertyValues'] = propertyValues; return pigeonMap; } static NSHttpCookieData decode(Object message) { final Map pigeonMap = message as Map; return NSHttpCookieData( - properties: (pigeonMap['properties'] as Map?)! - .cast(), + propertyKeys: (pigeonMap['propertyKeys'] as List?)! + .cast(), + propertyValues: + (pigeonMap['propertyValues'] as List?)!.cast(), ); } } @@ -286,7 +281,7 @@ class _WKWebsiteDataStoreHostApiCodec extends StandardMessageCodec { const _WKWebsiteDataStoreHostApiCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is WKWebsiteDataTypesEnumData) { + if (value is WKWebsiteDataTypeEnumData) { buffer.putUint8(128); writeValue(buffer, value.encode()); } else { @@ -298,7 +293,7 @@ class _WKWebsiteDataStoreHostApiCodec extends StandardMessageCodec { Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { case 128: - return WKWebsiteDataTypesEnumData.decode(readValue(buffer)!); + return WKWebsiteDataTypeEnumData.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -371,15 +366,15 @@ class WKWebsiteDataStoreHostApi { Future removeDataOfTypes( int arg_instanceId, - List arg_dataTypes, - double arg_secondsModifiedSinceEpoch) async { + List arg_dataTypes, + double arg_modificationTimeInSecondsSinceEpoch) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([ arg_instanceId, arg_dataTypes, - arg_secondsModifiedSinceEpoch + arg_modificationTimeInSecondsSinceEpoch ]) as Map?; if (replyMap == null) { throw PlatformException( @@ -420,35 +415,6 @@ class UIViewHostApi { static const MessageCodec codec = _UIViewHostApiCodec(); - Future> getContentOffset(int arg_instanceId) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.UIViewHostApi.getContentOffset', codec, - binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; - throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], - ); - } else if (replyMap['result'] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyMap['result'] as List?)!.cast(); - } - } - Future setBackgroundColor(int arg_instanceId, int? arg_value) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.UIViewHostApi.setBackgroundColor', codec, @@ -1352,7 +1318,7 @@ class _WKWebViewHostApiCodec extends StandardMessageCodec { } else if (value is WKUserScriptInjectionTimeEnumData) { buffer.putUint8(134); writeValue(buffer, value.encode()); - } else if (value is WKWebsiteDataTypesEnumData) { + } else if (value is WKWebsiteDataTypeEnumData) { buffer.putUint8(135); writeValue(buffer, value.encode()); } else { @@ -1385,7 +1351,7 @@ class _WKWebViewHostApiCodec extends StandardMessageCodec { return WKUserScriptInjectionTimeEnumData.decode(readValue(buffer)!); case 135: - return WKWebsiteDataTypesEnumData.decode(readValue(buffer)!); + return WKWebsiteDataTypeEnumData.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart index ec7bb5377de0..5c127c5654c6 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart @@ -146,6 +146,9 @@ enum NSHttpCookiePropertyKey { /// A String indicating the same-site policy for the cookie. /// + /// This is only supported on iOS version 13+. This value will be ignored on + /// versions < 13. + /// /// See https://developer.apple.com/documentation/foundation/nshttpcookiesamesitepolicy. sameSitePolicy, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart index ab63db67bba1..88f763fe6ba3 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart @@ -53,7 +53,7 @@ enum WKAudiovisualMediaType { /// Types of data that websites store. /// /// See https://developer.apple.com/documentation/webkit/wkwebsitedatarecord/data_store_record_types?language=objc. -enum WKWebsiteDataTypes { +enum WKWebsiteDataType { /// Cookies. cookies, @@ -67,13 +67,13 @@ enum WKWebsiteDataTypes { offlineWebApplicationCache, /// HTML local storage. - localStroage, + localStorage, /// HTML session storage. sessionStorage, /// WebSQL databases. - sqlDatabases, + webSQLDatabases, /// IndexedDB databases. indexedDBDatabases, @@ -291,7 +291,7 @@ class WKWebsiteDataStore { /// /// Returns whether any data was removed. Future removeDataOfTypes( - Set dataTypes, + Set dataTypes, DateTime since, ) { return _websiteDataStoreApi.removeDataOfTypesForInstances( @@ -440,7 +440,10 @@ class WKUserContentController { ); } - /// Uninstalls all custom message handlers associated with the user content controller. + /// Uninstalls all custom message handlers associated with the user content + /// controller. + /// + /// Only supported on iOS version 14+. Future removeAllScriptMessageHandlers() { return _userContentControllerApi.removeAllScriptMessageHandlersForInstances( this, @@ -550,6 +553,7 @@ class WKWebViewConfiguration { Future setMediaTypesRequiringUserActionForPlayback( Set types, ) { + assert(types.isNotEmpty); return _webViewConfigurationApi .setMediaTypesRequiringUserActionForPlaybackForInstances( this, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart index b970ab26bcc7..8bf29308371e 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart @@ -10,52 +10,53 @@ import '../common/web_kit.pigeon.dart'; import '../foundation/foundation.dart'; import 'web_kit.dart'; -Iterable _toWKWebsiteDataTypesEnumData( - Iterable types) { - return types.map((WKWebsiteDataTypes type) { - late final WKWebsiteDataTypesEnum value; +Iterable _toWKWebsiteDataTypeEnumData( + Iterable types) { + return types.map((WKWebsiteDataType type) { + late final WKWebsiteDataTypeEnum value; switch (type) { - case WKWebsiteDataTypes.cookies: - value = WKWebsiteDataTypesEnum.cookies; + case WKWebsiteDataType.cookies: + value = WKWebsiteDataTypeEnum.cookies; break; - case WKWebsiteDataTypes.memoryCache: - value = WKWebsiteDataTypesEnum.memoryCache; + case WKWebsiteDataType.memoryCache: + value = WKWebsiteDataTypeEnum.memoryCache; break; - case WKWebsiteDataTypes.diskCache: - value = WKWebsiteDataTypesEnum.diskCache; + case WKWebsiteDataType.diskCache: + value = WKWebsiteDataTypeEnum.diskCache; break; - case WKWebsiteDataTypes.offlineWebApplicationCache: - value = WKWebsiteDataTypesEnum.offlineWebApplicationCache; + case WKWebsiteDataType.offlineWebApplicationCache: + value = WKWebsiteDataTypeEnum.offlineWebApplicationCache; break; - case WKWebsiteDataTypes.localStroage: - value = WKWebsiteDataTypesEnum.localStroage; + case WKWebsiteDataType.localStorage: + value = WKWebsiteDataTypeEnum.localStorage; break; - case WKWebsiteDataTypes.sessionStorage: - value = WKWebsiteDataTypesEnum.sessionStorage; + case WKWebsiteDataType.sessionStorage: + value = WKWebsiteDataTypeEnum.sessionStorage; break; - case WKWebsiteDataTypes.sqlDatabases: - value = WKWebsiteDataTypesEnum.sqlDatabases; + case WKWebsiteDataType.webSQLDatabases: + value = WKWebsiteDataTypeEnum.webSQLDatabases; break; - case WKWebsiteDataTypes.indexedDBDatabases: - value = WKWebsiteDataTypesEnum.indexedDBDatabases; + case WKWebsiteDataType.indexedDBDatabases: + value = WKWebsiteDataTypeEnum.indexedDBDatabases; break; } - return WKWebsiteDataTypesEnumData(value: value); + return WKWebsiteDataTypeEnumData(value: value); }); } extension _NSHttpCookieConverter on NSHttpCookie { NSHttpCookieData toNSHttpCookieData() { + final Iterable keys = properties.keys; return NSHttpCookieData( - properties: properties.map( - (NSHttpCookiePropertyKey key, Object value) { - return MapEntry( - key.toNSHttpCookiePropertyKeyEnumData(), - value.toString(), - ); + propertyKeys: keys.map( + (NSHttpCookiePropertyKey key) { + return key.toNSHttpCookiePropertyKeyEnumData(); }, - ), + ).toList(), + propertyValues: keys + .map((NSHttpCookiePropertyKey key) => properties[key]!) + .toList(), ); } } @@ -258,12 +259,12 @@ class WKWebsiteDataStoreHostApiImpl extends WKWebsiteDataStoreHostApi { /// Calls [removeDataOfTypes] with the ids of the provided object instances. Future removeDataOfTypesForInstances( WKWebsiteDataStore instance, - Set dataTypes, { + Set dataTypes, { required double secondsModifiedSinceEpoch, }) { return removeDataOfTypes( instanceManager.getInstanceId(instance)!, - _toWKWebsiteDataTypesEnumData(dataTypes).toList(), + _toWKWebsiteDataTypeEnumData(dataTypes).toList(), secondsModifiedSinceEpoch, ); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_cookie_manager.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_cookie_manager.dart index c73111d73fd0..46322319ae89 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_cookie_manager.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_cookie_manager.dart @@ -19,7 +19,7 @@ class WebKitCookieManager extends WebViewCookieManagerPlatform { @override Future clearCookies() async { return websiteDataStore.removeDataOfTypes( - {WKWebsiteDataTypes.cookies}, + {WKWebsiteDataType.cookies}, DateTime.fromMillisecondsSinceEpoch(0), ); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 8c37112d7a24..0a177d854bc2 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -227,11 +227,11 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { @override Future clearCache() { return webView.configuration.websiteDataStore.removeDataOfTypes( - { - WKWebsiteDataTypes.memoryCache, - WKWebsiteDataTypes.diskCache, - WKWebsiteDataTypes.offlineWebApplicationCache, - WKWebsiteDataTypes.localStroage, + { + WKWebsiteDataType.memoryCache, + WKWebsiteDataType.diskCache, + WKWebsiteDataType.offlineWebApplicationCache, + WKWebsiteDataType.localStorage, }, DateTime.fromMillisecondsSinceEpoch(0), ); @@ -517,8 +517,12 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { Set removedJavaScriptChannels = const {}, }) async { webView.configuration.userContentController.removeAllUserScripts(); - webView.configuration.userContentController - .removeAllScriptMessageHandlers(); + // TODO(bparrishMines): This can be replaced with + // `removeAllScriptMessageHandlers` once Dart supports runtime version + // checking. (e.g. The equivalent to @availability in Objective-C.) + _scriptMessageHandlers.keys.forEach( + webView.configuration.userContentController.removeScriptMessageHandler, + ); removedJavaScriptChannels.forEach(_scriptMessageHandlers.remove); final Set remainingNames = _scriptMessageHandlers.keys.toSet(); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart index 509fa8e1f7dd..6844a5fa67ad 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart @@ -37,10 +37,10 @@ enum NSKeyValueObservingOptionsEnum { priorNotification, } +// TODO(bparrishMines): Enums need be wrapped in a data class because thay can't +// be used as primitive arguments. See https://github.com/flutter/flutter/issues/87307 class NSKeyValueObservingOptionsEnumData { - // TODO(bparrishMines): Generated code fails when enums are marked as nonnull. - // Change to nonnull once this is fixed: https://github.com/flutter/flutter/issues/100594 - late NSKeyValueObservingOptionsEnum? value; + late NSKeyValueObservingOptionsEnum value; } /// Mirror of NSKeyValueChange. @@ -53,8 +53,10 @@ enum NSKeyValueChangeEnum { replacement, } +// TODO(bparrishMines): Enums need be wrapped in a data class because thay can't +// be used as primitive arguments. See https://github.com/flutter/flutter/issues/87307 class NSKeyValueChangeEnumData { - late NSKeyValueChangeEnum? value; + late NSKeyValueChangeEnum value; } /// Mirror of NSKeyValueChangeKey. @@ -68,8 +70,10 @@ enum NSKeyValueChangeKeyEnum { oldValue, } +// TODO(bparrishMines): Enums need be wrapped in a data class because thay can't +// be used as primitive arguments. See https://github.com/flutter/flutter/issues/87307 class NSKeyValueChangeKeyEnumData { - late NSKeyValueChangeKeyEnum? value; + late NSKeyValueChangeKeyEnum value; } /// Mirror of WKUserScriptInjectionTime. @@ -80,8 +84,10 @@ enum WKUserScriptInjectionTimeEnum { atDocumentEnd, } +// TODO(bparrishMines): Enums need be wrapped in a data class because thay can't +// be used as primitive arguments. See https://github.com/flutter/flutter/issues/87307 class WKUserScriptInjectionTimeEnumData { - late WKUserScriptInjectionTimeEnum? value; + late WKUserScriptInjectionTimeEnum value; } /// Mirror of WKAudiovisualMediaTypes. @@ -94,26 +100,30 @@ enum WKAudiovisualMediaTypeEnum { all, } +// TODO(bparrishMines): Enums need be wrapped in a data class because thay can't +// be used as primitive arguments. See https://github.com/flutter/flutter/issues/87307 class WKAudiovisualMediaTypeEnumData { - late WKAudiovisualMediaTypeEnum? value; + late WKAudiovisualMediaTypeEnum value; } /// Mirror of WKWebsiteDataTypes. /// /// See https://developer.apple.com/documentation/webkit/wkwebsitedatarecord/data_store_record_types?language=objc. -enum WKWebsiteDataTypesEnum { +enum WKWebsiteDataTypeEnum { cookies, memoryCache, diskCache, offlineWebApplicationCache, - localStroage, + localStorage, sessionStorage, - sqlDatabases, + webSQLDatabases, indexedDBDatabases, } -class WKWebsiteDataTypesEnumData { - late WKWebsiteDataTypesEnum? value; +// TODO(bparrishMines): Enums need be wrapped in a data class because thay can't +// be used as primitive arguments. See https://github.com/flutter/flutter/issues/87307 +class WKWebsiteDataTypeEnumData { + late WKWebsiteDataTypeEnum value; } /// Mirror of WKNavigationActionPolicy. @@ -124,8 +134,10 @@ enum WKNavigationActionPolicyEnum { cancel, } +// TODO(bparrishMines): Enums need be wrapped in a data class because thay can't +// be used as primitive arguments. See https://github.com/flutter/flutter/issues/87307 class WKNavigationActionPolicyEnumData { - late WKNavigationActionPolicyEnum? value; + late WKNavigationActionPolicyEnum value; } /// Mirror of NSHTTPCookiePropertyKey. @@ -148,8 +160,10 @@ enum NSHttpCookiePropertyKeyEnum { version, } +// TODO(bparrishMines): Enums need be wrapped in a data class because thay can't +// be used as primitive arguments. See https://github.com/flutter/flutter/issues/87307 class NSHttpCookiePropertyKeyEnumData { - late NSHttpCookiePropertyKeyEnum? value; + late NSHttpCookiePropertyKeyEnum value; } /// Mirror of NSURLRequest. @@ -207,7 +221,13 @@ class WKScriptMessageData { /// /// See https://developer.apple.com/documentation/foundation/nshttpcookie?language=objc. class NSHttpCookieData { - late Map properties; + // TODO(bparrishMines): Change to a map when Objective-C data classes conform + // to `NSCopying`. See https://github.com/flutter/flutter/issues/103383. + // `NSDictionary`s are unable to use data classes as keys because they don't + // conform to `NSCopying`. This splits the map of properties into a list of + // keys and values with the ordered maintained. + late List propertyKeys; + late List propertyValues; } /// Mirror of WKWebsiteDataStore. @@ -216,7 +236,7 @@ class NSHttpCookieData { @HostApi(dartHostTestHandler: 'TestWKWebsiteDataStoreHostApi') abstract class WKWebsiteDataStoreHostApi { @ObjCSelector( - 'createDataStoreFromConfigurationWithIdentifier:configurationIdentifier:', + 'createFromWebViewConfigurationWithIdentifier:configurationIdentifier:', ) void createFromWebViewConfiguration( int instanceId, @@ -227,13 +247,13 @@ abstract class WKWebsiteDataStoreHostApi { void createDefaultDataStore(int instanceId); @ObjCSelector( - 'removeDataFromDataStoreWithIdentifier:ofTypes:secondsModifiedSinceEpoch:', + 'removeDataFromDataStoreWithIdentifier:ofTypes:modifiedSince:', ) @async bool removeDataOfTypes( int instanceId, - List dataTypes, - double secondsModifiedSinceEpoch, + List dataTypes, + double modificationTimeInSecondsSinceEpoch, ); } @@ -242,9 +262,6 @@ abstract class WKWebsiteDataStoreHostApi { /// See https://developer.apple.com/documentation/uikit/uiview?language=objc. @HostApi(dartHostTestHandler: 'TestUIViewHostApi') abstract class UIViewHostApi { - @ObjCSelector('contentOffsetForViewWithIdentifier:') - List getContentOffset(int instanceId); - @ObjCSelector('setBackgroundColorForViewWithIdentifier:toValue:') void setBackgroundColor(int instanceId, int? value); @@ -263,7 +280,7 @@ abstract class UIScrollViewHostApi { @ObjCSelector('contentOffsetForScrollViewWithIdentifier:') List getContentOffset(int instanceId); - @ObjCSelector('scrollByForScrollViewWithIdentifier:toX:y:') + @ObjCSelector('scrollByForScrollViewWithIdentifier:x:y:') void scrollBy(int instanceId, double x, double y); @ObjCSelector('setContentOffsetForScrollViewWithIdentifier:toX:y:') @@ -282,7 +299,7 @@ abstract class WKWebViewConfigurationHostApi { void createFromWebView(int instanceId, int webViewInstanceId); @ObjCSelector( - 'setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:isAlowed:', + 'setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:isAllowed:', ) void setAllowsInlineMediaPlayback(int instanceId, bool allow); @@ -335,7 +352,9 @@ abstract class WKUserContentControllerHostApi { /// See https://developer.apple.com/documentation/webkit/wkpreferences?language=objc. @HostApi(dartHostTestHandler: 'TestWKPreferencesHostApi') abstract class WKPreferencesHostApi { - @ObjCSelector('createFromWebViewConfiguration:configurationIdentifier:') + @ObjCSelector( + 'createFromWebViewConfigurationWithIdentifier:configurationIdentifier:', + ) void createFromWebViewConfiguration( int instanceId, int configurationInstanceId, @@ -498,5 +517,6 @@ abstract class WKHttpCookieStoreHostApi { ); @ObjCSelector('setCookieForStoreWithIdentifier:cookie:') + @async void setCookie(int instanceId, NSHttpCookieData cookie); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart index 042ddedbd769..1f963b4d9bc3 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart @@ -18,7 +18,7 @@ class _TestWKWebsiteDataStoreHostApiCodec extends StandardMessageCodec { const _TestWKWebsiteDataStoreHostApiCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is WKWebsiteDataTypesEnumData) { + if (value is WKWebsiteDataTypeEnumData) { buffer.putUint8(128); writeValue(buffer, value.encode()); } else { @@ -30,7 +30,7 @@ class _TestWKWebsiteDataStoreHostApiCodec extends StandardMessageCodec { Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { case 128: - return WKWebsiteDataTypesEnumData.decode(readValue(buffer)!); + return WKWebsiteDataTypeEnumData.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -47,8 +47,8 @@ abstract class TestWKWebsiteDataStoreHostApi { void createDefaultDataStore(int instanceId); Future removeDataOfTypes( int instanceId, - List dataTypes, - double secondsModifiedSinceEpoch); + List dataTypes, + double modificationTimeInSecondsSinceEpoch); static void setup(TestWKWebsiteDataStoreHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -110,15 +110,16 @@ abstract class TestWKWebsiteDataStoreHostApi { final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes was null, expected non-null int.'); - final List? arg_dataTypes = - (args[1] as List?)?.cast(); + final List? arg_dataTypes = + (args[1] as List?)?.cast(); assert(arg_dataTypes != null, - 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes was null, expected non-null List.'); - final double? arg_secondsModifiedSinceEpoch = (args[2] as double?); - assert(arg_secondsModifiedSinceEpoch != null, + 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes was null, expected non-null List.'); + final double? arg_modificationTimeInSecondsSinceEpoch = + (args[2] as double?); + assert(arg_modificationTimeInSecondsSinceEpoch != null, 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes was null, expected non-null double.'); - final bool output = await api.removeDataOfTypes( - arg_instanceId!, arg_dataTypes!, arg_secondsModifiedSinceEpoch!); + final bool output = await api.removeDataOfTypes(arg_instanceId!, + arg_dataTypes!, arg_modificationTimeInSecondsSinceEpoch!); return {'result': output}; }); } @@ -133,30 +134,10 @@ class _TestUIViewHostApiCodec extends StandardMessageCodec { abstract class TestUIViewHostApi { static const MessageCodec codec = _TestUIViewHostApiCodec(); - List getContentOffset(int instanceId); void setBackgroundColor(int instanceId, int? value); void setOpaque(int instanceId, bool opaque); static void setup(TestUIViewHostApi? api, {BinaryMessenger? binaryMessenger}) { - { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.UIViewHostApi.getContentOffset', codec, - binaryMessenger: binaryMessenger); - if (api == null) { - channel.setMockMessageHandler(null); - } else { - channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.UIViewHostApi.getContentOffset was null.'); - final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, - 'Argument for dev.flutter.pigeon.UIViewHostApi.getContentOffset was null, expected non-null int.'); - final List output = api.getContentOffset(arg_instanceId!); - return {'result': output}; - }); - } - } { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.UIViewHostApi.setBackgroundColor', codec, @@ -909,7 +890,7 @@ class _TestWKWebViewHostApiCodec extends StandardMessageCodec { } else if (value is WKUserScriptInjectionTimeEnumData) { buffer.putUint8(134); writeValue(buffer, value.encode()); - } else if (value is WKWebsiteDataTypesEnumData) { + } else if (value is WKWebsiteDataTypeEnumData) { buffer.putUint8(135); writeValue(buffer, value.encode()); } else { @@ -942,7 +923,7 @@ class _TestWKWebViewHostApiCodec extends StandardMessageCodec { return WKUserScriptInjectionTimeEnumData.decode(readValue(buffer)!); case 135: - return WKWebsiteDataTypesEnumData.decode(readValue(buffer)!); + return WKWebsiteDataTypeEnumData.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -1418,7 +1399,7 @@ abstract class TestWKHttpCookieStoreHostApi { void createFromWebsiteDataStore( int instanceId, int websiteDataStoreInstanceId); - void setCookie(int instanceId, NSHttpCookieData cookie); + Future setCookie(int instanceId, NSHttpCookieData cookie); static void setup(TestWKHttpCookieStoreHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -1462,7 +1443,7 @@ abstract class TestWKHttpCookieStoreHostApi { final NSHttpCookieData? arg_cookie = (args[1] as NSHttpCookieData?); assert(arg_cookie != null, 'Argument for dev.flutter.pigeon.WKHttpCookieStoreHostApi.setCookie was null, expected non-null NSHttpCookieData.'); - api.setCookie(arg_instanceId!, arg_cookie!); + await api.setCookie(arg_instanceId!, arg_cookie!); return {}; }); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.mocks.dart index 908709c90134..a9f9b2c322c7 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.mocks.dart @@ -185,10 +185,6 @@ class MockTestUIViewHostApi extends _i1.Mock implements _i2.TestUIViewHostApi { _i1.throwOnMissingStub(this); } - @override - List getContentOffset(int? instanceId) => - (super.noSuchMethod(Invocation.method(#getContentOffset, [instanceId]), - returnValue: []) as List); @override void setBackgroundColor(int? instanceId, int? value) => super.noSuchMethod( Invocation.method(#setBackgroundColor, [instanceId, value]), diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart index 8564bf889c2f..ae13ca9e6b6d 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart @@ -95,21 +95,21 @@ void main() { expect( websiteDataStore.removeDataOfTypes( - {WKWebsiteDataTypes.cookies}, + {WKWebsiteDataType.cookies}, DateTime.fromMillisecondsSinceEpoch(5000), ), completion(true), ); - final List typeData = + final List typeData = verify(mockPlatformHostApi.removeDataOfTypes( instanceManager.getInstanceId(websiteDataStore), captureAny, 5.0, - )).captured.single.cast() - as List; + )).captured.single.cast() + as List; - expect(typeData.single.value, WKWebsiteDataTypesEnum.cookies); + expect(typeData.single.value, WKWebsiteDataTypeEnum.cookies); }); }); @@ -169,10 +169,10 @@ void main() { ).captured.single as NSHttpCookieData; expect( - cookie.properties.entries.single.key!.value, + cookie.propertyKeys.single!.value, NSHttpCookiePropertyKeyEnum.comment, ); - expect(cookie.properties.entries.single.value, 'aComment'); + expect(cookie.propertyValues.single, 'aComment'); }); }); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart index d4f1fefc190c..4ffb7d4c19d3 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart @@ -2,11 +2,11 @@ // in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart. // Do not manually edit this file. -import 'dart:async' as _i4; +import 'dart:async' as _i3; import 'package:mockito/mockito.dart' as _i1; import 'package:webview_flutter_wkwebview/src/common/web_kit.pigeon.dart' - as _i3; + as _i4; import '../common/test_web_kit.pigeon.dart' as _i2; @@ -37,9 +37,10 @@ class MockTestWKHttpCookieStoreHostApi extends _i1.Mock [instanceId, websiteDataStoreInstanceId]), returnValueForMissingStub: null); @override - void setCookie(int? instanceId, _i3.NSHttpCookieData? cookie) => - super.noSuchMethod(Invocation.method(#setCookie, [instanceId, cookie]), - returnValueForMissingStub: null); + _i3.Future setCookie(int? instanceId, _i4.NSHttpCookieData? cookie) => + (super.noSuchMethod(Invocation.method(#setCookie, [instanceId, cookie]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i3.Future); } /// A class which mocks [TestWKNavigationDelegateHostApi]. @@ -149,7 +150,7 @@ class MockTestWKUserContentControllerHostApi extends _i1.Mock Invocation.method(#removeAllScriptMessageHandlers, [instanceId]), returnValueForMissingStub: null); @override - void addUserScript(int? instanceId, _i3.WKUserScriptData? userScript) => super + void addUserScript(int? instanceId, _i4.WKUserScriptData? userScript) => super .noSuchMethod(Invocation.method(#addUserScript, [instanceId, userScript]), returnValueForMissingStub: null); @override @@ -184,7 +185,7 @@ class MockTestWKWebViewConfigurationHostApi extends _i1.Mock returnValueForMissingStub: null); @override void setMediaTypesRequiringUserActionForPlayback( - int? instanceId, List<_i3.WKAudiovisualMediaTypeEnumData?>? types) => + int? instanceId, List<_i4.WKAudiovisualMediaTypeEnumData?>? types) => super.noSuchMethod( Invocation.method(#setMediaTypesRequiringUserActionForPlayback, [instanceId, types]), @@ -225,7 +226,7 @@ class MockTestWKWebViewHostApi extends _i1.Mock Invocation.method(#getEstimatedProgress, [instanceId]), returnValue: 0.0) as double); @override - void loadRequest(int? instanceId, _i3.NSUrlRequestData? request) => + void loadRequest(int? instanceId, _i4.NSUrlRequestData? request) => super.noSuchMethod(Invocation.method(#loadRequest, [instanceId, request]), returnValueForMissingStub: null); @override @@ -278,12 +279,12 @@ class MockTestWKWebViewHostApi extends _i1.Mock Invocation.method(#setCustomUserAgent, [instanceId, userAgent]), returnValueForMissingStub: null); @override - _i4.Future evaluateJavaScript( + _i3.Future evaluateJavaScript( int? instanceId, String? javaScriptString) => (super.noSuchMethod( Invocation.method( #evaluateJavaScript, [instanceId, javaScriptString]), - returnValue: Future.value()) as _i4.Future); + returnValue: Future.value()) as _i3.Future); } /// A class which mocks [TestWKWebsiteDataStoreHostApi]. @@ -307,12 +308,12 @@ class MockTestWKWebsiteDataStoreHostApi extends _i1.Mock Invocation.method(#createDefaultDataStore, [instanceId]), returnValueForMissingStub: null); @override - _i4.Future removeDataOfTypes( + _i3.Future removeDataOfTypes( int? instanceId, - List<_i3.WKWebsiteDataTypesEnumData?>? dataTypes, + List<_i4.WKWebsiteDataTypeEnumData?>? dataTypes, double? secondsModifiedSinceEpoch) => (super.noSuchMethod( Invocation.method(#removeDataOfTypes, [instanceId, dataTypes, secondsModifiedSinceEpoch]), - returnValue: Future.value(false)) as _i4.Future); + returnValue: Future.value(false)) as _i3.Future); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart index 8f23ff6b4a3d..6baff12eda17 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart @@ -37,12 +37,12 @@ void main() { test('clearCookies', () async { when(mockWebsiteDataStore.removeDataOfTypes( - {WKWebsiteDataTypes.cookies}, any)) + {WKWebsiteDataType.cookies}, any)) .thenAnswer((_) => Future.value(true)); expect(cookieManager.clearCookies(), completion(true)); when(mockWebsiteDataStore.removeDataOfTypes( - {WKWebsiteDataTypes.cookies}, any)) + {WKWebsiteDataType.cookies}, any)) .thenAnswer((_) => Future.value(false)); expect(cookieManager.clearCookies(), completion(false)); }); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart index a85c57f7bdb3..8d7751da785f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart @@ -79,7 +79,7 @@ class MockWKWebsiteDataStore extends _i1.Mock returnValue: _FakeWKHttpCookieStore_0()) as _i2.WKHttpCookieStore); @override _i3.Future removeDataOfTypes( - Set<_i2.WKWebsiteDataTypes>? dataTypes, DateTime? since) => + Set<_i2.WKWebsiteDataType>? dataTypes, DateTime? since) => (super.noSuchMethod( Invocation.method(#removeDataOfTypes, [dataTypes, since]), returnValue: Future.value(false)) as _i3.Future); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index e5042dab78e4..d77feee22dd1 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -330,7 +330,7 @@ void main() { final List javaScriptChannels = verifyInOrder([ mockUserContentController.removeAllUserScripts(), - mockUserContentController.removeAllScriptMessageHandlers(), + mockUserContentController.removeScriptMessageHandler('myChannel'), mockUserContentController.addScriptMessageHandler( captureAny, captureAny, @@ -372,7 +372,6 @@ void main() { )); verify(mockUserContentController.removeAllUserScripts()); - verify(mockUserContentController.removeAllScriptMessageHandlers()); verifyNever(mockUserContentController.addScriptMessageHandler( any, any, @@ -769,11 +768,11 @@ void main() { await buildWidget(tester); when( mockWebsiteDataStore.removeDataOfTypes( - { - WKWebsiteDataTypes.memoryCache, - WKWebsiteDataTypes.diskCache, - WKWebsiteDataTypes.offlineWebApplicationCache, - WKWebsiteDataTypes.localStroage, + { + WKWebsiteDataType.memoryCache, + WKWebsiteDataType.diskCache, + WKWebsiteDataType.offlineWebApplicationCache, + WKWebsiteDataType.localStorage, }, DateTime.fromMillisecondsSinceEpoch(0), ), @@ -835,12 +834,15 @@ void main() { await testController.removeJavascriptChannels({'c'}); - verify(mockUserContentController.removeAllScriptMessageHandlers()); verify(mockUserContentController.removeAllUserScripts()); + verify(mockUserContentController.removeScriptMessageHandler('c')); + verify(mockUserContentController.removeScriptMessageHandler('d')); final List javaScriptChannels = verify( mockUserContentController.addScriptMessageHandler( - captureAny, captureAny), + captureAny, + captureAny, + ), ).captured; expect( javaScriptChannels[0], diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index 1138d13f0c25..066f33a9774e 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -434,7 +434,7 @@ class MockWKWebsiteDataStore extends _i1.Mock returnValue: _FakeWKHttpCookieStore_6()) as _i3.WKHttpCookieStore); @override _i5.Future removeDataOfTypes( - Set<_i3.WKWebsiteDataTypes>? dataTypes, DateTime? since) => + Set<_i3.WKWebsiteDataType>? dataTypes, DateTime? since) => (super.noSuchMethod( Invocation.method(#removeDataOfTypes, [dataTypes, since]), returnValue: Future.value(false)) as _i5.Future); From 2ca342c22777a5ecb63999bcb969382af00f9f43 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 10 May 2022 19:19:10 -0400 Subject: [PATCH 544/600] Roll Flutter from 4bed76757db7 to df7111a848cb (42 revisions) (#5696) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 40188c21bd43..bca64d8293a3 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -4bed76757db7a4eea74d77d5a63804ef52198f37 +df7111a848cb0cc076a9c89d48332f0ec2700c45 From 3993fa7126376c2362bf078a86def2cf959091dd Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 11 May 2022 11:48:47 -0400 Subject: [PATCH 545/600] Re-sync analysis_options.yaml with flutter/flutter (#5695) The analysis options have gotten behind; this re-syncs to the current state of flutter/flutter. For options that are non-trivial to enable, either because they are non-trivial to fix, or touch a very large number of files, they are locally disabled with clear "LOCAL CHANGE" markers so that it's obvious where we are out of sync. For options that are simple to resolve, they are enabled in the PR. Part of https://github.com/flutter/flutter/issues/76229 --- analysis_options.yaml | 135 ++++++++++-------- packages/camera/camera/CHANGELOG.md | 4 + packages/camera/camera/example/lib/main.dart | 5 +- packages/camera/camera/pubspec.yaml | 2 +- packages/camera/camera_web/CHANGELOG.md | 4 + .../example/integration_test/camera_test.dart | 2 +- .../integration_test/camera_web_test.dart | 6 +- .../integration_test/helpers/mocks.dart | 4 +- .../camera/camera_web/lib/src/camera.dart | 3 + .../camera_web/lib/src/camera_service.dart | 2 +- .../camera/camera_web/lib/src/camera_web.dart | 4 +- packages/camera/camera_web/pubspec.yaml | 2 +- .../file_selector/file_selector/CHANGELOG.md | 4 + .../test/file_selector_test.dart | 2 + .../file_selector_web/CHANGELOG.md | 4 + .../file_selector_web_test.dart | 42 +++--- .../file_selector_web/lib/src/utils.dart | 2 +- .../file_selector_web/pubspec.yaml | 2 +- .../google_maps_flutter/CHANGELOG.md | 4 + .../example/lib/animate_camera.dart | 1 + .../example/lib/move_camera.dart | 1 + .../example/lib/place_circle.dart | 1 + .../example/lib/place_marker.dart | 3 +- .../example/lib/place_polygon.dart | 1 + .../example/lib/place_polyline.dart | 1 + .../example/lib/scrolling_map.dart | 2 +- .../example/lib/snapshot.dart | 1 + .../example/lib/tile_overlay.dart | 1 + .../google_sign_in_web/CHANGELOG.md | 4 + .../example/integration_test/auth2_test.dart | 2 +- .../integration_test/gapi_utils_test.dart | 2 + .../integration_test/src/test_utils.dart | 2 +- .../lib/google_sign_in_web.dart | 2 +- .../lib/src/generated/gapiauth2.dart | 19 +-- .../google_sign_in_web/lib/src/utils.dart | 1 + .../google_sign_in_web/pubspec.yaml | 2 +- .../image_picker/image_picker/CHANGELOG.md | 4 + .../image_picker/example/lib/main.dart | 11 +- .../image_picker/image_picker/pubspec.yaml | 2 +- .../image_picker_android/CHANGELOG.md | 4 + .../example/lib/main.dart | 11 +- .../image_picker_android/pubspec.yaml | 2 +- .../image_picker_for_web/CHANGELOG.md | 4 + .../integration_test/image_resizer_test.dart | 4 +- .../lib/src/image_resizer.dart | 3 +- .../image_picker_for_web/pubspec.yaml | 2 +- .../image_picker_ios/CHANGELOG.md | 4 + .../image_picker_ios/example/lib/main.dart | 55 +------ .../image_picker_ios/pubspec.yaml | 2 +- .../CHANGELOG.md | 4 + .../test/picked_file_io_test.dart | 2 +- .../image_picker_windows/CHANGELOG.md | 4 + .../example/lib/main.dart | 4 +- .../lib/image_picker_windows.dart | 2 +- .../image_picker_windows/pubspec.yaml | 2 +- .../in_app_purchase/CHANGELOG.md | 4 + .../in_app_purchase/example/lib/main.dart | 4 +- .../in_app_purchase/pubspec.yaml | 2 +- .../in_app_purchase_android/CHANGELOG.md | 4 + .../example/lib/main.dart | 4 +- .../billing_client_wrapper.dart | 4 +- ...pp_purchase_android_platform_addition.dart | 8 +- .../types/google_play_product_details.dart | 2 +- .../in_app_purchase_android/pubspec.yaml | 2 +- .../in_app_purchase_storekit/CHANGELOG.md | 4 + .../example/lib/main.dart | 4 +- .../in_app_purchase_storekit/pubspec.yaml | 2 +- .../store_kit_wrappers/sk_product_test.dart | 5 +- .../local_auth_android/CHANGELOG.md | 4 + .../lib/types/auth_messages_android.dart | 2 +- .../local_auth_android/pubspec.yaml | 2 +- .../CHANGELOG.md | 3 +- .../lib/src/method_channel_path_provider.dart | 1 + .../pubspec.yaml | 2 +- .../path_provider_windows/CHANGELOG.md | 4 + .../test/path_provider_windows_test.dart | 5 +- .../plugin_platform_interface/CHANGELOG.md | 1 + .../test/plugin_platform_interface_test.dart | 4 + .../quick_actions/quick_actions/CHANGELOG.md | 4 + .../test/quick_actions_test.dart | 6 +- .../shared_preferences/CHANGELOG.md | 4 + .../lib/shared_preferences.dart | 2 +- .../shared_preferences/pubspec.yaml | 2 +- .../test/shared_preferences_test.dart | 2 +- .../url_launcher/url_launcher/CHANGELOG.md | 4 + .../url_launcher/lib/src/legacy_api.dart | 4 +- .../url_launcher/url_launcher/pubspec.yaml | 2 +- .../mocks/mock_url_launcher_platform.dart | 2 + .../url_launcher_web/CHANGELOG.md | 4 + .../lib/url_launcher_web.dart | 1 + .../url_launcher_web/pubspec.yaml | 2 +- .../video_player/video_player/CHANGELOG.md | 4 + .../integration_test/video_player_test.dart | 2 +- .../video_player/example/lib/main.dart | 2 +- .../video_player/video_player/pubspec.yaml | 2 +- .../test/closed_caption_file_test.dart | 3 +- .../video_player/test/sub_rip_file_test.dart | 2 +- .../video_player_web/CHANGELOG.md | 4 + .../lib/src/video_player.dart | 1 + .../video_player_web/pubspec.yaml | 2 +- .../webview_flutter/CHANGELOG.md | 4 + .../webview_flutter_test.dart | 4 +- .../webview_flutter/example/lib/main.dart | 7 +- .../webview_flutter/pubspec.yaml | 2 +- .../test/webview_flutter_test.dart | 2 +- .../webview_flutter_android/CHANGELOG.md | 4 + .../webview_flutter_test.dart | 4 +- .../example/lib/main.dart | 5 +- .../webview_flutter_android/pubspec.yaml | 2 +- .../webview_flutter_web/CHANGELOG.md | 4 + .../lib/webview_flutter_web.dart | 11 +- .../webview_flutter_web/pubspec.yaml | 2 +- .../test/webview_flutter_web_test.dart | 4 +- .../webview_flutter_wkwebview/CHANGELOG.md | 4 + .../webview_flutter_test.dart | 4 +- .../example/lib/main.dart | 1 + .../lib/src/web_kit_webview_widget.dart | 12 +- .../webview_flutter_wkwebview/pubspec.yaml | 2 +- .../src/common/function_flutter_api_test.dart | 2 +- .../test/src/web_kit/web_kit_test.dart | 6 +- .../test/src/web_kit_webview_widget_test.dart | 16 +-- .../src/create_all_plugins_app_command.dart | 2 +- script/tool/lib/src/custom_test_command.dart | 2 +- script/tool/lib/src/format_command.dart | 5 +- .../tool/lib/src/license_check_command.dart | 3 +- .../lib/src/make_deps_path_based_command.dart | 4 +- .../tool/lib/src/pubspec_check_command.dart | 8 +- .../tool/lib/src/version_check_command.dart | 6 +- script/tool/test/analyze_command_test.dart | 2 +- .../tool/test/common/plugin_command_test.dart | 2 +- script/tool/test/format_command_test.dart | 5 +- .../test/publish_plugin_command_test.dart | 8 +- .../tool/test/pubspec_check_command_test.dart | 4 +- script/tool/test/util.dart | 2 +- .../tool/test/version_check_command_test.dart | 6 +- 135 files changed, 412 insertions(+), 292 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index ba5e0a9c4ced..87f7d6f9840b 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -9,23 +9,21 @@ # Specify analysis options. # -# Until there are meta linter rules, each desired lint must be explicitly enabled. -# See: https://github.com/dart-lang/linter/issues/288 -# # For a list of lints, see: http://dart-lang.github.io/linter/lints/ # See the configuration guide for more -# https://github.com/dart-lang/sdk/tree/master/pkg/analyzer#configuring-the-analyzer +# https://github.com/dart-lang/sdk/tree/main/pkg/analyzer#configuring-the-analyzer # # There are other similar analysis options files in the flutter repos, # which should be kept in sync with this file: # # - analysis_options.yaml (this file) # - packages/flutter/lib/analysis_options_user.yaml -# - https://github.com/flutter/plugins/blob/master/analysis_options.yaml -# - https://github.com/flutter/engine/blob/master/analysis_options.yaml +# - https://github.com/flutter/flutter/blob/master/analysis_options.yaml +# - https://github.com/flutter/engine/blob/main/analysis_options.yaml +# - https://github.com/flutter/packages/blob/main/analysis_options.yaml # -# This file contains the analysis options used by Flutter tools, such as IntelliJ, -# Android Studio, and the `flutter analyze` command. +# This file contains the analysis options used for code in the flutter/plugins +# repository. analyzer: strong-mode: @@ -36,7 +34,7 @@ analyzer: missing_required_param: warning # treat missing returns as a warning (not a hint) missing_return: warning - # allow having TODOs in the code + # allow having TODO comments in the code todo: ignore # allow self-reference to deprecated members (we do this because otherwise we have # to annotate every member in every test, assert, etc, when we deprecate something) @@ -45,9 +43,10 @@ analyzer: # Stream and not importing dart:async # Please see https://github.com/flutter/flutter/pull/24528 for details. sdk_version_async_exported_from_core: ignore + # Turned off until null-safe rollout is complete. + unnecessary_null_comparison: ignore ### Local flutter/plugins changes ### # Allow null checks for as long as mixed mode is officially supported. - unnecessary_null_comparison: false always_require_non_null_named_parameters: false # not needed with nnbd exclude: # Ignore generated files @@ -58,8 +57,7 @@ analyzer: linter: rules: - # these rules are documented on and in the same order as - # the Dart Lint rules page to make maintenance easier + # This list is derived from the list of all available lints located at # https://github.com/dart-lang/linter/blob/master/example/all.yaml - always_declare_return_types - always_put_control_body_on_new_line @@ -69,62 +67,68 @@ linter: # - always_use_package_imports # we do this commonly - annotate_overrides # - avoid_annotating_with_dynamic # conflicts with always_specify_types - # - avoid_as # required for implicit-casts: true - avoid_bool_literals_in_conditional_expressions - # - avoid_catches_without_on_clauses # we do this commonly - # - avoid_catching_errors # we do this commonly + # - avoid_catches_without_on_clauses # blocked on https://github.com/dart-lang/linter/issues/3023 + # - avoid_catching_errors # blocked on https://github.com/dart-lang/linter/issues/3023 - avoid_classes_with_only_static_members - # - avoid_double_and_int_checks # only useful when targeting JS runtime + - avoid_double_and_int_checks + # - avoid_dynamic_calls # LOCAL CHANGE - Needs to be enabled and violations fixed. - avoid_empty_else - avoid_equals_and_hash_code_on_mutable_classes - # - avoid_escaping_inner_quotes # not yet tested + - avoid_escaping_inner_quotes - avoid_field_initializers_in_const_classes + # - avoid_final_parameters # incompatible with prefer_final_parameters - avoid_function_literals_in_foreach_calls - # - avoid_implementing_value_types # not yet tested + # - avoid_implementing_value_types # LOCAL CHANGE - Needs to be enabled and violations fixed. - avoid_init_to_null - # - avoid_js_rounded_ints # only useful when targeting JS runtime + - avoid_js_rounded_ints + # - avoid_multiple_declarations_per_line # seems to be a stylistic choice we don't subscribe to - avoid_null_checks_in_equality_operators - # - avoid_positional_boolean_parameters # not yet tested - # - avoid_print # not yet tested + # - avoid_positional_boolean_parameters # would have been nice to enable this but by now there's too many places that break it + # - avoid_print # LOCAL CHANGE - Needs to be enabled and violations fixed. # - avoid_private_typedef_functions # we prefer having typedef (discussion in https://github.com/flutter/flutter/pull/16356) - # - avoid_redundant_argument_values # not yet tested + # - avoid_redundant_argument_values # LOCAL CHANGE - Needs to be enabled and violations fixed. - avoid_relative_lib_imports - avoid_renaming_method_parameters - avoid_return_types_on_setters - # - avoid_returning_null # there are plenty of valid reasons to return null - # - avoid_returning_null_for_future # not yet tested + # - avoid_returning_null # still violated by some pre-nnbd code that we haven't yet migrated + - avoid_returning_null_for_future - avoid_returning_null_for_void - # - avoid_returning_this # there are plenty of valid reasons to return this - # - avoid_setters_without_getters # not yet tested + # - avoid_returning_this # there are enough valid reasons to return `this` that this lint ends up with too many false positives + - avoid_setters_without_getters - avoid_shadowing_type_parameters - avoid_single_cascade_in_expression_statements - avoid_slow_async_io - # - avoid_type_to_string # we do this commonly + - avoid_type_to_string - avoid_types_as_parameter_names # - avoid_types_on_closure_parameters # conflicts with always_specify_types - # - avoid_unnecessary_containers # not yet tested + - avoid_unnecessary_containers - avoid_unused_constructor_parameters - avoid_void_async - # - avoid_web_libraries_in_flutter # not yet tested + # - avoid_web_libraries_in_flutter # we use web libraries in web-specific code, and our tests prevent us from using them elsewhere - await_only_futures - camel_case_extensions - camel_case_types - cancel_subscriptions - # - cascade_invocations # not yet tested + # - cascade_invocations # doesn't match the typical style of this repo - cast_nullable_to_non_nullable # - close_sinks # not reliable enough - # - comment_references # blocked on https://github.com/flutter/flutter/issues/20765 + # - comment_references # blocked on https://github.com/dart-lang/linter/issues/1142 + # - conditional_uri_does_not_exist # not yet tested # - constant_identifier_names # needs an opt-out https://github.com/dart-lang/linter/issues/204 - control_flow_in_finally # - curly_braces_in_flow_control_structures # not required by flutter style - # - diagnostic_describe_all_properties # not yet tested + # - depend_on_referenced_packages # LOCAL CHANGE - Needs to be enabled and violations fixed. + - deprecated_consistency + # - diagnostic_describe_all_properties # enabled only at the framework level (packages/flutter/lib) - directives_ordering - # - do_not_use_environment # we do this commonly + # - do_not_use_environment # there are appropriate times to use the environment, especially in our tests and build logic - empty_catches - empty_constructor_bodies - empty_statements + - eol_at_end_of_file - exhaustive_cases - # - file_names # not yet tested + - file_names - flutter_style_todos - hash_and_equals - implementation_imports @@ -137,22 +141,25 @@ linter: - library_private_types_in_public_api # - lines_longer_than_80_chars # not required by flutter style - list_remove_unrelated_type - # - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/sdk/issues/34181 - # - missing_whitespace_between_adjacent_strings # not yet tested + # - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/linter/issues/453 + - missing_whitespace_between_adjacent_strings - no_adjacent_strings_in_list - # - no_default_cases # too many false positives + # - no_default_cases # LOCAL CHANGE - Needs to be enabled and violations fixed. - no_duplicate_case_values + - no_leading_underscores_for_library_prefixes + # - no_leading_underscores_for_local_identifiers # LOCAL CHANGE - Needs to be enabled and violations fixed. - no_logic_in_create_state # - no_runtimeType_toString # ok in tests; we enable this only in packages/ - non_constant_identifier_names + - noop_primitive_operations - null_check_on_nullable_type_parameter - # - null_closures # not required by flutter style + - null_closures # - omit_local_variable_types # opposite of always_specify_types # - one_member_abstracts # too many false positives - # - only_throw_errors # https://github.com/flutter/flutter/issues/5792 + # - only_throw_errors # this does get disabled in a few places where we have legacy code that uses strings et al # LOCAL CHANGE - Needs to be enabled and violations fixed. - overridden_fields - package_api_docs - # - package_names # non conforming packages in sdk + - package_names - package_prefixed_library_names # - parameter_assignments # we do this commonly - prefer_adjacent_string_concatenation @@ -172,74 +179,90 @@ linter: - prefer_final_fields - prefer_final_in_for_each - prefer_final_locals + # - prefer_final_parameters # we should enable this one day when it can be auto-fixed (https://github.com/dart-lang/linter/issues/3104), see also parameter_assignments - prefer_for_elements_to_map_fromIterable - prefer_foreach - # - prefer_function_declarations_over_variables # not yet tested + - prefer_function_declarations_over_variables - prefer_generic_function_type_aliases - prefer_if_elements_to_conditional_expressions - prefer_if_null_operators - prefer_initializing_formals - prefer_inlined_adds - # - prefer_int_literals # not yet tested - # - prefer_interpolation_to_compose_strings # not yet tested + # - prefer_int_literals # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#use-double-literals-for-double-constants + - prefer_interpolation_to_compose_strings - prefer_is_empty - prefer_is_not_empty - prefer_is_not_operator - prefer_iterable_whereType - # - prefer_mixin # https://github.com/dart-lang/language/issues/32 - # - prefer_null_aware_operators # disable until NNBD, see https://github.com/flutter/flutter/pull/32711#issuecomment-492930932 - # - prefer_relative_imports # not yet tested + # - prefer_mixin # Has false positives, see https://github.com/dart-lang/linter/issues/3018 + # - prefer_null_aware_method_calls # "call()" is confusing to people new to the language since it's not documented anywhere + - prefer_null_aware_operators + # - prefer_relative_imports # LOCAL CHANGE - Needs to be enabled and violations fixed. - prefer_single_quotes - prefer_spread_collections - prefer_typing_uninitialized_variables - prefer_void_to_null - # - provide_deprecation_message # not yet tested + - provide_deprecation_message # - public_member_api_docs # enabled on a case-by-case basis; see e.g. packages/analysis_options.yaml - recursive_getters - # - sized_box_for_whitespace # not yet tested + # - require_trailing_commas # blocked on https://github.com/dart-lang/sdk/issues/47441 + - secure_pubspec_urls + # - sized_box_for_whitespace # LOCAL CHANGE - Needs to be enabled and violations fixed. + # - sized_box_shrink_expand # not yet tested - slash_for_doc_comments - sort_child_properties_last - sort_constructors_first + # - sort_pub_dependencies # prevents separating pinned transitive dependencies - sort_unnamed_constructors_first - test_types_in_equals - throw_in_finally - tighten_type_of_initializing_formals # - type_annotate_public_apis # subset of always_specify_types - type_init_formals - # - unawaited_futures # too many false positives - # - unnecessary_await_in_return # not yet tested + # - unawaited_futures # too many false positives, especially with the way AnimationController works + # - unnecessary_await_in_return # LOCAL CHANGE - Needs to be enabled and violations fixed. - unnecessary_brace_in_string_interps - unnecessary_const + - unnecessary_constructor_name # - unnecessary_final # conflicts with prefer_final_locals - unnecessary_getters_setters # - unnecessary_lambdas # has false positives: https://github.com/dart-lang/linter/issues/498 + - unnecessary_late - unnecessary_new - unnecessary_null_aware_assignments - # - unnecessary_null_checks # not yet tested + - unnecessary_null_checks - unnecessary_null_in_if_null_operators - unnecessary_nullable_for_final_variable_declarations - unnecessary_overrides - unnecessary_parenthesis - # - unnecessary_raw_strings # not yet tested + # - unnecessary_raw_strings # what's "necessary" is a matter of opinion; consistency across strings can help readability more than this lint - unnecessary_statements - unnecessary_string_escapes - unnecessary_string_interpolations - unnecessary_this - unrelated_type_equality_checks - # - unsafe_html # not yet tested + - unsafe_html + # - use_build_context_synchronously # LOCAL CHANGE - Needs to be enabled and violations fixed. + # - use_colored_box # not yet tested + # - use_decorated_box # not yet tested + # - use_enums # not yet tested - use_full_hex_values_for_flutter_colors - # - use_function_type_syntax_for_parameters # not yet tested + - use_function_type_syntax_for_parameters + - use_if_null_to_convert_nulls_to_bools - use_is_even_rather_than_modulo - use_key_in_widget_constructors - use_late_for_private_fields_and_variables + # - use_named_constants # LOCAL CHANGE - Needs to be enabled and violations fixed. - use_raw_strings - use_rethrow_when_possible - # - use_setters_to_change_properties # not yet tested + - use_setters_to_change_properties # - use_string_buffers # has false positives: https://github.com/dart-lang/sdk/issues/34182 + - use_super_parameters + - use_test_throws_matchers # - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review - valid_regexps - void_checks - ### Local flutter/plugins changes ### + ### Local flutter/plugins additions ### # These are from flutter/flutter/packages, so will need to be preserved # separately when moving to a shared file. - no_runtimeType_toString # use objectRuntimeType from package:foundation diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index bf0ccf86a82e..4715644a0c13 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.4+23 + +* Minor fixes for new analysis options. + ## 0.9.4+22 * Removes unnecessary imports. diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index a645326f2803..10a8a6f75e16 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -575,14 +575,13 @@ class _CameraExampleHomeState extends State Widget _cameraTogglesRowWidget() { final List toggles = []; - final Null Function(CameraDescription? description) onChanged = - (CameraDescription? description) { + void onChanged(CameraDescription? description) { if (description == null) { return; } onNewCameraSelected(description); - }; + } if (_cameras.isEmpty) { _ambiguate(SchedulerBinding.instance)?.addPostFrameCallback((_) async { diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index fde6663844c2..9d56f1b6e4da 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+22 +version: 0.9.4+23 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index 7a24e12e5029..5a9fb6608323 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.1+6 + +* Minor fixes for new analysis options. + ## 0.2.1+5 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/camera/camera_web/example/integration_test/camera_test.dart b/packages/camera/camera_web/example/integration_test/camera_test.dart index 20d1cbaf8e36..50451b9778af 100644 --- a/packages/camera/camera_web/example/integration_test/camera_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_test.dart @@ -1477,7 +1477,7 @@ void main() { }); group('dispose', () { - testWidgets('resets the video element\'s source', + testWidgets("resets the video element's source", (WidgetTester tester) async { final Camera camera = Camera( textureId: textureId, diff --git a/packages/camera/camera_web/example/integration_test/camera_web_test.dart b/packages/camera/camera_web/example/integration_test/camera_web_test.dart index 5a564dd66021..143783f5225b 100644 --- a/packages/camera/camera_web/example/integration_test/camera_web_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_web_test.dart @@ -498,7 +498,7 @@ void main() { isA().having( (CameraException e) => e.code, 'code', - exception.code.toString(), + exception.code, ), ), ); @@ -759,7 +759,7 @@ void main() { isA().having( (PlatformException e) => e.code, 'code', - exception.name.toString(), + exception.name, ), ), ); @@ -2495,7 +2495,7 @@ void main() { equals( CameraErrorEvent( cameraId, - 'Error code: ${CameraErrorCode.abort}, error message: The video element\'s source has not fully loaded.', + "Error code: ${CameraErrorCode.abort}, error message: The video element's source has not fully loaded.", ), ), ); diff --git a/packages/camera/camera_web/example/integration_test/helpers/mocks.dart b/packages/camera/camera_web/example/integration_test/helpers/mocks.dart index 3d9550fb7ab8..521c4bf5a18d 100644 --- a/packages/camera/camera_web/example/integration_test/helpers/mocks.dart +++ b/packages/camera/camera_web/example/integration_test/helpers/mocks.dart @@ -113,8 +113,8 @@ class FakeElementStream extends Fake final Stream _stream; @override - StreamSubscription listen(void onData(T event)?, - {Function? onError, void onDone()?, bool? cancelOnError}) { + StreamSubscription listen(void Function(T event)? onData, + {Function? onError, void Function()? onDone, bool? cancelOnError}) { return _stream.listen( onData, onError: onError, diff --git a/packages/camera/camera_web/lib/src/camera.dart b/packages/camera/camera_web/lib/src/camera.dart index 71368c65e99d..210a0df59eec 100644 --- a/packages/camera/camera_web/lib/src/camera.dart +++ b/packages/camera/camera_web/lib/src/camera.dart @@ -138,6 +138,9 @@ class Camera { /// A builder to merge a list of blobs into a single blob. @visibleForTesting + // TODO(stuartmorgan): Remove this 'ignore' once we don't analyze using 2.10 + // any more. It's a false positive that is fixed in later versions. + // ignore: prefer_function_declarations_over_variables html.Blob Function(List blobs, String type) blobBuilder = (List blobs, String type) => html.Blob(blobs, type); diff --git a/packages/camera/camera_web/lib/src/camera_service.dart b/packages/camera/camera_web/lib/src/camera_service.dart index f15845cf823b..b118169f0618 100644 --- a/packages/camera/camera_web/lib/src/camera_service.dart +++ b/packages/camera/camera_web/lib/src/camera_service.dart @@ -82,7 +82,7 @@ class CameraService { throw CameraWebException( cameraId, CameraErrorCode.type, - 'The camera options are incorrect or attempted' + 'The camera options are incorrect or attempted ' 'to access the media input from an insecure context.', ); case 'AbortError': diff --git a/packages/camera/camera_web/lib/src/camera_web.dart b/packages/camera/camera_web/lib/src/camera_web.dart index 6f9f10d68f84..26f965d49e16 100644 --- a/packages/camera/camera_web/lib/src/camera_web.dart +++ b/packages/camera/camera_web/lib/src/camera_web.dart @@ -290,7 +290,7 @@ class CameraPlugin extends CameraPlatform { cameraEventStreamController.add( CameraErrorEvent( cameraId, - 'Error code: ${CameraErrorCode.abort}, error message: The video element\'s source has not fully loaded.', + "Error code: ${CameraErrorCode.abort}, error message: The video element's source has not fully loaded.", ), ); }); @@ -400,7 +400,7 @@ class CameraPlugin extends CameraPlatform { // This wrapper allows use of both the old and new APIs. dynamic fullScreen() => documentElement.requestFullscreen(); await fullScreen(); - await screenOrientation.lock(orientationType.toString()); + await screenOrientation.lock(orientationType); } else { throw PlatformException( code: CameraErrorCode.orientationNotSupported.toString(), diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index 8bef974190b2..90d119549e86 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_web description: A Flutter plugin for getting information about and controlling the camera on Web. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.2.1+5 +version: 0.2.1+6 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index 17baf9f12469..639783361852 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Minor fixes for new analysis options. + ## 0.8.4+2 * Removes unnecessary imports. diff --git a/packages/file_selector/file_selector/test/file_selector_test.dart b/packages/file_selector/file_selector/test/file_selector_test.dart index fc3e668f9d9e..887ab64c3c0c 100644 --- a/packages/file_selector/file_selector/test/file_selector_test.dart +++ b/packages/file_selector/file_selector/test/file_selector_test.dart @@ -284,10 +284,12 @@ class FakeFileSelector extends Fake this.confirmButtonText = confirmButtonText; } + // ignore: use_setters_to_change_properties void setFileResponse(List files) { this.files = files; } + // ignore: use_setters_to_change_properties void setPathResponse(String path) { this.path = path; } diff --git a/packages/file_selector/file_selector_web/CHANGELOG.md b/packages/file_selector/file_selector_web/CHANGELOG.md index ce9d5590f9a9..3963601e2ac5 100644 --- a/packages/file_selector/file_selector_web/CHANGELOG.md +++ b/packages/file_selector/file_selector_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.1+5 + +* Minor fixes for new analysis options. + ## 0.8.1+4 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/file_selector/file_selector_web/example/integration_test/file_selector_web_test.dart b/packages/file_selector/file_selector_web/example/integration_test/file_selector_web_test.dart index fe57d1d1e15d..43c88a2a4241 100644 --- a/packages/file_selector/file_selector_web/example/integration_test/file_selector_web_test.dart +++ b/packages/file_selector/file_selector_web/example/integration_test/file_selector_web_test.dart @@ -19,10 +19,10 @@ void main() { testWidgets('works', (WidgetTester _) async { final XFile mockFile = createXFile('1001', 'identity.png'); - final MockDomHelper mockDomHelper = MockDomHelper() - ..setFiles([mockFile]) - ..expectAccept('.jpg,.jpeg,image/png,image/*') - ..expectMultiple(false); + final MockDomHelper mockDomHelper = MockDomHelper( + files: [mockFile], + expectAccept: '.jpg,.jpeg,image/png,image/*', + expectMultiple: false); final FileSelectorWeb plugin = FileSelectorWeb(domHelper: mockDomHelper); @@ -49,10 +49,10 @@ void main() { final XFile mockFile1 = createXFile('123456', 'file1.txt'); final XFile mockFile2 = createXFile('', 'file2.txt'); - final MockDomHelper mockDomHelper = MockDomHelper() - ..setFiles([mockFile1, mockFile2]) - ..expectAccept('.txt') - ..expectMultiple(true); + final MockDomHelper mockDomHelper = MockDomHelper( + files: [mockFile1, mockFile2], + expectAccept: '.txt', + expectMultiple: true); final FileSelectorWeb plugin = FileSelectorWeb(domHelper: mockDomHelper); @@ -90,9 +90,17 @@ void main() { } class MockDomHelper implements DomHelper { - List _files = []; - String _expectedAccept = ''; - bool _expectedMultiple = false; + MockDomHelper({ + List files = const [], + String expectAccept = '', + bool expectMultiple = false, + }) : _files = files, + _expectedAccept = expectAccept, + _expectedMultiple = expectMultiple; + + final List _files; + final String _expectedAccept; + final bool _expectedMultiple; @override Future> getFiles({ @@ -106,18 +114,6 @@ class MockDomHelper implements DomHelper { reason: 'Expected "multiple" value does not match.'); return Future>.value(_files); } - - void setFiles(List files) { - _files = files; - } - - void expectAccept(String accept) { - _expectedAccept = accept; - } - - void expectMultiple(bool multiple) { - _expectedMultiple = multiple; - } } XFile createXFile(String content, String name) { diff --git a/packages/file_selector/file_selector_web/lib/src/utils.dart b/packages/file_selector/file_selector_web/lib/src/utils.dart index 6a534645fda6..fe8d1f433647 100644 --- a/packages/file_selector/file_selector_web/lib/src/utils.dart +++ b/packages/file_selector/file_selector_web/lib/src/utils.dart @@ -36,5 +36,5 @@ void _assertTypeGroupIsValid(XTypeGroup group) { /// Append a dot at the beggining if it is not there png -> .png String _normalizeExtension(String ext) { - return ext.isNotEmpty && ext[0] != '.' ? '.' + ext : ext; + return ext.isNotEmpty && ext[0] != '.' ? '.$ext' : ext; } diff --git a/packages/file_selector/file_selector_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml index 2e12b6d175a3..488031995e55 100644 --- a/packages/file_selector/file_selector_web/pubspec.yaml +++ b/packages/file_selector/file_selector_web/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_web description: Web platform implementation of file_selector repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.1+4 +version: 0.8.1+5 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index d1f2f926e2ab..07ffbfdb4e5d 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Minor fixes for new analysis options. + ## 2.1.5 * Removes unnecessary imports. diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart index 09df2b98b146..3975d64449b8 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart @@ -28,6 +28,7 @@ class AnimateCamera extends StatefulWidget { class AnimateCameraState extends State { GoogleMapController? mapController; + // ignore: use_setters_to_change_properties void _onMapCreated(GoogleMapController controller) { mapController = controller; } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart index a6bae3009f0b..7fa8a0354eb2 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart @@ -28,6 +28,7 @@ class MoveCamera extends StatefulWidget { class MoveCameraState extends State { GoogleMapController? mapController; + // ignore: use_setters_to_change_properties void _onMapCreated(GoogleMapController controller) { mapController = controller; } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart index ef5033cfa1ee..7cbb63ac4e99 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart @@ -48,6 +48,7 @@ class PlaceCircleBodyState extends State { int widthsIndex = 0; List widths = [10, 20, 5]; + // ignore: use_setters_to_change_properties void _onMapCreated(GoogleMapController controller) { this.controller = controller; } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart index 1238d61547b8..b8efc4e52562 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart @@ -43,6 +43,7 @@ class PlaceMarkerBodyState extends State { int _markerIdCounter = 1; LatLng? markerPosition; + // ignore: use_setters_to_change_properties void _onMapCreated(GoogleMapController controller) { this.controller = controller; } @@ -207,7 +208,7 @@ class PlaceMarkerBodyState extends State { Future _changeInfo(MarkerId markerId) async { final Marker marker = markers[markerId]!; - final String newSnippet = marker.infoWindow.snippet! + '*'; + final String newSnippet = '${marker.infoWindow.snippet!}*'; setState(() { markers[markerId] = marker.copyWith( infoWindowParam: marker.infoWindow.copyWith( diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart index f1932141b8ab..cb0cc56d4754 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart @@ -49,6 +49,7 @@ class PlacePolygonBodyState extends State { int widthsIndex = 0; List widths = [10, 20, 5]; + // ignore: use_setters_to_change_properties void _onMapCreated(GoogleMapController controller) { this.controller = controller; } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart index b3a637ce7d15..7a7c5d2f4a16 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart @@ -77,6 +77,7 @@ class PlacePolylineBodyState extends State { [PatternItem.dot, PatternItem.gap(10.0)], ]; + // ignore: use_setters_to_change_properties void _onMapCreated(GoogleMapController controller) { this.controller = controller; } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart index ca9d3962ddd7..3d676e0713fd 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart @@ -66,7 +66,7 @@ class ScrollingMapBody extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: 30.0), child: Column( children: [ - const Text('This map doesn\'t consume the vertical drags.'), + const Text("This map doesn't consume the vertical drags."), const Padding( padding: EdgeInsets.only(bottom: 12.0), child: diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart index 849a9f469938..fbc7ae2a3e24 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart @@ -68,6 +68,7 @@ class _SnapshotBodyState extends State<_SnapshotBody> { ); } + // ignore: use_setters_to_change_properties void onMapCreated(GoogleMapController controller) { _mapController = controller; } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart index 81dfc2815866..d88e09988dc7 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart @@ -35,6 +35,7 @@ class TileOverlayBodyState extends State { GoogleMapController? controller; TileOverlay? _tileOverlay; + // ignore: use_setters_to_change_properties void _onMapCreated(GoogleMapController controller) { this.controller = controller; } diff --git a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md index 6e87bebf5d02..9a6b07650c64 100644 --- a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.10.1+2 + +* Minor fixes for new analysis options. + ## 0.10.1+1 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_test.dart b/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_test.dart index 9db024361580..d8c7655a11c4 100644 --- a/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_test.dart +++ b/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_test.dart @@ -117,7 +117,7 @@ void main() { expect(plugin.init(hostedDomain: ''), throwsAssertionError); }); - testWidgets('Init doesn\'t accept spaces in scopes', + testWidgets("Init doesn't accept spaces in scopes", (WidgetTester tester) async { expect( plugin.init( diff --git a/packages/google_sign_in/google_sign_in_web/example/integration_test/gapi_utils_test.dart b/packages/google_sign_in/google_sign_in_web/example/integration_test/gapi_utils_test.dart index 1447093d4115..b341d1d6b96d 100644 --- a/packages/google_sign_in/google_sign_in_web/example/integration_test/gapi_utils_test.dart +++ b/packages/google_sign_in/google_sign_in_web/example/integration_test/gapi_utils_test.dart @@ -51,10 +51,12 @@ class FakeGoogleUser extends Fake implements gapi.GoogleUser { @override gapi.BasicProfile? getBasicProfile() => _basicProfile; + // ignore: use_setters_to_change_properties void setIsSignedIn(bool isSignedIn) { _isSignedIn = isSignedIn; } + // ignore: use_setters_to_change_properties void setBasicProfile(gapi.BasicProfile basicProfile) { _basicProfile = basicProfile; } diff --git a/packages/google_sign_in/google_sign_in_web/example/integration_test/src/test_utils.dart b/packages/google_sign_in/google_sign_in_web/example/integration_test/src/test_utils.dart index 89f9b55f3ddf..56aa61df136e 100644 --- a/packages/google_sign_in/google_sign_in_web/example/integration_test/src/test_utils.dart +++ b/packages/google_sign_in/google_sign_in_web/example/integration_test/src/test_utils.dart @@ -6,5 +6,5 @@ import 'dart:convert'; String toBase64Url(String contents) { // Open the file - return 'data:text/javascript;base64,' + base64.encode(utf8.encode(contents)); + return 'data:text/javascript;base64,${base64.encode(utf8.encode(contents))}'; } diff --git a/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart b/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart index 533c353df310..ae6180d34acb 100644 --- a/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart +++ b/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart @@ -81,7 +81,7 @@ class GoogleSignInPlugin extends GoogleSignInPlatform { assert( !scopes.any((String scope) => scope.contains(' ')), - 'OAuth 2.0 Scopes for Google APIs can\'t contain spaces.' + "OAuth 2.0 Scopes for Google APIs can't contain spaces. " 'Check https://developers.google.com/identity/protocols/googlescopes ' 'for a list of valid OAuth 2.0 scopes.'); diff --git a/packages/google_sign_in/google_sign_in_web/lib/src/generated/gapiauth2.dart b/packages/google_sign_in/google_sign_in_web/lib/src/generated/gapiauth2.dart index 8e23713c90e9..e1721668f41f 100644 --- a/packages/google_sign_in/google_sign_in_web/lib/src/generated/gapiauth2.dart +++ b/packages/google_sign_in/google_sign_in_web/lib/src/generated/gapiauth2.dart @@ -57,8 +57,8 @@ class GoogleAuth { /// Calls the onInit function when the GoogleAuth object is fully initialized, or calls the onFailure function if /// initialization fails. - external dynamic then(dynamic onInit(GoogleAuth googleAuth), - [dynamic onFailure(GoogleAuthInitFailureError reason)]); + external dynamic then(dynamic Function(GoogleAuth googleAuth) onInit, + [dynamic Function(GoogleAuthInitFailureError reason) onFailure]); /// Signs out all accounts from the application. external dynamic signOut(); @@ -70,8 +70,8 @@ class GoogleAuth { external dynamic attachClickHandler( dynamic container, SigninOptions options, - dynamic onsuccess(GoogleUser googleUser), - dynamic onfailure(String reason)); + dynamic Function(GoogleUser googleUser) onsuccess, + dynamic Function(String reason) onfailure); } @anonymous @@ -104,7 +104,7 @@ abstract class IsSignedIn { external bool get(); /// Listen for changes in the current user's sign-in state. - external void listen(dynamic listener(bool signedIn)); + external void listen(dynamic Function(bool signedIn) listener); } @anonymous @@ -116,7 +116,7 @@ abstract class CurrentUser { external GoogleUser get(); /// Listen for changes in currentUser. - external void listen(dynamic listener(GoogleUser user)); + external void listen(dynamic Function(GoogleUser user) listener); } @anonymous @@ -440,7 +440,7 @@ external GoogleAuth? getAuthInstance(); /// Reference: https://developers.google.com/api-client-library/javascript/reference/referencedocs#gapiauth2authorizeparams-callback @JS('gapi.auth2.authorize') external void authorize( - AuthorizeConfig params, void callback(AuthorizeResponse response)); + AuthorizeConfig params, void Function(AuthorizeResponse response) callback); // End module gapi.auth2 // Module gapi.signin2 @@ -497,6 +497,7 @@ external void render( @JS() abstract class Promise { external factory Promise( - void executor(void resolve(T result), Function reject)); - external Promise then(void onFulfilled(T result), [Function onRejected]); + void Function(void Function(T result) resolve, Function reject) executor); + external Promise then(void Function(T result) onFulfilled, + [Function onRejected]); } diff --git a/packages/google_sign_in/google_sign_in_web/lib/src/utils.dart b/packages/google_sign_in/google_sign_in_web/lib/src/utils.dart index cae20d28db44..72424d8ea15b 100644 --- a/packages/google_sign_in/google_sign_in_web/lib/src/utils.dart +++ b/packages/google_sign_in/google_sign_in_web/lib/src/utils.dart @@ -29,6 +29,7 @@ Future injectJSLibraries( final html.ScriptElement script = html.ScriptElement() ..async = true ..defer = true + // ignore: unsafe_html ..src = library; // TODO(ditman): add a timeout race to fail this future loading.add(script.onLoad.first); diff --git a/packages/google_sign_in/google_sign_in_web/pubspec.yaml b/packages/google_sign_in/google_sign_in_web/pubspec.yaml index 3dcd0e8eef29..a270af985ea7 100644 --- a/packages/google_sign_in/google_sign_in_web/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_web/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Google Sign-In, a secure authentication system for signing in with a Google account on Android, iOS and Web. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 0.10.1+1 +version: 0.10.1+2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index a384a5272e05..156b9a3a6d23 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.5+2 + +* Minor fixes for new analysis options. + ## 0.8.5+1 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/image_picker/image_picker/example/lib/main.dart b/packages/image_picker/image_picker/example/lib/main.dart index a6f0e83c3abb..4eecc5fa2a1a 100755 --- a/packages/image_picker/image_picker/example/lib/main.dart +++ b/packages/image_picker/image_picker/example/lib/main.dart @@ -40,7 +40,7 @@ class MyHomePage extends StatefulWidget { class _MyHomePageState extends State { List? _imageFileList; - set _imageFile(XFile? value) { + void _setImageFileListFromFile(XFile? value) { _imageFileList = value == null ? null : [value]; } @@ -118,7 +118,7 @@ class _MyHomePageState extends State { imageQuality: quality, ); setState(() { - _imageFile = pickedFile; + _setImageFileListFromFile(pickedFile); }); } catch (e) { setState(() { @@ -228,8 +228,11 @@ class _MyHomePageState extends State { } else { isVideo = false; setState(() { - _imageFile = response.file; - _imageFileList = response.files; + if (response.files == null) { + _setImageFileListFromFile(response.file); + } else { + _imageFileList = response.files; + } }); } } else { diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 818486d8e145..cc34d8ab33f5 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.5+1 +version: 0.8.5+2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/image_picker/image_picker_android/CHANGELOG.md b/packages/image_picker/image_picker_android/CHANGELOG.md index 0514fc33d420..eede63435026 100644 --- a/packages/image_picker/image_picker_android/CHANGELOG.md +++ b/packages/image_picker/image_picker_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.4+13 + +* Minor fixes for new analysis options. + ## 0.8.4+12 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/image_picker/image_picker_android/example/lib/main.dart b/packages/image_picker/image_picker_android/example/lib/main.dart index d56aeb866195..212e064cc6e5 100755 --- a/packages/image_picker/image_picker_android/example/lib/main.dart +++ b/packages/image_picker/image_picker_android/example/lib/main.dart @@ -40,7 +40,7 @@ class MyHomePage extends StatefulWidget { class _MyHomePageState extends State { List? _imageFileList; - set _imageFile(XFile? value) { + void _setImageFileListFromFile(XFile? value) { _imageFileList = value == null ? null : [value]; } @@ -118,7 +118,7 @@ class _MyHomePageState extends State { imageQuality: quality, ); setState(() { - _imageFile = pickedFile; + _setImageFileListFromFile(pickedFile); }); } catch (e) { setState(() { @@ -228,8 +228,11 @@ class _MyHomePageState extends State { } else { isVideo = false; setState(() { - _imageFile = response.file; - _imageFileList = response.files; + if (response.files == null) { + _setImageFileListFromFile(response.file); + } else { + _imageFileList = response.files; + } }); } } else { diff --git a/packages/image_picker/image_picker_android/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml index 90d136c2c89b..095534654ac5 100755 --- a/packages/image_picker/image_picker_android/pubspec.yaml +++ b/packages/image_picker/image_picker_android/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_android description: Android implementation of the image_picker plugin. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.4+12 +version: 0.8.4+13 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/image_picker/image_picker_for_web/CHANGELOG.md b/packages/image_picker/image_picker_for_web/CHANGELOG.md index c33b3b9981de..b69ba597aca3 100644 --- a/packages/image_picker/image_picker_for_web/CHANGELOG.md +++ b/packages/image_picker/image_picker_for_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.8 + +* Minor fixes for new analysis options. + ## 2.1.7 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart b/packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart index 91794a7d5e78..1efd7b29a810 100644 --- a/packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart +++ b/packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart @@ -33,8 +33,8 @@ void main() { testWidgets('image is loaded correctly ', (WidgetTester tester) async { final html.ImageElement imageElement = await imageResizer.loadImage(pngFile.path); - expect(imageElement.width!, 10); - expect(imageElement.height!, 10); + expect(imageElement.width, 10); + expect(imageElement.height, 10); }); testWidgets( diff --git a/packages/image_picker/image_picker_for_web/lib/src/image_resizer.dart b/packages/image_picker/image_picker_for_web/lib/src/image_resizer.dart index e063099e3319..ba794acae3be 100644 --- a/packages/image_picker/image_picker_for_web/lib/src/image_resizer.dart +++ b/packages/image_picker/image_picker_for_web/lib/src/image_resizer.dart @@ -39,6 +39,7 @@ class ImageResizer { final Completer imageLoadCompleter = Completer(); final html.ImageElement imageElement = html.ImageElement(); + // ignore: unsafe_html imageElement.src = blobUrl; imageElement.onLoad.listen((html.Event event) { @@ -81,7 +82,7 @@ class ImageResizer { await canvas.toBlob(originalFile.mimeType, calculatedImageQuality); return XFile(html.Url.createObjectUrlFromBlob(blob), mimeType: originalFile.mimeType, - name: 'scaled_' + originalFile.name, + name: 'scaled_${originalFile.name}', lastModified: DateTime.now(), length: blob.size); } diff --git a/packages/image_picker/image_picker_for_web/pubspec.yaml b/packages/image_picker/image_picker_for_web/pubspec.yaml index b0c5deb0da7a..0b2c6d2fc0ff 100644 --- a/packages/image_picker/image_picker_for_web/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_for_web description: Web platform implementation of image_picker repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_for_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 2.1.7 +version: 2.1.8 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index af391db02689..96b1c7f0d0a4 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.5+2 + +* Minor fixes for new analysis options. + ## 0.8.5+1 * Removes unnecessary imports. diff --git a/packages/image_picker/image_picker_ios/example/lib/main.dart b/packages/image_picker/image_picker_ios/example/lib/main.dart index d56aeb866195..c5372b8e7ad8 100755 --- a/packages/image_picker/image_picker_ios/example/lib/main.dart +++ b/packages/image_picker/image_picker_ios/example/lib/main.dart @@ -40,7 +40,7 @@ class MyHomePage extends StatefulWidget { class _MyHomePageState extends State { List? _imageFileList; - set _imageFile(XFile? value) { + void _setImageFileListFromFile(XFile? value) { _imageFileList = value == null ? null : [value]; } @@ -118,7 +118,7 @@ class _MyHomePageState extends State { imageQuality: quality, ); setState(() { - _imageFile = pickedFile; + _setImageFileListFromFile(pickedFile); }); } catch (e) { setState(() { @@ -216,27 +216,6 @@ class _MyHomePageState extends State { } } - Future retrieveLostData() async { - final LostDataResponse response = await _picker.getLostData(); - if (response.isEmpty) { - return; - } - if (response.file != null) { - if (response.type == RetrieveType.video) { - isVideo = true; - await _playVideo(response.file); - } else { - isVideo = false; - setState(() { - _imageFile = response.file; - _imageFileList = response.files; - }); - } - } else { - _retrieveDataError = response.exception!.code; - } - } - @override Widget build(BuildContext context) { return Scaffold( @@ -244,35 +223,7 @@ class _MyHomePageState extends State { title: Text(widget.title!), ), body: Center( - child: !kIsWeb && defaultTargetPlatform == TargetPlatform.android - ? FutureBuilder( - future: retrieveLostData(), - builder: (BuildContext context, AsyncSnapshot snapshot) { - switch (snapshot.connectionState) { - case ConnectionState.none: - case ConnectionState.waiting: - return const Text( - 'You have not yet picked an image.', - textAlign: TextAlign.center, - ); - case ConnectionState.done: - return _handlePreview(); - default: - if (snapshot.hasError) { - return Text( - 'Pick image/video error: ${snapshot.error}}', - textAlign: TextAlign.center, - ); - } else { - return const Text( - 'You have not yet picked an image.', - textAlign: TextAlign.center, - ); - } - } - }, - ) - : _handlePreview(), + child: _handlePreview(), ), floatingActionButton: Column( mainAxisAlignment: MainAxisAlignment.end, diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index 76ca20614f18..d1de0a14ea69 100755 --- a/packages/image_picker/image_picker_ios/pubspec.yaml +++ b/packages/image_picker/image_picker_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_ios description: iOS implementation of the video_picker plugin. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.5+1 +version: 0.8.5+2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md index 9f6d1749c671..2defdd2d84cc 100644 --- a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md +++ b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Minor fixes for new analysis options. + ## 2.4.4 * Internal code cleanup for stricter analysis options. diff --git a/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart b/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart index 3201d3adea41..3e6cd0e01ca6 100644 --- a/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart +++ b/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart @@ -13,7 +13,7 @@ import 'package:image_picker_platform_interface/image_picker_platform_interface. final String pathPrefix = Directory.current.path.endsWith('test') ? './assets/' : './test/assets/'; -final String path = pathPrefix + 'hello.txt'; +final String path = '${pathPrefix}hello.txt'; const String expectedStringContents = 'Hello, world!'; final Uint8List bytes = Uint8List.fromList(utf8.encode(expectedStringContents)); final File textFile = File(path); diff --git a/packages/image_picker/image_picker_windows/CHANGELOG.md b/packages/image_picker/image_picker_windows/CHANGELOG.md index e72ab244068f..b8a265568633 100644 --- a/packages/image_picker/image_picker_windows/CHANGELOG.md +++ b/packages/image_picker/image_picker_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.0+2 + +* Minor fixes for new analysis options. + ## 0.1.0+1 * Removes unnecessary imports. diff --git a/packages/image_picker/image_picker_windows/example/lib/main.dart b/packages/image_picker/image_picker_windows/example/lib/main.dart index b3ba3574522a..e340a185bf3d 100644 --- a/packages/image_picker/image_picker_windows/example/lib/main.dart +++ b/packages/image_picker/image_picker_windows/example/lib/main.dart @@ -40,7 +40,7 @@ class _MyHomePageState extends State { List? _imageFileList; // This must be called from within a setState() callback - set _imageFile(PickedFile? value) { + void _setImageFileListFromFile(PickedFile? value) { _imageFileList = value == null ? null : [value]; } @@ -102,7 +102,7 @@ class _MyHomePageState extends State { imageQuality: quality, ); setState(() { - _imageFile = pickedFile; + _setImageFileListFromFile(pickedFile); }); } catch (e) { setState(() { diff --git a/packages/image_picker/image_picker_windows/lib/image_picker_windows.dart b/packages/image_picker/image_picker_windows/lib/image_picker_windows.dart index 9bd26c471b4e..0c6d6fbd6d66 100644 --- a/packages/image_picker/image_picker_windows/lib/image_picker_windows.dart +++ b/packages/image_picker/image_picker_windows/lib/image_picker_windows.dart @@ -46,7 +46,7 @@ class ImagePickerWindows extends ImagePickerPlatform { /// The file selector used to prompt the user to select images or videos. @visibleForTesting - static late FileSelectorPlatform fileSelector = FileSelectorWindows(); + static FileSelectorPlatform fileSelector = FileSelectorWindows(); /// Registers this class as the default instance of [ImagePickerPlatform]. static void registerWith() { diff --git a/packages/image_picker/image_picker_windows/pubspec.yaml b/packages/image_picker/image_picker_windows/pubspec.yaml index afadf3e39148..af96030debdf 100644 --- a/packages/image_picker/image_picker_windows/pubspec.yaml +++ b/packages/image_picker/image_picker_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_windows description: Windows platform implementation of image_picker repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.1.0+1 +version: 0.1.0+2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md index 8412c23ee8e8..6f0d4877f8a3 100644 --- a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.0.4 + +* Minor fixes for new analysis options. + ## 3.0.3 * Removes unnecessary imports. diff --git a/packages/in_app_purchase/in_app_purchase/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase/example/lib/main.dart index 34346a0bd339..5dbdd8c14b29 100644 --- a/packages/in_app_purchase/in_app_purchase/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase/example/lib/main.dart @@ -193,8 +193,8 @@ class _MyAppState extends State<_MyApp> { final Widget storeHeader = ListTile( leading: Icon(_isAvailable ? Icons.check : Icons.block, color: _isAvailable ? Colors.green : ThemeData.light().errorColor), - title: Text( - 'The store is ' + (_isAvailable ? 'available' : 'unavailable') + '.'), + title: + Text('The store is ${_isAvailable ? 'available' : 'unavailable'}.'), ); final List children = [storeHeader]; diff --git a/packages/in_app_purchase/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/pubspec.yaml index d2f875293876..4b9b9b7d64ff 100644 --- a/packages/in_app_purchase/in_app_purchase/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase description: A Flutter plugin for in-app purchases. Exposes APIs for making in-app purchases through the App Store and Google Play. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 3.0.3 +version: 3.0.4 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md index 4b9e58c08d06..18284f29a2d9 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.2+5 + +* Minor fixes for new analysis options. + ## 0.2.2+4 * Removes unnecessary imports. diff --git a/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart index 1da943535f70..b1d90d40b97c 100644 --- a/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart @@ -187,8 +187,8 @@ class _MyAppState extends State<_MyApp> { final Widget storeHeader = ListTile( leading: Icon(_isAvailable ? Icons.check : Icons.block, color: _isAvailable ? Colors.green : ThemeData.light().errorColor), - title: Text( - 'The store is ' + (_isAvailable ? 'available' : 'unavailable') + '.'), + title: + Text('The store is ${_isAvailable ? 'available' : 'unavailable'}.'), ); final List children = [storeHeader]; diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.dart index 7378aeb84cfc..70343fcfff0b 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.dart @@ -84,7 +84,9 @@ class BillingClient { /// **Deprecation warning:** it is no longer required to call /// [enablePendingPurchases] when initializing your application. @Deprecated( - 'The requirement to call `enablePendingPurchases()` has become obsolete since Google Play no longer accepts app submissions that don\'t support pending purchases.') + 'The requirement to call `enablePendingPurchases()` has become obsolete ' + "since Google Play no longer accepts app submissions that don't support " + 'pending purchases.') void enablePendingPurchases() { // No-op, until it is time to completely remove this method from the API. } diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform_addition.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform_addition.dart index dd629164866f..db53ff4077d2 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform_addition.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform_addition.dart @@ -25,7 +25,9 @@ class InAppPurchaseAndroidPlatformAddition // ignore: deprecated_member_use_from_same_package /// See also [enablePendingPurchases] for more on pending purchases. @Deprecated( - 'The requirement to call `enablePendingPurchases()` has become obsolete since Google Play no longer accepts app submissions that don\'t support pending purchases.') + 'The requirement to call `enablePendingPurchases()` has become obsolete ' + "since Google Play no longer accepts app submissions that don't support " + 'pending purchases.') static bool get enablePendingPurchase => true; /// Enable the [InAppPurchaseConnection] to handle pending purchases. @@ -33,7 +35,9 @@ class InAppPurchaseAndroidPlatformAddition /// **Deprecation warning:** it is no longer required to call /// [enablePendingPurchases] when initializing your application. @Deprecated( - 'The requirement to call `enablePendingPurchases()` has become obsolete since Google Play no longer accepts app submissions that don\'t support pending purchases.') + 'The requirement to call `enablePendingPurchases()` has become obsolete ' + "since Google Play no longer accepts app submissions that don't support " + 'pending purchases.') static void enablePendingPurchases() { // No-op, until it is time to completely remove this method from the API. } diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/types/google_play_product_details.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/types/google_play_product_details.dart index 58fd34e0ad55..15ed16c7e2ec 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/types/google_play_product_details.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/types/google_play_product_details.dart @@ -39,7 +39,7 @@ class GooglePlayProductDetails extends ProductDetails { title: skuDetails.title, description: skuDetails.description, price: skuDetails.price, - rawPrice: ((skuDetails.priceAmountMicros) / 1000000.0).toDouble(), + rawPrice: skuDetails.priceAmountMicros / 1000000.0, currencyCode: skuDetails.priceCurrencyCode, currencySymbol: skuDetails.priceCurrencySymbol, skuDetails: skuDetails, diff --git a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml index 7de778177c31..103251909f14 100644 --- a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_android description: An implementation for the Android platform of the Flutter `in_app_purchase` plugin. This uses the Android BillingClient APIs. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.2.2+4 +version: 0.2.2+5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md index 7342077ab176..aba1d6ed3555 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.0+7 + +* Minor fixes for new analysis options. + ## 0.3.0+6 * Removes unnecessary imports. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart index f45a8c7f8741..5ebf1b051942 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart @@ -192,8 +192,8 @@ class _MyAppState extends State<_MyApp> { final Widget storeHeader = ListTile( leading: Icon(_isAvailable ? Icons.check : Icons.block, color: _isAvailable ? Colors.green : ThemeData.light().errorColor), - title: Text( - 'The store is ' + (_isAvailable ? 'available' : 'unavailable') + '.'), + title: + Text('The store is ${_isAvailable ? 'available' : 'unavailable'}.'), ); final List children = [storeHeader]; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml index 24b693c98c36..235d491fbff0 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_storekit description: An implementation for the iOS platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase_storekit issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.3.0+6 +version: 0.3.0+7 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_test.dart index 41329335dcf4..12fb21436ace 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_test.dart @@ -16,7 +16,7 @@ void main() { () { final SKProductSubscriptionPeriodWrapper wrapper = SKProductSubscriptionPeriodWrapper.fromJson( - buildSubscriptionPeriodMap(dummySubscription)!); + buildSubscriptionPeriodMap(dummySubscription)); expect(wrapper, equals(dummySubscription)); }); @@ -95,8 +95,7 @@ void main() { expect(product.title, wrapper.localizedTitle); expect(product.description, wrapper.localizedDescription); expect(product.id, wrapper.productIdentifier); - expect(product.price, - wrapper.priceLocale.currencySymbol + wrapper.price.toString()); + expect(product.price, wrapper.priceLocale.currencySymbol + wrapper.price); expect(product.skProduct, wrapper); }); diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index 9f9bd7b535a9..f18e76bf1156 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.4 + +* Minor fixes for new analysis options. + ## 1.0.3 * Removes unnecessary imports. diff --git a/packages/local_auth/local_auth_android/lib/types/auth_messages_android.dart b/packages/local_auth/local_auth_android/lib/types/auth_messages_android.dart index ad901248e63c..c82f6820055c 100644 --- a/packages/local_auth/local_auth_android/lib/types/auth_messages_android.dart +++ b/packages/local_auth/local_auth_android/lib/types/auth_messages_android.dart @@ -187,6 +187,6 @@ String get androidDeviceCredentialsSetupDescription => /// biometric on their device. String get androidGoToSettingsDescription => Intl.message( 'Biometric authentication is not set up on your device. Go to ' - '\'Settings > Security\' to add biometric authentication.', + "'Settings > Security' to add biometric authentication.", desc: 'Message advising the user to go to the settings and configure ' 'biometric on their device.'); diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml index cdd4e8225504..0feea23256a2 100644 --- a/packages/local_auth/local_auth_android/pubspec.yaml +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_android description: Android implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.3 +version: 1.0.4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider_platform_interface/CHANGELOG.md b/packages/path_provider/path_provider_platform_interface/CHANGELOG.md index 4ed22f09a893..4eea4b36ba8a 100644 --- a/packages/path_provider/path_provider_platform_interface/CHANGELOG.md +++ b/packages/path_provider/path_provider_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.0.4 +* Minor fixes for new analysis options. * Removes unnecessary imports. ## 2.0.3 diff --git a/packages/path_provider/path_provider_platform_interface/lib/src/method_channel_path_provider.dart b/packages/path_provider/path_provider_platform_interface/lib/src/method_channel_path_provider.dart index 73e6ab48a585..fe632743b098 100644 --- a/packages/path_provider/path_provider_platform_interface/lib/src/method_channel_path_provider.dart +++ b/packages/path_provider/path_provider_platform_interface/lib/src/method_channel_path_provider.dart @@ -22,6 +22,7 @@ class MethodChannelPathProvider extends PathProviderPlatform { /// This API is only exposed for the unit tests. It should not be used by /// any code outside of the plugin itself. @visibleForTesting + // ignore: use_setters_to_change_properties void setMockPathProviderPlatform(Platform platform) { _platform = platform; } diff --git a/packages/path_provider/path_provider_platform_interface/pubspec.yaml b/packages/path_provider/path_provider_platform_interface/pubspec.yaml index d1b0b3821e21..90b40ac7a3d4 100644 --- a/packages/path_provider/path_provider_platform_interface/pubspec.yaml +++ b/packages/path_provider/path_provider_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.0.3 +version: 2.0.4 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/path_provider/path_provider_windows/CHANGELOG.md b/packages/path_provider/path_provider_windows/CHANGELOG.md index 014b6b36da2b..d933b0d51da6 100644 --- a/packages/path_provider/path_provider_windows/CHANGELOG.md +++ b/packages/path_provider/path_provider_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Minor fixes for new analysis options. + ## 2.0.6 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/path_provider/path_provider_windows/test/path_provider_windows_test.dart b/packages/path_provider/path_provider_windows/test/path_provider_windows_test.dart index e977e07d99e6..7e4118c1ccc6 100644 --- a/packages/path_provider/path_provider_windows/test/path_provider_windows_test.dart +++ b/packages/path_provider/path_provider_windows/test/path_provider_windows_test.dart @@ -78,9 +78,8 @@ void main() { if (path != null) { expect( path, - endsWith(r'AppData\Roaming\' - r'A _Bad_ Company_ Name\' - r'A__Terrible__App__Name')); + endsWith( + r'AppData\Roaming\A _Bad_ Company_ Name\A__Terrible__App__Name')); expect(Directory(path).existsSync(), isTrue); } }, skip: !Platform.isWindows); diff --git a/packages/plugin_platform_interface/CHANGELOG.md b/packages/plugin_platform_interface/CHANGELOG.md index 72229cb63410..0e9b701444fd 100644 --- a/packages/plugin_platform_interface/CHANGELOG.md +++ b/packages/plugin_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Minor fixes for new analysis options. * Adds additional tests for `PlatformInterface` and `MockPlatformInterfaceMixin`. ## 2.1.2 diff --git a/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart b/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart index 9e1ddc09e92b..329cecb16091 100644 --- a/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart +++ b/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart @@ -11,6 +11,7 @@ class SamplePluginPlatform extends PlatformInterface { static final Object _token = Object(); + // ignore: avoid_setters_without_getters static set instance(SamplePluginPlatform instance) { PlatformInterface.verify(instance, _token); // A real implementation would set a static instance field here. @@ -35,6 +36,7 @@ class ConstTokenPluginPlatform extends PlatformInterface { static const Object _token = Object(); // invalid + // ignore: avoid_setters_without_getters static set instance(ConstTokenPluginPlatform instance) { PlatformInterface.verify(instance, _token); } @@ -47,6 +49,7 @@ class VerifyTokenPluginPlatform extends PlatformInterface { static final Object _token = Object(); + // ignore: avoid_setters_without_getters static set instance(VerifyTokenPluginPlatform instance) { PlatformInterface.verifyToken(instance, _token); // A real implementation would set a static instance field here. @@ -68,6 +71,7 @@ class ConstVerifyTokenPluginPlatform extends PlatformInterface { static const Object _token = Object(); // invalid + // ignore: avoid_setters_without_getters static set instance(ConstVerifyTokenPluginPlatform instance) { PlatformInterface.verifyToken(instance, _token); } diff --git a/packages/quick_actions/quick_actions/CHANGELOG.md b/packages/quick_actions/quick_actions/CHANGELOG.md index 73540a863364..d7703a85a548 100644 --- a/packages/quick_actions/quick_actions/CHANGELOG.md +++ b/packages/quick_actions/quick_actions/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Minor fixes for new analysis options. + ## 0.6.0+11 * Removes unnecessary imports. diff --git a/packages/quick_actions/quick_actions/test/quick_actions_test.dart b/packages/quick_actions/quick_actions/test/quick_actions_test.dart index 2747818ae302..be9fd5e7720a 100644 --- a/packages/quick_actions/quick_actions/test/quick_actions_test.dart +++ b/packages/quick_actions/quick_actions/test/quick_actions_test.dart @@ -21,7 +21,7 @@ void main() { test('initialize() PlatformInterface', () async { const QuickActions quickActions = QuickActions(); - final QuickActionHandler handler = (String type) {}; + void handler(String type) {} await quickActions.initialize(handler); verify(QuickActionsPlatform.instance.initialize(handler)).called(1); @@ -29,7 +29,7 @@ void main() { test('setShortcutItems() PlatformInterface', () { const QuickActions quickActions = QuickActions(); - final QuickActionHandler handler = (String type) {}; + void handler(String type) {} quickActions.initialize(handler); quickActions.setShortcutItems([]); @@ -40,7 +40,7 @@ void main() { test('clearShortcutItems() PlatformInterface', () { const QuickActions quickActions = QuickActions(); - final QuickActionHandler handler = (String type) {}; + void handler(String type) {} quickActions.initialize(handler); quickActions.clearShortcutItems(); diff --git a/packages/shared_preferences/shared_preferences/CHANGELOG.md b/packages/shared_preferences/shared_preferences/CHANGELOG.md index 22c39aad98fd..4bf0f6a6b144 100644 --- a/packages/shared_preferences/shared_preferences/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.15 + +* Minor fixes for new analysis options. + ## 2.0.14 * Adds OS version support information to README. diff --git a/packages/shared_preferences/shared_preferences/lib/shared_preferences.dart b/packages/shared_preferences/shared_preferences/lib/shared_preferences.dart index 5e2a65889bee..77f04800a5bb 100644 --- a/packages/shared_preferences/shared_preferences/lib/shared_preferences.dart +++ b/packages/shared_preferences/shared_preferences/lib/shared_preferences.dart @@ -140,7 +140,7 @@ class SharedPreferences { /// Always returns true. /// On iOS, synchronize is marked deprecated. On Android, we commit every set. - @deprecated + @Deprecated('This method is now a no-op, and should no longer be called.') Future commit() async => true; /// Completes with true once the user preferences for the app has been cleared. diff --git a/packages/shared_preferences/shared_preferences/pubspec.yaml b/packages/shared_preferences/shared_preferences/pubspec.yaml index 4218095c0efe..14b56fe69889 100644 --- a/packages/shared_preferences/shared_preferences/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for reading and writing simple key-value pairs. Wraps NSUserDefaults on iOS and SharedPreferences on Android. repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.14 +version: 2.0.15 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences/test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences/test/shared_preferences_test.dart index 11498cfa5dcb..0a02c46404fc 100755 --- a/packages/shared_preferences/shared_preferences/test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences/test/shared_preferences_test.dart @@ -172,7 +172,7 @@ void main() { group('mocking', () { const String _key = 'dummy'; - const String _prefixedKey = 'flutter.' + _key; + const String _prefixedKey = 'flutter.$_key'; test('test 1', () async { SharedPreferences.setMockInitialValues( diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index 493412c3e006..043edeb3d8e2 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.1.2 + +* Minor fixes for new analysis options. + ## 6.1.1 * Removes unnecessary imports. diff --git a/packages/url_launcher/url_launcher/lib/src/legacy_api.dart b/packages/url_launcher/url_launcher/lib/src/legacy_api.dart index a61b200003a0..f6faf3fa3d0e 100644 --- a/packages/url_launcher/url_launcher/lib/src/legacy_api.dart +++ b/packages/url_launcher/url_launcher/lib/src/legacy_api.dart @@ -76,10 +76,10 @@ Future launch( final bool isWebURL = url != null && (url.scheme == 'http' || url.scheme == 'https'); - if ((forceSafariVC == true || forceWebView == true) && !isWebURL) { + if ((forceSafariVC ?? false || forceWebView) && !isWebURL) { throw PlatformException( code: 'NOT_A_WEB_SCHEME', - message: 'To use webview or safariVC, you need to pass' + message: 'To use webview or safariVC, you need to pass ' 'in a web URL. This $urlString is not a web URL.'); } diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index c14b62a1e70a..2cf75df6b0ef 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.1.1 +version: 6.1.2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher/test/mocks/mock_url_launcher_platform.dart b/packages/url_launcher/url_launcher/test/mocks/mock_url_launcher_platform.dart index 789c1435df80..5c53257f7630 100644 --- a/packages/url_launcher/url_launcher/test/mocks/mock_url_launcher_platform.dart +++ b/packages/url_launcher/url_launcher/test/mocks/mock_url_launcher_platform.dart @@ -25,6 +25,7 @@ class MockUrlLauncher extends Fake bool canLaunchCalled = false; bool launchCalled = false; + // ignore: use_setters_to_change_properties void setCanLaunchExpectations(String url) { this.url = url; } @@ -49,6 +50,7 @@ class MockUrlLauncher extends Fake this.webOnlyWindowName = webOnlyWindowName; } + // ignore: use_setters_to_change_properties void setResponse(bool response) { this.response = response; } diff --git a/packages/url_launcher/url_launcher_web/CHANGELOG.md b/packages/url_launcher/url_launcher_web/CHANGELOG.md index b53a92cee707..068650be6d53 100644 --- a/packages/url_launcher/url_launcher_web/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.11 + +* Minor fixes for new analysis options. + ## 2.0.10 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/url_launcher/url_launcher_web/lib/url_launcher_web.dart b/packages/url_launcher/url_launcher_web/lib/url_launcher_web.dart index 72540c3c3b80..636cd8c513a3 100644 --- a/packages/url_launcher/url_launcher_web/lib/url_launcher_web.dart +++ b/packages/url_launcher/url_launcher_web/lib/url_launcher_web.dart @@ -64,6 +64,7 @@ class UrlLauncherPlugin extends UrlLauncherPlatform { // See https://github.com/flutter/flutter/issues/51461 for reference. final String target = webOnlyWindowName ?? ((_isSafari && _isSafariTargetTopScheme(url)) ? '_top' : ''); + // ignore: unsafe_html return _window.open(url, target); } diff --git a/packages/url_launcher/url_launcher_web/pubspec.yaml b/packages/url_launcher/url_launcher_web/pubspec.yaml index cd8ed2d269c8..cef323035379 100644 --- a/packages/url_launcher/url_launcher_web/pubspec.yaml +++ b/packages/url_launcher/url_launcher_web/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_web description: Web platform implementation of url_launcher repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 2.0.10 +version: 2.0.11 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index ede890162f86..1dbc4f73e9c2 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.4.2 + +* Minor fixes for new analysis options. + ## 2.4.1 * Removes unnecessary imports. 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 151eb93149ee..633d636c7c69 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 @@ -182,7 +182,7 @@ void main() { child: FutureBuilder( future: started(), builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.data == true) { + if (snapshot.data ?? false) { return AspectRatio( aspectRatio: _controller.value.aspectRatio, child: VideoPlayer(_controller), diff --git a/packages/video_player/video_player/example/lib/main.dart b/packages/video_player/video_player/example/lib/main.dart index f5875975cea5..63afc4a28bc8 100644 --- a/packages/video_player/video_player/example/lib/main.dart +++ b/packages/video_player/video_player/example/lib/main.dart @@ -424,7 +424,7 @@ class _PlayerVideoAndPopPageState extends State<_PlayerVideoAndPopPage> { child: FutureBuilder( future: started(), builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.data == true) { + if (snapshot.data ?? false) { return AspectRatio( aspectRatio: _videoPlayerController.value.aspectRatio, child: VideoPlayer(_videoPlayerController), diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index b0ca56429271..05cfcf154f88 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for displaying inline video with other Flutter widgets on Android, iOS, and web. repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.4.1 +version: 2.4.2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player/test/closed_caption_file_test.dart b/packages/video_player/video_player/test/closed_caption_file_test.dart index b5c0a8e1db12..a20f9479dc45 100644 --- a/packages/video_player/video_player/test/closed_caption_file_test.dart +++ b/packages/video_player/video_player/test/closed_caption_file_test.dart @@ -21,8 +21,7 @@ void main() { 'number: 1, ' 'start: 0:00:01.000000, ' 'end: 0:00:02.000000, ' - 'text: caption' - ')'); + 'text: caption)'); }); }); } diff --git a/packages/video_player/video_player/test/sub_rip_file_test.dart b/packages/video_player/video_player/test/sub_rip_file_test.dart index ea3bfda036ec..82fe6ce033ab 100644 --- a/packages/video_player/video_player/test/sub_rip_file_test.dart +++ b/packages/video_player/video_player/test/sub_rip_file_test.dart @@ -57,7 +57,7 @@ void main() { ); expect( fourthCaption.text, - '- [ Machinery Beeping ]\n- I\'m not sure what that was,', + "- [ Machinery Beeping ]\n- I'm not sure what that was,", ); }); diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md index 094ffda207c5..e36d044901a4 100644 --- a/packages/video_player/video_player_web/CHANGELOG.md +++ b/packages/video_player/video_player_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.10 + +* Minor fixes for new analysis options. + ## 2.0.9 * Removes unnecessary imports. diff --git a/packages/video_player/video_player_web/lib/src/video_player.dart b/packages/video_player/video_player_web/lib/src/video_player.dart index 45d90d675b83..076167383ce7 100644 --- a/packages/video_player/video_player_web/lib/src/video_player.dart +++ b/packages/video_player/video_player_web/lib/src/video_player.dart @@ -134,6 +134,7 @@ class VideoPlayer { } /// Controls whether the video should start again after it finishes. + // ignore: use_setters_to_change_properties void setLooping(bool value) { _videoElement.loop = value; } diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml index 7af0dc46dde8..04fba273a2b6 100644 --- a/packages/video_player/video_player_web/pubspec.yaml +++ b/packages/video_player/video_player_web/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_web description: Web platform implementation of video_player. repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.0.9 +version: 2.0.10 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index fa47a6cc3143..31c16da8807b 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.0.4 + +* Minor fixes for new analysis options. + ## 3.0.3 * Removes unnecessary imports. diff --git a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart index cc001f336849..066ac030595f 100644 --- a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart @@ -912,8 +912,8 @@ Future main() async { group('NavigationDelegate', () { const String blankPage = ''; - final String blankPageEncoded = 'data:text/html;charset=utf-8;base64,' + - base64Encode(const Utf8Encoder().convert(blankPage)); + final String blankPageEncoded = 'data:text/html;charset=utf-8;base64,' + '${base64Encode(const Utf8Encoder().convert(blankPage))}'; testWidgets('can allow requests', (WidgetTester tester) async { final Completer controllerCompleter = diff --git a/packages/webview_flutter/webview_flutter/example/lib/main.dart b/packages/webview_flutter/webview_flutter/example/lib/main.dart index 3d8731127970..79197b02315c 100644 --- a/packages/webview_flutter/webview_flutter/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter/example/lib/main.dart @@ -40,8 +40,8 @@ const String kLocalExamplePage = '''

Local demo page

- This is an example page used to demonstrate how to load a local file or HTML - string using the Flutter + This is an example page used to demonstrate how to load a local file or HTML + string using the Flutter webview plugin.

@@ -155,7 +155,7 @@ class _WebViewExampleState extends State { onPressed: () async { String? url; if (controller.hasData) { - url = (await controller.data!.currentUrl())!; + url = await controller.data!.currentUrl(); } ScaffoldMessenger.of(context).showSnackBar( SnackBar( @@ -345,6 +345,7 @@ class SampleMenu extends StatelessWidget { Future _onListCache( WebViewController controller, BuildContext context) async { await controller.runJavascript('caches.keys()' + // ignore: missing_whitespace_between_adjacent_strings '.then((cacheKeys) => JSON.stringify({"cacheKeys" : cacheKeys, "localStorage" : localStorage}))' '.then((caches) => Toaster.postMessage(caches))'); } diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index a48f6f912c2d..9639b6abe76e 100644 --- a/packages/webview_flutter/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter description: A Flutter plugin that provides a WebView widget on Android and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 3.0.3 +version: 3.0.4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart index ad25cadf1dc4..d7189917c221 100644 --- a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart @@ -588,7 +588,7 @@ void main() { }); test('Only valid JavaScript channel names are allowed', () { - final JavascriptMessageHandler noOp = (JavascriptMessage msg) {}; + void noOp(JavascriptMessage msg) {} JavascriptChannel(name: 'Tts1', onMessageReceived: noOp); JavascriptChannel(name: '_Alarm', onMessageReceived: noOp); JavascriptChannel(name: 'foo_bar_', onMessageReceived: noOp); diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 4a451442f6cc..41a6fa273149 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.8.8 + +* Minor fixes for new analysis options. + ## 2.8.7 * Removes unnecessary imports. diff --git a/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart index 4c06fa6b3c18..383fe4508081 100644 --- a/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart @@ -1009,8 +1009,8 @@ Future main() async { group('NavigationDelegate', () { const String blankPage = ''; - final String blankPageEncoded = 'data:text/html;charset=utf-8;base64,' + - base64Encode(const Utf8Encoder().convert(blankPage)); + final String blankPageEncoded = 'data:text/html;charset=utf-8;base64,' + '${base64Encode(const Utf8Encoder().convert(blankPage))}'; testWidgets('can allow requests', (WidgetTester tester) async { final Completer controllerCompleter = diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart index 349a64916e8b..4492e6e6e26f 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart @@ -56,8 +56,8 @@ const String kExamplePage = '''

Local demo page

- This is an example page used to demonstrate how to load a local file or HTML - string using the Flutter + This is an example page used to demonstrate how to load a local file or HTML + string using the Flutter webview plugin.

@@ -341,6 +341,7 @@ class _SampleMenu extends StatelessWidget { Future _onListCache( WebViewController controller, BuildContext context) async { await controller.runJavascript('caches.keys()' + // ignore: missing_whitespace_between_adjacent_strings '.then((cacheKeys) => JSON.stringify({"cacheKeys" : cacheKeys, "localStorage" : localStorage}))' '.then((caches) => Snackbar.postMessage(caches))'); } diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 407887f2ba95..d6cf2b2a1c17 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_android description: A Flutter plugin that provides a WebView widget on Android. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.8.7 +version: 2.8.8 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md index b7254e1a0a7a..8ab70f9a78d3 100644 --- a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.0+3 + +* Minor fixes for new analysis options. + ## 0.1.0+2 * Removes unnecessary imports. diff --git a/packages/webview_flutter/webview_flutter_web/lib/webview_flutter_web.dart b/packages/webview_flutter/webview_flutter_web/lib/webview_flutter_web.dart index aa427eb6874a..637c24926275 100644 --- a/packages/webview_flutter/webview_flutter_web/lib/webview_flutter_web.dart +++ b/packages/webview_flutter/webview_flutter_web/lib/webview_flutter_web.dart @@ -44,6 +44,7 @@ class WebWebViewPlatform implements WebViewPlatform { final IFrameElement element = document.getElementById('webview-$viewId')! as IFrameElement; if (creationParams.initialUrl != null) { + // ignore: unsafe_html element.src = creationParams.initialUrl; } onWebViewPlatformCreated(WebWebViewPlatformController( @@ -70,6 +71,7 @@ class WebWebViewPlatformController implements WebViewPlatformController { /// Setter for setting the HttpRequestFactory, for testing purposes. @visibleForTesting + // ignore: avoid_setters_without_getters set httpRequestFactory(HttpRequestFactory factory) { _httpRequestFactory = factory; } @@ -131,6 +133,7 @@ class WebWebViewPlatformController implements WebViewPlatformController { @override Future loadUrl(String url, Map? headers) async { + // ignore: unsafe_html _element.src = url; } @@ -179,7 +182,8 @@ class WebWebViewPlatformController implements WebViewPlatformController { String html, { String? baseUrl, }) async { - _element.src = 'data:text/html,' + Uri.encodeFull(html); + // ignore: unsafe_html + _element.src = 'data:text/html,${Uri.encodeFull(html)}'; } @override @@ -194,8 +198,9 @@ class WebWebViewPlatformController implements WebViewPlatformController { sendData: request.body); final String contentType = httpReq.getResponseHeader('content-type') ?? 'text/html'; + // ignore: unsafe_html _element.src = - 'data:$contentType,' + Uri.encodeFull(httpReq.responseText ?? ''); + 'data:$contentType,${Uri.encodeFull(httpReq.responseText ?? '')}'; } @override @@ -265,7 +270,7 @@ class HttpRequestFactory { String? mimeType, Map? requestHeaders, dynamic sendData, - void onProgress(ProgressEvent e)?}) { + void Function(ProgressEvent e)? onProgress}) { return HttpRequest.request(url, method: method, withCredentials: withCredentials, diff --git a/packages/webview_flutter/webview_flutter_web/pubspec.yaml b/packages/webview_flutter/webview_flutter_web/pubspec.yaml index 35a7b74a764c..a834c9b77d51 100644 --- a/packages/webview_flutter/webview_flutter_web/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_web/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_web description: A Flutter plugin that provides a WebView widget on web. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 0.1.0+2 +version: 0.1.0+3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.dart b/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.dart index 90e2ea465782..6058dcf07272 100644 --- a/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.dart +++ b/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.dart @@ -64,7 +64,7 @@ void main() { // Run controller.loadHtmlString('test html'); // Verify - verify(mockElement.src = 'data:text/html,' + Uri.encodeFull('test html')); + verify(mockElement.src = 'data:text/html,${Uri.encodeFull('test html')}'); }); group('loadRequest', () { @@ -123,7 +123,7 @@ void main() { sendData: Uint8List.fromList('test body'.codeUnits), )); verify( - mockElement.src = 'data:text/plain,' + Uri.encodeFull('test data')); + mockElement.src = 'data:text/plain,${Uri.encodeFull('test data')}'); }); }); }); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index 6db769b0d922..00aa7293c9ad 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.7.5 + +* Minor fixes for new analysis options. + ## 2.7.4 * Removes unnecessary imports. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart index aa376f8358e9..40018eee8d98 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart @@ -861,8 +861,8 @@ Future main() async { group('NavigationDelegate', () { const String blankPage = ''; - final String blankPageEncoded = 'data:text/html;charset=utf-8;base64,' + - base64Encode(const Utf8Encoder().convert(blankPage)); + final String blankPageEncoded = 'data:text/html;charset=utf-8;base64,' + '${base64Encode(const Utf8Encoder().convert(blankPage))}'; testWidgets('can allow requests', (WidgetTester tester) async { final Completer controllerCompleter = diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart index 7b30923e1e54..3f61ebfdd6f8 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart @@ -326,6 +326,7 @@ class _SampleMenu extends StatelessWidget { Future _onListCache( WebViewController controller, BuildContext context) async { await controller.runJavascript('caches.keys()' + // ignore: missing_whitespace_between_adjacent_strings '.then((cacheKeys) => JSON.stringify({"cacheKeys" : cacheKeys, "localStorage" : localStorage}))' '.then((caches) => Snackbar.postMessage(caches))'); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 0a177d854bc2..19051af6ae1a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -167,7 +167,7 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { webView.setNavigationDelegate(navigationDelegate); if (params.userAgent != null) { - webView.setCustomUserAgent(params.userAgent!); + webView.setCustomUserAgent(params.userAgent); } if (params.webSettings != null) { @@ -177,7 +177,7 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { if (params.backgroundColor != null) { webView.setOpaque(false); webView.setBackgroundColor(Colors.transparent); - webView.scrollView.setBackgroundColor(params.backgroundColor!); + webView.scrollView.setBackgroundColor(params.backgroundColor); } if (params.initialUrl != null) { @@ -496,10 +496,10 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { Future _disableZoom() { const WKUserScript userScript = WKUserScript( - "var meta = document.createElement('meta');" - "meta.name = 'viewport';" - "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0," - "user-scalable=no';" + "var meta = document.createElement('meta');\n" + "meta.name = 'viewport';\n" + "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, " + "user-scalable=no';\n" "var head = document.getElementsByTagName('head')[0];head.appendChild(meta);", WKUserScriptInjectionTime.atDocumentEnd, isMainFrameOnly: true, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 365e64720d4f..d85bf329a58e 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_wkwebview description: A Flutter plugin that provides a WebView widget based on Apple's WKWebView control. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_wkwebview issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.7.4 +version: 2.7.5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/function_flutter_api_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/function_flutter_api_test.dart index 63e59386ceaf..6af9510b4f03 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/function_flutter_api_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/function_flutter_api_test.dart @@ -18,7 +18,7 @@ void main() { }); test('dispose', () { - final Function function = () {}; + void function() {} final int functionInstanceId = instanceManager.tryAddInstance(function)!; FoundationFlutterApis.instance = FoundationFlutterApis( diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart index ae13ca9e6b6d..b09f3461c397 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart @@ -163,7 +163,7 @@ void main() { final NSHttpCookieData cookie = verify( mockPlatformHostApi.setCookie( - instanceManager.getInstanceId(httpCookieStore)!, + instanceManager.getInstanceId(httpCookieStore), captureAny, ), ).captured.single as NSHttpCookieData; @@ -373,8 +373,8 @@ void main() { instanceManager: instanceManager, ); verify(mockPlatformHostApi.createFromWebView( - instanceManager.getInstanceId(configurationFromWebView)!, - instanceManager.getInstanceId(webView)!, + instanceManager.getInstanceId(configurationFromWebView), + instanceManager.getInstanceId(webView), )); }); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index d77feee22dd1..271fd5c062e2 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -400,10 +400,10 @@ void main() { WKUserScriptInjectionTime.atDocumentEnd); expect( zoomScript.source, - "var meta = document.createElement('meta');" - "meta.name = 'viewport';" - "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0," - "user-scalable=no';" + "var meta = document.createElement('meta');\n" + "meta.name = 'viewport';\n" + "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, " + "user-scalable=no';\n" "var head = document.getElementsByTagName('head')[0];head.appendChild(meta);", ); }); @@ -892,10 +892,10 @@ void main() { zoomScript.injectionTime, WKUserScriptInjectionTime.atDocumentEnd); expect( zoomScript.source, - "var meta = document.createElement('meta');" - "meta.name = 'viewport';" - "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0," - "user-scalable=no';" + "var meta = document.createElement('meta');\n" + "meta.name = 'viewport';\n" + "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, " + "user-scalable=no';\n" "var head = document.getElementsByTagName('head')[0];head.appendChild(meta);", ); }); diff --git a/script/tool/lib/src/create_all_plugins_app_command.dart b/script/tool/lib/src/create_all_plugins_app_command.dart index ad836e19d9c6..595779b8be68 100644 --- a/script/tool/lib/src/create_all_plugins_app_command.dart +++ b/script/tool/lib/src/create_all_plugins_app_command.dart @@ -110,7 +110,7 @@ class CreateAllPluginsAppCommand extends PluginCommand { newGradle.writeln(' multiDexEnabled true'); } else if (line.contains('dependencies {')) { newGradle.writeln( - ' implementation \'com.google.guava:guava:27.0.1-android\'\n', + " implementation 'com.google.guava:guava:27.0.1-android'\n", ); // Tests for https://github.com/flutter/flutter/issues/43383 newGradle.writeln( diff --git a/script/tool/lib/src/custom_test_command.dart b/script/tool/lib/src/custom_test_command.dart index cd9ac32606a6..0ef6e602c070 100644 --- a/script/tool/lib/src/custom_test_command.dart +++ b/script/tool/lib/src/custom_test_command.dart @@ -30,7 +30,7 @@ class CustomTestCommand extends PackageLoopingCommand { @override final String description = 'Runs package-specific custom tests defined in ' - 'a package\'s tool/$_scriptName file.\n\n' + "a package's tool/$_scriptName file.\n\n" 'This command requires "dart" to be in your path.'; @override diff --git a/script/tool/lib/src/format_command.dart b/script/tool/lib/src/format_command.dart index 10c0779de927..f640cbaa5f6c 100644 --- a/script/tool/lib/src/format_command.dart +++ b/script/tool/lib/src/format_command.dart @@ -130,8 +130,7 @@ class FormatCommand extends PluginCommand { if (clangFiles.isNotEmpty) { final String clangFormat = getStringArg('clang-format'); if (!await _hasDependency(clangFormat)) { - printError( - 'Unable to run \'clang-format\'. Make sure that it is in your ' + printError('Unable to run "clang-format". Make sure that it is in your ' 'path, or provide a full path with --clang-format.'); throw ToolExit(_exitDependencyMissing); } @@ -156,7 +155,7 @@ class FormatCommand extends PluginCommand { final String java = getStringArg('java'); if (!await _hasDependency(java)) { printError( - 'Unable to run \'java\'. Make sure that it is in your path, or ' + 'Unable to run "java". Make sure that it is in your path, or ' 'provide a full path with --java.'); throw ToolExit(_exitDependencyMissing); } diff --git a/script/tool/lib/src/license_check_command.dart b/script/tool/lib/src/license_check_command.dart index 87e4c8b14861..5e74d846c13f 100644 --- a/script/tool/lib/src/license_check_command.dart +++ b/script/tool/lib/src/license_check_command.dart @@ -241,8 +241,7 @@ class LicenseCheckCommand extends PluginCommand { } // Sort by path for more usable output. - final int Function(File, File) pathCompare = - (File a, File b) => a.path.compareTo(b.path); + int pathCompare(File a, File b) => a.path.compareTo(b.path); incorrectFirstPartyFiles.sort(pathCompare); unrecognizedThirdPartyFiles.sort(pathCompare); diff --git a/script/tool/lib/src/make_deps_path_based_command.dart b/script/tool/lib/src/make_deps_path_based_command.dart index 9b861c34ec91..4bbecb4d2244 100644 --- a/script/tool/lib/src/make_deps_path_based_command.dart +++ b/script/tool/lib/src/make_deps_path_based_command.dart @@ -169,8 +169,8 @@ class MakeDepsPathBasedCommand extends PluginCommand { // then re-serialiazing so that it's a localized change, rather than // rewriting the whole file (e.g., destroying comments), which could be // more disruptive for local use. - String newPubspecContents = pubspecContents + - ''' + String newPubspecContents = ''' +$pubspecContents $_dependencyOverrideWarningComment dependency_overrides: diff --git a/script/tool/lib/src/pubspec_check_command.dart b/script/tool/lib/src/pubspec_check_command.dart index 654675ebb858..23c9c00e33f0 100644 --- a/script/tool/lib/src/pubspec_check_command.dart +++ b/script/tool/lib/src/pubspec_check_command.dart @@ -225,8 +225,8 @@ class PubspecCheckCommand extends PackageLoopingCommand { bool _checkIssueLink(Pubspec pubspec) { return pubspec.issueTracker ?.toString() - .startsWith(_expectedIssueLinkFormat) == - true; + .startsWith(_expectedIssueLinkFormat) ?? + false; } // Validates the "implements" keyword for a plugin, returning an error @@ -287,8 +287,8 @@ class PubspecCheckCommand extends PackageLoopingCommand { .where((String package) => !dependencies.contains(package)); if (missingPackages.isNotEmpty) { return 'The following default_packages are missing ' - 'corresponding dependencies:\n ' + - missingPackages.join('\n '); + 'corresponding dependencies:\n' + ' ${missingPackages.join('\n ')}'; } return null; diff --git a/script/tool/lib/src/version_check_command.dart b/script/tool/lib/src/version_check_command.dart index c0e67764360e..b816ee56999c 100644 --- a/script/tool/lib/src/version_check_command.dart +++ b/script/tool/lib/src/version_check_command.dart @@ -403,7 +403,7 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body} final String badNextErrorMessage = '${indentation}When bumping the version ' 'for release, the NEXT section should be incorporated into the new ' - 'version\'s release notes.'; + "version's release notes."; // Skip validation for the special NEXT version that's used to accumulate // changes that don't warrant publishing on their own. @@ -531,7 +531,7 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog. final Directory gitRoot = packagesDir.fileSystem.directory((await gitDir).path); final String relativePackagePath = - getRelativePosixPath(package.directory, from: gitRoot) + '/'; + '${getRelativePosixPath(package.directory, from: gitRoot)}/'; bool hasChanges = false; bool needsVersionChange = false; bool hasChangelogChange = false; @@ -594,7 +594,7 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog. 'change description.'); } else { printError( - 'No CHANGELOG change found. If this PR needs an exemption from' + 'No CHANGELOG change found. If this PR needs an exemption from ' 'the standard policy of listing all changes in the CHANGELOG, ' 'please add a line starting with\n' '$_missingChangelogChangeJustificationMarker\n' diff --git a/script/tool/test/analyze_command_test.dart b/script/tool/test/analyze_command_test.dart index e293e8b85e98..a9b83349306f 100644 --- a/script/tool/test/analyze_command_test.dart +++ b/script/tool/test/analyze_command_test.dart @@ -93,7 +93,7 @@ void main() { ])); }); - test('don\'t elide a non-contained example package', () async { + test("don't elide a non-contained example package", () async { final RepositoryPackage plugin1 = createFakePlugin('a', packagesDir); final RepositoryPackage plugin2 = createFakePlugin('example', packagesDir); diff --git a/script/tool/test/common/plugin_command_test.dart b/script/tool/test/common/plugin_command_test.dart index 7ed3d239b2ad..8c6b38682418 100644 --- a/script/tool/test/common/plugin_command_test.dart +++ b/script/tool/test/common/plugin_command_test.dart @@ -162,7 +162,7 @@ void main() { expect(command.plugins, unorderedEquals([plugin2.path])); }); - test('exclude packages when packages flag isn\'t specified', () async { + test("exclude packages when packages flag isn't specified", () async { createFakePlugin('plugin1', packagesDir); createFakePlugin('plugin2', packagesDir); await runCapturingPrint( diff --git a/script/tool/test/format_command_test.dart b/script/tool/test/format_command_test.dart index 3fa7782245a7..5bd6f97832f7 100644 --- a/script/tool/test/format_command_test.dart +++ b/script/tool/test/format_command_test.dart @@ -218,7 +218,7 @@ void main() { output, containsAllInOrder([ contains( - 'Unable to run \'java\'. Make sure that it is in your path, or ' + 'Unable to run "java". Make sure that it is in your path, or ' 'provide a full path with --java.'), ])); }); @@ -330,8 +330,7 @@ void main() { expect( output, containsAllInOrder([ - contains( - 'Unable to run \'clang-format\'. Make sure that it is in your ' + contains('Unable to run "clang-format". Make sure that it is in your ' 'path, or provide a full path with --clang-format.'), ])); }); diff --git a/script/tool/test/publish_plugin_command_test.dart b/script/tool/test/publish_plugin_command_test.dart index 857828ab9306..d443f8ff0178 100644 --- a/script/tool/test/publish_plugin_command_test.dart +++ b/script/tool/test/publish_plugin_command_test.dart @@ -103,7 +103,7 @@ void main() { expect( output, containsAllInOrder([ - contains('There are files in the package directory that haven\'t ' + contains("There are files in the package directory that haven't " 'been saved in git. Refusing to publish these files:\n\n' '?? /packages/foo/tmp\n\n' 'If the directory should be clean, you can run `git clean -xdf && ' @@ -113,7 +113,7 @@ void main() { ])); }); - test('fails immediately if the remote doesn\'t exist', () async { + test("fails immediately if the remote doesn't exist", () async { createFakePlugin('foo', packagesDir, examples: []); processRunner.mockProcessesForExecutable['git-remote'] = [ @@ -877,8 +877,8 @@ class MockStdin extends Mock implements io.Stdin { } @override - StreamSubscription> listen(void onData(List event)?, - {Function? onError, void onDone()?, bool? cancelOnError}) { + StreamSubscription> listen(void Function(List event)? onData, + {Function? onError, void Function()? onDone, bool? cancelOnError}) { return _controller.stream.listen(onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError); } diff --git a/script/tool/test/pubspec_check_command_test.dart b/script/tool/test/pubspec_check_command_test.dart index 89bb98abd80c..fbe31c72bc2b 100644 --- a/script/tool/test/pubspec_check_command_test.dart +++ b/script/tool/test/pubspec_check_command_test.dart @@ -43,7 +43,7 @@ String _headerSection( repositoryPath, ]; final String repoLink = - 'https://github.com/' + repoLinkPathComponents.join('/'); + 'https://github.com/${repoLinkPathComponents.join('/')}'; final String issueTrackerLink = 'https://github.com/flutter/flutter/issues?' 'q=is%3Aissue+is%3Aopen+label%3A%22p%3A+$name%22'; description ??= 'A test package for validating that the pubspec.yaml ' @@ -55,7 +55,7 @@ ${includeRepository ? 'repository: $repoLink' : ''} ${includeHomepage ? 'homepage: $repoLink' : ''} ${includeIssueTracker ? 'issue_tracker: $issueTrackerLink' : ''} version: 1.0.0 -${publishable ? '' : 'publish_to: \'none\''} +${publishable ? '' : "publish_to: 'none'"} '''; } diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart index b0a8990e1300..effdd03891dc 100644 --- a/script/tool/test/util.dart +++ b/script/tool/test/util.dart @@ -313,7 +313,7 @@ String _pluginPlatformSection( assert(false, 'Unrecognized platform: $platform'); break; } - entry = lines.join('\n') + '\n'; + entry = '${lines.join('\n')}\n'; } return entry; diff --git a/script/tool/test/version_check_command_test.dart b/script/tool/test/version_check_command_test.dart index 5b8ed97e20c5..6af3c112f9eb 100644 --- a/script/tool/test/version_check_command_test.dart +++ b/script/tool/test/version_check_command_test.dart @@ -561,7 +561,7 @@ This is necessary because of X, Y, and Z output, containsAllInOrder([ contains('When bumping the version for release, the NEXT section ' - 'should be incorporated into the new version\'s release notes.') + "should be incorporated into the new version's release notes.") ]), ); }); @@ -595,7 +595,7 @@ This is necessary because of X, Y, and Z output, containsAllInOrder([ contains('When bumping the version for release, the NEXT section ' - 'should be incorporated into the new version\'s release notes.'), + "should be incorporated into the new version's release notes."), contains('plugin:\n' ' CHANGELOG.md failed validation.'), ]), @@ -627,7 +627,7 @@ This is necessary because of X, Y, and Z output, containsAllInOrder([ contains('When bumping the version for release, the NEXT section ' - 'should be incorporated into the new version\'s release notes.') + "should be incorporated into the new version's release notes.") ]), ); }); From ff01c1da8335347b7c6bae2a38ede6d4800f27c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Silva=20Oliveira?= Date: Wed, 11 May 2022 19:34:10 +0200 Subject: [PATCH 546/600] [camera] Fix preview pause orientation (#5209) --- packages/camera/camera/CHANGELOG.md | 4 +++ .../camera/lib/src/camera_controller.dart | 8 +++--- packages/camera/camera/pubspec.yaml | 2 +- packages/camera/camera/test/camera_test.dart | 25 +++++++++++++++++++ 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 4715644a0c13..cde2ca284434 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.4+24 + +* Fixes preview orientation when pausing preview with locked orientation. + ## 0.9.4+23 * Minor fixes for new analysis options. diff --git a/packages/camera/camera/lib/src/camera_controller.dart b/packages/camera/camera/lib/src/camera_controller.dart index 1492ca193761..5014795320f2 100644 --- a/packages/camera/camera/lib/src/camera_controller.dart +++ b/packages/camera/camera/lib/src/camera_controller.dart @@ -359,8 +359,8 @@ class CameraController extends ValueNotifier { await CameraPlatform.instance.pausePreview(_cameraId); value = value.copyWith( isPreviewPaused: true, - previewPauseOrientation: - Optional.of(value.deviceOrientation)); + previewPauseOrientation: Optional.of( + value.lockedCaptureOrientation ?? value.deviceOrientation)); } on PlatformException catch (e) { throw CameraException(e.code, e.message); } @@ -520,7 +520,7 @@ class CameraController extends ValueNotifier { value = value.copyWith( isRecordingVideo: true, isRecordingPaused: false, - recordingOrientation: Optional.fromNullable( + recordingOrientation: Optional.of( value.lockedCaptureOrientation ?? value.deviceOrientation)); } on PlatformException catch (e) { throw CameraException(e.code, e.message); @@ -762,7 +762,7 @@ class CameraController extends ValueNotifier { await CameraPlatform.instance.lockCaptureOrientation( _cameraId, orientation ?? value.deviceOrientation); value = value.copyWith( - lockedCaptureOrientation: Optional.fromNullable( + lockedCaptureOrientation: Optional.of( orientation ?? value.deviceOrientation)); } on PlatformException catch (e) { throw CameraException(e.code, e.message); diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 9d56f1b6e4da..d763843d0572 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+23 +version: 0.9.4+24 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera/test/camera_test.dart b/packages/camera/camera/test/camera_test.dart index 34a474b2b4f3..0d3195ba4b4b 100644 --- a/packages/camera/camera/test/camera_test.dart +++ b/packages/camera/camera/test/camera_test.dart @@ -13,6 +13,7 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:quiver/core.dart'; List get mockAvailableCameras => [ const CameraDescription( @@ -1180,6 +1181,30 @@ void main() { expect(cameraController.value.isPreviewPaused, equals(true)); }); + test( + 'pausePreview() sets previewPauseOrientation according to locked orientation', + () async { + final CameraController cameraController = CameraController( + const CameraDescription( + name: 'cam', + lensDirection: CameraLensDirection.back, + sensorOrientation: 90), + ResolutionPreset.max); + await cameraController.initialize(); + cameraController.value = cameraController.value.copyWith( + isPreviewPaused: false, + deviceOrientation: DeviceOrientation.portraitUp, + lockedCaptureOrientation: + Optional.of(DeviceOrientation.landscapeRight)); + + await cameraController.pausePreview(); + + expect(cameraController.value.deviceOrientation, + equals(DeviceOrientation.portraitUp)); + expect(cameraController.value.previewPauseOrientation, + equals(DeviceOrientation.landscapeRight)); + }); + test('pausePreview() throws $CameraException on $PlatformException', () async { final CameraController cameraController = CameraController( From a15d65d0ef3da2205b017bdaa7b86ecb314e4080 Mon Sep 17 00:00:00 2001 From: Jami Couch Date: Wed, 11 May 2022 14:44:09 -0500 Subject: [PATCH 547/600] [google_sign_in] Add forceCodeForRefreshToken parameter (and new SignInInitParameters class) (#5325) --- .../google_sign_in_platform_interface/AUTHORS | 1 + .../CHANGELOG.md | 3 +- .../google_sign_in_platform_interface.dart | 18 ++++++++- .../src/method_channel_google_sign_in.dart | 18 +++++++-- .../lib/src/types.dart | 37 +++++++++++++++++++ .../pubspec.yaml | 2 +- .../method_channel_google_sign_in_test.dart | 19 ++++++++++ 7 files changed, 90 insertions(+), 8 deletions(-) diff --git a/packages/google_sign_in/google_sign_in_platform_interface/AUTHORS b/packages/google_sign_in/google_sign_in_platform_interface/AUTHORS index 493a0b4ef9c2..35d24a5ae0b5 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/AUTHORS +++ b/packages/google_sign_in/google_sign_in_platform_interface/AUTHORS @@ -64,3 +64,4 @@ Aleksandr Yurkovskiy Anton Borries Alex Li Rahul Raj <64.rahulraj@gmail.com> +Twin Sun, LLC diff --git a/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md b/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md index da214d3ce6a9..abf01c847d83 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 2.1.3 * Removes unnecessary imports. +* Adds `SignInInitParameters` class to hold all sign in params, including the new `forceCodeForRefreshToken`. ## 2.1.2 diff --git a/packages/google_sign_in/google_sign_in_platform_interface/lib/google_sign_in_platform_interface.dart b/packages/google_sign_in/google_sign_in_platform_interface/lib/google_sign_in_platform_interface.dart index 50f261bfa578..69d8455b6bd2 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/lib/google_sign_in_platform_interface.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/lib/google_sign_in_platform_interface.dart @@ -63,8 +63,7 @@ abstract class GoogleSignInPlatform { /// if the provided instance is a class implemented with `implements`. void _verifyProvidesDefaultImplementations() {} - /// Initializes the plugin. You must call this method before calling other - /// methods. + /// Initializes the plugin. Deprecated: call [initWithParams] instead. /// /// The [hostedDomain] argument specifies a hosted domain restriction. By /// setting this, sign in will be restricted to accounts of the user in the @@ -89,6 +88,21 @@ abstract class GoogleSignInPlatform { throw UnimplementedError('init() has not been implemented.'); } + /// Initializes the plugin with specified [params]. You must call this method + /// before calling other methods. + /// + /// See: + /// + /// * [SignInInitParameters] + Future initWithParams(SignInInitParameters params) async { + await init( + scopes: params.scopes, + signInOption: params.signInOption, + hostedDomain: params.hostedDomain, + clientId: params.clientId, + ); + } + /// Attempts to reuse pre-existing credentials to sign in again, without user interaction. Future signInSilently() async { throw UnimplementedError('signInSilently() has not been implemented.'); diff --git a/packages/google_sign_in/google_sign_in_platform_interface/lib/src/method_channel_google_sign_in.dart b/packages/google_sign_in/google_sign_in_platform_interface/lib/src/method_channel_google_sign_in.dart index e56d2028a205..8b755fbf1cdd 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/lib/src/method_channel_google_sign_in.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/lib/src/method_channel_google_sign_in.dart @@ -25,11 +25,21 @@ class MethodChannelGoogleSignIn extends GoogleSignInPlatform { String? hostedDomain, String? clientId, }) { + return initWithParams(SignInInitParameters( + scopes: scopes, + signInOption: signInOption, + hostedDomain: hostedDomain, + clientId: clientId)); + } + + @override + Future initWithParams(SignInInitParameters params) { return channel.invokeMethod('init', { - 'signInOption': signInOption.toString(), - 'scopes': scopes, - 'hostedDomain': hostedDomain, - 'clientId': clientId, + 'signInOption': params.signInOption.toString(), + 'scopes': params.scopes, + 'hostedDomain': params.hostedDomain, + 'clientId': params.clientId, + 'forceCodeForRefreshToken': params.forceCodeForRefreshToken, }); } diff --git a/packages/google_sign_in/google_sign_in_platform_interface/lib/src/types.dart b/packages/google_sign_in/google_sign_in_platform_interface/lib/src/types.dart index bc50a1d2516d..2cac5e886729 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/lib/src/types.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/lib/src/types.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter/widgets.dart'; import 'package:quiver/core.dart'; /// Default configuration options to use when signing in. @@ -22,6 +23,42 @@ enum SignInOption { games } +/// The parameters to use when initializing the sign in process. +/// +/// See: +/// https://developers.google.com/identity/sign-in/web/reference#gapiauth2initparams +@immutable +class SignInInitParameters { + /// The parameters to use when initializing the sign in process. + const SignInInitParameters({ + this.scopes = const [], + this.signInOption = SignInOption.standard, + this.hostedDomain, + this.clientId, + this.forceCodeForRefreshToken = false, + }); + + /// The list of OAuth scope codes to request when signing in. + final List scopes; + + /// The user experience to use when signing in. [SignInOption.games] is + /// only supported on Android. + final SignInOption signInOption; + + /// Restricts sign in to accounts of the user in the specified domain. + /// By default, the list of accounts will not be restricted. + final String? hostedDomain; + + /// The client ID to use when signing in. + final String? clientId; + + /// If true, ensures the authorization code can be exchanged for an access + /// token. + /// + /// This is only used on Android. + final bool forceCodeForRefreshToken; +} + /// Holds information about the signed in user. class GoogleSignInUserData { /// Uses the given data to construct an instance. diff --git a/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml b/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml index a5bbaedd51e7..0deafe80a863 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.1.2 +version: 2.1.3 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/google_sign_in/google_sign_in_platform_interface/test/method_channel_google_sign_in_test.dart b/packages/google_sign_in/google_sign_in_platform_interface/test/method_channel_google_sign_in_test.dart index b6604d1e658e..1ffdc5a4f95e 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/test/method_channel_google_sign_in_test.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/test/method_channel_google_sign_in_test.dart @@ -107,6 +107,7 @@ void main() { 'scopes': ['two', 'scopes'], 'signInOption': 'SignInOption.games', 'clientId': 'fakeClientId', + 'forceCodeForRefreshToken': false, }), () { googleSignIn.getTokens( @@ -136,5 +137,23 @@ void main() { expect(log, tests.values); }); + + test('initWithParams passes through arguments to the channel', () async { + await googleSignIn.initWithParams(const SignInInitParameters( + hostedDomain: 'example.com', + scopes: ['two', 'scopes'], + signInOption: SignInOption.games, + clientId: 'fakeClientId', + forceCodeForRefreshToken: true)); + expect(log, [ + isMethodCall('init', arguments: { + 'hostedDomain': 'example.com', + 'scopes': ['two', 'scopes'], + 'signInOption': 'SignInOption.games', + 'clientId': 'fakeClientId', + 'forceCodeForRefreshToken': true, + }), + ]); + }); }); } From c13e8a50688b65b740a47adc1f6149ffcb9640eb Mon Sep 17 00:00:00 2001 From: Piotr Mitkowski Date: Wed, 11 May 2022 22:29:12 +0200 Subject: [PATCH 548/600] [image_picker] add requestFullMetadata for iOS (optional permissions) - platform interface (#5603) --- .../CHANGELOG.md | 8 +- .../method_channel_image_picker.dart | 20 +- .../image_picker_platform.dart | 32 ++ .../lib/src/types/image_picker_options.dart | 50 +++ .../lib/src/types/types.dart | 1 + .../pubspec.yaml | 2 +- .../new_method_channel_image_picker_test.dart | 322 ++++++++++++++++-- 7 files changed, 409 insertions(+), 26 deletions(-) create mode 100644 packages/image_picker/image_picker_platform_interface/lib/src/types/image_picker_options.dart diff --git a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md index 2defdd2d84cc..0a4e98bf7dbe 100644 --- a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md +++ b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md @@ -1,6 +1,10 @@ -## NEXT +## 2.5.0 -* Minor fixes for new analysis options. +* Deprecates `getImage` in favor of a new method `getImageFromSource`. + * Adds `requestFullMetadata` option that allows disabling extra permission requests + on certain platforms. + * Moves optional image picking parameters to `ImagePickerOptions` class. +* Minor fixes for new analysis options. ## 2.4.4 diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart index e1e6082c8047..ba5d60d7a677 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart @@ -87,6 +87,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform { double? maxHeight, int? imageQuality, CameraDevice preferredCameraDevice = CameraDevice.rear, + bool requestFullMetadata = true, }) { if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) { throw ArgumentError.value( @@ -108,7 +109,8 @@ class MethodChannelImagePicker extends ImagePickerPlatform { 'maxWidth': maxWidth, 'maxHeight': maxHeight, 'imageQuality': imageQuality, - 'cameraDevice': preferredCameraDevice.index + 'cameraDevice': preferredCameraDevice.index, + 'requestFullMetadata': requestFullMetadata, }, ); } @@ -197,6 +199,22 @@ class MethodChannelImagePicker extends ImagePickerPlatform { return path != null ? XFile(path) : null; } + @override + Future getImageFromSource({ + required ImageSource source, + ImagePickerOptions options = const ImagePickerOptions(), + }) async { + final String? path = await _getImagePath( + source: source, + maxHeight: options.maxHeight, + maxWidth: options.maxWidth, + imageQuality: options.imageQuality, + preferredCameraDevice: options.preferredCameraDevice, + requestFullMetadata: options.requestFullMetadata, + ); + return path != null ? XFile(path) : null; + } + @override Future?> getMultiImage({ double? maxWidth, diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart b/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart index 8f02e1683267..d1d06f904fe6 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart @@ -146,6 +146,8 @@ abstract class ImagePickerPlatform extends PlatformInterface { throw UnimplementedError('retrieveLostData() has not been implemented.'); } + /// This method is deprecated in favor of [getImageFromSource] and will be removed in a future update. + /// /// Returns an [XFile] with the image that was picked. /// /// The `source` argument controls where the image comes from. This can @@ -251,4 +253,34 @@ abstract class ImagePickerPlatform extends PlatformInterface { Future getLostData() { throw UnimplementedError('getLostData() has not been implemented.'); } + + /// Returns an [XFile] with the image that was picked. + /// + /// The `source` argument controls where the image comes from. This can + /// be either [ImageSource.camera] or [ImageSource.gallery]. + /// + /// The `options` argument controls additional settings that can be used when + /// picking an image. See [ImagePickerOptions] for more details. + /// + /// Where iOS supports HEIC images, Android 8 and below doesn't. Android 9 and + /// above only support HEIC images if used in addition to a size modification, + /// of which the usage is explained in [ImagePickerOptions]. + /// + /// In Android, the MainActivity can be destroyed for various reasons. If that + /// happens, the result will be lost in this call. You can then call [getLostData] + /// when your app relaunches to retrieve the lost data. + /// + /// If no images were picked, the return value is null. + Future getImageFromSource({ + required ImageSource source, + ImagePickerOptions options = const ImagePickerOptions(), + }) { + return getImage( + source: source, + maxHeight: options.maxHeight, + maxWidth: options.maxWidth, + imageQuality: options.imageQuality, + preferredCameraDevice: options.preferredCameraDevice, + ); + } } diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/image_picker_options.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/image_picker_options.dart new file mode 100644 index 000000000000..cdc89a920178 --- /dev/null +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/image_picker_options.dart @@ -0,0 +1,50 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:image_picker_platform_interface/src/types/types.dart'; + +/// Specifies options for picking a single image from the device's camera or gallery. +class ImagePickerOptions { + /// Creates an instance with the given [maxHeight], [maxWidth], [imageQuality], + /// [referredCameraDevice] and [requestFullMetadata]. + const ImagePickerOptions({ + this.maxHeight, + this.maxWidth, + this.imageQuality, + this.preferredCameraDevice = CameraDevice.rear, + this.requestFullMetadata = true, + }); + + /// The maximum width of the image, in pixels. + /// + /// If null, the image will only be resized if [maxHeight] is specified. + final double? maxWidth; + + /// The maximum height of the image, in pixels. + /// + /// If null, the image will only be resized if [maxWidth] is specified. + final double? maxHeight; + + /// Modifies the quality of the image, ranging from 0-100 where 100 is the + /// original/max quality. + /// + /// Compression is only supported for certain image types such as JPEG. If + /// compression is not supported for the image that is picked, a warning + /// message will be logged. + /// + /// If null, the image will be returned with the original quality. + final int? imageQuality; + + /// Used to specify the camera to use when the `source` is [ImageSource.camera]. + /// + /// Ignored if the source is not [ImageSource.camera], or the chosen camera is not + /// supported on the device. Defaults to [CameraDevice.rear]. + final CameraDevice preferredCameraDevice; + + /// If true, requests full image metadata, which may require extra permissions + /// on some platforms, (e.g., NSPhotoLibraryUsageDescription on iOS). + // + // Defaults to true. + final bool requestFullMetadata; +} diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart index 7f2844230287..dad86c5d1ba1 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. export 'camera_device.dart'; +export 'image_picker_options.dart'; export 'image_source.dart'; export 'lost_data_response.dart'; export 'picked_file/picked_file.dart'; diff --git a/packages/image_picker/image_picker_platform_interface/pubspec.yaml b/packages/image_picker/image_picker_platform_interface/pubspec.yaml index 54fd17e47260..be6d5442d03b 100644 --- a/packages/image_picker/image_picker_platform_interface/pubspec.yaml +++ b/packages/image_picker/image_picker_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/i issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.4.4 +version: 2.5.0 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart b/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart index 79d971f217f0..72ed363ef7ae 100644 --- a/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart +++ b/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart @@ -40,14 +40,16 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), isMethodCall('pickImage', arguments: { 'source': 1, 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), ], ); @@ -93,55 +95,62 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), isMethodCall('pickImage', arguments: { 'source': 0, 'maxWidth': 10.0, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), isMethodCall('pickImage', arguments: { 'source': 0, 'maxWidth': null, 'maxHeight': 10.0, 'imageQuality': null, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), isMethodCall('pickImage', arguments: { 'source': 0, 'maxWidth': 10.0, 'maxHeight': 20.0, 'imageQuality': null, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), isMethodCall('pickImage', arguments: { 'source': 0, 'maxWidth': 10.0, 'maxHeight': null, 'imageQuality': 70, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), isMethodCall('pickImage', arguments: { 'source': 0, 'maxWidth': null, 'maxHeight': 10.0, 'imageQuality': 70, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), isMethodCall('pickImage', arguments: { 'source': 0, 'maxWidth': 10.0, 'maxHeight': 20.0, 'imageQuality': 70, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), ], ); }); - test('does not accept a invalid imageQuality argument', () { + test('does not accept an invalid imageQuality argument', () { expect( () => picker.pickImage(imageQuality: -1, source: ImageSource.gallery), throwsArgumentError, @@ -196,6 +205,7 @@ void main() { 'maxHeight': null, 'imageQuality': null, 'cameraDevice': 0, + 'requestFullMetadata': true, }), ], ); @@ -215,6 +225,7 @@ void main() { 'maxHeight': null, 'imageQuality': null, 'cameraDevice': 1, + 'requestFullMetadata': true, }), ], ); @@ -320,7 +331,7 @@ void main() { ); }); - test('does not accept a invalid imageQuality argument', () { + test('does not accept an invalid imageQuality argument', () { returnValue = ['0', '1']; expect( () => picker.pickMultiImage(imageQuality: -1), @@ -509,14 +520,16 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), isMethodCall('pickImage', arguments: { 'source': 1, 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), ], ); @@ -562,55 +575,62 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), isMethodCall('pickImage', arguments: { 'source': 0, 'maxWidth': 10.0, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), isMethodCall('pickImage', arguments: { 'source': 0, 'maxWidth': null, 'maxHeight': 10.0, 'imageQuality': null, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), isMethodCall('pickImage', arguments: { 'source': 0, 'maxWidth': 10.0, 'maxHeight': 20.0, 'imageQuality': null, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), isMethodCall('pickImage', arguments: { 'source': 0, 'maxWidth': 10.0, 'maxHeight': null, 'imageQuality': 70, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), isMethodCall('pickImage', arguments: { 'source': 0, 'maxWidth': null, 'maxHeight': 10.0, 'imageQuality': 70, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), isMethodCall('pickImage', arguments: { 'source': 0, 'maxWidth': 10.0, 'maxHeight': 20.0, 'imageQuality': 70, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), ], ); }); - test('does not accept a invalid imageQuality argument', () { + test('does not accept an invalid imageQuality argument', () { expect( () => picker.getImage(imageQuality: -1, source: ImageSource.gallery), throwsArgumentError, @@ -664,6 +684,7 @@ void main() { 'maxHeight': null, 'imageQuality': null, 'cameraDevice': 0, + 'requestFullMetadata': true, }), ], ); @@ -683,6 +704,7 @@ void main() { 'maxHeight': null, 'imageQuality': null, 'cameraDevice': 1, + 'requestFullMetadata': true, }), ], ); @@ -788,7 +810,7 @@ void main() { ); }); - test('does not accept a invalid imageQuality argument', () { + test('does not accept an invalid imageQuality argument', () { returnValue = ['0', '1']; expect( () => picker.getMultiImage(imageQuality: -1), @@ -979,5 +1001,261 @@ void main() { expect(picker.getLostData(), throwsAssertionError); }); }); + + group('#getImageFromSource', () { + test('passes the image source argument correctly', () async { + await picker.getImageFromSource(source: ImageSource.camera); + await picker.getImageFromSource(source: ImageSource.gallery); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 1, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('passes the width and height arguments correctly', () async { + await picker.getImageFromSource(source: ImageSource.camera); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(maxWidth: 10.0), + ); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(maxHeight: 10.0), + ); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions( + maxWidth: 10.0, + maxHeight: 20.0, + ), + ); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions( + maxWidth: 10.0, + imageQuality: 70, + ), + ); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions( + maxHeight: 10.0, + imageQuality: 70, + ), + ); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions( + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ), + ); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': 70, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': 70, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': 70, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('does not accept an invalid imageQuality argument', () { + expect( + () => picker.getImageFromSource( + source: ImageSource.gallery, + options: const ImagePickerOptions(imageQuality: -1), + ), + throwsArgumentError, + ); + + expect( + () => picker.getImageFromSource( + source: ImageSource.gallery, + options: const ImagePickerOptions(imageQuality: 101), + ), + throwsArgumentError, + ); + + expect( + () => picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(imageQuality: -1), + ), + throwsArgumentError, + ); + + expect( + () => picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(imageQuality: 101), + ), + throwsArgumentError, + ); + }); + + test('does not accept a negative width or height argument', () { + expect( + () => picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(maxWidth: -1.0), + ), + throwsArgumentError, + ); + + expect( + () => picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(maxHeight: -1.0), + ), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + picker.channel + .setMockMethodCallHandler((MethodCall methodCall) => null); + + expect(await picker.getImageFromSource(source: ImageSource.gallery), + isNull); + expect(await picker.getImageFromSource(source: ImageSource.camera), + isNull); + }); + + test('camera position defaults to back', () async { + await picker.getImageFromSource(source: ImageSource.camera); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('camera position can set to front', () async { + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions( + preferredCameraDevice: CameraDevice.front, + ), + ); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 1, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('passes the full metadata argument correctly', () async { + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(requestFullMetadata: false), + ); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': false, + }), + ], + ); + }); + }); }); } From d76b52d8996c4d91ca614e185567f6cac179ca35 Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Wed, 11 May 2022 18:30:42 -0700 Subject: [PATCH 549/600] [google_sign_in] Fix tests to recognize new request attribute. (#5702) --- packages/google_sign_in/google_sign_in/CHANGELOG.md | 4 ++++ .../google_sign_in/test/google_sign_in_test.dart | 2 ++ 2 files changed, 6 insertions(+) diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index 5b47536cd2e2..8416e81f2412 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Fixes tests to recognize new default `forceCodeForRefreshToken` request attribute. + ## 5.3.1 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart index 119ee50a383b..3b0654637d68 100644 --- a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart +++ b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart @@ -96,6 +96,7 @@ void main() { 'scopes': [], 'hostedDomain': null, 'clientId': fakeClientId, + 'forceCodeForRefreshToken': false, }), isMethodCall('signIn', arguments: null), ], @@ -431,5 +432,6 @@ Matcher _isSignInMethodCall({String signInOption = 'SignInOption.standard'}) { 'scopes': [], 'hostedDomain': null, 'clientId': null, + 'forceCodeForRefreshToken': false, }); } From 298a26d909bf454b8a5054a12f6b6edd12cc5f59 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 12 May 2022 00:44:09 -0400 Subject: [PATCH 550/600] Roll Flutter from df7111a848cb to a9ac7fb03be1 (2 revisions) (#5697) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index bca64d8293a3..e1efdd362301 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -df7111a848cb0cc076a9c89d48332f0ec2700c45 +a9ac7fb03be1d574d726e98b90743015716de7fd From 4479e26172b997a4118599ca45742a7ef224e5b6 Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Thu, 12 May 2022 04:59:09 -0700 Subject: [PATCH 551/600] [ios_platform_images] Ignore ImageProvider.load deprecation. (#5701) --- packages/ios_platform_images/CHANGELOG.md | 4 ++++ packages/ios_platform_images/lib/ios_platform_images.dart | 2 ++ packages/ios_platform_images/pubspec.yaml | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/ios_platform_images/CHANGELOG.md b/packages/ios_platform_images/CHANGELOG.md index cf2632feaac7..d432bdb7ee8e 100644 --- a/packages/ios_platform_images/CHANGELOG.md +++ b/packages/ios_platform_images/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.0+7 + +* Ignores the warning for the upcoming deprecation of `ImageProvider.load`. + ## 0.2.0+6 * Removes unnecessary imports. diff --git a/packages/ios_platform_images/lib/ios_platform_images.dart b/packages/ios_platform_images/lib/ios_platform_images.dart index 4064fb312506..6a85ea0d189b 100644 --- a/packages/ios_platform_images/lib/ios_platform_images.dart +++ b/packages/ios_platform_images/lib/ios_platform_images.dart @@ -63,7 +63,9 @@ class _FutureMemoryImage extends ImageProvider<_FutureMemoryImage> { } /// See [ImageProvider.load]. + // TODO(jmagman): Implement the new API once it lands, https://github.com/flutter/flutter/issues/103556 @override + // ignore:deprecated_member_use ImageStreamCompleter load(_FutureMemoryImage key, DecoderCallback decode) { return _FutureImageStreamCompleter( codec: _loadAsync(key, decode), diff --git a/packages/ios_platform_images/pubspec.yaml b/packages/ios_platform_images/pubspec.yaml index 41a177560299..ddc02e6235db 100644 --- a/packages/ios_platform_images/pubspec.yaml +++ b/packages/ios_platform_images/pubspec.yaml @@ -2,7 +2,7 @@ name: ios_platform_images description: A plugin to share images between Flutter and iOS in add-to-app setups. repository: https://github.com/flutter/plugins/tree/main/packages/ios_platform_images issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+ios_platform_images%22 -version: 0.2.0+6 +version: 0.2.0+7 environment: sdk: ">=2.14.0 <3.0.0" From 3eedf6c8003be469cc24bd699781be4c48f304d0 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 12 May 2022 19:13:39 -0400 Subject: [PATCH 552/600] [image_picker] Switch unit tests to mock plaform implementation (#5706) `image_picker`'s app-facing tests were never updated during federation to use a mock platform implementation, and instead were still mocking method channels. That makes them fragile to implementation details of the default method channel implementation that is part of another package, and thus subject to breakage when the method channel changes. This converts them to using a mock platform implementation, so it's only testing the layer within this package. Removes some tests that were testing things that only made sense at the method channel layer. Adds argument assertions that there were tests for, but were previously only enforced in the implementations. As these are API constraints, they should be enforced at the API layer, not at each implementation's layer as they currently are. --- .../image_picker/image_picker/CHANGELOG.md | 7 + .../image_picker/lib/image_picker.dart | 22 + .../image_picker/image_picker/pubspec.yaml | 4 +- .../test/image_picker_deprecated_test.dart | 436 ++++++----------- .../image_picker/test/image_picker_test.dart | 449 +++++++----------- .../test/image_picker_test.mocks.dart | 136 ++++++ 6 files changed, 476 insertions(+), 578 deletions(-) create mode 100644 packages/image_picker/image_picker/test/image_picker_test.mocks.dart diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index 156b9a3a6d23..36a47b1a3d42 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,3 +1,10 @@ +## 0.8.5+3 + +* Adds argument error assertions to the app-facing package, to ensure + consistency across platform implementations. +* Updates tests to use a mock platform instead of relying on default + method channel implementation internals. + ## 0.8.5+2 * Minor fixes for new analysis options. diff --git a/packages/image_picker/image_picker/lib/image_picker.dart b/packages/image_picker/image_picker/lib/image_picker.dart index 5bc99d7f0bb2..84c649028c96 100755 --- a/packages/image_picker/image_picker/lib/image_picker.dart +++ b/packages/image_picker/image_picker/lib/image_picker.dart @@ -207,6 +207,17 @@ class ImagePicker { int? imageQuality, CameraDevice preferredCameraDevice = CameraDevice.rear, }) { + if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) { + throw ArgumentError.value( + imageQuality, 'imageQuality', 'must be between 0 and 100'); + } + if (maxWidth != null && maxWidth < 0) { + throw ArgumentError.value(maxWidth, 'maxWidth', 'cannot be negative'); + } + if (maxHeight != null && maxHeight < 0) { + throw ArgumentError.value(maxHeight, 'maxHeight', 'cannot be negative'); + } + return platform.getImage( source: source, maxWidth: maxWidth, @@ -245,6 +256,17 @@ class ImagePicker { double? maxHeight, int? imageQuality, }) { + if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) { + throw ArgumentError.value( + imageQuality, 'imageQuality', 'must be between 0 and 100'); + } + if (maxWidth != null && maxWidth < 0) { + throw ArgumentError.value(maxWidth, 'maxWidth', 'cannot be negative'); + } + if (maxHeight != null && maxHeight < 0) { + throw ArgumentError.value(maxHeight, 'maxHeight', 'cannot be negative'); + } + return platform.getMultiImage( maxWidth: maxWidth, maxHeight: maxHeight, diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index cc34d8ab33f5..9d0cedeec484 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.5+2 +version: 0.8.5+3 environment: sdk: ">=2.14.0 <3.0.0" @@ -28,6 +28,8 @@ dependencies: image_picker_platform_interface: ^2.3.0 dev_dependencies: + build_runner: ^2.1.10 + cross_file: ^0.3.1+1 # Mockito generates a direct include. flutter_test: sdk: flutter mockito: ^5.0.0 diff --git a/packages/image_picker/image_picker/test/image_picker_deprecated_test.dart b/packages/image_picker/image_picker/test/image_picker_deprecated_test.dart index 00049e14f808..b3db08020d7e 100644 --- a/packages/image_picker/image_picker/test/image_picker_deprecated_test.dart +++ b/packages/image_picker/image_picker/test/image_picker_deprecated_test.dart @@ -14,63 +14,46 @@ import 'package:image_picker_platform_interface/image_picker_platform_interface. import 'package:mockito/mockito.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - group('$ImagePicker', () { - const MethodChannel channel = - MethodChannel('plugins.flutter.io/image_picker'); +import 'image_picker_test.mocks.dart' as base_mock; - final List log = []; +// Add the mixin to make the platform interface accept the mock. +class MockImagePickerPlatform extends base_mock.MockImagePickerPlatform + with MockPlatformInterfaceMixin {} - final ImagePicker picker = ImagePicker(); +void main() { + group('ImagePicker', () { + late MockImagePickerPlatform mockPlatform; - test('ImagePicker platform instance overrides the actual platform used', - () { - final ImagePickerPlatform savedPlatform = ImagePickerPlatform.instance; - final MockPlatform mockPlatform = MockPlatform(); + setUp(() { + mockPlatform = MockImagePickerPlatform(); ImagePickerPlatform.instance = mockPlatform; - expect(ImagePicker.platform, mockPlatform); - ImagePickerPlatform.instance = savedPlatform; }); group('#Single image/video', () { setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - log.add(methodCall); - return ''; - }); - - log.clear(); + when(mockPlatform.pickImage( + source: anyNamed('source'), + maxWidth: anyNamed('maxWidth'), + maxHeight: anyNamed('maxHeight'), + imageQuality: anyNamed('imageQuality'), + preferredCameraDevice: anyNamed('preferredCameraDevice'))) + .thenAnswer((Invocation _) async => null); }); group('#pickImage', () { test('passes the image source argument correctly', () async { + final ImagePicker picker = ImagePicker(); await picker.getImage(source: ImageSource.camera); await picker.getImage(source: ImageSource.gallery); - expect( - log, - [ - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 1, - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 0 - }), - ], - ); + verifyInOrder([ + mockPlatform.pickImage(source: ImageSource.camera), + mockPlatform.pickImage(source: ImageSource.gallery), + ]); }); test('passes the width and height arguments correctly', () async { + final ImagePicker picker = ImagePicker(); await picker.getImage(source: ImageSource.camera); await picker.getImage( source: ImageSource.camera, @@ -95,277 +78,182 @@ void main() { maxHeight: 20.0, imageQuality: 70); - expect( - log, - [ - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': 10.0, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': 10.0, - 'imageQuality': null, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': 10.0, - 'maxHeight': 20.0, - 'imageQuality': null, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': 10.0, - 'maxHeight': null, - 'imageQuality': 70, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': 10.0, - 'imageQuality': 70, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': 10.0, - 'maxHeight': 20.0, - 'imageQuality': 70, - 'cameraDevice': 0 - }), - ], - ); - }); - - test('does not accept a negative width or height argument', () { - expect( - picker.getImage(source: ImageSource.camera, maxWidth: -1.0), - throwsArgumentError, - ); - - expect( - picker.getImage(source: ImageSource.camera, maxHeight: -1.0), - throwsArgumentError, - ); + verifyInOrder([ + mockPlatform.pickImage( + source: ImageSource.camera, + maxWidth: null, + maxHeight: null, + imageQuality: null), + mockPlatform.pickImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: null, + imageQuality: null), + mockPlatform.pickImage( + source: ImageSource.camera, + maxWidth: null, + maxHeight: 10.0, + imageQuality: null), + mockPlatform.pickImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: null), + mockPlatform.pickImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: null, + imageQuality: 70), + mockPlatform.pickImage( + source: ImageSource.camera, + maxWidth: null, + maxHeight: 10.0, + imageQuality: 70), + mockPlatform.pickImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70), + ]); }); - test('handles a null image path response gracefully', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) => null); + test('handles a null image file response gracefully', () async { + final ImagePicker picker = ImagePicker(); expect(await picker.getImage(source: ImageSource.gallery), isNull); expect(await picker.getImage(source: ImageSource.camera), isNull); }); test('camera position defaults to back', () async { + final ImagePicker picker = ImagePicker(); await picker.getImage(source: ImageSource.camera); - expect( - log, - [ - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 0, - }), - ], - ); + verify(mockPlatform.pickImage( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.rear)); }); test('camera position can set to front', () async { + final ImagePicker picker = ImagePicker(); await picker.getImage( source: ImageSource.camera, preferredCameraDevice: CameraDevice.front); - expect( - log, - [ - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 1, - }), - ], - ); + verify(mockPlatform.pickImage( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front)); }); }); group('#pickVideo', () { + setUp(() { + when(mockPlatform.pickVideo( + source: anyNamed('source'), + preferredCameraDevice: anyNamed('preferredCameraDevice'), + maxDuration: anyNamed('maxDuration'))) + .thenAnswer((Invocation _) async => null); + }); + test('passes the image source argument correctly', () async { + final ImagePicker picker = ImagePicker(); await picker.getVideo(source: ImageSource.camera); await picker.getVideo(source: ImageSource.gallery); - expect( - log, - [ - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'cameraDevice': 0, - 'maxDuration': null, - }), - isMethodCall('pickVideo', arguments: { - 'source': 1, - 'cameraDevice': 0, - 'maxDuration': null, - }), - ], - ); + verifyInOrder([ + mockPlatform.pickVideo(source: ImageSource.camera), + mockPlatform.pickVideo(source: ImageSource.gallery), + ]); }); test('passes the duration argument correctly', () async { + final ImagePicker picker = ImagePicker(); await picker.getVideo(source: ImageSource.camera); await picker.getVideo( source: ImageSource.camera, maxDuration: const Duration(seconds: 10)); - await picker.getVideo( - source: ImageSource.camera, - maxDuration: const Duration(minutes: 1)); - await picker.getVideo( - source: ImageSource.camera, - maxDuration: const Duration(hours: 1)); - expect( - log, - [ - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'maxDuration': null, - 'cameraDevice': 0, - }), - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'maxDuration': 10, - 'cameraDevice': 0, - }), - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'maxDuration': 60, - 'cameraDevice': 0, - }), - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'maxDuration': 3600, - 'cameraDevice': 0, - }), - ], - ); + + verifyInOrder([ + mockPlatform.pickVideo( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.rear, + maxDuration: null), + mockPlatform.pickVideo( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.rear, + maxDuration: const Duration(seconds: 10)), + ]); }); - test('handles a null video path response gracefully', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) => null); + test('handles a null video file response gracefully', () async { + final ImagePicker picker = ImagePicker(); expect(await picker.getVideo(source: ImageSource.gallery), isNull); expect(await picker.getVideo(source: ImageSource.camera), isNull); }); test('camera position defaults to back', () async { + final ImagePicker picker = ImagePicker(); await picker.getVideo(source: ImageSource.camera); - expect( - log, - [ - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'cameraDevice': 0, - 'maxDuration': null, - }), - ], - ); + verify(mockPlatform.pickVideo( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.rear)); }); test('camera position can set to front', () async { + final ImagePicker picker = ImagePicker(); await picker.getVideo( source: ImageSource.camera, preferredCameraDevice: CameraDevice.front); - expect( - log, - [ - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'maxDuration': null, - 'cameraDevice': 1, - }), - ], - ); + verify(mockPlatform.pickVideo( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front)); }); }); group('#retrieveLostData', () { test('retrieveLostData get success response', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return { - 'type': 'image', - 'path': '/example/path', - }; - }); + final ImagePicker picker = ImagePicker(); + when(mockPlatform.retrieveLostData()).thenAnswer( + (Invocation _) async => LostData( + file: PickedFile('/example/path'), type: RetrieveType.image)); + final LostData response = await picker.getLostData(); + expect(response.type, RetrieveType.image); expect(response.file!.path, '/example/path'); }); test('retrieveLostData get error response', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return { - 'type': 'video', - 'errorCode': 'test_error_code', - 'errorMessage': 'test_error_message', - }; - }); + final ImagePicker picker = ImagePicker(); + when(mockPlatform.retrieveLostData()).thenAnswer( + (Invocation _) async => LostData( + exception: PlatformException( + code: 'test_error_code', message: 'test_error_message'), + type: RetrieveType.video)); + final LostData response = await picker.getLostData(); + expect(response.type, RetrieveType.video); expect(response.exception!.code, 'test_error_code'); expect(response.exception!.message, 'test_error_message'); }); - - test('retrieveLostData get null response', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return null; - }); - expect((await picker.getLostData()).isEmpty, true); - }); - - test('retrieveLostData get both path and error should throw', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return { - 'type': 'video', - 'errorCode': 'test_error_code', - 'errorMessage': 'test_error_message', - 'path': '/example/path', - }; - }); - expect(picker.getLostData(), throwsAssertionError); - }); }); }); group('Multi images', () { setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - log.add(methodCall); - return []; - }); - log.clear(); + when(mockPlatform.pickMultiImage( + maxWidth: anyNamed('maxWidth'), + maxHeight: anyNamed('maxHeight'), + imageQuality: anyNamed('imageQuality'))) + .thenAnswer((Invocation _) async => null); }); group('#pickMultiImage', () { test('passes the width and height arguments correctly', () async { + final ImagePicker picker = ImagePicker(); await picker.getMultiImage(); await picker.getMultiImage( maxWidth: 10.0, @@ -388,62 +276,26 @@ void main() { await picker.getMultiImage( maxWidth: 10.0, maxHeight: 20.0, imageQuality: 70); - expect( - log, - [ - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': 10.0, - 'maxHeight': null, - 'imageQuality': null, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': null, - 'maxHeight': 10.0, - 'imageQuality': null, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': 10.0, - 'maxHeight': 20.0, - 'imageQuality': null, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': 10.0, - 'maxHeight': null, - 'imageQuality': 70, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': null, - 'maxHeight': 10.0, - 'imageQuality': 70, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': 10.0, - 'maxHeight': 20.0, - 'imageQuality': 70, - }), - ], - ); + verifyInOrder([ + mockPlatform.pickMultiImage( + maxWidth: null, maxHeight: null, imageQuality: null), + mockPlatform.pickMultiImage( + maxWidth: 10.0, maxHeight: null, imageQuality: null), + mockPlatform.pickMultiImage( + maxWidth: null, maxHeight: 10.0, imageQuality: null), + mockPlatform.pickMultiImage( + maxWidth: 10.0, maxHeight: 20.0, imageQuality: null), + mockPlatform.pickMultiImage( + maxWidth: 10.0, maxHeight: null, imageQuality: 70), + mockPlatform.pickMultiImage( + maxWidth: null, maxHeight: 10.0, imageQuality: 70), + mockPlatform.pickMultiImage( + maxWidth: 10.0, maxHeight: 20.0, imageQuality: 70), + ]); }); - test('does not accept a negative width or height argument', () { - expect( - picker.getMultiImage(maxWidth: -1.0), - throwsArgumentError, - ); - - expect( - picker.getMultiImage(maxHeight: -1.0), - throwsArgumentError, - ); - }); - - test('handles a null image path response gracefully', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) => null); + test('handles a null image file response gracefully', () async { + final ImagePicker picker = ImagePicker(); expect(await picker.getMultiImage(), isNull); expect(await picker.getMultiImage(), isNull); @@ -452,7 +304,3 @@ void main() { }); }); } - -class MockPlatform extends Mock - with MockPlatformInterfaceMixin - implements ImagePickerPlatform {} diff --git a/packages/image_picker/image_picker/test/image_picker_test.dart b/packages/image_picker/image_picker/test/image_picker_test.dart index b41fbe3381df..f981195fe1b3 100644 --- a/packages/image_picker/image_picker/test/image_picker_test.dart +++ b/packages/image_picker/image_picker/test/image_picker_test.dart @@ -6,66 +6,51 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:image_picker/image_picker.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; +import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - group('$ImagePicker', () { - const MethodChannel channel = - MethodChannel('plugins.flutter.io/image_picker'); +import 'image_picker_test.mocks.dart' as base_mock; - final List log = []; +// Add the mixin to make the platform interface accept the mock. +class MockImagePickerPlatform extends base_mock.MockImagePickerPlatform + with MockPlatformInterfaceMixin {} - final ImagePicker picker = ImagePicker(); +@GenerateMocks([ImagePickerPlatform]) +void main() { + group('ImagePicker', () { + late MockImagePickerPlatform mockPlatform; - test('ImagePicker platform instance overrides the actual platform used', - () { - final ImagePickerPlatform savedPlatform = ImagePickerPlatform.instance; - final MockPlatform mockPlatform = MockPlatform(); + setUp(() { + mockPlatform = MockImagePickerPlatform(); ImagePickerPlatform.instance = mockPlatform; - expect(ImagePicker.platform, mockPlatform); - ImagePickerPlatform.instance = savedPlatform; }); group('#Single image/video', () { - setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - log.add(methodCall); - return ''; + group('#pickImage', () { + setUp(() { + when(mockPlatform.getImage( + source: anyNamed('source'), + maxWidth: anyNamed('maxWidth'), + maxHeight: anyNamed('maxHeight'), + imageQuality: anyNamed('imageQuality'), + preferredCameraDevice: anyNamed('preferredCameraDevice'))) + .thenAnswer((Invocation _) async => null); }); - log.clear(); - }); - - group('#pickImage', () { test('passes the image source argument correctly', () async { + final ImagePicker picker = ImagePicker(); await picker.pickImage(source: ImageSource.camera); await picker.pickImage(source: ImageSource.gallery); - expect( - log, - [ - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 1, - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 0 - }), - ], - ); + verifyInOrder([ + mockPlatform.getImage(source: ImageSource.camera), + mockPlatform.getImage(source: ImageSource.gallery), + ]); }); test('passes the width and height arguments correctly', () async { + final ImagePicker picker = ImagePicker(); await picker.pickImage(source: ImageSource.camera); await picker.pickImage( source: ImageSource.camera, @@ -90,242 +75,184 @@ void main() { maxHeight: 20.0, imageQuality: 70); - expect( - log, - [ - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': 10.0, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': 10.0, - 'imageQuality': null, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': 10.0, - 'maxHeight': 20.0, - 'imageQuality': null, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': 10.0, - 'maxHeight': null, - 'imageQuality': 70, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': 10.0, - 'imageQuality': 70, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': 10.0, - 'maxHeight': 20.0, - 'imageQuality': 70, - 'cameraDevice': 0 - }), - ], - ); + verifyInOrder([ + mockPlatform.getImage( + source: ImageSource.camera, + maxWidth: null, + maxHeight: null, + imageQuality: null), + mockPlatform.getImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: null, + imageQuality: null), + mockPlatform.getImage( + source: ImageSource.camera, + maxWidth: null, + maxHeight: 10.0, + imageQuality: null), + mockPlatform.getImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: null), + mockPlatform.getImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: null, + imageQuality: 70), + mockPlatform.getImage( + source: ImageSource.camera, + maxWidth: null, + maxHeight: 10.0, + imageQuality: 70), + mockPlatform.getImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70), + ]); }); test('does not accept a negative width or height argument', () { + final ImagePicker picker = ImagePicker(); expect( - picker.pickImage(source: ImageSource.camera, maxWidth: -1.0), + () => picker.pickImage(source: ImageSource.camera, maxWidth: -1.0), throwsArgumentError, ); expect( - picker.pickImage(source: ImageSource.camera, maxHeight: -1.0), + () => picker.pickImage(source: ImageSource.camera, maxHeight: -1.0), throwsArgumentError, ); }); - test('handles a null image path response gracefully', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) => null); + test('handles a null image file response gracefully', () async { + final ImagePicker picker = ImagePicker(); expect(await picker.pickImage(source: ImageSource.gallery), isNull); expect(await picker.pickImage(source: ImageSource.camera), isNull); }); test('camera position defaults to back', () async { + final ImagePicker picker = ImagePicker(); await picker.pickImage(source: ImageSource.camera); - expect( - log, - [ - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 0, - }), - ], - ); + verify(mockPlatform.getImage( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.rear)); }); test('camera position can set to front', () async { + final ImagePicker picker = ImagePicker(); await picker.pickImage( source: ImageSource.camera, preferredCameraDevice: CameraDevice.front); - expect( - log, - [ - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 1, - }), - ], - ); + verify(mockPlatform.getImage( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front)); }); }); group('#pickVideo', () { + setUp(() { + when(mockPlatform.getVideo( + source: anyNamed('source'), + preferredCameraDevice: anyNamed('preferredCameraDevice'), + maxDuration: anyNamed('maxDuration'))) + .thenAnswer((Invocation _) async => null); + }); + test('passes the image source argument correctly', () async { + final ImagePicker picker = ImagePicker(); await picker.pickVideo(source: ImageSource.camera); await picker.pickVideo(source: ImageSource.gallery); - expect( - log, - [ - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'cameraDevice': 0, - 'maxDuration': null, - }), - isMethodCall('pickVideo', arguments: { - 'source': 1, - 'cameraDevice': 0, - 'maxDuration': null, - }), - ], - ); + verifyInOrder([ + mockPlatform.getVideo(source: ImageSource.camera), + mockPlatform.getVideo(source: ImageSource.gallery), + ]); }); test('passes the duration argument correctly', () async { + final ImagePicker picker = ImagePicker(); await picker.pickVideo(source: ImageSource.camera); await picker.pickVideo( source: ImageSource.camera, maxDuration: const Duration(seconds: 10)); - await picker.pickVideo( - source: ImageSource.camera, - maxDuration: const Duration(minutes: 1)); - await picker.pickVideo( - source: ImageSource.camera, - maxDuration: const Duration(hours: 1)); - expect( - log, - [ - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'maxDuration': null, - 'cameraDevice': 0, - }), - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'maxDuration': 10, - 'cameraDevice': 0, - }), - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'maxDuration': 60, - 'cameraDevice': 0, - }), - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'maxDuration': 3600, - 'cameraDevice': 0, - }), - ], - ); + + verifyInOrder([ + mockPlatform.getVideo( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.rear, + maxDuration: null), + mockPlatform.getVideo( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.rear, + maxDuration: const Duration(seconds: 10)), + ]); }); - test('handles a null video path response gracefully', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) => null); + test('handles a null video file response gracefully', () async { + final ImagePicker picker = ImagePicker(); expect(await picker.pickVideo(source: ImageSource.gallery), isNull); expect(await picker.pickVideo(source: ImageSource.camera), isNull); }); test('camera position defaults to back', () async { + final ImagePicker picker = ImagePicker(); await picker.pickVideo(source: ImageSource.camera); - expect( - log, - [ - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'cameraDevice': 0, - 'maxDuration': null, - }), - ], - ); + verify(mockPlatform.getVideo( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.rear)); }); test('camera position can set to front', () async { + final ImagePicker picker = ImagePicker(); await picker.pickVideo( source: ImageSource.camera, preferredCameraDevice: CameraDevice.front); - expect( - log, - [ - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'maxDuration': null, - 'cameraDevice': 1, - }), - ], - ); + verify(mockPlatform.getVideo( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front)); }); }); group('#retrieveLostData', () { test('retrieveLostData get success response', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return { - 'type': 'image', - 'path': '/example/path', - }; - }); + final ImagePicker picker = ImagePicker(); + final XFile lostFile = XFile('/example/path'); + when(mockPlatform.getLostData()).thenAnswer((Invocation _) async => + LostDataResponse( + file: lostFile, + files: [lostFile], + type: RetrieveType.image)); + final LostDataResponse response = await picker.retrieveLostData(); + expect(response.type, RetrieveType.image); expect(response.file!.path, '/example/path'); }); test('retrieveLostData should successfully retrieve multiple files', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return { - 'type': 'image', - 'path': '/example/path1', - 'pathList': ['/example/path0', '/example/path1'], - }; - }); + final ImagePicker picker = ImagePicker(); + final List lostFiles = [ + XFile('/example/path0'), + XFile('/example/path1'), + ]; + when(mockPlatform.getLostData()).thenAnswer((Invocation _) async => + LostDataResponse( + file: lostFiles.last, + files: lostFiles, + type: RetrieveType.image)); final LostDataResponse response = await picker.retrieveLostData(); + expect(response.type, RetrieveType.image); expect(response.file, isNotNull); expect(response.file!.path, '/example/path1'); @@ -334,51 +261,34 @@ void main() { }); test('retrieveLostData get error response', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return { - 'type': 'video', - 'errorCode': 'test_error_code', - 'errorMessage': 'test_error_message', - }; - }); + final ImagePicker picker = ImagePicker(); + when(mockPlatform.getLostData()).thenAnswer((Invocation _) async => + LostDataResponse( + exception: PlatformException( + code: 'test_error_code', message: 'test_error_message'), + type: RetrieveType.video)); + final LostDataResponse response = await picker.retrieveLostData(); + expect(response.type, RetrieveType.video); expect(response.exception!.code, 'test_error_code'); expect(response.exception!.message, 'test_error_message'); }); - - test('retrieveLostData get null response', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return null; - }); - expect((await picker.retrieveLostData()).isEmpty, true); - }); - - test('retrieveLostData get both path and error should throw', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return { - 'type': 'video', - 'errorCode': 'test_error_code', - 'errorMessage': 'test_error_message', - 'path': '/example/path', - }; - }); - expect(picker.retrieveLostData(), throwsAssertionError); - }); }); }); group('#Multi images', () { setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - log.add(methodCall); - return []; - }); - log.clear(); + when(mockPlatform.getMultiImage( + maxWidth: anyNamed('maxWidth'), + maxHeight: anyNamed('maxHeight'), + imageQuality: anyNamed('imageQuality'))) + .thenAnswer((Invocation _) async => null); }); group('#pickMultiImage', () { test('passes the width and height arguments correctly', () async { + final ImagePicker picker = ImagePicker(); await picker.pickMultiImage(); await picker.pickMultiImage( maxWidth: 10.0, @@ -401,62 +311,39 @@ void main() { await picker.pickMultiImage( maxWidth: 10.0, maxHeight: 20.0, imageQuality: 70); - expect( - log, - [ - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': 10.0, - 'maxHeight': null, - 'imageQuality': null, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': null, - 'maxHeight': 10.0, - 'imageQuality': null, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': 10.0, - 'maxHeight': 20.0, - 'imageQuality': null, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': 10.0, - 'maxHeight': null, - 'imageQuality': 70, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': null, - 'maxHeight': 10.0, - 'imageQuality': 70, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': 10.0, - 'maxHeight': 20.0, - 'imageQuality': 70, - }), - ], - ); + verifyInOrder([ + mockPlatform.getMultiImage( + maxWidth: null, maxHeight: null, imageQuality: null), + mockPlatform.getMultiImage( + maxWidth: 10.0, maxHeight: null, imageQuality: null), + mockPlatform.getMultiImage( + maxWidth: null, maxHeight: 10.0, imageQuality: null), + mockPlatform.getMultiImage( + maxWidth: 10.0, maxHeight: 20.0, imageQuality: null), + mockPlatform.getMultiImage( + maxWidth: 10.0, maxHeight: null, imageQuality: 70), + mockPlatform.getMultiImage( + maxWidth: null, maxHeight: 10.0, imageQuality: 70), + mockPlatform.getMultiImage( + maxWidth: 10.0, maxHeight: 20.0, imageQuality: 70), + ]); }); test('does not accept a negative width or height argument', () { + final ImagePicker picker = ImagePicker(); expect( - picker.pickMultiImage(maxWidth: -1.0), + () => picker.pickMultiImage(maxWidth: -1.0), throwsArgumentError, ); expect( - picker.pickMultiImage(maxHeight: -1.0), + () => picker.pickMultiImage(maxHeight: -1.0), throwsArgumentError, ); }); - test('handles a null image path response gracefully', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) => null); + test('handles a null image file response gracefully', () async { + final ImagePicker picker = ImagePicker(); expect(await picker.pickMultiImage(), isNull); expect(await picker.pickMultiImage(), isNull); @@ -465,7 +352,3 @@ void main() { }); }); } - -class MockPlatform extends Mock - with MockPlatformInterfaceMixin - implements ImagePickerPlatform {} diff --git a/packages/image_picker/image_picker/test/image_picker_test.mocks.dart b/packages/image_picker/image_picker/test/image_picker_test.mocks.dart new file mode 100644 index 000000000000..641a104a33c5 --- /dev/null +++ b/packages/image_picker/image_picker/test/image_picker_test.mocks.dart @@ -0,0 +1,136 @@ +// Mocks generated by Mockito 5.1.0 from annotations +// in image_picker/test/image_picker_test.dart. +// Do not manually edit this file. + +import 'dart:async' as _i4; + +import 'package:cross_file/cross_file.dart' as _i5; +import 'package:image_picker_platform_interface/src/platform_interface/image_picker_platform.dart' + as _i3; +import 'package:image_picker_platform_interface/src/types/types.dart' as _i2; +import 'package:mockito/mockito.dart' as _i1; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types + +class _FakeLostData_0 extends _i1.Fake implements _i2.LostData {} + +class _FakeLostDataResponse_1 extends _i1.Fake implements _i2.LostDataResponse { +} + +/// A class which mocks [ImagePickerPlatform]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockImagePickerPlatform extends _i1.Mock + implements _i3.ImagePickerPlatform { + MockImagePickerPlatform() { + _i1.throwOnMissingStub(this); + } + + @override + _i4.Future<_i2.PickedFile?> pickImage( + {_i2.ImageSource? source, + double? maxWidth, + double? maxHeight, + int? imageQuality, + _i2.CameraDevice? preferredCameraDevice = _i2.CameraDevice.rear}) => + (super.noSuchMethod( + Invocation.method(#pickImage, [], { + #source: source, + #maxWidth: maxWidth, + #maxHeight: maxHeight, + #imageQuality: imageQuality, + #preferredCameraDevice: preferredCameraDevice + }), + returnValue: Future<_i2.PickedFile?>.value()) + as _i4.Future<_i2.PickedFile?>); + @override + _i4.Future?> pickMultiImage( + {double? maxWidth, double? maxHeight, int? imageQuality}) => + (super.noSuchMethod( + Invocation.method(#pickMultiImage, [], { + #maxWidth: maxWidth, + #maxHeight: maxHeight, + #imageQuality: imageQuality + }), + returnValue: Future?>.value()) + as _i4.Future?>); + @override + _i4.Future<_i2.PickedFile?> pickVideo( + {_i2.ImageSource? source, + _i2.CameraDevice? preferredCameraDevice = _i2.CameraDevice.rear, + Duration? maxDuration}) => + (super.noSuchMethod( + Invocation.method(#pickVideo, [], { + #source: source, + #preferredCameraDevice: preferredCameraDevice, + #maxDuration: maxDuration + }), + returnValue: Future<_i2.PickedFile?>.value()) + as _i4.Future<_i2.PickedFile?>); + @override + _i4.Future<_i2.LostData> retrieveLostData() => + (super.noSuchMethod(Invocation.method(#retrieveLostData, []), + returnValue: Future<_i2.LostData>.value(_FakeLostData_0())) + as _i4.Future<_i2.LostData>); + @override + _i4.Future<_i5.XFile?> getImage( + {_i2.ImageSource? source, + double? maxWidth, + double? maxHeight, + int? imageQuality, + _i2.CameraDevice? preferredCameraDevice = _i2.CameraDevice.rear}) => + (super.noSuchMethod( + Invocation.method(#getImage, [], { + #source: source, + #maxWidth: maxWidth, + #maxHeight: maxHeight, + #imageQuality: imageQuality, + #preferredCameraDevice: preferredCameraDevice + }), + returnValue: Future<_i5.XFile?>.value()) as _i4.Future<_i5.XFile?>); + @override + _i4.Future?> getMultiImage( + {double? maxWidth, double? maxHeight, int? imageQuality}) => + (super.noSuchMethod( + Invocation.method(#getMultiImage, [], { + #maxWidth: maxWidth, + #maxHeight: maxHeight, + #imageQuality: imageQuality + }), + returnValue: Future?>.value()) + as _i4.Future?>); + @override + _i4.Future<_i5.XFile?> getVideo( + {_i2.ImageSource? source, + _i2.CameraDevice? preferredCameraDevice = _i2.CameraDevice.rear, + Duration? maxDuration}) => + (super.noSuchMethod( + Invocation.method(#getVideo, [], { + #source: source, + #preferredCameraDevice: preferredCameraDevice, + #maxDuration: maxDuration + }), + returnValue: Future<_i5.XFile?>.value()) as _i4.Future<_i5.XFile?>); + @override + _i4.Future<_i2.LostDataResponse> getLostData() => + (super.noSuchMethod(Invocation.method(#getLostData, []), + returnValue: + Future<_i2.LostDataResponse>.value(_FakeLostDataResponse_1())) + as _i4.Future<_i2.LostDataResponse>); + @override + _i4.Future<_i5.XFile?> getImageFromSource( + {_i2.ImageSource? source, + _i2.ImagePickerOptions? options = const _i2.ImagePickerOptions()}) => + (super.noSuchMethod( + Invocation.method( + #getImageFromSource, [], {#source: source, #options: options}), + returnValue: Future<_i5.XFile?>.value()) as _i4.Future<_i5.XFile?>); +} From 3d995d484ce3436ebbb891640bcd48771abeb00b Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 12 May 2022 20:29:07 -0400 Subject: [PATCH 553/600] [google_sign_in] Switch unit tests to mock platform implementation (#5703) --- .../google_sign_in/CHANGELOG.md | 3 +- .../google_sign_in/pubspec.yaml | 2 + .../test/google_sign_in_test.dart | 406 +++++++----------- .../test/google_sign_in_test.mocks.dart | 100 +++++ 4 files changed, 260 insertions(+), 251 deletions(-) create mode 100644 packages/google_sign_in/google_sign_in/test/google_sign_in_test.mocks.dart diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index 8416e81f2412..9edadf7b0469 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,6 +1,7 @@ ## NEXT -* Fixes tests to recognize new default `forceCodeForRefreshToken` request attribute. +* Updates tests to use a mock platform instead of relying on default + method channel implementation internals. ## 5.3.1 diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index e58b27af08b7..d1c13c6a8ec4 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/pubspec.yaml @@ -29,6 +29,7 @@ dependencies: google_sign_in_web: ^0.10.0 dev_dependencies: + build_runner: ^2.1.10 flutter_driver: sdk: flutter flutter_test: @@ -36,6 +37,7 @@ dev_dependencies: http: ^0.13.0 integration_test: sdk: flutter + mockito: ^5.1.0 # The example deliberately includes limited-use secrets. false_secrets: diff --git a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart index 3b0654637d68..61acfd81bf09 100644 --- a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart +++ b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart @@ -4,229 +4,176 @@ import 'dart:async'; -import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_sign_in/google_sign_in.dart'; -import 'package:google_sign_in/testing.dart'; import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'google_sign_in_test.mocks.dart'; + +@GenerateMocks([GoogleSignInPlatform]) void main() { - TestWidgetsFlutterBinding.ensureInitialized(); + late MockGoogleSignInPlatform mockPlatform; group('GoogleSignIn', () { - const MethodChannel channel = MethodChannel( - 'plugins.flutter.io/google_sign_in', - ); - - const Map kUserData = { - 'email': 'john.doe@gmail.com', - 'id': '8162538176523816253123', - 'photoUrl': 'https://lh5.googleusercontent.com/photo.jpg', - 'displayName': 'John Doe', - 'serverAuthCode': '789' - }; - - const Map kDefaultResponses = { - 'init': null, - 'signInSilently': kUserData, - 'signIn': kUserData, - 'signOut': null, - 'disconnect': null, - 'isSignedIn': true, - 'requestScopes': true, - 'getTokens': { - 'idToken': '123', - 'accessToken': '456', - 'serverAuthCode': '789', - }, - }; - - final List log = []; - late Map responses; - late GoogleSignIn googleSignIn; + final GoogleSignInUserData kDefaultUser = GoogleSignInUserData( + email: 'john.doe@gmail.com', + id: '8162538176523816253123', + photoUrl: 'https://lh5.googleusercontent.com/photo.jpg', + displayName: 'John Doe', + serverAuthCode: '789'); setUp(() { - responses = Map.from(kDefaultResponses); - channel.setMockMethodCallHandler((MethodCall methodCall) { - log.add(methodCall); - final dynamic response = responses[methodCall.method]; - if (response != null && response is Exception) { - return Future.error('$response'); - } - return Future.value(response); - }); - googleSignIn = GoogleSignIn(); - log.clear(); + mockPlatform = MockGoogleSignInPlatform(); + when(mockPlatform.isMock).thenReturn(true); + when(mockPlatform.signInSilently()) + .thenAnswer((Invocation _) async => kDefaultUser); + when(mockPlatform.signIn()) + .thenAnswer((Invocation _) async => kDefaultUser); + + GoogleSignInPlatform.instance = mockPlatform; }); test('signInSilently', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); + await googleSignIn.signInSilently(); + expect(googleSignIn.currentUser, isNotNull); - expect( - log, - [ - _isSignInMethodCall(), - isMethodCall('signInSilently', arguments: null), - ], - ); + _verifyInit(mockPlatform); + verify(mockPlatform.signInSilently()); }); test('signIn', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); + await googleSignIn.signIn(); + expect(googleSignIn.currentUser, isNotNull); - expect( - log, - [ - _isSignInMethodCall(), - isMethodCall('signIn', arguments: null), - ], - ); + _verifyInit(mockPlatform); + verify(mockPlatform.signIn()); }); test('signIn prioritize clientId parameter when available', () async { const String fakeClientId = 'fakeClientId'; - googleSignIn = GoogleSignIn(clientId: fakeClientId); + final GoogleSignIn googleSignIn = GoogleSignIn(clientId: fakeClientId); + await googleSignIn.signIn(); - expect(googleSignIn.currentUser, isNotNull); - expect( - log, - [ - isMethodCall('init', arguments: { - 'signInOption': 'SignInOption.standard', - 'scopes': [], - 'hostedDomain': null, - 'clientId': fakeClientId, - 'forceCodeForRefreshToken': false, - }), - isMethodCall('signIn', arguments: null), - ], - ); + + _verifyInit(mockPlatform, clientId: fakeClientId); + verify(mockPlatform.signIn()); }); test('signOut', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); + await googleSignIn.signOut(); - expect(googleSignIn.currentUser, isNull); - expect(log, [ - _isSignInMethodCall(), - isMethodCall('signOut', arguments: null), - ]); + + _verifyInit(mockPlatform); + verify(mockPlatform.signOut()); }); test('disconnect; null response', () async { - await googleSignIn.disconnect(); - expect(googleSignIn.currentUser, isNull); - expect( - log, - [ - _isSignInMethodCall(), - isMethodCall('disconnect', arguments: null), - ], - ); - }); + final GoogleSignIn googleSignIn = GoogleSignIn(); - test('disconnect; empty response as on iOS', () async { - responses['disconnect'] = {}; await googleSignIn.disconnect(); + expect(googleSignIn.currentUser, isNull); - expect( - log, - [ - _isSignInMethodCall(), - isMethodCall('disconnect', arguments: null), - ], - ); + _verifyInit(mockPlatform); + verify(mockPlatform.disconnect()); }); test('isSignedIn', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); + when(mockPlatform.isSignedIn()).thenAnswer((Invocation _) async => true); + final bool result = await googleSignIn.isSignedIn(); + expect(result, isTrue); - expect(log, [ - _isSignInMethodCall(), - isMethodCall('isSignedIn', arguments: null), - ]); + _verifyInit(mockPlatform); + verify(mockPlatform.isSignedIn()); }); test('signIn works even if a previous call throws error in other zone', () async { - responses['signInSilently'] = Exception('Not a user'); + final GoogleSignIn googleSignIn = GoogleSignIn(); + + when(mockPlatform.signInSilently()).thenThrow(Exception('Not a user')); await runZonedGuarded(() async { expect(await googleSignIn.signInSilently(), isNull); }, (Object e, StackTrace st) {}); expect(await googleSignIn.signIn(), isNotNull); - expect( - log, - [ - _isSignInMethodCall(), - isMethodCall('signInSilently', arguments: null), - isMethodCall('signIn', arguments: null), - ], - ); + _verifyInit(mockPlatform); + verify(mockPlatform.signInSilently()); + verify(mockPlatform.signIn()); }); test('concurrent calls of the same method trigger sign in once', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); final List> futures = >[ googleSignIn.signInSilently(), googleSignIn.signInSilently(), ]; + expect(futures.first, isNot(futures.last), reason: 'Must return new Future'); + final List users = await Future.wait(futures); + expect(googleSignIn.currentUser, isNotNull); expect(users, [ googleSignIn.currentUser, googleSignIn.currentUser ]); - expect( - log, - [ - _isSignInMethodCall(), - isMethodCall('signInSilently', arguments: null), - ], - ); + _verifyInit(mockPlatform); + verify(mockPlatform.signInSilently()).called(1); }); test('can sign in after previously failed attempt', () async { - responses['signInSilently'] = Exception('Not a user'); + final GoogleSignIn googleSignIn = GoogleSignIn(); + when(mockPlatform.signInSilently()).thenThrow(Exception('Not a user')); + expect(await googleSignIn.signInSilently(), isNull); expect(await googleSignIn.signIn(), isNotNull); - expect( - log, - [ - _isSignInMethodCall(), - isMethodCall('signInSilently', arguments: null), - isMethodCall('signIn', arguments: null), - ], - ); + + _verifyInit(mockPlatform); + verify(mockPlatform.signInSilently()); + verify(mockPlatform.signIn()); }); test('concurrent calls of different signIn methods', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); final List> futures = >[ googleSignIn.signInSilently(), googleSignIn.signIn(), ]; expect(futures.first, isNot(futures.last)); + final List users = await Future.wait(futures); - expect( - log, - [ - _isSignInMethodCall(), - isMethodCall('signInSilently', arguments: null), - ], - ); + expect(users.first, users.last, reason: 'Must return the same user'); expect(googleSignIn.currentUser, users.last); + _verifyInit(mockPlatform); + verify(mockPlatform.signInSilently()); + verifyNever(mockPlatform.signIn()); }); test('can sign in after aborted flow', () async { - responses['signIn'] = null; + final GoogleSignIn googleSignIn = GoogleSignIn(); + + when(mockPlatform.signIn()).thenAnswer((Invocation _) async => null); expect(await googleSignIn.signIn(), isNull); - responses['signIn'] = kUserData; + + when(mockPlatform.signIn()) + .thenAnswer((Invocation _) async => kDefaultUser); expect(await googleSignIn.signIn(), isNotNull); }); test('signOut/disconnect methods always trigger native calls', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); final List> futures = >[ googleSignIn.signOut(), @@ -234,20 +181,16 @@ void main() { googleSignIn.disconnect(), googleSignIn.disconnect(), ]; + await Future.wait(futures); - expect( - log, - [ - _isSignInMethodCall(), - isMethodCall('signOut', arguments: null), - isMethodCall('signOut', arguments: null), - isMethodCall('disconnect', arguments: null), - isMethodCall('disconnect', arguments: null), - ], - ); + + _verifyInit(mockPlatform); + verify(mockPlatform.signOut()).called(2); + verify(mockPlatform.disconnect()).called(2); }); test('queue of many concurrent calls', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); final List> futures = >[ googleSignIn.signInSilently(), @@ -255,183 +198,146 @@ void main() { googleSignIn.signIn(), googleSignIn.disconnect(), ]; + await Future.wait(futures); - expect( - log, - [ - _isSignInMethodCall(), - isMethodCall('signInSilently', arguments: null), - isMethodCall('signOut', arguments: null), - isMethodCall('signIn', arguments: null), - isMethodCall('disconnect', arguments: null), - ], - ); + + _verifyInit(mockPlatform); + verifyInOrder([ + mockPlatform.signInSilently(), + mockPlatform.signOut(), + mockPlatform.signIn(), + mockPlatform.disconnect(), + ]); }); test('signInSilently suppresses errors by default', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) { - throw 'I am an error'; - }); + final GoogleSignIn googleSignIn = GoogleSignIn(); + when(mockPlatform.signInSilently()).thenThrow(Exception('I am an error')); expect(await googleSignIn.signInSilently(), isNull); // should not throw }); - test('signInSilently forwards errors', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) { - throw 'I am an error'; - }); + test('signInSilently forwards exceptions', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); + when(mockPlatform.signInSilently()).thenThrow(Exception('I am an error')); expect(googleSignIn.signInSilently(suppressErrors: false), - throwsA(isInstanceOf())); + throwsA(isInstanceOf())); }); test('signInSilently allows re-authentication to be requested', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); await googleSignIn.signInSilently(); expect(googleSignIn.currentUser, isNotNull); await googleSignIn.signInSilently(reAuthenticate: true); - expect( - log, - [ - _isSignInMethodCall(), - isMethodCall('signInSilently', arguments: null), - isMethodCall('signInSilently', arguments: null), - ], - ); + _verifyInit(mockPlatform); + verify(mockPlatform.signInSilently()).called(2); }); test('can sign in after init failed before', () async { - int initCount = 0; - channel.setMockMethodCallHandler((MethodCall methodCall) { - if (methodCall.method == 'init') { - initCount++; - if (initCount == 1) { - throw 'First init fails'; - } - } - return Future.value(responses[methodCall.method]); - }); - expect(googleSignIn.signIn(), throwsA(isInstanceOf())); + final GoogleSignIn googleSignIn = GoogleSignIn(); + + when(mockPlatform.init()).thenThrow(Exception('First init fails')); + expect(googleSignIn.signIn(), throwsA(isInstanceOf())); + + when(mockPlatform.init()).thenAnswer((Invocation _) async {}); expect(await googleSignIn.signIn(), isNotNull); }); test('created with standard factory uses correct options', () async { - googleSignIn = GoogleSignIn.standard(); + final GoogleSignIn googleSignIn = GoogleSignIn.standard(); await googleSignIn.signInSilently(); expect(googleSignIn.currentUser, isNotNull); - expect( - log, - [ - _isSignInMethodCall(), - isMethodCall('signInSilently', arguments: null), - ], - ); + _verifyInit(mockPlatform); + verify(mockPlatform.signInSilently()); }); test('created with defaultGamesSignIn factory uses correct options', () async { - googleSignIn = GoogleSignIn.games(); + final GoogleSignIn googleSignIn = GoogleSignIn.games(); await googleSignIn.signInSilently(); expect(googleSignIn.currentUser, isNotNull); - expect( - log, - [ - _isSignInMethodCall(signInOption: 'SignInOption.games'), - isMethodCall('signInSilently', arguments: null), - ], - ); + _verifyInit(mockPlatform, signInOption: SignInOption.games); + verify(mockPlatform.signInSilently()); }); test('authentication', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); + when(mockPlatform.getTokens( + email: anyNamed('email'), + shouldRecoverAuth: anyNamed('shouldRecoverAuth'))) + .thenAnswer((Invocation _) async => GoogleSignInTokenData( + idToken: '123', + accessToken: '456', + serverAuthCode: '789', + )); + await googleSignIn.signIn(); - log.clear(); final GoogleSignInAccount user = googleSignIn.currentUser!; final GoogleSignInAuthentication auth = await user.authentication; expect(auth.accessToken, '456'); expect(auth.idToken, '123'); - expect( - log, - [ - isMethodCall('getTokens', arguments: { - 'email': 'john.doe@gmail.com', - 'shouldRecoverAuth': true, - }), - ], - ); + verify(mockPlatform.getTokens( + email: 'john.doe@gmail.com', shouldRecoverAuth: true)); }); test('requestScopes returns true once new scope is granted', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); + when(mockPlatform.requestScopes(any)) + .thenAnswer((Invocation _) async => true); + await googleSignIn.signIn(); final bool result = await googleSignIn.requestScopes(['testScope']); expect(result, isTrue); - expect( - log, - [ - _isSignInMethodCall(), - isMethodCall('signIn', arguments: null), - isMethodCall('requestScopes', arguments: { - 'scopes': ['testScope'], - }), - ], - ); - }); - }); - - group('GoogleSignIn with fake backend', () { - const FakeUser kUserData = FakeUser( - id: '8162538176523816253123', - displayName: 'John Doe', - email: 'john.doe@gmail.com', - photoUrl: 'https://lh5.googleusercontent.com/photo.jpg', - serverAuthCode: '789'); - - late GoogleSignIn googleSignIn; - - setUp(() { - final MethodChannelGoogleSignIn platformInstance = - GoogleSignInPlatform.instance as MethodChannelGoogleSignIn; - platformInstance.channel.setMockMethodCallHandler( - (FakeSignInBackend()..user = kUserData).handleMethodCall); - googleSignIn = GoogleSignIn(); + _verifyInit(mockPlatform); + verify(mockPlatform.signIn()); + verify(mockPlatform.requestScopes(['testScope'])); }); test('user starts as null', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); expect(googleSignIn.currentUser, isNull); }); test('can sign in and sign out', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); await googleSignIn.signIn(); final GoogleSignInAccount user = googleSignIn.currentUser!; - expect(user.displayName, equals(kUserData.displayName)); - expect(user.email, equals(kUserData.email)); - expect(user.id, equals(kUserData.id)); - expect(user.photoUrl, equals(kUserData.photoUrl)); - expect(user.serverAuthCode, equals(kUserData.serverAuthCode)); + expect(user.displayName, equals(kDefaultUser.displayName)); + expect(user.email, equals(kDefaultUser.email)); + expect(user.id, equals(kDefaultUser.id)); + expect(user.photoUrl, equals(kDefaultUser.photoUrl)); + expect(user.serverAuthCode, equals(kDefaultUser.serverAuthCode)); await googleSignIn.disconnect(); expect(googleSignIn.currentUser, isNull); }); test('disconnect when signout already succeeds', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); await googleSignIn.disconnect(); expect(googleSignIn.currentUser, isNull); }); }); } -Matcher _isSignInMethodCall({String signInOption = 'SignInOption.standard'}) { - return isMethodCall('init', arguments: { - 'signInOption': signInOption, - 'scopes': [], - 'hostedDomain': null, - 'clientId': null, - 'forceCodeForRefreshToken': false, - }); +void _verifyInit( + MockGoogleSignInPlatform mockSignIn, { + SignInOption signInOption = SignInOption.standard, + String? clientId, +}) { + verify(mockSignIn.init( + signInOption: signInOption, + scopes: [], + hostedDomain: null, + clientId: clientId, + )); } diff --git a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.mocks.dart b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.mocks.dart new file mode 100644 index 000000000000..4e669628391c --- /dev/null +++ b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.mocks.dart @@ -0,0 +1,100 @@ +// Mocks generated by Mockito 5.1.0 from annotations +// in google_sign_in/test/google_sign_in_test.dart. +// Do not manually edit this file. + +import 'dart:async' as _i4; + +import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart' + as _i3; +import 'package:google_sign_in_platform_interface/src/types.dart' as _i2; +import 'package:mockito/mockito.dart' as _i1; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types + +class _FakeGoogleSignInTokenData_0 extends _i1.Fake + implements _i2.GoogleSignInTokenData {} + +/// A class which mocks [GoogleSignInPlatform]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockGoogleSignInPlatform extends _i1.Mock + implements _i3.GoogleSignInPlatform { + MockGoogleSignInPlatform() { + _i1.throwOnMissingStub(this); + } + + @override + bool get isMock => + (super.noSuchMethod(Invocation.getter(#isMock), returnValue: false) + as bool); + @override + _i4.Future init( + {List? scopes = const [], + _i2.SignInOption? signInOption = _i2.SignInOption.standard, + String? hostedDomain, + String? clientId}) => + (super.noSuchMethod( + Invocation.method(#init, [], { + #scopes: scopes, + #signInOption: signInOption, + #hostedDomain: hostedDomain, + #clientId: clientId + }), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future initWithParams(_i2.SignInInitParameters? params) => + (super.noSuchMethod(Invocation.method(#initWithParams, [params]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future<_i2.GoogleSignInUserData?> signInSilently() => + (super.noSuchMethod(Invocation.method(#signInSilently, []), + returnValue: Future<_i2.GoogleSignInUserData?>.value()) + as _i4.Future<_i2.GoogleSignInUserData?>); + @override + _i4.Future<_i2.GoogleSignInUserData?> signIn() => + (super.noSuchMethod(Invocation.method(#signIn, []), + returnValue: Future<_i2.GoogleSignInUserData?>.value()) + as _i4.Future<_i2.GoogleSignInUserData?>); + @override + _i4.Future<_i2.GoogleSignInTokenData> getTokens( + {String? email, bool? shouldRecoverAuth}) => + (super.noSuchMethod( + Invocation.method(#getTokens, [], + {#email: email, #shouldRecoverAuth: shouldRecoverAuth}), + returnValue: Future<_i2.GoogleSignInTokenData>.value( + _FakeGoogleSignInTokenData_0())) + as _i4.Future<_i2.GoogleSignInTokenData>); + @override + _i4.Future signOut() => + (super.noSuchMethod(Invocation.method(#signOut, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future disconnect() => + (super.noSuchMethod(Invocation.method(#disconnect, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future isSignedIn() => + (super.noSuchMethod(Invocation.method(#isSignedIn, []), + returnValue: Future.value(false)) as _i4.Future); + @override + _i4.Future clearAuthCache({String? token}) => (super.noSuchMethod( + Invocation.method(#clearAuthCache, [], {#token: token}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future requestScopes(List? scopes) => + (super.noSuchMethod(Invocation.method(#requestScopes, [scopes]), + returnValue: Future.value(false)) as _i4.Future); +} From 885be9a9c28d57583c1c0cbb7ba7d1446b20e71f Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Thu, 12 May 2022 18:34:11 -0700 Subject: [PATCH 554/600] [ios_platform_images] Ignore ImageProvider.load deprecation (#5707) --- packages/ios_platform_images/CHANGELOG.md | 4 ++++ packages/ios_platform_images/lib/ios_platform_images.dart | 2 +- packages/ios_platform_images/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/ios_platform_images/CHANGELOG.md b/packages/ios_platform_images/CHANGELOG.md index d432bdb7ee8e..ee8e96132fea 100644 --- a/packages/ios_platform_images/CHANGELOG.md +++ b/packages/ios_platform_images/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.0+8 + +* Ignores the warning for the upcoming deprecation of `ImageProvider.load` in the correct line. + ## 0.2.0+7 * Ignores the warning for the upcoming deprecation of `ImageProvider.load`. diff --git a/packages/ios_platform_images/lib/ios_platform_images.dart b/packages/ios_platform_images/lib/ios_platform_images.dart index 6a85ea0d189b..1c5b4b817f40 100644 --- a/packages/ios_platform_images/lib/ios_platform_images.dart +++ b/packages/ios_platform_images/lib/ios_platform_images.dart @@ -62,10 +62,10 @@ class _FutureMemoryImage extends ImageProvider<_FutureMemoryImage> { return SynchronousFuture<_FutureMemoryImage>(this); } + // ignore:deprecated_member_use /// See [ImageProvider.load]. // TODO(jmagman): Implement the new API once it lands, https://github.com/flutter/flutter/issues/103556 @override - // ignore:deprecated_member_use ImageStreamCompleter load(_FutureMemoryImage key, DecoderCallback decode) { return _FutureImageStreamCompleter( codec: _loadAsync(key, decode), diff --git a/packages/ios_platform_images/pubspec.yaml b/packages/ios_platform_images/pubspec.yaml index ddc02e6235db..4ff67ee137b4 100644 --- a/packages/ios_platform_images/pubspec.yaml +++ b/packages/ios_platform_images/pubspec.yaml @@ -2,7 +2,7 @@ name: ios_platform_images description: A plugin to share images between Flutter and iOS in add-to-app setups. repository: https://github.com/flutter/plugins/tree/main/packages/ios_platform_images issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+ios_platform_images%22 -version: 0.2.0+7 +version: 0.2.0+8 environment: sdk: ">=2.14.0 <3.0.0" From a2a29355cc0d616280f8eb55211c595b16d0b51c Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 12 May 2022 22:19:08 -0400 Subject: [PATCH 555/600] Roll Flutter from a9ac7fb03be1 to 8bec125aaf72 (38 revisions) (#5704) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index e1efdd362301..ee33c592e2fb 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -a9ac7fb03be1d574d726e98b90743015716de7fd +8bec125aaf72667bf5840dca74b5916cb5dbe9ec From 900e212954d9691e0c9279d01e109de3626fdacb Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 13 May 2022 00:54:05 -0400 Subject: [PATCH 556/600] Roll Flutter from 8bec125aaf72 to 6bba577bf230 (17 revisions) (#5709) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index ee33c592e2fb..e4d9f0fecf0a 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -8bec125aaf72667bf5840dca74b5916cb5dbe9ec +6bba577bf230f5387ee01d7336ba755cb4c8bc9a From 7441ca729cfad84132b9a01cc34403cb111bc7f5 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 13 May 2022 04:29:07 -0400 Subject: [PATCH 557/600] Roll Flutter from 6bba577bf230 to b3d7a691f6f9 (1 revision) (#5710) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index e4d9f0fecf0a..85e95ee3914a 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -6bba577bf230f5387ee01d7336ba755cb4c8bc9a +b3d7a691f6f93da38919fdb358fa03f6b8bcd1b9 From 0620072e157f7bb717682f19a72e7672685931e4 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 13 May 2022 07:09:07 -0400 Subject: [PATCH 558/600] Roll Flutter from b3d7a691f6f9 to c13bc34717a6 (2 revisions) (#5715) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 85e95ee3914a..7e0b68684831 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b3d7a691f6f93da38919fdb358fa03f6b8bcd1b9 +c13bc34717a6595b86fecf590e44a7d27513340e From 1e71398a1cef2d90d40ff01b38a751c975385215 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 13 May 2022 08:24:06 -0400 Subject: [PATCH 559/600] [ci] Update the legacy analysis versions (#5699) --- .cirrus.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index c26118d1443f..eaaac8d7b5b5 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -157,11 +157,10 @@ task: depends_on: analyze env: matrix: - CHANNEL: "2.5.3" + CHANNEL: "2.10.5" CHANNEL: "2.8.1" analyze_script: - ./script/tool_runner.sh analyze --skip-if-not-supporting-flutter-version="$CHANNEL" --custom-analysis=script/configs/custom_analysis.yaml - - echo "If this test fails, the minumum Flutter version should be updated" - name: readme_excerpts env: CIRRUS_CLONE_SUBMODULES: true From 053c4bdb3812365a61e054c462c567aa75f3cf2b Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 13 May 2022 09:24:13 -0400 Subject: [PATCH 560/600] Roll Flutter from c13bc34717a6 to ac80477e8d85 (2 revisions) (#5717) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 7e0b68684831..16035180091a 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c13bc34717a6595b86fecf590e44a7d27513340e +ac80477e8d853f45d6075b7502917e8a5b4350a6 From debc272fb02a61832f92af717a6e0cb3ebce5c9a Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 13 May 2022 14:44:11 -0400 Subject: [PATCH 561/600] Roll Flutter from ac80477e8d85 to 2b2cda15293d (2 revisions) (#5719) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 16035180091a..6934eda08a73 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ac80477e8d853f45d6075b7502917e8a5b4350a6 +2b2cda15293d86d5c27d345505cde25b9efcb153 From 7dc808a3f4692073f948efd41f577c3cf8f2b7d3 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 13 May 2022 20:34:12 -0400 Subject: [PATCH 562/600] [tools] Fix `publish` flag calculation (#5694) --- script/tool/CHANGELOG.md | 1 + .../tool/lib/src/common/plugin_command.dart | 4 ++- .../tool/lib/src/publish_plugin_command.dart | 17 ++++++----- .../test/publish_plugin_command_test.dart | 29 +++++++++++++++++++ 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 9ed2a9278653..a8a8268c047f 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,6 +1,7 @@ ## NEXT - Fixes changelog validation when reverting to a `NEXT` state. +- Fixes multiplication of `--force` flag when publishing multiple packages. ## 0.8.5 diff --git a/script/tool/lib/src/common/plugin_command.dart b/script/tool/lib/src/common/plugin_command.dart index 0ec890368fb5..be9fb23e57a5 100644 --- a/script/tool/lib/src/common/plugin_command.dart +++ b/script/tool/lib/src/common/plugin_command.dart @@ -192,7 +192,9 @@ abstract class PluginCommand extends Command { /// Convenience accessor for List arguments. List getStringListArg(String key) { - return (argResults![key] as List?) ?? []; + // Clone the list so that if a caller modifies the result it won't change + // the actual arguments list for future queries. + return List.from(argResults![key] as List? ?? []); } /// If true, commands should log timing information that might be useful in diff --git a/script/tool/lib/src/publish_plugin_command.dart b/script/tool/lib/src/publish_plugin_command.dart index 05f0afd0c06f..7aa70bd4fd1c 100644 --- a/script/tool/lib/src/publish_plugin_command.dart +++ b/script/tool/lib/src/publish_plugin_command.dart @@ -121,6 +121,8 @@ class PublishPluginCommand extends PackageLoopingCommand { List _existingGitTags = []; // The remote to push tags to. late _RemoteInfo _remote; + // Flags to pass to `pub publish`. + late List _publishFlags; @override String get successSummaryMessage => 'published'; @@ -149,6 +151,11 @@ class PublishPluginCommand extends PackageLoopingCommand { _existingGitTags = (existingTagsResult.stdout as String).split('\n') ..removeWhere((String element) => element.isEmpty); + _publishFlags = [ + ...getStringListArg(_pubFlagsOption), + if (getBoolArg(_skipConfirmationFlag)) '--force', + ]; + if (getBoolArg(_dryRunFlag)) { print('=============== DRY RUN ==============='); } @@ -333,22 +340,18 @@ Safe to ignore if the package is deleted in this commit. Future _publish(RepositoryPackage package) async { print('Publishing...'); - final List publishFlags = getStringListArg(_pubFlagsOption); - print('Running `pub publish ${publishFlags.join(' ')}` in ' + print('Running `pub publish ${_publishFlags.join(' ')}` in ' '${package.directory.absolute.path}...\n'); if (getBoolArg(_dryRunFlag)) { return true; } - if (getBoolArg(_skipConfirmationFlag)) { - publishFlags.add('--force'); - } - if (publishFlags.contains('--force')) { + if (_publishFlags.contains('--force')) { _ensureValidPubCredential(); } final io.Process publish = await processRunner.start( - flutterCommand, ['pub', 'publish'] + publishFlags, + flutterCommand, ['pub', 'publish', ..._publishFlags], workingDirectory: package.directory); publish.stdout.transform(utf8.decoder).listen((String data) => print(data)); publish.stderr.transform(utf8.decoder).listen((String data) => print(data)); diff --git a/script/tool/test/publish_plugin_command_test.dart b/script/tool/test/publish_plugin_command_test.dart index d443f8ff0178..f3be3b48b1f1 100644 --- a/script/tool/test/publish_plugin_command_test.dart +++ b/script/tool/test/publish_plugin_command_test.dart @@ -224,6 +224,35 @@ void main() { plugin.path))); }); + test('--force is only added once, regardless of plugin count', () async { + _createMockCredentialFile(); + final RepositoryPackage plugin1 = + createFakePlugin('plugin_a', packagesDir, examples: []); + final RepositoryPackage plugin2 = + createFakePlugin('plugin_b', packagesDir, examples: []); + + await runCapturingPrint(commandRunner, [ + 'publish-plugin', + '--packages=plugin_a,plugin_b', + '--skip-confirmation', + '--pub-publish-flags', + '--server=bar' + ]); + + expect( + processRunner.recordedCalls, + containsAllInOrder([ + ProcessCall( + flutterCommand, + const ['pub', 'publish', '--server=bar', '--force'], + plugin1.path), + ProcessCall( + flutterCommand, + const ['pub', 'publish', '--server=bar', '--force'], + plugin2.path), + ])); + }); + test('throws if pub publish fails', () async { createFakePlugin('foo', packagesDir, examples: []); From 486071d7f9bb948e0d8cfeb0abceaf3d36e2374f Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Fri, 13 May 2022 18:24:19 -0700 Subject: [PATCH 563/600] [camera]handle iOS camera access permission (#5215) --- packages/camera/camera/CHANGELOG.md | 4 + packages/camera/camera/README.md | 25 ++++ .../ios/Runner.xcodeproj/project.pbxproj | 6 +- ...eraCaptureSessionQueueRaceConditionTests.m | 9 +- .../RunnerTests/CameraMethodChannelTests.m | 6 +- .../ios/RunnerTests/CameraPermissionTests.m | 123 ++++++++++++++++++ packages/camera/camera/example/lib/main.dart | 32 ++++- .../example/lib/readme_full_example.dart | 11 ++ .../ios/Classes/CameraPermissionUtils.h | 20 +++ .../ios/Classes/CameraPermissionUtils.m | 39 ++++++ .../camera/camera/ios/Classes/CameraPlugin.m | 63 +++++---- .../camera/ios/Classes/CameraPlugin.modulemap | 1 + .../camera/ios/Classes/CameraPlugin_Test.h | 6 + .../ios/Classes/FLTThreadSafeFlutterResult.h | 20 ++- .../ios/Classes/FLTThreadSafeFlutterResult.m | 4 + packages/camera/camera/pubspec.yaml | 2 +- 16 files changed, 330 insertions(+), 41 deletions(-) create mode 100644 packages/camera/camera/example/ios/RunnerTests/CameraPermissionTests.m create mode 100644 packages/camera/camera/ios/Classes/CameraPermissionUtils.h create mode 100644 packages/camera/camera/ios/Classes/CameraPermissionUtils.m diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index cde2ca284434..8d713c60c276 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.5 + +* Adds camera access permission handling logic on iOS to fix a related crash when using the camera for the first time. + ## 0.9.4+24 * Fixes preview orientation when pausing preview with locked orientation. diff --git a/packages/camera/camera/README.md b/packages/camera/camera/README.md index 0bcaeaeb3b7c..6b2ed7a6b687 100644 --- a/packages/camera/camera/README.md +++ b/packages/camera/camera/README.md @@ -80,6 +80,20 @@ void didChangeAppLifecycleState(AppLifecycleState state) { } ``` +### Handling camera access permissions + +Permission errors may be thrown when initializing the camera controller, and you are expected to handle them properly. + +Here is a list of all permission error codes that can be thrown: + +- `CameraAccessDenied`: Thrown when user denies the camera access permission. + +- `CameraAccessDeniedWithoutPrompt`: iOS only for now. Thrown when user has previously denied the permission. iOS does not allow prompting alert dialog a second time. Users will have to go to Settings > Privacy in order to enable camera access. + +- `CameraAccessRestricted`: iOS only for now. Thrown when camera access is restricted and users cannot grant permission (parental control). + +- `cameraPermission`: Android and Web only. A legacy error code for all kinds of camera permission errors. + ### Example Here is a small example flutter app displaying a full screen camera preview. @@ -119,6 +133,17 @@ class _CameraAppState extends State { return; } setState(() {}); + }).catchError((Object e) { + if (e is CameraException) { + switch (e.code) { + case 'CameraAccessDenied': + print('User denied camera access.'); + break; + default: + print('Handle other errors.'); + break; + } + } }); } diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj index 37f56d0ed52e..b5187d5dd1fa 100644 --- a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 46; objects = { /* Begin PBXBuildFile section */ @@ -26,6 +26,7 @@ E04F108627A87CA600573D0C /* FLTSavePhotoDelegateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */; }; E071CF7227B3061B006EF3BA /* FLTCamPhotoCaptureTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */; }; E071CF7427B31DE4006EF3BA /* FLTCamSampleBufferTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */; }; + E0B0D2BB27DFF2AF00E71E4B /* CameraPermissionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0B0D2BA27DFF2AF00E71E4B /* CameraPermissionTests.m */; }; E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */; }; E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */; }; E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */; }; @@ -91,6 +92,7 @@ E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTSavePhotoDelegateTests.m; sourceTree = ""; }; E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTCamPhotoCaptureTests.m; sourceTree = ""; }; E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTCamSampleBufferTests.m; sourceTree = ""; }; + E0B0D2BA27DFF2AF00E71E4B /* CameraPermissionTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPermissionTests.m; sourceTree = ""; }; E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeMethodChannelTests.m; sourceTree = ""; }; E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeTextureRegistryTests.m; sourceTree = ""; }; E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeEventChannelTests.m; sourceTree = ""; }; @@ -136,6 +138,7 @@ E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */, E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */, E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */, + E0B0D2BA27DFF2AF00E71E4B /* CameraPermissionTests.m */, E01EE4A72799F3A5008C1950 /* QueueUtilsTests.m */, E0CDBAC027CD9729002561D9 /* CameraTestUtils.h */, E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */, @@ -422,6 +425,7 @@ 788A065A27B0E02900533D74 /* StreamingTest.m in Sources */, E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */, E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */, + E0B0D2BB27DFF2AF00E71E4B /* CameraPermissionTests.m in Sources */, E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */, E01EE4A82799F3A5008C1950 /* QueueUtilsTests.m in Sources */, ); diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m b/packages/camera/camera/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m index 667a122d9375..e99ce4e89a94 100644 --- a/packages/camera/camera/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m +++ b/packages/camera/camera/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m @@ -29,10 +29,11 @@ - (void)testFixForCaptureSessionQueueNullPointerCrashDueToRaceCondition { result:^(id _Nullable result) { [disposeExpectation fulfill]; }]; - [camera handleMethodCall:createCall - result:^(id _Nullable result) { - [createExpectation fulfill]; - }]; + [camera createCameraOnSessionQueueWithCreateMethodCall:createCall + result:[[FLTThreadSafeFlutterResult alloc] + initWithResult:^(id _Nullable result) { + [createExpectation fulfill]; + }]]; [self waitForExpectationsWithTimeout:1 handler:nil]; // `captureSessionQueue` must not be nil after `create` call. Otherwise a nil // `captureSessionQueue` passed into `AVCaptureVideoDataOutput::setSampleBufferDelegate:queue:` diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraMethodChannelTests.m b/packages/camera/camera/example/ios/RunnerTests/CameraMethodChannelTests.m index 254a33c7ee4e..62b9cda2ef7b 100644 --- a/packages/camera/camera/example/ios/RunnerTests/CameraMethodChannelTests.m +++ b/packages/camera/camera/example/ios/RunnerTests/CameraMethodChannelTests.m @@ -17,8 +17,7 @@ @implementation CameraMethodChannelTests - (void)testCreate_ShouldCallResultOnMainThread { CameraPlugin *camera = [[CameraPlugin alloc] initWithRegistry:nil messenger:nil]; - XCTestExpectation *expectation = - [[XCTestExpectation alloc] initWithDescription:@"Result finished"]; + XCTestExpectation *expectation = [self expectationWithDescription:@"Result finished"]; // Set up mocks for initWithCameraName method id avCaptureDeviceInputMock = OCMClassMock([AVCaptureDeviceInput class]); @@ -37,7 +36,8 @@ - (void)testCreate_ShouldCallResultOnMainThread { methodCallWithMethodName:@"create" arguments:@{@"resolutionPreset" : @"medium", @"enableAudio" : @(1)}]; - [camera handleMethodCallAsync:call result:resultObject]; + [camera createCameraOnSessionQueueWithCreateMethodCall:call result:resultObject]; + [self waitForExpectationsWithTimeout:1 handler:nil]; // Verify the result NSDictionary *dictionaryResult = (NSDictionary *)resultObject.receivedResult; diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraPermissionTests.m b/packages/camera/camera/example/ios/RunnerTests/CameraPermissionTests.m new file mode 100644 index 000000000000..961b931b7704 --- /dev/null +++ b/packages/camera/camera/example/ios/RunnerTests/CameraPermissionTests.m @@ -0,0 +1,123 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import camera; +@import camera.Test; +@import AVFoundation; +@import XCTest; +#import +#import "CameraTestUtils.h" + +@interface CameraPermissionTests : XCTestCase + +@end + +@implementation CameraPermissionTests + +- (void)testRequestCameraPermission_completeWithoutErrorIfPrevoiuslyAuthorized { + XCTestExpectation *expectation = + [self expectationWithDescription: + @"Must copmlete without error if camera access was previously authorized."]; + + id mockDevice = OCMClassMock([AVCaptureDevice class]); + OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeVideo]) + .andReturn(AVAuthorizationStatusAuthorized); + + FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) { + if (error == nil) { + [expectation fulfill]; + } + }); + [self waitForExpectationsWithTimeout:1 handler:nil]; +} +- (void)testRequestCameraPermission_completeWithErrorIfPreviouslyDenied { + XCTestExpectation *expectation = + [self expectationWithDescription: + @"Must complete with error if camera access was previously denied."]; + FlutterError *expectedError = + [FlutterError errorWithCode:@"CameraAccessDeniedWithoutPrompt" + message:@"User has previously denied the camera access request. Go to " + @"Settings to enable camera access." + details:nil]; + + id mockDevice = OCMClassMock([AVCaptureDevice class]); + OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeVideo]) + .andReturn(AVAuthorizationStatusDenied); + FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) { + if ([error isEqual:expectedError]) { + [expectation fulfill]; + } + }); + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testRequestCameraPermission_completeWithErrorIfRestricted { + XCTestExpectation *expectation = + [self expectationWithDescription:@"Must complete with error if camera access is restricted."]; + FlutterError *expectedError = [FlutterError errorWithCode:@"CameraAccessRestricted" + message:@"Camera access is restricted. " + details:nil]; + + id mockDevice = OCMClassMock([AVCaptureDevice class]); + OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeVideo]) + .andReturn(AVAuthorizationStatusRestricted); + + FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) { + if ([error isEqual:expectedError]) { + [expectation fulfill]; + } + }); + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testRequestCameraPermission_completeWithoutErrorIfUserGrantAccess { + XCTestExpectation *grantedExpectation = [self + expectationWithDescription:@"Must complete without error if user choose to grant access"]; + + id mockDevice = OCMClassMock([AVCaptureDevice class]); + OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeVideo]) + .andReturn(AVAuthorizationStatusNotDetermined); + // Mimic user choosing "allow" in permission dialog. + OCMStub([mockDevice requestAccessForMediaType:AVMediaTypeVideo + completionHandler:[OCMArg checkWithBlock:^BOOL(void (^block)(BOOL)) { + block(YES); + return YES; + }]]); + + FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) { + if (error == nil) { + [grantedExpectation fulfill]; + } + }); + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testRequestCameraPermission_completeWithErrorIfUserDenyAccess { + XCTestExpectation *expectation = + [self expectationWithDescription:@"Must complete with error if user choose to deny access"]; + FlutterError *expectedError = + [FlutterError errorWithCode:@"CameraAccessDenied" + message:@"User denied the camera access request." + details:nil]; + + id mockDevice = OCMClassMock([AVCaptureDevice class]); + OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeVideo]) + .andReturn(AVAuthorizationStatusNotDetermined); + + // Mimic user choosing "deny" in permission dialog. + OCMStub([mockDevice requestAccessForMediaType:AVMediaTypeVideo + completionHandler:[OCMArg checkWithBlock:^BOOL(void (^block)(BOOL)) { + block(NO); + return YES; + }]]); + FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) { + if ([error isEqual:expectedError]) { + [expectation fulfill]; + } + }); + + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +@end diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index 10a8a6f75e16..34942ba5aa77 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -633,8 +633,15 @@ class _CameraExampleHomeState extends State } Future onNewCameraSelected(CameraDescription cameraDescription) async { - if (controller != null) { - await controller!.dispose(); + final CameraController? oldController = controller; + if (oldController != null) { + // `controller` needs to be set to null before getting disposed, + // to avoid a race condition when we use the controller that is being + // disposed. This happens when camera permission dialog shows up, + // which triggers `didChangeAppLifecycleState`, which disposes and + // re-creates the controller. + controller = null; + await oldController.dispose(); } final CameraController cameraController = CameraController( @@ -678,7 +685,26 @@ class _CameraExampleHomeState extends State .then((double value) => _minAvailableZoom = value), ]); } on CameraException catch (e) { - _showCameraException(e); + switch (e.code) { + case 'CameraAccessDenied': + showInSnackBar('You have denied camera access.'); + break; + case 'CameraAccessDeniedWithoutPrompt': + // iOS only + showInSnackBar('Please go to Settings app to enable camera access.'); + break; + case 'CameraAccessRestricted': + // iOS only + showInSnackBar('Camera access is restricted.'); + break; + case 'cameraPermission': + // Android & web only + showInSnackBar('Unknown permission error.'); + break; + default: + _showCameraException(e); + break; + } } if (mounted) { diff --git a/packages/camera/camera/example/lib/readme_full_example.dart b/packages/camera/camera/example/lib/readme_full_example.dart index a310fd9daeb0..a3c232ec44f7 100644 --- a/packages/camera/camera/example/lib/readme_full_example.dart +++ b/packages/camera/camera/example/lib/readme_full_example.dart @@ -36,6 +36,17 @@ class _CameraAppState extends State { return; } setState(() {}); + }).catchError((Object e) { + if (e is CameraException) { + switch (e.code) { + case 'CameraAccessDenied': + print('User denied camera access.'); + break; + default: + print('Handle other errors.'); + break; + } + } }); } diff --git a/packages/camera/camera/ios/Classes/CameraPermissionUtils.h b/packages/camera/camera/ios/Classes/CameraPermissionUtils.h new file mode 100644 index 000000000000..80f55db7be32 --- /dev/null +++ b/packages/camera/camera/ios/Classes/CameraPermissionUtils.h @@ -0,0 +1,20 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import Foundation; +#import + +typedef void (^FLTCameraPermissionRequestCompletionHandler)(FlutterError *); + +/// Requests camera access permission. +/// +/// If it is the first time requesting camera access, a permission dialog will show up on the +/// screen. Otherwise AVFoundation simply returns the user's previous choice, and in this case the +/// user will have to update the choice in Settings app. +/// +/// @param handler if access permission is (or was previously) granted, completion handler will be +/// called without error; Otherwise completion handler will be called with error. Handler can be +/// called on an arbitrary dispatch queue. +extern void FLTRequestCameraPermissionWithCompletionHandler( + FLTCameraPermissionRequestCompletionHandler handler); diff --git a/packages/camera/camera/ios/Classes/CameraPermissionUtils.m b/packages/camera/camera/ios/Classes/CameraPermissionUtils.m new file mode 100644 index 000000000000..6318338ea6a2 --- /dev/null +++ b/packages/camera/camera/ios/Classes/CameraPermissionUtils.m @@ -0,0 +1,39 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import AVFoundation; +#import "CameraPermissionUtils.h" + +void FLTRequestCameraPermissionWithCompletionHandler( + FLTCameraPermissionRequestCompletionHandler handler) { + switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]) { + case AVAuthorizationStatusAuthorized: + handler(nil); + break; + case AVAuthorizationStatusDenied: + handler([FlutterError errorWithCode:@"CameraAccessDeniedWithoutPrompt" + message:@"User has previously denied the camera access request. " + @"Go to Settings to enable camera access." + details:nil]); + break; + case AVAuthorizationStatusRestricted: + handler([FlutterError errorWithCode:@"CameraAccessRestricted" + message:@"Camera access is restricted. " + details:nil]); + break; + case AVAuthorizationStatusNotDetermined: { + [AVCaptureDevice + requestAccessForMediaType:AVMediaTypeVideo + completionHandler:^(BOOL granted) { + // handler can be invoked on an arbitrary dispatch queue. + handler(granted ? nil + : [FlutterError + errorWithCode:@"CameraAccessDenied" + message:@"User denied the camera access request." + details:nil]); + }]; + break; + } + } +} diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.m b/packages/camera/camera/ios/Classes/CameraPlugin.m index c0a3833dcd64..43d541e411b4 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin.m +++ b/packages/camera/camera/ios/Classes/CameraPlugin.m @@ -7,6 +7,7 @@ @import AVFoundation; +#import "CameraPermissionUtils.h" #import "CameraProperties.h" #import "FLTCam.h" #import "FLTThreadSafeEventChannel.h" @@ -131,31 +132,14 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call [result sendNotImplemented]; } } else if ([@"create" isEqualToString:call.method]) { - NSString *cameraName = call.arguments[@"cameraName"]; - NSString *resolutionPreset = call.arguments[@"resolutionPreset"]; - NSNumber *enableAudio = call.arguments[@"enableAudio"]; - NSError *error; - FLTCam *cam = [[FLTCam alloc] initWithCameraName:cameraName - resolutionPreset:resolutionPreset - enableAudio:[enableAudio boolValue] - orientation:[[UIDevice currentDevice] orientation] - captureSessionQueue:_captureSessionQueue - error:&error]; - - if (error) { - [result sendError:error]; - } else { - if (_camera) { - [_camera close]; + FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) { + // Create FLTCam only if granted camera access. + if (error) { + [result sendFlutterError:error]; + } else { + [self createCameraOnSessionQueueWithCreateMethodCall:call result:result]; } - _camera = cam; - [self.registry registerTexture:cam - completion:^(int64_t textureId) { - [result sendSuccessWithData:@{ - @"cameraId" : @(textureId), - }]; - }]; - } + }); } else if ([@"startImageStream" isEqualToString:call.method]) { [_camera startImageStreamWithMessenger:_messenger]; [result sendSuccess]; @@ -274,4 +258,35 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call } } +- (void)createCameraOnSessionQueueWithCreateMethodCall:(FlutterMethodCall *)createMethodCall + result:(FLTThreadSafeFlutterResult *)result { + dispatch_async(self.captureSessionQueue, ^{ + NSString *cameraName = createMethodCall.arguments[@"cameraName"]; + NSString *resolutionPreset = createMethodCall.arguments[@"resolutionPreset"]; + NSNumber *enableAudio = createMethodCall.arguments[@"enableAudio"]; + NSError *error; + FLTCam *cam = [[FLTCam alloc] initWithCameraName:cameraName + resolutionPreset:resolutionPreset + enableAudio:[enableAudio boolValue] + orientation:[[UIDevice currentDevice] orientation] + captureSessionQueue:self.captureSessionQueue + error:&error]; + + if (error) { + [result sendError:error]; + } else { + if (self.camera) { + [self.camera close]; + } + self.camera = cam; + [self.registry registerTexture:cam + completion:^(int64_t textureId) { + [result sendSuccessWithData:@{ + @"cameraId" : @(textureId), + }]; + }]; + } + }); +} + @end diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.modulemap b/packages/camera/camera/ios/Classes/CameraPlugin.modulemap index a23848aaccfc..897302799497 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin.modulemap +++ b/packages/camera/camera/ios/Classes/CameraPlugin.modulemap @@ -6,6 +6,7 @@ framework module camera { explicit module Test { header "CameraPlugin_Test.h" + header "CameraPermissionUtils.h" header "CameraProperties.h" header "FLTCam.h" header "FLTCam_Test.h" diff --git a/packages/camera/camera/ios/Classes/CameraPlugin_Test.h b/packages/camera/camera/ios/Classes/CameraPlugin_Test.h index 826b05043f78..d1903e0829b4 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin_Test.h +++ b/packages/camera/camera/ios/Classes/CameraPlugin_Test.h @@ -38,4 +38,10 @@ /// that triggered the orientation change. - (void)orientationChanged:(NSNotification *)notification; +/// Creates FLTCam on session queue and reports the creation result. +/// @param createMethodCall the create method call +/// @param result a thread safe flutter result wrapper object to report creation result. +- (void)createCameraOnSessionQueueWithCreateMethodCall:(FlutterMethodCall *)createMethodCall + result:(FLTThreadSafeFlutterResult *)result; + @end diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.h b/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.h index 70c9f868eda9..6677505671a3 100644 --- a/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.h +++ b/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.h @@ -4,6 +4,8 @@ #import +NS_ASSUME_NONNULL_BEGIN + /** * A thread safe wrapper for FlutterResult that can be called from any thread, by dispatching its * underlying engine calls to the main thread. @@ -13,13 +15,13 @@ /** * Gets the original FlutterResult object wrapped by this FLTThreadSafeFlutterResult instance. */ -@property(readonly, nonatomic, nonnull) FlutterResult flutterResult; +@property(readonly, nonatomic) FlutterResult flutterResult; /** * Initializes with a FlutterResult object. * @param result The FlutterResult object that the result will be given to. */ -- (nonnull instancetype)initWithResult:(nonnull FlutterResult)result; +- (instancetype)initWithResult:(FlutterResult)result; /** * Sends a successful result on the main thread without any data. @@ -30,18 +32,24 @@ * Sends a successful result on the main thread with data. * @param data Result data that is send to the Flutter Dart side. */ -- (void)sendSuccessWithData:(nonnull id)data; +- (void)sendSuccessWithData:(id)data; /** * Sends an NSError as result on the main thread. * @param error Error that will be send as FlutterError. */ -- (void)sendError:(nonnull NSError *)error; +- (void)sendError:(NSError *)error; + +/** + * Sends a FlutterError as result on the main thread. + * @param flutterError FlutterError that will be sent to the Flutter Dart side. + */ +- (void)sendFlutterError:(FlutterError *)flutterError; /** * Sends a FlutterError as result on the main thread. */ -- (void)sendErrorWithCode:(nonnull NSString *)code +- (void)sendErrorWithCode:(NSString *)code message:(nullable NSString *)message details:(nullable id)details; @@ -50,3 +58,5 @@ */ - (void)sendNotImplemented; @end + +NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.m b/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.m index 58c2e788cdc0..ad125f7f32ed 100644 --- a/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.m +++ b/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.m @@ -39,6 +39,10 @@ - (void)sendErrorWithCode:(NSString *)code [self send:flutterError]; } +- (void)sendFlutterError:(FlutterError *)flutterError { + [self send:flutterError]; +} + - (void)sendNotImplemented { [self send:FlutterMethodNotImplemented]; } diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index d763843d0572..59cde43dd66a 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+24 +version: 0.9.5 environment: sdk: ">=2.14.0 <3.0.0" From 4b1d47eebbeb976c5dd4db1a95913cf608a53373 Mon Sep 17 00:00:00 2001 From: Huey Zhang Date: Tue, 17 May 2022 01:24:12 +0800 Subject: [PATCH 564/600] [image_picker] Fix 'messages.g.h' file not found (#5635) --- packages/image_picker/image_picker_ios/CHANGELOG.md | 4 ++++ .../image_picker_ios/ios/Classes/FLTImagePickerPlugin_Test.h | 2 +- packages/image_picker/image_picker_ios/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index 96b1c7f0d0a4..e39a314ca49a 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.5+3 + +* Fixes 'messages.g.h' file not found. + ## 0.8.5+2 * Minor fixes for new analysis options. diff --git a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin_Test.h b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin_Test.h index 2c4167746c8e..64c20452987d 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin_Test.h +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin_Test.h @@ -6,7 +6,7 @@ #import -#import +#import "messages.g.h" NS_ASSUME_NONNULL_BEGIN diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index d1de0a14ea69..edb884a377db 100755 --- a/packages/image_picker/image_picker_ios/pubspec.yaml +++ b/packages/image_picker/image_picker_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_ios description: iOS implementation of the video_picker plugin. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.5+2 +version: 0.8.5+3 environment: sdk: ">=2.14.0 <3.0.0" From 49a0d369ac99ffdea5cba63979ff20ca528c046a Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 16 May 2022 19:02:11 -0400 Subject: [PATCH 565/600] [ci] Manually roll Flutter master (#5765) --- .ci/flutter_master.version | 2 +- packages/camera/camera/CHANGELOG.md | 4 +++ .../camera/ios/Classes/FLTSavePhotoDelegate.m | 4 +++ packages/camera/camera/pubspec.yaml | 2 +- .../image_picker_ios/CHANGELOG.md | 4 +++ .../Classes/FLTImagePickerPhotoAssetUtil.m | 3 ++ .../image_picker_ios/pubspec.yaml | 2 +- .../local_auth/local_auth_ios/CHANGELOG.md | 6 +++- .../ios/Classes/FLTLocalAuthPlugin.m | 29 ++++++++++++++++++- .../local_auth/local_auth_ios/pubspec.yaml | 2 +- 10 files changed, 52 insertions(+), 6 deletions(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 6934eda08a73..7bede728c582 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2b2cda15293d86d5c27d345505cde25b9efcb153 +036cae36697a6c078fe28ac6088a2a493d0e7958 diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 8d713c60c276..d101f60cf041 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.5+1 + +* Suppresses warnings for pre-iOS-11 codepaths. + ## 0.9.5 * Adds camera access permission handling logic on iOS to fix a related crash when using the camera for the first time. diff --git a/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.m b/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.m index ced3cb5e407f..1df1708c54e8 100644 --- a/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.m +++ b/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.m @@ -42,6 +42,9 @@ - (void)handlePhotoCaptureResultWithError:(NSError *)error }); } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#pragma clang diagnostic ignored "-Wdeprecated-implementations" - (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhotoSampleBuffer:(CMSampleBufferRef)photoSampleBuffer previewPhotoSampleBuffer:(CMSampleBufferRef)previewPhotoSampleBuffer @@ -56,6 +59,7 @@ - (void)captureOutput:(AVCapturePhotoOutput *)output previewPhotoSampleBuffer]; }]; } +#pragma clang diagnostic pop - (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhoto:(AVCapturePhoto *)photo diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 59cde43dd66a..14acf32e2324 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.5 +version: 0.9.5+1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index e39a314ca49a..e994fcc50c8e 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.5+4 + +* Suppresses warnings for pre-iOS-11 codepaths. + ## 0.8.5+3 * Fixes 'messages.g.h' file not found. diff --git a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPhotoAssetUtil.m b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPhotoAssetUtil.m index 4c705fe54350..37a1a9897cd3 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPhotoAssetUtil.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPhotoAssetUtil.m @@ -14,6 +14,8 @@ + (PHAsset *)getAssetFromImagePickerInfo:(NSDictionary *)info { if (@available(iOS 11, *)) { return [info objectForKey:UIImagePickerControllerPHAsset]; } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" NSURL *referenceURL = [info objectForKey:UIImagePickerControllerReferenceURL]; if (!referenceURL) { return nil; @@ -21,6 +23,7 @@ + (PHAsset *)getAssetFromImagePickerInfo:(NSDictionary *)info { PHFetchResult *result = [PHAsset fetchAssetsWithALAssetURLs:@[ referenceURL ] options:nil]; return result.firstObject; +#pragma clang diagnostic pop } + (PHAsset *)getAssetFromPHPickerResult:(PHPickerResult *)result API_AVAILABLE(ios(14)) { diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index edb884a377db..88f4d3352228 100755 --- a/packages/image_picker/image_picker_ios/pubspec.yaml +++ b/packages/image_picker/image_picker_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_ios description: iOS implementation of the video_picker plugin. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.5+3 +version: 0.8.5+4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_ios/CHANGELOG.md b/packages/local_auth/local_auth_ios/CHANGELOG.md index 2237cbe216f0..d44836788ce3 100644 --- a/packages/local_auth/local_auth_ios/CHANGELOG.md +++ b/packages/local_auth/local_auth_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.6 + +* Suppresses warnings for pre-iOS-11 codepaths. + ## 1.0.5 * Removes unnecessary imports. @@ -6,7 +10,7 @@ ## 1.0.4 -* Fixes `deviceSupportsBiometrics` to return true when biometric hardware +* Fixes `deviceSupportsBiometrics` to return true when biometric hardware is available but not enrolled. ## 1.0.3 diff --git a/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m b/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m index eb7f637f7850..8f61fecfd814 100644 --- a/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m +++ b/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m @@ -84,7 +84,16 @@ - (void)alertMessage:(NSString *)message handler:^(UIAlertAction *action) { if (UIApplicationOpenSettingsURLString != NULL) { NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; - [[UIApplication sharedApplication] openURL:url]; + if (@available(iOS 10, *)) { + [[UIApplication sharedApplication] openURL:url + options:@{} + completionHandler:NULL]; + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [[UIApplication sharedApplication] openURL:url]; +#pragma clang diagnostic pop + } result(@NO); } }]; @@ -113,9 +122,12 @@ - (void)deviceSupportsBiometrics:(FlutterResult)result { result(@YES); return; } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" } else if (authError.code == LAErrorTouchIDNotEnrolled) { result(@YES); return; +#pragma clang diagnostic pop } } @@ -205,9 +217,14 @@ - (void)handleAuthReplyWithSuccess:(BOOL)success } else { switch (error.code) { case LAErrorPasscodeNotSet: +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + // TODO(stuartmorgan): Remove the pragma and s/TouchID/Biometry/ in these constants when + // iOS 10 support is dropped. The values are the same, only the names have changed. case LAErrorTouchIDNotAvailable: case LAErrorTouchIDNotEnrolled: case LAErrorTouchIDLockout: +#pragma clang diagnostic pop case LAErrorUserFallback: [self handleErrors:error flutterArguments:arguments withFlutterResult:result]; return; @@ -228,7 +245,12 @@ - (void)handleErrors:(NSError *)authError NSString *errorCode = @"NotAvailable"; switch (authError.code) { case LAErrorPasscodeNotSet: +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + // TODO(stuartmorgan): Remove the pragma and s/TouchID/Biometry/ in this constant when + // iOS 10 support is dropped. The values are the same, only the names have changed. case LAErrorTouchIDNotEnrolled: +#pragma clang diagnostic pop if ([arguments[@"useErrorDialogs"] boolValue]) { [self alertMessage:arguments[@"goToSettingDescriptionIOS"] firstButton:arguments[@"okButton"] @@ -238,7 +260,12 @@ - (void)handleErrors:(NSError *)authError } errorCode = authError.code == LAErrorPasscodeNotSet ? @"PasscodeNotSet" : @"NotEnrolled"; break; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + // TODO(stuartmorgan): Remove the pragma and s/TouchID/Biometry/ in this constant when + // iOS 10 support is dropped. The values are the same, only the names have changed. case LAErrorTouchIDLockout: +#pragma clang diagnostic pop [self alertMessage:arguments[@"lockOut"] firstButton:arguments[@"okButton"] flutterResult:result diff --git a/packages/local_auth/local_auth_ios/pubspec.yaml b/packages/local_auth/local_auth_ios/pubspec.yaml index dded42b673f4..f491dfb49679 100644 --- a/packages/local_auth/local_auth_ios/pubspec.yaml +++ b/packages/local_auth/local_auth_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_ios description: iOS implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.5 +version: 1.0.6 environment: sdk: ">=2.14.0 <3.0.0" From 46e8a3d73dbc33fc22032299076650a2b45be0a0 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 16 May 2022 22:17:12 -0400 Subject: [PATCH 566/600] Roll Flutter from 036cae36697a to bf7a32628eef (49 revisions) (#5768) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 7bede728c582..bc6d07c1c54d 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -036cae36697a6c078fe28ac6088a2a493d0e7958 +bf7a32628eef08649ded09376424d2e78cce0efb From 1d51057f35faf91b161e905681f3b3a17c345dbc Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 16 May 2022 23:22:13 -0400 Subject: [PATCH 567/600] Roll Flutter from bf7a32628eef to bb9bbc601345 (1 revision) (#5769) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index bc6d07c1c54d..0151ec44ba18 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -bf7a32628eef08649ded09376424d2e78cce0efb +bb9bbc601345447db5d46e3bd97d2dcf4e6bfbd7 From 54cdccb24327e5258e7c00ac1cbd13b0dc5d7124 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 17 May 2022 00:27:11 -0400 Subject: [PATCH 568/600] Roll Flutter from bb9bbc601345 to fd312f1ccff9 (1 revision) (#5770) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 0151ec44ba18..ae8e0b5b1d80 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -bb9bbc601345447db5d46e3bd97d2dcf4e6bfbd7 +fd312f1ccff909fde28d2247a489bf210bbc6c48 From 8ea876cd0fcfdd6a5d30938eee4d2b651fb90921 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 17 May 2022 03:02:07 -0400 Subject: [PATCH 569/600] Roll Flutter from fd312f1ccff9 to c248854d176b (1 revision) (#5771) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index ae8e0b5b1d80..a0755e305782 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -fd312f1ccff909fde28d2247a489bf210bbc6c48 +c248854d176b92ac0d50a4ad0edc19bd12210400 From a692f840ce727e4a1f9b6936c9d0219330af5742 Mon Sep 17 00:00:00 2001 From: ChineseDragon Date: Tue, 17 May 2022 22:32:11 +0800 Subject: [PATCH 570/600] [in_app_purchase] fixed a memory leak error (#5358) --- .../in_app_purchase/in_app_purchase_storekit/CHANGELOG.md | 4 ++++ .../ios/Classes/InAppPurchasePlugin.m | 7 +------ .../in_app_purchase/in_app_purchase_storekit/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md index aba1d6ed3555..365c7133864b 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.0+8 + +* Fixes a memory leak on iOS. + ## 0.3.0+7 * Minor fixes for new analysis options. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/InAppPurchasePlugin.m b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/InAppPurchasePlugin.m index a580a46b011d..d64c24563b62 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/InAppPurchasePlugin.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/InAppPurchasePlugin.m @@ -25,9 +25,6 @@ @interface InAppPurchasePlugin () // Callback channel to dart used for when a function from the payment queue delegate is triggered. @property(strong, nonatomic, readonly) FlutterMethodChannel *paymentQueueDelegateCallbackChannel; - -@property(strong, nonatomic, readonly) NSObject *registry; -@property(strong, nonatomic, readonly) NSObject *messenger; @property(strong, nonatomic, readonly) NSObject *registrar; @property(strong, nonatomic, readonly) FIAPReceiptManager *receiptManager; @@ -57,8 +54,6 @@ - (instancetype)initWithReceiptManager:(FIAPReceiptManager *)receiptManager { - (instancetype)initWithRegistrar:(NSObject *)registrar { self = [self initWithReceiptManager:[FIAPReceiptManager new]]; _registrar = registrar; - _registry = [registrar textures]; - _messenger = [registrar messenger]; __weak typeof(self) weakSelf = self; _paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:[SKPaymentQueue defaultQueue] @@ -347,7 +342,7 @@ - (void)registerPaymentQueueDelegate:(FlutterResult)result { if (@available(iOS 13.0, *)) { _paymentQueueDelegateCallbackChannel = [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/in_app_purchase_payment_queue_delegate" - binaryMessenger:_messenger]; + binaryMessenger:[_registrar messenger]]; _paymentQueueDelegate = [[FIAPPaymentQueueDelegate alloc] initWithMethodChannel:_paymentQueueDelegateCallbackChannel]; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml index 235d491fbff0..ebd5e55acdad 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_storekit description: An implementation for the iOS platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase_storekit issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.3.0+7 +version: 0.3.0+8 environment: sdk: ">=2.14.0 <3.0.0" From 550ba3cd8af9377131bf7c451fe510b6d085be7c Mon Sep 17 00:00:00 2001 From: Alexandre Zollinger Chohfi Date: Tue, 17 May 2022 08:17:11 -0700 Subject: [PATCH 571/600] [local_auth] Windows support. (#4806) --- .../local_auth/local_auth_windows/AUTHORS | 7 + .../local_auth_windows/CHANGELOG.md | 3 + .../local_auth/local_auth_windows/LICENSE | 25 ++ .../local_auth/local_auth_windows/README.md | 11 + .../local_auth_windows/example/.gitignore | 46 ++++ .../local_auth_windows/example/.metadata | 10 + .../local_auth_windows/example/README.md | 3 + .../integration_test/local_auth_test.dart | 19 ++ .../local_auth_windows/example/lib/main.dart | 241 +++++++++++++++++ .../local_auth_windows/example/pubspec.yaml | 28 ++ .../example/test_driver/integration_test.dart | 7 + .../example/windows/.gitignore | 17 ++ .../example/windows/CMakeLists.txt | 100 +++++++ .../example/windows/flutter/CMakeLists.txt | 103 +++++++ .../windows/flutter/generated_plugins.cmake | 24 ++ .../example/windows/runner/CMakeLists.txt | 17 ++ .../example/windows/runner/Runner.rc | 121 +++++++++ .../example/windows/runner/flutter_window.cpp | 65 +++++ .../example/windows/runner/flutter_window.h | 37 +++ .../example/windows/runner/main.cpp | 46 ++++ .../example/windows/runner/resource.h | 16 ++ .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../windows/runner/runner.exe.manifest | 20 ++ .../example/windows/runner/utils.cpp | 67 +++++ .../example/windows/runner/utils.h | 23 ++ .../example/windows/runner/win32_window.cpp | 241 +++++++++++++++++ .../example/windows/runner/win32_window.h | 99 +++++++ .../lib/local_auth_windows.dart | 82 ++++++ .../lib/types/auth_messages_windows.dart | 22 ++ .../local_auth_windows/pubspec.yaml | 26 ++ .../test/local_auth_test.dart | 79 ++++++ .../local_auth_windows/windows/CMakeLists.txt | 120 +++++++++ .../local_auth_windows/local_auth_plugin.h | 26 ++ .../local_auth_windows/windows/local_auth.h | 89 ++++++ .../windows/local_auth_plugin.cpp | 236 ++++++++++++++++ .../windows/local_auth_windows.cpp | 15 ++ .../windows/test/local_auth_plugin_test.cpp | 253 ++++++++++++++++++ .../local_auth_windows/windows/test/mocks.h | 63 +++++ 38 files changed, 2407 insertions(+) create mode 100644 packages/local_auth/local_auth_windows/AUTHORS create mode 100644 packages/local_auth/local_auth_windows/CHANGELOG.md create mode 100644 packages/local_auth/local_auth_windows/LICENSE create mode 100644 packages/local_auth/local_auth_windows/README.md create mode 100644 packages/local_auth/local_auth_windows/example/.gitignore create mode 100644 packages/local_auth/local_auth_windows/example/.metadata create mode 100644 packages/local_auth/local_auth_windows/example/README.md create mode 100644 packages/local_auth/local_auth_windows/example/integration_test/local_auth_test.dart create mode 100644 packages/local_auth/local_auth_windows/example/lib/main.dart create mode 100644 packages/local_auth/local_auth_windows/example/pubspec.yaml create mode 100644 packages/local_auth/local_auth_windows/example/test_driver/integration_test.dart create mode 100644 packages/local_auth/local_auth_windows/example/windows/.gitignore create mode 100644 packages/local_auth/local_auth_windows/example/windows/CMakeLists.txt create mode 100644 packages/local_auth/local_auth_windows/example/windows/flutter/CMakeLists.txt create mode 100644 packages/local_auth/local_auth_windows/example/windows/flutter/generated_plugins.cmake create mode 100644 packages/local_auth/local_auth_windows/example/windows/runner/CMakeLists.txt create mode 100644 packages/local_auth/local_auth_windows/example/windows/runner/Runner.rc create mode 100644 packages/local_auth/local_auth_windows/example/windows/runner/flutter_window.cpp create mode 100644 packages/local_auth/local_auth_windows/example/windows/runner/flutter_window.h create mode 100644 packages/local_auth/local_auth_windows/example/windows/runner/main.cpp create mode 100644 packages/local_auth/local_auth_windows/example/windows/runner/resource.h create mode 100644 packages/local_auth/local_auth_windows/example/windows/runner/resources/app_icon.ico create mode 100644 packages/local_auth/local_auth_windows/example/windows/runner/runner.exe.manifest create mode 100644 packages/local_auth/local_auth_windows/example/windows/runner/utils.cpp create mode 100644 packages/local_auth/local_auth_windows/example/windows/runner/utils.h create mode 100644 packages/local_auth/local_auth_windows/example/windows/runner/win32_window.cpp create mode 100644 packages/local_auth/local_auth_windows/example/windows/runner/win32_window.h create mode 100644 packages/local_auth/local_auth_windows/lib/local_auth_windows.dart create mode 100644 packages/local_auth/local_auth_windows/lib/types/auth_messages_windows.dart create mode 100644 packages/local_auth/local_auth_windows/pubspec.yaml create mode 100644 packages/local_auth/local_auth_windows/test/local_auth_test.dart create mode 100644 packages/local_auth/local_auth_windows/windows/CMakeLists.txt create mode 100644 packages/local_auth/local_auth_windows/windows/include/local_auth_windows/local_auth_plugin.h create mode 100644 packages/local_auth/local_auth_windows/windows/local_auth.h create mode 100644 packages/local_auth/local_auth_windows/windows/local_auth_plugin.cpp create mode 100644 packages/local_auth/local_auth_windows/windows/local_auth_windows.cpp create mode 100644 packages/local_auth/local_auth_windows/windows/test/local_auth_plugin_test.cpp create mode 100644 packages/local_auth/local_auth_windows/windows/test/mocks.h diff --git a/packages/local_auth/local_auth_windows/AUTHORS b/packages/local_auth/local_auth_windows/AUTHORS new file mode 100644 index 000000000000..5db3d584e6bc --- /dev/null +++ b/packages/local_auth/local_auth_windows/AUTHORS @@ -0,0 +1,7 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. +Alexandre Zollinger Chohfi \ No newline at end of file diff --git a/packages/local_auth/local_auth_windows/CHANGELOG.md b/packages/local_auth/local_auth_windows/CHANGELOG.md new file mode 100644 index 000000000000..7cf171f305de --- /dev/null +++ b/packages/local_auth/local_auth_windows/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +* Initial release of Windows support. diff --git a/packages/local_auth/local_auth_windows/LICENSE b/packages/local_auth/local_auth_windows/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/local_auth/local_auth_windows/LICENSE @@ -0,0 +1,25 @@ +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/local_auth/local_auth_windows/README.md b/packages/local_auth/local_auth_windows/README.md new file mode 100644 index 000000000000..0c2984f40003 --- /dev/null +++ b/packages/local_auth/local_auth_windows/README.md @@ -0,0 +1,11 @@ +# local\_auth\_windows + +The Windows implementation of [`local_auth`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use `local_auth` +normally. This package will be automatically included in your app when you do. + +[1]: https://pub.dev/packages/local_auth +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin \ No newline at end of file diff --git a/packages/local_auth/local_auth_windows/example/.gitignore b/packages/local_auth/local_auth_windows/example/.gitignore new file mode 100644 index 000000000000..0fa6b675c0a5 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/.gitignore @@ -0,0 +1,46 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/packages/local_auth/local_auth_windows/example/.metadata b/packages/local_auth/local_auth_windows/example/.metadata new file mode 100644 index 000000000000..166a9984ca13 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: c860cba910319332564e1e9d470a17074c1f2dfd + channel: stable + +project_type: app diff --git a/packages/local_auth/local_auth_windows/example/README.md b/packages/local_auth/local_auth_windows/example/README.md new file mode 100644 index 000000000000..8f48b8563cad --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/README.md @@ -0,0 +1,3 @@ +# local_auth_example + +Demonstrates how to use the local_auth plugin. \ No newline at end of file diff --git a/packages/local_auth/local_auth_windows/example/integration_test/local_auth_test.dart b/packages/local_auth/local_auth_windows/example/integration_test/local_auth_test.dart new file mode 100644 index 000000000000..cedaaf28ff24 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/integration_test/local_auth_test.dart @@ -0,0 +1,19 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + +import 'package:local_auth_windows/local_auth_windows.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('canCheckBiometrics', (WidgetTester tester) async { + expect( + LocalAuthWindows().getEnrolledBiometrics(), + completion(isList), + ); + }); +} diff --git a/packages/local_auth/local_auth_windows/example/lib/main.dart b/packages/local_auth/local_auth_windows/example/lib/main.dart new file mode 100644 index 000000000000..ef26ec5545c5 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/lib/main.dart @@ -0,0 +1,241 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; +import 'package:local_auth_windows/local_auth_windows.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + _SupportState _supportState = _SupportState.unknown; + bool? _deviceSupportsBiometrics; + List? _enrolledBiometrics; + String _authorized = 'Not Authorized'; + bool _isAuthenticating = false; + + @override + void initState() { + super.initState(); + LocalAuthPlatform.instance.isDeviceSupported().then( + (bool isSupported) => setState(() => _supportState = isSupported + ? _SupportState.supported + : _SupportState.unsupported), + ); + } + + Future _checkBiometrics() async { + late bool deviceSupportsBiometrics; + try { + deviceSupportsBiometrics = + await LocalAuthPlatform.instance.deviceSupportsBiometrics(); + } on PlatformException catch (e) { + deviceSupportsBiometrics = false; + print(e); + } + if (!mounted) { + return; + } + + setState(() { + _deviceSupportsBiometrics = deviceSupportsBiometrics; + }); + } + + Future _getEnrolledBiometrics() async { + late List availableBiometrics; + try { + availableBiometrics = + await LocalAuthPlatform.instance.getEnrolledBiometrics(); + } on PlatformException catch (e) { + availableBiometrics = []; + print(e); + } + if (!mounted) { + return; + } + + setState(() { + _enrolledBiometrics = availableBiometrics; + }); + } + + Future _authenticate() async { + bool authenticated = false; + try { + setState(() { + _isAuthenticating = true; + _authorized = 'Authenticating'; + }); + authenticated = await LocalAuthPlatform.instance.authenticate( + localizedReason: 'Let OS determine authentication method', + authMessages: [const WindowsAuthMessages()], + options: const AuthenticationOptions( + useErrorDialogs: true, + stickyAuth: true, + ), + ); + setState(() { + _isAuthenticating = false; + }); + } on PlatformException catch (e) { + print(e); + setState(() { + _isAuthenticating = false; + _authorized = 'Error - ${e.message}'; + }); + return; + } + if (!mounted) { + return; + } + + setState( + () => _authorized = authenticated ? 'Authorized' : 'Not Authorized'); + } + + Future _authenticateWithBiometrics() async { + bool authenticated = false; + try { + setState(() { + _isAuthenticating = true; + _authorized = 'Authenticating'; + }); + authenticated = await LocalAuthPlatform.instance.authenticate( + localizedReason: + 'Scan your fingerprint (or face or whatever) to authenticate', + authMessages: [const WindowsAuthMessages()], + options: const AuthenticationOptions( + useErrorDialogs: true, + stickyAuth: true, + biometricOnly: true, + ), + ); + setState(() { + _isAuthenticating = false; + _authorized = 'Authenticating'; + }); + } on PlatformException catch (e) { + print(e); + setState(() { + _isAuthenticating = false; + _authorized = 'Error - ${e.message}'; + }); + return; + } + if (!mounted) { + return; + } + + final String message = authenticated ? 'Authorized' : 'Not Authorized'; + setState(() { + _authorized = message; + }); + } + + Future _cancelAuthentication() async { + await LocalAuthPlatform.instance.stopAuthentication(); + setState(() => _isAuthenticating = false); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Plugin example app'), + ), + body: ListView( + padding: const EdgeInsets.only(top: 30), + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (_supportState == _SupportState.unknown) + const CircularProgressIndicator() + else if (_supportState == _SupportState.supported) + const Text('This device is supported') + else + const Text('This device is not supported'), + const Divider(height: 100), + Text( + 'Device supports biometrics: $_deviceSupportsBiometrics\n'), + ElevatedButton( + onPressed: _checkBiometrics, + child: const Text('Check biometrics'), + ), + const Divider(height: 100), + Text('Enrolled biometrics: $_enrolledBiometrics\n'), + ElevatedButton( + onPressed: _getEnrolledBiometrics, + child: const Text('Get enrolled biometrics'), + ), + const Divider(height: 100), + Text('Current State: $_authorized\n'), + if (_isAuthenticating) + ElevatedButton( + onPressed: _cancelAuthentication, + child: Row( + mainAxisSize: MainAxisSize.min, + children: const [ + Text('Cancel Authentication'), + Icon(Icons.cancel), + ], + ), + ) + else + Column( + children: [ + ElevatedButton( + onPressed: _authenticate, + child: Row( + mainAxisSize: MainAxisSize.min, + children: const [ + Text('Authenticate'), + Icon(Icons.perm_device_information), + ], + ), + ), + ElevatedButton( + onPressed: _authenticateWithBiometrics, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text(_isAuthenticating + ? 'Cancel' + : 'Authenticate: biometrics only'), + const Icon(Icons.fingerprint), + ], + ), + ), + ], + ), + ], + ), + ], + ), + ), + ); + } +} + +enum _SupportState { + unknown, + supported, + unsupported, +} diff --git a/packages/local_auth/local_auth_windows/example/pubspec.yaml b/packages/local_auth/local_auth_windows/example/pubspec.yaml new file mode 100644 index 000000000000..266c9fc7140d --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/pubspec.yaml @@ -0,0 +1,28 @@ +name: local_auth_windows_example +description: Demonstrates how to use the local_auth_windows plugin. +publish_to: none + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.8.0" + +dependencies: + flutter: + sdk: flutter + local_auth_platform_interface: ^1.0.0 + local_auth_windows: + # When depending on this package from a real application you should use: + # local_auth_windows: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + +dev_dependencies: + flutter_driver: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/local_auth/local_auth_windows/example/test_driver/integration_test.dart b/packages/local_auth/local_auth_windows/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4f10f2a522f3 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/test_driver/integration_test.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); diff --git a/packages/local_auth/local_auth_windows/example/windows/.gitignore b/packages/local_auth/local_auth_windows/example/windows/.gitignore new file mode 100644 index 000000000000..d492d0d98c8f --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/packages/local_auth/local_auth_windows/example/windows/CMakeLists.txt b/packages/local_auth/local_auth_windows/example/windows/CMakeLists.txt new file mode 100644 index 000000000000..2163be881bd2 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/CMakeLists.txt @@ -0,0 +1,100 @@ +cmake_minimum_required(VERSION 3.14) +project(local_auth_windows_example LANGUAGES CXX) + +set(BINARY_NAME "local_auth_windows_example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() + +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build +add_subdirectory("runner") + +# Enable the test target. +set(include_local_auth_windows_tests TRUE) +# Provide an alias for the test target using the name expected by repo tooling. +add_custom_target(unit_tests DEPENDS local_auth_windows_test) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/packages/local_auth/local_auth_windows/example/windows/flutter/CMakeLists.txt b/packages/local_auth/local_auth_windows/example/windows/flutter/CMakeLists.txt new file mode 100644 index 000000000000..b2e4bd8d658b --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,103 @@ +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/packages/local_auth/local_auth_windows/example/windows/flutter/generated_plugins.cmake b/packages/local_auth/local_auth_windows/example/windows/flutter/generated_plugins.cmake new file mode 100644 index 000000000000..ef187dcae56f --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/flutter/generated_plugins.cmake @@ -0,0 +1,24 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + local_auth_windows +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/CMakeLists.txt b/packages/local_auth/local_auth_windows/example/windows/runner/CMakeLists.txt new file mode 100644 index 000000000000..de2d8916b72b --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/runner/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) +apply_standard_settings(${BINARY_NAME}) +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/Runner.rc b/packages/local_auth/local_auth_windows/example/windows/runner/Runner.rc new file mode 100644 index 000000000000..5fdea291cf19 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#ifdef FLUTTER_BUILD_NUMBER +#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#else +#define VERSION_AS_NUMBER 1,0,0 +#endif + +#ifdef FLUTTER_BUILD_NAME +#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "example" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2022 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "example.exe" "\0" + VALUE "ProductName", "example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/flutter_window.cpp b/packages/local_auth/local_auth_windows/example/windows/runner/flutter_window.cpp new file mode 100644 index 000000000000..8254bd9ff3c1 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/runner/flutter_window.cpp @@ -0,0 +1,65 @@ +// 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. + +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/flutter_window.h b/packages/local_auth/local_auth_windows/example/windows/runner/flutter_window.h new file mode 100644 index 000000000000..f1fc669093d0 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/runner/flutter_window.h @@ -0,0 +1,37 @@ +// 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. + +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/main.cpp b/packages/local_auth/local_auth_windows/example/windows/runner/main.cpp new file mode 100644 index 000000000000..4e37ae286c01 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/runner/main.cpp @@ -0,0 +1,46 @@ +// 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. + +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t* command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"local_auth_windows_example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/resource.h b/packages/local_auth/local_auth_windows/example/windows/runner/resource.h new file mode 100644 index 000000000000..d5d958dc4257 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/resources/app_icon.ico b/packages/local_auth/local_auth_windows/example/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/runner.exe.manifest b/packages/local_auth/local_auth_windows/example/windows/runner/runner.exe.manifest new file mode 100644 index 000000000000..c977c4a42589 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/utils.cpp b/packages/local_auth/local_auth_windows/example/windows/runner/utils.cpp new file mode 100644 index 000000000000..fb7e945a63b7 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/runner/utils.cpp @@ -0,0 +1,67 @@ +// 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. + +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE* unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = + ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, + nullptr, 0, nullptr, nullptr); + if (target_length == 0) { + return std::string(); + } + std::string utf8_string; + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/utils.h b/packages/local_auth/local_auth_windows/example/windows/runner/utils.h new file mode 100644 index 000000000000..bd81e1e02338 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/runner/utils.h @@ -0,0 +1,23 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/win32_window.cpp b/packages/local_auth/local_auth_windows/example/windows/runner/win32_window.cpp new file mode 100644 index 000000000000..85aa3614e8ad --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/runner/win32_window.cpp @@ -0,0 +1,241 @@ +// 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. + +#include "win32_window.h" + +#include + +#include "resource.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + FreeLibrary(user32_module); + } +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { ++g_active_window_count; } + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::CreateAndShow(const std::wstring& title, const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { return window_handle_; } + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/win32_window.h b/packages/local_auth/local_auth_windows/example/windows/runner/win32_window.h new file mode 100644 index 000000000000..d2a730052223 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/runner/win32_window.h @@ -0,0 +1,99 @@ +// 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. + +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates and shows a win32 window with |title| and position and size using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring& title, const Point& origin, + const Size& size); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/packages/local_auth/local_auth_windows/lib/local_auth_windows.dart b/packages/local_auth/local_auth_windows/lib/local_auth_windows.dart new file mode 100644 index 000000000000..1d65e81050f1 --- /dev/null +++ b/packages/local_auth/local_auth_windows/lib/local_auth_windows.dart @@ -0,0 +1,82 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; +import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; +import 'package:local_auth_windows/types/auth_messages_windows.dart'; + +export 'package:local_auth_platform_interface/types/auth_messages.dart'; +export 'package:local_auth_platform_interface/types/auth_options.dart'; +export 'package:local_auth_platform_interface/types/biometric_type.dart'; +export 'package:local_auth_windows/types/auth_messages_windows.dart'; + +const MethodChannel _channel = + MethodChannel('plugins.flutter.io/local_auth_windows'); + +/// The implementation of [LocalAuthPlatform] for Windows. +class LocalAuthWindows extends LocalAuthPlatform { + /// Registers this class as the default instance of [LocalAuthPlatform]. + static void registerWith() { + LocalAuthPlatform.instance = LocalAuthWindows(); + } + + @override + Future authenticate({ + required String localizedReason, + required Iterable authMessages, + AuthenticationOptions options = const AuthenticationOptions(), + }) async { + assert(localizedReason.isNotEmpty); + final Map args = { + 'localizedReason': localizedReason, + 'useErrorDialogs': options.useErrorDialogs, + 'stickyAuth': options.stickyAuth, + 'sensitiveTransaction': options.sensitiveTransaction, + 'biometricOnly': options.biometricOnly, + }; + args.addAll(const WindowsAuthMessages().args); + for (final AuthMessages messages in authMessages) { + if (messages is WindowsAuthMessages) { + args.addAll(messages.args); + } + } + return (await _channel.invokeMethod('authenticate', args)) ?? false; + } + + @override + Future deviceSupportsBiometrics() async { + return (await _channel.invokeMethod('deviceSupportsBiometrics')) ?? + false; + } + + @override + Future> getEnrolledBiometrics() async { + final List result = (await _channel.invokeListMethod( + 'getEnrolledBiometrics', + )) ?? + []; + final List biometrics = []; + for (final String value in result) { + switch (value) { + case 'weak': + biometrics.add(BiometricType.weak); + break; + case 'strong': + biometrics.add(BiometricType.strong); + break; + } + } + return biometrics; + } + + @override + Future isDeviceSupported() async => + (await _channel.invokeMethod('isDeviceSupported')) ?? false; + + /// Always returns false as this method is not supported on Windows. + @override + Future stopAuthentication() async { + return false; + } +} diff --git a/packages/local_auth/local_auth_windows/lib/types/auth_messages_windows.dart b/packages/local_auth/local_auth_windows/lib/types/auth_messages_windows.dart new file mode 100644 index 000000000000..e47e8737153c --- /dev/null +++ b/packages/local_auth/local_auth_windows/lib/types/auth_messages_windows.dart @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; +import 'package:local_auth_platform_interface/types/auth_messages.dart'; + +/// Windows side authentication messages. +/// +/// Provides default values for all messages. +/// +/// Currently unused. +@immutable +class WindowsAuthMessages extends AuthMessages { + /// Constructs a new instance. + const WindowsAuthMessages(); + + @override + Map get args { + return {}; + } +} diff --git a/packages/local_auth/local_auth_windows/pubspec.yaml b/packages/local_auth/local_auth_windows/pubspec.yaml new file mode 100644 index 000000000000..9edeffbb6530 --- /dev/null +++ b/packages/local_auth/local_auth_windows/pubspec.yaml @@ -0,0 +1,26 @@ +name: local_auth_windows +description: Windows implementation of the local_auth plugin. +repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_windows +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 +version: 1.0.0 + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.8.0" + +flutter: + plugin: + implements: local_auth + platforms: + windows: + pluginClass: LocalAuthPlugin + dartPluginClass: LocalAuthWindows + +dependencies: + flutter: + sdk: flutter + local_auth_platform_interface: ^1.0.0 + +dev_dependencies: + flutter_test: + sdk: flutter \ No newline at end of file diff --git a/packages/local_auth/local_auth_windows/test/local_auth_test.dart b/packages/local_auth/local_auth_windows/test/local_auth_test.dart new file mode 100644 index 000000000000..b11c19e7b339 --- /dev/null +++ b/packages/local_auth/local_auth_windows/test/local_auth_test.dart @@ -0,0 +1,79 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:local_auth_windows/local_auth_windows.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('authenticate', () { + const MethodChannel channel = MethodChannel( + 'plugins.flutter.io/local_auth_windows', + ); + + final List log = []; + late LocalAuthWindows localAuthentication; + + setUp(() { + channel.setMockMethodCallHandler((MethodCall methodCall) { + log.add(methodCall); + switch (methodCall.method) { + case 'getEnrolledBiometrics': + return Future>.value(['weak', 'strong']); + default: + return Future.value(true); + } + }); + localAuthentication = LocalAuthWindows(); + log.clear(); + }); + + test('authenticate with no arguments passes expected defaults', () async { + await localAuthentication.authenticate( + authMessages: [const WindowsAuthMessages()], + localizedReason: 'My localized reason'); + expect( + log, + [ + isMethodCall('authenticate', + arguments: { + 'localizedReason': 'My localized reason', + 'useErrorDialogs': true, + 'stickyAuth': false, + 'sensitiveTransaction': true, + 'biometricOnly': false, + }..addAll(const WindowsAuthMessages().args)), + ], + ); + }); + + test('authenticate passes all options.', () async { + await localAuthentication.authenticate( + authMessages: [const WindowsAuthMessages()], + localizedReason: 'My localized reason', + options: const AuthenticationOptions( + useErrorDialogs: false, + stickyAuth: true, + sensitiveTransaction: false, + biometricOnly: true, + ), + ); + expect( + log, + [ + isMethodCall('authenticate', + arguments: { + 'localizedReason': 'My localized reason', + 'useErrorDialogs': false, + 'stickyAuth': true, + 'sensitiveTransaction': false, + 'biometricOnly': true, + }..addAll(const WindowsAuthMessages().args)), + ], + ); + }); + }); +} diff --git a/packages/local_auth/local_auth_windows/windows/CMakeLists.txt b/packages/local_auth/local_auth_windows/windows/CMakeLists.txt new file mode 100644 index 000000000000..bcf59bb827c7 --- /dev/null +++ b/packages/local_auth/local_auth_windows/windows/CMakeLists.txt @@ -0,0 +1,120 @@ +cmake_minimum_required(VERSION 3.15) +set(PROJECT_NAME "local_auth_windows") +set(WIL_VERSION "1.0.220201.1") +set(CPPWINRT_VERSION "2.0.220418.1") +project(${PROJECT_NAME} LANGUAGES CXX) +include(FetchContent) + +set(PLUGIN_NAME "${PROJECT_NAME}_plugin") + +FetchContent_Declare(nuget + URL "https://dist.nuget.org/win-x86-commandline/v6.0.0/nuget.exe" + URL_HASH SHA256=04eb6c4fe4213907e2773e1be1bbbd730e9a655a3c9c58387ce8d4a714a5b9e1 + DOWNLOAD_NO_EXTRACT true +) + +find_program(NUGET nuget) +if (NOT NUGET) + message("Nuget.exe not found, trying to download or use cached version.") + FetchContent_MakeAvailable(nuget) + set(NUGET ${nuget_SOURCE_DIR}/nuget.exe) +endif() + +execute_process(COMMAND + ${NUGET} install Microsoft.Windows.ImplementationLibrary -Version ${WIL_VERSION} -OutputDirectory ${CMAKE_BINARY_DIR}/packages + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + RESULT_VARIABLE ret) +if (NOT ret EQUAL 0) + message(FATAL_ERROR "Failed to install nuget package Microsoft.Windows.ImplementationLibrary.${WIL_VERSION}") +endif() + +execute_process(COMMAND + ${NUGET} install Microsoft.Windows.CppWinRT -Version ${CPPWINRT_VERSION} -OutputDirectory packages + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + RESULT_VARIABLE ret) +if (NOT ret EQUAL 0) + message(FATAL_ERROR "Failed to install nuget package Microsoft.Windows.CppWinRT.${CPPWINRT_VERSION}") +endif() + +set(CPPWINRT ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT.${CPPWINRT_VERSION}/bin/cppwinrt.exe) +execute_process(COMMAND + ${CPPWINRT} -input sdk -output include + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + RESULT_VARIABLE ret) +if (NOT ret EQUAL 0) + message(FATAL_ERROR "Failed to run cppwinrt.exe") +endif() + +include_directories(BEFORE SYSTEM ${CMAKE_BINARY_DIR}/include) + +list(APPEND PLUGIN_SOURCES + "local_auth_plugin.cpp" +) + +add_library(${PLUGIN_NAME} SHARED + "include/local_auth_windows/local_auth_plugin.h" + "local_auth_windows.cpp" + "local_auth.h" + ${PLUGIN_SOURCES} +) +apply_standard_settings(${PLUGIN_NAME}) +set_target_properties(${PLUGIN_NAME} PROPERTIES CXX_VISIBILITY_PRESET hidden) +target_compile_features(${PLUGIN_NAME} PRIVATE cxx_std_20) +target_compile_options(${PLUGIN_NAME} PRIVATE /await) +target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) +target_include_directories(${PLUGIN_NAME} INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(${PLUGIN_NAME} PRIVATE ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.ImplementationLibrary.${WIL_VERSION}/build/native/Microsoft.Windows.ImplementationLibrary.targets) +target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin windowsapp) + +# List of absolute paths to libraries that should be bundled with the plugin +set(file_chooser_bundled_libraries + "" + PARENT_SCOPE +) + + +# === Tests === + +if (${include_${PROJECT_NAME}_tests}) +set(TEST_RUNNER "${PROJECT_NAME}_test") +enable_testing() +# TODO(stuartmorgan): Consider using a single shared, pre-checked-in googletest +# instance rather than downloading for each plugin. This approach makes sense +# for a template, but not for a monorepo with many plugins. +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/release-1.11.0.zip +) +# Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +# Disable install commands for gtest so it doesn't end up in the bundle. +set(INSTALL_GTEST OFF CACHE BOOL "Disable installation of googletest" FORCE) + +FetchContent_MakeAvailable(googletest) + +# The plugin's C API is not very useful for unit testing, so build the sources +# directly into the test binary rather than using the DLL. +add_executable(${TEST_RUNNER} + test/mocks.h + test/local_auth_plugin_test.cpp + ${PLUGIN_SOURCES} +) +apply_standard_settings(${TEST_RUNNER}) +target_include_directories(${TEST_RUNNER} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}") +target_compile_features(${TEST_RUNNER} PRIVATE cxx_std_20) +target_compile_options(${TEST_RUNNER} PRIVATE /await) +target_link_libraries(${TEST_RUNNER} PRIVATE ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.ImplementationLibrary.${WIL_VERSION}/build/native/Microsoft.Windows.ImplementationLibrary.targets) +target_link_libraries(${TEST_RUNNER} PRIVATE flutter_wrapper_plugin) +target_link_libraries(${TEST_RUNNER} PRIVATE windowsapp) +target_link_libraries(${TEST_RUNNER} PRIVATE gtest_main gmock) + +# flutter_wrapper_plugin has link dependencies on the Flutter DLL. +add_custom_command(TARGET ${TEST_RUNNER} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${FLUTTER_LIBRARY}" $ +) + +include(GoogleTest) +gtest_discover_tests(${TEST_RUNNER}) +endif() diff --git a/packages/local_auth/local_auth_windows/windows/include/local_auth_windows/local_auth_plugin.h b/packages/local_auth/local_auth_windows/windows/include/local_auth_windows/local_auth_plugin.h new file mode 100644 index 000000000000..0604de8ee2bb --- /dev/null +++ b/packages/local_auth/local_auth_windows/windows/include/local_auth_windows/local_auth_plugin.h @@ -0,0 +1,26 @@ +// 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. +#ifndef FLUTTER_PLUGIN_LOCAL_AUTH_WINDOWS_PLUGIN_H_ +#define FLUTTER_PLUGIN_LOCAL_AUTH_WINDOWS_PLUGIN_H_ + +#include + +#ifdef FLUTTER_PLUGIN_IMPL +#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport) +#else +#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport) +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +FLUTTER_PLUGIN_EXPORT void LocalAuthPluginRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // FLUTTER_PLUGIN_LOCAL_AUTH_WINDOWS_PLUGIN_H_ diff --git a/packages/local_auth/local_auth_windows/windows/local_auth.h b/packages/local_auth/local_auth_windows/windows/local_auth.h new file mode 100644 index 000000000000..94b91f88345a --- /dev/null +++ b/packages/local_auth/local_auth_windows/windows/local_auth.h @@ -0,0 +1,89 @@ +// 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. + +#include +#include +#include +#include +#include +#include +#include + +#include "include/local_auth_windows/local_auth_plugin.h" + +// Include prior to C++/WinRT Headers +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace local_auth_windows { + +// Abstract class that is used to determine whether a user +// has given consent to a particular action, and if the system +// supports asking this question. +class UserConsentVerifier { + public: + UserConsentVerifier() {} + virtual ~UserConsentVerifier() = default; + + // Abstract method that request the user's verification + // given the provided reason. + virtual winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI::UserConsentVerificationResult> + RequestVerificationForWindowAsync(std::wstring localized_reason) = 0; + + // Abstract method that returns weather the system supports Windows Hello. + virtual winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability> + CheckAvailabilityAsync() = 0; + + // Disallow copy and move. + UserConsentVerifier(const UserConsentVerifier&) = delete; + UserConsentVerifier& operator=(const UserConsentVerifier&) = delete; +}; + +class LocalAuthPlugin : public flutter::Plugin { + public: + static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); + + // Creates a plugin instance that will create the dialog and associate + // it with the HWND returned from the provided function. + LocalAuthPlugin(std::function window_provider); + + // Creates a plugin instance with the given UserConsentVerifier instance. + // Exists for unit testing with mock implementations. + LocalAuthPlugin(std::unique_ptr user_consent_verifier); + + // Handles method calls from Dart on this plugin's channel. + void HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result); + + virtual ~LocalAuthPlugin(); + + private: + std::unique_ptr user_consent_verifier_; + + // Starts authentication process. + winrt::fire_and_forget Authenticate( + const flutter::MethodCall& method_call, + std::unique_ptr> result); + + // Returns enrolled biometric types available on device. + winrt::fire_and_forget GetEnrolledBiometrics( + std::unique_ptr> result); + + // Returns whether the system supports Windows Hello. + winrt::fire_and_forget IsDeviceSupported( + std::unique_ptr> result); +}; + +} // namespace local_auth_windows \ No newline at end of file diff --git a/packages/local_auth/local_auth_windows/windows/local_auth_plugin.cpp b/packages/local_auth/local_auth_windows/windows/local_auth_plugin.cpp new file mode 100644 index 000000000000..7a25abb53010 --- /dev/null +++ b/packages/local_auth/local_auth_windows/windows/local_auth_plugin.cpp @@ -0,0 +1,236 @@ +// 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. +#include + +#include "local_auth.h" + +namespace { + +template +// Helper method for getting an argument from an EncodableValue. +T GetArgument(const std::string arg, const flutter::EncodableValue* args, + T fallback) { + T result{fallback}; + const auto* arguments = std::get_if(args); + if (arguments) { + auto result_it = arguments->find(flutter::EncodableValue(arg)); + if (result_it != arguments->end()) { + result = std::get(result_it->second); + } + } + return result; +} + +// Returns the window's HWND for a given FlutterView. +HWND GetRootWindow(flutter::FlutterView* view) { + return ::GetAncestor(view->GetNativeWindow(), GA_ROOT); +} + +// Converts the given UTF-8 string to UTF-16. +std::wstring Utf16FromUtf8(const std::string& utf8_string) { + if (utf8_string.empty()) { + return std::wstring(); + } + int target_length = + ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8_string.data(), + static_cast(utf8_string.length()), nullptr, 0); + if (target_length == 0) { + return std::wstring(); + } + std::wstring utf16_string; + utf16_string.resize(target_length); + int converted_length = + ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8_string.data(), + static_cast(utf8_string.length()), + utf16_string.data(), target_length); + if (converted_length == 0) { + return std::wstring(); + } + return utf16_string; +} + +} // namespace + +namespace local_auth_windows { + +// Creates an instance of the UserConsentVerifier that +// calls the native Windows APIs to get the user's consent. +class UserConsentVerifierImpl : public UserConsentVerifier { + public: + explicit UserConsentVerifierImpl(std::function window_provider) + : get_root_window_(std::move(window_provider)){}; + virtual ~UserConsentVerifierImpl() = default; + + // Calls the native Windows API to get the user's consent + // with the provided reason. + winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI::UserConsentVerificationResult> + RequestVerificationForWindowAsync(std::wstring localized_reason) override { + winrt::impl::com_ref + user_consent_verifier_interop = winrt::get_activation_factory< + winrt::Windows::Security::Credentials::UI::UserConsentVerifier, + IUserConsentVerifierInterop>(); + + HWND root_window_handle = get_root_window_(); + + auto reason = wil::make_unique_string( + localized_reason.c_str(), localized_reason.size()); + + winrt::Windows::Security::Credentials::UI::UserConsentVerificationResult + consent_result = co_await winrt::capture< + winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerificationResult>>( + user_consent_verifier_interop, + &::IUserConsentVerifierInterop::RequestVerificationForWindowAsync, + root_window_handle, reason.get()); + + return consent_result; + } + + // Calls the native Windows API to check for the Windows Hello availability. + winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability> + CheckAvailabilityAsync() override { + return winrt::Windows::Security::Credentials::UI::UserConsentVerifier:: + CheckAvailabilityAsync(); + } + + // Disallow copy and move. + UserConsentVerifierImpl(const UserConsentVerifierImpl&) = delete; + UserConsentVerifierImpl& operator=(const UserConsentVerifierImpl&) = delete; + + private: + // The provider for the root window to attach the dialog to. + std::function get_root_window_; +}; + +// static +void LocalAuthPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarWindows* registrar) { + auto channel = + std::make_unique>( + registrar->messenger(), "plugins.flutter.io/local_auth_windows", + &flutter::StandardMethodCodec::GetInstance()); + + auto plugin = std::make_unique( + [registrar]() { return GetRootWindow(registrar->GetView()); }); + + channel->SetMethodCallHandler( + [plugin_pointer = plugin.get()](const auto& call, auto result) { + plugin_pointer->HandleMethodCall(call, std::move(result)); + }); + + registrar->AddPlugin(std::move(plugin)); +} + +// Default constructor for LocalAuthPlugin. +LocalAuthPlugin::LocalAuthPlugin(std::function window_provider) + : user_consent_verifier_(std::make_unique( + std::move(window_provider))) {} + +LocalAuthPlugin::LocalAuthPlugin( + std::unique_ptr user_consent_verifier) + : user_consent_verifier_(std::move(user_consent_verifier)) {} + +LocalAuthPlugin::~LocalAuthPlugin() {} + +void LocalAuthPlugin::HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result) { + if (method_call.method_name().compare("authenticate") == 0) { + Authenticate(method_call, std::move(result)); + } else if (method_call.method_name().compare("getEnrolledBiometrics") == 0) { + GetEnrolledBiometrics(std::move(result)); + } else if (method_call.method_name().compare("isDeviceSupported") == 0 || + method_call.method_name().compare("deviceSupportsBiometrics") == + 0) { + IsDeviceSupported(std::move(result)); + } else { + result->NotImplemented(); + } +} + +// Starts authentication process. +winrt::fire_and_forget LocalAuthPlugin::Authenticate( + const flutter::MethodCall& method_call, + std::unique_ptr> result) { + std::wstring reason = Utf16FromUtf8(GetArgument( + "localizedReason", method_call.arguments(), std::string())); + + bool biometric_only = + GetArgument("biometricOnly", method_call.arguments(), false); + if (biometric_only) { + result->Error("biometricOnlyNotSupported", + "Windows doesn't support the biometricOnly parameter."); + co_return; + } + + winrt::Windows::Security::Credentials::UI::UserConsentVerifierAvailability + ucv_availability = + co_await user_consent_verifier_->CheckAvailabilityAsync(); + + if (ucv_availability == + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::DeviceNotPresent) { + result->Error("NoHardware", "No biometric hardware found"); + co_return; + } else if (ucv_availability == + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::NotConfiguredForUser) { + result->Error("NotEnrolled", "No biometrics enrolled on this device."); + co_return; + } else if (ucv_availability != + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::Available) { + result->Error("NotAvailable", "Required security features not enabled"); + co_return; + } + + try { + winrt::Windows::Security::Credentials::UI::UserConsentVerificationResult + consent_result = + co_await user_consent_verifier_->RequestVerificationForWindowAsync( + reason); + + result->Success(flutter::EncodableValue( + consent_result == winrt::Windows::Security::Credentials::UI:: + UserConsentVerificationResult::Verified)); + } catch (...) { + result->Success(flutter::EncodableValue(false)); + } +} + +// Returns biometric types available on device. +winrt::fire_and_forget LocalAuthPlugin::GetEnrolledBiometrics( + std::unique_ptr> result) { + try { + flutter::EncodableList biometrics; + winrt::Windows::Security::Credentials::UI::UserConsentVerifierAvailability + ucv_availability = + co_await user_consent_verifier_->CheckAvailabilityAsync(); + if (ucv_availability == winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::Available) { + biometrics.push_back(flutter::EncodableValue("weak")); + biometrics.push_back(flutter::EncodableValue("strong")); + } + result->Success(biometrics); + } catch (const std::exception& e) { + result->Error("no_biometrics_available", e.what()); + } +} + +// Returns whether the device supports Windows Hello or not. +winrt::fire_and_forget LocalAuthPlugin::IsDeviceSupported( + std::unique_ptr> result) { + winrt::Windows::Security::Credentials::UI::UserConsentVerifierAvailability + ucv_availability = + co_await user_consent_verifier_->CheckAvailabilityAsync(); + result->Success(flutter::EncodableValue( + ucv_availability == winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::Available)); +} + +} // namespace local_auth_windows diff --git a/packages/local_auth/local_auth_windows/windows/local_auth_windows.cpp b/packages/local_auth/local_auth_windows/windows/local_auth_windows.cpp new file mode 100644 index 000000000000..6e5e6a186afb --- /dev/null +++ b/packages/local_auth/local_auth_windows/windows/local_auth_windows.cpp @@ -0,0 +1,15 @@ +// 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. + +#include + +#include "include/local_auth_windows/local_auth_plugin.h" +#include "local_auth.h" + +void LocalAuthPluginRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar) { + local_auth_windows::LocalAuthPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarManager::GetInstance() + ->GetRegistrar(registrar)); +} diff --git a/packages/local_auth/local_auth_windows/windows/test/local_auth_plugin_test.cpp b/packages/local_auth/local_auth_windows/windows/test/local_auth_plugin_test.cpp new file mode 100644 index 000000000000..3828b05eef07 --- /dev/null +++ b/packages/local_auth/local_auth_windows/windows/test/local_auth_plugin_test.cpp @@ -0,0 +1,253 @@ +// 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. + +#include "include/local_auth_windows/local_auth_plugin.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "mocks.h" + +namespace local_auth_windows { +namespace test { + +using flutter::EncodableList; +using flutter::EncodableMap; +using flutter::EncodableValue; +using ::testing::_; +using ::testing::DoAll; +using ::testing::EndsWith; +using ::testing::Eq; +using ::testing::Pointee; +using ::testing::Return; + +TEST(LocalAuthPlugin, IsDeviceSupportedHandlerSuccessIfVerifierAvailable) { + std::unique_ptr result = + std::make_unique(); + + std::unique_ptr mockConsentVerifier = + std::make_unique(); + + EXPECT_CALL(*mockConsentVerifier, CheckAvailabilityAsync) + .Times(1) + .WillOnce([]() -> winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability> { + co_return winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::Available; + }); + + LocalAuthPlugin plugin(std::move(mockConsentVerifier)); + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(true)))); + + plugin.HandleMethodCall( + flutter::MethodCall("isDeviceSupported", + std::make_unique()), + std::move(result)); +} + +TEST(LocalAuthPlugin, IsDeviceSupportedHandlerSuccessIfVerifierNotAvailable) { + std::unique_ptr result = + std::make_unique(); + + std::unique_ptr mockConsentVerifier = + std::make_unique(); + + EXPECT_CALL(*mockConsentVerifier, CheckAvailabilityAsync) + .Times(1) + .WillOnce([]() -> winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability> { + co_return winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::DeviceNotPresent; + }); + + LocalAuthPlugin plugin(std::move(mockConsentVerifier)); + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(false)))); + + plugin.HandleMethodCall( + flutter::MethodCall("isDeviceSupported", + std::make_unique()), + std::move(result)); +} + +TEST(LocalAuthPlugin, + GetEnrolledBiometricsHandlerReturnEmptyListIfVerifierNotAvailable) { + std::unique_ptr result = + std::make_unique(); + + std::unique_ptr mockConsentVerifier = + std::make_unique(); + + EXPECT_CALL(*mockConsentVerifier, CheckAvailabilityAsync) + .Times(1) + .WillOnce([]() -> winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability> { + co_return winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::DeviceNotPresent; + }); + + LocalAuthPlugin plugin(std::move(mockConsentVerifier)); + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableList()))); + + plugin.HandleMethodCall( + flutter::MethodCall("getEnrolledBiometrics", + std::make_unique()), + std::move(result)); +} + +TEST(LocalAuthPlugin, + GetEnrolledBiometricsHandlerReturnNonEmptyListIfVerifierAvailable) { + std::unique_ptr result = + std::make_unique(); + + std::unique_ptr mockConsentVerifier = + std::make_unique(); + + EXPECT_CALL(*mockConsentVerifier, CheckAvailabilityAsync) + .Times(1) + .WillOnce([]() -> winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability> { + co_return winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::Available; + }); + + LocalAuthPlugin plugin(std::move(mockConsentVerifier)); + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, + SuccessInternal(Pointee(EncodableList( + {EncodableValue("weak"), EncodableValue("strong")})))); + + plugin.HandleMethodCall( + flutter::MethodCall("getEnrolledBiometrics", + std::make_unique()), + std::move(result)); +} + +TEST(LocalAuthPlugin, AuthenticateHandlerDoesNotSupportBiometricOnly) { + std::unique_ptr result = + std::make_unique(); + + std::unique_ptr mockConsentVerifier = + std::make_unique(); + + LocalAuthPlugin plugin(std::move(mockConsentVerifier)); + + EXPECT_CALL(*result, ErrorInternal).Times(1); + EXPECT_CALL(*result, SuccessInternal).Times(0); + + std::unique_ptr args = + std::make_unique(EncodableMap({ + {"localizedReason", EncodableValue("My Reason")}, + {"biometricOnly", EncodableValue(true)}, + })); + + plugin.HandleMethodCall(flutter::MethodCall("authenticate", std::move(args)), + std::move(result)); +} + +TEST(LocalAuthPlugin, AuthenticateHandlerWorksWhenAuthorized) { + std::unique_ptr result = + std::make_unique(); + + std::unique_ptr mockConsentVerifier = + std::make_unique(); + + EXPECT_CALL(*mockConsentVerifier, CheckAvailabilityAsync) + .Times(1) + .WillOnce([]() -> winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability> { + co_return winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::Available; + }); + + EXPECT_CALL(*mockConsentVerifier, RequestVerificationForWindowAsync) + .Times(1) + .WillOnce([](std::wstring localizedReason) + -> winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerificationResult> { + EXPECT_EQ(localizedReason, L"My Reason"); + co_return winrt::Windows::Security::Credentials::UI:: + UserConsentVerificationResult::Verified; + }); + + LocalAuthPlugin plugin(std::move(mockConsentVerifier)); + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(true)))); + + std::unique_ptr args = + std::make_unique(EncodableMap({ + {"localizedReason", EncodableValue("My Reason")}, + {"biometricOnly", EncodableValue(false)}, + })); + + plugin.HandleMethodCall(flutter::MethodCall("authenticate", std::move(args)), + std::move(result)); +} + +TEST(LocalAuthPlugin, AuthenticateHandlerWorksWhenNotAuthorized) { + std::unique_ptr result = + std::make_unique(); + + std::unique_ptr mockConsentVerifier = + std::make_unique(); + + EXPECT_CALL(*mockConsentVerifier, CheckAvailabilityAsync) + .Times(1) + .WillOnce([]() -> winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability> { + co_return winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::Available; + }); + + EXPECT_CALL(*mockConsentVerifier, RequestVerificationForWindowAsync) + .Times(1) + .WillOnce([](std::wstring localizedReason) + -> winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerificationResult> { + EXPECT_EQ(localizedReason, L"My Reason"); + co_return winrt::Windows::Security::Credentials::UI:: + UserConsentVerificationResult::Canceled; + }); + + LocalAuthPlugin plugin(std::move(mockConsentVerifier)); + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(false)))); + + std::unique_ptr args = + std::make_unique(EncodableMap({ + {"localizedReason", EncodableValue("My Reason")}, + {"biometricOnly", EncodableValue(false)}, + })); + + plugin.HandleMethodCall(flutter::MethodCall("authenticate", std::move(args)), + std::move(result)); +} + +} // namespace test +} // namespace local_auth_windows diff --git a/packages/local_auth/local_auth_windows/windows/test/mocks.h b/packages/local_auth/local_auth_windows/windows/test/mocks.h new file mode 100644 index 000000000000..d82ae801b4b9 --- /dev/null +++ b/packages/local_auth/local_auth_windows/windows/test/mocks.h @@ -0,0 +1,63 @@ +// 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. + +#ifndef PACKAGES_LOCAL_AUTH_LOCAL_AUTH_WINDOWS_WINDOWS_TEST_MOCKS_H_ +#define PACKAGES_LOCAL_AUTH_LOCAL_AUTH_WINDOWS_WINDOWS_TEST_MOCKS_H_ + +#include +#include +#include +#include +#include +#include + +#include "../local_auth.h" + +namespace local_auth_windows { +namespace test { + +namespace { + +using flutter::EncodableMap; +using flutter::EncodableValue; +using ::testing::_; + +class MockMethodResult : public flutter::MethodResult<> { + public: + ~MockMethodResult() = default; + + MOCK_METHOD(void, SuccessInternal, (const EncodableValue* result), + (override)); + MOCK_METHOD(void, ErrorInternal, + (const std::string& error_code, const std::string& error_message, + const EncodableValue* details), + (override)); + MOCK_METHOD(void, NotImplementedInternal, (), (override)); +}; + +class MockUserConsentVerifier : public UserConsentVerifier { + public: + explicit MockUserConsentVerifier(){}; + virtual ~MockUserConsentVerifier() = default; + + MOCK_METHOD(winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerificationResult>, + RequestVerificationForWindowAsync, (std::wstring localizedReason), + (override)); + MOCK_METHOD(winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability>, + CheckAvailabilityAsync, (), (override)); + + // Disallow copy and move. + MockUserConsentVerifier(const MockUserConsentVerifier&) = delete; + MockUserConsentVerifier& operator=(const MockUserConsentVerifier&) = delete; +}; + +} // namespace +} // namespace test +} // namespace local_auth_windows + +#endif // PACKAGES_LOCAL_AUTH_LOCAL_AUTH_WINDOWS_WINDOWS_TEST_MOCKS_H_ From d436a7c52f9f9235d60fb36f0a962acb975505c5 Mon Sep 17 00:00:00 2001 From: Hwanseok Barth Kang Date: Wed, 18 May 2022 02:07:13 +0900 Subject: [PATCH 572/600] [google_sign_in_platform_interface] Add availability to mock models (#5669) --- .../CHANGELOG.md | 1 + .../lib/src/types.dart | 4 +- ...oogle_sign_in_platform_interface_test.dart | 38 +++++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md b/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md index abf01c847d83..e78060d84744 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ ## 2.1.3 +* Enables mocking models by changing overridden operator == parameter type from `dynamic` to `Object`. * Removes unnecessary imports. * Adds `SignInInitParameters` class to hold all sign in params, including the new `forceCodeForRefreshToken`. diff --git a/packages/google_sign_in/google_sign_in_platform_interface/lib/src/types.dart b/packages/google_sign_in/google_sign_in_platform_interface/lib/src/types.dart index 2cac5e886729..e1f455d6ea44 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/lib/src/types.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/lib/src/types.dart @@ -116,7 +116,7 @@ class GoogleSignInUserData { @override // TODO(stuartmorgan): Make this class immutable in the next breaking change. // ignore: avoid_equals_and_hash_code_on_mutable_classes - bool operator ==(dynamic other) { + bool operator ==(Object other) { if (identical(this, other)) { return true; } @@ -159,7 +159,7 @@ class GoogleSignInTokenData { @override // TODO(stuartmorgan): Make this class immutable in the next breaking change. // ignore: avoid_equals_and_hash_code_on_mutable_classes - bool operator ==(dynamic other) { + bool operator ==(Object other) { if (identical(this, other)) { return true; } diff --git a/packages/google_sign_in/google_sign_in_platform_interface/test/google_sign_in_platform_interface_test.dart b/packages/google_sign_in/google_sign_in_platform_interface/test/google_sign_in_platform_interface_test.dart index 78e57a3eb2ac..bf960abc7375 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/test/google_sign_in_platform_interface_test.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/test/google_sign_in_platform_interface_test.dart @@ -29,6 +29,44 @@ void main() { GoogleSignInPlatform.instance = ImplementsWithIsMock(); }); }); + + group('GoogleSignInTokenData', () { + test('can be compared by == operator', () { + final GoogleSignInTokenData firstInstance = GoogleSignInTokenData( + accessToken: 'accessToken', + idToken: 'idToken', + serverAuthCode: 'serverAuthCode', + ); + final GoogleSignInTokenData secondInstance = GoogleSignInTokenData( + accessToken: 'accessToken', + idToken: 'idToken', + serverAuthCode: 'serverAuthCode', + ); + expect(firstInstance == secondInstance, isTrue); + }); + }); + + group('GoogleSignInUserData', () { + test('can be compared by == operator', () { + final GoogleSignInUserData firstInstance = GoogleSignInUserData( + email: 'email', + id: 'id', + displayName: 'displayName', + photoUrl: 'photoUrl', + idToken: 'idToken', + serverAuthCode: 'serverAuthCode', + ); + final GoogleSignInUserData secondInstance = GoogleSignInUserData( + email: 'email', + id: 'id', + displayName: 'displayName', + photoUrl: 'photoUrl', + idToken: 'idToken', + serverAuthCode: 'serverAuthCode', + ); + expect(firstInstance == secondInstance, isTrue); + }); + }); } class ImplementsWithIsMock extends Mock implements GoogleSignInPlatform { From ad618d9ea8c84150b719a4510c79eb4faa845be5 Mon Sep 17 00:00:00 2001 From: godofredoc Date: Tue, 17 May 2022 11:07:16 -0700 Subject: [PATCH 573/600] Update cirrus secret. (#5774) --- .cirrus.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index eaaac8d7b5b5..c4abdbc5adc1 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -1,4 +1,4 @@ -gcp_credentials: ENCRYPTED[!2c88dee9c9d9805b214c9f7ad8f3bc8fae936cdb0f881d562101151c408c7e024a41222677d5831df90c60d2dd6cd80a!] +gcp_credentials: ENCRYPTED[!ebad0a1f4f7a446b77944c33651460a7ab010b4617273cb016cf354eb8fc22aa92e37a3c58bfa4a0c40a799351e027a6!] # Don't run on release tags since it creates O(n^2) tasks where n is the # number of plugins. From f25fc10f93018ee15a4906f91ff314ae129c3223 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 17 May 2022 14:57:14 -0400 Subject: [PATCH 574/600] Add more Android plugin owners (#5624) --- CODEOWNERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CODEOWNERS b/CODEOWNERS index bc3e392d1766..26a2a2bc1602 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -18,6 +18,8 @@ packages/**/*_web/** @ditman # - Android packages/camera/camera/android/** @camsim99 +packages/espresso/** @blasten +packages/flutter_plugin_android_lifecycle/** @blasten packages/google_maps_flutter/google_maps_flutter/android/** @GaryQian packages/google_sign_in/google_sign_in_android/** @camsim99 packages/image_picker/image_picker_android/** @GaryQian From 94201eaeb51a44b63fd63b36ccbbb29600538b82 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 17 May 2022 16:02:11 -0400 Subject: [PATCH 575/600] [google_maps_flutter] Fix native unit tests on M1 (#5772) --- packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md | 1 + .../example/ios/Runner.xcodeproj/project.pbxproj | 2 ++ 2 files changed, 3 insertions(+) diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index 07ffbfdb4e5d..57d24e2196a5 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Fixes iOS native unit tests on M1 devices. * Minor fixes for new analysis options. ## 2.1.5 diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj b/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj index 6a0466c3c6d9..a8d37106bb83 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj @@ -651,6 +651,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; INFOPLIST_FILE = RunnerTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MTL_FAST_MATH = YES; @@ -666,6 +667,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; INFOPLIST_FILE = RunnerTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MTL_FAST_MATH = YES; From 1ee23b92c90f4eccc5a5290f80a0fe063db8922e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 May 2022 13:52:13 -0700 Subject: [PATCH 576/600] [video_player]: Bump exoplayer from 2.17.0 to 2.17.1 in /packages/video_player/video_player_android/android (#5579) --- packages/video_player/video_player_android/CHANGELOG.md | 4 ++++ .../video_player_android/android/build.gradle | 8 ++++---- packages/video_player/video_player_android/pubspec.yaml | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index 08acba895eba..28d0188213d6 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.4 + +* Updates ExoPlayer to 2.17.1. + ## 2.3.3 * Removes unnecessary imports. diff --git a/packages/video_player/video_player_android/android/build.gradle b/packages/video_player/video_player_android/android/build.gradle index e565a9364bd8..32cc203729b8 100644 --- a/packages/video_player/video_player_android/android/build.gradle +++ b/packages/video_player/video_player_android/android/build.gradle @@ -43,10 +43,10 @@ android { } dependencies { - implementation 'com.google.android.exoplayer:exoplayer-core:2.17.0' - implementation 'com.google.android.exoplayer:exoplayer-hls:2.17.0' - implementation 'com.google.android.exoplayer:exoplayer-dash:2.17.0' - implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.17.0' + implementation 'com.google.android.exoplayer:exoplayer-core:2.17.1' + implementation 'com.google.android.exoplayer:exoplayer-hls:2.17.1' + implementation 'com.google.android.exoplayer:exoplayer-dash:2.17.1' + implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.17.1' testImplementation 'junit:junit:4.12' testImplementation 'org.mockito:mockito-inline:3.9.0' } diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index bc69fd41369a..7e23b3210859 100644 --- a/packages/video_player/video_player_android/pubspec.yaml +++ b/packages/video_player/video_player_android/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_android description: Android implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.3 +version: 2.3.4 environment: sdk: ">=2.14.0 <3.0.0" From af3a088d18ee21a03050d7592f5e78d662cb035d Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 17 May 2022 20:32:10 -0400 Subject: [PATCH 577/600] [tools] Validate example READMEs (#5775) --- packages/espresso/example/README.md | 2 +- .../file_selector/example/README.md | 5 - .../google_maps_flutter/example/README.md | 5 - .../google_sign_in/example/README.md | 5 - .../google_sign_in_android/example/README.md | 5 - .../google_sign_in_ios/example/README.md | 5 - .../image_picker/example/README.md | 5 - .../image_picker_android/example/README.md | 5 - .../image_picker_ios/example/README.md | 5 - .../image_picker_windows/example/README.md | 5 - .../ios_platform_images/example/README.md | 13 -- .../local_auth/local_auth/example/README.md | 5 - .../local_auth_android/example/README.md | 5 - .../local_auth_ios/example/README.md | 5 - .../path_provider/example/README.md | 5 - .../path_provider_android/example/README.md | 5 - .../path_provider_ios/example/README.md | 5 - .../path_provider_linux/example/README.md | 13 -- .../path_provider_macos/example/README.md | 5 - .../path_provider_windows/example/README.md | 5 - .../quick_actions/example/README.md | 5 - .../quick_actions_android/example/README.md | 5 - .../quick_actions_ios/example/README.md | 5 - .../shared_preferences/example/README.md | 5 - .../example/README.md | 5 - .../shared_preferences_ios/example/README.md | 5 - .../example/README.md | 5 - .../example/README.md | 5 - .../example/README.md | 13 -- .../url_launcher/example/README.md | 5 - .../url_launcher_android/example/README.md | 5 - .../url_launcher_ios/example/README.md | 5 - .../url_launcher_linux/example/README.md | 5 - .../url_launcher_macos/example/README.md | 5 - .../video_player/example/README.md | 5 - .../video_player_android/example/README.md | 5 - .../example/README.md | 5 - .../webview_flutter/example/README.md | 5 - .../webview_flutter_android/example/README.md | 5 - .../webview_flutter_web/example/README.md | 5 - .../example/README.md | 5 - script/tool/CHANGELOG.md | 2 + .../src/common/package_looping_command.dart | 43 +++- .../tool/lib/src/pubspec_check_command.dart | 3 +- script/tool/lib/src/readme_check_command.dart | 73 +++++-- script/tool/lib/src/test_command.dart | 3 +- .../common/package_looping_command_test.dart | 80 ++++++- script/tool/test/list_command_test.dart | 3 + .../tool/test/readme_check_command_test.dart | 200 +++++++++++++++--- script/tool/test/util.dart | 17 +- .../tool/test/version_check_command_test.dart | 44 ++-- 51 files changed, 373 insertions(+), 321 deletions(-) diff --git a/packages/espresso/example/README.md b/packages/espresso/example/README.md index 224544e9f83f..edb498a11338 100644 --- a/packages/espresso/example/README.md +++ b/packages/espresso/example/README.md @@ -8,7 +8,7 @@ The espresso package only runs tests on Android. The example runs on iOS, but th To run the Espresso tests: -``` +```java flutter build apk --debug ./gradlew app:connectedAndroidTest ``` diff --git a/packages/file_selector/file_selector/example/README.md b/packages/file_selector/file_selector/example/README.md index 93260dc716b2..e1dcf70473c9 100644 --- a/packages/file_selector/file_selector/example/README.md +++ b/packages/file_selector/file_selector/example/README.md @@ -1,8 +1,3 @@ # file_selector_example Demonstrates how to use the file_selector plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/google_maps_flutter/google_maps_flutter/example/README.md b/packages/google_maps_flutter/google_maps_flutter/example/README.md index b92b9c326143..c8852649b065 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/README.md +++ b/packages/google_maps_flutter/google_maps_flutter/example/README.md @@ -1,8 +1,3 @@ # google_maps_flutter_example Demonstrates how to use the google_maps_flutter plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/google_sign_in/google_sign_in/example/README.md b/packages/google_sign_in/google_sign_in/example/README.md index 0e246e11a8be..24fdb3ec042d 100644 --- a/packages/google_sign_in/google_sign_in/example/README.md +++ b/packages/google_sign_in/google_sign_in/example/README.md @@ -1,8 +1,3 @@ # google_sign_in_example Demonstrates how to use the google_sign_in plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/google_sign_in/google_sign_in_android/example/README.md b/packages/google_sign_in/google_sign_in_android/example/README.md index 79d99dc72982..8eb153eb8efd 100644 --- a/packages/google_sign_in/google_sign_in_android/example/README.md +++ b/packages/google_sign_in/google_sign_in_android/example/README.md @@ -1,8 +1,3 @@ # google_sign_in_android example Exercises the Android implementation of `GoogleSignInPlatform`. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/google_sign_in/google_sign_in_ios/example/README.md b/packages/google_sign_in/google_sign_in_ios/example/README.md index ca3dc7023fc9..04c3372dc3b0 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/README.md +++ b/packages/google_sign_in/google_sign_in_ios/example/README.md @@ -1,8 +1,3 @@ # google_sign_in_ios example Exercises the iOS implementation of `GoogleSignInPlatform`. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/image_picker/image_picker/example/README.md b/packages/image_picker/image_picker/example/README.md index 129aa856c8f2..18497eb11032 100755 --- a/packages/image_picker/image_picker/example/README.md +++ b/packages/image_picker/image_picker/example/README.md @@ -1,8 +1,3 @@ # image_picker_example Demonstrates how to use the image_picker plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/image_picker/image_picker_android/example/README.md b/packages/image_picker/image_picker_android/example/README.md index 86d5c23ba209..16b5c51839f8 100755 --- a/packages/image_picker/image_picker_android/example/README.md +++ b/packages/image_picker/image_picker_android/example/README.md @@ -1,8 +1,3 @@ # image_picker_example Demonstrates how to use the `image_picker` plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/image_picker/image_picker_ios/example/README.md b/packages/image_picker/image_picker_ios/example/README.md index 86d5c23ba209..16b5c51839f8 100755 --- a/packages/image_picker/image_picker_ios/example/README.md +++ b/packages/image_picker/image_picker_ios/example/README.md @@ -1,8 +1,3 @@ # image_picker_example Demonstrates how to use the `image_picker` plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/image_picker/image_picker_windows/example/README.md b/packages/image_picker/image_picker_windows/example/README.md index ae730a5ec846..7f61053c6e30 100644 --- a/packages/image_picker/image_picker_windows/example/README.md +++ b/packages/image_picker/image_picker_windows/example/README.md @@ -1,8 +1,3 @@ # image_picker_windows_example Demonstrates how to use the image_picker_windows plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/ios_platform_images/example/README.md b/packages/ios_platform_images/example/README.md index 2f34abc202f2..91fc3baf5f49 100644 --- a/packages/ios_platform_images/example/README.md +++ b/packages/ios_platform_images/example/README.md @@ -1,16 +1,3 @@ # ios_platform_images_example Demonstrates how to use the ios_platform_images plugin. - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) - -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. diff --git a/packages/local_auth/local_auth/example/README.md b/packages/local_auth/local_auth/example/README.md index a4a6091c9ba6..bd004a77d86b 100644 --- a/packages/local_auth/local_auth/example/README.md +++ b/packages/local_auth/local_auth/example/README.md @@ -1,8 +1,3 @@ # local_auth_example Demonstrates how to use the local_auth plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/local_auth/local_auth_android/example/README.md b/packages/local_auth/local_auth_android/example/README.md index a4a6091c9ba6..bd004a77d86b 100644 --- a/packages/local_auth/local_auth_android/example/README.md +++ b/packages/local_auth/local_auth_android/example/README.md @@ -1,8 +1,3 @@ # local_auth_example Demonstrates how to use the local_auth plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/local_auth/local_auth_ios/example/README.md b/packages/local_auth/local_auth_ios/example/README.md index a4a6091c9ba6..bd004a77d86b 100644 --- a/packages/local_auth/local_auth_ios/example/README.md +++ b/packages/local_auth/local_auth_ios/example/README.md @@ -1,8 +1,3 @@ # local_auth_example Demonstrates how to use the local_auth plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/path_provider/path_provider/example/README.md b/packages/path_provider/path_provider/example/README.md index 1f8ea7189ccd..801f44409938 100644 --- a/packages/path_provider/path_provider/example/README.md +++ b/packages/path_provider/path_provider/example/README.md @@ -1,8 +1,3 @@ # path_provider_example Demonstrates how to use the path_provider plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/path_provider/path_provider_android/example/README.md b/packages/path_provider/path_provider_android/example/README.md index 1f8ea7189ccd..801f44409938 100644 --- a/packages/path_provider/path_provider_android/example/README.md +++ b/packages/path_provider/path_provider_android/example/README.md @@ -1,8 +1,3 @@ # path_provider_example Demonstrates how to use the path_provider plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/path_provider/path_provider_ios/example/README.md b/packages/path_provider/path_provider_ios/example/README.md index 1f8ea7189ccd..801f44409938 100644 --- a/packages/path_provider/path_provider_ios/example/README.md +++ b/packages/path_provider/path_provider_ios/example/README.md @@ -1,8 +1,3 @@ # path_provider_example Demonstrates how to use the path_provider plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/path_provider/path_provider_linux/example/README.md b/packages/path_provider/path_provider_linux/example/README.md index 751fe4b811f0..333d0f55cec7 100644 --- a/packages/path_provider/path_provider_linux/example/README.md +++ b/packages/path_provider/path_provider_linux/example/README.md @@ -1,16 +1,3 @@ # path_provider_linux_example Demonstrates how to use the path_provider_linux plugin. - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) - -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. diff --git a/packages/path_provider/path_provider_macos/example/README.md b/packages/path_provider/path_provider_macos/example/README.md index 4f413873b346..158869595c01 100644 --- a/packages/path_provider/path_provider_macos/example/README.md +++ b/packages/path_provider/path_provider_macos/example/README.md @@ -1,8 +1,3 @@ # path_provider_macos_example Demonstrates how to use the path_provider_macos plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/path_provider/path_provider_windows/example/README.md b/packages/path_provider/path_provider_windows/example/README.md index 32f66a86d11d..63723991a2e9 100644 --- a/packages/path_provider/path_provider_windows/example/README.md +++ b/packages/path_provider/path_provider_windows/example/README.md @@ -1,8 +1,3 @@ # path_provider_windows_example Demonstrates how to use the path_provider_windows plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/quick_actions/quick_actions/example/README.md b/packages/quick_actions/quick_actions/example/README.md index d1b72891de9e..c8a629019fc9 100644 --- a/packages/quick_actions/quick_actions/example/README.md +++ b/packages/quick_actions/quick_actions/example/README.md @@ -1,8 +1,3 @@ # quick_actions_example Demonstrates how to use the quick_actions plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/quick_actions/quick_actions_android/example/README.md b/packages/quick_actions/quick_actions_android/example/README.md index d1b72891de9e..c8a629019fc9 100644 --- a/packages/quick_actions/quick_actions_android/example/README.md +++ b/packages/quick_actions/quick_actions_android/example/README.md @@ -1,8 +1,3 @@ # quick_actions_example Demonstrates how to use the quick_actions plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/quick_actions/quick_actions_ios/example/README.md b/packages/quick_actions/quick_actions_ios/example/README.md index d1b72891de9e..c8a629019fc9 100644 --- a/packages/quick_actions/quick_actions_ios/example/README.md +++ b/packages/quick_actions/quick_actions_ios/example/README.md @@ -1,8 +1,3 @@ # quick_actions_example Demonstrates how to use the quick_actions plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/shared_preferences/shared_preferences/example/README.md b/packages/shared_preferences/shared_preferences/example/README.md index 7dd9e9c4aa42..c060637c7ec5 100644 --- a/packages/shared_preferences/shared_preferences/example/README.md +++ b/packages/shared_preferences/shared_preferences/example/README.md @@ -1,8 +1,3 @@ # shared_preferences_example Demonstrates how to use the shared_preferences plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/shared_preferences/shared_preferences_android/example/README.md b/packages/shared_preferences/shared_preferences_android/example/README.md index 7dd9e9c4aa42..c060637c7ec5 100644 --- a/packages/shared_preferences/shared_preferences_android/example/README.md +++ b/packages/shared_preferences/shared_preferences_android/example/README.md @@ -1,8 +1,3 @@ # shared_preferences_example Demonstrates how to use the shared_preferences plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/shared_preferences/shared_preferences_ios/example/README.md b/packages/shared_preferences/shared_preferences_ios/example/README.md index 7dd9e9c4aa42..c060637c7ec5 100644 --- a/packages/shared_preferences/shared_preferences_ios/example/README.md +++ b/packages/shared_preferences/shared_preferences_ios/example/README.md @@ -1,8 +1,3 @@ # shared_preferences_example Demonstrates how to use the shared_preferences plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/shared_preferences/shared_preferences_linux/example/README.md b/packages/shared_preferences/shared_preferences_linux/example/README.md index 7dd9e9c4aa42..c060637c7ec5 100644 --- a/packages/shared_preferences/shared_preferences_linux/example/README.md +++ b/packages/shared_preferences/shared_preferences_linux/example/README.md @@ -1,8 +1,3 @@ # shared_preferences_example Demonstrates how to use the shared_preferences plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/shared_preferences/shared_preferences_macos/example/README.md b/packages/shared_preferences/shared_preferences_macos/example/README.md index 7dd9e9c4aa42..c060637c7ec5 100644 --- a/packages/shared_preferences/shared_preferences_macos/example/README.md +++ b/packages/shared_preferences/shared_preferences_macos/example/README.md @@ -1,8 +1,3 @@ # shared_preferences_example Demonstrates how to use the shared_preferences plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/shared_preferences/shared_preferences_windows/example/README.md b/packages/shared_preferences/shared_preferences_windows/example/README.md index d85bb4107622..30c7f7e50c3b 100644 --- a/packages/shared_preferences/shared_preferences_windows/example/README.md +++ b/packages/shared_preferences/shared_preferences_windows/example/README.md @@ -1,16 +1,3 @@ # shared_preferences_windows_example Demonstrates how to use the shared_preferences_windows plugin. - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) - -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. diff --git a/packages/url_launcher/url_launcher/example/README.md b/packages/url_launcher/url_launcher/example/README.md index c200da8974d1..35b4bdb7031e 100644 --- a/packages/url_launcher/url_launcher/example/README.md +++ b/packages/url_launcher/url_launcher/example/README.md @@ -1,8 +1,3 @@ # url_launcher_example Demonstrates how to use the url_launcher plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/url_launcher/url_launcher_android/example/README.md b/packages/url_launcher/url_launcher_android/example/README.md index c200da8974d1..35b4bdb7031e 100644 --- a/packages/url_launcher/url_launcher_android/example/README.md +++ b/packages/url_launcher/url_launcher_android/example/README.md @@ -1,8 +1,3 @@ # url_launcher_example Demonstrates how to use the url_launcher plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/url_launcher/url_launcher_ios/example/README.md b/packages/url_launcher/url_launcher_ios/example/README.md index c200da8974d1..35b4bdb7031e 100644 --- a/packages/url_launcher/url_launcher_ios/example/README.md +++ b/packages/url_launcher/url_launcher_ios/example/README.md @@ -1,8 +1,3 @@ # url_launcher_example Demonstrates how to use the url_launcher plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/url_launcher/url_launcher_linux/example/README.md b/packages/url_launcher/url_launcher_linux/example/README.md index c200da8974d1..35b4bdb7031e 100644 --- a/packages/url_launcher/url_launcher_linux/example/README.md +++ b/packages/url_launcher/url_launcher_linux/example/README.md @@ -1,8 +1,3 @@ # url_launcher_example Demonstrates how to use the url_launcher plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/url_launcher/url_launcher_macos/example/README.md b/packages/url_launcher/url_launcher_macos/example/README.md index c200da8974d1..35b4bdb7031e 100644 --- a/packages/url_launcher/url_launcher_macos/example/README.md +++ b/packages/url_launcher/url_launcher_macos/example/README.md @@ -1,8 +1,3 @@ # url_launcher_example Demonstrates how to use the url_launcher plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/video_player/video_player/example/README.md b/packages/video_player/video_player/example/README.md index 8ceb0ff485fa..f5974e947c00 100644 --- a/packages/video_player/video_player/example/README.md +++ b/packages/video_player/video_player/example/README.md @@ -1,8 +1,3 @@ # video_player_example Demonstrates how to use the video_player plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/video_player/video_player_android/example/README.md b/packages/video_player/video_player_android/example/README.md index 8ceb0ff485fa..f5974e947c00 100644 --- a/packages/video_player/video_player_android/example/README.md +++ b/packages/video_player/video_player_android/example/README.md @@ -1,8 +1,3 @@ # video_player_example Demonstrates how to use the video_player plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/video_player/video_player_avfoundation/example/README.md b/packages/video_player/video_player_avfoundation/example/README.md index 8ceb0ff485fa..f5974e947c00 100644 --- a/packages/video_player/video_player_avfoundation/example/README.md +++ b/packages/video_player/video_player_avfoundation/example/README.md @@ -1,8 +1,3 @@ # video_player_example Demonstrates how to use the video_player plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/webview_flutter/webview_flutter/example/README.md b/packages/webview_flutter/webview_flutter/example/README.md index 850ee74397a9..e5bd6e20db63 100644 --- a/packages/webview_flutter/webview_flutter/example/README.md +++ b/packages/webview_flutter/webview_flutter/example/README.md @@ -1,8 +1,3 @@ # webview_flutter_example Demonstrates how to use the webview_flutter plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/webview_flutter/webview_flutter_android/example/README.md b/packages/webview_flutter/webview_flutter_android/example/README.md index 850ee74397a9..e5bd6e20db63 100644 --- a/packages/webview_flutter/webview_flutter_android/example/README.md +++ b/packages/webview_flutter/webview_flutter_android/example/README.md @@ -1,8 +1,3 @@ # webview_flutter_example Demonstrates how to use the webview_flutter plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/webview_flutter/webview_flutter_web/example/README.md b/packages/webview_flutter/webview_flutter_web/example/README.md index 850ee74397a9..e5bd6e20db63 100644 --- a/packages/webview_flutter/webview_flutter_web/example/README.md +++ b/packages/webview_flutter/webview_flutter_web/example/README.md @@ -1,8 +1,3 @@ # webview_flutter_example Demonstrates how to use the webview_flutter plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/README.md b/packages/webview_flutter/webview_flutter_wkwebview/example/README.md index 850ee74397a9..e5bd6e20db63 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/README.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/README.md @@ -1,8 +1,3 @@ # webview_flutter_example Demonstrates how to use the webview_flutter plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index a8a8268c047f..0e2a33e15eaf 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -2,6 +2,8 @@ - Fixes changelog validation when reverting to a `NEXT` state. - Fixes multiplication of `--force` flag when publishing multiple packages. +- Checks for template boilerplate in `readme-check`. +- `readme-check` now validates example READMEs when present. ## 0.8.5 diff --git a/script/tool/lib/src/common/package_looping_command.dart b/script/tool/lib/src/common/package_looping_command.dart index b48743be3170..a295215f3628 100644 --- a/script/tool/lib/src/common/package_looping_command.dart +++ b/script/tool/lib/src/common/package_looping_command.dart @@ -16,6 +16,20 @@ import 'plugin_command.dart'; import 'process_runner.dart'; import 'repository_package.dart'; +/// Enumeration options for package looping commands. +enum PackageLoopingType { + /// Only enumerates the top level packages, without including any of their + /// subpackages. + topLevelOnly, + + /// Enumerates the top level packages and any example packages they contain. + includeExamples, + + /// Enumerates all packages recursively, including both example and + /// non-example subpackages. + includeAllSubpackages, +} + /// Possible outcomes of a command run for a package. enum RunState { /// The command succeeded for the package. @@ -109,9 +123,26 @@ abstract class PackageLoopingCommand extends PluginCommand { /// Note: Consistent behavior across commands whenever possibel is a goal for /// this tool, so this should be overridden only in rare cases. Stream getPackagesToProcess() async* { - yield* includeSubpackages - ? getTargetPackagesAndSubpackages(filterExcluded: false) - : getTargetPackages(filterExcluded: false); + switch (packageLoopingType) { + case PackageLoopingType.topLevelOnly: + yield* getTargetPackages(filterExcluded: false); + break; + case PackageLoopingType.includeExamples: + await for (final PackageEnumerationEntry packageEntry + in getTargetPackages(filterExcluded: false)) { + yield packageEntry; + yield* Stream.fromIterable(packageEntry + .package + .getExamples() + .map((RepositoryPackage package) => PackageEnumerationEntry( + package, + excluded: packageEntry.excluded))); + } + break; + case PackageLoopingType.includeAllSubpackages: + yield* getTargetPackagesAndSubpackages(filterExcluded: false); + break; + } } /// Runs the command for [package], returning a list of errors. @@ -140,9 +171,9 @@ abstract class PackageLoopingCommand extends PluginCommand { /// to make the output structure easier to follow. bool get hasLongOutput => true; - /// Whether to loop over all packages (e.g., including example/), rather than - /// only top-level packages. - bool get includeSubpackages => false; + /// Whether to loop over top-level packages only, or some or all of their + /// sub-packages as well. + PackageLoopingType get packageLoopingType => PackageLoopingType.topLevelOnly; /// The text to output at the start when reporting one or more failures. /// This will be followed by a list of packages that reported errors, with diff --git a/script/tool/lib/src/pubspec_check_command.dart b/script/tool/lib/src/pubspec_check_command.dart index 23c9c00e33f0..3598a39da960 100644 --- a/script/tool/lib/src/pubspec_check_command.dart +++ b/script/tool/lib/src/pubspec_check_command.dart @@ -64,7 +64,8 @@ class PubspecCheckCommand extends PackageLoopingCommand { bool get hasLongOutput => false; @override - bool get includeSubpackages => true; + PackageLoopingType get packageLoopingType => + PackageLoopingType.includeAllSubpackages; @override Future runForPackage(RepositoryPackage package) async { diff --git a/script/tool/lib/src/readme_check_command.dart b/script/tool/lib/src/readme_check_command.dart index 0cb64920dea4..6e79b736781f 100644 --- a/script/tool/lib/src/readme_check_command.dart +++ b/script/tool/lib/src/readme_check_command.dart @@ -54,34 +54,72 @@ class ReadmeCheckCommand extends PackageLoopingCommand { @override Future runForPackage(RepositoryPackage package) async { - final File readme = package.readmeFile; + final List errors = _validateReadme(package.readmeFile, + mainPackage: package, isExample: false); + for (final RepositoryPackage packageToCheck in package.getExamples()) { + errors.addAll(_validateReadme(packageToCheck.readmeFile, + mainPackage: package, isExample: true)); + } - if (!readme.existsSync()) { - return PackageResult.fail(['Missing README.md']); + // If there's an example/README.md for a multi-example package, validate + // that as well, as it will be shown on pub.dev. + final Directory exampleDir = package.directory.childDirectory('example'); + final File exampleDirReadme = exampleDir.childFile('README.md'); + if (exampleDir.existsSync() && !isPackage(exampleDir)) { + errors.addAll(_validateReadme(exampleDirReadme, + mainPackage: package, isExample: true)); } - final List errors = []; + return errors.isEmpty + ? PackageResult.success() + : PackageResult.fail(errors); + } + + List _validateReadme(File readme, + {required RepositoryPackage mainPackage, required bool isExample}) { + if (!readme.existsSync()) { + if (isExample) { + print('${indentation}No README for ' + '${getRelativePosixPath(readme.parent, from: mainPackage.directory)}'); + return []; + } else { + printError('${indentation}No README found at ' + '${getRelativePosixPath(readme, from: mainPackage.directory)}'); + return ['Missing README.md']; + } + } - final Pubspec pubspec = package.parsePubspec(); - final bool isPlugin = pubspec.flutter?['plugin'] != null; + print('${indentation}Checking ' + '${getRelativePosixPath(readme, from: mainPackage.directory)}...'); - final List readmeLines = package.readmeFile.readAsLinesSync(); + final List readmeLines = readme.readAsLinesSync(); + final List errors = []; final String? blockValidationError = _validateCodeBlocks(readmeLines); if (blockValidationError != null) { errors.add(blockValidationError); } - if (isPlugin && (!package.isFederated || package.isAppFacing)) { - final String? error = _validateSupportedPlatforms(readmeLines, pubspec); - if (error != null) { - errors.add(error); + if (_containsTemplateBoilerplate(readmeLines)) { + printError('${indentation}The boilerplate section about getting started ' + 'with Flutter should not be left in.'); + errors.add('Contains template boilerplate'); + } + + // Check if this is the main readme for a plugin, and if so enforce extra + // checks. + if (!isExample) { + final Pubspec pubspec = mainPackage.parsePubspec(); + final bool isPlugin = pubspec.flutter?['plugin'] != null; + if (isPlugin && (!mainPackage.isFederated || mainPackage.isAppFacing)) { + final String? error = _validateSupportedPlatforms(readmeLines, pubspec); + if (error != null) { + errors.add(error); + } } } - return errors.isEmpty - ? PackageResult.success() - : PackageResult.fail(errors); + return errors; } /// Validates that code blocks (``` ... ```) follow repository standards. @@ -223,4 +261,11 @@ ${indentation * 2}Please use standard capitalizations: ${sortedListString(expect // https://github.com/flutter/flutter/issues/84200 return null; } + + /// Returns true if the README still has the boilerplate from the + /// `flutter create` templates. + bool _containsTemplateBoilerplate(List readmeLines) { + return readmeLines.any((String line) => + line.contains('For help getting started with Flutter')); + } } diff --git a/script/tool/lib/src/test_command.dart b/script/tool/lib/src/test_command.dart index 27a01c95e851..5101b8f19e7e 100644 --- a/script/tool/lib/src/test_command.dart +++ b/script/tool/lib/src/test_command.dart @@ -37,7 +37,8 @@ class TestCommand extends PackageLoopingCommand { 'This command requires "flutter" to be in your path.'; @override - bool get includeSubpackages => true; + PackageLoopingType get packageLoopingType => + PackageLoopingType.includeAllSubpackages; @override Future runForPackage(RepositoryPackage package) async { diff --git a/script/tool/test/common/package_looping_command_test.dart b/script/tool/test/common/package_looping_command_test.dart index 3dd6a47ae2fb..a7e7dfdf6ebb 100644 --- a/script/tool/test/common/package_looping_command_test.dart +++ b/script/tool/test/common/package_looping_command_test.dart @@ -98,7 +98,7 @@ void main() { TestPackageLoopingCommand createTestCommand({ String gitDiffResponse = '', bool hasLongOutput = true, - bool includeSubpackages = false, + PackageLoopingType packageLoopingType = PackageLoopingType.topLevelOnly, bool failsDuringInit = false, bool warnsDuringInit = false, bool warnsDuringCleanup = false, @@ -122,7 +122,7 @@ void main() { packagesDir, platform: mockPlatform, hasLongOutput: hasLongOutput, - includeSubpackages: includeSubpackages, + packageLoopingType: packageLoopingType, failsDuringInit: failsDuringInit, warnsDuringInit: warnsDuringInit, warnsDuringCleanup: warnsDuringCleanup, @@ -236,14 +236,17 @@ void main() { unorderedEquals([package1.path, package2.path])); }); - test('includes subpackages when requested', () async { + test('includes all subpackages when requested', () async { final RepositoryPackage plugin = createFakePlugin('a_plugin', packagesDir, examples: ['example1', 'example2']); final RepositoryPackage package = createFakePackage('a_package', packagesDir); + final RepositoryPackage subPackage = createFakePackage( + 'sub_package', package.directory, + examples: []); - final TestPackageLoopingCommand command = - createTestCommand(includeSubpackages: true); + final TestPackageLoopingCommand command = createTestCommand( + packageLoopingType: PackageLoopingType.includeAllSubpackages); await runCommand(command); expect( @@ -254,18 +257,72 @@ void main() { getExampleDir(plugin).childDirectory('example2').path, package.path, getExampleDir(package).path, + subPackage.path, ])); }); + test('includes examples when requested', () async { + final RepositoryPackage plugin = createFakePlugin('a_plugin', packagesDir, + examples: ['example1', 'example2']); + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); + final RepositoryPackage subPackage = + createFakePackage('sub_package', package.directory); + + final TestPackageLoopingCommand command = createTestCommand( + packageLoopingType: PackageLoopingType.includeExamples); + await runCommand(command); + + expect( + command.checkedPackages, + unorderedEquals([ + plugin.path, + getExampleDir(plugin).childDirectory('example1').path, + getExampleDir(plugin).childDirectory('example2').path, + package.path, + getExampleDir(package).path, + ])); + expect(command.checkedPackages, isNot(contains(subPackage.path))); + }); + test('excludes subpackages when main package is excluded', () async { final RepositoryPackage excluded = createFakePlugin( 'a_plugin', packagesDir, examples: ['example1', 'example2']); final RepositoryPackage included = createFakePackage('a_package', packagesDir); + final RepositoryPackage subpackage = + createFakePackage('sub_package', excluded.directory); - final TestPackageLoopingCommand command = - createTestCommand(includeSubpackages: true); + final TestPackageLoopingCommand command = createTestCommand( + packageLoopingType: PackageLoopingType.includeAllSubpackages); + await runCommand(command, arguments: ['--exclude=a_plugin']); + + final Iterable examples = excluded.getExamples(); + + expect( + command.checkedPackages, + unorderedEquals([ + included.path, + getExampleDir(included).path, + ])); + expect(command.checkedPackages, isNot(contains(excluded.path))); + expect(examples.length, 2); + for (final RepositoryPackage example in examples) { + expect(command.checkedPackages, isNot(contains(example.path))); + } + expect(command.checkedPackages, isNot(contains(subpackage.path))); + }); + + test('excludes examples when main package is excluded', () async { + final RepositoryPackage excluded = createFakePlugin( + 'a_plugin', packagesDir, + examples: ['example1', 'example2']); + final RepositoryPackage included = + createFakePackage('a_package', packagesDir); + + final TestPackageLoopingCommand command = createTestCommand( + packageLoopingType: PackageLoopingType.includeExamples); await runCommand(command, arguments: ['--exclude=a_plugin']); final Iterable examples = excluded.getExamples(); @@ -290,8 +347,9 @@ void main() { final RepositoryPackage included = createFakePackage('a_package', packagesDir); - final TestPackageLoopingCommand command = - createTestCommand(includeSubpackages: true, hasLongOutput: false); + final TestPackageLoopingCommand command = createTestCommand( + packageLoopingType: PackageLoopingType.includeAllSubpackages, + hasLongOutput: false); final List output = await runCommand(command, arguments: [ '--skip-if-not-supporting-flutter-version=2.5.0' ]); @@ -769,7 +827,7 @@ class TestPackageLoopingCommand extends PackageLoopingCommand { Directory packagesDir, { required Platform platform, this.hasLongOutput = true, - this.includeSubpackages = false, + this.packageLoopingType = PackageLoopingType.topLevelOnly, this.customFailureListHeader, this.customFailureListFooter, this.failsDuringInit = false, @@ -795,7 +853,7 @@ class TestPackageLoopingCommand extends PackageLoopingCommand { bool hasLongOutput; @override - bool includeSubpackages; + PackageLoopingType packageLoopingType; @override String get failureListHeader => diff --git a/script/tool/test/list_command_test.dart b/script/tool/test/list_command_test.dart index c2042c26638c..f74431c5cee7 100644 --- a/script/tool/test/list_command_test.dart +++ b/script/tool/test/list_command_test.dart @@ -101,15 +101,18 @@ void main() { '/packages/plugin1/pubspec.yaml', '/packages/plugin1/AUTHORS', '/packages/plugin1/CHANGELOG.md', + '/packages/plugin1/README.md', '/packages/plugin1/example/pubspec.yaml', '/packages/plugin2/pubspec.yaml', '/packages/plugin2/AUTHORS', '/packages/plugin2/CHANGELOG.md', + '/packages/plugin2/README.md', '/packages/plugin2/example/example1/pubspec.yaml', '/packages/plugin2/example/example2/pubspec.yaml', '/packages/plugin3/pubspec.yaml', '/packages/plugin3/AUTHORS', '/packages/plugin3/CHANGELOG.md', + '/packages/plugin3/README.md', ]), ); }); diff --git a/script/tool/test/readme_check_command_test.dart b/script/tool/test/readme_check_command_test.dart index f53fa06a8133..fa4fc604dd73 100644 --- a/script/tool/test/readme_check_command_test.dart +++ b/script/tool/test/readme_check_command_test.dart @@ -37,8 +37,33 @@ void main() { runner.addCommand(command); }); - test('fails when README is missing', () async { - createFakePackage('a_package', packagesDir); + test('prints paths of checked READMEs', () async { + final RepositoryPackage package = createFakePackage( + 'a_package', packagesDir, + examples: ['example1', 'example2']); + for (final RepositoryPackage example in package.getExamples()) { + example.readmeFile.writeAsStringSync('A readme'); + } + getExampleDir(package).childFile('README.md').writeAsStringSync('A readme'); + + final List output = + await runCapturingPrint(runner, ['readme-check']); + + expect( + output, + containsAll([ + contains(' Checking README.md...'), + contains(' Checking example/README.md...'), + contains(' Checking example/example1/README.md...'), + contains(' Checking example/example2/README.md...'), + ]), + ); + }); + + test('fails when package README is missing', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); + package.readmeFile.deleteSync(); Error? commandError; final List output = await runCapturingPrint( @@ -55,6 +80,143 @@ void main() { ); }); + test('passes when example README is missing', () async { + createFakePackage('a_package', packagesDir); + + final List output = + await runCapturingPrint(runner, ['readme-check']); + + expect( + output, + containsAllInOrder([ + contains('No README for example'), + ]), + ); + }); + + test('does not inculde non-example subpackages', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); + const String subpackageName = 'special_test'; + final RepositoryPackage miscSubpackage = + createFakePackage(subpackageName, package.directory); + miscSubpackage.readmeFile.delete(); + + final List output = + await runCapturingPrint(runner, ['readme-check']); + + expect(output, isNot(contains(subpackageName))); + }); + + test('fails when README still has plugin template boilerplate', () async { + final RepositoryPackage package = createFakePlugin('a_plugin', packagesDir); + package.readmeFile.writeAsStringSync(''' +## Getting Started + +This project is a starting point for a Flutter +[plug-in package](https://flutter.dev/developing-packages/), +a specialized package that includes platform-specific implementation code for +Android and/or iOS. + +For help getting started with Flutter development, view the +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The boilerplate section about getting started with Flutter ' + 'should not be left in.'), + contains('Contains template boilerplate'), + ]), + ); + }); + + test('fails when example README still has application template boilerplate', + () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); + package.getExamples().first.readmeFile.writeAsStringSync(''' +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The boilerplate section about getting started with Flutter ' + 'should not be left in.'), + contains('Contains template boilerplate'), + ]), + ); + }); + + test( + 'fails when multi-example top-level example directory README still has ' + 'application template boilerplate', () async { + final RepositoryPackage package = createFakePackage( + 'a_package', packagesDir, + examples: ['example1', 'example2']); + package.directory + .childDirectory('example') + .childFile('README.md') + .writeAsStringSync(''' +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The boilerplate section about getting started with Flutter ' + 'should not be left in.'), + contains('Contains template boilerplate'), + ]), + ); + }); + group('plugin OS support', () { test( 'does not check support table for anything other than app-facing plugin packages', @@ -62,20 +224,12 @@ void main() { const String federatedPluginName = 'a_federated_plugin'; final Directory federatedDir = packagesDir.childDirectory(federatedPluginName); - final List packages = [ - // A non-plugin package. - createFakePackage('a_package', packagesDir), - // Non-app-facing parts of a federated plugin. - createFakePlugin( - '${federatedPluginName}_platform_interface', federatedDir), - createFakePlugin('${federatedPluginName}_android', federatedDir), - ]; - - for (final RepositoryPackage package in packages) { - package.readmeFile.writeAsStringSync(''' -A very useful package. -'''); - } + // A non-plugin package. + createFakePackage('a_package', packagesDir); + // Non-app-facing parts of a federated plugin. + createFakePlugin( + '${federatedPluginName}_platform_interface', federatedDir); + createFakePlugin('${federatedPluginName}_android', federatedDir); final List output = await runCapturingPrint(runner, [ 'readme-check', @@ -94,12 +248,7 @@ A very useful package. test('fails when non-federated plugin is missing an OS support table', () async { - final RepositoryPackage plugin = - createFakePlugin('a_plugin', packagesDir); - - plugin.readmeFile.writeAsStringSync(''' -A very useful plugin. -'''); + createFakePlugin('a_plugin', packagesDir); Error? commandError; final List output = await runCapturingPrint( @@ -119,12 +268,7 @@ A very useful plugin. test( 'fails when app-facing part of a federated plugin is missing an OS support table', () async { - final RepositoryPackage plugin = - createFakePlugin('a_plugin', packagesDir.childDirectory('a_plugin')); - - plugin.readmeFile.writeAsStringSync(''' -A very useful plugin. -'''); + createFakePlugin('a_plugin', packagesDir.childDirectory('a_plugin')); Error? commandError; final List output = await runCapturingPrint( diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart index effdd03891dc..7255bf9a9276 100644 --- a/script/tool/test/util.dart +++ b/script/tool/test/util.dart @@ -125,7 +125,7 @@ RepositoryPackage createFakePlugin( /// separators, of extra files to create in the package. /// /// If [includeCommonFiles] is true, common but non-critical files like -/// CHANGELOG.md and AUTHORS will be included. +/// CHANGELOG.md, README.md, and AUTHORS will be included. /// /// If non-null, [directoryName] will be used for the directory instead of /// [name]. @@ -152,11 +152,12 @@ RepositoryPackage createFakePackage( version: version, flutterConstraint: flutterConstraint); if (includeCommonFiles) { - createFakeCHANGELOG(package, ''' + package.changelogFile.writeAsStringSync(''' ## $version * Some changes. '''); - createFakeAuthors(package); + package.readmeFile.writeAsStringSync('A very useful package'); + package.authorsFile.writeAsStringSync('Google Inc.'); } if (examples.length == 1) { @@ -188,11 +189,6 @@ RepositoryPackage createFakePackage( return package; } -void createFakeCHANGELOG(RepositoryPackage package, String texts) { - package.changelogFile.createSync(); - package.changelogFile.writeAsStringSync(texts); -} - /// Creates a `pubspec.yaml` file with a flutter dependency. /// /// [platformSupport] is a map of platform string to the support details for @@ -267,11 +263,6 @@ $pluginSection package.pubspecFile.writeAsStringSync(yaml); } -void createFakeAuthors(RepositoryPackage package) { - package.authorsFile.createSync(); - package.authorsFile.writeAsStringSync('Google Inc.'); -} - String _pluginPlatformSection( String platform, PlatformDetails support, String packageName) { String entry = ''; diff --git a/script/tool/test/version_check_command_test.dart b/script/tool/test/version_check_command_test.dart index 6af3c112f9eb..a310f0f09fcb 100644 --- a/script/tool/test/version_check_command_test.dart +++ b/script/tool/test/version_check_command_test.dart @@ -421,7 +421,7 @@ This is necessary because of X, Y, and Z ## $version * Some changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); final List output = await runCapturingPrint( runner, ['version-check', '--base-sha=main']); expect( @@ -439,7 +439,7 @@ This is necessary because of X, Y, and Z ## 1.0.2 * Some changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); Error? commandError; final List output = await runCapturingPrint( runner, ['version-check', '--base-sha=main', '--against-pub'], @@ -465,7 +465,7 @@ This is necessary because of X, Y, and Z ## $version * Some changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); final List output = await runCapturingPrint( runner, ['version-check', '--base-sha=main']); expect( @@ -488,7 +488,7 @@ This is necessary because of X, Y, and Z ## 1.0.0 * Some other changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); bool hasError = false; final List output = await runCapturingPrint( runner, ['version-check', '--base-sha=main', '--against-pub'], @@ -518,7 +518,7 @@ This is necessary because of X, Y, and Z ## $version * Some other changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -547,7 +547,7 @@ This is necessary because of X, Y, and Z ## 1.0.0 * Some other changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); bool hasError = false; final List output = await runCapturingPrint( runner, ['version-check', '--base-sha=main', '--against-pub'], @@ -580,7 +580,7 @@ This is necessary because of X, Y, and Z ## 1.0.0 * Some other changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); bool hasError = false; final List output = await runCapturingPrint( @@ -612,7 +612,7 @@ This is necessary because of X, Y, and Z ## 1.0.0 * Some other changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); bool hasError = false; final List output = await runCapturingPrint( @@ -642,8 +642,8 @@ This is necessary because of X, Y, and Z ## 1.0.0 * Some other changes. '''; - createFakeCHANGELOG(plugin, changelog); - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); + plugin.changelogFile.writeAsStringSync(changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.1'), ]; @@ -671,7 +671,7 @@ This is necessary because of X, Y, and Z # 1.0.0 * Some other changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -704,7 +704,7 @@ This is necessary because of X, Y, and Z ## Alpha * Some changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -749,7 +749,7 @@ This is necessary because of X, Y, and Z ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -778,7 +778,7 @@ This is necessary because of X, Y, and Z ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -813,7 +813,7 @@ packages/plugin/lib/plugin.dart ## 1.0.1 * Some changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -844,7 +844,7 @@ packages/plugin/pubspec.yaml ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -874,7 +874,7 @@ tool/plugin/lib/plugin.dart ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -907,7 +907,7 @@ packages/plugin/CHANGELOG.md ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -948,7 +948,7 @@ No version change: Code change is only to implementation comments. ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -983,7 +983,7 @@ packages/plugin/example/lib/foo.dart ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -1014,7 +1014,7 @@ packages/plugin/CHANGELOG.md ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -1048,7 +1048,7 @@ packages/another_plugin/CHANGELOG.md ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; From 364c53f8a37d1dc3260b1f3373336ef8704608f0 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Wed, 18 May 2022 15:32:09 +0200 Subject: [PATCH 578/600] [webview_flutter] Initial v4.0 platform interface implementation (#5109) --- .../CHANGELOG.md | 3 +- .../v4/src/platform_navigation_delegate.dart | 89 ++++ .../v4/src/platform_webview_controller.dart | 285 +++++++++++ .../src/platform_webview_cookie_manager.dart | 55 +++ .../lib/v4/src/platform_webview_widget.dart | 37 ++ .../lib/v4/src/types/javascript_message.dart | 51 ++ .../lib/v4/src/types/javascript_mode.dart | 12 + .../lib/v4/src/types/load_request_params.dart | 89 ++++ ...m_navigation_delegate_creation_params.dart | 44 ++ ...rm_webview_controller_creation_params.dart | 45 ++ ...ebview_cookie_manager_creation_params.dart | 45 ++ ...atform_webview_widget_creation_params.dart | 79 +++ .../lib/v4/src/types/types.dart | 13 + .../lib/v4/src/types/web_resource_error.dart | 119 +++++ .../lib/v4/src/types/webview_cookie.dart | 41 ++ .../lib/v4/src/webview_platform.dart | 82 +++ .../webview_flutter_platform_interface.dart | 10 + .../pubspec.yaml | 4 +- .../v4/platform_navigation_delegate_test.dart | 141 ++++++ .../v4/platform_webview_controller_test.dart | 467 ++++++++++++++++++ ...latform_webview_controller_test.mocks.dart | 72 +++ .../src/v4/platform_webview_widget_test.dart | 89 ++++ .../test/src/v4/webview_platform_test.dart | 109 ++++ .../src/v4/webview_platform_test.mocks.dart | 78 +++ 24 files changed, 2057 insertions(+), 2 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_navigation_delegate.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_controller.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_cookie_manager.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_widget.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/javascript_message.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/javascript_mode.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/load_request_params.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_navigation_delegate_creation_params.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_webview_controller_creation_params.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_webview_cookie_manager_creation_params.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_webview_widget_creation_params.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/types.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/web_resource_error.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/webview_cookie.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/webview_platform.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/webview_flutter_platform_interface.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_navigation_delegate_test.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_controller_test.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_controller_test.mocks.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_widget_test.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/webview_platform_test.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/webview_platform_test.mocks.dart diff --git a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md index c7462ddd47d0..8f6c413ce0cc 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 1.9.0 +* Adds the first iteration of the v4 webview_flutter interface implementation. * Removes unnecessary imports. ## 1.8.2 diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_navigation_delegate.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_navigation_delegate.dart new file mode 100644 index 000000000000..a66f1defdf60 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_navigation_delegate.dart @@ -0,0 +1,89 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'webview_platform.dart'; + +/// An interface defining navigation events that occur on the native platform. +/// +/// The [PlatformWebViewController] is notifying this delegate on events that +/// happened on the platform's webview. Platform implementations should +/// implement this class and pass an instance to the [PlatformWebViewController]. +abstract class PlatformNavigationDelegate extends PlatformInterface { + /// Creates a new [PlatformNavigationDelegate] + factory PlatformNavigationDelegate( + PlatformNavigationDelegateCreationParams params) { + final PlatformNavigationDelegate callbackDelegate = + WebViewPlatform.instance!.createPlatformNavigationDelegate(params); + PlatformInterface.verify(callbackDelegate, _token); + return callbackDelegate; + } + + /// Used by the platform implementation to create a new [PlatformNavigationDelegate]. + /// + /// Should only be used by platform implementations because they can't extend + /// a class that only contains a factory constructor. + @protected + PlatformNavigationDelegate.implementation(this.params) : super(token: _token); + + static final Object _token = Object(); + + /// The parameters used to initialize the [PlatformNavigationDelegate]. + final PlatformNavigationDelegateCreationParams params; + + /// Invoked when a navigation request is pending. + /// + /// See [PlatformWebViewController.setPlatformNavigationDelegate]. + Future setOnNavigationRequest( + FutureOr Function({required String url, required bool isForMainFrame}) + onNavigationRequest, + ) { + throw UnimplementedError( + 'setOnNavigationRequest is not implemented on the current platform.'); + } + + /// Invoked when a page has started loading. + /// + /// See [PlatformWebViewController.setPlatformNavigationDelegate]. + Future setOnPageStarted( + void Function(String url) onPageStarted, + ) { + throw UnimplementedError( + 'setOnPageStarted is not implemented on the current platform.'); + } + + /// Invoked when a page has finished loading. + /// + /// See [PlatformWebViewController.setPlatformNavigationDelegate]. + Future setOnPageFinished( + void Function(String url) onPageFinished, + ) { + throw UnimplementedError( + 'setOnPageFinished is not implemented on the current platform.'); + } + + /// Invoked when a page is loading to report the progress. + /// + /// See [PlatformWebViewController.setPlatformNavigationDelegate]. + Future setOnProgress( + void Function(int progress) onProgress, + ) { + throw UnimplementedError( + 'setOnProgress is not implemented on the current platform.'); + } + + /// Invoked when a resource loading error occurred. + /// + /// See [PlatformWebViewController.setPlatformNavigationDelegate]. + Future setOnWebResourceError( + void Function(WebResourceError error) onWebResourceError, + ) { + throw UnimplementedError( + 'setOnWebResourceError is not implemented on the current platform.'); + } +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_controller.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_controller.dart new file mode 100644 index 000000000000..3585ec8b1886 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_controller.dart @@ -0,0 +1,285 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:math'; +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'platform_navigation_delegate.dart'; +import 'webview_platform.dart'; + +/// Interface for a platform implementation of a web view controller. +/// +/// Platform implementations should extend this class rather than implement it +/// as `webview_flutter` does not consider newly added methods to be breaking +/// changes. Extending this class (using `extends`) ensures that the subclass +/// will get the default implementation, while platform implementations that +/// `implements` this interface will be broken by newly added +/// [PlatformWebViewCookieManager] methods. +abstract class PlatformWebViewController extends PlatformInterface { + /// Creates a new [PlatformWebViewController] + factory PlatformWebViewController( + PlatformWebViewControllerCreationParams params) { + final PlatformWebViewController webViewControllerDelegate = + WebViewPlatform.instance!.createPlatformWebViewController(params); + PlatformInterface.verify(webViewControllerDelegate, _token); + return webViewControllerDelegate; + } + + /// Used by the platform implementation to create a new [PlatformWebViewController]. + /// + /// Should only be used by platform implementations because they can't extend + /// a class that only contains a factory constructor. + @protected + PlatformWebViewController.implementation(this.params) : super(token: _token); + + static final Object _token = Object(); + + /// The parameters used to initialize the [PlatformWebViewController]. + final PlatformWebViewControllerCreationParams params; + + /// Loads the file located on the specified [absoluteFilePath]. + /// + /// The [absoluteFilePath] parameter should contain the absolute path to the + /// file as it is stored on the device. For example: + /// `/Users/username/Documents/www/index.html`. + /// + /// Throws an ArgumentError if the [absoluteFilePath] does not exist. + Future loadFile( + String absoluteFilePath, + ) { + throw UnimplementedError( + 'loadFile is not implemented on the current platform'); + } + + /// Loads the Flutter asset specified in the pubspec.yaml file. + /// + /// Throws an ArgumentError if [key] is not part of the specified assets + /// in the pubspec.yaml file. + Future loadFlutterAsset( + String key, + ) { + throw UnimplementedError( + 'loadFlutterAsset is not implemented on the current platform'); + } + + /// Loads the supplied HTML string. + /// + /// The [baseUrl] parameter is used when resolving relative URLs within the + /// HTML string. + Future loadHtmlString( + String html, { + String? baseUrl, + }) { + throw UnimplementedError( + 'loadHtmlString is not implemented on the current platform'); + } + + /// Makes a specific HTTP request ands loads the response in the webview. + /// + /// [WebViewRequest.method] must be one of the supported HTTP methods + /// in [WebViewRequestMethod]. + /// + /// If [WebViewRequest.headers] is not empty, its key-value pairs will be + /// added as the headers for the request. + /// + /// If [WebViewRequest.body] is not null, it will be added as the body + /// for the request. + /// + /// Throws an ArgumentError if [WebViewRequest.uri] has empty scheme. + Future loadRequest( + LoadRequestParams params, + ) { + throw UnimplementedError( + 'loadRequest is not implemented on the current platform'); + } + + /// Accessor to the current URL that the WebView is displaying. + /// + /// If no URL was ever loaded, returns `null`. + Future currentUrl() { + throw UnimplementedError( + 'currentUrl is not implemented on the current platform'); + } + + /// Checks whether there's a back history item. + Future canGoBack() { + throw UnimplementedError( + 'canGoBack is not implemented on the current platform'); + } + + /// Checks whether there's a forward history item. + Future canGoForward() { + throw UnimplementedError( + 'canGoForward is not implemented on the current platform'); + } + + /// Goes back in the history of this WebView. + /// + /// If there is no back history item this is a no-op. + Future goBack() { + throw UnimplementedError( + 'goBack is not implemented on the current platform'); + } + + /// Goes forward in the history of this WebView. + /// + /// If there is no forward history item this is a no-op. + Future goForward() { + throw UnimplementedError( + 'goForward is not implemented on the current platform'); + } + + /// Reloads the current URL. + Future reload() { + throw UnimplementedError( + 'reload is not implemented on the current platform'); + } + + /// Clears all caches used by the [WebView]. + /// + /// The following caches are cleared: + /// 1. Browser HTTP Cache. + /// 2. [Cache API](https://developers.google.com/web/fundamentals/instant-and-offline/web-storage/cache-api) caches. + /// These are not yet supported in iOS WkWebView. Service workers tend to use this cache. + /// 3. Application cache. + Future clearCache() { + throw UnimplementedError( + 'clearCache is not implemented on the current platform'); + } + + /// Clears the local storage used by the [WebView]. + Future clearLocalStorage() { + throw UnimplementedError( + 'clearLocalStorage is not implemented on the current platform'); + } + + /// Sets the [PlatformNavigationDelegate] containing the callback methods that + /// are called during navigation events. + Future setPlatformNavigationDelegate( + PlatformNavigationDelegate handler) { + throw UnimplementedError( + 'setPlatformNavigationDelegate is not implemented on the current platform'); + } + + /// Runs the given JavaScript in the context of the current page. + /// + /// The Future completes with an error if a JavaScript error occurred. + Future runJavaScript(String javaScript) { + throw UnimplementedError( + 'runJavaScript is not implemented on the current platform'); + } + + /// Runs the given JavaScript in the context of the current page, and returns the result. + /// + /// The Future completes with an error if a JavaScript error occurred, or if the + /// type the given expression evaluates to is unsupported. Unsupported values include + /// certain non-primitive types on iOS, as well as `undefined` or `null` on iOS 14+. + Future runJavaScriptReturningResult(String javaScript) { + throw UnimplementedError( + 'runJavaScriptReturningResult is not implemented on the current platform'); + } + + /// Adds a new JavaScript channel to the set of enabled channels. + Future addJavaScriptChannel( + JavaScriptChannelParams javaScriptChannelParams, + ) { + throw UnimplementedError( + 'addJavaScriptChannel is not implemented on the current platform'); + } + + /// Removes the JavaScript channel with the matching name from the set of + /// enabled channels. + /// + /// This disables the channel with the matching name if it was previously + /// enabled through the [addJavaScriptChannel]. + Future removeJavaScriptChannel(String javaScriptChannelName) { + throw UnimplementedError( + 'removeJavaScriptChannel is not implemented on the current platform'); + } + + /// Returns the title of the currently loaded page. + Future getTitle() { + throw UnimplementedError( + 'getTitle is not implemented on the current platform'); + } + + /// Set the scrolled position of this view. + /// + /// The parameters `x` and `y` specify the position to scroll to in WebView pixels. + Future scrollTo(int x, int y) { + throw UnimplementedError( + 'scrollTo is not implemented on the current platform'); + } + + /// Move the scrolled position of this view. + /// + /// The parameters `x` and `y` specify the amount of WebView pixels to scroll by. + Future scrollBy(int x, int y) { + throw UnimplementedError( + 'scrollBy is not implemented on the current platform'); + } + + /// Return the current scroll position of this view. + /// + /// Scroll position is measured from the top left. + Future> getScrollPosition() { + throw UnimplementedError( + 'getScrollPosition is not implemented on the current platform'); + } + + /// Whether to enable the platform's webview content debugging tools. + Future enableDebugging(bool enabled) { + throw UnimplementedError( + 'enableDebugging is not implemented on the current platform'); + } + + /// Whether to allow swipe based navigation on supported platforms. + Future enableGestureNavigation(bool enabled) { + throw UnimplementedError( + 'enableGestureNavigation is not implemented on the current platform'); + } + + /// Whhether to support zooming using its on-screen zoom controls and gestures. + Future enableZoom(bool enabled) { + throw UnimplementedError( + 'enableZoom is not implemented on the current platform'); + } + + /// Set the current background color of this view. + Future setBackgroundColor(Color color) { + throw UnimplementedError( + 'setBackgroundColor is not implemented on the current platform'); + } + + /// Sets the JavaScript execution mode to be used by the webview. + Future setJavaScriptMode(JavaScriptMode javaScriptMode) { + throw UnimplementedError( + 'setJavaScriptMode is not implemented on the current platform'); + } + + /// Sets the value used for the HTTP `User-Agent:` request header. + Future setUserAgent(String? userAgent) { + throw UnimplementedError( + 'setUserAgent is not implemented on the current platform'); + } +} + +/// Describes the parameters necessary for registering a JavaScript channel. +class JavaScriptChannelParams { + /// Creates a new [JavaScriptChannelParams] object. + JavaScriptChannelParams({ + required this.name, + required this.onMessageReceived, + }); + + /// The name that identifies the JavaScript channel. + final String name; + + /// The callback method that is invoked when a [JavaScriptMessage] is + /// received. + final void Function(JavaScriptMessage) onMessageReceived; +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_cookie_manager.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_cookie_manager.dart new file mode 100644 index 000000000000..9e981c9022c6 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_cookie_manager.dart @@ -0,0 +1,55 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'webview_platform.dart'; + +/// Interface for a platform implementation of a cookie manager. +/// +/// Platform implementations should extend this class rather than implement it +/// as `webview_flutter` does not consider newly added methods to be breaking +/// changes. Extending this class (using `extends`) ensures that the subclass +/// will get the default implementation, while platform implementations that +/// `implements` this interface will be broken by newly added +/// [PlatformWebViewCookieManager] methods. +abstract class PlatformWebViewCookieManager extends PlatformInterface { + /// Creates a new [PlatformWebViewCookieManager] + factory PlatformWebViewCookieManager( + PlatformWebViewCookieManagerCreationParams params) { + final PlatformWebViewCookieManager cookieManagerDelegate = + WebViewPlatform.instance!.createPlatformCookieManager(params); + PlatformInterface.verify(cookieManagerDelegate, _token); + return cookieManagerDelegate; + } + + /// Used by the platform implementation to create a new + /// [PlatformWebViewCookieManager]. + /// + /// Should only be used by platform implementations because they can't extend + /// a class that only contains a factory constructor. + @protected + PlatformWebViewCookieManager.implementation(this.params) + : super(token: _token); + + static final Object _token = Object(); + + /// The parameters used to initialize the [PlatformWebViewCookieManager]. + final PlatformWebViewCookieManagerCreationParams params; + + /// Clears all cookies for all [WebView] instances. + /// + /// Returns true if cookies were present before clearing, else false. + Future clearCookies() { + throw UnimplementedError( + 'clearCookies is not implemented on the current platform'); + } + + /// Sets a cookie for all [WebView] instances. + Future setCookie(WebViewCookie cookie) { + throw UnimplementedError( + 'setCookie is not implemented on the current platform'); + } +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_widget.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_widget.dart new file mode 100644 index 000000000000..40334c650b3a --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_widget.dart @@ -0,0 +1,37 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/widgets.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'webview_platform.dart'; + +/// Interface for a platform implementation of a web view widget. +abstract class PlatformWebViewWidget extends PlatformInterface { + /// Creates a new [PlatformWebViewWidget] + factory PlatformWebViewWidget(PlatformWebViewWidgetCreationParams params) { + final PlatformWebViewWidget webViewWidgetDelegate = + WebViewPlatform.instance!.createPlatformWebViewWidget(params); + PlatformInterface.verify(webViewWidgetDelegate, _token); + return webViewWidgetDelegate; + } + + /// Used by the platform implementation to create a new + /// [PlatformWebViewWidget]. + /// + /// Should only be used by platform implementations because they can't extend + /// a class that only contains a factory constructor. + @protected + PlatformWebViewWidget.implementation(this.params) : super(token: _token); + + static final Object _token = Object(); + + /// The parameters used to initialize the [PlatformWebViewWidget]. + final PlatformWebViewWidgetCreationParams params; + + /// Builds a new WebView. + /// + /// Returns a Widget tree that embeds the created web view. + Widget build(BuildContext context); +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/javascript_message.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/javascript_message.dart new file mode 100644 index 000000000000..b37661a045a9 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/javascript_message.dart @@ -0,0 +1,51 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; + +/// A message that was sent by JavaScript code running in a [WebView]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class and providing a factory method that takes the +/// [JavaScriptMessage] as a parameter. +/// +/// {@tool sample} +/// This example demonstrates how to extend the [JavaScriptMessage] to +/// provide additional platform specific parameters. +/// +/// When extending [JavaScriptMessage] additional parameters should always +/// accept `null` or have a default value to prevent breaking changes. +/// +/// ```dart +/// @immutable +/// class WKWebViewScriptMessage extends JavaScriptMessage { +/// WKWebViewScriptMessage._( +/// JavaScriptMessage javaScriptMessage, +/// this.extraData, +/// ) : super(javaScriptMessage.message); +/// +/// factory WKWebViewScriptMessage.fromJavaScripMessage( +/// JavaScriptMessage javaScripMessage, { +/// String? extraData, +/// }) { +/// return WKWebViewScriptMessage._( +/// javaScriptMessage, +/// extraData: extraData, +/// ); +/// } +/// +/// final String? extraData; +/// } +/// ``` +/// {@end-tool} +@immutable +class JavaScriptMessage { + /// Creates a new JavaScript message object. + const JavaScriptMessage({ + required this.message, + }); + + /// The contents of the message that was sent by the JavaScript code. + final String message; +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/javascript_mode.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/javascript_mode.dart new file mode 100644 index 000000000000..bcbebff8bb1a --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/javascript_mode.dart @@ -0,0 +1,12 @@ +// 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. + +/// Describes the state of JavaScript support in a given web view. +enum JavaScriptMode { + /// JavaScript execution is disabled. + disabled, + + /// JavaScript execution is not restricted. + unrestricted, +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/load_request_params.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/load_request_params.dart new file mode 100644 index 000000000000..2da51f8dc19f --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/load_request_params.dart @@ -0,0 +1,89 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:typed_data'; + +import 'package:flutter/foundation.dart'; + +import '../platform_webview_controller.dart'; + +/// Defines the supported HTTP methods for loading a page in [PlatformWebViewController]. +enum LoadRequestMethod { + /// HTTP GET method. + get, + + /// HTTP POST method. + post, +} + +/// Extension methods on the [LoadRequestMethod] enum. +extension LoadRequestMethodExtensions on LoadRequestMethod { + /// Converts [LoadRequestMethod] to [String] format. + String serialize() { + switch (this) { + case LoadRequestMethod.get: + return 'get'; + case LoadRequestMethod.post: + return 'post'; + } + } +} + +/// Defines the parameters that can be used to load a page with the [PlatformWebViewController]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +/// +/// {@tool sample} +/// This example demonstrates how to extend the [LoadRequestParams] to +/// provide additional platform specific parameters. +/// +/// When extending [LoadRequestParams] additional parameters should always +/// accept `null` or have a default value to prevent breaking changes. +/// +/// ```dart +/// class AndroidLoadRequestParams extends LoadRequestParams { +/// AndroidLoadRequestParams._({ +/// required LoadRequestParams params, +/// this.historyUrl, +/// }) : super( +/// uri: params.uri, +/// method: params.method, +/// body: params.body, +/// headers: params.headers, +/// ); +/// +/// factory AndroidLoadRequestParams.fromLoadRequestParams( +/// LoadRequestParams params, { +/// Uri? historyUrl, +/// }) { +/// return AndroidLoadRequestParams._(params, historyUrl: historyUrl); +/// } +/// +/// final Uri? historyUrl; +/// } +/// ``` +/// {@end-tool} +@immutable +class LoadRequestParams { + /// Used by the platform implementation to create a new [LoadRequestParams]. + const LoadRequestParams({ + required this.uri, + required this.method, + required this.headers, + this.body, + }); + + /// URI for the request. + final Uri uri; + + /// HTTP method used to make the request. + final LoadRequestMethod method; + + /// Headers for the request. + final Map headers; + + /// HTTP body for the request. + final Uint8List? body; +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_navigation_delegate_creation_params.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_navigation_delegate_creation_params.dart new file mode 100644 index 000000000000..b20e5eb3ed48 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_navigation_delegate_creation_params.dart @@ -0,0 +1,44 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; + +/// Object specifying creation parameters for creating a [PlatformNavigationDelegate]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +/// +/// {@tool sample} +/// This example demonstrates how to extend the [PlatformNavigationDelegateCreationParams] to +/// provide additional platform specific parameters. +/// +/// When extending [PlatformNavigationDelegateCreationParams] additional +/// parameters should always accept `null` or have a default value to prevent +/// breaking changes. +/// +/// ```dart +/// class AndroidNavigationDelegateCreationParams extends PlatformNavigationDelegateCreationParams { +/// AndroidNavigationDelegateCreationParams._( +/// // This parameter prevents breaking changes later. +/// // ignore: avoid_unused_constructor_parameters +/// PlatformNavigationDelegateCreationParams params, { +/// this.filter, +/// }) : super(); +/// +/// factory AndroidNavigationDelegateCreationParams.fromPlatformNavigationDelegateCreationParams( +/// PlatformNavigationDelegateCreationParams params, { +/// String? filter, +/// }) { +/// return AndroidNavigationDelegateCreationParams._(params, filter: filter); +/// } +/// +/// final String? filter; +/// } +/// ``` +/// {@end-tool} +@immutable +class PlatformNavigationDelegateCreationParams { + /// Used by the platform implementation to create a new [PlatformNavigationkDelegate]. + const PlatformNavigationDelegateCreationParams(); +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_webview_controller_creation_params.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_webview_controller_creation_params.dart new file mode 100644 index 000000000000..778396a79845 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_webview_controller_creation_params.dart @@ -0,0 +1,45 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; + +/// Object specifying creation parameters for creating a [PlatformWebViewController]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +/// +/// {@tool sample} +/// This example demonstrates how to extend the [PlatformWebViewControllerCreationParams] to +/// provide additional platform specific parameters. +/// +/// When extending [PlatformWebViewControllerCreationParams] additional parameters +/// should always accept `null` or have a default value to prevent breaking +/// changes. +/// +/// ```dart +/// class WKWebViewControllerCreationParams +/// extends PlatformWebViewControllerCreationParams { +/// WKWebViewControllerCreationParams._( +/// // This parameter prevents breaking changes later. +/// // ignore: avoid_unused_constructor_parameters +/// PlatformWebViewControllerCreationParams params, { +/// this.domain, +/// }) : super(); +/// +/// factory WKWebViewControllerCreationParams.fromPlatformWebViewControllerCreationParams( +/// PlatformWebViewControllerCreationParams params, { +/// String? domain, +/// }) { +/// return WKWebViewControllerCreationParams._(params, domain: domain); +/// } +/// +/// final String? domain; +/// } +/// ``` +/// {@end-tool} +@immutable +class PlatformWebViewControllerCreationParams { + /// Used by the platform implementation to create a new [PlatformWebViewController]. + const PlatformWebViewControllerCreationParams(); +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_webview_cookie_manager_creation_params.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_webview_cookie_manager_creation_params.dart new file mode 100644 index 000000000000..e8c4938f649f --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_webview_cookie_manager_creation_params.dart @@ -0,0 +1,45 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; + +/// Object specifying creation parameters for creating a [PlatformWebViewCookieManager]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +/// +/// {@tool sample} +/// This example demonstrates how to extend the [PlatformWebViewCookieManagerCreationParams] to +/// provide additional platform specific parameters. +/// +/// When extending [PlatformWebViewCookieManagerCreationParams] additional +/// parameters should always accept `null` or have a default value to prevent +/// breaking changes. +/// +/// ```dart +/// class WKWebViewCookieManagerCreationParams +/// extends PlatformWebViewCookieManagerCreationParams { +/// WKWebViewCookieManagerCreationParams._( +/// // This parameter prevents breaking changes later. +/// // ignore: avoid_unused_constructor_parameters +/// PlatformWebViewCookieManagerCreationParams params, { +/// this.uri, +/// }) : super(); +/// +/// factory WKWebViewCookieManagerCreationParams.fromPlatformWebViewCookieManagerCreationParams( +/// PlatformWebViewCookieManagerCreationParams params, { +/// Uri? uri, +/// }) { +/// return WKWebViewCookieManagerCreationParams._(params, uri: uri); +/// } +/// +/// final Uri? uri; +/// } +/// ``` +/// {@end-tool} +@immutable +class PlatformWebViewCookieManagerCreationParams { + /// Used by the platform implementation to create a new [PlatformWebViewCookieManagerDelegate]. + const PlatformWebViewCookieManagerCreationParams(); +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_webview_widget_creation_params.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_webview_widget_creation_params.dart new file mode 100644 index 000000000000..1812d7e39c29 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_webview_widget_creation_params.dart @@ -0,0 +1,79 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; + +import '../platform_webview_controller.dart'; + +/// Object specifying creation parameters for creating a [WebViewWidgetDelegate]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +/// +/// {@tool sample} +/// This example demonstrates how to extend the [PlatformWebViewWidgetCreationParams] to +/// provide additional platform specific parameters. +/// +/// When extending [PlatformWebViewWidgetCreationParams] additional parameters +/// should always accept `null` or have a default value to prevent breaking +/// changes. +/// +/// ```dart +/// class WKWebViewWidgetCreationParams extends PlatformWebViewWidgetCreationParams { +/// WKWebViewWidgetCreationParams._( +/// // This parameter prevents breaking changes later. +/// // ignore: avoid_unused_constructor_parameters +/// PlatformWebViewWidgetCreationParams params, { +/// this.domain, +/// }) : super( +/// key: params.key, +/// controller: params.controller, +/// gestureRecognizers: params.gestureRecognizers, +/// ); +/// +/// factory WKWebViewWidgetCreationParams.fromPlatformWebViewWidgetCreationParams( +/// PlatformWebViewWidgetCreationParams params, { +/// String? domain, +/// }) { +/// return WKWebViewWidgetCreationParams._(params, domain: domain); +/// } +/// +/// final String? domain; +/// } +/// ``` +/// {@end-tool} +@immutable +class PlatformWebViewWidgetCreationParams { + /// Used by the platform implementation to create a new [PlatformWebViewWidget]. + const PlatformWebViewWidgetCreationParams({ + this.key, + required this.controller, + this.gestureRecognizers = const >{}, + }); + + /// Controls how one widget replaces another widget in the tree. + /// + /// See also: + /// + /// * The discussions at [Key] and [GlobalKey]. + final Key? key; + + /// The [PlatformWebViewController] that allows controlling the native web + /// view. + final PlatformWebViewController controller; + + /// The `gestureRecognizers` specifies which gestures should be consumed by the + /// web view. + /// + /// It is possible for other gesture recognizers to be competing with the web + /// view on pointer events, e.g if the web view is inside a [ListView] the + /// [ListView] will want to handle vertical drags. The web view will claim + /// gestures that are recognized by any of the recognizers on this list. + /// + /// When `gestureRecognizers` is empty (default), the web view will only handle + /// pointer events for gestures that were not claimed by any other gesture + /// recognizer. + final Set> gestureRecognizers; +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/types.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/types.dart new file mode 100644 index 000000000000..05504fffd211 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/types.dart @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +export 'javascript_message.dart'; +export 'javascript_mode.dart'; +export 'load_request_params.dart'; +export 'platform_navigation_delegate_creation_params.dart'; +export 'platform_webview_controller_creation_params.dart'; +export 'platform_webview_cookie_manager_creation_params.dart'; +export 'platform_webview_widget_creation_params.dart'; +export 'web_resource_error.dart'; +export 'webview_cookie.dart'; diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/web_resource_error.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/web_resource_error.dart new file mode 100644 index 000000000000..465799472912 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/web_resource_error.dart @@ -0,0 +1,119 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; + +/// Possible error type categorizations used by [WebResourceError]. +enum WebResourceErrorType { + /// User authentication failed on server. + authentication, + + /// Malformed URL. + badUrl, + + /// Failed to connect to the server. + connect, + + /// Failed to perform SSL handshake. + failedSslHandshake, + + /// Generic file error. + file, + + /// File not found. + fileNotFound, + + /// Server or proxy hostname lookup failed. + hostLookup, + + /// Failed to read or write to the server. + io, + + /// User authentication failed on proxy. + proxyAuthentication, + + /// Too many redirects. + redirectLoop, + + /// Connection timed out. + timeout, + + /// Too many requests during this load. + tooManyRequests, + + /// Generic error. + unknown, + + /// Resource load was canceled by Safe Browsing. + unsafeResource, + + /// Unsupported authentication scheme (not basic or digest). + unsupportedAuthScheme, + + /// Unsupported URI scheme. + unsupportedScheme, + + /// The web content process was terminated. + webContentProcessTerminated, + + /// The web view was invalidated. + webViewInvalidated, + + /// A JavaScript exception occurred. + javaScriptExceptionOccurred, + + /// The result of JavaScript execution could not be returned. + javaScriptResultTypeIsUnsupported, +} + +/// Error returned in `WebView.onWebResourceError` when a web resource loading error has occurred. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +/// +/// {@tool sample} +/// This example demonstrates how to extend the [WebResourceError] to +/// provide additional platform specific parameters. +/// +/// When extending [WebResourceError] additional parameters should always +/// accept `null` or have a default value to prevent breaking changes. +/// +/// ```dart +/// class IOSWebResourceError extends WebResourceError { +/// IOSWebResourceError._(WebResourceError error, {required this.domain}) +/// : super( +/// errorCode: error.errorCode, +/// description: error.description, +/// errorType: error.errorType, +/// ); +/// +/// factory IOSWebResourceError.fromWebResourceError( +/// WebResourceError error, { +/// required String? domain, +/// }) { +/// return IOSWebResourceError._(error, domain: domain); +/// } +/// +/// final String? domain; +/// } +/// ``` +/// {@end-tool} +@immutable +class WebResourceError { + /// Used by the platform implementation to create a new [WebResourceError]. + const WebResourceError({ + required this.errorCode, + required this.description, + this.errorType, + }); + + /// Raw code of the error from the respective platform. + final int errorCode; + + /// Description of the error that can be used to communicate the problem to the user. + final String description; + + /// The type this error can be categorized as. + final WebResourceErrorType? errorType; +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/webview_cookie.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/webview_cookie.dart new file mode 100644 index 000000000000..7f56a312049f --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/webview_cookie.dart @@ -0,0 +1,41 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; + +/// A cookie that can be set globally for all web views using [WebViewCookieManagerPlatform]. +@immutable +class WebViewCookie { + /// Creates a new [WebViewCookieDelegate] + const WebViewCookie({ + required this.name, + required this.value, + required this.domain, + this.path = '/', + }); + + /// The cookie-name of the cookie. + /// + /// Its value should match "cookie-name" in RFC6265bis: + /// https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-02#section-4.1.1 + final String name; + + /// The cookie-value of the cookie. + /// + /// Its value should match "cookie-value" in RFC6265bis: + /// https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-02#section-4.1.1 + final String value; + + /// The domain-value of the cookie. + /// + /// Its value should match "domain-value" in RFC6265bis: + /// https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-02#section-4.1.1 + final String domain; + + /// The path-value of the cookie, set to `/` by default. + /// + /// Its value should match "path-value" in RFC6265bis: + /// https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-02#section-4.1.1 + final String path; +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/webview_platform.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/webview_platform.dart new file mode 100644 index 000000000000..c5c5dffc6a22 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/webview_platform.dart @@ -0,0 +1,82 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'platform_navigation_delegate.dart'; +import 'platform_webview_controller.dart'; +import 'platform_webview_cookie_manager.dart'; +import 'platform_webview_widget.dart'; +import 'types/types.dart'; + +export 'types/types.dart'; + +/// Interface for a platform implementation of a WebView. +abstract class WebViewPlatform extends PlatformInterface { + /// Creates a new [WebViewPlatform]. + WebViewPlatform() : super(token: _token); + + static final Object _token = Object(); + + static WebViewPlatform? _instance; + + /// The instance of [WebViewPlatform] to use. + static WebViewPlatform? get instance => _instance; + + /// Platform-specific plugins should set this with their own platform-specific + /// class that extends [WebViewPlatform] when they register themselves. + static set instance(WebViewPlatform? instance) { + if (instance == null) { + throw AssertionError( + 'Platform interfaces can only be set to a non-null instance'); + } + + PlatformInterface.verify(instance, _token); + _instance = instance; + } + + /// Creates a new [PlatformWebViewCookieManager]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebViewCookieManager] in `webview_flutter` instead. + PlatformWebViewCookieManager createPlatformCookieManager( + PlatformWebViewCookieManagerCreationParams params, + ) { + throw UnimplementedError( + 'createPlatformCookieManager is not implemented on the current platform.'); + } + + /// Creates a new [PlatformNavigationDelegate]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [NavigationDelegate] in `webview_flutter` instead. + PlatformNavigationDelegate createPlatformNavigationDelegate( + PlatformNavigationDelegateCreationParams params, + ) { + throw UnimplementedError( + 'createPlatformNavigationDelegate is not implemented on the current platform.'); + } + + /// Create a new [PlatformWebViewController]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebViewController] in `webview_flutter` instead. + PlatformWebViewController createPlatformWebViewController( + PlatformWebViewControllerCreationParams params, + ) { + throw UnimplementedError( + 'createPlatformWebViewController is not implemented on the current platform.'); + } + + /// Create a new [PlatformWebViewWidget]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebViewWidget] in `webview_flutter` instead. + PlatformWebViewWidget createPlatformWebViewWidget( + PlatformWebViewWidgetCreationParams params, + ) { + throw UnimplementedError( + 'createPlatformWebViewWidget is not implemented on the current platform.'); + } +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/webview_flutter_platform_interface.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/webview_flutter_platform_interface.dart new file mode 100644 index 000000000000..d14fec163327 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/webview_flutter_platform_interface.dart @@ -0,0 +1,10 @@ +// 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. + +export 'src/platform_navigation_delegate.dart'; +export 'src/platform_webview_controller.dart'; +export 'src/platform_webview_cookie_manager.dart'; +export 'src/platform_webview_widget.dart'; +export 'src/types/types.dart'; +export 'src/webview_platform.dart'; diff --git a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml index f9e754931bb8..c339a0f4a2ce 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutte issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview_flutter%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 1.8.2 +version: 1.9.0 environment: sdk: ">=2.12.0 <3.0.0" @@ -13,9 +13,11 @@ environment: dependencies: flutter: sdk: flutter + meta: ^1.7.0 plugin_platform_interface: ^2.1.0 dev_dependencies: + build_runner: ^2.1.8 flutter_test: sdk: flutter mockito: ^5.0.0 diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_navigation_delegate_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_navigation_delegate_test.dart new file mode 100644 index 000000000000..5674c1522408 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_navigation_delegate_test.dart @@ -0,0 +1,141 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:webview_flutter_platform_interface/v4/src/platform_navigation_delegate.dart'; +import 'package:webview_flutter_platform_interface/v4/src/webview_platform.dart'; + +import 'webview_platform_test.mocks.dart'; + +void main() { + setUp(() { + WebViewPlatform.instance = MockWebViewPlatformWithMixin(); + }); + + test('Cannot be implemented with `implements`', () { + const PlatformNavigationDelegateCreationParams params = + PlatformNavigationDelegateCreationParams(); + when(WebViewPlatform.instance!.createPlatformNavigationDelegate(params)) + .thenReturn(ImplementsPlatformNavigationDelegate()); + + expect(() { + PlatformNavigationDelegate(params); + }, throwsNoSuchMethodError); + }); + + test('Can be extended', () { + const PlatformNavigationDelegateCreationParams params = + PlatformNavigationDelegateCreationParams(); + when(WebViewPlatform.instance!.createPlatformNavigationDelegate(params)) + .thenReturn(ExtendsPlatformNavigationDelegate(params)); + + expect(PlatformNavigationDelegate(params), isNotNull); + }); + + test('Can be mocked with `implements`', () { + const PlatformNavigationDelegateCreationParams params = + PlatformNavigationDelegateCreationParams(); + when(WebViewPlatform.instance!.createPlatformNavigationDelegate(params)) + .thenReturn(MockNavigationDelegate()); + + expect(PlatformNavigationDelegate(params), isNotNull); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of setOnNavigationRequest should throw unimplemented error', + () { + final PlatformNavigationDelegate callbackDelegate = + ExtendsPlatformNavigationDelegate( + const PlatformNavigationDelegateCreationParams()); + + expect( + () => callbackDelegate.setOnNavigationRequest( + ({required bool isForMainFrame, required String url}) => true), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of setOnPageStarted should throw unimplemented error', + () { + final PlatformNavigationDelegate callbackDelegate = + ExtendsPlatformNavigationDelegate( + const PlatformNavigationDelegateCreationParams()); + + expect( + () => callbackDelegate.setOnPageStarted((String url) {}), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of setOnPageFinished should throw unimplemented error', + () { + final PlatformNavigationDelegate callbackDelegate = + ExtendsPlatformNavigationDelegate( + const PlatformNavigationDelegateCreationParams()); + + expect( + () => callbackDelegate.setOnPageFinished((String url) {}), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of setOnProgress should throw unimplemented error', + () { + final PlatformNavigationDelegate callbackDelegate = + ExtendsPlatformNavigationDelegate( + const PlatformNavigationDelegateCreationParams()); + + expect( + () => callbackDelegate.setOnProgress((int progress) {}), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of setOnWebResourceError should throw unimplemented error', + () { + final PlatformNavigationDelegate callbackDelegate = + ExtendsPlatformNavigationDelegate( + const PlatformNavigationDelegateCreationParams()); + + expect( + () => callbackDelegate.setOnWebResourceError((WebResourceError error) {}), + throwsUnimplementedError, + ); + }); +} + +class MockWebViewPlatformWithMixin extends MockWebViewPlatform + with + // ignore: prefer_mixin + MockPlatformInterfaceMixin {} + +class ImplementsPlatformNavigationDelegate + implements PlatformNavigationDelegate { + @override + dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); +} + +class MockNavigationDelegate extends Mock + with + // ignore: prefer_mixin + MockPlatformInterfaceMixin + implements + PlatformNavigationDelegate {} + +class ExtendsPlatformNavigationDelegate extends PlatformNavigationDelegate { + ExtendsPlatformNavigationDelegate( + PlatformNavigationDelegateCreationParams params) + : super.implementation(params); +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_controller_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_controller_test.dart new file mode 100644 index 000000000000..b6d043cac9c8 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_controller_test.dart @@ -0,0 +1,467 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:webview_flutter_platform_interface/v4/src/platform_navigation_delegate.dart'; +import 'package:webview_flutter_platform_interface/v4/src/platform_webview_controller.dart'; +import 'package:webview_flutter_platform_interface/v4/src/webview_platform.dart'; + +import 'platform_navigation_delegate_test.dart'; +import 'webview_platform_test.mocks.dart'; + +@GenerateMocks([PlatformNavigationDelegate]) +void main() { + setUp(() { + WebViewPlatform.instance = MockWebViewPlatformWithMixin(); + }); + + test('Cannot be implemented with `implements`', () { + when((WebViewPlatform.instance! as MockWebViewPlatform) + .createPlatformWebViewController(any)) + .thenReturn(ImplementsPlatformWebViewController()); + + expect(() { + PlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + }, throwsNoSuchMethodError); + }); + + test('Can be extended', () { + const PlatformWebViewControllerCreationParams params = + PlatformWebViewControllerCreationParams(); + when((WebViewPlatform.instance! as MockWebViewPlatform) + .createPlatformWebViewController(any)) + .thenReturn(ExtendsPlatformWebViewController(params)); + + expect(PlatformWebViewController(params), isNotNull); + }); + + test('Can be mocked with `implements`', () { + when((WebViewPlatform.instance! as MockWebViewPlatform) + .createPlatformWebViewController(any)) + .thenReturn(MockWebViewControllerDelegate()); + + expect( + PlatformWebViewController( + const PlatformWebViewControllerCreationParams()), + isNotNull); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of loadFile should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.loadFile(''), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of loadFlutterAsset should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.loadFlutterAsset(''), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of loadHtmlString should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.loadHtmlString(''), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of loadRequest should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.loadRequest(MockLoadRequestParamsDelegate()), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of currentUrl should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.currentUrl(), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of canGoBack should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.canGoBack(), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of canGoForward should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.canGoForward(), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of goBack should throw unimplemented error', () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.goBack(), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of goForward should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.goForward(), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of reload should throw unimplemented error', () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.reload(), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of clearCache should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.clearCache(), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of clearLocalStorage should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.clearLocalStorage(), + throwsUnimplementedError, + ); + }); + + test( + 'Default implementation of the setNavigationCallback should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => + controller.setPlatformNavigationDelegate(MockNavigationDelegate()), + throwsUnimplementedError, + ); + }, + ); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of runJavaScript should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.runJavaScript('javaScript'), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of runJavaScriptReturningResult should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.runJavaScriptReturningResult('javaScript'), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of addJavaScriptChannel should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.addJavaScriptChannel( + JavaScriptChannelParams( + name: 'test', + onMessageReceived: (_) {}, + ), + ), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of removeJavaScriptChannel should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.removeJavaScriptChannel('test'), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of getTitle should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.getTitle(), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of scrollTo should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.scrollTo(0, 0), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of scrollBy should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.scrollBy(0, 0), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of getScrollPosition should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.getScrollPosition(), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of enableDebugging should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.enableDebugging(true), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of enableGestureNavigation should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.enableGestureNavigation(true), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of enableZoom should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.enableZoom(true), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of setBackgroundColor should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.setBackgroundColor(Colors.blue), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of setJavaScriptMode should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.setJavaScriptMode(JavaScriptMode.disabled), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of setUserAgent should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.setUserAgent(null), + throwsUnimplementedError, + ); + }); +} + +class MockWebViewPlatformWithMixin extends MockWebViewPlatform + with + // ignore: prefer_mixin + MockPlatformInterfaceMixin {} + +class ImplementsPlatformWebViewController implements PlatformWebViewController { + @override + dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); +} + +class MockWebViewControllerDelegate extends Mock + with + // ignore: prefer_mixin + MockPlatformInterfaceMixin + implements + PlatformWebViewController {} + +class ExtendsPlatformWebViewController extends PlatformWebViewController { + ExtendsPlatformWebViewController( + PlatformWebViewControllerCreationParams params) + : super.implementation(params); +} + +// ignore: must_be_immutable +class MockLoadRequestParamsDelegate extends Mock + with + //ignore: prefer_mixin + MockPlatformInterfaceMixin + implements + LoadRequestParams {} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_controller_test.mocks.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_controller_test.mocks.dart new file mode 100644 index 000000000000..47e67379f124 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_controller_test.mocks.dart @@ -0,0 +1,72 @@ +// Mocks generated by Mockito 5.0.16 from annotations +// in webview_flutter_platform_interface/test/src/v4/platform_webview_controller_test.dart. +// Do not manually edit this file. + +import 'dart:async' as _i4; + +import 'package:mockito/mockito.dart' as _i1; +import 'package:webview_flutter_platform_interface/v4/src/platform_navigation_delegate.dart' + as _i3; +import 'package:webview_flutter_platform_interface/v4/src/webview_platform.dart' + as _i2; + +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types + +class _FakePlatformNavigationDelegateCreationParams_0 extends _i1.Fake + implements _i2.PlatformNavigationDelegateCreationParams {} + +/// A class which mocks [PlatformNavigationDelegate]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockPlatformNavigationDelegate extends _i1.Mock + implements _i3.PlatformNavigationDelegate { + MockPlatformNavigationDelegate() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.PlatformNavigationDelegateCreationParams get params => + (super.noSuchMethod(Invocation.getter(#params), + returnValue: _FakePlatformNavigationDelegateCreationParams_0()) + as _i2.PlatformNavigationDelegateCreationParams); + @override + _i4.Future setOnNavigationRequest( + _i4.FutureOr Function({bool isForMainFrame, String url})? + onNavigationRequest) => + (super.noSuchMethod( + Invocation.method(#setOnNavigationRequest, [onNavigationRequest]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future setOnPageStarted(void Function(String)? onPageStarted) => + (super.noSuchMethod(Invocation.method(#setOnPageStarted, [onPageStarted]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future setOnPageFinished(void Function(String)? onPageFinished) => + (super.noSuchMethod( + Invocation.method(#setOnPageFinished, [onPageFinished]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future setOnProgress(void Function(int)? onProgress) => + (super.noSuchMethod(Invocation.method(#setOnProgress, [onProgress]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future setOnWebResourceError( + void Function(_i2.WebResourceError)? onWebResourceError) => + (super.noSuchMethod( + Invocation.method(#setOnWebResourceError, [onWebResourceError]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + String toString() => super.toString(); +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_widget_test.dart new file mode 100644 index 000000000000..30fa52ece24a --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_widget_test.dart @@ -0,0 +1,89 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:webview_flutter_platform_interface/v4/src/platform_webview_controller.dart'; +import 'package:webview_flutter_platform_interface/v4/src/platform_webview_widget.dart'; +import 'package:webview_flutter_platform_interface/v4/src/webview_platform.dart'; + +import 'webview_platform_test.mocks.dart'; + +void main() { + setUp(() { + WebViewPlatform.instance = MockWebViewPlatformWithMixin(); + }); + + test('Cannot be implemented with `implements`', () { + final MockWebViewControllerDelegate controller = + MockWebViewControllerDelegate(); + final PlatformWebViewWidgetCreationParams params = + PlatformWebViewWidgetCreationParams(controller: controller); + when(WebViewPlatform.instance!.createPlatformWebViewWidget(params)) + .thenReturn(ImplementsWebViewWidgetDelegate()); + + expect(() { + PlatformWebViewWidget(params); + }, throwsNoSuchMethodError); + }); + + test('Can be extended', () { + final MockWebViewControllerDelegate controller = + MockWebViewControllerDelegate(); + final PlatformWebViewWidgetCreationParams params = + PlatformWebViewWidgetCreationParams(controller: controller); + when(WebViewPlatform.instance!.createPlatformWebViewWidget(params)) + .thenReturn(ExtendsWebViewWidgetDelegate(params)); + + expect(PlatformWebViewWidget(params), isNotNull); + }); + + test('Can be mocked with `implements`', () { + final MockWebViewControllerDelegate controller = + MockWebViewControllerDelegate(); + final PlatformWebViewWidgetCreationParams params = + PlatformWebViewWidgetCreationParams(controller: controller); + when(WebViewPlatform.instance!.createPlatformWebViewWidget(params)) + .thenReturn(MockWebViewWidgetDelegate()); + + expect(PlatformWebViewWidget(params), isNotNull); + }); +} + +class MockWebViewPlatformWithMixin extends MockWebViewPlatform + with + // ignore: prefer_mixin + MockPlatformInterfaceMixin {} + +class ImplementsWebViewWidgetDelegate implements PlatformWebViewWidget { + @override + dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); +} + +class MockWebViewWidgetDelegate extends Mock + with + // ignore: prefer_mixin + MockPlatformInterfaceMixin + implements + PlatformWebViewWidget {} + +class ExtendsWebViewWidgetDelegate extends PlatformWebViewWidget { + ExtendsWebViewWidgetDelegate(PlatformWebViewWidgetCreationParams params) + : super.implementation(params); + + @override + Widget build(BuildContext context) { + throw UnimplementedError( + 'build is not implemented for ExtendedWebViewWidgetDelegate.'); + } +} + +class MockWebViewControllerDelegate extends Mock + with + // ignore: prefer_mixin + MockPlatformInterfaceMixin + implements + PlatformWebViewController {} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/webview_platform_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/webview_platform_test.dart new file mode 100644 index 000000000000..f09156919512 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/webview_platform_test.dart @@ -0,0 +1,109 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:webview_flutter_platform_interface/v4/src/platform_webview_controller.dart'; +import 'package:webview_flutter_platform_interface/v4/src/webview_platform.dart'; + +import 'webview_platform_test.mocks.dart'; + +@GenerateMocks([WebViewPlatform]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + test('Default instance WebViewPlatform instance should be null', () { + expect(WebViewPlatform.instance, isNull); + }); + + test('Cannot be implemented with `implements`', () { + expect(() { + WebViewPlatform.instance = ImplementsWebViewPlatform(); + }, throwsNoSuchMethodError); + }); + + test('Can be extended', () { + WebViewPlatform.instance = ExtendsWebViewPlatform(); + }); + + test('Can be mocked with `implements`', () { + final MockWebViewPlatform mock = MockWebViewPlatformWithMixin(); + WebViewPlatform.instance = mock; + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of createCookieManagerDelegate should throw unimplemented error', + () { + final WebViewPlatform webViewPlatform = ExtendsWebViewPlatform(); + + expect( + () => webViewPlatform.createPlatformCookieManager( + const PlatformWebViewCookieManagerCreationParams()), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of createNavigationCallbackHandlerDelegate should throw unimplemented error', + () { + final WebViewPlatform webViewPlatform = ExtendsWebViewPlatform(); + + expect( + () => webViewPlatform.createPlatformNavigationDelegate( + const PlatformNavigationDelegateCreationParams()), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of createWebViewControllerDelegate should throw unimplemented error', + () { + final WebViewPlatform webViewPlatform = ExtendsWebViewPlatform(); + + expect( + () => webViewPlatform.createPlatformWebViewController( + const PlatformWebViewControllerCreationParams()), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of createWebViewWidgetDelegate should throw unimplemented error', + () { + final WebViewPlatform webViewPlatform = ExtendsWebViewPlatform(); + final MockWebViewControllerDelegate controller = + MockWebViewControllerDelegate(); + + expect( + () => webViewPlatform.createPlatformWebViewWidget( + PlatformWebViewWidgetCreationParams(controller: controller)), + throwsUnimplementedError, + ); + }); +} + +class ImplementsWebViewPlatform implements WebViewPlatform { + @override + dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); +} + +class MockWebViewPlatformWithMixin extends MockWebViewPlatform + with + // ignore: prefer_mixin + MockPlatformInterfaceMixin {} + +class ExtendsWebViewPlatform extends WebViewPlatform {} + +class MockWebViewControllerDelegate extends Mock + with + // ignore: prefer_mixin + MockPlatformInterfaceMixin + implements + PlatformWebViewController {} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/webview_platform_test.mocks.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/webview_platform_test.mocks.dart new file mode 100644 index 000000000000..5ce007579473 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/webview_platform_test.mocks.dart @@ -0,0 +1,78 @@ +// Mocks generated by Mockito 5.0.16 from annotations +// in webview_flutter_platform_interface/test/src/v4/webview_platform_test.dart. +// Do not manually edit this file. + +import 'package:mockito/mockito.dart' as _i1; +import 'package:webview_flutter_platform_interface/v4/src/platform_navigation_delegate.dart' + as _i3; +import 'package:webview_flutter_platform_interface/v4/src/platform_webview_controller.dart' + as _i4; +import 'package:webview_flutter_platform_interface/v4/src/platform_webview_cookie_manager.dart' + as _i2; +import 'package:webview_flutter_platform_interface/v4/src/platform_webview_widget.dart' + as _i5; +import 'package:webview_flutter_platform_interface/v4/src/types/types.dart' + as _i7; +import 'package:webview_flutter_platform_interface/v4/src/webview_platform.dart' + as _i6; + +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types + +class _FakePlatformWebViewCookieManager_0 extends _i1.Fake + implements _i2.PlatformWebViewCookieManager {} + +class _FakePlatformNavigationDelegate_1 extends _i1.Fake + implements _i3.PlatformNavigationDelegate {} + +class _FakePlatformWebViewController_2 extends _i1.Fake + implements _i4.PlatformWebViewController {} + +class _FakePlatformWebViewWidget_3 extends _i1.Fake + implements _i5.PlatformWebViewWidget {} + +/// A class which mocks [WebViewPlatform]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockWebViewPlatform extends _i1.Mock implements _i6.WebViewPlatform { + MockWebViewPlatform() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.PlatformWebViewCookieManager createPlatformCookieManager( + _i7.PlatformWebViewCookieManagerCreationParams? params) => + (super.noSuchMethod( + Invocation.method(#createPlatformCookieManager, [params]), + returnValue: _FakePlatformWebViewCookieManager_0()) + as _i2.PlatformWebViewCookieManager); + @override + _i3.PlatformNavigationDelegate createPlatformNavigationDelegate( + _i7.PlatformNavigationDelegateCreationParams? params) => + (super.noSuchMethod( + Invocation.method(#createPlatformNavigationDelegate, [params]), + returnValue: _FakePlatformNavigationDelegate_1()) + as _i3.PlatformNavigationDelegate); + @override + _i4.PlatformWebViewController createPlatformWebViewController( + _i7.PlatformWebViewControllerCreationParams? params) => + (super.noSuchMethod( + Invocation.method(#createPlatformWebViewController, [params]), + returnValue: _FakePlatformWebViewController_2()) + as _i4.PlatformWebViewController); + @override + _i5.PlatformWebViewWidget createPlatformWebViewWidget( + _i7.PlatformWebViewWidgetCreationParams? params) => + (super.noSuchMethod( + Invocation.method(#createPlatformWebViewWidget, [params]), + returnValue: _FakePlatformWebViewWidget_3()) + as _i5.PlatformWebViewWidget); + @override + String toString() => super.toString(); +} From bd3f490353b059b510c6f4e52e5d1118af2445ba Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Wed, 18 May 2022 07:17:12 -0700 Subject: [PATCH 579/600] [camera] Request access permission for audio (#5766) --- packages/camera/camera/CHANGELOG.md | 4 + packages/camera/camera/README.md | 8 +- .../ios/RunnerTests/CameraPermissionTests.m | 108 ++++++++++++++++++ packages/camera/camera/example/lib/main.dart | 11 ++ .../ios/Classes/CameraPermissionUtils.h | 12 ++ .../ios/Classes/CameraPermissionUtils.m | 92 +++++++++++---- .../camera/camera/ios/Classes/CameraPlugin.m | 38 ++++-- packages/camera/camera/pubspec.yaml | 2 +- 8 files changed, 242 insertions(+), 33 deletions(-) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index d101f60cf041..9af23011ec50 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.6 + +* Adds audio access permission handling logic on iOS to fix an issue with `prepareForVideoRecording` not awaiting for the audio permission request result. + ## 0.9.5+1 * Suppresses warnings for pre-iOS-11 codepaths. diff --git a/packages/camera/camera/README.md b/packages/camera/camera/README.md index 6b2ed7a6b687..ec9d7379c60b 100644 --- a/packages/camera/camera/README.md +++ b/packages/camera/camera/README.md @@ -88,10 +88,16 @@ Here is a list of all permission error codes that can be thrown: - `CameraAccessDenied`: Thrown when user denies the camera access permission. -- `CameraAccessDeniedWithoutPrompt`: iOS only for now. Thrown when user has previously denied the permission. iOS does not allow prompting alert dialog a second time. Users will have to go to Settings > Privacy in order to enable camera access. +- `CameraAccessDeniedWithoutPrompt`: iOS only for now. Thrown when user has previously denied the permission. iOS does not allow prompting alert dialog a second time. Users will have to go to Settings > Privacy > Camera in order to enable camera access. - `CameraAccessRestricted`: iOS only for now. Thrown when camera access is restricted and users cannot grant permission (parental control). +- `AudioAccessDenied`: Thrown when user denies the audio access permission. + +- `AudioAccessDeniedWithoutPrompt`: iOS only for now. Thrown when user has previously denied the permission. iOS does not allow prompting alert dialog a second time. Users will have to go to Settings > Privacy > Microphone in order to enable audio access. + +- `AudioAccessRestricted`: iOS only for now. Thrown when audio access is restricted and users cannot grant permission (parental control). + - `cameraPermission`: Android and Web only. A legacy error code for all kinds of camera permission errors. ### Example diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraPermissionTests.m b/packages/camera/camera/example/ios/RunnerTests/CameraPermissionTests.m index 961b931b7704..541e0288254c 100644 --- a/packages/camera/camera/example/ios/RunnerTests/CameraPermissionTests.m +++ b/packages/camera/camera/example/ios/RunnerTests/CameraPermissionTests.m @@ -15,6 +15,8 @@ @interface CameraPermissionTests : XCTestCase @implementation CameraPermissionTests +#pragma mark - camera permissions + - (void)testRequestCameraPermission_completeWithoutErrorIfPrevoiuslyAuthorized { XCTestExpectation *expectation = [self expectationWithDescription: @@ -120,4 +122,110 @@ - (void)testRequestCameraPermission_completeWithErrorIfUserDenyAccess { [self waitForExpectationsWithTimeout:1 handler:nil]; } +#pragma mark - audio permissions + +- (void)testRequestAudioPermission_completeWithoutErrorIfPrevoiuslyAuthorized { + XCTestExpectation *expectation = + [self expectationWithDescription: + @"Must copmlete without error if audio access was previously authorized."]; + + id mockDevice = OCMClassMock([AVCaptureDevice class]); + OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeAudio]) + .andReturn(AVAuthorizationStatusAuthorized); + + FLTRequestAudioPermissionWithCompletionHandler(^(FlutterError *error) { + if (error == nil) { + [expectation fulfill]; + } + }); + [self waitForExpectationsWithTimeout:1 handler:nil]; +} +- (void)testRequestAudioPermission_completeWithErrorIfPreviouslyDenied { + XCTestExpectation *expectation = + [self expectationWithDescription: + @"Must complete with error if audio access was previously denied."]; + FlutterError *expectedError = + [FlutterError errorWithCode:@"AudioAccessDeniedWithoutPrompt" + message:@"User has previously denied the audio access request. Go to " + @"Settings to enable audio access." + details:nil]; + + id mockDevice = OCMClassMock([AVCaptureDevice class]); + OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeAudio]) + .andReturn(AVAuthorizationStatusDenied); + FLTRequestAudioPermissionWithCompletionHandler(^(FlutterError *error) { + if ([error isEqual:expectedError]) { + [expectation fulfill]; + } + }); + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testRequestAudioPermission_completeWithErrorIfRestricted { + XCTestExpectation *expectation = + [self expectationWithDescription:@"Must complete with error if audio access is restricted."]; + FlutterError *expectedError = [FlutterError errorWithCode:@"AudioAccessRestricted" + message:@"Audio access is restricted. " + details:nil]; + + id mockDevice = OCMClassMock([AVCaptureDevice class]); + OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeAudio]) + .andReturn(AVAuthorizationStatusRestricted); + + FLTRequestAudioPermissionWithCompletionHandler(^(FlutterError *error) { + if ([error isEqual:expectedError]) { + [expectation fulfill]; + } + }); + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testRequestAudioPermission_completeWithoutErrorIfUserGrantAccess { + XCTestExpectation *grantedExpectation = [self + expectationWithDescription:@"Must complete without error if user choose to grant access"]; + + id mockDevice = OCMClassMock([AVCaptureDevice class]); + OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeAudio]) + .andReturn(AVAuthorizationStatusNotDetermined); + // Mimic user choosing "allow" in permission dialog. + OCMStub([mockDevice requestAccessForMediaType:AVMediaTypeAudio + completionHandler:[OCMArg checkWithBlock:^BOOL(void (^block)(BOOL)) { + block(YES); + return YES; + }]]); + + FLTRequestAudioPermissionWithCompletionHandler(^(FlutterError *error) { + if (error == nil) { + [grantedExpectation fulfill]; + } + }); + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testRequestAudioPermission_completeWithErrorIfUserDenyAccess { + XCTestExpectation *expectation = + [self expectationWithDescription:@"Must complete with error if user choose to deny access"]; + FlutterError *expectedError = [FlutterError errorWithCode:@"AudioAccessDenied" + message:@"User denied the audio access request." + details:nil]; + + id mockDevice = OCMClassMock([AVCaptureDevice class]); + OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeAudio]) + .andReturn(AVAuthorizationStatusNotDetermined); + + // Mimic user choosing "deny" in permission dialog. + OCMStub([mockDevice requestAccessForMediaType:AVMediaTypeAudio + completionHandler:[OCMArg checkWithBlock:^BOOL(void (^block)(BOOL)) { + block(NO); + return YES; + }]]); + FLTRequestAudioPermissionWithCompletionHandler(^(FlutterError *error) { + if ([error isEqual:expectedError]) { + [expectation fulfill]; + } + }); + + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + @end diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index 34942ba5aa77..c0181a5d36a1 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -697,6 +697,17 @@ class _CameraExampleHomeState extends State // iOS only showInSnackBar('Camera access is restricted.'); break; + case 'AudioAccessDenied': + showInSnackBar('You have denied audio access.'); + break; + case 'AudioAccessDeniedWithoutPrompt': + // iOS only + showInSnackBar('Please go to Settings app to enable audio access.'); + break; + case 'AudioAccessRestricted': + // iOS only + showInSnackBar('Audio access is restricted.'); + break; case 'cameraPermission': // Android & web only showInSnackBar('Unknown permission error.'); diff --git a/packages/camera/camera/ios/Classes/CameraPermissionUtils.h b/packages/camera/camera/ios/Classes/CameraPermissionUtils.h index 80f55db7be32..5cbbab055f34 100644 --- a/packages/camera/camera/ios/Classes/CameraPermissionUtils.h +++ b/packages/camera/camera/ios/Classes/CameraPermissionUtils.h @@ -18,3 +18,15 @@ typedef void (^FLTCameraPermissionRequestCompletionHandler)(FlutterError *); /// called on an arbitrary dispatch queue. extern void FLTRequestCameraPermissionWithCompletionHandler( FLTCameraPermissionRequestCompletionHandler handler); + +/// Requests audio access permission. +/// +/// If it is the first time requesting audio access, a permission dialog will show up on the +/// screen. Otherwise AVFoundation simply returns the user's previous choice, and in this case the +/// user will have to update the choice in Settings app. +/// +/// @param handler if access permission is (or was previously) granted, completion handler will be +/// called without error; Otherwise completion handler will be called with error. Handler can be +/// called on an arbitrary dispatch queue. +extern void FLTRequestAudioPermissionWithCompletionHandler( + FLTCameraPermissionRequestCompletionHandler handler); diff --git a/packages/camera/camera/ios/Classes/CameraPermissionUtils.m b/packages/camera/camera/ios/Classes/CameraPermissionUtils.m index 6318338ea6a2..098265a6b74d 100644 --- a/packages/camera/camera/ios/Classes/CameraPermissionUtils.m +++ b/packages/camera/camera/ios/Classes/CameraPermissionUtils.m @@ -5,35 +5,83 @@ @import AVFoundation; #import "CameraPermissionUtils.h" -void FLTRequestCameraPermissionWithCompletionHandler( - FLTCameraPermissionRequestCompletionHandler handler) { - switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]) { +void FLTRequestPermission(BOOL forAudio, FLTCameraPermissionRequestCompletionHandler handler) { + AVMediaType mediaType; + if (forAudio) { + mediaType = AVMediaTypeAudio; + } else { + mediaType = AVMediaTypeVideo; + } + + switch ([AVCaptureDevice authorizationStatusForMediaType:mediaType]) { case AVAuthorizationStatusAuthorized: handler(nil); break; - case AVAuthorizationStatusDenied: - handler([FlutterError errorWithCode:@"CameraAccessDeniedWithoutPrompt" - message:@"User has previously denied the camera access request. " - @"Go to Settings to enable camera access." - details:nil]); + case AVAuthorizationStatusDenied: { + FlutterError *flutterError; + if (forAudio) { + flutterError = + [FlutterError errorWithCode:@"AudioAccessDeniedWithoutPrompt" + message:@"User has previously denied the audio access request. " + @"Go to Settings to enable audio access." + details:nil]; + } else { + flutterError = + [FlutterError errorWithCode:@"CameraAccessDeniedWithoutPrompt" + message:@"User has previously denied the camera access request. " + @"Go to Settings to enable camera access." + details:nil]; + } + handler(flutterError); break; - case AVAuthorizationStatusRestricted: - handler([FlutterError errorWithCode:@"CameraAccessRestricted" - message:@"Camera access is restricted. " - details:nil]); + } + case AVAuthorizationStatusRestricted: { + FlutterError *flutterError; + if (forAudio) { + flutterError = [FlutterError errorWithCode:@"AudioAccessRestricted" + message:@"Audio access is restricted. " + details:nil]; + } else { + flutterError = [FlutterError errorWithCode:@"CameraAccessRestricted" + message:@"Camera access is restricted. " + details:nil]; + } + handler(flutterError); break; + } case AVAuthorizationStatusNotDetermined: { - [AVCaptureDevice - requestAccessForMediaType:AVMediaTypeVideo - completionHandler:^(BOOL granted) { - // handler can be invoked on an arbitrary dispatch queue. - handler(granted ? nil - : [FlutterError - errorWithCode:@"CameraAccessDenied" - message:@"User denied the camera access request." - details:nil]); - }]; + [AVCaptureDevice requestAccessForMediaType:mediaType + completionHandler:^(BOOL granted) { + // handler can be invoked on an arbitrary dispatch queue. + if (granted) { + handler(nil); + } else { + FlutterError *flutterError; + if (forAudio) { + flutterError = [FlutterError + errorWithCode:@"AudioAccessDenied" + message:@"User denied the audio access request." + details:nil]; + } else { + flutterError = [FlutterError + errorWithCode:@"CameraAccessDenied" + message:@"User denied the camera access request." + details:nil]; + } + handler(flutterError); + } + }]; break; } } } + +void FLTRequestCameraPermissionWithCompletionHandler( + FLTCameraPermissionRequestCompletionHandler handler) { + FLTRequestPermission(/*forAudio*/ NO, handler); +} + +void FLTRequestAudioPermissionWithCompletionHandler( + FLTCameraPermissionRequestCompletionHandler handler) { + FLTRequestPermission(/*forAudio*/ YES, handler); +} diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.m b/packages/camera/camera/ios/Classes/CameraPlugin.m index 43d541e411b4..64952e8d70f1 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin.m +++ b/packages/camera/camera/ios/Classes/CameraPlugin.m @@ -132,14 +132,7 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call [result sendNotImplemented]; } } else if ([@"create" isEqualToString:call.method]) { - FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) { - // Create FLTCam only if granted camera access. - if (error) { - [result sendFlutterError:error]; - } else { - [self createCameraOnSessionQueueWithCreateMethodCall:call result:result]; - } - }); + [self handleCreateMethodCall:call result:result]; } else if ([@"startImageStream" isEqualToString:call.method]) { [_camera startImageStreamWithMessenger:_messenger]; [result sendSuccess]; @@ -194,7 +187,7 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call [_camera close]; [result sendSuccess]; } else if ([@"prepareForVideoRecording" isEqualToString:call.method]) { - [_camera setUpCaptureSessionForAudio]; + [self.camera setUpCaptureSessionForAudio]; [result sendSuccess]; } else if ([@"startVideoRecording" isEqualToString:call.method]) { [_camera startVideoRecordingWithResult:result]; @@ -258,6 +251,33 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call } } +- (void)handleCreateMethodCall:(FlutterMethodCall *)call + result:(FLTThreadSafeFlutterResult *)result { + // Create FLTCam only if granted camera access (and audio access if audio is enabled) + FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) { + if (error) { + [result sendFlutterError:error]; + } else { + // Request audio permission on `create` call with `enableAudio` argument instead of the + // `prepareForVideoRecording` call. This is because `prepareForVideoRecording` call is + // optional, and used as a workaround to fix a missing frame issue on iOS. + BOOL audioEnabled = [call.arguments[@"enableAudio"] boolValue]; + if (audioEnabled) { + // Setup audio capture session only if granted audio access. + FLTRequestAudioPermissionWithCompletionHandler(^(FlutterError *error) { + if (error) { + [result sendFlutterError:error]; + } else { + [self createCameraOnSessionQueueWithCreateMethodCall:call result:result]; + } + }); + } else { + [self createCameraOnSessionQueueWithCreateMethodCall:call result:result]; + } + } + }); +} + - (void)createCameraOnSessionQueueWithCreateMethodCall:(FlutterMethodCall *)createMethodCall result:(FLTThreadSafeFlutterResult *)result { dispatch_async(self.captureSessionQueue, ^{ diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 14acf32e2324..593e7b5bb978 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.5+1 +version: 0.9.6 environment: sdk: ">=2.14.0 <3.0.0" From 07467dc23408d83f1e264fcdab160ad8e7dfdae3 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 18 May 2022 11:02:10 -0400 Subject: [PATCH 580/600] Roll Flutter from c248854d176b to 1994027986cf (1 revision) (#5777) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index a0755e305782..73d69ae3af72 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c248854d176b92ac0d50a4ad0edc19bd12210400 +1994027986cf59a4c307d6612706ce08c16b1ae8 From 76abf7679404513981b52ae51d0a701a222db34e Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 18 May 2022 11:47:12 -0400 Subject: [PATCH 581/600] [ci/tools] Add iOS/macOS analysis to catch deprecated code (#5778) --- .cirrus.yml | 8 ++ .../google_sign_in_ios/example/ios/Podfile | 3 + script/tool/CHANGELOG.md | 3 + .../tool/lib/src/xcode_analyze_command.dart | 25 +++++- .../tool/test/xcode_analyze_command_test.dart | 76 +++++++++++++++++++ 5 files changed, 113 insertions(+), 2 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index c4abdbc5adc1..69fd8955302a 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -334,6 +334,11 @@ task: - ./script/tool_runner.sh build-examples --ios xcode_analyze_script: - ./script/tool_runner.sh xcode-analyze --ios + xcode_analyze_deprecation_script: + # Ensure we don't accidentally introduce deprecated code. + # TODO(stuartmorgan): Update this to a newer version of iOS to get + # ahead of upcoming deprecations. + - ./script/tool_runner.sh xcode-analyze --ios --ios-min-version=11.0 native_test_script: - ./script/tool_runner.sh native-test --ios --ios-destination "platform=iOS Simulator,name=iPhone 11,OS=latest" drive_script: @@ -362,6 +367,9 @@ task: - ./script/tool_runner.sh build-examples --macos xcode_analyze_script: - ./script/tool_runner.sh xcode-analyze --macos + xcode_analyze_deprecation_script: + # Ensure we don't accidentally introduce deprecated code. + - ./script/tool_runner.sh xcode-analyze --macos --macos-min-version=12.3 native_test_script: - ./script/tool_runner.sh native-test --macos drive_script: diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile b/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile index e577a3081fe8..b20e1ad2fca0 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile @@ -25,6 +25,9 @@ end require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) +# Suppress warnings from transitive dependencies that cause analysis to fail. +pod 'AppAuth', :inhibit_warnings => true + flutter_ios_podfile_setup target 'Runner' do diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 0e2a33e15eaf..04101c228793 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -2,6 +2,9 @@ - Fixes changelog validation when reverting to a `NEXT` state. - Fixes multiplication of `--force` flag when publishing multiple packages. +- Adds minimum deployment target flags to `xcode-analyze` to allow + enforcing deprecation warning handling in advance of actually dropping + support for an OS version. - Checks for template boilerplate in `readme-check`. - `readme-check` now validates example READMEs when present. diff --git a/script/tool/lib/src/xcode_analyze_command.dart b/script/tool/lib/src/xcode_analyze_command.dart index 4298acb1c7e5..a81bf15477af 100644 --- a/script/tool/lib/src/xcode_analyze_command.dart +++ b/script/tool/lib/src/xcode_analyze_command.dart @@ -23,8 +23,20 @@ class XcodeAnalyzeCommand extends PackageLoopingCommand { super(packagesDir, processRunner: processRunner, platform: platform) { argParser.addFlag(platformIOS, help: 'Analyze iOS'); argParser.addFlag(platformMacOS, help: 'Analyze macOS'); + argParser.addOption(_minIOSVersionArg, + help: 'Sets the minimum iOS deployment version to use when compiling, ' + 'overriding the default minimum version. This can be used to find ' + 'deprecation warnings that will affect the plugin in the future.'); + argParser.addOption(_minMacOSVersionArg, + help: + 'Sets the minimum macOS deployment version to use when compiling, ' + 'overriding the default minimum version. This can be used to find ' + 'deprecation warnings that will affect the plugin in the future.'); } + static const String _minIOSVersionArg = 'ios-min-version'; + static const String _minMacOSVersionArg = 'macos-min-version'; + final Xcode _xcode; @override @@ -57,15 +69,24 @@ class XcodeAnalyzeCommand extends PackageLoopingCommand { return PackageResult.skip('Not implemented for target platform(s).'); } + final String minIOSVersion = getStringArg(_minIOSVersionArg); + final String minMacOSVersion = getStringArg(_minMacOSVersionArg); + final List failures = []; if (testIOS && !await _analyzePlugin(package, 'iOS', extraFlags: [ '-destination', - 'generic/platform=iOS Simulator' + 'generic/platform=iOS Simulator', + if (minIOSVersion.isNotEmpty) + 'IPHONEOS_DEPLOYMENT_TARGET=$minIOSVersion', ])) { failures.add('iOS'); } - if (testMacOS && !await _analyzePlugin(package, 'macOS')) { + if (testMacOS && + !await _analyzePlugin(package, 'macOS', extraFlags: [ + if (minMacOSVersion.isNotEmpty) + 'MACOSX_DEPLOYMENT_TARGET=$minMacOSVersion', + ])) { failures.add('macOS'); } diff --git a/script/tool/test/xcode_analyze_command_test.dart b/script/tool/test/xcode_analyze_command_test.dart index 51e8e3283295..418c695f295c 100644 --- a/script/tool/test/xcode_analyze_command_test.dart +++ b/script/tool/test/xcode_analyze_command_test.dart @@ -123,6 +123,47 @@ void main() { ])); }); + test('passes min iOS deployment version when requested', () async { + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, + platformSupport: { + platformIOS: const PlatformDetails(PlatformSupport.inline) + }); + + final Directory pluginExampleDirectory = getExampleDir(plugin); + + final List output = await runCapturingPrint(runner, + ['xcode-analyze', '--ios', '--ios-min-version=14.0']); + + expect( + output, + containsAllInOrder([ + contains('Running for plugin'), + contains('plugin/example (iOS) passed analysis.') + ])); + + expect( + processRunner.recordedCalls, + orderedEquals([ + ProcessCall( + 'xcrun', + const [ + 'xcodebuild', + 'analyze', + '-workspace', + 'ios/Runner.xcworkspace', + '-scheme', + 'Runner', + '-configuration', + 'Debug', + '-destination', + 'generic/platform=iOS Simulator', + 'IPHONEOS_DEPLOYMENT_TARGET=14.0', + 'GCC_TREAT_WARNINGS_AS_ERRORS=YES', + ], + pluginExampleDirectory.path), + ])); + }); + test('fails if xcrun fails', () async { createFakePlugin('plugin', packagesDir, platformSupport: { @@ -218,6 +259,41 @@ void main() { ])); }); + test('passes min macOS deployment version when requested', () async { + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, + platformSupport: { + platformMacOS: const PlatformDetails(PlatformSupport.inline), + }); + + final Directory pluginExampleDirectory = getExampleDir(plugin); + + final List output = await runCapturingPrint(runner, + ['xcode-analyze', '--macos', '--macos-min-version=12.0']); + + expect(output, + contains(contains('plugin/example (macOS) passed analysis.'))); + + expect( + processRunner.recordedCalls, + orderedEquals([ + ProcessCall( + 'xcrun', + const [ + 'xcodebuild', + 'analyze', + '-workspace', + 'macos/Runner.xcworkspace', + '-scheme', + 'Runner', + '-configuration', + 'Debug', + 'MACOSX_DEPLOYMENT_TARGET=12.0', + 'GCC_TREAT_WARNINGS_AS_ERRORS=YES', + ], + pluginExampleDirectory.path), + ])); + }); + test('fails if xcrun fails', () async { createFakePlugin('plugin', packagesDir, platformSupport: { From 4660811ce76af8ede81b2c042639c95405460c8f Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 18 May 2022 13:57:11 -0400 Subject: [PATCH 582/600] Add more CODEOWNERS (#5779) --- CODEOWNERS | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/CODEOWNERS b/CODEOWNERS index 26a2a2bc1602..68e4cb768a6a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -5,8 +5,17 @@ # reviewed by someone else. # Plugin-level rules. +packages/camera/** @bparrishMines +packages/file_selector/** @stuartmorgan +packages/google_maps_flutter/** @stuartmorgan +packages/google_sign_in/** @stuartmorgan +packages/image_picker/** @stuartmorgan +packages/local_auth/** @stuartmorgan packages/path_provider/** @gaaclarke +packages/plugin_platform_interface/** @stuartmorgan +packages/quick_actions/** @stuartmorgan packages/shared_preferences/** @gaaclarke +packages/url_launcher/** @stuartmorgan packages/video_player/** @gaaclarke packages/webview_flutter/** @bparrishMines @@ -44,3 +53,23 @@ packages/shared_preferences/shared_preferences_ios/** @cyanglaz packages/url_launcher/url_launcher_ios/** @jmagman packages/video_player/video_player_avfoundation/** @hellohuanlin packages/webview_flutter/webview_flutter_wkwebview/** @cyanglaz + +# - Linux +packages/path_provider/path_provider_linux/** @cbracken +packages/shared_preferences/shared_preferences_linux/** @cbracken +packages/url_launcher/url_launcher_linux/** @cbracken + +# - macOS +packages/file_selector/file_selector_macos/** @cbracken +packages/path_provider/path_provider_macos/** @cbracken +packages/shared_preferences/shared_preferences_macos/** @cbracken +packages/url_launcher/url_launcher_macos/** @cbracken + +# - Windows +packages/camera/camera_windows/** @cbracken +packages/file_selector/file_selector_windows/** @cbracken +packages/image_picker/image_picker_windows/** @cbracken +packages/local_auth/local_auth_windows/** @cbracken +packages/path_provider/path_provider_windows/** @cbracken +packages/shared_preferences/shared_preferences_windows/** @cbracken +packages/url_launcher/url_launcher_windows/** @cbracken From 4ed29beeea588fbadf6e40645beddeb3f40a7d82 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 18 May 2022 19:17:29 -0400 Subject: [PATCH 583/600] [tools] Add `update-release-info` (#5643) --- script/tool/CHANGELOG.md | 4 +- script/tool/README.md | 25 + .../lib/src/common/package_state_utils.dart | 95 +++ script/tool/lib/src/main.dart | 2 + .../lib/src/update_release_info_command.dart | 310 +++++++++ .../tool/lib/src/version_check_command.dart | 41 +- script/tool/pubspec.yaml | 2 +- .../test/common/package_state_utils_test.dart | 140 ++++ .../update_release_info_command_test.dart | 645 ++++++++++++++++++ script/tool/test/util.dart | 14 +- 10 files changed, 1238 insertions(+), 40 deletions(-) create mode 100644 script/tool/lib/src/common/package_state_utils.dart create mode 100644 script/tool/lib/src/update_release_info_command.dart create mode 100644 script/tool/test/common/package_state_utils_test.dart create mode 100644 script/tool/test/update_release_info_command_test.dart diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 04101c228793..4b40aecefa9a 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,5 +1,7 @@ -## NEXT +## 0.8.6 +- Adds `update-release-info` to apply changelog and optional version changes + across multiple packages. - Fixes changelog validation when reverting to a `NEXT` state. - Fixes multiplication of `--force` flag when publishing multiple packages. - Adds minimum deployment target flags to `xcode-analyze` to allow diff --git a/script/tool/README.md b/script/tool/README.md index d52ee08fdc34..cc9621f44086 100644 --- a/script/tool/README.md +++ b/script/tool/README.md @@ -118,6 +118,31 @@ cd dart run ./script/tool/bin/flutter_plugin_tools.dart update-excerpts --packages plugin_name ``` +### Update CHANGELOG and Version + +`update-release-info` will automatically update the version and `CHANGELOG.md` +following standard repository style and practice. It can be used for +single-package updates to handle the details of getting the `CHANGELOG.md` +format correct, but is especially useful for bulk updates across multiple packages. + +For instance, if you add a new analysis option that requires production +code changes across many packages: + +```sh +cd +dart run ./script/tool/bin/flutter_plugin_tools.dart update-release-info \ + --version=minimal \ + --changelog="Fixes violations of new analysis option some_new_option." +``` + +The `minimal` option for `--version` will skip unchanged packages, and treat +each changed package as either `bugfix` or `next` depending on the files that +have changed in that package, so it is often the best choice for a bulk change. + +For cases where you know the change time, `minor` or `bugfix` will make the +corresponding version bump, or `next` will update only `CHANGELOG.md` without +changing the version. + ### Publish a Release **Releases are automated for `flutter/plugins` and `flutter/packages`.** diff --git a/script/tool/lib/src/common/package_state_utils.dart b/script/tool/lib/src/common/package_state_utils.dart new file mode 100644 index 000000000000..437bbf6df370 --- /dev/null +++ b/script/tool/lib/src/common/package_state_utils.dart @@ -0,0 +1,95 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:meta/meta.dart'; +import 'package:path/path.dart' as p; + +import 'repository_package.dart'; + +/// The state of a package on disk relative to git state. +@immutable +class PackageChangeState { + /// Creates a new immutable state instance. + const PackageChangeState({ + required this.hasChanges, + required this.hasChangelogChange, + required this.needsVersionChange, + }); + + /// True if there are any changes to files in the package. + final bool hasChanges; + + /// True if the package's CHANGELOG.md has been changed. + final bool hasChangelogChange; + + /// True if any changes in the package require a version change according + /// to repository policy. + final bool needsVersionChange; +} + +/// Checks [package] against [changedPaths] to determine what changes it has +/// and how those changes relate to repository policy about CHANGELOG and +/// version updates. +/// +/// [changedPaths] should be a list of POSIX-style paths from a common root, +/// and [relativePackagePath] should be the path to [package] from that same +/// root. Commonly these will come from `gitVersionFinder.getChangedFiles()` +/// and `getRelativePoixPath(package.directory, gitDir.path)` respectively; +/// they are arguments mainly to allow for caching the changed paths for an +/// entire command run. +PackageChangeState checkPackageChangeState( + RepositoryPackage package, { + required List changedPaths, + required String relativePackagePath, +}) { + final String packagePrefix = relativePackagePath.endsWith('/') + ? relativePackagePath + : '$relativePackagePath/'; + + bool hasChanges = false; + bool hasChangelogChange = false; + bool needsVersionChange = false; + for (final String path in changedPaths) { + // Only consider files within the package. + if (!path.startsWith(packagePrefix)) { + continue; + } + final String packageRelativePath = path.substring(packagePrefix.length); + hasChanges = true; + + final List components = p.posix.split(packageRelativePath); + if (components.isEmpty) { + continue; + } + final bool isChangelog = components.first == 'CHANGELOG.md'; + if (isChangelog) { + hasChangelogChange = true; + } + + if (!needsVersionChange && + !isChangelog && + // One of a few special files example will be shown on pub.dev, but for + // anything else in the example publishing has no purpose. + !(components.first == 'example' && + !{'main.dart', 'readme.md', 'example.md'} + .contains(components.last.toLowerCase())) && + // Changes to tests don't need to be published. + !components.contains('test') && + !components.contains('androidTest') && + !components.contains('RunnerTests') && + !components.contains('RunnerUITests') && + // The top-level "tool" directory is for non-client-facing utility code, + // so doesn't need to be published. + components.first != 'tool' && + // Ignoring lints doesn't affect clients. + !components.contains('lint-baseline.xml')) { + needsVersionChange = true; + } + } + + return PackageChangeState( + hasChanges: hasChanges, + hasChangelogChange: hasChangelogChange, + needsVersionChange: needsVersionChange); +} diff --git a/script/tool/lib/src/main.dart b/script/tool/lib/src/main.dart index 9c572ee270e0..739aef56878d 100644 --- a/script/tool/lib/src/main.dart +++ b/script/tool/lib/src/main.dart @@ -29,6 +29,7 @@ import 'pubspec_check_command.dart'; import 'readme_check_command.dart'; import 'test_command.dart'; import 'update_excerpts_command.dart'; +import 'update_release_info_command.dart'; import 'version_check_command.dart'; import 'xcode_analyze_command.dart'; @@ -70,6 +71,7 @@ void main(List args) { ..addCommand(ReadmeCheckCommand(packagesDir)) ..addCommand(TestCommand(packagesDir)) ..addCommand(UpdateExcerptsCommand(packagesDir)) + ..addCommand(UpdateReleaseInfoCommand(packagesDir)) ..addCommand(VersionCheckCommand(packagesDir)) ..addCommand(XcodeAnalyzeCommand(packagesDir)); diff --git a/script/tool/lib/src/update_release_info_command.dart b/script/tool/lib/src/update_release_info_command.dart new file mode 100644 index 000000000000..b998615ead17 --- /dev/null +++ b/script/tool/lib/src/update_release_info_command.dart @@ -0,0 +1,310 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:args/command_runner.dart'; +import 'package:file/file.dart'; +import 'package:flutter_plugin_tools/src/common/core.dart'; +import 'package:git/git.dart'; +import 'package:pub_semver/pub_semver.dart'; +import 'package:yaml_edit/yaml_edit.dart'; + +import 'common/git_version_finder.dart'; +import 'common/package_looping_command.dart'; +import 'common/package_state_utils.dart'; +import 'common/repository_package.dart'; + +/// Supported version change types, from smallest to largest component. +enum _VersionIncrementType { build, bugfix, minor } + +/// Possible results of attempting to update a CHANGELOG.md file. +enum _ChangelogUpdateOutcome { addedSection, updatedSection, failed } + +/// A state machine for the process of updating a CHANGELOG.md. +enum _ChangelogUpdateState { + /// Looking for the first version section. + findingFirstSection, + + /// Looking for the first list entry in an existing section. + findingFirstListItem, + + /// Finished with updates. + finishedUpdating, +} + +/// A command to update the changelog, and optionally version, of packages. +class UpdateReleaseInfoCommand extends PackageLoopingCommand { + /// Creates a publish metadata updater command instance. + UpdateReleaseInfoCommand( + Directory packagesDir, { + GitDir? gitDir, + }) : super(packagesDir, gitDir: gitDir) { + argParser.addOption(_changelogFlag, + mandatory: true, + help: 'The changelog entry to add. ' + 'Each line will be a separate list entry.'); + argParser.addOption(_versionTypeFlag, + mandatory: true, + help: 'The version change level', + allowed: [ + _versionNext, + _versionMinimal, + _versionBugfix, + _versionMinor, + ], + allowedHelp: { + _versionNext: + 'No version change; just adds a NEXT entry to the changelog.', + _versionBugfix: 'Increments the bugfix version.', + _versionMinor: 'Increments the minor version.', + _versionMinimal: 'Depending on the changes to each package: ' + 'increments the bugfix version (for publishable changes), ' + "uses NEXT (for changes that don't need to be published), " + 'or skips (if no changes).', + }); + } + + static const String _changelogFlag = 'changelog'; + static const String _versionTypeFlag = 'version'; + + static const String _versionNext = 'next'; + static const String _versionBugfix = 'bugfix'; + static const String _versionMinor = 'minor'; + static const String _versionMinimal = 'minimal'; + + // The version change type, if there is a set type for all platforms. + // + // If null, either there is no version change, or it is dynamic (`minimal`). + _VersionIncrementType? _versionChange; + + // The cache of changed files, for dynamic version change determination. + // + // Only set for `minimal` version change. + late final List _changedFiles; + + @override + final String name = 'update-release-info'; + + @override + final String description = 'Updates CHANGELOG.md files, and optionally the ' + 'version in pubspec.yaml, in a way that is consistent with version-check ' + 'enforcement.'; + + @override + bool get hasLongOutput => false; + + @override + Future initializeRun() async { + if (getStringArg(_changelogFlag).trim().isEmpty) { + throw UsageException('Changelog message must not be empty.', usage); + } + switch (getStringArg(_versionTypeFlag)) { + case _versionMinor: + _versionChange = _VersionIncrementType.minor; + break; + case _versionBugfix: + _versionChange = _VersionIncrementType.bugfix; + break; + case _versionMinimal: + final GitVersionFinder gitVersionFinder = await retrieveVersionFinder(); + _changedFiles = await gitVersionFinder.getChangedFiles(); + // Anothing other than a fixed change is null. + _versionChange = null; + break; + case _versionNext: + _versionChange = null; + break; + default: + throw UnimplementedError('Unimplemented version change type'); + } + } + + @override + Future runForPackage(RepositoryPackage package) async { + String nextVersionString; + + _VersionIncrementType? versionChange = _versionChange; + + // If the change type is `minimal` determine what changes, if any, are + // needed. + if (versionChange == null && + getStringArg(_versionTypeFlag) == _versionMinimal) { + final Directory gitRoot = + packagesDir.fileSystem.directory((await gitDir).path); + final String relativePackagePath = + getRelativePosixPath(package.directory, from: gitRoot); + final PackageChangeState state = checkPackageChangeState(package, + changedPaths: _changedFiles, + relativePackagePath: relativePackagePath); + + if (!state.hasChanges) { + return PackageResult.skip('No changes to package'); + } + if (state.needsVersionChange) { + versionChange = _VersionIncrementType.bugfix; + } + } + + if (versionChange != null) { + final Version? updatedVersion = + _updatePubspecVersion(package, versionChange); + if (updatedVersion == null) { + return PackageResult.fail( + ['Could not determine current version.']); + } + nextVersionString = updatedVersion.toString(); + print('${indentation}Incremented version to $nextVersionString.'); + } else { + nextVersionString = 'NEXT'; + } + + final _ChangelogUpdateOutcome updateOutcome = + _updateChangelog(package, nextVersionString); + switch (updateOutcome) { + case _ChangelogUpdateOutcome.addedSection: + print('${indentation}Added a $nextVersionString section.'); + break; + case _ChangelogUpdateOutcome.updatedSection: + print('${indentation}Updated NEXT section.'); + break; + case _ChangelogUpdateOutcome.failed: + return PackageResult.fail(['Could not update CHANGELOG.md.']); + } + + return PackageResult.success(); + } + + _ChangelogUpdateOutcome _updateChangelog( + RepositoryPackage package, String version) { + if (!package.changelogFile.existsSync()) { + printError('${indentation}Missing CHANGELOG.md.'); + return _ChangelogUpdateOutcome.failed; + } + + final String newHeader = '## $version'; + final RegExp listItemPattern = RegExp(r'^(\s*[-*])'); + + final StringBuffer newChangelog = StringBuffer(); + _ChangelogUpdateState state = _ChangelogUpdateState.findingFirstSection; + bool updatedExistingSection = false; + + for (final String line in package.changelogFile.readAsLinesSync()) { + switch (state) { + case _ChangelogUpdateState.findingFirstSection: + final String trimmedLine = line.trim(); + if (trimmedLine.isEmpty) { + // Discard any whitespace at the top of the file. + } else if (trimmedLine == '## NEXT') { + // Replace the header with the new version (which may also be NEXT). + newChangelog.writeln(newHeader); + // Find the existing list to add to. + state = _ChangelogUpdateState.findingFirstListItem; + } else { + // The first content in the file isn't a NEXT section, so just add + // the new section. + [ + newHeader, + '', + ..._changelogAdditionsAsList(), + '', + line, // Don't drop the current line. + ].forEach(newChangelog.writeln); + state = _ChangelogUpdateState.finishedUpdating; + } + break; + case _ChangelogUpdateState.findingFirstListItem: + final RegExpMatch? match = listItemPattern.firstMatch(line); + if (match != null) { + final String listMarker = match[1]!; + // Add the new items on top. If the new change is changing the + // version, then the new item should be more relevant to package + // clients than anything that was already there. If it's still + // NEXT, the order doesn't matter. + [ + ..._changelogAdditionsAsList(listMarker: listMarker), + line, // Don't drop the current line. + ].forEach(newChangelog.writeln); + state = _ChangelogUpdateState.finishedUpdating; + updatedExistingSection = true; + } else if (line.trim().isEmpty) { + // Scan past empty lines, but keep them. + newChangelog.writeln(line); + } else { + printError(' Existing NEXT section has unrecognized format.'); + return _ChangelogUpdateOutcome.failed; + } + break; + case _ChangelogUpdateState.finishedUpdating: + // Once changes are done, add the rest of the lines as-is. + newChangelog.writeln(line); + break; + } + } + + package.changelogFile.writeAsStringSync(newChangelog.toString()); + + return updatedExistingSection + ? _ChangelogUpdateOutcome.updatedSection + : _ChangelogUpdateOutcome.addedSection; + } + + /// Returns the changelog to add as a Markdown list, using the given list + /// bullet style (default to the repository standard of '*'), and adding + /// any missing periods. + /// + /// E.g., 'A line\nAnother line.' will become: + /// ``` + /// [ '* A line.', '* Another line.' ] + /// ``` + Iterable _changelogAdditionsAsList({String listMarker = '*'}) { + return getStringArg(_changelogFlag).split('\n').map((String entry) { + String standardizedEntry = entry.trim(); + if (!standardizedEntry.endsWith('.')) { + standardizedEntry = '$standardizedEntry.'; + } + return '$listMarker $standardizedEntry'; + }); + } + + /// Updates the version in [package]'s pubspec according to [type], returning + /// the new version, or null if there was an error updating the version. + Version? _updatePubspecVersion( + RepositoryPackage package, _VersionIncrementType type) { + final Pubspec pubspec = package.parsePubspec(); + final Version? currentVersion = pubspec.version; + if (currentVersion == null) { + printError('${indentation}No version in pubspec.yaml'); + return null; + } + + // For versions less than 1.0, shift the change down one component per + // Dart versioning conventions. + final _VersionIncrementType adjustedType = currentVersion.major > 0 + ? type + : _VersionIncrementType.values[type.index - 1]; + + final Version newVersion = _nextVersion(currentVersion, adjustedType); + + // Write the new version to the pubspec. + final YamlEditor editablePubspec = + YamlEditor(package.pubspecFile.readAsStringSync()); + editablePubspec.update(['version'], newVersion.toString()); + package.pubspecFile.writeAsStringSync(editablePubspec.toString()); + + return newVersion; + } + + Version _nextVersion(Version version, _VersionIncrementType type) { + switch (type) { + case _VersionIncrementType.minor: + return version.nextMinor; + case _VersionIncrementType.bugfix: + return version.nextPatch; + case _VersionIncrementType.build: + final int buildNumber = + version.build.isEmpty ? 0 : version.build.first as int; + return Version(version.major, version.minor, version.patch, + build: '${buildNumber + 1}'); + } + } +} diff --git a/script/tool/lib/src/version_check_command.dart b/script/tool/lib/src/version_check_command.dart index b816ee56999c..62abdb2a432b 100644 --- a/script/tool/lib/src/version_check_command.dart +++ b/script/tool/lib/src/version_check_command.dart @@ -13,6 +13,7 @@ import 'package:pub_semver/pub_semver.dart'; import 'common/core.dart'; import 'common/git_version_finder.dart'; import 'common/package_looping_command.dart'; +import 'common/package_state_utils.dart'; import 'common/process_runner.dart'; import 'common/pub_version_finder.dart'; import 'common/repository_package.dart'; @@ -531,44 +532,16 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog. final Directory gitRoot = packagesDir.fileSystem.directory((await gitDir).path); final String relativePackagePath = - '${getRelativePosixPath(package.directory, from: gitRoot)}/'; - bool hasChanges = false; - bool needsVersionChange = false; - bool hasChangelogChange = false; - for (final String path in _changedFiles) { - // Only consider files within the package. - if (!path.startsWith(relativePackagePath)) { - continue; - } - hasChanges = true; - - final List components = p.posix.split(path); - final bool isChangelog = components.last == 'CHANGELOG.md'; - if (isChangelog) { - hasChangelogChange = true; - } + getRelativePosixPath(package.directory, from: gitRoot); - if (!needsVersionChange && - !isChangelog && - // The example's main.dart is shown on pub.dev, but for anything else - // in the example publishing has no purpose. - !(components.contains('example') && components.last != 'main.dart') && - // Changes to tests don't need to be published. - !components.contains('test') && - !components.contains('androidTest') && - !components.contains('RunnerTests') && - !components.contains('RunnerUITests') && - // Ignoring lints doesn't affect clients. - !components.contains('lint-baseline.xml')) { - needsVersionChange = true; - } - } + final PackageChangeState state = checkPackageChangeState(package, + changedPaths: _changedFiles, relativePackagePath: relativePackagePath); - if (!hasChanges) { + if (!state.hasChanges) { return null; } - if (needsVersionChange) { + if (state.needsVersionChange) { if (_getChangeDescription().split('\n').any((String line) => line.startsWith(_missingVersionChangeJustificationMarker))) { logWarning('Ignoring lack of version change due to ' @@ -586,7 +559,7 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog. } } - if (!hasChangelogChange) { + if (!state.hasChangelogChange) { if (_getChangeDescription().split('\n').any((String line) => line.startsWith(_missingChangelogChangeJustificationMarker))) { logWarning('Ignoring lack of CHANGELOG update due to ' diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index 32bfc1b62281..138c1183fa1c 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.8.5 +version: 0.8.6 dependencies: args: ^2.1.0 diff --git a/script/tool/test/common/package_state_utils_test.dart b/script/tool/test/common/package_state_utils_test.dart new file mode 100644 index 000000000000..cc9116a9ea25 --- /dev/null +++ b/script/tool/test/common/package_state_utils_test.dart @@ -0,0 +1,140 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +import 'package:file/file.dart'; +import 'package:file/memory.dart'; +import 'package:flutter_plugin_tools/src/common/package_state_utils.dart'; +import 'package:test/test.dart'; + +import '../util.dart'; + +void main() { + late FileSystem fileSystem; + late Directory packagesDir; + + setUp(() { + fileSystem = MemoryFileSystem(); + packagesDir = createPackagesDirectory(fileSystem: fileSystem); + }); + + group('checkPackageChangeState', () { + test('reports version change needed for code changes', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); + + const List changedFiles = [ + 'packages/a_package/lib/plugin.dart', + ]; + + final PackageChangeState state = checkPackageChangeState(package, + changedPaths: changedFiles, + relativePackagePath: 'packages/a_package'); + + expect(state.hasChanges, true); + expect(state.needsVersionChange, true); + }); + + test('handles trailing slash on package path', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); + + const List changedFiles = [ + 'packages/a_package/lib/plugin.dart', + ]; + + final PackageChangeState state = checkPackageChangeState(package, + changedPaths: changedFiles, + relativePackagePath: 'packages/a_package/'); + + expect(state.hasChanges, true); + expect(state.needsVersionChange, true); + expect(state.hasChangelogChange, false); + }); + + test('does not report version change exempt changes', () async { + final RepositoryPackage package = + createFakePlugin('a_plugin', packagesDir); + + const List changedFiles = [ + 'packages/a_plugin/example/android/lint-baseline.xml', + 'packages/a_plugin/example/android/src/androidTest/foo/bar/FooTest.java', + 'packages/a_plugin/example/ios/RunnerTests/Foo.m', + 'packages/a_plugin/example/ios/RunnerUITests/info.plist', + 'packages/a_plugin/tool/a_development_tool.dart', + 'packages/a_plugin/CHANGELOG.md', + ]; + + final PackageChangeState state = checkPackageChangeState(package, + changedPaths: changedFiles, + relativePackagePath: 'packages/a_plugin/'); + + expect(state.hasChanges, true); + expect(state.needsVersionChange, false); + expect(state.hasChangelogChange, true); + }); + + test('only considers a root "tool" folder to be special', () async { + final RepositoryPackage package = + createFakePlugin('a_plugin', packagesDir); + + const List changedFiles = [ + 'packages/a_plugin/lib/foo/tool/tool_thing.dart', + ]; + + final PackageChangeState state = checkPackageChangeState(package, + changedPaths: changedFiles, + relativePackagePath: 'packages/a_plugin/'); + + expect(state.hasChanges, true); + expect(state.needsVersionChange, true); + }); + + test('requires a version change for example main', () async { + final RepositoryPackage package = + createFakePlugin('a_plugin', packagesDir); + + const List changedFiles = [ + 'packages/a_plugin/example/lib/main.dart', + ]; + + final PackageChangeState state = checkPackageChangeState(package, + changedPaths: changedFiles, + relativePackagePath: 'packages/a_plugin/'); + + expect(state.hasChanges, true); + expect(state.needsVersionChange, true); + }); + + test('requires a version change for example readme.md', () async { + final RepositoryPackage package = + createFakePlugin('a_plugin', packagesDir); + + const List changedFiles = [ + 'packages/a_plugin/example/README.md', + ]; + + final PackageChangeState state = checkPackageChangeState(package, + changedPaths: changedFiles, + relativePackagePath: 'packages/a_plugin/'); + + expect(state.hasChanges, true); + expect(state.needsVersionChange, true); + }); + + test('requires a version change for example example.md', () async { + final RepositoryPackage package = + createFakePlugin('a_plugin', packagesDir); + + const List changedFiles = [ + 'packages/a_plugin/example/lib/example.md', + ]; + + final PackageChangeState state = checkPackageChangeState(package, + changedPaths: changedFiles, + relativePackagePath: 'packages/a_plugin/'); + + expect(state.hasChanges, true); + expect(state.needsVersionChange, true); + }); + }); +} diff --git a/script/tool/test/update_release_info_command_test.dart b/script/tool/test/update_release_info_command_test.dart new file mode 100644 index 000000000000..7e7ff54d5947 --- /dev/null +++ b/script/tool/test/update_release_info_command_test.dart @@ -0,0 +1,645 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io' as io; + +import 'package:args/command_runner.dart'; +import 'package:file/file.dart'; +import 'package:file/memory.dart'; +import 'package:flutter_plugin_tools/src/common/core.dart'; +import 'package:flutter_plugin_tools/src/update_release_info_command.dart'; +import 'package:mockito/mockito.dart'; +import 'package:test/test.dart'; + +import 'common/plugin_command_test.mocks.dart'; +import 'mocks.dart'; +import 'util.dart'; + +void main() { + late FileSystem fileSystem; + late Directory packagesDir; + late MockGitDir gitDir; + late RecordingProcessRunner processRunner; + late CommandRunner runner; + + setUp(() { + fileSystem = MemoryFileSystem(); + packagesDir = createPackagesDirectory(fileSystem: fileSystem); + processRunner = RecordingProcessRunner(); + + gitDir = MockGitDir(); + when(gitDir.path).thenReturn(packagesDir.parent.path); + when(gitDir.runCommand(any, throwOnError: anyNamed('throwOnError'))) + .thenAnswer((Invocation invocation) { + final List arguments = + invocation.positionalArguments[0]! as List; + // Route git calls through a process runner, to make mock output + // consistent with other processes. Attach the first argument to the + // command to make targeting the mock results easier. + final String gitCommand = arguments.removeAt(0); + return processRunner.run('git-$gitCommand', arguments); + }); + + final UpdateReleaseInfoCommand command = UpdateReleaseInfoCommand( + packagesDir, + gitDir: gitDir, + ); + runner = CommandRunner( + 'update_release_info_command', 'Test for update_release_info_command'); + runner.addCommand(command); + }); + + group('flags', () { + test('fails if --changelog is missing', () async { + Exception? commandError; + await runCapturingPrint(runner, [ + 'update-release-info', + '--version=next', + ], exceptionHandler: (Exception e) { + commandError = e; + }); + + expect(commandError, isA()); + }); + + test('fails if --changelog is blank', () async { + Exception? commandError; + await runCapturingPrint(runner, [ + 'update-release-info', + '--version=next', + '--changelog', + '', + ], exceptionHandler: (Exception e) { + commandError = e; + }); + + expect(commandError, isA()); + }); + + test('fails if --version is missing', () async { + Exception? commandError; + await runCapturingPrint( + runner, ['update-release-info', '--changelog', ''], + exceptionHandler: (Exception e) { + commandError = e; + }); + + expect(commandError, isA()); + }); + + test('fails if --version is an unknown value', () async { + Exception? commandError; + await runCapturingPrint(runner, [ + 'update-release-info', + '--version=foo', + '--changelog', + '', + ], exceptionHandler: (Exception e) { + commandError = e; + }); + + expect(commandError, isA()); + }); + }); + + group('changelog', () { + test('adds new NEXT section', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.0'); + + const String originalChangelog = ''' +## 1.0.0 + +* Previous changes. +'''; + package.changelogFile.writeAsStringSync(originalChangelog); + + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=next', + '--changelog', + 'A change.' + ]); + + final String newChangelog = package.changelogFile.readAsStringSync(); + const String expectedChangeLog = ''' +## NEXT + +* A change. + +$originalChangelog'''; + + expect( + output, + containsAllInOrder([ + contains(' Added a NEXT section.'), + ]), + ); + expect(newChangelog, expectedChangeLog); + }); + + test('adds to existing NEXT section', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.0'); + + const String originalChangelog = ''' +## NEXT + +* Already-pending changes. + +## 1.0.0 + +* Old changes. +'''; + package.changelogFile.writeAsStringSync(originalChangelog); + + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=next', + '--changelog', + 'A change.' + ]); + + final String newChangelog = package.changelogFile.readAsStringSync(); + const String expectedChangeLog = ''' +## NEXT + +* A change. +* Already-pending changes. + +## 1.0.0 + +* Old changes. +'''; + + expect(output, + containsAllInOrder([contains(' Updated NEXT section.')])); + expect(newChangelog, expectedChangeLog); + }); + + test('adds new version section', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.0'); + + const String originalChangelog = ''' +## 1.0.0 + +* Previous changes. +'''; + package.changelogFile.writeAsStringSync(originalChangelog); + + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=bugfix', + '--changelog', + 'A change.' + ]); + + final String newChangelog = package.changelogFile.readAsStringSync(); + const String expectedChangeLog = ''' +## 1.0.1 + +* A change. + +$originalChangelog'''; + + expect( + output, + containsAllInOrder([ + contains(' Added a 1.0.1 section.'), + ]), + ); + expect(newChangelog, expectedChangeLog); + }); + + test('converts existing NEXT section to version section', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.0'); + + const String originalChangelog = ''' +## NEXT + +* Already-pending changes. + +## 1.0.0 + +* Old changes. +'''; + package.changelogFile.writeAsStringSync(originalChangelog); + + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=bugfix', + '--changelog', + 'A change.' + ]); + + final String newChangelog = package.changelogFile.readAsStringSync(); + const String expectedChangeLog = ''' +## 1.0.1 + +* A change. +* Already-pending changes. + +## 1.0.0 + +* Old changes. +'''; + + expect(output, + containsAllInOrder([contains(' Updated NEXT section.')])); + expect(newChangelog, expectedChangeLog); + }); + + test('treats multiple lines as multiple list items', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.0'); + + const String originalChangelog = ''' +## 1.0.0 + +* Previous changes. +'''; + package.changelogFile.writeAsStringSync(originalChangelog); + + await runCapturingPrint(runner, [ + 'update-release-info', + '--version=bugfix', + '--changelog', + 'First change.\nSecond change.' + ]); + + final String newChangelog = package.changelogFile.readAsStringSync(); + const String expectedChangeLog = ''' +## 1.0.1 + +* First change. +* Second change. + +$originalChangelog'''; + + expect(newChangelog, expectedChangeLog); + }); + + test('adds a period to any lines missing it, and removes whitespace', + () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.0'); + + const String originalChangelog = ''' +## 1.0.0 + +* Previous changes. +'''; + package.changelogFile.writeAsStringSync(originalChangelog); + + await runCapturingPrint(runner, [ + 'update-release-info', + '--version=bugfix', + '--changelog', + 'First change \nSecond change' + ]); + + final String newChangelog = package.changelogFile.readAsStringSync(); + const String expectedChangeLog = ''' +## 1.0.1 + +* First change. +* Second change. + +$originalChangelog'''; + + expect(newChangelog, expectedChangeLog); + }); + + test('handles non-standard changelog format', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.0'); + + const String originalChangelog = ''' +# 1.0.0 + +* A version with the wrong heading format. +'''; + package.changelogFile.writeAsStringSync(originalChangelog); + + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=next', + '--changelog', + 'A change.' + ]); + + final String newChangelog = package.changelogFile.readAsStringSync(); + const String expectedChangeLog = ''' +## NEXT + +* A change. + +$originalChangelog'''; + + expect(output, + containsAllInOrder([contains(' Added a NEXT section.')])); + expect(newChangelog, expectedChangeLog); + }); + + test('adds to existing NEXT section using - list style', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.0'); + + const String originalChangelog = ''' +## NEXT + + - Already-pending changes. + +## 1.0.0 + + - Previous changes. +'''; + package.changelogFile.writeAsStringSync(originalChangelog); + + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=next', + '--changelog', + 'A change.' + ]); + + final String newChangelog = package.changelogFile.readAsStringSync(); + const String expectedChangeLog = ''' +## NEXT + + - A change. + - Already-pending changes. + +## 1.0.0 + + - Previous changes. +'''; + + expect(output, + containsAllInOrder([contains(' Updated NEXT section.')])); + expect(newChangelog, expectedChangeLog); + }); + + test('skips for "minimal" when there are no changes at all', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.1'); + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: ''' +packages/different_package/test/plugin_test.dart +'''), + ]; + final String originalChangelog = package.changelogFile.readAsStringSync(); + + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=minimal', + '--changelog', + 'A change.', + ]); + + final String version = package.parsePubspec().version?.toString() ?? ''; + expect(version, '1.0.1'); + expect(package.changelogFile.readAsStringSync(), originalChangelog); + expect( + output, + containsAllInOrder([ + contains('No changes to package'), + contains('Skipped 1 package') + ])); + }); + + test('fails if CHANGELOG.md is missing', () async { + createFakePackage('a_package', packagesDir, includeCommonFiles: false); + + Error? commandError; + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=minor', + '--changelog', + 'A change.', + ], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect(output, + containsAllInOrder([contains(' Missing CHANGELOG.md.')])); + }); + + test('fails if CHANGELOG.md has unexpected NEXT block format', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.0'); + + const String originalChangelog = ''' +## NEXT + +Some free-form text that isn't a list. + +## 1.0.0 + +- Previous changes. +'''; + package.changelogFile.writeAsStringSync(originalChangelog); + + Error? commandError; + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=minor', + '--changelog', + 'A change.', + ], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains(' Existing NEXT section has unrecognized format.') + ])); + }); + }); + + group('pubspec', () { + test('does not change for --next', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.0'); + + await runCapturingPrint(runner, [ + 'update-release-info', + '--version=next', + '--changelog', + 'A change.' + ]); + + final String version = package.parsePubspec().version?.toString() ?? ''; + expect(version, '1.0.0'); + }); + + test('updates bugfix version for pre-1.0 without existing build number', + () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '0.1.0'); + + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=bugfix', + '--changelog', + 'A change.', + ]); + + final String version = package.parsePubspec().version?.toString() ?? ''; + expect(version, '0.1.0+1'); + expect( + output, + containsAllInOrder( + [contains(' Incremented version to 0.1.0+1')])); + }); + + test('updates bugfix version for pre-1.0 with existing build number', + () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '0.1.0+2'); + + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=bugfix', + '--changelog', + 'A change.', + ]); + + final String version = package.parsePubspec().version?.toString() ?? ''; + expect(version, '0.1.0+3'); + expect( + output, + containsAllInOrder( + [contains(' Incremented version to 0.1.0+3')])); + }); + + test('updates bugfix version for post-1.0', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.1'); + + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=bugfix', + '--changelog', + 'A change.', + ]); + + final String version = package.parsePubspec().version?.toString() ?? ''; + expect(version, '1.0.2'); + expect( + output, + containsAllInOrder( + [contains(' Incremented version to 1.0.2')])); + }); + + test('updates minor version for pre-1.0', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '0.1.0+2'); + + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=minor', + '--changelog', + 'A change.', + ]); + + final String version = package.parsePubspec().version?.toString() ?? ''; + expect(version, '0.1.1'); + expect( + output, + containsAllInOrder( + [contains(' Incremented version to 0.1.1')])); + }); + + test('updates minor version for post-1.0', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.1'); + + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=minor', + '--changelog', + 'A change.', + ]); + + final String version = package.parsePubspec().version?.toString() ?? ''; + expect(version, '1.1.0'); + expect( + output, + containsAllInOrder( + [contains(' Incremented version to 1.1.0')])); + }); + + test('updates bugfix version for "minimal" with publish-worthy changes', + () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.1'); + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: ''' +packages/a_package/lib/plugin.dart +'''), + ]; + + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=minimal', + '--changelog', + 'A change.', + ]); + + final String version = package.parsePubspec().version?.toString() ?? ''; + expect(version, '1.0.2'); + expect( + output, + containsAllInOrder( + [contains(' Incremented version to 1.0.2')])); + }); + + test('no version change for "minimal" with non-publish-worthy changes', + () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.1'); + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: ''' +packages/a_package/test/plugin_test.dart +'''), + ]; + + await runCapturingPrint(runner, [ + 'update-release-info', + '--version=minimal', + '--changelog', + 'A change.', + ]); + + final String version = package.parsePubspec().version?.toString() ?? ''; + expect(version, '1.0.1'); + }); + + test('fails if there is no version in pubspec', () async { + createFakePackage('a_package', packagesDir, version: null); + + Error? commandError; + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=minor', + '--changelog', + 'A change.', + ], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder( + [contains('Could not determine current version.')])); + }); + }); +} diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart index 7255bf9a9276..4c99873e937d 100644 --- a/script/tool/test/util.dart +++ b/script/tool/test/util.dart @@ -310,14 +310,15 @@ String _pluginPlatformSection( return entry; } -typedef ErrorHandler = void Function(Error error); - /// Run the command [runner] with the given [args] and return /// what was printed. /// A custom [errorHandler] can be used to handle the runner error as desired without throwing. Future> runCapturingPrint( - CommandRunner runner, List args, - {ErrorHandler? errorHandler}) async { + CommandRunner runner, + List args, { + Function(Error error)? errorHandler, + Function(Exception error)? exceptionHandler, +}) async { final List prints = []; final ZoneSpecification spec = ZoneSpecification( print: (_, __, ___, String message) { @@ -333,6 +334,11 @@ Future> runCapturingPrint( rethrow; } errorHandler(e); + } on Exception catch (e) { + if (exceptionHandler == null) { + rethrow; + } + exceptionHandler(e); } return prints; From f33222a1a9cbbf067ef56af6816b553ea458e1f8 Mon Sep 17 00:00:00 2001 From: Alexandre Zollinger Chohfi Date: Wed, 18 May 2022 17:02:19 -0700 Subject: [PATCH 584/600] [local_auth] Adds federated Windows support (#5776) --- packages/local_auth/local_auth/CHANGELOG.md | 4 + packages/local_auth/local_auth/README.md | 8 +- .../local_auth/example/windows/CMakeLists.txt | 95 +++++++ .../example/windows/flutter/CMakeLists.txt | 103 ++++++++ .../windows/flutter/generated_plugins.cmake | 24 ++ .../example/windows/runner/CMakeLists.txt | 17 ++ .../example/windows/runner/Runner.rc | 121 +++++++++ .../example/windows/runner/flutter_window.cpp | 64 +++++ .../example/windows/runner/flutter_window.h | 36 +++ .../example/windows/runner/main.cpp | 45 ++++ .../example/windows/runner/resource.h | 16 ++ .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../windows/runner/runner.exe.manifest | 20 ++ .../example/windows/runner/utils.cpp | 66 +++++ .../local_auth/example/windows/runner/utils.h | 22 ++ .../example/windows/runner/win32_window.cpp | 240 ++++++++++++++++++ .../example/windows/runner/win32_window.h | 98 +++++++ .../local_auth/lib/error_codes.dart | 3 + .../local_auth/lib/src/local_auth.dart | 4 +- packages/local_auth/local_auth/pubspec.yaml | 5 +- .../local_auth/test/local_auth_test.dart | 2 + 21 files changed, 988 insertions(+), 5 deletions(-) create mode 100644 packages/local_auth/local_auth/example/windows/CMakeLists.txt create mode 100644 packages/local_auth/local_auth/example/windows/flutter/CMakeLists.txt create mode 100644 packages/local_auth/local_auth/example/windows/flutter/generated_plugins.cmake create mode 100644 packages/local_auth/local_auth/example/windows/runner/CMakeLists.txt create mode 100644 packages/local_auth/local_auth/example/windows/runner/Runner.rc create mode 100644 packages/local_auth/local_auth/example/windows/runner/flutter_window.cpp create mode 100644 packages/local_auth/local_auth/example/windows/runner/flutter_window.h create mode 100644 packages/local_auth/local_auth/example/windows/runner/main.cpp create mode 100644 packages/local_auth/local_auth/example/windows/runner/resource.h create mode 100644 packages/local_auth/local_auth/example/windows/runner/resources/app_icon.ico create mode 100644 packages/local_auth/local_auth/example/windows/runner/runner.exe.manifest create mode 100644 packages/local_auth/local_auth/example/windows/runner/utils.cpp create mode 100644 packages/local_auth/local_auth/example/windows/runner/utils.h create mode 100644 packages/local_auth/local_auth/example/windows/runner/win32_window.cpp create mode 100644 packages/local_auth/local_auth/example/windows/runner/win32_window.h diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index 8a2743e6140b..1aae73d7393e 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.0 + +* Adds Windows support. + ## 2.0.2 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/local_auth/local_auth/README.md b/packages/local_auth/local_auth/README.md index 7ec4829ba521..3077f8e60a1f 100644 --- a/packages/local_auth/local_auth/README.md +++ b/packages/local_auth/local_auth/README.md @@ -8,9 +8,9 @@ the user. On supported devices, this includes authentication with biometrics such as fingerprint or facial recognition. -| | Android | iOS | -|-------------|-----------|------| -| **Support** | SDK 16+\* | 9.0+ | +| | Android | iOS | Windows | +|-------------|-----------|------|-------------| +| **Support** | SDK 16+\* | 9.0+ | Windows 10+ | ## Usage @@ -90,6 +90,8 @@ final bool didAuthenticate = await auth.authenticate( options: const AuthenticationOptions(biometricOnly: true)); ``` +*Note*: `biometricOnly` is not supported on Windows since the Windows implementation's underlying API (Windows Hello) doesn't support selecting the authentication method. + #### Dialogs The plugin provides default dialogs for the following cases: diff --git a/packages/local_auth/local_auth/example/windows/CMakeLists.txt b/packages/local_auth/local_auth/example/windows/CMakeLists.txt new file mode 100644 index 000000000000..e013bd88bcb1 --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/CMakeLists.txt @@ -0,0 +1,95 @@ +cmake_minimum_required(VERSION 3.14) +project(example LANGUAGES CXX) + +set(BINARY_NAME "example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() + +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build +add_subdirectory("runner") + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) \ No newline at end of file diff --git a/packages/local_auth/local_auth/example/windows/flutter/CMakeLists.txt b/packages/local_auth/local_auth/example/windows/flutter/CMakeLists.txt new file mode 100644 index 000000000000..d83cc95319b6 --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,103 @@ +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) \ No newline at end of file diff --git a/packages/local_auth/local_auth/example/windows/flutter/generated_plugins.cmake b/packages/local_auth/local_auth/example/windows/flutter/generated_plugins.cmake new file mode 100644 index 000000000000..ef187dcae56f --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/flutter/generated_plugins.cmake @@ -0,0 +1,24 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + local_auth_windows +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/packages/local_auth/local_auth/example/windows/runner/CMakeLists.txt b/packages/local_auth/local_auth/example/windows/runner/CMakeLists.txt new file mode 100644 index 000000000000..2520aa9e5fc7 --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) +apply_standard_settings(${BINARY_NAME}) +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") +add_dependencies(${BINARY_NAME} flutter_assemble) \ No newline at end of file diff --git a/packages/local_auth/local_auth/example/windows/runner/Runner.rc b/packages/local_auth/local_auth/example/windows/runner/Runner.rc new file mode 100644 index 000000000000..7e35b9f56a22 --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#ifdef FLUTTER_BUILD_NUMBER +#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#else +#define VERSION_AS_NUMBER 1,0,0 +#endif + +#ifdef FLUTTER_BUILD_NAME +#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "io.flutter.plugins" "\0" + VALUE "FileDescription", "example" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2022 io.flutter.plugins. All rights reserved." "\0" + VALUE "OriginalFilename", "example.exe" "\0" + VALUE "ProductName", "example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/packages/local_auth/local_auth/example/windows/runner/flutter_window.cpp b/packages/local_auth/local_auth/example/windows/runner/flutter_window.cpp new file mode 100644 index 000000000000..217bf9b69e67 --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/flutter_window.cpp @@ -0,0 +1,64 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/packages/local_auth/local_auth/example/windows/runner/flutter_window.h b/packages/local_auth/local_auth/example/windows/runner/flutter_window.h new file mode 100644 index 000000000000..7cbf3d3ebbb2 --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/flutter_window.h @@ -0,0 +1,36 @@ +// 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. +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/packages/local_auth/local_auth/example/windows/runner/main.cpp b/packages/local_auth/local_auth/example/windows/runner/main.cpp new file mode 100644 index 000000000000..1285aabf714a --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/main.cpp @@ -0,0 +1,45 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t* command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/packages/local_auth/local_auth/example/windows/runner/resource.h b/packages/local_auth/local_auth/example/windows/runner/resource.h new file mode 100644 index 000000000000..d5d958dc4257 --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/packages/local_auth/local_auth/example/windows/runner/resources/app_icon.ico b/packages/local_auth/local_auth/example/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/packages/local_auth/local_auth/example/windows/runner/runner.exe.manifest b/packages/local_auth/local_auth/example/windows/runner/runner.exe.manifest new file mode 100644 index 000000000000..c977c4a42589 --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/packages/local_auth/local_auth/example/windows/runner/utils.cpp b/packages/local_auth/local_auth/example/windows/runner/utils.cpp new file mode 100644 index 000000000000..8b8eaa54539a --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/utils.cpp @@ -0,0 +1,66 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE* unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = + ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, + nullptr, 0, nullptr, nullptr); + if (target_length == 0) { + return std::string(); + } + std::string utf8_string; + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/packages/local_auth/local_auth/example/windows/runner/utils.h b/packages/local_auth/local_auth/example/windows/runner/utils.h new file mode 100644 index 000000000000..6d1cc48f0426 --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/utils.h @@ -0,0 +1,22 @@ +// 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. +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/packages/local_auth/local_auth/example/windows/runner/win32_window.cpp b/packages/local_auth/local_auth/example/windows/runner/win32_window.cpp new file mode 100644 index 000000000000..34738de2d35b --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/win32_window.cpp @@ -0,0 +1,240 @@ +// 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. +#include "win32_window.h" + +#include + +#include "resource.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + FreeLibrary(user32_module); + } +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { ++g_active_window_count; } + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::CreateAndShow(const std::wstring& title, const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { return window_handle_; } + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} diff --git a/packages/local_auth/local_auth/example/windows/runner/win32_window.h b/packages/local_auth/local_auth/example/windows/runner/win32_window.h new file mode 100644 index 000000000000..0f8bd1b7f920 --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/win32_window.h @@ -0,0 +1,98 @@ +// 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. +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates and shows a win32 window with |title| and position and size using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring& title, const Point& origin, + const Size& size); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/packages/local_auth/local_auth/lib/error_codes.dart b/packages/local_auth/local_auth/lib/error_codes.dart index 15660ee948df..8959bf297700 100644 --- a/packages/local_auth/local_auth/lib/error_codes.dart +++ b/packages/local_auth/local_auth/lib/error_codes.dart @@ -24,3 +24,6 @@ const String lockedOut = 'LockedOut'; /// Indicates the API is locked out more persistently than [lockedOut]. /// Strong authentication like PIN/Pattern/Password is required to unlock. const String permanentlyLockedOut = 'PermanentlyLockedOut'; + +/// Indicates that the biometricOnly parameter can't be true on Windows +const String biometricOnlyNotSupported = 'biometricOnlyNotSupported'; diff --git a/packages/local_auth/local_auth/lib/src/local_auth.dart b/packages/local_auth/local_auth/lib/src/local_auth.dart index 206bd04f7b32..e369f67187a5 100644 --- a/packages/local_auth/local_auth/lib/src/local_auth.dart +++ b/packages/local_auth/local_auth/lib/src/local_auth.dart @@ -14,6 +14,7 @@ import 'package:flutter/services.dart'; import 'package:local_auth_android/local_auth_android.dart'; import 'package:local_auth_ios/local_auth_ios.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; +import 'package:local_auth_windows/local_auth_windows.dart'; /// A Flutter plugin for authenticating the user identity locally. class LocalAuthentication { @@ -39,7 +40,8 @@ class LocalAuthentication { {required String localizedReason, Iterable authMessages = const [ IOSAuthMessages(), - AndroidAuthMessages() + AndroidAuthMessages(), + WindowsAuthMessages() ], AuthenticationOptions options = const AuthenticationOptions()}) { return LocalAuthPlatform.instance.authenticate( diff --git a/packages/local_auth/local_auth/pubspec.yaml b/packages/local_auth/local_auth/pubspec.yaml index 119b8d778cbc..a555150617b8 100644 --- a/packages/local_auth/local_auth/pubspec.yaml +++ b/packages/local_auth/local_auth/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Android and iOS devices to allow local authentication via fingerprint, touch ID, face ID, passcode, pin, or pattern. repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 2.0.2 +version: 2.1.0 environment: sdk: ">=2.14.0 <3.0.0" @@ -16,6 +16,8 @@ flutter: default_package: local_auth_android ios: default_package: local_auth_ios + windows: + default_package: local_auth_windows dependencies: flutter: @@ -24,6 +26,7 @@ dependencies: local_auth_android: ^1.0.0 local_auth_ios: ^1.0.1 local_auth_platform_interface: ^1.0.1 + local_auth_windows: ^1.0.0 dev_dependencies: flutter_driver: diff --git a/packages/local_auth/local_auth/test/local_auth_test.dart b/packages/local_auth/local_auth/test/local_auth_test.dart index 069f9fec2966..844c981e8120 100644 --- a/packages/local_auth/local_auth/test/local_auth_test.dart +++ b/packages/local_auth/local_auth/test/local_auth_test.dart @@ -8,6 +8,7 @@ import 'package:local_auth/local_auth.dart'; import 'package:local_auth_android/local_auth_android.dart'; import 'package:local_auth_ios/local_auth_ios.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; +import 'package:local_auth_windows/local_auth_windows.dart'; import 'package:mockito/mockito.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; @@ -34,6 +35,7 @@ void main() { authMessages: [ const IOSAuthMessages(), const AndroidAuthMessages(), + const WindowsAuthMessages(), ], options: const AuthenticationOptions(), )).called(1); From 1e753ab0c4c1988ceba3cc3da58089dd29893206 Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Thu, 19 May 2022 04:48:10 -0700 Subject: [PATCH 585/600] [google_sign_in] Upgrade to GoogleSignIn 6.2, support arm64 simulators (#5708) --- .../google_sign_in/CHANGELOG.md | 1 + .../google_sign_in/example/ios/Podfile | 4 - .../ios/Runner.xcodeproj/project.pbxproj | 2 - .../google_sign_in_ios/CHANGELOG.md | 4 + .../google_sign_in_ios/example/ios/Podfile | 4 - .../ios/Runner.xcodeproj/project.pbxproj | 6 +- .../ios/RunnerTests/GoogleSignInTests.m | 430 +++++++++++++----- .../ios/Classes/FLTGoogleSignInPlugin.m | 248 +++++----- .../ios/google_sign_in_ios.podspec | 7 +- .../google_sign_in_ios/pubspec.yaml | 4 +- 10 files changed, 458 insertions(+), 252 deletions(-) diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index 9edadf7b0469..86a3b565da21 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -2,6 +2,7 @@ * Updates tests to use a mock platform instead of relying on default method channel implementation internals. +* Removes example workaround to build for arm64 iOS simulators. ## 5.3.1 diff --git a/packages/google_sign_in/google_sign_in/example/ios/Podfile b/packages/google_sign_in/google_sign_in/example/ios/Podfile index 56085c312df7..f7d6a5e68c3a 100644 --- a/packages/google_sign_in/google_sign_in/example/ios/Podfile +++ b/packages/google_sign_in/google_sign_in/example/ios/Podfile @@ -34,9 +34,5 @@ end post_install do |installer| installer.pods_project.targets.each do |target| flutter_additional_ios_build_settings(target) - target.build_configurations.each do |build_configuration| - # GoogleSignIn does not support arm64 simulators. - build_configuration.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64 i386' - end end end diff --git a/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/project.pbxproj b/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/project.pbxproj index 8909bb9b31c6..6c698e15ba15 100644 --- a/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/project.pbxproj @@ -426,7 +426,6 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ENABLE_BITCODE = NO; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", @@ -448,7 +447,6 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ENABLE_BITCODE = NO; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", diff --git a/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md b/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md index 90069d05f442..e5de49da7f22 100644 --- a/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 5.3.0 + +* Supports arm64 iOS simulators by increasing GoogleSignIn dependency to version 6.2. + ## 5.2.7 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile b/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile index b20e1ad2fca0..6c315d202770 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile @@ -42,9 +42,5 @@ end post_install do |installer| installer.pods_project.targets.each do |target| flutter_additional_ios_build_settings(target) - target.build_configurations.each do |build_configuration| - # GoogleSignIn does not support arm64 simulators. - build_configuration.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64 i386' - end end end diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj index f2bf4ebc514e..a7f2019ac311 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -384,7 +384,7 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", - "${PODS_ROOT}/GoogleSignIn/Resources/GoogleSignIn.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/GoogleSignIn/GoogleSignIn.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( @@ -603,7 +603,6 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ENABLE_BITCODE = NO; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", @@ -625,7 +624,6 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ENABLE_BITCODE = NO; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", @@ -647,7 +645,6 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; INFOPLIST_FILE = RunnerTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; @@ -662,7 +659,6 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; INFOPLIST_FILE = RunnerTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m b/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m index 3bc08d18604a..7efd490f30fe 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m @@ -64,66 +64,103 @@ - (void)testSignOut { } - (void)testDisconnect { + [[self.mockSignIn stub] disconnectWithCallback:[OCMArg invokeBlockWithArgs:[NSNull null], nil]]; FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"disconnect" arguments:nil]; + XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; [self.plugin handleMethodCall:methodCall - result:^(id result){ + result:^(NSDictionary *result) { + XCTAssertEqualObjects(result, @{}); + [expectation fulfill]; }]; - OCMVerify([self.mockSignIn disconnect]); + [self waitForExpectationsWithTimeout:5.0 handler:nil]; } -#pragma mark - Init - -- (void)testInitGoogleServiceInfoPlist { - FlutterMethodCall *methodCall = [FlutterMethodCall - methodCallWithMethodName:@"init" - arguments:@{@"scopes" : @[ @"mockScope1" ], @"hostedDomain" : @"example.com"}]; +- (void)testDisconnectIgnoresError { + NSError *error = [NSError errorWithDomain:kGIDSignInErrorDomain + code:kGIDSignInErrorCodeHasNoAuthInKeychain + userInfo:nil]; + [[self.mockSignIn stub] disconnectWithCallback:[OCMArg invokeBlockWithArgs:error, nil]]; + FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"disconnect" + arguments:nil]; XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; [self.plugin handleMethodCall:methodCall - result:^(id result) { - XCTAssertNil(result); + result:^(NSDictionary *result) { + XCTAssertEqualObjects(result, @{}); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:5.0 handler:nil]; - - id mockSignIn = self.mockSignIn; - OCMVerify([mockSignIn setScopes:@[ @"mockScope1" ]]); - OCMVerify([mockSignIn setHostedDomain:@"example.com"]); - - // Set in example app GoogleService-Info.plist. - OCMVerify([mockSignIn - setClientID:@"479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com"]); - OCMVerify([mockSignIn setServerClientID:@"YOUR_SERVER_CLIENT_ID"]); } -- (void)testInitNullDomain { - FlutterMethodCall *methodCall = +#pragma mark - Init + +- (void)testInitGoogleServiceInfoPlist { + FlutterMethodCall *initMethodCall = [FlutterMethodCall methodCallWithMethodName:@"init" - arguments:@{@"hostedDomain" : [NSNull null]}]; + arguments:@{@"hostedDomain" : @"example.com"}]; - XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; - [self.plugin handleMethodCall:methodCall - result:^(id r) { - [expectation fulfill]; + XCTestExpectation *initExpectation = + [self expectationWithDescription:@"expect result returns true"]; + [self.plugin handleMethodCall:initMethodCall + result:^(id result) { + XCTAssertNil(result); + [initExpectation fulfill]; }]; [self waitForExpectationsWithTimeout:5.0 handler:nil]; - OCMVerify([self.mockSignIn setHostedDomain:nil]); + + // Initialization values used in the next sign in request. + FlutterMethodCall *signInMethodCall = [FlutterMethodCall methodCallWithMethodName:@"signIn" + arguments:nil]; + [self.plugin handleMethodCall:signInMethodCall + result:^(id r){ + }]; + OCMVerify([self.mockSignIn + signInWithConfiguration:[OCMArg checkWithBlock:^BOOL(GIDConfiguration *configuration) { + // Set in example app GoogleService-Info.plist. + return + [configuration.hostedDomain isEqualToString:@"example.com"] && + [configuration.clientID + isEqualToString: + @"479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com"] && + [configuration.serverClientID isEqualToString:@"YOUR_SERVER_CLIENT_ID"]; + }] + presentingViewController:[OCMArg isKindOfClass:[FlutterViewController class]] + hint:nil + additionalScopes:OCMOCK_ANY + callback:OCMOCK_ANY]); } -- (void)testInitDynamicClientId { - FlutterMethodCall *methodCall = - [FlutterMethodCall methodCallWithMethodName:@"init" - arguments:@{@"clientId" : @"mockClientId"}]; +- (void)testInitDynamicClientIdNullDomain { + FlutterMethodCall *initMethodCall = [FlutterMethodCall + methodCallWithMethodName:@"init" + arguments:@{@"hostedDomain" : [NSNull null], @"clientId" : @"mockClientId"}]; - XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; - [self.plugin handleMethodCall:methodCall - result:^(id r) { - [expectation fulfill]; + XCTestExpectation *initExpectation = + [self expectationWithDescription:@"expect result returns true"]; + [self.plugin handleMethodCall:initMethodCall + result:^(id result) { + XCTAssertNil(result); + [initExpectation fulfill]; }]; [self waitForExpectationsWithTimeout:5.0 handler:nil]; - OCMVerify([self.mockSignIn setClientID:@"mockClientId"]); + + // Initialization values used in the next sign in request. + FlutterMethodCall *signInMethodCall = [FlutterMethodCall methodCallWithMethodName:@"signIn" + arguments:nil]; + [self.plugin handleMethodCall:signInMethodCall + result:^(id r){ + }]; + OCMVerify([self.mockSignIn + signInWithConfiguration:[OCMArg checkWithBlock:^BOOL(GIDConfiguration *configuration) { + return configuration.hostedDomain == nil && + [configuration.clientID isEqualToString:@"mockClientId"]; + }] + presentingViewController:[OCMArg isKindOfClass:[FlutterViewController class]] + hint:nil + additionalScopes:OCMOCK_ANY + callback:OCMOCK_ANY]); } #pragma mark - Is signed in @@ -161,59 +198,195 @@ - (void)testIsSignedIn { #pragma mark - Sign in silently - (void)testSignInSilently { - OCMExpect([self.mockSignIn restorePreviousSignIn]); + id mockUser = OCMClassMock([GIDGoogleUser class]); + OCMStub([mockUser userID]).andReturn(@"mockID"); + + [[self.mockSignIn stub] + restorePreviousSignInWithCallback:[OCMArg invokeBlockWithArgs:mockUser, [NSNull null], nil]]; FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"signInSilently" arguments:nil]; + XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; [self.plugin handleMethodCall:methodCall - result:^(id result){ + result:^(NSDictionary *result) { + XCTAssertEqualObjects(result[@"displayName"], [NSNull null]); + XCTAssertEqualObjects(result[@"email"], [NSNull null]); + XCTAssertEqualObjects(result[@"id"], @"mockID"); + XCTAssertEqualObjects(result[@"photoUrl"], [NSNull null]); + XCTAssertEqualObjects(result[@"serverAuthCode"], [NSNull null]); + [expectation fulfill]; }]; - OCMVerifyAll(self.mockSignIn); + [self waitForExpectationsWithTimeout:5.0 handler:nil]; } -- (void)testSignInSilentlyFailsConcurrently { +- (void)testSignInSilentlyWithError { + NSError *error = [NSError errorWithDomain:kGIDSignInErrorDomain + code:kGIDSignInErrorCodeHasNoAuthInKeychain + userInfo:nil]; + + [[self.mockSignIn stub] + restorePreviousSignInWithCallback:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; + FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"signInSilently" arguments:nil]; XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; - - OCMExpect([self.mockSignIn restorePreviousSignIn]).andDo(^(NSInvocation *invocation) { - // Simulate calling the same method while the previous one is in flight. - [self.plugin handleMethodCall:methodCall - result:^(FlutterError *result) { - XCTAssertEqualObjects(result.code, @"concurrent-requests"); - [expectation fulfill]; - }]; - }); - [self.plugin handleMethodCall:methodCall - result:^(id result){ + result:^(FlutterError *result) { + XCTAssertEqualObjects(result.code, @"sign_in_required"); + [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5.0 handler:nil]; } #pragma mark - Sign in - (void)testSignIn { + id mockUser = OCMClassMock([GIDGoogleUser class]); + id mockUserProfile = OCMClassMock([GIDProfileData class]); + OCMStub([mockUserProfile name]).andReturn(@"mockDisplay"); + OCMStub([mockUserProfile email]).andReturn(@"mock@example.com"); + OCMStub([mockUserProfile hasImage]).andReturn(YES); + OCMStub([mockUserProfile imageURLWithDimension:1337]) + .andReturn([NSURL URLWithString:@"https://example.com/profile.png"]); + + OCMStub([mockUser profile]).andReturn(mockUserProfile); + OCMStub([mockUser userID]).andReturn(@"mockID"); + OCMStub([mockUser serverAuthCode]).andReturn(@"mockAuthCode"); + + [[self.mockSignIn expect] + signInWithConfiguration:[OCMArg checkWithBlock:^BOOL(GIDConfiguration *configuration) { + return [configuration.clientID + isEqualToString: + @"479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com"]; + }] + presentingViewController:[OCMArg isKindOfClass:[FlutterViewController class]] + hint:nil + additionalScopes:@[] + callback:[OCMArg invokeBlockWithArgs:mockUser, [NSNull null], nil]]; + + FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"signIn" + arguments:nil]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; + [self.plugin + handleMethodCall:methodCall + result:^(NSDictionary *result) { + XCTAssertEqualObjects(result[@"displayName"], @"mockDisplay"); + XCTAssertEqualObjects(result[@"email"], @"mock@example.com"); + XCTAssertEqualObjects(result[@"id"], @"mockID"); + XCTAssertEqualObjects(result[@"photoUrl"], @"https://example.com/profile.png"); + XCTAssertEqualObjects(result[@"serverAuthCode"], @"mockAuthCode"); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:5.0 handler:nil]; + + OCMVerifyAll(self.mockSignIn); +} + +- (void)testSignInWithInitializedScopes { + FlutterMethodCall *initMethodCall = + [FlutterMethodCall methodCallWithMethodName:@"init" + arguments:@{@"scopes" : @[ @"initial1", @"initial2" ]}]; + + XCTestExpectation *initExpectation = + [self expectationWithDescription:@"expect result returns true"]; + [self.plugin handleMethodCall:initMethodCall + result:^(id result) { + XCTAssertNil(result); + [initExpectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:5.0 handler:nil]; + + id mockUser = OCMClassMock([GIDGoogleUser class]); + OCMStub([mockUser userID]).andReturn(@"mockID"); + + [[self.mockSignIn expect] + signInWithConfiguration:OCMOCK_ANY + presentingViewController:OCMOCK_ANY + hint:nil + additionalScopes:[OCMArg checkWithBlock:^BOOL(NSArray *scopes) { + return [[NSSet setWithArray:scopes] + isEqualToSet:[NSSet setWithObjects:@"initial1", @"initial2", nil]]; + }] + callback:[OCMArg invokeBlockWithArgs:mockUser, [NSNull null], nil]]; + FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"signIn" arguments:nil]; + XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; [self.plugin handleMethodCall:methodCall - result:^(NSNumber *result){ + result:^(NSDictionary *result) { + XCTAssertEqualObjects(result[@"id"], @"mockID"); + [expectation fulfill]; }]; + [self waitForExpectationsWithTimeout:5.0 handler:nil]; - id mockSignIn = self.mockSignIn; - OCMVerify([mockSignIn - setPresentingViewController:[OCMArg isKindOfClass:[FlutterViewController class]]]); - OCMVerify([mockSignIn signIn]); + OCMVerifyAll(self.mockSignIn); +} + +- (void)testSignInAlreadyGranted { + id mockUser = OCMClassMock([GIDGoogleUser class]); + OCMStub([mockUser userID]).andReturn(@"mockID"); + + [[self.mockSignIn stub] + signInWithConfiguration:OCMOCK_ANY + presentingViewController:OCMOCK_ANY + hint:nil + additionalScopes:OCMOCK_ANY + callback:[OCMArg invokeBlockWithArgs:mockUser, [NSNull null], nil]]; + + NSError *error = [NSError errorWithDomain:kGIDSignInErrorDomain + code:kGIDSignInErrorCodeScopesAlreadyGranted + userInfo:nil]; + [[self.mockSignIn stub] addScopes:OCMOCK_ANY + presentingViewController:OCMOCK_ANY + callback:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; + + FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"signIn" + arguments:nil]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; + [self.plugin handleMethodCall:methodCall + result:^(NSDictionary *result) { + XCTAssertEqualObjects(result[@"id"], @"mockID"); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:5.0 handler:nil]; +} + +- (void)testSignInError { + NSError *error = [NSError errorWithDomain:kGIDSignInErrorDomain + code:kGIDSignInErrorCodeCanceled + userInfo:nil]; + [[self.mockSignIn stub] + signInWithConfiguration:OCMOCK_ANY + presentingViewController:OCMOCK_ANY + hint:nil + additionalScopes:OCMOCK_ANY + callback:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; + + FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"signIn" + arguments:nil]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; + [self.plugin handleMethodCall:methodCall + result:^(FlutterError *result) { + XCTAssertEqualObjects(result.code, @"sign_in_canceled"); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:5.0 handler:nil]; } -- (void)testSignInExecption { +- (void)testSignInException { FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"signIn" arguments:nil]; - OCMExpect([self.mockSignIn signIn]) + OCMExpect([self.mockSignIn signInWithConfiguration:OCMOCK_ANY + presentingViewController:OCMOCK_ANY + hint:OCMOCK_ANY + additionalScopes:OCMOCK_ANY + callback:OCMOCK_ANY]) .andThrow([NSException exceptionWithName:@"MockName" reason:@"MockReason" userInfo:nil]); __block FlutterError *error; @@ -237,7 +410,7 @@ - (void)testGetTokens { OCMStub([mockAuthentication idToken]).andReturn(@"mockIdToken"); OCMStub([mockAuthentication accessToken]).andReturn(@"mockAccessToken"); [[mockAuthentication stub] - getTokensWithHandler:[OCMArg invokeBlockWithArgs:mockAuthentication, [NSNull null], nil]]; + doWithFreshTokens:[OCMArg invokeBlockWithArgs:mockAuthentication, [NSNull null], nil]]; OCMStub([mockUser authentication]).andReturn(mockAuthentication); FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"getTokens" @@ -262,7 +435,7 @@ - (void)testGetTokensNoAuthKeychainError { code:kGIDSignInErrorCodeHasNoAuthInKeychain userInfo:nil]; [[mockAuthentication stub] - getTokensWithHandler:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; + doWithFreshTokens:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; OCMStub([mockUser authentication]).andReturn(mockAuthentication); FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"getTokens" @@ -287,7 +460,7 @@ - (void)testGetTokensCancelledError { code:kGIDSignInErrorCodeCanceled userInfo:nil]; [[mockAuthentication stub] - getTokensWithHandler:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; + doWithFreshTokens:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; OCMStub([mockUser authentication]).andReturn(mockAuthentication); FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"getTokens" @@ -310,7 +483,7 @@ - (void)testGetTokensURLError { id mockAuthentication = OCMClassMock([GIDAuthentication class]); NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorTimedOut userInfo:nil]; [[mockAuthentication stub] - getTokensWithHandler:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; + doWithFreshTokens:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; OCMStub([mockUser authentication]).andReturn(mockAuthentication); FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"getTokens" @@ -333,7 +506,7 @@ - (void)testGetTokensUnknownError { id mockAuthentication = OCMClassMock([GIDAuthentication class]); NSError *error = [NSError errorWithDomain:@"BogusDomain" code:42 userInfo:nil]; [[mockAuthentication stub] - getTokensWithHandler:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; + doWithFreshTokens:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; OCMStub([mockUser authentication]).andReturn(mockAuthentication); FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"getTokens" @@ -352,7 +525,12 @@ - (void)testGetTokensUnknownError { #pragma mark - Request scopes - (void)testRequestScopesResultErrorIfNotSignedIn { - OCMStub([self.mockSignIn currentUser]).andReturn(nil); + NSError *error = [NSError errorWithDomain:kGIDSignInErrorDomain + code:kGIDSignInErrorCodeNoCurrentUser + userInfo:nil]; + [[self.mockSignIn stub] addScopes:@[ @"mockScope1" ] + presentingViewController:OCMOCK_ANY + callback:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"requestScopes" @@ -368,14 +546,16 @@ - (void)testRequestScopesResultErrorIfNotSignedIn { } - (void)testRequestScopesIfNoMissingScope { - // Mock Google Signin internal calls - GIDGoogleUser *mockUser = OCMClassMock([GIDGoogleUser class]); - OCMStub([self.mockSignIn currentUser]).andReturn(mockUser); - NSArray *requestedScopes = @[ @"mockScope1" ]; - OCMStub(mockUser.grantedScopes).andReturn(requestedScopes); + NSError *error = [NSError errorWithDomain:kGIDSignInErrorDomain + code:kGIDSignInErrorCodeScopesAlreadyGranted + userInfo:nil]; + [[self.mockSignIn stub] addScopes:@[ @"mockScope1" ] + presentingViewController:OCMOCK_ANY + callback:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; + FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"requestScopes" - arguments:@{@"scopes" : requestedScopes}]; + arguments:@{@"scopes" : @[ @"mockScope1" ]}]; XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; [self.plugin handleMethodCall:methodCall @@ -386,39 +566,50 @@ - (void)testRequestScopesIfNoMissingScope { [self waitForExpectationsWithTimeout:5.0 handler:nil]; } -- (void)testRequestScopesRequestsIfNotGranted { - // Mock Google Signin internal calls - GIDGoogleUser *mockUser = OCMClassMock([GIDGoogleUser class]); - OCMStub([self.mockSignIn currentUser]).andReturn(mockUser); - NSArray *requestedScopes = @[ @"mockScope1" ]; - OCMStub(mockUser.grantedScopes).andReturn(@[]); - id mockSignIn = self.mockSignIn; - OCMStub([mockSignIn scopes]).andReturn(@[]); +- (void)testRequestScopesWithUnknownError { + NSError *error = [NSError errorWithDomain:@"BogusDomain" code:42 userInfo:nil]; + [[self.mockSignIn stub] addScopes:@[ @"mockScope1" ] + presentingViewController:OCMOCK_ANY + callback:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"requestScopes" - arguments:@{@"scopes" : requestedScopes}]; + arguments:@{@"scopes" : @[ @"mockScope1" ]}]; + XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; [self.plugin handleMethodCall:methodCall - result:^(id r){ + result:^(NSNumber *result) { + XCTAssertFalse(result.boolValue); + [expectation fulfill]; }]; + [self waitForExpectationsWithTimeout:5.0 handler:nil]; +} + +- (void)testRequestScopesException { + FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"requestScopes" + arguments:nil]; + OCMExpect([self.mockSignIn addScopes:@[] presentingViewController:OCMOCK_ANY callback:OCMOCK_ANY]) + .andThrow([NSException exceptionWithName:@"MockName" reason:@"MockReason" userInfo:nil]); - OCMVerify([mockSignIn setScopes:@[ @"mockScope1" ]]); - OCMVerify([mockSignIn signIn]); + [self.plugin handleMethodCall:methodCall + result:^(FlutterError *result) { + XCTAssertEqualObjects(result.code, @"request_scopes"); + XCTAssertEqualObjects(result.message, @"MockReason"); + XCTAssertEqualObjects(result.details, @"MockName"); + }]; } -- (void)testRequestScopesReturnsFalseIfNotGranted { - // Mock Google Signin internal calls +- (void)testRequestScopesReturnsFalseIfOnlySubsetGranted { GIDGoogleUser *mockUser = OCMClassMock([GIDGoogleUser class]); OCMStub([self.mockSignIn currentUser]).andReturn(mockUser); - NSArray *requestedScopes = @[ @"mockScope1" ]; - OCMStub(mockUser.grantedScopes).andReturn(@[]); + NSArray *requestedScopes = @[ @"mockScope1", @"mockScope2" ]; + + // Only grant one of the two requested scopes. + OCMStub(mockUser.grantedScopes).andReturn(@[ @"mockScope1" ]); - OCMStub([self.mockSignIn signIn]).andDo(^(NSInvocation *invocation) { - [((NSObject *)self.plugin) signIn:self.mockSignIn - didSignInForUser:mockUser - withError:nil]; - }); + [[self.mockSignIn stub] addScopes:requestedScopes + presentingViewController:OCMOCK_ANY + callback:[OCMArg invokeBlockWithArgs:mockUser, [NSNull null], nil]]; FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"requestScopes" @@ -433,20 +624,53 @@ - (void)testRequestScopesReturnsFalseIfNotGranted { [self waitForExpectationsWithTimeout:5.0 handler:nil]; } +- (void)testRequestsInitializedScopes { + FlutterMethodCall *initMethodCall = + [FlutterMethodCall methodCallWithMethodName:@"init" + arguments:@{@"scopes" : @[ @"initial1", @"initial2" ]}]; + + XCTestExpectation *initExpectation = + [self expectationWithDescription:@"expect result returns true"]; + [self.plugin handleMethodCall:initMethodCall + result:^(id result) { + XCTAssertNil(result); + [initExpectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:5.0 handler:nil]; + + // Include one of the initially requested scopes. + NSArray *addedScopes = @[ @"initial1", @"addScope1", @"addScope2" ]; + + FlutterMethodCall *methodCall = + [FlutterMethodCall methodCallWithMethodName:@"requestScopes" + arguments:@{@"scopes" : addedScopes}]; + + [self.plugin handleMethodCall:methodCall + result:^(id result){ + }]; + + // All four scopes are requested. + [[self.mockSignIn verify] + addScopes:[OCMArg checkWithBlock:^BOOL(NSArray *scopes) { + return [[NSSet setWithArray:scopes] + isEqualToSet:[NSSet setWithObjects:@"initial1", @"initial2", + @"addScope1", @"addScope2", nil]]; + }] + presentingViewController:OCMOCK_ANY + callback:OCMOCK_ANY]; +} + - (void)testRequestScopesReturnsTrueIfGranted { - // Mock Google Signin internal calls GIDGoogleUser *mockUser = OCMClassMock([GIDGoogleUser class]); OCMStub([self.mockSignIn currentUser]).andReturn(mockUser); - NSArray *requestedScopes = @[ @"mockScope1" ]; - NSMutableArray *availableScopes = [NSMutableArray new]; - OCMStub(mockUser.grantedScopes).andReturn(availableScopes); - - OCMStub([self.mockSignIn signIn]).andDo(^(NSInvocation *invocation) { - [availableScopes addObject:@"mockScope1"]; - [((NSObject *)self.plugin) signIn:self.mockSignIn - didSignInForUser:mockUser - withError:nil]; - }); + NSArray *requestedScopes = @[ @"mockScope1", @"mockScope2" ]; + + // Grant both of the requested scopes. + OCMStub(mockUser.grantedScopes).andReturn(requestedScopes); + + [[self.mockSignIn stub] addScopes:requestedScopes + presentingViewController:OCMOCK_ANY + callback:[OCMArg invokeBlockWithArgs:mockUser, [NSNull null], nil]]; FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"requestScopes" diff --git a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m index 5ad69e2ad052..55d09bd903d4 100644 --- a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m +++ b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m @@ -36,17 +36,28 @@ details:error.localizedDescription]; } -@interface FLTGoogleSignInPlugin () +@interface FLTGoogleSignInPlugin () + +// Configuration wrapping Google Cloud Console, Google Apps, OpenID, +// and other initialization metadata. +@property(strong) GIDConfiguration *configuration; + +// Permissions requested during at sign in "init" method call +// unioned with scopes requested later with incremental authorization +// "requestScopes" method call. +// The "email" and "profile" base scopes are always implicitly requested. +@property(copy) NSSet *requestedScopes; + +// Instance used to manage Google Sign In authentication including +// sign in, sign out, and requesting additional scopes. @property(strong, readonly) GIDSignIn *signIn; // Redeclared as not a designated initializer. - (instancetype)init; + @end -@implementation FLTGoogleSignInPlugin { - FlutterResult _accountRequest; - NSArray *_additionalScopesRequest; -} +@implementation FLTGoogleSignInPlugin + (void)registerWithRegistrar:(NSObject *)registrar { FlutterMethodChannel *channel = @@ -65,11 +76,11 @@ - (instancetype)initWithSignIn:(GIDSignIn *)signIn { self = [super init]; if (self) { _signIn = signIn; - _signIn.delegate = self; // On the iOS simulator, we get "Broken pipe" errors after sign-in for some // unknown reason. We can avoid crashing the app by ignoring them. signal(SIGPIPE, SIG_IGN); + _requestedScopes = [[NSSet alloc] init]; } return self; } @@ -78,25 +89,14 @@ - (instancetype)initWithSignIn:(GIDSignIn *)signIn { - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { if ([call.method isEqualToString:@"init"]) { - NSString *path = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info" ofType:@"plist"]; - if (path) { - NSMutableDictionary *plist = - [[NSMutableDictionary alloc] initWithContentsOfFile:path]; - BOOL hasDynamicClientId = [call.arguments[@"clientId"] isKindOfClass:[NSString class]]; - - if (hasDynamicClientId) { - self.signIn.clientID = call.arguments[@"clientId"]; - } else { - self.signIn.clientID = plist[kClientIdKey]; - } - - self.signIn.serverClientID = plist[kServerClientIdKey]; - self.signIn.scopes = call.arguments[@"scopes"]; - if (call.arguments[@"hostedDomain"] == [NSNull null]) { - self.signIn.hostedDomain = nil; - } else { - self.signIn.hostedDomain = call.arguments[@"hostedDomain"]; + GIDConfiguration *configuration = + [self configurationWithClientIdArgument:call.arguments[@"clientId"] + hostedDomainArgument:call.arguments[@"hostedDomain"]]; + if (configuration != nil) { + if ([call.arguments[@"scopes"] isKindOfClass:[NSArray class]]) { + self.requestedScopes = [NSSet setWithArray:call.arguments[@"scopes"]]; } + self.configuration = configuration; result(nil); } else { result([FlutterError errorWithCode:@"missing-config" @@ -104,26 +104,31 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result details:nil]); } } else if ([call.method isEqualToString:@"signInSilently"]) { - if ([self setAccountRequest:result]) { - [self.signIn restorePreviousSignIn]; - } + [self.signIn restorePreviousSignInWithCallback:^(GIDGoogleUser *user, NSError *error) { + [self didSignInForUser:user result:result withError:error]; + }]; } else if ([call.method isEqualToString:@"isSignedIn"]) { result(@([self.signIn hasPreviousSignIn])); } else if ([call.method isEqualToString:@"signIn"]) { - self.signIn.presentingViewController = [self topViewController]; - - if ([self setAccountRequest:result]) { - @try { - [self.signIn signIn]; - } @catch (NSException *e) { - result([FlutterError errorWithCode:@"google_sign_in" message:e.reason details:e.name]); - [e raise]; - } + @try { + GIDConfiguration *configuration = self.configuration + ?: [self configurationWithClientIdArgument:nil + hostedDomainArgument:nil]; + [self.signIn signInWithConfiguration:configuration + presentingViewController:[self topViewController] + hint:nil + additionalScopes:self.requestedScopes.allObjects + callback:^(GIDGoogleUser *user, NSError *error) { + [self didSignInForUser:user result:result withError:error]; + }]; + } @catch (NSException *e) { + result([FlutterError errorWithCode:@"google_sign_in" message:e.reason details:e.name]); + [e raise]; } } else if ([call.method isEqualToString:@"getTokens"]) { GIDGoogleUser *currentUser = self.signIn.currentUser; GIDAuthentication *auth = currentUser.authentication; - [auth getTokensWithHandler:^void(GIDAuthentication *authentication, NSError *error) { + [auth doWithFreshTokens:^void(GIDAuthentication *authentication, NSError *error) { result(error != nil ? getFlutterError(error) : @{ @"idToken" : authentication.idToken, @"accessToken" : authentication.accessToken, @@ -133,61 +138,49 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result [self.signIn signOut]; result(nil); } else if ([call.method isEqualToString:@"disconnect"]) { - if ([self setAccountRequest:result]) { - [self.signIn disconnect]; - } + [self.signIn disconnectWithCallback:^(NSError *error) { + [self respondWithAccount:@{} result:result error:nil]; + }]; } else if ([call.method isEqualToString:@"requestScopes"]) { - GIDGoogleUser *user = self.signIn.currentUser; - if (user == nil) { - result([FlutterError errorWithCode:@"sign_in_required" - message:@"No account to grant scopes." - details:nil]); - return; + id scopeArgument = call.arguments[@"scopes"]; + if ([scopeArgument isKindOfClass:[NSArray class]]) { + self.requestedScopes = [self.requestedScopes setByAddingObjectsFromArray:scopeArgument]; } - - NSArray *currentScopes = self.signIn.scopes; - NSArray *scopes = call.arguments[@"scopes"]; - NSArray *missingScopes = [scopes - filteredArrayUsingPredicate:[NSPredicate - predicateWithBlock:^BOOL(id scope, NSDictionary *bindings) { - return ![user.grantedScopes containsObject:scope]; - }]]; - - if (!missingScopes || !missingScopes.count) { - result(@(YES)); - return; - } - - if ([self setAccountRequest:result]) { - _additionalScopesRequest = missingScopes; - self.signIn.scopes = [currentScopes arrayByAddingObjectsFromArray:missingScopes]; - self.signIn.presentingViewController = [self topViewController]; - self.signIn.loginHint = user.profile.email; - @try { - [self.signIn signIn]; - } @catch (NSException *e) { - result([FlutterError errorWithCode:@"request_scopes" message:e.reason details:e.name]); - } + NSSet *requestedScopes = self.requestedScopes; + + @try { + [self.signIn addScopes:requestedScopes.allObjects + presentingViewController:[self topViewController] + callback:^(GIDGoogleUser *addedScopeUser, NSError *addedScopeError) { + if ([addedScopeError.domain isEqualToString:kGIDSignInErrorDomain] && + addedScopeError.code == kGIDSignInErrorCodeNoCurrentUser) { + result([FlutterError errorWithCode:@"sign_in_required" + message:@"No account to grant scopes." + details:nil]); + } else if ([addedScopeError.domain + isEqualToString:kGIDSignInErrorDomain] && + addedScopeError.code == + kGIDSignInErrorCodeScopesAlreadyGranted) { + // Scopes already granted, report success. + result(@YES); + } else if (addedScopeUser == nil) { + result(@NO); + } else { + NSSet *grantedScopes = + [NSSet setWithArray:addedScopeUser.grantedScopes]; + BOOL granted = [requestedScopes isSubsetOfSet:grantedScopes]; + result(@(granted)); + } + }]; + } @catch (NSException *e) { + result([FlutterError errorWithCode:@"request_scopes" message:e.reason details:e.name]); } } else { result(FlutterMethodNotImplemented); } } -- (BOOL)setAccountRequest:(FlutterResult)request { - if (_accountRequest != nil) { - request([FlutterError errorWithCode:@"concurrent-requests" - message:@"Concurrent requests to account signin" - details:nil]); - return NO; - } - _accountRequest = request; - return YES; -} - -- (BOOL)application:(UIApplication *)app - openURL:(NSURL *)url - options:(NSDictionary *)options { +- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options { return [self.signIn handleURL:url]; } @@ -203,57 +196,58 @@ - (void)signIn:(GIDSignIn *)signIn dismissViewController:(UIViewController *)vie [viewController dismissViewControllerAnimated:YES completion:nil]; } -#pragma mark - protocol +#pragma mark - private methods + +/// @return @c nil if GoogleService-Info.plist not found. +- (GIDConfiguration *)configurationWithClientIdArgument:(id)clientIDArg + hostedDomainArgument:(id)hostedDomainArg { + NSString *plistPath = [NSBundle.mainBundle pathForResource:@"GoogleService-Info" ofType:@"plist"]; + if (plistPath == nil) { + return nil; + } + + NSDictionary *plist = [[NSDictionary alloc] initWithContentsOfFile:plistPath]; + + BOOL hasDynamicClientId = [clientIDArg isKindOfClass:[NSString class]]; + NSString *clientID = hasDynamicClientId ? clientIDArg : plist[kClientIdKey]; + + NSString *hostedDomain = nil; + if (hostedDomainArg != [NSNull null]) { + hostedDomain = hostedDomainArg; + } + return [[GIDConfiguration alloc] initWithClientID:clientID + serverClientID:plist[kServerClientIdKey] + hostedDomain:hostedDomain + openIDRealm:nil]; +} -- (void)signIn:(GIDSignIn *)signIn - didSignInForUser:(GIDGoogleUser *)user - withError:(NSError *)error { +- (void)didSignInForUser:(GIDGoogleUser *)user + result:(FlutterResult)result + withError:(NSError *)error { if (error != nil) { // Forward all errors and let Dart side decide how to handle. - [self respondWithAccount:nil error:error]; + [self respondWithAccount:nil result:result error:error]; } else { - if (_additionalScopesRequest) { - bool granted = YES; - for (NSString *scope in _additionalScopesRequest) { - if (![user.grantedScopes containsObject:scope]) { - granted = NO; - break; - } - } - _accountRequest(@(granted)); - _accountRequest = nil; - _additionalScopesRequest = nil; - return; - } else { - NSURL *photoUrl; - if (user.profile.hasImage) { - // Placeholder that will be replaced by on the Dart side based on screen - // size - photoUrl = [user.profile imageURLWithDimension:1337]; - } - [self respondWithAccount:@{ - @"displayName" : user.profile.name ?: [NSNull null], - @"email" : user.profile.email ?: [NSNull null], - @"id" : user.userID ?: [NSNull null], - @"photoUrl" : [photoUrl absoluteString] ?: [NSNull null], - @"serverAuthCode" : user.serverAuthCode ?: [NSNull null] - } - error:nil]; + NSURL *photoUrl; + if (user.profile.hasImage) { + // Placeholder that will be replaced by on the Dart side based on screen size. + photoUrl = [user.profile imageURLWithDimension:1337]; + } + [self respondWithAccount:@{ + @"displayName" : user.profile.name ?: [NSNull null], + @"email" : user.profile.email ?: [NSNull null], + @"id" : user.userID ?: [NSNull null], + @"photoUrl" : [photoUrl absoluteString] ?: [NSNull null], + @"serverAuthCode" : user.serverAuthCode ?: [NSNull null] } + result:result + error:nil]; } } -- (void)signIn:(GIDSignIn *)signIn - didDisconnectWithUser:(GIDGoogleUser *)user - withError:(NSError *)error { - [self respondWithAccount:@{} error:nil]; -} - -#pragma mark - private methods - -- (void)respondWithAccount:(NSDictionary *)account error:(NSError *)error { - FlutterResult result = _accountRequest; - _accountRequest = nil; +- (void)respondWithAccount:(NSDictionary *)account + result:(FlutterResult)result + error:(NSError *)error { result(error != nil ? getFlutterError(error) : account); } diff --git a/packages/google_sign_in/google_sign_in_ios/ios/google_sign_in_ios.podspec b/packages/google_sign_in/google_sign_in_ios/ios/google_sign_in_ios.podspec index f583f6cffbf0..18a213579a23 100644 --- a/packages/google_sign_in/google_sign_in_ios/ios/google_sign_in_ios.podspec +++ b/packages/google_sign_in/google_sign_in_ios/ios/google_sign_in_ios.podspec @@ -16,11 +16,8 @@ Enables Google Sign-In in Flutter apps. s.public_header_files = 'Classes/**/*.h' s.module_map = 'Classes/FLTGoogleSignInPlugin.modulemap' s.dependency 'Flutter' - s.dependency 'GoogleSignIn', '~> 5.0' + s.dependency 'GoogleSignIn', '~> 6.2' s.static_framework = true - s.platform = :ios, '9.0' - - # GoogleSignIn ~> 5.0 does not support arm64 simulators. - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'arm64' } + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } end diff --git a/packages/google_sign_in/google_sign_in_ios/pubspec.yaml b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml index b6f541b22ce1..5604ee6a5f18 100644 --- a/packages/google_sign_in/google_sign_in_ios/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml @@ -1,8 +1,8 @@ name: google_sign_in_ios -description: Android implementation of the google_sign_in plugin. +description: iOS implementation of the google_sign_in plugin. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.2.7 +version: 5.3.0 environment: sdk: ">=2.14.0 <3.0.0" From 65620edc0718454e63ab9a78aaa8df1f9fde1e44 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 19 May 2022 10:33:10 -0400 Subject: [PATCH 586/600] Roll Flutter from 1994027986cf to a4a8e73bce15 (31 revisions) (#5782) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 73d69ae3af72..822ceaa4a9b1 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -1994027986cf59a4c307d6612706ce08c16b1ae8 +a4a8e73bce152ab39d6ae839ca51e447f87293fa From 74f8a63f279778aa3514ba4b8516a48ab96917ed Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 19 May 2022 11:28:11 -0400 Subject: [PATCH 587/600] [ci] Updates iOS deprecation check to iOS 13 (#5786) --- .cirrus.yml | 4 +- .../google_sign_in_ios/CHANGELOG.md | 4 ++ .../google_sign_in_ios/example/ios/Podfile | 1 + .../ios/Classes/FLTGoogleSignInPlugin.m | 5 ++ .../google_sign_in_ios/pubspec.yaml | 2 +- .../image_picker_ios/CHANGELOG.md | 4 ++ .../ios/Classes/FLTImagePickerPlugin.m | 44 ++++++++++++----- .../FLTPHPickerSaveImageToPathOperation.m | 47 +++++++++++++------ .../image_picker_ios/pubspec.yaml | 2 +- .../url_launcher_ios/CHANGELOG.md | 4 ++ .../ios/Classes/FLTURLLauncherPlugin.m | 5 ++ .../url_launcher_ios/pubspec.yaml | 2 +- 12 files changed, 92 insertions(+), 32 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 69fd8955302a..2b8fde823826 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -336,9 +336,7 @@ task: - ./script/tool_runner.sh xcode-analyze --ios xcode_analyze_deprecation_script: # Ensure we don't accidentally introduce deprecated code. - # TODO(stuartmorgan): Update this to a newer version of iOS to get - # ahead of upcoming deprecations. - - ./script/tool_runner.sh xcode-analyze --ios --ios-min-version=11.0 + - ./script/tool_runner.sh xcode-analyze --ios --ios-min-version=13.0 native_test_script: - ./script/tool_runner.sh native-test --ios --ios-destination "platform=iOS Simulator,name=iPhone 11,OS=latest" drive_script: diff --git a/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md b/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md index e5de49da7f22..c17f1415b724 100644 --- a/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 5.3.1 + +* Suppresses warnings for pre-iOS-13 codepaths. + ## 5.3.0 * Supports arm64 iOS simulators by increasing GoogleSignIn dependency to version 6.2. diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile b/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile index 6c315d202770..b95dfa75ea04 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile @@ -27,6 +27,7 @@ require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelpe # Suppress warnings from transitive dependencies that cause analysis to fail. pod 'AppAuth', :inhibit_warnings => true +pod 'GTMAppAuth', :inhibit_warnings => true flutter_ios_podfile_setup diff --git a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m index 55d09bd903d4..608cdc2bec6d 100644 --- a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m +++ b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m @@ -252,8 +252,13 @@ - (void)respondWithAccount:(NSDictionary *)account } - (UIViewController *)topViewController { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + // TODO(stuartmorgan) Provide a non-deprecated codepath. See + // https://github.com/flutter/flutter/issues/104117 return [self topViewControllerFromViewController:[UIApplication sharedApplication] .keyWindow.rootViewController]; +#pragma clang diagnostic pop } /** diff --git a/packages/google_sign_in/google_sign_in_ios/pubspec.yaml b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml index 5604ee6a5f18..13f045b6006c 100644 --- a/packages/google_sign_in/google_sign_in_ios/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: google_sign_in_ios description: iOS implementation of the google_sign_in plugin. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.3.0 +version: 5.3.1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index e994fcc50c8e..86ee7d0a2d00 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.5+5 + +* Adds non-deprecated codepaths for iOS 13+. + ## 0.8.5+4 * Suppresses warnings for pre-iOS-11 codepaths. diff --git a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m index 76ed9623a57c..18d4ad2f054c 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m @@ -567,18 +567,38 @@ - (void)imagePickerController:(UIImagePickerController *)picker // Image picked without an original asset (e.g. User took a photo directly) [self saveImageWithPickerInfo:info image:image imageQuality:desiredImageQuality]; } else { - [[PHImageManager defaultManager] - requestImageDataForAsset:originalAsset - options:nil - resultHandler:^(NSData *_Nullable imageData, NSString *_Nullable dataUTI, - UIImageOrientation orientation, NSDictionary *_Nullable info) { - // maxWidth and maxHeight are used only for GIF images. - [self saveImageWithOriginalImageData:imageData - image:image - maxWidth:maxWidth - maxHeight:maxHeight - imageQuality:desiredImageQuality]; - }]; + void (^resultHandler)(NSData *imageData, NSString *dataUTI, NSDictionary *info) = ^( + NSData *_Nullable imageData, NSString *_Nullable dataUTI, NSDictionary *_Nullable info) { + // maxWidth and maxHeight are used only for GIF images. + [self saveImageWithOriginalImageData:imageData + image:image + maxWidth:maxWidth + maxHeight:maxHeight + imageQuality:desiredImageQuality]; + }; + if (@available(iOS 13.0, *)) { + [[PHImageManager defaultManager] + requestImageDataAndOrientationForAsset:originalAsset + options:nil + resultHandler:^(NSData *_Nullable imageData, + NSString *_Nullable dataUTI, + CGImagePropertyOrientation orientation, + NSDictionary *_Nullable info) { + resultHandler(imageData, dataUTI, info); + }]; + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [[PHImageManager defaultManager] + requestImageDataForAsset:originalAsset + options:nil + resultHandler:^(NSData *_Nullable imageData, NSString *_Nullable dataUTI, + UIImageOrientation orientation, + NSDictionary *_Nullable info) { + resultHandler(imageData, dataUTI, info); + }]; +#pragma clang diagnostic pop + } } } } diff --git a/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.m b/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.m index 9e7e7e87a30c..a81c95f1b120 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.m @@ -122,20 +122,39 @@ - (void)processImage:(UIImage *)localImage API_AVAILABLE(ios(14)) { isMetadataAvailable:originalAsset != nil]; } if (originalAsset) { - [[PHImageManager defaultManager] - requestImageDataForAsset:originalAsset - options:nil - resultHandler:^(NSData *_Nullable imageData, NSString *_Nullable dataUTI, - UIImageOrientation orientation, NSDictionary *_Nullable info) { - // maxWidth and maxHeight are used only for GIF images. - NSString *savedPath = [FLTImagePickerPhotoAssetUtil - saveImageWithOriginalImageData:imageData - image:localImage - maxWidth:self.maxWidth - maxHeight:self.maxHeight - imageQuality:self.desiredImageQuality]; - [self completeOperationWithPath:savedPath]; - }]; + void (^resultHandler)(NSData *imageData, NSString *dataUTI, NSDictionary *info) = + ^(NSData *_Nullable imageData, NSString *_Nullable dataUTI, NSDictionary *_Nullable info) { + // maxWidth and maxHeight are used only for GIF images. + NSString *savedPath = [FLTImagePickerPhotoAssetUtil + saveImageWithOriginalImageData:imageData + image:localImage + maxWidth:self.maxWidth + maxHeight:self.maxHeight + imageQuality:self.desiredImageQuality]; + [self completeOperationWithPath:savedPath]; + }; + if (@available(iOS 13.0, *)) { + [[PHImageManager defaultManager] + requestImageDataAndOrientationForAsset:originalAsset + options:nil + resultHandler:^(NSData *_Nullable imageData, + NSString *_Nullable dataUTI, + CGImagePropertyOrientation orientation, + NSDictionary *_Nullable info) { + resultHandler(imageData, dataUTI, info); + }]; + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [[PHImageManager defaultManager] + requestImageDataForAsset:originalAsset + options:nil + resultHandler:^(NSData *_Nullable imageData, NSString *_Nullable dataUTI, + UIImageOrientation orientation, NSDictionary *_Nullable info) { + resultHandler(imageData, dataUTI, info); + }]; +#pragma clang diagnostic pop + } } else { // Image picked without an original asset (e.g. User pick image without permission) NSString *savedPath = diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index 88f4d3352228..e1bccad60ea4 100755 --- a/packages/image_picker/image_picker_ios/pubspec.yaml +++ b/packages/image_picker/image_picker_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_ios description: iOS implementation of the video_picker plugin. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.5+4 +version: 0.8.5+5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_ios/CHANGELOG.md b/packages/url_launcher/url_launcher_ios/CHANGELOG.md index 5f6dd37142bb..5fc00bff486e 100644 --- a/packages/url_launcher/url_launcher_ios/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.0.17 + +* Suppresses warnings for pre-iOS-13 codepaths. + ## 6.0.16 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/url_launcher/url_launcher_ios/ios/Classes/FLTURLLauncherPlugin.m b/packages/url_launcher/url_launcher_ios/ios/Classes/FLTURLLauncherPlugin.m index 1aceedc8b1de..af720c87b8b2 100644 --- a/packages/url_launcher/url_launcher_ios/ios/Classes/FLTURLLauncherPlugin.m +++ b/packages/url_launcher/url_launcher_ios/ios/Classes/FLTURLLauncherPlugin.m @@ -136,8 +136,13 @@ - (void)closeWebViewWithResult:(FlutterResult)result API_AVAILABLE(ios(9.0)) { } - (UIViewController *)topViewController { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + // TODO(stuartmorgan) Provide a non-deprecated codepath. See + // https://github.com/flutter/flutter/issues/104117 return [self topViewControllerFromViewController:[UIApplication sharedApplication] .keyWindow.rootViewController]; +#pragma clang diagnostic pop } /** diff --git a/packages/url_launcher/url_launcher_ios/pubspec.yaml b/packages/url_launcher/url_launcher_ios/pubspec.yaml index 0b21bad35204..9bb1616441b3 100644 --- a/packages/url_launcher/url_launcher_ios/pubspec.yaml +++ b/packages/url_launcher/url_launcher_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_ios description: iOS implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.0.16 +version: 6.0.17 environment: sdk: ">=2.14.0 <3.0.0" From 3e49ef75f3c1d50b7c78b616ab95c5131b56c75b Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 19 May 2022 18:28:14 -0400 Subject: [PATCH 588/600] [various] Set minimum Flutter versions to 2.8 (#5792) --- packages/camera/camera/example/pubspec.yaml | 2 +- packages/camera/camera/pubspec.yaml | 2 +- packages/camera/camera_web/example/pubspec.yaml | 2 +- packages/espresso/example/pubspec.yaml | 2 +- packages/espresso/pubspec.yaml | 2 +- packages/file_selector/file_selector/pubspec.yaml | 2 +- packages/file_selector/file_selector_macos/example/pubspec.yaml | 2 +- packages/file_selector/file_selector_macos/pubspec.yaml | 2 +- .../file_selector/file_selector_platform_interface/pubspec.yaml | 2 +- packages/file_selector/file_selector_web/example/pubspec.yaml | 2 +- packages/file_selector/file_selector_web/pubspec.yaml | 2 +- .../file_selector/file_selector_windows/example/pubspec.yaml | 2 +- packages/file_selector/file_selector_windows/pubspec.yaml | 2 +- packages/flutter_plugin_android_lifecycle/pubspec.yaml | 2 +- .../google_maps_flutter/example/pubspec.yaml | 2 +- packages/google_maps_flutter/google_maps_flutter/pubspec.yaml | 2 +- .../google_maps_flutter_platform_interface/pubspec.yaml | 2 +- .../google_maps_flutter_web/example/pubspec.yaml | 2 +- .../google_maps_flutter/google_maps_flutter_web/pubspec.yaml | 2 +- packages/google_sign_in/google_sign_in/example/pubspec.yaml | 2 +- packages/google_sign_in/google_sign_in/pubspec.yaml | 2 +- .../google_sign_in/google_sign_in_android/example/pubspec.yaml | 2 +- packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml | 2 +- .../google_sign_in_platform_interface/pubspec.yaml | 2 +- packages/image_picker/image_picker/example/pubspec.yaml | 2 +- packages/image_picker/image_picker/pubspec.yaml | 2 +- packages/image_picker/image_picker_android/example/pubspec.yaml | 2 +- packages/image_picker/image_picker_android/pubspec.yaml | 2 +- packages/image_picker/image_picker_for_web/example/pubspec.yaml | 2 +- packages/image_picker/image_picker_for_web/pubspec.yaml | 2 +- packages/image_picker/image_picker_ios/example/pubspec.yaml | 2 +- .../image_picker/image_picker_platform_interface/pubspec.yaml | 2 +- packages/image_picker/image_picker_windows/example/pubspec.yaml | 2 +- packages/image_picker/image_picker_windows/pubspec.yaml | 2 +- packages/in_app_purchase/in_app_purchase/example/pubspec.yaml | 2 +- packages/in_app_purchase/in_app_purchase/pubspec.yaml | 2 +- .../in_app_purchase_android/example/pubspec.yaml | 2 +- packages/in_app_purchase/in_app_purchase_android/pubspec.yaml | 2 +- .../in_app_purchase_platform_interface/pubspec.yaml | 2 +- .../in_app_purchase_storekit/example/pubspec.yaml | 2 +- packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml | 2 +- packages/ios_platform_images/example/pubspec.yaml | 2 +- packages/ios_platform_images/pubspec.yaml | 2 +- packages/local_auth/local_auth/example/pubspec.yaml | 2 +- packages/path_provider/path_provider/example/pubspec.yaml | 2 +- packages/path_provider/path_provider/pubspec.yaml | 2 +- .../path_provider/path_provider_android/example/pubspec.yaml | 2 +- packages/path_provider/path_provider_ios/example/pubspec.yaml | 2 +- packages/path_provider/path_provider_linux/example/pubspec.yaml | 2 +- packages/path_provider/path_provider_linux/pubspec.yaml | 2 +- packages/path_provider/path_provider_macos/example/pubspec.yaml | 2 +- packages/path_provider/path_provider_macos/pubspec.yaml | 2 +- .../path_provider/path_provider_platform_interface/pubspec.yaml | 2 +- .../path_provider/path_provider_windows/example/pubspec.yaml | 2 +- packages/path_provider/path_provider_windows/pubspec.yaml | 2 +- packages/quick_actions/quick_actions/example/pubspec.yaml | 2 +- .../quick_actions/quick_actions_platform_interface/pubspec.yaml | 2 +- .../shared_preferences/shared_preferences/example/pubspec.yaml | 2 +- packages/shared_preferences/shared_preferences/pubspec.yaml | 2 +- .../shared_preferences_android/example/pubspec.yaml | 2 +- .../shared_preferences_ios/example/pubspec.yaml | 2 +- .../shared_preferences_linux/example/pubspec.yaml | 2 +- .../shared_preferences/shared_preferences_linux/pubspec.yaml | 2 +- .../shared_preferences_macos/example/pubspec.yaml | 2 +- .../shared_preferences/shared_preferences_macos/pubspec.yaml | 2 +- .../shared_preferences_platform_interface/pubspec.yaml | 2 +- .../shared_preferences_web/example/pubspec.yaml | 2 +- packages/shared_preferences/shared_preferences_web/pubspec.yaml | 2 +- .../shared_preferences_windows/example/pubspec.yaml | 2 +- .../shared_preferences/shared_preferences_windows/pubspec.yaml | 2 +- packages/url_launcher/url_launcher/example/pubspec.yaml | 2 +- packages/url_launcher/url_launcher/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_android/example/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_ios/example/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_linux/example/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_linux/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_macos/example/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_macos/pubspec.yaml | 2 +- .../url_launcher/url_launcher_platform_interface/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_web/example/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_windows/example/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_windows/pubspec.yaml | 2 +- packages/video_player/video_player_web/example/pubspec.yaml | 2 +- packages/video_player/video_player_web/pubspec.yaml | 2 +- packages/webview_flutter/webview_flutter/example/pubspec.yaml | 2 +- packages/webview_flutter/webview_flutter/pubspec.yaml | 2 +- packages/webview_flutter/webview_flutter_android/pubspec.yaml | 2 +- .../webview_flutter_platform_interface/pubspec.yaml | 2 +- packages/webview_flutter/webview_flutter_web/pubspec.yaml | 2 +- packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml | 2 +- 90 files changed, 90 insertions(+), 90 deletions(-) diff --git a/packages/camera/camera/example/pubspec.yaml b/packages/camera/camera/example/pubspec.yaml index af4d078ff836..e9ae2c74a6be 100644 --- a/packages/camera/camera/example/pubspec.yaml +++ b/packages/camera/camera/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: camera: diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 593e7b5bb978..aca6bebbe066 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -8,7 +8,7 @@ version: 0.9.6 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/camera/camera_web/example/pubspec.yaml b/packages/camera/camera_web/example/pubspec.yaml index 911648ef030d..441c6eb7988f 100644 --- a/packages/camera/camera_web/example/pubspec.yaml +++ b/packages/camera/camera_web/example/pubspec.yaml @@ -3,7 +3,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/espresso/example/pubspec.yaml b/packages/espresso/example/pubspec.yaml index 6a5fcdd466fe..c896585be839 100644 --- a/packages/espresso/example/pubspec.yaml +++ b/packages/espresso/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/espresso/pubspec.yaml b/packages/espresso/pubspec.yaml index ac0199cf045f..36b557029f3a 100644 --- a/packages/espresso/pubspec.yaml +++ b/packages/espresso/pubspec.yaml @@ -7,7 +7,7 @@ version: 0.2.0+2 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index 7026f7f32287..1c502c055c9a 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -7,7 +7,7 @@ version: 0.8.4+2 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/file_selector/file_selector_macos/example/pubspec.yaml b/packages/file_selector/file_selector_macos/example/pubspec.yaml index 2a11958e85cb..dbe127282a17 100644 --- a/packages/file_selector/file_selector_macos/example/pubspec.yaml +++ b/packages/file_selector/file_selector_macos/example/pubspec.yaml @@ -5,7 +5,7 @@ version: 1.0.0 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" dependencies: file_selector_macos: diff --git a/packages/file_selector/file_selector_macos/pubspec.yaml b/packages/file_selector/file_selector_macos/pubspec.yaml index 41077c1c04e6..e6f8e9b3c212 100644 --- a/packages/file_selector/file_selector_macos/pubspec.yaml +++ b/packages/file_selector/file_selector_macos/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.8.2+1 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/file_selector/file_selector_platform_interface/pubspec.yaml b/packages/file_selector/file_selector_platform_interface/pubspec.yaml index 42917751ed58..81ad53a8bab2 100644 --- a/packages/file_selector/file_selector_platform_interface/pubspec.yaml +++ b/packages/file_selector/file_selector_platform_interface/pubspec.yaml @@ -8,7 +8,7 @@ version: 2.0.4 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" dependencies: cross_file: ^0.3.0 diff --git a/packages/file_selector/file_selector_web/example/pubspec.yaml b/packages/file_selector/file_selector_web/example/pubspec.yaml index 8998587615e5..d8b93ee816f3 100644 --- a/packages/file_selector/file_selector_web/example/pubspec.yaml +++ b/packages/file_selector/file_selector_web/example/pubspec.yaml @@ -3,7 +3,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.2.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/file_selector/file_selector_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml index 488031995e55..c685cca9e884 100644 --- a/packages/file_selector/file_selector_web/pubspec.yaml +++ b/packages/file_selector/file_selector_web/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.8.1+5 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/file_selector/file_selector_windows/example/pubspec.yaml b/packages/file_selector/file_selector_windows/example/pubspec.yaml index b66a2023deb2..a3e69a6186f8 100644 --- a/packages/file_selector/file_selector_windows/example/pubspec.yaml +++ b/packages/file_selector/file_selector_windows/example/pubspec.yaml @@ -5,7 +5,7 @@ version: 1.0.0 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" dependencies: file_selector_platform_interface: ^2.0.0 diff --git a/packages/file_selector/file_selector_windows/pubspec.yaml b/packages/file_selector/file_selector_windows/pubspec.yaml index 152b63ef4a3f..3ca568004ca9 100644 --- a/packages/file_selector/file_selector_windows/pubspec.yaml +++ b/packages/file_selector/file_selector_windows/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.8.2+1 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/flutter_plugin_android_lifecycle/pubspec.yaml b/packages/flutter_plugin_android_lifecycle/pubspec.yaml index c109d0936589..8073cdd9fd76 100644 --- a/packages/flutter_plugin_android_lifecycle/pubspec.yaml +++ b/packages/flutter_plugin_android_lifecycle/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.0.6 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml index dbc32b0ef2f8..196f054e1fc0 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: cupertino_icons: ^0.1.0 diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index a294dd09981f..831f3ccd2963 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.1.5 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml index 8df31fcf626b..998f31936c3b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml @@ -8,7 +8,7 @@ version: 2.1.6 environment: sdk: '>=2.12.0 <3.0.0' - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: collection: ^1.15.0 diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/example/pubspec.yaml index 95a3d4253440..a962e5b864c6 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none # Tests require flutter beta or greater to run. environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.1.0" + flutter: ">=2.8.0" dependencies: google_maps_flutter_web: diff --git a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml index 271d87d21092..ca8af82dc2db 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.3.2+2 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/google_sign_in/google_sign_in/example/pubspec.yaml b/packages/google_sign_in/google_sign_in/example/pubspec.yaml index af9ed877e523..822f83895cfb 100644 --- a/packages/google_sign_in/google_sign_in/example/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index d1c13c6a8ec4..2a287b1cccd4 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/pubspec.yaml @@ -8,7 +8,7 @@ version: 5.3.1 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml b/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml index 316cdd893a2c..3aa8a80ee585 100644 --- a/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml b/packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml index f2d32c521c1a..ed51e3b63a58 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml b/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml index 0deafe80a863..9f6ab508d249 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml @@ -8,7 +8,7 @@ version: 2.1.3 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/image_picker/image_picker/example/pubspec.yaml b/packages/image_picker/image_picker/example/pubspec.yaml index 4fe823587398..23c682af3922 100755 --- a/packages/image_picker/image_picker/example/pubspec.yaml +++ b/packages/image_picker/image_picker/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 9d0cedeec484..acc085a06bb9 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -7,7 +7,7 @@ version: 0.8.5+3 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/image_picker/image_picker_android/example/pubspec.yaml b/packages/image_picker/image_picker_android/example/pubspec.yaml index 0d88ae139c71..b5afb16235db 100755 --- a/packages/image_picker/image_picker_android/example/pubspec.yaml +++ b/packages/image_picker/image_picker_android/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/image_picker/image_picker_android/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml index 095534654ac5..6accfb0eb4ac 100755 --- a/packages/image_picker/image_picker_android/pubspec.yaml +++ b/packages/image_picker/image_picker_android/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.8.4+13 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/image_picker/image_picker_for_web/example/pubspec.yaml b/packages/image_picker/image_picker_for_web/example/pubspec.yaml index a9d6c7b9b5bd..72316ee60988 100644 --- a/packages/image_picker/image_picker_for_web/example/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/example/pubspec.yaml @@ -3,7 +3,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.2.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/image_picker/image_picker_for_web/pubspec.yaml b/packages/image_picker/image_picker_for_web/pubspec.yaml index 0b2c6d2fc0ff..508e32aca5bd 100644 --- a/packages/image_picker/image_picker_for_web/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.1.8 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/image_picker/image_picker_ios/example/pubspec.yaml b/packages/image_picker/image_picker_ios/example/pubspec.yaml index a47893d7687f..84fa77e64d70 100755 --- a/packages/image_picker/image_picker_ios/example/pubspec.yaml +++ b/packages/image_picker/image_picker_ios/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/image_picker/image_picker_platform_interface/pubspec.yaml b/packages/image_picker/image_picker_platform_interface/pubspec.yaml index be6d5442d03b..4ce1d2fc52f1 100644 --- a/packages/image_picker/image_picker_platform_interface/pubspec.yaml +++ b/packages/image_picker/image_picker_platform_interface/pubspec.yaml @@ -8,7 +8,7 @@ version: 2.5.0 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" dependencies: cross_file: ^0.3.1+1 diff --git a/packages/image_picker/image_picker_windows/example/pubspec.yaml b/packages/image_picker/image_picker_windows/example/pubspec.yaml index 68c9395c6097..df1dd49327bd 100644 --- a/packages/image_picker/image_picker_windows/example/pubspec.yaml +++ b/packages/image_picker/image_picker_windows/example/pubspec.yaml @@ -5,7 +5,7 @@ version: 1.0.0 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/image_picker/image_picker_windows/pubspec.yaml b/packages/image_picker/image_picker_windows/pubspec.yaml index af96030debdf..3b6fd922cbea 100644 --- a/packages/image_picker/image_picker_windows/pubspec.yaml +++ b/packages/image_picker/image_picker_windows/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.1.0+2 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml index 4a79b190bff9..9db9d63c3a79 100644 --- a/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/in_app_purchase/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/pubspec.yaml index 4b9b9b7d64ff..23d771c68afb 100644 --- a/packages/in_app_purchase/in_app_purchase/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/pubspec.yaml @@ -6,7 +6,7 @@ version: 3.0.4 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/in_app_purchase/in_app_purchase_android/example/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_android/example/pubspec.yaml index 9c16efc66e95..0d37b3df1ee5 100644 --- a/packages/in_app_purchase/in_app_purchase_android/example/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_android/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.9.1+hotfix.2" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml index 103251909f14..277be296836c 100644 --- a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.2.2+5 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/in_app_purchase/in_app_purchase_platform_interface/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_platform_interface/pubspec.yaml index 2a0a6bf061d4..98698b80718c 100644 --- a/packages/in_app_purchase/in_app_purchase_platform_interface/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_platform_interface/pubspec.yaml @@ -8,7 +8,7 @@ version: 1.3.1 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/example/pubspec.yaml index 597dfb0703bb..a98e1693aa40 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml index ebd5e55acdad..b154ff304a98 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.3.0+8 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/ios_platform_images/example/pubspec.yaml b/packages/ios_platform_images/example/pubspec.yaml index aa8fea54b287..10be0d6be998 100644 --- a/packages/ios_platform_images/example/pubspec.yaml +++ b/packages/ios_platform_images/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: cupertino_icons: ^1.0.2 diff --git a/packages/ios_platform_images/pubspec.yaml b/packages/ios_platform_images/pubspec.yaml index 4ff67ee137b4..1ce98c121637 100644 --- a/packages/ios_platform_images/pubspec.yaml +++ b/packages/ios_platform_images/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.2.0+8 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/local_auth/local_auth/example/pubspec.yaml b/packages/local_auth/local_auth/example/pubspec.yaml index c8496fcc0da7..305005b34364 100644 --- a/packages/local_auth/local_auth/example/pubspec.yaml +++ b/packages/local_auth/local_auth/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/path_provider/path_provider/example/pubspec.yaml b/packages/path_provider/path_provider/example/pubspec.yaml index a279bbade6bf..ea6f499622f9 100644 --- a/packages/path_provider/path_provider/example/pubspec.yaml +++ b/packages/path_provider/path_provider/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/path_provider/path_provider/pubspec.yaml b/packages/path_provider/path_provider/pubspec.yaml index 6ca8325843e4..1e73497a39ff 100644 --- a/packages/path_provider/path_provider/pubspec.yaml +++ b/packages/path_provider/path_provider/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.0.10 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/path_provider/path_provider_android/example/pubspec.yaml b/packages/path_provider/path_provider_android/example/pubspec.yaml index 75617d8f9747..d546d9f2d729 100644 --- a/packages/path_provider/path_provider_android/example/pubspec.yaml +++ b/packages/path_provider/path_provider_android/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/path_provider/path_provider_ios/example/pubspec.yaml b/packages/path_provider/path_provider_ios/example/pubspec.yaml index 2166076db2b9..00ac1f1af3a7 100644 --- a/packages/path_provider/path_provider_ios/example/pubspec.yaml +++ b/packages/path_provider/path_provider_ios/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/path_provider/path_provider_linux/example/pubspec.yaml b/packages/path_provider/path_provider_linux/example/pubspec.yaml index 252f3510a789..47ed4be220a6 100644 --- a/packages/path_provider/path_provider_linux/example/pubspec.yaml +++ b/packages/path_provider/path_provider_linux/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: "none" environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/path_provider/path_provider_linux/pubspec.yaml b/packages/path_provider/path_provider_linux/pubspec.yaml index 16438a3870d1..46e248f81c18 100644 --- a/packages/path_provider/path_provider_linux/pubspec.yaml +++ b/packages/path_provider/path_provider_linux/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.1.6 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/path_provider/path_provider_macos/example/pubspec.yaml b/packages/path_provider/path_provider_macos/example/pubspec.yaml index d8b93545ed53..42ed28b818d6 100644 --- a/packages/path_provider/path_provider_macos/example/pubspec.yaml +++ b/packages/path_provider/path_provider_macos/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/path_provider/path_provider_macos/pubspec.yaml b/packages/path_provider/path_provider_macos/pubspec.yaml index 444165b86c3f..4381041079b5 100644 --- a/packages/path_provider/path_provider_macos/pubspec.yaml +++ b/packages/path_provider/path_provider_macos/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.0.6 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/path_provider/path_provider_platform_interface/pubspec.yaml b/packages/path_provider/path_provider_platform_interface/pubspec.yaml index 90b40ac7a3d4..92ec432dc394 100644 --- a/packages/path_provider/path_provider_platform_interface/pubspec.yaml +++ b/packages/path_provider/path_provider_platform_interface/pubspec.yaml @@ -8,7 +8,7 @@ version: 2.0.4 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/path_provider/path_provider_windows/example/pubspec.yaml b/packages/path_provider/path_provider_windows/example/pubspec.yaml index d943347df1ff..d48219648b30 100644 --- a/packages/path_provider/path_provider_windows/example/pubspec.yaml +++ b/packages/path_provider/path_provider_windows/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/path_provider/path_provider_windows/pubspec.yaml b/packages/path_provider/path_provider_windows/pubspec.yaml index 49afdd6293e7..f75dd058b36b 100644 --- a/packages/path_provider/path_provider_windows/pubspec.yaml +++ b/packages/path_provider/path_provider_windows/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.0.6 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/quick_actions/quick_actions/example/pubspec.yaml b/packages/quick_actions/quick_actions/example/pubspec.yaml index c4ee86039761..64e61b71e720 100644 --- a/packages/quick_actions/quick_actions/example/pubspec.yaml +++ b/packages/quick_actions/quick_actions/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.9.1+hotfix.2" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml b/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml index aef2e248bade..c465b2aaf99b 100644 --- a/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml +++ b/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml @@ -8,7 +8,7 @@ version: 1.0.2 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/shared_preferences/shared_preferences/example/pubspec.yaml b/packages/shared_preferences/shared_preferences/example/pubspec.yaml index 1cb0f185baf4..4ec5cbbb471f 100644 --- a/packages/shared_preferences/shared_preferences/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/shared_preferences/shared_preferences/pubspec.yaml b/packages/shared_preferences/shared_preferences/pubspec.yaml index 14b56fe69889..a1cea06f5a04 100644 --- a/packages/shared_preferences/shared_preferences/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/pubspec.yaml @@ -7,7 +7,7 @@ version: 2.0.15 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/shared_preferences/shared_preferences_android/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_android/example/pubspec.yaml index 060b94b3ae82..d23270ba386f 100644 --- a/packages/shared_preferences/shared_preferences_android/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_android/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/shared_preferences/shared_preferences_ios/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_ios/example/pubspec.yaml index 4a00e6d23c0a..9f5f7124669d 100644 --- a/packages/shared_preferences/shared_preferences_ios/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_ios/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml index d34973b9dde6..4d44d4e69f93 100644 --- a/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml index 8f3ce1723bc9..922437256748 100644 --- a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.1.1 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/shared_preferences/shared_preferences_macos/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_macos/example/pubspec.yaml index d6f07f8eb2af..b9dfb75c92e7 100644 --- a/packages/shared_preferences/shared_preferences_macos/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_macos/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.12.8" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/shared_preferences/shared_preferences_macos/pubspec.yaml b/packages/shared_preferences/shared_preferences_macos/pubspec.yaml index 615d0b05ba99..9259ef5888fa 100644 --- a/packages/shared_preferences/shared_preferences_macos/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_macos/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.0.4 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml b/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml index 8d775ab8b58c..43669d624f2d 100644 --- a/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.0.0 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/shared_preferences/shared_preferences_web/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_web/example/pubspec.yaml index 832ba912e5a8..656fdeb01876 100644 --- a/packages/shared_preferences/shared_preferences_web/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_web/example/pubspec.yaml @@ -3,7 +3,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.2.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/shared_preferences/shared_preferences_web/pubspec.yaml b/packages/shared_preferences/shared_preferences_web/pubspec.yaml index 9ff76d27714c..c50958363d16 100644 --- a/packages/shared_preferences/shared_preferences_web/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_web/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.0.4 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml index 96762e933a9d..c7a0eb82cc07 100644 --- a/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/shared_preferences/shared_preferences_windows/pubspec.yaml b/packages/shared_preferences/shared_preferences_windows/pubspec.yaml index 99326cb24f18..57e086b81ed3 100644 --- a/packages/shared_preferences/shared_preferences_windows/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_windows/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.1.1 environment: sdk: '>=2.12.0 <3.0.0' - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/url_launcher/url_launcher/example/pubspec.yaml b/packages/url_launcher/url_launcher/example/pubspec.yaml index 3b2bba9833a3..43b5265c45ec 100644 --- a/packages/url_launcher/url_launcher/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index 2cf75df6b0ef..319b6bfd3e0b 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -7,7 +7,7 @@ version: 6.1.2 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/url_launcher/url_launcher_android/example/pubspec.yaml b/packages/url_launcher/url_launcher_android/example/pubspec.yaml index 9af7b2876da9..cdb19458ba07 100644 --- a/packages/url_launcher/url_launcher_android/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_android/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/url_launcher/url_launcher_ios/example/pubspec.yaml b/packages/url_launcher/url_launcher_ios/example/pubspec.yaml index da4c72cd13bb..2e39e92d5638 100644 --- a/packages/url_launcher/url_launcher_ios/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_ios/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/url_launcher/url_launcher_linux/example/pubspec.yaml b/packages/url_launcher/url_launcher_linux/example/pubspec.yaml index 5e6c3fc5384f..90ea19dd2a04 100644 --- a/packages/url_launcher/url_launcher_linux/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_linux/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/url_launcher/url_launcher_linux/pubspec.yaml b/packages/url_launcher/url_launcher_linux/pubspec.yaml index c9472045e499..0bbd4b590cd2 100644 --- a/packages/url_launcher/url_launcher_linux/pubspec.yaml +++ b/packages/url_launcher/url_launcher_linux/pubspec.yaml @@ -6,7 +6,7 @@ version: 3.0.1 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/url_launcher/url_launcher_macos/example/pubspec.yaml b/packages/url_launcher/url_launcher_macos/example/pubspec.yaml index 9bc3062dd08f..2652df03448a 100644 --- a/packages/url_launcher/url_launcher_macos/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_macos/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/url_launcher/url_launcher_macos/pubspec.yaml b/packages/url_launcher/url_launcher_macos/pubspec.yaml index edda6b67cfb3..8f93e57c9dc4 100644 --- a/packages/url_launcher/url_launcher_macos/pubspec.yaml +++ b/packages/url_launcher/url_launcher_macos/pubspec.yaml @@ -6,7 +6,7 @@ version: 3.0.1 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml index cdbdfefba93a..140a1aee9938 100644 --- a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml +++ b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml @@ -8,7 +8,7 @@ version: 2.0.5 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/url_launcher/url_launcher_web/example/pubspec.yaml b/packages/url_launcher/url_launcher_web/example/pubspec.yaml index bface463cfe2..a25f4ce148e9 100644 --- a/packages/url_launcher/url_launcher_web/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_web/example/pubspec.yaml @@ -3,7 +3,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.2.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/url_launcher/url_launcher_windows/example/pubspec.yaml b/packages/url_launcher/url_launcher_windows/example/pubspec.yaml index 08350fdaab65..22b524df2488 100644 --- a/packages/url_launcher/url_launcher_windows/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_windows/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/url_launcher/url_launcher_windows/pubspec.yaml b/packages/url_launcher/url_launcher_windows/pubspec.yaml index c3f224e26adf..2717e3807e21 100644 --- a/packages/url_launcher/url_launcher_windows/pubspec.yaml +++ b/packages/url_launcher/url_launcher_windows/pubspec.yaml @@ -6,7 +6,7 @@ version: 3.0.1 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/video_player/video_player_web/example/pubspec.yaml b/packages/video_player/video_player_web/example/pubspec.yaml index bc70b7d21aca..6fb2cd07ddf1 100644 --- a/packages/video_player/video_player_web/example/pubspec.yaml +++ b/packages/video_player/video_player_web/example/pubspec.yaml @@ -3,7 +3,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.2.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml index 04fba273a2b6..36b89abd1f31 100644 --- a/packages/video_player/video_player_web/pubspec.yaml +++ b/packages/video_player/video_player_web/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.0.10 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/webview_flutter/webview_flutter/example/pubspec.yaml b/packages/webview_flutter/webview_flutter/example/pubspec.yaml index ae3b57e07a89..f1da7cd17b7e 100644 --- a/packages/webview_flutter/webview_flutter/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index 9639b6abe76e..fc1e50f16e24 100644 --- a/packages/webview_flutter/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/pubspec.yaml @@ -6,7 +6,7 @@ version: 3.0.4 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index d6cf2b2a1c17..04da12f765d4 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.8.8 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml index c339a0f4a2ce..b3a20f8d029d 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml @@ -8,7 +8,7 @@ version: 1.9.0 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/webview_flutter/webview_flutter_web/pubspec.yaml b/packages/webview_flutter/webview_flutter_web/pubspec.yaml index a834c9b77d51..6464e20fe37c 100644 --- a/packages/webview_flutter/webview_flutter_web/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_web/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.1.0+3 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index d85bf329a58e..9ce70bc7fdca 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.7.5 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: From a87aad1458a6d461c7a70d64fddafe3c925fdca8 Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Thu, 19 May 2022 17:38:21 -0700 Subject: [PATCH 589/600] [google_maps_flutter_web] Remove custom analysis file. (#5791) --- .../google_maps_flutter_web/CHANGELOG.md | 8 + .../analysis_options.yaml | 1 - .../google_maps_controller_test.dart | 284 ++++++++++-------- .../google_maps_controller_test.mocks.dart | 11 +- .../google_maps_plugin_test.dart | 242 +++++++++------ .../google_maps_plugin_test.mocks.dart | 17 +- .../example/integration_test/marker_test.dart | 56 ++-- .../integration_test/markers_test.dart | 138 +++++---- .../integration_test/projection_test.dart | 26 +- .../resources/icon_image_base64.dart | 2 +- .../example/integration_test/shape_test.dart | 43 ++- .../example/integration_test/shapes_test.dart | 217 ++++++------- .../example/lib/main.dart | 7 +- .../example/pubspec.yaml | 14 +- .../lib/google_maps_flutter_web.dart | 29 +- .../lib/src/circle.dart | 8 +- .../lib/src/circles.dart | 46 ++- .../lib/src/convert.dart | 231 ++++++++------ .../lib/src/google_maps_controller.dart | 127 ++++---- .../lib/src/google_maps_flutter_web.dart | 24 +- .../lib/src/marker.dart | 24 +- .../lib/src/markers.dart | 57 ++-- .../lib/src/polygon.dart | 10 +- .../lib/src/polygons.dart | 48 +-- .../lib/src/polyline.dart | 10 +- .../lib/src/polylines.dart | 48 +-- .../lib/src/shims/dart_ui.dart | 2 +- .../lib/src/shims/dart_ui_fake.dart | 11 +- .../to_screen_location.dart | 18 +- .../lib/src/types.dart | 2 +- .../google_maps_flutter_web/pubspec.yaml | 5 +- script/configs/custom_analysis.yaml | 1 - 32 files changed, 977 insertions(+), 790 deletions(-) delete mode 100644 packages/google_maps_flutter/google_maps_flutter_web/analysis_options.yaml diff --git a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md index f2fe971f4591..8bd3d40babbc 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md @@ -1,3 +1,11 @@ +## 0.3.3 + +* Removes custom `analysis_options.yaml` (and fixes code to comply with newest rules). +* Updates `package:google_maps` dependency to latest (`^6.1.0`). +* Ensures that `convert.dart` sanitizes user-created HTML before passing it to the + Maps JS SDK with `sanitizeHtml` from `package:sanitize_html`. + [More info](https://pub.dev/documentation/sanitize_html/latest/sanitize_html/sanitizeHtml.html). + ## 0.3.2+2 * Removes unnecessary imports. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/analysis_options.yaml b/packages/google_maps_flutter/google_maps_flutter_web/analysis_options.yaml deleted file mode 100644 index 5aeb4e7c5e21..000000000000 --- a/packages/google_maps_flutter/google_maps_flutter_web/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../../analysis_options_legacy.yaml diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.dart index 39aa641b10e4..17fdd81df645 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.dart @@ -18,9 +18,9 @@ import 'google_maps_controller_test.mocks.dart'; // This value is used when comparing long~num, like // LatLng values. -const _acceptableDelta = 0.0000000001; +const double _acceptableDelta = 0.0000000001; -@GenerateMocks([], customMocks: [ +@GenerateMocks([], customMocks: >[ MockSpec(returnNullOnMissingStub: true), MockSpec(returnNullOnMissingStub: true), MockSpec(returnNullOnMissingStub: true), @@ -32,9 +32,9 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); group('GoogleMapController', () { - final int mapId = 33930; + const int mapId = 33930; late GoogleMapController controller; - late StreamController stream; + late StreamController> stream; // Creates a controller with the default mapId and stream controller, and any `options` needed. GoogleMapController _createController({ @@ -59,7 +59,7 @@ void main() { } setUp(() { - stream = StreamController.broadcast(); + stream = StreamController>.broadcast(); }); group('construct/dispose', () { @@ -70,13 +70,13 @@ void main() { testWidgets('constructor creates widget', (WidgetTester tester) async { expect(controller.widget, isNotNull); expect(controller.widget, isA()); - expect((controller.widget as HtmlElementView).viewType, + expect((controller.widget! as HtmlElementView).viewType, endsWith('$mapId')); }); testWidgets('widget is cached when reused', (WidgetTester tester) async { - final first = controller.widget; - final again = controller.widget; + final Widget? first = controller.widget; + final Widget? again = controller.widget; expect(identical(first, again), isTrue); }); @@ -104,7 +104,7 @@ void main() { expect(() async { await controller.getScreenCoordinate( - LatLng(43.3072465, -5.6918241), + const LatLng(43.3072465, -5.6918241), ); }, throwsAssertionError); }); @@ -115,7 +115,7 @@ void main() { expect(() async { await controller.getLatLng( - ScreenCoordinate(x: 640, y: 480), + const ScreenCoordinate(x: 640, y: 480), ); }, throwsAssertionError); }); @@ -143,7 +143,12 @@ void main() { controller.dispose(); expect(() { - controller.updateCircles(CircleUpdates.from({}, {})); + controller.updateCircles( + CircleUpdates.from( + {}, + {}, + ), + ); }, throwsAssertionError); }); @@ -152,7 +157,12 @@ void main() { controller.dispose(); expect(() { - controller.updatePolygons(PolygonUpdates.from({}, {})); + controller.updatePolygons( + PolygonUpdates.from( + {}, + {}, + ), + ); }, throwsAssertionError); }); @@ -161,7 +171,12 @@ void main() { controller.dispose(); expect(() { - controller.updatePolylines(PolylineUpdates.from({}, {})); + controller.updatePolylines( + PolylineUpdates.from( + {}, + {}, + ), + ); }, throwsAssertionError); }); @@ -170,15 +185,20 @@ void main() { controller.dispose(); expect(() { - controller.updateMarkers(MarkerUpdates.from({}, {})); + controller.updateMarkers( + MarkerUpdates.from( + {}, + {}, + ), + ); }, throwsAssertionError); expect(() { - controller.showInfoWindow(MarkerId('any')); + controller.showInfoWindow(const MarkerId('any')); }, throwsAssertionError); expect(() { - controller.hideInfoWindow(MarkerId('any')); + controller.hideInfoWindow(const MarkerId('any')); }, throwsAssertionError); }); @@ -186,7 +206,7 @@ void main() { (WidgetTester tester) async { controller.dispose(); - expect(controller.isInfoWindowShown(MarkerId('any')), false); + expect(controller.isInfoWindowShown(const MarkerId('any')), false); }); }); }); @@ -219,16 +239,23 @@ void main() { controller.init(); // Trigger events on the map, and verify they've been broadcast to the stream - final capturedEvents = stream.stream.take(5); + final Stream> capturedEvents = stream.stream.take(5); gmaps.Event.trigger( - map, 'click', [gmaps.MapMouseEvent()..latLng = gmaps.LatLng(0, 0)]); - gmaps.Event.trigger(map, 'rightclick', - [gmaps.MapMouseEvent()..latLng = gmaps.LatLng(0, 0)]); - gmaps.Event.trigger(map, 'bounds_changed', []); // Causes 2 events - gmaps.Event.trigger(map, 'idle', []); + map, + 'click', + [gmaps.MapMouseEvent()..latLng = gmaps.LatLng(0, 0)], + ); + gmaps.Event.trigger( + map, + 'rightclick', + [gmaps.MapMouseEvent()..latLng = gmaps.LatLng(0, 0)], + ); + // The following line causes 2 events + gmaps.Event.trigger(map, 'bounds_changed', []); + gmaps.Event.trigger(map, 'idle', []); - final events = await capturedEvents.toList(); + final List> events = await capturedEvents.toList(); expect(events[0], isA()); expect(events[1], isA()); @@ -237,7 +264,7 @@ void main() { expect(events[4], isA()); }); - testWidgets('binds geometry controllers to map\'s', + testWidgets("binds geometry controllers to map's", (WidgetTester tester) async { controller = _createController(); controller.debugSetOverrides( @@ -257,44 +284,44 @@ void main() { }); testWidgets('renders initial geometry', (WidgetTester tester) async { - controller = _createController(circles: { - Circle( + controller = _createController(circles: { + const Circle( circleId: CircleId('circle-1'), zIndex: 1234, ), - }, markers: { - Marker( + }, markers: { + const Marker( markerId: MarkerId('marker-1'), infoWindow: InfoWindow( title: 'title for test', snippet: 'snippet for test', ), ), - }, polygons: { - Polygon(polygonId: PolygonId('polygon-1'), points: [ + }, polygons: { + const Polygon(polygonId: PolygonId('polygon-1'), points: [ LatLng(43.355114, -5.851333), LatLng(43.354797, -5.851860), LatLng(43.354469, -5.851318), LatLng(43.354762, -5.850824), ]), - Polygon( + const Polygon( polygonId: PolygonId('polygon-2-with-holes'), - points: [ + points: [ LatLng(43.355114, -5.851333), LatLng(43.354797, -5.851860), LatLng(43.354469, -5.851318), LatLng(43.354762, -5.850824), ], - holes: [ - [ + holes: >[ + [ LatLng(41.354797, -6.851860), LatLng(41.354469, -6.851318), LatLng(41.354762, -6.850824), ] ], ), - }, polylines: { - Polyline(polylineId: PolylineId('polyline-1'), points: [ + }, polylines: { + const Polyline(polylineId: PolylineId('polyline-1'), points: [ LatLng(43.355114, -5.851333), LatLng(43.354797, -5.851860), LatLng(43.354469, -5.851318), @@ -311,14 +338,16 @@ void main() { controller.init(); - final capturedCircles = + final Set capturedCircles = verify(circles.addCircles(captureAny)).captured[0] as Set; - final capturedMarkers = + final Set capturedMarkers = verify(markers.addMarkers(captureAny)).captured[0] as Set; - final capturedPolygons = verify(polygons.addPolygons(captureAny)) - .captured[0] as Set; - final capturedPolylines = verify(polylines.addPolylines(captureAny)) - .captured[0] as Set; + final Set capturedPolygons = + verify(polygons.addPolygons(captureAny)).captured[0] + as Set; + final Set capturedPolylines = + verify(polylines.addPolylines(captureAny)).captured[0] + as Set; expect(capturedCircles.first.circleId.value, 'circle-1'); expect(capturedCircles.first.zIndex, 1234); @@ -334,8 +363,8 @@ void main() { testWidgets('empty infoWindow does not create InfoWindow instance.', (WidgetTester tester) async { - controller = _createController(markers: { - Marker(markerId: MarkerId('marker-1')), + controller = _createController(markers: { + const Marker(markerId: MarkerId('marker-1')), }); controller.debugSetOverrides( @@ -344,7 +373,7 @@ void main() { controller.init(); - final capturedMarkers = + final Set capturedMarkers = verify(markers.addMarkers(captureAny)).captured[0] as Set; expect(capturedMarkers.first.infoWindow, InfoWindow.noText); @@ -356,11 +385,12 @@ void main() { capturedOptions = null; }); testWidgets('translates initial options', (WidgetTester tester) async { - controller = _createController(options: { + controller = _createController(options: { 'mapType': 2, 'zoomControlsEnabled': true, }); - controller.debugSetOverrides(createMap: (_, options) { + controller.debugSetOverrides( + createMap: (_, gmaps.MapOptions options) { capturedOptions = options; return map; }); @@ -377,10 +407,11 @@ void main() { testWidgets('disables gestureHandling with scrollGesturesEnabled false', (WidgetTester tester) async { - controller = _createController(options: { + controller = _createController(options: { 'scrollGesturesEnabled': false, }); - controller.debugSetOverrides(createMap: (_, options) { + controller.debugSetOverrides( + createMap: (_, gmaps.MapOptions options) { capturedOptions = options; return map; }); @@ -395,10 +426,11 @@ void main() { testWidgets('disables gestureHandling with zoomGesturesEnabled false', (WidgetTester tester) async { - controller = _createController(options: { + controller = _createController(options: { 'zoomGesturesEnabled': false, }); - controller.debugSetOverrides(createMap: (_, options) { + controller.debugSetOverrides( + createMap: (_, gmaps.MapOptions options) { capturedOptions = options; return map; }); @@ -414,7 +446,7 @@ void main() { testWidgets('sets initial position when passed', (WidgetTester tester) async { controller = _createController( - initialCameraPosition: CameraPosition( + initialCameraPosition: const CameraPosition( target: LatLng(43.308, -5.6910), zoom: 12, bearing: 0, @@ -422,7 +454,8 @@ void main() { ), ); - controller.debugSetOverrides(createMap: (_, options) { + controller.debugSetOverrides( + createMap: (_, gmaps.MapOptions options) { capturedOptions = options; return map; }); @@ -444,7 +477,7 @@ void main() { testWidgets('initializes with traffic layer', (WidgetTester tester) async { - controller = _createController(options: { + controller = _createController(options: { 'trafficEnabled': true, }); controller.debugSetOverrides(createMap: (_, __) => map); @@ -472,7 +505,7 @@ void main() { group('updateRawOptions', () { testWidgets('can update `options`', (WidgetTester tester) async { - controller.updateRawOptions({ + controller.updateRawOptions({ 'mapType': 2, }); @@ -482,13 +515,13 @@ void main() { testWidgets('can turn on/off traffic', (WidgetTester tester) async { expect(controller.trafficLayer, isNull); - controller.updateRawOptions({ + controller.updateRawOptions({ 'trafficEnabled': true, }); expect(controller.trafficLayer, isNotNull); - controller.updateRawOptions({ + controller.updateRawOptions({ 'trafficEnabled': false, }); @@ -498,11 +531,11 @@ void main() { group('viewport getters', () { testWidgets('getVisibleRegion', (WidgetTester tester) async { - final gmCenter = map.center!; - final center = + final gmaps.LatLng gmCenter = map.center!; + final LatLng center = LatLng(gmCenter.lat.toDouble(), gmCenter.lng.toDouble()); - final bounds = await controller.getVisibleRegion(); + final LatLngBounds bounds = await controller.getVisibleRegion(); expect(bounds.contains(center), isTrue, reason: @@ -516,10 +549,14 @@ void main() { group('moveCamera', () { testWidgets('newLatLngZoom', (WidgetTester tester) async { - await (controller - .moveCamera(CameraUpdate.newLatLngZoom(LatLng(19, 26), 12))); + await controller.moveCamera( + CameraUpdate.newLatLngZoom( + const LatLng(19, 26), + 12, + ), + ); - final gmCenter = map.center!; + final gmaps.LatLng gmCenter = map.center!; expect(map.zoom, 12); expect(gmCenter.lat, closeTo(19, _acceptableDelta)); @@ -528,10 +565,7 @@ void main() { }); group('map.projection methods', () { - // These are too much for dart mockito, can't mock: - // map.projection.method() (in Javascript ;) ) - - // Caused https://github.com/flutter/flutter/issues/67606 + // Tested in projection_test.dart }); }); @@ -542,116 +576,122 @@ void main() { }); testWidgets('updateCircles', (WidgetTester tester) async { - final mock = MockCirclesController(); + final MockCirclesController mock = MockCirclesController(); controller.debugSetOverrides(circles: mock); - final previous = { - Circle(circleId: CircleId('to-be-updated')), - Circle(circleId: CircleId('to-be-removed')), + final Set previous = { + const Circle(circleId: CircleId('to-be-updated')), + const Circle(circleId: CircleId('to-be-removed')), }; - final current = { - Circle(circleId: CircleId('to-be-updated'), visible: false), - Circle(circleId: CircleId('to-be-added')), + final Set current = { + const Circle(circleId: CircleId('to-be-updated'), visible: false), + const Circle(circleId: CircleId('to-be-added')), }; controller.updateCircles(CircleUpdates.from(previous, current)); - verify(mock.removeCircles({ - CircleId('to-be-removed'), + verify(mock.removeCircles({ + const CircleId('to-be-removed'), })); - verify(mock.addCircles({ - Circle(circleId: CircleId('to-be-added')), + verify(mock.addCircles({ + const Circle(circleId: CircleId('to-be-added')), })); - verify(mock.changeCircles({ - Circle(circleId: CircleId('to-be-updated'), visible: false), + verify(mock.changeCircles({ + const Circle(circleId: CircleId('to-be-updated'), visible: false), })); }); testWidgets('updateMarkers', (WidgetTester tester) async { - final mock = MockMarkersController(); + final MockMarkersController mock = MockMarkersController(); controller.debugSetOverrides(markers: mock); - final previous = { - Marker(markerId: MarkerId('to-be-updated')), - Marker(markerId: MarkerId('to-be-removed')), + final Set previous = { + const Marker(markerId: MarkerId('to-be-updated')), + const Marker(markerId: MarkerId('to-be-removed')), }; - final current = { - Marker(markerId: MarkerId('to-be-updated'), visible: false), - Marker(markerId: MarkerId('to-be-added')), + final Set current = { + const Marker(markerId: MarkerId('to-be-updated'), visible: false), + const Marker(markerId: MarkerId('to-be-added')), }; controller.updateMarkers(MarkerUpdates.from(previous, current)); - verify(mock.removeMarkers({ - MarkerId('to-be-removed'), + verify(mock.removeMarkers({ + const MarkerId('to-be-removed'), })); - verify(mock.addMarkers({ - Marker(markerId: MarkerId('to-be-added')), + verify(mock.addMarkers({ + const Marker(markerId: MarkerId('to-be-added')), })); - verify(mock.changeMarkers({ - Marker(markerId: MarkerId('to-be-updated'), visible: false), + verify(mock.changeMarkers({ + const Marker(markerId: MarkerId('to-be-updated'), visible: false), })); }); testWidgets('updatePolygons', (WidgetTester tester) async { - final mock = MockPolygonsController(); + final MockPolygonsController mock = MockPolygonsController(); controller.debugSetOverrides(polygons: mock); - final previous = { - Polygon(polygonId: PolygonId('to-be-updated')), - Polygon(polygonId: PolygonId('to-be-removed')), + final Set previous = { + const Polygon(polygonId: PolygonId('to-be-updated')), + const Polygon(polygonId: PolygonId('to-be-removed')), }; - final current = { - Polygon(polygonId: PolygonId('to-be-updated'), visible: false), - Polygon(polygonId: PolygonId('to-be-added')), + final Set current = { + const Polygon(polygonId: PolygonId('to-be-updated'), visible: false), + const Polygon(polygonId: PolygonId('to-be-added')), }; controller.updatePolygons(PolygonUpdates.from(previous, current)); - verify(mock.removePolygons({ - PolygonId('to-be-removed'), + verify(mock.removePolygons({ + const PolygonId('to-be-removed'), })); - verify(mock.addPolygons({ - Polygon(polygonId: PolygonId('to-be-added')), + verify(mock.addPolygons({ + const Polygon(polygonId: PolygonId('to-be-added')), })); - verify(mock.changePolygons({ - Polygon(polygonId: PolygonId('to-be-updated'), visible: false), + verify(mock.changePolygons({ + const Polygon(polygonId: PolygonId('to-be-updated'), visible: false), })); }); testWidgets('updatePolylines', (WidgetTester tester) async { - final mock = MockPolylinesController(); + final MockPolylinesController mock = MockPolylinesController(); controller.debugSetOverrides(polylines: mock); - final previous = { - Polyline(polylineId: PolylineId('to-be-updated')), - Polyline(polylineId: PolylineId('to-be-removed')), + final Set previous = { + const Polyline(polylineId: PolylineId('to-be-updated')), + const Polyline(polylineId: PolylineId('to-be-removed')), }; - final current = { - Polyline(polylineId: PolylineId('to-be-updated'), visible: false), - Polyline(polylineId: PolylineId('to-be-added')), + final Set current = { + const Polyline( + polylineId: PolylineId('to-be-updated'), + visible: false, + ), + const Polyline(polylineId: PolylineId('to-be-added')), }; controller.updatePolylines(PolylineUpdates.from(previous, current)); - verify(mock.removePolylines({ - PolylineId('to-be-removed'), + verify(mock.removePolylines({ + const PolylineId('to-be-removed'), })); - verify(mock.addPolylines({ - Polyline(polylineId: PolylineId('to-be-added')), + verify(mock.addPolylines({ + const Polyline(polylineId: PolylineId('to-be-added')), })); - verify(mock.changePolylines({ - Polyline(polylineId: PolylineId('to-be-updated'), visible: false), + verify(mock.changePolylines({ + const Polyline( + polylineId: PolylineId('to-be-updated'), + visible: false, + ), })); }); testWidgets('infoWindow visibility', (WidgetTester tester) async { - final mock = MockMarkersController(); - final markerId = MarkerId('marker-with-infowindow'); + final MockMarkersController mock = MockMarkersController(); + const MarkerId markerId = MarkerId('marker-with-infowindow'); when(mock.isInfoWindowShown(markerId)).thenReturn(true); controller.debugSetOverrides(markers: mock); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart index 530707c6c328..9565935bd8ed 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.0.16 from annotations +// Mocks generated by Mockito 5.2.0 from annotations // in google_maps_flutter_web_integration_tests/integration_test/google_maps_controller_test.dart. // Do not manually edit this file. @@ -8,6 +8,7 @@ import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platf import 'package:google_maps_flutter_web/google_maps_flutter_web.dart' as _i3; import 'package:mockito/mockito.dart' as _i1; +// ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references @@ -58,8 +59,6 @@ class MockCirclesController extends _i1.Mock implements _i3.CirclesController { void bindToMap(int? mapId, _i2.GMap? googleMap) => super.noSuchMethod(Invocation.method(#bindToMap, [mapId, googleMap]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [PolygonsController]. @@ -102,8 +101,6 @@ class MockPolygonsController extends _i1.Mock void bindToMap(int? mapId, _i2.GMap? googleMap) => super.noSuchMethod(Invocation.method(#bindToMap, [mapId, googleMap]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [PolylinesController]. @@ -146,8 +143,6 @@ class MockPolylinesController extends _i1.Mock void bindToMap(int? mapId, _i2.GMap? googleMap) => super.noSuchMethod(Invocation.method(#bindToMap, [mapId, googleMap]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [MarkersController]. @@ -201,6 +196,4 @@ class MockMarkersController extends _i1.Mock implements _i3.MarkersController { void bindToMap(int? mapId, _i2.GMap? googleMap) => super.noSuchMethod(Invocation.method(#bindToMap, [mapId, googleMap]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.dart index a3cf86e593fe..f0fd5a232e00 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.dart @@ -5,19 +5,18 @@ import 'dart:async'; import 'dart:js_util' show getProperty; -import 'package:integration_test/integration_test.dart'; import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps/google_maps.dart' as gmaps; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; -import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; -import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; - import 'google_maps_plugin_test.mocks.dart'; -@GenerateMocks([], customMocks: [ +@GenerateMocks([], customMocks: >[ MockSpec(returnNullOnMissingStub: true), ]) @@ -51,7 +50,7 @@ void main() { group('after buildWidget', () { setUp(() { - plugin.debugSetMapById({0: controller}); + plugin.debugSetMapById({0: controller}); }); testWidgets('cannot call methods after dispose', @@ -69,13 +68,15 @@ void main() { }); group('buildView', () { - final testMapId = 33930; - final initialCameraPosition = CameraPosition(target: LatLng(0, 0)); + const int testMapId = 33930; + const CameraPosition initialCameraPosition = + CameraPosition(target: LatLng(0, 0)); testWidgets( 'returns an HtmlElementView and caches the controller for later', (WidgetTester tester) async { - final Map cache = {}; + final Map cache = + {}; plugin.debugSetMapById(cache); final Widget widget = plugin.buildView( @@ -106,11 +107,14 @@ void main() { testWidgets('returns cached instance if it already exists', (WidgetTester tester) async { - final expected = HtmlElementView(viewType: 'only-for-testing'); + const HtmlElementView expected = + HtmlElementView(viewType: 'only-for-testing'); when(controller.widget).thenReturn(expected); - plugin.debugSetMapById({testMapId: controller}); + plugin.debugSetMapById({ + testMapId: controller, + }); - final widget = plugin.buildView( + final Widget widget = plugin.buildView( testMapId, onPlatformViewCreated, initialCameraPosition: initialCameraPosition, @@ -122,7 +126,8 @@ void main() { testWidgets( 'asynchronously reports onPlatformViewCreated the first time it happens', (WidgetTester tester) async { - final Map cache = {}; + final Map cache = + {}; plugin.debugSetMapById(cache); plugin.buildView( @@ -157,47 +162,53 @@ void main() { }); group('setMapStyles', () { - String mapStyle = '''[{ - "featureType": "poi.park", - "elementType": "labels.text.fill", - "stylers": [{"color": "#6b9a76"}] - }]'''; + const String mapStyle = ''' +[{ + "featureType": "poi.park", + "elementType": "labels.text.fill", + "stylers": [{"color": "#6b9a76"}] +}]'''; testWidgets('translates styles for controller', (WidgetTester tester) async { - plugin.debugSetMapById({0: controller}); + plugin.debugSetMapById({0: controller}); await plugin.setMapStyle(mapStyle, mapId: 0); - var captured = + final dynamic captured = verify(controller.updateRawOptions(captureThat(isMap))).captured[0]; expect(captured, contains('styles')); - var styles = captured['styles']; + final List styles = + captured['styles'] as List; expect(styles.length, 1); // Let's peek inside the styles... - var style = styles[0] as gmaps.MapTypeStyle; + final gmaps.MapTypeStyle style = styles[0]; expect(style.featureType, 'poi.park'); expect(style.elementType, 'labels.text.fill'); expect(style.stylers?.length, 1); - expect(getProperty(style.stylers![0]!, 'color'), '#6b9a76'); + expect(getProperty(style.stylers![0]!, 'color'), '#6b9a76'); }); }); group('Noop methods:', () { - int mapId = 0; + const int mapId = 0; setUp(() { - plugin.debugSetMapById({mapId: controller}); + plugin.debugSetMapById({mapId: controller}); }); // Options testWidgets('updateTileOverlays', (WidgetTester tester) async { - final update = - plugin.updateTileOverlays(mapId: mapId, newTileOverlays: {}); + final Future update = plugin.updateTileOverlays( + mapId: mapId, + newTileOverlays: {}, + ); expect(update, completion(null)); }); testWidgets('updateTileOverlays', (WidgetTester tester) async { - final update = - plugin.clearTileCache(TileOverlayId('any'), mapId: mapId); + final Future update = plugin.clearTileCache( + const TileOverlayId('any'), + mapId: mapId, + ); expect(update, completion(null)); }); }); @@ -205,13 +216,15 @@ void main() { // These methods only pass-through values from the plugin to the controller // so we verify them all together here... group('Pass-through methods:', () { - int mapId = 0; + const int mapId = 0; setUp(() { - plugin.debugSetMapById({mapId: controller}); + plugin.debugSetMapById({mapId: controller}); }); // Options testWidgets('updateMapOptions', (WidgetTester tester) async { - final expectedMapOptions = {'someOption': 12345}; + final Map expectedMapOptions = { + 'someOption': 12345 + }; await plugin.updateMapOptions(expectedMapOptions, mapId: mapId); @@ -219,28 +232,40 @@ void main() { }); // Geometry testWidgets('updateMarkers', (WidgetTester tester) async { - final expectedUpdates = MarkerUpdates.from({}, {}); + final MarkerUpdates expectedUpdates = MarkerUpdates.from( + {}, + {}, + ); await plugin.updateMarkers(expectedUpdates, mapId: mapId); verify(controller.updateMarkers(expectedUpdates)); }); testWidgets('updatePolygons', (WidgetTester tester) async { - final expectedUpdates = PolygonUpdates.from({}, {}); + final PolygonUpdates expectedUpdates = PolygonUpdates.from( + {}, + {}, + ); await plugin.updatePolygons(expectedUpdates, mapId: mapId); verify(controller.updatePolygons(expectedUpdates)); }); testWidgets('updatePolylines', (WidgetTester tester) async { - final expectedUpdates = PolylineUpdates.from({}, {}); + final PolylineUpdates expectedUpdates = PolylineUpdates.from( + {}, + {}, + ); await plugin.updatePolylines(expectedUpdates, mapId: mapId); verify(controller.updatePolylines(expectedUpdates)); }); testWidgets('updateCircles', (WidgetTester tester) async { - final expectedUpdates = CircleUpdates.from({}, {}); + final CircleUpdates expectedUpdates = CircleUpdates.from( + {}, + {}, + ); await plugin.updateCircles(expectedUpdates, mapId: mapId); @@ -248,16 +273,18 @@ void main() { }); // Camera testWidgets('animateCamera', (WidgetTester tester) async { - final expectedUpdates = - CameraUpdate.newLatLng(LatLng(43.3626, -5.8433)); + final CameraUpdate expectedUpdates = CameraUpdate.newLatLng( + const LatLng(43.3626, -5.8433), + ); await plugin.animateCamera(expectedUpdates, mapId: mapId); verify(controller.moveCamera(expectedUpdates)); }); testWidgets('moveCamera', (WidgetTester tester) async { - final expectedUpdates = - CameraUpdate.newLatLng(LatLng(43.3628, -5.8478)); + final CameraUpdate expectedUpdates = CameraUpdate.newLatLng( + const LatLng(43.3628, -5.8478), + ); await plugin.moveCamera(expectedUpdates, mapId: mapId); @@ -268,8 +295,8 @@ void main() { testWidgets('getVisibleRegion', (WidgetTester tester) async { when(controller.getVisibleRegion()) .thenAnswer((_) async => LatLngBounds( - northeast: LatLng(47.2359634, -68.0192019), - southwest: LatLng(34.5019594, -120.4974629), + northeast: const LatLng(47.2359634, -68.0192019), + southwest: const LatLng(34.5019594, -120.4974629), )); await plugin.getVisibleRegion(mapId: mapId); @@ -285,10 +312,10 @@ void main() { testWidgets('getScreenCoordinate', (WidgetTester tester) async { when(controller.getScreenCoordinate(any)).thenAnswer( - (_) async => ScreenCoordinate(x: 320, y: 240) // fake return + (_) async => const ScreenCoordinate(x: 320, y: 240) // fake return ); - final latLng = LatLng(43.3613, -5.8499); + const LatLng latLng = LatLng(43.3613, -5.8499); await plugin.getScreenCoordinate(latLng, mapId: mapId); @@ -296,11 +323,11 @@ void main() { }); testWidgets('getLatLng', (WidgetTester tester) async { - when(controller.getLatLng(any)) - .thenAnswer((_) async => LatLng(43.3613, -5.8499) // fake return - ); + when(controller.getLatLng(any)).thenAnswer( + (_) async => const LatLng(43.3613, -5.8499) // fake return + ); - final coordinates = ScreenCoordinate(x: 19, y: 26); + const ScreenCoordinate coordinates = ScreenCoordinate(x: 19, y: 26); await plugin.getLatLng(coordinates, mapId: mapId); @@ -309,7 +336,7 @@ void main() { // InfoWindows testWidgets('showMarkerInfoWindow', (WidgetTester tester) async { - final markerId = MarkerId('testing-123'); + const MarkerId markerId = MarkerId('testing-123'); await plugin.showMarkerInfoWindow(markerId, mapId: mapId); @@ -317,7 +344,7 @@ void main() { }); testWidgets('hideMarkerInfoWindow', (WidgetTester tester) async { - final markerId = MarkerId('testing-123'); + const MarkerId markerId = MarkerId('testing-123'); await plugin.hideMarkerInfoWindow(markerId, mapId: mapId); @@ -327,7 +354,7 @@ void main() { testWidgets('isMarkerInfoWindowShown', (WidgetTester tester) async { when(controller.isInfoWindowShown(any)).thenReturn(true); - final markerId = MarkerId('testing-123'); + const MarkerId markerId = MarkerId('testing-123'); await plugin.isMarkerInfoWindowShown(markerId, mapId: mapId); @@ -337,18 +364,18 @@ void main() { // Verify all event streams are filtered correctly from the main one... group('Event Streams', () { - int mapId = 0; - late StreamController streamController; + const int mapId = 0; + late StreamController> streamController; setUp(() { - streamController = StreamController.broadcast(); + streamController = StreamController>.broadcast(); when(controller.events) - .thenAnswer((realInvocation) => streamController.stream); - plugin.debugSetMapById({mapId: controller}); + .thenAnswer((Invocation realInvocation) => streamController.stream); + plugin.debugSetMapById({mapId: controller}); }); // Dispatches a few events in the global streamController, and expects *only* the passed event to be there. Future _testStreamFiltering( - Stream stream, MapEvent event) async { + Stream> stream, MapEvent event) async { Timer.run(() { streamController.add(_OtherMapEvent(mapId)); streamController.add(event); @@ -356,7 +383,7 @@ void main() { streamController.close(); }); - final events = await stream.toList(); + final List> events = await stream.toList(); expect(events.length, 1); expect(events[0], event); @@ -364,113 +391,144 @@ void main() { // Camera events testWidgets('onCameraMoveStarted', (WidgetTester tester) async { - final event = CameraMoveStartedEvent(mapId); + final CameraMoveStartedEvent event = CameraMoveStartedEvent(mapId); - final stream = plugin.onCameraMoveStarted(mapId: mapId); + final Stream stream = + plugin.onCameraMoveStarted(mapId: mapId); await _testStreamFiltering(stream, event); }); testWidgets('onCameraMoveStarted', (WidgetTester tester) async { - final event = CameraMoveEvent( + final CameraMoveEvent event = CameraMoveEvent( mapId, - CameraPosition( + const CameraPosition( target: LatLng(43.3790, -5.8660), ), ); - final stream = plugin.onCameraMove(mapId: mapId); + final Stream stream = + plugin.onCameraMove(mapId: mapId); await _testStreamFiltering(stream, event); }); testWidgets('onCameraIdle', (WidgetTester tester) async { - final event = CameraIdleEvent(mapId); + final CameraIdleEvent event = CameraIdleEvent(mapId); - final stream = plugin.onCameraIdle(mapId: mapId); + final Stream stream = + plugin.onCameraIdle(mapId: mapId); await _testStreamFiltering(stream, event); }); // Marker events testWidgets('onMarkerTap', (WidgetTester tester) async { - final event = MarkerTapEvent(mapId, MarkerId('test-123')); + final MarkerTapEvent event = MarkerTapEvent( + mapId, + const MarkerId('test-123'), + ); - final stream = plugin.onMarkerTap(mapId: mapId); + final Stream stream = plugin.onMarkerTap(mapId: mapId); await _testStreamFiltering(stream, event); }); testWidgets('onInfoWindowTap', (WidgetTester tester) async { - final event = InfoWindowTapEvent(mapId, MarkerId('test-123')); + final InfoWindowTapEvent event = InfoWindowTapEvent( + mapId, + const MarkerId('test-123'), + ); - final stream = plugin.onInfoWindowTap(mapId: mapId); + final Stream stream = + plugin.onInfoWindowTap(mapId: mapId); await _testStreamFiltering(stream, event); }); testWidgets('onMarkerDragStart', (WidgetTester tester) async { - final event = MarkerDragStartEvent( + final MarkerDragStartEvent event = MarkerDragStartEvent( mapId, - LatLng(43.3677, -5.8372), - MarkerId('test-123'), + const LatLng(43.3677, -5.8372), + const MarkerId('test-123'), ); - final stream = plugin.onMarkerDragStart(mapId: mapId); + final Stream stream = + plugin.onMarkerDragStart(mapId: mapId); await _testStreamFiltering(stream, event); }); testWidgets('onMarkerDrag', (WidgetTester tester) async { - final event = MarkerDragEvent( + final MarkerDragEvent event = MarkerDragEvent( mapId, - LatLng(43.3677, -5.8372), - MarkerId('test-123'), + const LatLng(43.3677, -5.8372), + const MarkerId('test-123'), ); - final stream = plugin.onMarkerDrag(mapId: mapId); + final Stream stream = + plugin.onMarkerDrag(mapId: mapId); await _testStreamFiltering(stream, event); }); testWidgets('onMarkerDragEnd', (WidgetTester tester) async { - final event = MarkerDragEndEvent( + final MarkerDragEndEvent event = MarkerDragEndEvent( mapId, - LatLng(43.3677, -5.8372), - MarkerId('test-123'), + const LatLng(43.3677, -5.8372), + const MarkerId('test-123'), ); - final stream = plugin.onMarkerDragEnd(mapId: mapId); + final Stream stream = + plugin.onMarkerDragEnd(mapId: mapId); await _testStreamFiltering(stream, event); }); // Geometry testWidgets('onPolygonTap', (WidgetTester tester) async { - final event = PolygonTapEvent(mapId, PolygonId('test-123')); + final PolygonTapEvent event = PolygonTapEvent( + mapId, + const PolygonId('test-123'), + ); - final stream = plugin.onPolygonTap(mapId: mapId); + final Stream stream = + plugin.onPolygonTap(mapId: mapId); await _testStreamFiltering(stream, event); }); testWidgets('onPolylineTap', (WidgetTester tester) async { - final event = PolylineTapEvent(mapId, PolylineId('test-123')); + final PolylineTapEvent event = PolylineTapEvent( + mapId, + const PolylineId('test-123'), + ); - final stream = plugin.onPolylineTap(mapId: mapId); + final Stream stream = + plugin.onPolylineTap(mapId: mapId); await _testStreamFiltering(stream, event); }); testWidgets('onCircleTap', (WidgetTester tester) async { - final event = CircleTapEvent(mapId, CircleId('test-123')); + final CircleTapEvent event = CircleTapEvent( + mapId, + const CircleId('test-123'), + ); - final stream = plugin.onCircleTap(mapId: mapId); + final Stream stream = plugin.onCircleTap(mapId: mapId); await _testStreamFiltering(stream, event); }); // Map taps testWidgets('onTap', (WidgetTester tester) async { - final event = MapTapEvent(mapId, LatLng(43.3597, -5.8458)); + final MapTapEvent event = MapTapEvent( + mapId, + const LatLng(43.3597, -5.8458), + ); - final stream = plugin.onTap(mapId: mapId); + final Stream stream = plugin.onTap(mapId: mapId); await _testStreamFiltering(stream, event); }); testWidgets('onLongPress', (WidgetTester tester) async { - final event = MapLongPressEvent(mapId, LatLng(43.3608, -5.8425)); + final MapLongPressEvent event = MapLongPressEvent( + mapId, + const LatLng(43.3608, -5.8425), + ); - final stream = plugin.onLongPress(mapId: mapId); + final Stream stream = + plugin.onLongPress(mapId: mapId); await _testStreamFiltering(stream, event); }); @@ -478,6 +536,6 @@ void main() { }); } -class _OtherMapEvent extends MapEvent { +class _OtherMapEvent extends MapEvent { _OtherMapEvent(int mapId) : super(mapId, null); } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.mocks.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.mocks.dart index d2df11c6ffa9..bbc92ffc6096 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.mocks.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.0.16 from annotations +// Mocks generated by Mockito 5.2.0 from annotations // in google_maps_flutter_web_integration_tests/integration_test/google_maps_plugin_test.dart. // Do not manually edit this file. @@ -9,6 +9,7 @@ import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platf import 'package:google_maps_flutter_web/google_maps_flutter_web.dart' as _i4; import 'package:mockito/mockito.dart' as _i1; +// ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references @@ -34,15 +35,15 @@ class _FakeLatLng_3 extends _i1.Fake implements _i3.LatLng {} class MockGoogleMapController extends _i1.Mock implements _i4.GoogleMapController { @override - _i2.StreamController<_i3.MapEvent> get stream => + _i2.StreamController<_i3.MapEvent> get stream => (super.noSuchMethod(Invocation.getter(#stream), - returnValue: _FakeStreamController_0<_i3.MapEvent>()) - as _i2.StreamController<_i3.MapEvent>); + returnValue: _FakeStreamController_0<_i3.MapEvent>()) + as _i2.StreamController<_i3.MapEvent>); @override - _i2.Stream<_i3.MapEvent> get events => + _i2.Stream<_i3.MapEvent> get events => (super.noSuchMethod(Invocation.getter(#events), - returnValue: Stream<_i3.MapEvent>.empty()) - as _i2.Stream<_i3.MapEvent>); + returnValue: Stream<_i3.MapEvent>.empty()) + as _i2.Stream<_i3.MapEvent>); @override bool get isInitialized => (super.noSuchMethod(Invocation.getter(#isInitialized), returnValue: false) @@ -126,6 +127,4 @@ class MockGoogleMapController extends _i1.Mock @override void dispose() => super.noSuchMethod(Invocation.method(#dispose, []), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_test.dart index cfa36febbbfe..e07ade03bba3 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_test.dart @@ -5,10 +5,10 @@ import 'dart:async'; import 'dart:html' as html; -import 'package:integration_test/integration_test.dart'; +import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps/google_maps.dart' as gmaps; import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; -import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; /// Test Markers void main() { @@ -40,7 +40,7 @@ void main() { } setUp(() { - _methodCalledCompleter = Completer(); + _methodCalledCompleter = Completer(); methodCalled = _methodCalledCompleter.future; }); @@ -55,7 +55,7 @@ void main() { MarkerController(marker: marker, onTap: onTap); // Trigger a click event... - gmaps.Event.trigger(marker, 'click', [gmaps.MapMouseEvent()]); + gmaps.Event.trigger(marker, 'click', [gmaps.MapMouseEvent()]); // The event handling is now truly async. Wait for it... expect(await methodCalled, isTrue); @@ -66,7 +66,7 @@ void main() { // Trigger a drag end event... gmaps.Event.trigger(marker, 'dragstart', - [gmaps.MapMouseEvent()..latLng = gmaps.LatLng(0, 0)]); + [gmaps.MapMouseEvent()..latLng = gmaps.LatLng(0, 0)]); expect(await methodCalled, isTrue); }); @@ -76,7 +76,10 @@ void main() { // Trigger a drag end event... gmaps.Event.trigger( - marker, 'drag', [gmaps.MapMouseEvent()..latLng = gmaps.LatLng(0, 0)]); + marker, + 'drag', + [gmaps.MapMouseEvent()..latLng = gmaps.LatLng(0, 0)], + ); expect(await methodCalled, isTrue); }); @@ -85,15 +88,19 @@ void main() { MarkerController(marker: marker, onDragEnd: onDragEnd); // Trigger a drag end event... - gmaps.Event.trigger(marker, 'dragend', - [gmaps.MapMouseEvent()..latLng = gmaps.LatLng(0, 0)]); + gmaps.Event.trigger( + marker, + 'dragend', + [gmaps.MapMouseEvent()..latLng = gmaps.LatLng(0, 0)], + ); expect(await methodCalled, isTrue); }); testWidgets('update', (WidgetTester tester) async { - final controller = MarkerController(marker: marker); - final options = gmaps.MarkerOptions()..draggable = true; + final MarkerController controller = MarkerController(marker: marker); + final gmaps.MarkerOptions options = gmaps.MarkerOptions() + ..draggable = true; expect(marker.draggable, isNull); @@ -104,7 +111,7 @@ void main() { testWidgets('infoWindow null, showInfoWindow.', (WidgetTester tester) async { - final controller = MarkerController(marker: marker); + final MarkerController controller = MarkerController(marker: marker); controller.showInfoWindow(); @@ -112,11 +119,13 @@ void main() { }); testWidgets('showInfoWindow', (WidgetTester tester) async { - final infoWindow = gmaps.InfoWindow(); - final map = gmaps.GMap(html.DivElement()); + final gmaps.InfoWindow infoWindow = gmaps.InfoWindow(); + final gmaps.GMap map = gmaps.GMap(html.DivElement()); marker.set('map', map); - final controller = - MarkerController(marker: marker, infoWindow: infoWindow); + final MarkerController controller = MarkerController( + marker: marker, + infoWindow: infoWindow, + ); controller.showInfoWindow(); @@ -125,11 +134,13 @@ void main() { }); testWidgets('hideInfoWindow', (WidgetTester tester) async { - final infoWindow = gmaps.InfoWindow(); - final map = gmaps.GMap(html.DivElement()); + final gmaps.InfoWindow infoWindow = gmaps.InfoWindow(); + final gmaps.GMap map = gmaps.GMap(html.DivElement()); marker.set('map', map); - final controller = - MarkerController(marker: marker, infoWindow: infoWindow); + final MarkerController controller = MarkerController( + marker: marker, + infoWindow: infoWindow, + ); controller.hideInfoWindow(); @@ -141,8 +152,8 @@ void main() { late MarkerController controller; setUp(() { - final infoWindow = gmaps.InfoWindow(); - final map = gmaps.GMap(html.DivElement()); + final gmaps.InfoWindow infoWindow = gmaps.InfoWindow(); + final gmaps.GMap map = gmaps.GMap(html.DivElement()); marker.set('map', map); controller = MarkerController(marker: marker, infoWindow: infoWindow); }); @@ -155,7 +166,8 @@ void main() { testWidgets('cannot call update after remove', (WidgetTester tester) async { - final options = gmaps.MarkerOptions()..draggable = true; + final gmaps.MarkerOptions options = gmaps.MarkerOptions() + ..draggable = true; controller.remove(); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/markers_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/markers_test.dart index 6f2bf610f77d..90195ec6397b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/markers_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/markers_test.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:html' as html; import 'dart:js_util' show getProperty; +import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps/google_maps.dart' as gmaps; @@ -20,54 +21,56 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); group('MarkersController', () { - late StreamController events; + late StreamController> events; late MarkersController controller; late gmaps.GMap map; setUp(() { - events = StreamController(); + events = StreamController>(); controller = MarkersController(stream: events); map = gmaps.GMap(html.DivElement()); controller.bindToMap(123, map); }); testWidgets('addMarkers', (WidgetTester tester) async { - final markers = { - Marker(markerId: MarkerId('1')), - Marker(markerId: MarkerId('2')), + final Set markers = { + const Marker(markerId: MarkerId('1')), + const Marker(markerId: MarkerId('2')), }; controller.addMarkers(markers); expect(controller.markers.length, 2); - expect(controller.markers, contains(MarkerId('1'))); - expect(controller.markers, contains(MarkerId('2'))); - expect(controller.markers, isNot(contains(MarkerId('66')))); + expect(controller.markers, contains(const MarkerId('1'))); + expect(controller.markers, contains(const MarkerId('2'))); + expect(controller.markers, isNot(contains(const MarkerId('66')))); }); testWidgets('changeMarkers', (WidgetTester tester) async { - final markers = { - Marker(markerId: MarkerId('1')), + final Set markers = { + const Marker(markerId: MarkerId('1')), }; controller.addMarkers(markers); - expect(controller.markers[MarkerId('1')]?.marker?.draggable, isFalse); + expect( + controller.markers[const MarkerId('1')]?.marker?.draggable, isFalse); // Update the marker with radius 10 - final updatedMarkers = { - Marker(markerId: MarkerId('1'), draggable: true), + final Set updatedMarkers = { + const Marker(markerId: MarkerId('1'), draggable: true), }; controller.changeMarkers(updatedMarkers); expect(controller.markers.length, 1); - expect(controller.markers[MarkerId('1')]?.marker?.draggable, isTrue); + expect( + controller.markers[const MarkerId('1')]?.marker?.draggable, isTrue); }); testWidgets('removeMarkers', (WidgetTester tester) async { - final markers = { - Marker(markerId: MarkerId('1')), - Marker(markerId: MarkerId('2')), - Marker(markerId: MarkerId('3')), + final Set markers = { + const Marker(markerId: MarkerId('1')), + const Marker(markerId: MarkerId('2')), + const Marker(markerId: MarkerId('3')), }; controller.addMarkers(markers); @@ -75,91 +78,93 @@ void main() { expect(controller.markers.length, 3); // Remove some markers... - final markerIdsToRemove = { - MarkerId('1'), - MarkerId('3'), + final Set markerIdsToRemove = { + const MarkerId('1'), + const MarkerId('3'), }; controller.removeMarkers(markerIdsToRemove); expect(controller.markers.length, 1); - expect(controller.markers, isNot(contains(MarkerId('1')))); - expect(controller.markers, contains(MarkerId('2'))); - expect(controller.markers, isNot(contains(MarkerId('3')))); + expect(controller.markers, isNot(contains(const MarkerId('1')))); + expect(controller.markers, contains(const MarkerId('2'))); + expect(controller.markers, isNot(contains(const MarkerId('3')))); }); testWidgets('InfoWindow show/hide', (WidgetTester tester) async { - final markers = { - Marker( + final Set markers = { + const Marker( markerId: MarkerId('1'), - infoWindow: InfoWindow(title: "Title", snippet: "Snippet"), + infoWindow: InfoWindow(title: 'Title', snippet: 'Snippet'), ), }; controller.addMarkers(markers); - expect(controller.markers[MarkerId('1')]?.infoWindowShown, isFalse); + expect(controller.markers[const MarkerId('1')]?.infoWindowShown, isFalse); - controller.showMarkerInfoWindow(MarkerId('1')); + controller.showMarkerInfoWindow(const MarkerId('1')); - expect(controller.markers[MarkerId('1')]?.infoWindowShown, isTrue); + expect(controller.markers[const MarkerId('1')]?.infoWindowShown, isTrue); - controller.hideMarkerInfoWindow(MarkerId('1')); + controller.hideMarkerInfoWindow(const MarkerId('1')); - expect(controller.markers[MarkerId('1')]?.infoWindowShown, isFalse); + expect(controller.markers[const MarkerId('1')]?.infoWindowShown, isFalse); }); // https://github.com/flutter/flutter/issues/67380 testWidgets('only single InfoWindow is visible', (WidgetTester tester) async { - final markers = { - Marker( + final Set markers = { + const Marker( markerId: MarkerId('1'), - infoWindow: InfoWindow(title: "Title", snippet: "Snippet"), + infoWindow: InfoWindow(title: 'Title', snippet: 'Snippet'), ), - Marker( + const Marker( markerId: MarkerId('2'), - infoWindow: InfoWindow(title: "Title", snippet: "Snippet"), + infoWindow: InfoWindow(title: 'Title', snippet: 'Snippet'), ), }; controller.addMarkers(markers); - expect(controller.markers[MarkerId('1')]?.infoWindowShown, isFalse); - expect(controller.markers[MarkerId('2')]?.infoWindowShown, isFalse); + expect(controller.markers[const MarkerId('1')]?.infoWindowShown, isFalse); + expect(controller.markers[const MarkerId('2')]?.infoWindowShown, isFalse); - controller.showMarkerInfoWindow(MarkerId('1')); + controller.showMarkerInfoWindow(const MarkerId('1')); - expect(controller.markers[MarkerId('1')]?.infoWindowShown, isTrue); - expect(controller.markers[MarkerId('2')]?.infoWindowShown, isFalse); + expect(controller.markers[const MarkerId('1')]?.infoWindowShown, isTrue); + expect(controller.markers[const MarkerId('2')]?.infoWindowShown, isFalse); - controller.showMarkerInfoWindow(MarkerId('2')); + controller.showMarkerInfoWindow(const MarkerId('2')); - expect(controller.markers[MarkerId('1')]?.infoWindowShown, isFalse); - expect(controller.markers[MarkerId('2')]?.infoWindowShown, isTrue); + expect(controller.markers[const MarkerId('1')]?.infoWindowShown, isFalse); + expect(controller.markers[const MarkerId('2')]?.infoWindowShown, isTrue); }); // https://github.com/flutter/flutter/issues/66622 testWidgets('markers with custom bitmap icon work', (WidgetTester tester) async { - final bytes = Base64Decoder().convert(iconImageBase64); - final markers = { + final Uint8List bytes = const Base64Decoder().convert(iconImageBase64); + final Set markers = { Marker( - markerId: MarkerId('1'), icon: BitmapDescriptor.fromBytes(bytes)), + markerId: const MarkerId('1'), + icon: BitmapDescriptor.fromBytes(bytes), + ), }; controller.addMarkers(markers); expect(controller.markers.length, 1); - expect(controller.markers[MarkerId('1')]?.marker?.icon, isNotNull); + expect(controller.markers[const MarkerId('1')]?.marker?.icon, isNotNull); - final blobUrl = getProperty( - controller.markers[MarkerId('1')]!.marker!.icon!, + final String blobUrl = getProperty( + controller.markers[const MarkerId('1')]!.marker!.icon!, 'url', ); expect(blobUrl, startsWith('blob:')); - final response = await http.get(Uri.parse(blobUrl)); + final http.Response response = await http.get(Uri.parse(blobUrl)); expect(response.bodyBytes, bytes, reason: @@ -169,8 +174,8 @@ void main() { // https://github.com/flutter/flutter/issues/67854 testWidgets('InfoWindow snippet can have links', (WidgetTester tester) async { - final markers = { - Marker( + final Set markers = { + const Marker( markerId: MarkerId('1'), infoWindow: InfoWindow( title: 'title for test', @@ -182,19 +187,20 @@ void main() { controller.addMarkers(markers); expect(controller.markers.length, 1); - final content = controller.markers[MarkerId('1')]?.infoWindow?.content - as html.HtmlElement; - expect(content.innerHtml, contains('title for test')); + final html.HtmlElement? content = controller.markers[const MarkerId('1')] + ?.infoWindow?.content as html.HtmlElement?; + expect(content?.innerHtml, contains('title for test')); expect( - content.innerHtml, + content?.innerHtml, contains( - 'Go to Google >>>')); + 'Go to Google >>>', + )); }); // https://github.com/flutter/flutter/issues/67289 testWidgets('InfoWindow content is clickable', (WidgetTester tester) async { - final markers = { - Marker( + final Set markers = { + const Marker( markerId: MarkerId('1'), infoWindow: InfoWindow( title: 'title for test', @@ -206,15 +212,15 @@ void main() { controller.addMarkers(markers); expect(controller.markers.length, 1); - final content = controller.markers[MarkerId('1')]?.infoWindow?.content - as html.HtmlElement; + final html.HtmlElement? content = controller.markers[const MarkerId('1')] + ?.infoWindow?.content as html.HtmlElement?; - content.click(); + content?.click(); - final event = await events.stream.first; + final MapEvent event = await events.stream.first; expect(event, isA()); - expect((event as InfoWindowTapEvent).value, equals(MarkerId('1'))); + expect((event as InfoWindowTapEvent).value, equals(const MarkerId('1'))); }); }); } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/projection_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/projection_test.dart index 1bf0f10f50c8..14e4156b87ec 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/projection_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/projection_test.dart @@ -17,20 +17,20 @@ import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platf import 'package:integration_test/integration_test.dart'; // This value is used when comparing long~num, like LatLng values. -const _acceptableLatLngDelta = 0.0000000001; +const double _acceptableLatLngDelta = 0.0000000001; // This value is used when comparing pixel measurements, mostly to gloss over // browser rounding errors. -const _acceptablePixelDelta = 1; +const int _acceptablePixelDelta = 1; /// Test Google Map Controller void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); group('Methods that require a proper Projection', () { - final LatLng center = LatLng(43.3078, -5.6958); - final Size size = Size(320, 240); - final CameraPosition initialCamera = CameraPosition( + const LatLng center = LatLng(43.3078, -5.6958); + const Size size = Size(320, 240); + const CameraPosition initialCamera = CameraPosition( target: center, zoom: 14, ); @@ -48,7 +48,7 @@ void main() { group('getScreenCoordinate', () { testWidgets('target of map is in center of widget', (WidgetTester tester) async { - pumpCenteredMap( + await pumpCenteredMap( tester, initialCamera: initialCamera, size: size, @@ -72,7 +72,7 @@ void main() { testWidgets('NorthWest of visible region corresponds to x:0, y:0', (WidgetTester tester) async { - pumpCenteredMap( + await pumpCenteredMap( tester, initialCamera: initialCamera, size: size, @@ -96,7 +96,7 @@ void main() { testWidgets( 'SouthEast of visible region corresponds to x:size.width, y:size.height', (WidgetTester tester) async { - pumpCenteredMap( + await pumpCenteredMap( tester, initialCamera: initialCamera, size: size, @@ -121,7 +121,7 @@ void main() { group('getLatLng', () { testWidgets('Center of widget is the target of map', (WidgetTester tester) async { - pumpCenteredMap( + await pumpCenteredMap( tester, initialCamera: initialCamera, size: size, @@ -146,7 +146,7 @@ void main() { testWidgets('Top-left of widget is NorthWest bound of map', (WidgetTester tester) async { - pumpCenteredMap( + await pumpCenteredMap( tester, initialCamera: initialCamera, size: size, @@ -161,7 +161,7 @@ void main() { ); final LatLng coords = await controller.getLatLng( - ScreenCoordinate(x: 0, y: 0), + const ScreenCoordinate(x: 0, y: 0), ); expect( @@ -176,7 +176,7 @@ void main() { testWidgets('Bottom-right of widget is SouthWest bound of map', (WidgetTester tester) async { - pumpCenteredMap( + await pumpCenteredMap( tester, initialCamera: initialCamera, size: size, @@ -208,7 +208,7 @@ void main() { } // Pumps a CenteredMap Widget into a given tester, with some parameters -void pumpCenteredMap( +Future pumpCenteredMap( WidgetTester tester, { required CameraPosition initialCamera, Size size = const Size(320, 240), diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/resources/icon_image_base64.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/resources/icon_image_base64.dart index 6010f0107031..d08e96a65333 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/resources/icon_image_base64.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/resources/icon_image_base64.dart @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -final iconImageBase64 = +const String iconImageBase64 = 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAIRlWElmTU' '0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIA' 'AIdpAAQAAAABAAAAWgAAAAAAAABIAAAAAQAAAEgAAAABAAOgAQADAAAAAQABAACgAgAEAAAAAQ' diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/shape_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/shape_test.dart index 547aaec6dc0a..d1426760ceae 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/shape_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/shape_test.dart @@ -4,10 +4,10 @@ import 'dart:async'; -import 'package:integration_test/integration_test.dart'; +import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps/google_maps.dart' as gmaps; import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; -import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; /// Test Shapes (Circle, Polygon, Polyline) void main() { @@ -27,7 +27,7 @@ void main() { } setUp(() { - _methodCalledCompleter = Completer(); + _methodCalledCompleter = Completer(); methodCalled = _methodCalledCompleter.future; }); @@ -42,15 +42,16 @@ void main() { CircleController(circle: circle, consumeTapEvents: true, onTap: onTap); // Trigger a click event... - gmaps.Event.trigger(circle, 'click', [gmaps.MapMouseEvent()]); + gmaps.Event.trigger(circle, 'click', [gmaps.MapMouseEvent()]); // The event handling is now truly async. Wait for it... expect(await methodCalled, isTrue); }); testWidgets('update', (WidgetTester tester) async { - final controller = CircleController(circle: circle); - final options = gmaps.CircleOptions()..draggable = true; + final CircleController controller = CircleController(circle: circle); + final gmaps.CircleOptions options = gmaps.CircleOptions() + ..draggable = true; expect(circle.draggable, isNull); @@ -74,7 +75,8 @@ void main() { testWidgets('cannot call update after remove', (WidgetTester tester) async { - final options = gmaps.CircleOptions()..draggable = true; + final gmaps.CircleOptions options = gmaps.CircleOptions() + ..draggable = true; controller.remove(); @@ -96,15 +98,16 @@ void main() { PolygonController(polygon: polygon, consumeTapEvents: true, onTap: onTap); // Trigger a click event... - gmaps.Event.trigger(polygon, 'click', [gmaps.MapMouseEvent()]); + gmaps.Event.trigger(polygon, 'click', [gmaps.MapMouseEvent()]); // The event handling is now truly async. Wait for it... expect(await methodCalled, isTrue); }); testWidgets('update', (WidgetTester tester) async { - final controller = PolygonController(polygon: polygon); - final options = gmaps.PolygonOptions()..draggable = true; + final PolygonController controller = PolygonController(polygon: polygon); + final gmaps.PolygonOptions options = gmaps.PolygonOptions() + ..draggable = true; expect(polygon.draggable, isNull); @@ -128,7 +131,8 @@ void main() { testWidgets('cannot call update after remove', (WidgetTester tester) async { - final options = gmaps.PolygonOptions()..draggable = true; + final gmaps.PolygonOptions options = gmaps.PolygonOptions() + ..draggable = true; controller.remove(); @@ -148,18 +152,24 @@ void main() { testWidgets('onTap gets called', (WidgetTester tester) async { PolylineController( - polyline: polyline, consumeTapEvents: true, onTap: onTap); + polyline: polyline, + consumeTapEvents: true, + onTap: onTap, + ); // Trigger a click event... - gmaps.Event.trigger(polyline, 'click', [gmaps.MapMouseEvent()]); + gmaps.Event.trigger(polyline, 'click', [gmaps.MapMouseEvent()]); // The event handling is now truly async. Wait for it... expect(await methodCalled, isTrue); }); testWidgets('update', (WidgetTester tester) async { - final controller = PolylineController(polyline: polyline); - final options = gmaps.PolylineOptions()..draggable = true; + final PolylineController controller = PolylineController( + polyline: polyline, + ); + final gmaps.PolylineOptions options = gmaps.PolylineOptions() + ..draggable = true; expect(polyline.draggable, isNull); @@ -183,7 +193,8 @@ void main() { testWidgets('cannot call update after remove', (WidgetTester tester) async { - final options = gmaps.PolylineOptions()..draggable = true; + final gmaps.PolylineOptions options = gmaps.PolylineOptions() + ..draggable = true; controller.remove(); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/shapes_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/shapes_test.dart index 80b4e0823bb5..b9bc2d371c9b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/shapes_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/shapes_test.dart @@ -3,20 +3,20 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:ui'; import 'dart:html' as html; +import 'dart:ui'; -import 'package:integration_test/integration_test.dart'; -import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; -import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; +import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps/google_maps.dart' as gmaps; import 'package:google_maps/google_maps_geometry.dart' as geometry; -import 'package:flutter_test/flutter_test.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; +import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; +import 'package:integration_test/integration_test.dart'; // This value is used when comparing the results of // converting from a byte value to a double between 0 and 1. // (For Color opacity values, for example) -const _acceptableDelta = 0.01; +const double _acceptableDelta = 0.01; /// Test Shapes (Circle, Polygon, Polyline) void main() { @@ -29,51 +29,51 @@ void main() { }); group('CirclesController', () { - late StreamController events; + late StreamController> events; late CirclesController controller; setUp(() { - events = StreamController(); + events = StreamController>(); controller = CirclesController(stream: events); controller.bindToMap(123, map); }); testWidgets('addCircles', (WidgetTester tester) async { - final circles = { - Circle(circleId: CircleId('1')), - Circle(circleId: CircleId('2')), + final Set circles = { + const Circle(circleId: CircleId('1')), + const Circle(circleId: CircleId('2')), }; controller.addCircles(circles); expect(controller.circles.length, 2); - expect(controller.circles, contains(CircleId('1'))); - expect(controller.circles, contains(CircleId('2'))); - expect(controller.circles, isNot(contains(CircleId('66')))); + expect(controller.circles, contains(const CircleId('1'))); + expect(controller.circles, contains(const CircleId('2'))); + expect(controller.circles, isNot(contains(const CircleId('66')))); }); testWidgets('changeCircles', (WidgetTester tester) async { - final circles = { - Circle(circleId: CircleId('1')), + final Set circles = { + const Circle(circleId: CircleId('1')), }; controller.addCircles(circles); - expect(controller.circles[CircleId('1')]?.circle?.visible, isTrue); + expect(controller.circles[const CircleId('1')]?.circle?.visible, isTrue); - final updatedCircles = { - Circle(circleId: CircleId('1'), visible: false), + final Set updatedCircles = { + const Circle(circleId: CircleId('1'), visible: false), }; controller.changeCircles(updatedCircles); expect(controller.circles.length, 1); - expect(controller.circles[CircleId('1')]?.circle?.visible, isFalse); + expect(controller.circles[const CircleId('1')]?.circle?.visible, isFalse); }); testWidgets('removeCircles', (WidgetTester tester) async { - final circles = { - Circle(circleId: CircleId('1')), - Circle(circleId: CircleId('2')), - Circle(circleId: CircleId('3')), + final Set circles = { + const Circle(circleId: CircleId('1')), + const Circle(circleId: CircleId('2')), + const Circle(circleId: CircleId('3')), }; controller.addCircles(circles); @@ -81,22 +81,22 @@ void main() { expect(controller.circles.length, 3); // Remove some circles... - final circleIdsToRemove = { - CircleId('1'), - CircleId('3'), + final Set circleIdsToRemove = { + const CircleId('1'), + const CircleId('3'), }; controller.removeCircles(circleIdsToRemove); expect(controller.circles.length, 1); - expect(controller.circles, isNot(contains(CircleId('1')))); - expect(controller.circles, contains(CircleId('2'))); - expect(controller.circles, isNot(contains(CircleId('3')))); + expect(controller.circles, isNot(contains(const CircleId('1')))); + expect(controller.circles, contains(const CircleId('2'))); + expect(controller.circles, isNot(contains(const CircleId('3')))); }); testWidgets('Converts colors to CSS', (WidgetTester tester) async { - final circles = { - Circle( + final Set circles = { + const Circle( circleId: CircleId('1'), fillColor: Color(0x7FFABADA), strokeColor: Color(0xFFC0FFEE), @@ -105,7 +105,7 @@ void main() { controller.addCircles(circles); - final circle = controller.circles.values.first.circle!; + final gmaps.Circle circle = controller.circles.values.first.circle!; expect(circle.get('fillColor'), '#fabada'); expect(circle.get('fillOpacity'), closeTo(0.5, _acceptableDelta)); @@ -115,52 +115,54 @@ void main() { }); group('PolygonsController', () { - late StreamController events; + late StreamController> events; late PolygonsController controller; setUp(() { - events = StreamController(); + events = StreamController>(); controller = PolygonsController(stream: events); controller.bindToMap(123, map); }); testWidgets('addPolygons', (WidgetTester tester) async { - final polygons = { - Polygon(polygonId: PolygonId('1')), - Polygon(polygonId: PolygonId('2')), + final Set polygons = { + const Polygon(polygonId: PolygonId('1')), + const Polygon(polygonId: PolygonId('2')), }; controller.addPolygons(polygons); expect(controller.polygons.length, 2); - expect(controller.polygons, contains(PolygonId('1'))); - expect(controller.polygons, contains(PolygonId('2'))); - expect(controller.polygons, isNot(contains(PolygonId('66')))); + expect(controller.polygons, contains(const PolygonId('1'))); + expect(controller.polygons, contains(const PolygonId('2'))); + expect(controller.polygons, isNot(contains(const PolygonId('66')))); }); testWidgets('changePolygons', (WidgetTester tester) async { - final polygons = { - Polygon(polygonId: PolygonId('1')), + final Set polygons = { + const Polygon(polygonId: PolygonId('1')), }; controller.addPolygons(polygons); - expect(controller.polygons[PolygonId('1')]?.polygon?.visible, isTrue); + expect( + controller.polygons[const PolygonId('1')]?.polygon?.visible, isTrue); // Update the polygon - final updatedPolygons = { - Polygon(polygonId: PolygonId('1'), visible: false), + final Set updatedPolygons = { + const Polygon(polygonId: PolygonId('1'), visible: false), }; controller.changePolygons(updatedPolygons); expect(controller.polygons.length, 1); - expect(controller.polygons[PolygonId('1')]?.polygon?.visible, isFalse); + expect( + controller.polygons[const PolygonId('1')]?.polygon?.visible, isFalse); }); testWidgets('removePolygons', (WidgetTester tester) async { - final polygons = { - Polygon(polygonId: PolygonId('1')), - Polygon(polygonId: PolygonId('2')), - Polygon(polygonId: PolygonId('3')), + final Set polygons = { + const Polygon(polygonId: PolygonId('1')), + const Polygon(polygonId: PolygonId('2')), + const Polygon(polygonId: PolygonId('3')), }; controller.addPolygons(polygons); @@ -168,22 +170,22 @@ void main() { expect(controller.polygons.length, 3); // Remove some polygons... - final polygonIdsToRemove = { - PolygonId('1'), - PolygonId('3'), + final Set polygonIdsToRemove = { + const PolygonId('1'), + const PolygonId('3'), }; controller.removePolygons(polygonIdsToRemove); expect(controller.polygons.length, 1); - expect(controller.polygons, isNot(contains(PolygonId('1')))); - expect(controller.polygons, contains(PolygonId('2'))); - expect(controller.polygons, isNot(contains(PolygonId('3')))); + expect(controller.polygons, isNot(contains(const PolygonId('1')))); + expect(controller.polygons, contains(const PolygonId('2'))); + expect(controller.polygons, isNot(contains(const PolygonId('3')))); }); testWidgets('Converts colors to CSS', (WidgetTester tester) async { - final polygons = { - Polygon( + final Set polygons = { + const Polygon( polygonId: PolygonId('1'), fillColor: Color(0x7FFABADA), strokeColor: Color(0xFFC0FFEE), @@ -192,7 +194,7 @@ void main() { controller.addPolygons(polygons); - final polygon = controller.polygons.values.first.polygon!; + final gmaps.Polygon polygon = controller.polygons.values.first.polygon!; expect(polygon.get('fillColor'), '#fabada'); expect(polygon.get('fillOpacity'), closeTo(0.5, _acceptableDelta)); @@ -201,16 +203,16 @@ void main() { }); testWidgets('Handle Polygons with holes', (WidgetTester tester) async { - final polygons = { - Polygon( + final Set polygons = { + const Polygon( polygonId: PolygonId('BermudaTriangle'), - points: [ + points: [ LatLng(25.774, -80.19), LatLng(18.466, -66.118), LatLng(32.321, -64.757), ], - holes: [ - [ + holes: >[ + [ LatLng(28.745, -70.579), LatLng(29.57, -67.514), LatLng(27.339, -66.668), @@ -222,21 +224,21 @@ void main() { controller.addPolygons(polygons); expect(controller.polygons.length, 1); - expect(controller.polygons, contains(PolygonId('BermudaTriangle'))); - expect(controller.polygons, isNot(contains(PolygonId('66')))); + expect(controller.polygons, contains(const PolygonId('BermudaTriangle'))); + expect(controller.polygons, isNot(contains(const PolygonId('66')))); }); testWidgets('Polygon with hole has a hole', (WidgetTester tester) async { - final polygons = { - Polygon( + final Set polygons = { + const Polygon( polygonId: PolygonId('BermudaTriangle'), - points: [ + points: [ LatLng(25.774, -80.19), LatLng(18.466, -66.118), LatLng(32.321, -64.757), ], - holes: [ - [ + holes: >[ + [ LatLng(28.745, -70.579), LatLng(29.57, -67.514), LatLng(27.339, -66.668), @@ -247,24 +249,24 @@ void main() { controller.addPolygons(polygons); - final polygon = controller.polygons.values.first.polygon; - final pointInHole = gmaps.LatLng(28.632, -68.401); + final gmaps.Polygon? polygon = controller.polygons.values.first.polygon; + final gmaps.LatLng pointInHole = gmaps.LatLng(28.632, -68.401); expect(geometry.Poly.containsLocation(pointInHole, polygon), false); }); testWidgets('Hole Path gets reversed to display correctly', (WidgetTester tester) async { - final polygons = { - Polygon( + final Set polygons = { + const Polygon( polygonId: PolygonId('BermudaTriangle'), - points: [ + points: [ LatLng(25.774, -80.19), LatLng(18.466, -66.118), LatLng(32.321, -64.757), ], - holes: [ - [ + holes: >[ + [ LatLng(27.339, -66.668), LatLng(29.57, -67.514), LatLng(28.745, -70.579), @@ -275,7 +277,8 @@ void main() { controller.addPolygons(polygons); - final paths = controller.polygons.values.first.polygon!.paths!; + final gmaps.MVCArray?> paths = + controller.polygons.values.first.polygon!.paths!; expect(paths.getAt(1)?.getAt(0)?.lat, 28.745); expect(paths.getAt(1)?.getAt(1)?.lat, 29.57); @@ -284,51 +287,51 @@ void main() { }); group('PolylinesController', () { - late StreamController events; + late StreamController> events; late PolylinesController controller; setUp(() { - events = StreamController(); + events = StreamController>(); controller = PolylinesController(stream: events); controller.bindToMap(123, map); }); testWidgets('addPolylines', (WidgetTester tester) async { - final polylines = { - Polyline(polylineId: PolylineId('1')), - Polyline(polylineId: PolylineId('2')), + final Set polylines = { + const Polyline(polylineId: PolylineId('1')), + const Polyline(polylineId: PolylineId('2')), }; controller.addPolylines(polylines); expect(controller.lines.length, 2); - expect(controller.lines, contains(PolylineId('1'))); - expect(controller.lines, contains(PolylineId('2'))); - expect(controller.lines, isNot(contains(PolylineId('66')))); + expect(controller.lines, contains(const PolylineId('1'))); + expect(controller.lines, contains(const PolylineId('2'))); + expect(controller.lines, isNot(contains(const PolylineId('66')))); }); testWidgets('changePolylines', (WidgetTester tester) async { - final polylines = { - Polyline(polylineId: PolylineId('1')), + final Set polylines = { + const Polyline(polylineId: PolylineId('1')), }; controller.addPolylines(polylines); - expect(controller.lines[PolylineId('1')]?.line?.visible, isTrue); + expect(controller.lines[const PolylineId('1')]?.line?.visible, isTrue); - final updatedPolylines = { - Polyline(polylineId: PolylineId('1'), visible: false), + final Set updatedPolylines = { + const Polyline(polylineId: PolylineId('1'), visible: false), }; controller.changePolylines(updatedPolylines); expect(controller.lines.length, 1); - expect(controller.lines[PolylineId('1')]?.line?.visible, isFalse); + expect(controller.lines[const PolylineId('1')]?.line?.visible, isFalse); }); testWidgets('removePolylines', (WidgetTester tester) async { - final polylines = { - Polyline(polylineId: PolylineId('1')), - Polyline(polylineId: PolylineId('2')), - Polyline(polylineId: PolylineId('3')), + final Set polylines = { + const Polyline(polylineId: PolylineId('1')), + const Polyline(polylineId: PolylineId('2')), + const Polyline(polylineId: PolylineId('3')), }; controller.addPolylines(polylines); @@ -336,22 +339,22 @@ void main() { expect(controller.lines.length, 3); // Remove some polylines... - final polylineIdsToRemove = { - PolylineId('1'), - PolylineId('3'), + final Set polylineIdsToRemove = { + const PolylineId('1'), + const PolylineId('3'), }; controller.removePolylines(polylineIdsToRemove); expect(controller.lines.length, 1); - expect(controller.lines, isNot(contains(PolylineId('1')))); - expect(controller.lines, contains(PolylineId('2'))); - expect(controller.lines, isNot(contains(PolylineId('3')))); + expect(controller.lines, isNot(contains(const PolylineId('1')))); + expect(controller.lines, contains(const PolylineId('2'))); + expect(controller.lines, isNot(contains(const PolylineId('3')))); }); testWidgets('Converts colors to CSS', (WidgetTester tester) async { - final lines = { - Polyline( + final Set lines = { + const Polyline( polylineId: PolylineId('1'), color: Color(0x7FFABADA), ), @@ -359,7 +362,7 @@ void main() { controller.addPolylines(lines); - final line = controller.lines.values.first.line!; + final gmaps.Polyline line = controller.lines.values.first.line!; expect(line.get('strokeColor'), '#fabada'); expect(line.get('strokeOpacity'), closeTo(0.5, _acceptableDelta)); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/lib/main.dart index d1ba571b5bd0..e93a60e19906 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/lib/main.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/lib/main.dart @@ -5,11 +5,14 @@ import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// App for testing class MyApp extends StatefulWidget { + /// Constructor with key + const MyApp({Key? key}) : super(key: key); + @override State createState() => _MyAppState(); } @@ -17,6 +20,6 @@ class MyApp extends StatefulWidget { class _MyAppState extends State { @override Widget build(BuildContext context) { - return Text('Testing... Look at the console output for results!'); + return const Text('Testing... Look at the console output for results!'); } } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/example/pubspec.yaml index a962e5b864c6..fb6359fe5b8f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/pubspec.yaml @@ -7,21 +7,21 @@ environment: flutter: ">=2.8.0" dependencies: - google_maps_flutter_web: - path: ../ flutter: sdk: flutter + google_maps_flutter_web: + path: ../ dev_dependencies: build_runner: ^2.1.1 - google_maps: ^5.2.0 - google_maps_flutter: # Used for projection_test.dart - path: ../../google_maps_flutter - http: ^0.13.0 - mockito: ^5.0.0 flutter_driver: sdk: flutter flutter_test: sdk: flutter + google_maps: ^6.1.0 + google_maps_flutter: # Used for projection_test.dart + path: ../../google_maps_flutter + http: ^0.13.0 integration_test: sdk: flutter + mockito: ^5.0.0 diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart index c3079dc2492d..7ae646687f19 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart @@ -5,36 +5,33 @@ library google_maps_flutter_web; import 'dart:async'; +import 'dart:convert'; import 'dart:html'; import 'dart:js_util'; -import 'src/shims/dart_ui.dart' as ui; // Conditionally imports dart:ui in web -import 'dart:convert'; -import 'package:flutter/widgets.dart'; -import 'package:flutter/material.dart'; import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; import 'package:flutter/gestures.dart'; - -import 'package:sanitize_html/sanitize_html.dart'; - -import 'package:stream_transform/stream_transform.dart'; - -import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:google_maps/google_maps.dart' as gmaps; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; +import 'package:sanitize_html/sanitize_html.dart'; +import 'package:stream_transform/stream_transform.dart'; +import 'src/shims/dart_ui.dart' as ui; // Conditionally imports dart:ui in web import 'src/third_party/to_screen_location/to_screen_location.dart'; import 'src/types.dart'; -part 'src/google_maps_flutter_web.dart'; -part 'src/google_maps_controller.dart'; part 'src/circle.dart'; part 'src/circles.dart'; +part 'src/convert.dart'; +part 'src/google_maps_controller.dart'; +part 'src/google_maps_flutter_web.dart'; +part 'src/marker.dart'; +part 'src/markers.dart'; part 'src/polygon.dart'; part 'src/polygons.dart'; part 'src/polyline.dart'; part 'src/polylines.dart'; -part 'src/marker.dart'; -part 'src/markers.dart'; -part 'src/convert.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart index 65057d8c869e..9cd3ba1c079c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart @@ -6,10 +6,6 @@ part of google_maps_flutter_web; /// The `CircleController` class wraps a [gmaps.Circle] and its `onTap` behavior. class CircleController { - gmaps.Circle? _circle; - - final bool _consumeTapEvents; - /// Creates a `CircleController`, which wraps a [gmaps.Circle] object and its `onTap` behavior. CircleController({ required gmaps.Circle circle, @@ -24,6 +20,10 @@ class CircleController { } } + gmaps.Circle? _circle; + + final bool _consumeTapEvents; + /// Returns the wrapped [gmaps.Circle]. Only used for testing. @visibleForTesting gmaps.Circle? get circle => _circle; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart index ae8faa038ea6..bc6eac14200f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart @@ -6,17 +6,17 @@ part of google_maps_flutter_web; /// This class manages all the [CircleController]s associated to a [GoogleMapController]. class CirclesController extends GeometryController { + /// Initialize the cache. The [StreamController] comes from the [GoogleMapController], and is shared with other controllers. + CirclesController({ + required StreamController> stream, + }) : _streamController = stream, + _circleIdToController = {}; + // A cache of [CircleController]s indexed by their [CircleId]. final Map _circleIdToController; // The stream over which circles broadcast their events - StreamController _streamController; - - /// Initialize the cache. The [StreamController] comes from the [GoogleMapController], and is shared with other controllers. - CirclesController({ - required StreamController stream, - }) : _streamController = stream, - _circleIdToController = Map(); + final StreamController> _streamController; /// Returns the cache of [CircleController]s. Test only. @visibleForTesting @@ -26,9 +26,7 @@ class CirclesController extends GeometryController { /// /// Wraps each [Circle] into its corresponding [CircleController]. void addCircles(Set circlesToAdd) { - circlesToAdd.forEach((circle) { - _addCircle(circle); - }); + circlesToAdd.forEach(_addCircle); } void _addCircle(Circle circle) { @@ -36,10 +34,9 @@ class CirclesController extends GeometryController { return; } - final populationOptions = _circleOptionsFromCircle(circle); - gmaps.Circle gmCircle = gmaps.Circle(populationOptions); - gmCircle.map = googleMap; - CircleController controller = CircleController( + final gmaps.CircleOptions circleOptions = _circleOptionsFromCircle(circle); + final gmaps.Circle gmCircle = gmaps.Circle(circleOptions)..map = googleMap; + final CircleController controller = CircleController( circle: gmCircle, consumeTapEvents: circle.consumeTapEvents, onTap: () { @@ -50,24 +47,25 @@ class CirclesController extends GeometryController { /// Updates a set of [Circle] objects with new options. void changeCircles(Set circlesToChange) { - circlesToChange.forEach((circleToChange) { - _changeCircle(circleToChange); - }); + circlesToChange.forEach(_changeCircle); } void _changeCircle(Circle circle) { - final circleController = _circleIdToController[circle.circleId]; + final CircleController? circleController = + _circleIdToController[circle.circleId]; circleController?.update(_circleOptionsFromCircle(circle)); } /// Removes a set of [CircleId]s from the cache. void removeCircles(Set circleIdsToRemove) { - circleIdsToRemove.forEach((circleId) { - final CircleController? circleController = - _circleIdToController[circleId]; - circleController?.remove(); - _circleIdToController.remove(circleId); - }); + circleIdsToRemove.forEach(_removeCircle); + } + + // Removes a circle and its controller by its [CircleId]. + void _removeCircle(CircleId circleId) { + final CircleController? circleController = _circleIdToController[circleId]; + circleController?.remove(); + _circleIdToController.remove(circleId); } // Handles the global onCircleTap function to funnel events from circles into the stream. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart index c026a03be804..c6f3164ff207 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart @@ -5,17 +5,17 @@ part of google_maps_flutter_web; // Default values for when the gmaps objects return null/undefined values. -final _nullGmapsLatLng = gmaps.LatLng(0, 0); -final _nullGmapsLatLngBounds = +final gmaps.LatLng _nullGmapsLatLng = gmaps.LatLng(0, 0); +final gmaps.LatLngBounds _nullGmapsLatLngBounds = gmaps.LatLngBounds(_nullGmapsLatLng, _nullGmapsLatLng); // Defaults taken from the Google Maps Platform SDK documentation. -final _defaultCssColor = '#000000'; -final _defaultCssOpacity = 0.0; +const String _defaultCssColor = '#000000'; +const double _defaultCssOpacity = 0.0; // Indices in the plugin side don't match with the ones // in the gmaps lib. This translates from plugin -> gmaps. -final _mapTypeToMapTypeId = { +final Map _mapTypeToMapTypeId = { 0: gmaps.MapTypeId.ROADMAP, // "none" in the plugin 1: gmaps.MapTypeId.ROADMAP, 2: gmaps.MapTypeId.SATELLITE, @@ -28,7 +28,7 @@ String _getCssColor(Color color) { if (color == null) { return _defaultCssColor; } - return '#' + color.value.toRadixString(16).padLeft(8, '0').substring(2); + return '#${color.value.toRadixString(16).padLeft(8, '0').substring(2)}'; } // Extracts the opacity from a [Color]. @@ -55,17 +55,19 @@ double _getCssOpacity(Color color) { // indoorViewEnabled seems to not have an equivalent in web // buildingsEnabled seems to not have an equivalent in web // padding seems to behave differently in web than mobile. You can't move UI elements in web. -gmaps.MapOptions _rawOptionsToGmapsOptions(Map rawOptions) { - gmaps.MapOptions options = gmaps.MapOptions(); +gmaps.MapOptions _rawOptionsToGmapsOptions(Map rawOptions) { + final gmaps.MapOptions options = gmaps.MapOptions(); if (_mapTypeToMapTypeId.containsKey(rawOptions['mapType'])) { options.mapTypeId = _mapTypeToMapTypeId[rawOptions['mapType']]; } if (rawOptions['minMaxZoomPreference'] != null) { + final List minMaxPreference = + rawOptions['minMaxZoomPreference']! as List; options - ..minZoom = rawOptions['minMaxZoomPreference'][0] - ..maxZoom = rawOptions['minMaxZoomPreference'][1]; + ..minZoom = minMaxPreference[0] as num? + ..maxZoom = minMaxPreference[1] as num?; } if (rawOptions['cameraTargetBounds'] != null) { @@ -74,11 +76,11 @@ gmaps.MapOptions _rawOptionsToGmapsOptions(Map rawOptions) { } if (rawOptions['zoomControlsEnabled'] != null) { - options.zoomControl = rawOptions['zoomControlsEnabled']; + options.zoomControl = rawOptions['zoomControlsEnabled'] as bool?; } if (rawOptions['styles'] != null) { - options.styles = rawOptions['styles']; + options.styles = rawOptions['styles'] as List?; } if (rawOptions['scrollGesturesEnabled'] == false || @@ -110,37 +112,42 @@ gmaps.MapOptions _applyInitialPosition( } // Extracts the status of the traffic layer from the rawOptions map. -bool _isTrafficLayerEnabled(Map rawOptions) { - return rawOptions['trafficEnabled'] ?? false; +bool _isTrafficLayerEnabled(Map rawOptions) { + return rawOptions['trafficEnabled'] as bool? ?? false; } // The keys we'd expect to see in a serialized MapTypeStyle JSON object. -final _mapStyleKeys = { +final Set _mapStyleKeys = { 'elementType', 'featureType', 'stylers', }; // Checks if the passed in Map contains some of the _mapStyleKeys. -bool _isJsonMapStyle(Map value) { +bool _isJsonMapStyle(Map value) { return _mapStyleKeys.intersection(value.keys.toSet()).isNotEmpty; } // Converts an incoming JSON-encoded Style info, into the correct gmaps array. List _mapStyles(String? mapStyleJson) { - List styles = []; + List styles = []; if (mapStyleJson != null) { - styles = json - .decode(mapStyleJson, reviver: (key, value) { - if (value is Map && _isJsonMapStyle(value)) { - return gmaps.MapTypeStyle() - ..elementType = value['elementType'] - ..featureType = value['featureType'] - ..stylers = - (value['stylers'] as List).map((e) => jsify(e)).toList(); - } - return value; - }) + styles = (json.decode(mapStyleJson, reviver: (Object? key, Object? value) { + if (value is Map && _isJsonMapStyle(value as Map)) { + List stylers = []; + if (value['stylers'] != null) { + stylers = (value['stylers']! as List) + .map((Object? e) => e != null ? jsify(e) : null) + .toList(); + } + return gmaps.MapTypeStyle() + ..elementType = value['elementType'] as String? + ..featureType = value['featureType'] as String? + ..stylers = stylers; + } + return value; + }) as List) + .where((Object? element) => element != null) .cast() .toList(); // .toList calls are required so the JS API understands the underlying data structure. @@ -173,12 +180,12 @@ CameraPosition _gmViewportToCameraPosition(gmaps.GMap map) { } // Convert plugin objects to gmaps.Options objects -// TODO: Move to their appropriate objects, maybe make these copy constructors: +// TODO(ditman): Move to their appropriate objects, maybe make them copy constructors? // Marker.fromMarker(anotherMarker, moreOptions); gmaps.InfoWindowOptions? _infoWindowOptionsFromMarker(Marker marker) { - final markerTitle = marker.infoWindow.title ?? ''; - final markerSnippet = marker.infoWindow.snippet ?? ''; + final String markerTitle = marker.infoWindow.title ?? ''; + final String markerSnippet = marker.infoWindow.snippet ?? ''; // If both the title and snippet of an infowindow are empty, we don't really // want an infowindow... @@ -200,6 +207,13 @@ gmaps.InfoWindowOptions? _infoWindowOptionsFromMarker(Marker marker) { if (markerSnippet.isNotEmpty) { final HtmlElement snippet = DivElement() ..className = 'infowindow-snippet' + // `sanitizeHtml` is used to clean the (potential) user input from (potential) + // XSS attacks through the contents of the marker InfoWindow. + // See: https://pub.dev/documentation/sanitize_html/latest/sanitize_html/sanitizeHtml.html + // See: b/159137885, b/159598165 + // The NodeTreeSanitizer.trusted just tells setInnerHtml to leave the output + // of `sanitizeHtml` untouched. + // ignore: unsafe_html ..setInnerHtml( sanitizeHtml(markerSnippet), treeSanitizer: NodeTreeSanitizer.trusted, @@ -210,7 +224,7 @@ gmaps.InfoWindowOptions? _infoWindowOptionsFromMarker(Marker marker) { return gmaps.InfoWindowOptions() ..content = container ..zIndex = marker.zIndex; - // TODO: Compute the pixelOffset of the infoWindow, from the size of the Marker, + // TODO(ditman): Compute the pixelOffset of the infoWindow, from the size of the Marker, // and the marker.infoWindow.anchor property. } @@ -221,7 +235,7 @@ gmaps.MarkerOptions _markerOptionsFromMarker( Marker marker, gmaps.Marker? currentMarker, ) { - final iconConfig = marker.icon.toJson() as List; + final List iconConfig = marker.icon.toJson() as List; gmaps.Icon? icon; if (iconConfig != null) { @@ -231,19 +245,24 @@ gmaps.MarkerOptions _markerOptionsFromMarker( // already encoded in the iconConfig[1] icon = gmaps.Icon() - ..url = ui.webOnlyAssetManager.getAssetUrl(iconConfig[1]); + ..url = ui.webOnlyAssetManager.getAssetUrl(iconConfig[1]! as String); // iconConfig[3] may contain the [width, height] of the image, if passed! if (iconConfig.length >= 4 && iconConfig[3] != null) { - final size = gmaps.Size(iconConfig[3][0], iconConfig[3][1]); + final List rawIconSize = iconConfig[3]! as List; + final gmaps.Size size = gmaps.Size( + rawIconSize[0] as num?, + rawIconSize[1] as num?, + ); icon ..size = size ..scaledSize = size; } } else if (iconConfig[0] == 'fromBytes') { // Grab the bytes, and put them into a blob - List bytes = iconConfig[1]; - final blob = Blob([bytes]); // Let the browser figure out the encoding + final List bytes = iconConfig[1]! as List; + // Create a Blob from bytes, but let the browser figure out the encoding + final Blob blob = Blob([bytes]); icon = gmaps.Icon()..url = Url.createObjectUrlFromBlob(blob); } } @@ -253,18 +272,18 @@ gmaps.MarkerOptions _markerOptionsFromMarker( marker.position.latitude, marker.position.longitude, ) - ..title = sanitizeHtml(marker.infoWindow.title ?? "") + ..title = sanitizeHtml(marker.infoWindow.title ?? '') ..zIndex = marker.zIndex ..visible = marker.visible ..opacity = marker.alpha ..draggable = marker.draggable ..icon = icon; - // TODO: Compute anchor properly, otherwise infowindows attach to the wrong spot. + // TODO(ditman): Compute anchor properly, otherwise infowindows attach to the wrong spot. // Flat and Rotation are not supported directly on the web. } gmaps.CircleOptions _circleOptionsFromCircle(Circle circle) { - final circleOptions = gmaps.CircleOptions() + final gmaps.CircleOptions circleOptions = gmaps.CircleOptions() ..strokeColor = _getCssColor(circle.strokeColor) ..strokeOpacity = _getCssOpacity(circle.strokeColor) ..strokeWeight = circle.strokeWidth @@ -279,28 +298,25 @@ gmaps.CircleOptions _circleOptionsFromCircle(Circle circle) { gmaps.PolygonOptions _polygonOptionsFromPolygon( gmaps.GMap googleMap, Polygon polygon) { - List path = []; - polygon.points.forEach((point) { - path.add(_latLngToGmLatLng(point)); - }); - final polygonDirection = _isPolygonClockwise(path); - List> paths = [path]; - int holeIndex = 0; - polygon.holes.forEach((hole) { - List holePath = - hole.map((point) => _latLngToGmLatLng(point)).toList(); - if (_isPolygonClockwise(holePath) == polygonDirection) { - holePath = holePath.reversed.toList(); - if (kDebugMode) { - print( - 'Hole [$holeIndex] in Polygon [${polygon.polygonId.value}] has been reversed.' - ' Ensure holes in polygons are "wound in the opposite direction to the outer path."' - ' More info: https://github.com/flutter/flutter/issues/74096'); - } - } - paths.add(holePath); - holeIndex++; - }); + // Convert all points to GmLatLng + final List path = + polygon.points.map(_latLngToGmLatLng).toList(); + + final bool isClockwisePolygon = _isPolygonClockwise(path); + + final List> paths = >[path]; + + for (int i = 0; i < polygon.holes.length; i++) { + final List hole = polygon.holes[i]; + final List correctHole = _ensureHoleHasReverseWinding( + hole, + isClockwisePolygon, + holeId: i, + polygonId: polygon.polygonId, + ); + paths.add(correctHole); + } + return gmaps.PolygonOptions() ..paths = paths ..strokeColor = _getCssColor(polygon.strokeColor) @@ -313,6 +329,27 @@ gmaps.PolygonOptions _polygonOptionsFromPolygon( ..geodesic = polygon.geodesic; } +List _ensureHoleHasReverseWinding( + List hole, + bool polyIsClockwise, { + required int holeId, + required PolygonId polygonId, +}) { + List holePath = hole.map(_latLngToGmLatLng).toList(); + final bool holeIsClockwise = _isPolygonClockwise(holePath); + + if (holeIsClockwise == polyIsClockwise) { + holePath = holePath.reversed.toList(); + if (kDebugMode) { + print('Hole [$holeId] in Polygon [${polygonId.value}] has been reversed.' + ' Ensure holes in polygons are "wound in the opposite direction to the outer path."' + ' More info: https://github.com/flutter/flutter/issues/74096'); + } + } + + return holePath; +} + /// Calculates the direction of a given Polygon /// based on: https://stackoverflow.com/a/1165943 /// @@ -325,8 +362,8 @@ gmaps.PolygonOptions _polygonOptionsFromPolygon( /// the `path` is a transformed version of [Polygon.points] or each of the /// [Polygon.holes], guaranteeing that `lat` and `lng` can be accessed with `!`. bool _isPolygonClockwise(List path) { - var direction = 0.0; - for (var i = 0; i < path.length; i++) { + double direction = 0.0; + for (int i = 0; i < path.length; i++) { direction = direction + ((path[(i + 1) % path.length].lat - path[i].lat) * (path[(i + 1) % path.length].lng + path[i].lng)); @@ -336,10 +373,8 @@ bool _isPolygonClockwise(List path) { gmaps.PolylineOptions _polylineOptionsFromPolyline( gmaps.GMap googleMap, Polyline polyline) { - List paths = []; - polyline.points.forEach((point) { - paths.add(_latLngToGmLatLng(point)); - }); + final List paths = + polyline.points.map(_latLngToGmLatLng).toList(); return gmaps.PolylineOptions() ..path = paths @@ -358,40 +393,50 @@ gmaps.PolylineOptions _polylineOptionsFromPolyline( // Translates a [CameraUpdate] into operations on a [gmaps.GMap]. void _applyCameraUpdate(gmaps.GMap map, CameraUpdate update) { - final json = update.toJson() as List; + final List json = update.toJson() as List; switch (json[0]) { case 'newCameraPosition': - map.heading = json[1]['bearing']; - map.zoom = json[1]['zoom']; - map.panTo(gmaps.LatLng(json[1]['target'][0], json[1]['target'][1])); - map.tilt = json[1]['tilt']; + map.heading = json[1]['bearing'] as num?; + map.zoom = json[1]['zoom'] as num?; + map.panTo( + gmaps.LatLng( + json[1]['target'][0] as num?, + json[1]['target'][1] as num?, + ), + ); + map.tilt = json[1]['tilt'] as num?; break; case 'newLatLng': - map.panTo(gmaps.LatLng(json[1][0], json[1][1])); + map.panTo(gmaps.LatLng(json[1][0] as num?, json[1][1] as num?)); break; case 'newLatLngZoom': - map.zoom = json[2]; - map.panTo(gmaps.LatLng(json[1][0], json[1][1])); + map.zoom = json[2] as num?; + map.panTo(gmaps.LatLng(json[1][0] as num?, json[1][1] as num?)); break; case 'newLatLngBounds': - map.fitBounds(gmaps.LatLngBounds( - gmaps.LatLng(json[1][0][0], json[1][0][1]), - gmaps.LatLng(json[1][1][0], json[1][1][1]))); + map.fitBounds( + gmaps.LatLngBounds( + gmaps.LatLng(json[1][0][0] as num?, json[1][0][1] as num?), + gmaps.LatLng(json[1][1][0] as num?, json[1][1][1] as num?), + ), + ); // padding = json[2]; // Needs package:google_maps ^4.0.0 to adjust the padding in fitBounds break; case 'scrollBy': - map.panBy(json[1], json[2]); + map.panBy(json[1] as num?, json[2] as num?); break; case 'zoomBy': gmaps.LatLng? focusLatLng; - double zoomDelta = json[1] ?? 0; + final double zoomDelta = json[1] as double? ?? 0; // Web only supports integer changes... - int newZoomDelta = zoomDelta < 0 ? zoomDelta.floor() : zoomDelta.ceil(); + final int newZoomDelta = + zoomDelta < 0 ? zoomDelta.floor() : zoomDelta.ceil(); if (json.length == 3) { // With focus try { - focusLatLng = _pixelToLatLng(map, json[2][0], json[2][1]); + focusLatLng = + _pixelToLatLng(map, json[2][0] as int, json[2][1] as int); } catch (e) { // https://github.com/a14n/dart-google-maps/issues/87 // print('Error computing new focus LatLng. JS Error: ' + e.toString()); @@ -409,7 +454,7 @@ void _applyCameraUpdate(gmaps.GMap map, CameraUpdate update) { map.zoom = (map.zoom ?? 0) - 1; break; case 'zoomTo': - map.zoom = json[1]; + map.zoom = json[1] as num?; break; default: throw UnimplementedError('Unimplemented CameraMove: ${json[0]}.'); @@ -418,9 +463,9 @@ void _applyCameraUpdate(gmaps.GMap map, CameraUpdate update) { // original JS by: Byron Singh (https://stackoverflow.com/a/30541162) gmaps.LatLng _pixelToLatLng(gmaps.GMap map, int x, int y) { - final bounds = map.bounds; - final projection = map.projection; - final zoom = map.zoom; + final gmaps.LatLngBounds? bounds = map.bounds; + final gmaps.Projection? projection = map.projection; + final num? zoom = map.zoom; assert( bounds != null, 'Map Bounds required to compute LatLng of screen x/y.'); @@ -429,15 +474,15 @@ gmaps.LatLng _pixelToLatLng(gmaps.GMap map, int x, int y) { assert(zoom != null, 'Current map zoom level required to compute LatLng of screen x/y'); - final ne = bounds!.northEast; - final sw = bounds.southWest; + final gmaps.LatLng ne = bounds!.northEast; + final gmaps.LatLng sw = bounds.southWest; - final topRight = projection!.fromLatLngToPoint!(ne)!; - final bottomLeft = projection.fromLatLngToPoint!(sw)!; + final gmaps.Point topRight = projection!.fromLatLngToPoint!(ne)!; + final gmaps.Point bottomLeft = projection.fromLatLngToPoint!(sw)!; - final scale = 1 << (zoom!.toInt()); // 2 ^ zoom + final int scale = 1 << (zoom!.toInt()); // 2 ^ zoom - final point = + final gmaps.Point point = gmaps.Point((x / scale) + bottomLeft.x!, (y / scale) + topRight.y!); return projection.fromPointToLatLng!(point)!; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart index edf47764f346..b7e902014281 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart @@ -11,6 +11,43 @@ typedef DebugCreateMapFunction = gmaps.GMap Function( /// Encapsulates a [gmaps.GMap], its events, and where in the DOM it's rendered. class GoogleMapController { + /// Initializes the GMap, and the sub-controllers related to it. Wires events. + GoogleMapController({ + required int mapId, + required StreamController> streamController, + required CameraPosition initialCameraPosition, + Set markers = const {}, + Set polygons = const {}, + Set polylines = const {}, + Set circles = const {}, + Map mapOptions = const {}, + }) : _mapId = mapId, + _streamController = streamController, + _initialCameraPosition = initialCameraPosition, + _markers = markers, + _polygons = polygons, + _polylines = polylines, + _circles = circles, + _rawMapOptions = mapOptions { + _circlesController = CirclesController(stream: _streamController); + _polygonsController = PolygonsController(stream: _streamController); + _polylinesController = PolylinesController(stream: _streamController); + _markersController = MarkersController(stream: _streamController); + + // Register the view factory that will hold the `_div` that holds the map in the DOM. + // The `_div` needs to be created outside of the ViewFactory (and cached!) so we can + // use it to create the [gmaps.GMap] in the `init()` method of this class. + _div = DivElement() + ..id = _getViewType(mapId) + ..style.width = '100%' + ..style.height = '100%'; + + ui.platformViewRegistry.registerViewFactory( + _getViewType(mapId), + (int viewId) => _div, + ); + } + // The internal ID of the map. Used to broadcast events, DOM IDs and everything where a unique ID is needed. final int _mapId; @@ -51,14 +88,14 @@ class GoogleMapController { gmaps.GMap? _googleMap; // The StreamController used by this controller and the geometry ones. - final StreamController _streamController; + final StreamController> _streamController; /// The StreamController for the events of this Map. Only for integration testing. @visibleForTesting - StreamController get stream => _streamController; + StreamController> get stream => _streamController; /// The Stream over which this controller broadcasts events. - Stream get events => _streamController.stream; + Stream> get events => _streamController.stream; // Geometry controllers, for different features of the map. CirclesController? _circlesController; @@ -71,46 +108,6 @@ class GoogleMapController { // Keeps track if the map is moving or not. bool _mapIsMoving = false; - /// Initializes the GMap, and the sub-controllers related to it. Wires events. - GoogleMapController({ - required int mapId, - required StreamController streamController, - required CameraPosition initialCameraPosition, - Set markers = const {}, - Set polygons = const {}, - Set polylines = const {}, - Set circles = const {}, - Set tileOverlays = const {}, - Set> gestureRecognizers = - const >{}, - Map mapOptions = const {}, - }) : _mapId = mapId, - _streamController = streamController, - _initialCameraPosition = initialCameraPosition, - _markers = markers, - _polygons = polygons, - _polylines = polylines, - _circles = circles, - _rawMapOptions = mapOptions { - _circlesController = CirclesController(stream: this._streamController); - _polygonsController = PolygonsController(stream: this._streamController); - _polylinesController = PolylinesController(stream: this._streamController); - _markersController = MarkersController(stream: this._streamController); - - // Register the view factory that will hold the `_div` that holds the map in the DOM. - // The `_div` needs to be created outside of the ViewFactory (and cached!) so we can - // use it to create the [gmaps.GMap] in the `init()` method of this class. - _div = DivElement() - ..id = _getViewType(mapId) - ..style.width = '100%' - ..style.height = '100%'; - - ui.platformViewRegistry.registerViewFactory( - _getViewType(mapId), - (int viewId) => _div, - ); - } - /// Overrides certain properties to install mocks defined during testing. @visibleForTesting void debugSetOverrides({ @@ -161,12 +158,12 @@ class GoogleMapController { /// Failure to call this method would result in the GMap not rendering at all, /// and most of the public methods on this class no-op'ing. void init() { - var options = _rawOptionsToGmapsOptions(_rawMapOptions); + gmaps.MapOptions options = _rawOptionsToGmapsOptions(_rawMapOptions); // Initial position can only to be set here! options = _applyInitialPosition(_initialCameraPosition, options); // Create the map... - final map = _createMap(_div, options); + final gmaps.GMap map = _createMap(_div, options); _googleMap = map; _attachMapEvents(map); @@ -185,23 +182,23 @@ class GoogleMapController { // Funnels map gmap events into the plugin's stream controller. void _attachMapEvents(gmaps.GMap map) { - map.onTilesloaded.first.then((event) { + map.onTilesloaded.first.then((void _) { // Report the map as ready to go the first time the tiles load _streamController.add(WebMapReadyEvent(_mapId)); }); - map.onClick.listen((event) { + map.onClick.listen((gmaps.IconMouseEvent event) { assert(event.latLng != null); _streamController.add( MapTapEvent(_mapId, _gmLatLngToLatLng(event.latLng!)), ); }); - map.onRightclick.listen((event) { + map.onRightclick.listen((gmaps.MapMouseEvent event) { assert(event.latLng != null); _streamController.add( MapLongPressEvent(_mapId, _gmLatLngToLatLng(event.latLng!)), ); }); - map.onBoundsChanged.listen((event) { + map.onBoundsChanged.listen((void _) { if (!_mapIsMoving) { _mapIsMoving = true; _streamController.add(CameraMoveStartedEvent(_mapId)); @@ -210,7 +207,7 @@ class GoogleMapController { CameraMoveEvent(_mapId, _gmViewportToCameraPosition(map)), ); }); - map.onIdle.listen((event) { + map.onIdle.listen((void _) { _mapIsMoving = false; _streamController.add(CameraIdleEvent(_mapId)); }); @@ -243,15 +240,15 @@ class GoogleMapController { // Renders the initial sets of geometry. void _renderInitialGeometry({ - Set markers = const {}, - Set circles = const {}, - Set polygons = const {}, - Set polylines = const {}, + Set markers = const {}, + Set circles = const {}, + Set polygons = const {}, + Set polylines = const {}, }) { assert( _controllersBoundToMap, - 'Geometry controllers must be bound to a map before any geometry can ' + - 'be added to them. Ensure _attachGeometryControllers is called first.'); + 'Geometry controllers must be bound to a map before any geometry can ' + 'be added to them. Ensure _attachGeometryControllers is called first.'); // The above assert will only succeed if the controllers have been bound to a map // in the [_attachGeometryControllers] method, which ensures that all these @@ -280,13 +277,14 @@ class GoogleMapController { void updateRawOptions(Map optionsUpdate) { assert(_googleMap != null, 'Cannot update options on a null map.'); - final newOptions = _mergeRawOptions(optionsUpdate); + final Map newOptions = _mergeRawOptions(optionsUpdate); _setOptions(_rawOptionsToGmapsOptions(newOptions)); _setTrafficLayer(_googleMap!, _isTrafficLayerEnabled(newOptions)); } // Sets new [gmaps.MapOptions] on the wrapped map. + // ignore: use_setters_to_change_properties void _setOptions(gmaps.MapOptions options) { _googleMap?.options = options; } @@ -309,9 +307,11 @@ class GoogleMapController { Future getVisibleRegion() async { assert(_googleMap != null, 'Cannot get the visible region of a null map.'); - return _gmLatLngBoundsTolatLngBounds( - await _googleMap!.bounds ?? _nullGmapsLatLngBounds, - ); + final gmaps.LatLngBounds bounds = + await Future.value(_googleMap!.bounds) ?? + _nullGmapsLatLngBounds; + + return _gmLatLngBoundsTolatLngBounds(bounds); } /// Returns the [ScreenCoordinate] for a given viewport [LatLng]. @@ -319,7 +319,8 @@ class GoogleMapController { assert(_googleMap != null, 'Cannot get the screen coordinates with a null map.'); - final point = toScreenLocation(_googleMap!, _latLngToGmLatLng(latLng)); + final gmaps.Point point = + toScreenLocation(_googleMap!, _latLngToGmLatLng(latLng)); return ScreenCoordinate(x: point.x!.toInt(), y: point.y!.toInt()); } @@ -424,8 +425,8 @@ class GoogleMapController { } } -/// An event fired when a [mapId] on web is interactive. -class WebMapReadyEvent extends MapEvent { +/// A MapEvent event fired when a [mapId] on web is interactive. +class WebMapReadyEvent extends MapEvent { /// Build a WebMapReady Event for the map represented by `mapId`. WebMapReadyEvent(int mapId) : super(mapId, null); } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart index 47bfdc7bba15..043952d176a0 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart @@ -14,23 +14,24 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { } // A cache of map controllers by map Id. - Map _mapById = Map(); + Map _mapById = {}; /// Allows tests to inject controllers without going through the buildView flow. @visibleForTesting + // ignore: use_setters_to_change_properties void debugSetMapById(Map mapById) { _mapById = mapById; } // Convenience getter for a stream of events filtered by their mapId. - Stream _events(int mapId) => _map(mapId).events; + Stream> _events(int mapId) => _map(mapId).events; // Convenience getter for a map controller by its mapId. GoogleMapController _map(int mapId) { - final controller = _mapById[mapId]; + final GoogleMapController? controller = _mapById[mapId]; assert(controller != null, 'Maps cannot be retrieved before calling buildView!'); - return controller; + return controller!; } @override @@ -134,7 +135,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { String? mapStyle, { required int mapId, }) async { - _map(mapId).updateRawOptions({ + _map(mapId).updateRawOptions({ 'styles': _mapStyles(mapStyle), }); } @@ -303,13 +304,13 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { }) { // Bail fast if we've already rendered this map ID... if (_mapById[creationId]?.widget != null) { - return _mapById[creationId].widget; + return _mapById[creationId]!.widget!; } - final StreamController controller = - StreamController.broadcast(); + final StreamController> controller = + StreamController>.broadcast(); - final mapController = GoogleMapController( + final GoogleMapController mapController = GoogleMapController( initialCameraPosition: initialCameraPosition, mapId: creationId, streamController: controller, @@ -322,7 +323,10 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { _mapById[creationId] = mapController; - mapController.events.whereType().first.then((event) { + mapController.events + .whereType() + .first + .then((WebMapReadyEvent event) { assert(creationId == event.mapId, 'Received WebMapReadyEvent for the wrong map'); // Notify the plugin now that there's a fully initialized controller. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart index c4cd40f43323..9d607e9bbc6a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart @@ -6,14 +6,6 @@ part of google_maps_flutter_web; /// The `MarkerController` class wraps a [gmaps.Marker], how it handles events, and its associated (optional) [gmaps.InfoWindow] widget. class MarkerController { - gmaps.Marker? _marker; - - final bool _consumeTapEvents; - - final gmaps.InfoWindow? _infoWindow; - - bool _infoWindowShown = false; - /// Creates a `MarkerController`, which wraps a [gmaps.Marker] object, its `onTap`/`onDrag` behavior, and its associated [gmaps.InfoWindow]. MarkerController({ required gmaps.Marker marker, @@ -27,12 +19,12 @@ class MarkerController { _infoWindow = infoWindow, _consumeTapEvents = consumeTapEvents { if (onTap != null) { - marker.onClick.listen((event) { + marker.onClick.listen((gmaps.MapMouseEvent event) { onTap.call(); }); } if (onDragStart != null) { - marker.onDragstart.listen((event) { + marker.onDragstart.listen((gmaps.MapMouseEvent event) { if (marker != null) { marker.position = event.latLng; } @@ -40,7 +32,7 @@ class MarkerController { }); } if (onDrag != null) { - marker.onDrag.listen((event) { + marker.onDrag.listen((gmaps.MapMouseEvent event) { if (marker != null) { marker.position = event.latLng; } @@ -48,7 +40,7 @@ class MarkerController { }); } if (onDragEnd != null) { - marker.onDragend.listen((event) { + marker.onDragend.listen((gmaps.MapMouseEvent event) { if (marker != null) { marker.position = event.latLng; } @@ -57,6 +49,14 @@ class MarkerController { } } + gmaps.Marker? _marker; + + final bool _consumeTapEvents; + + final gmaps.InfoWindow? _infoWindow; + + bool _infoWindowShown = false; + /// Returns `true` if this Controller will use its own `onTap` handler to consume events. bool get consumeTapEvents => _consumeTapEvents; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart index 542a48bcb707..1a712b109677 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart @@ -6,17 +6,17 @@ part of google_maps_flutter_web; /// This class manages a set of [MarkerController]s associated to a [GoogleMapController]. class MarkersController extends GeometryController { + /// Initialize the cache. The [StreamController] comes from the [GoogleMapController], and is shared with other controllers. + MarkersController({ + required StreamController> stream, + }) : _streamController = stream, + _markerIdToController = {}; + // A cache of [MarkerController]s indexed by their [MarkerId]. final Map _markerIdToController; // The stream over which markers broadcast their events - StreamController _streamController; - - /// Initialize the cache. The [StreamController] comes from the [GoogleMapController], and is shared with other controllers. - MarkersController({ - required StreamController stream, - }) : _streamController = stream, - _markerIdToController = Map(); + final StreamController> _streamController; /// Returns the cache of [MarkerController]s. Test only. @visibleForTesting @@ -34,32 +34,35 @@ class MarkersController extends GeometryController { return; } - final infoWindowOptions = _infoWindowOptionsFromMarker(marker); + final gmaps.InfoWindowOptions? infoWindowOptions = + _infoWindowOptionsFromMarker(marker); gmaps.InfoWindow? gmInfoWindow; if (infoWindowOptions != null) { gmInfoWindow = gmaps.InfoWindow(infoWindowOptions); // Google Maps' JS SDK does not have a click event on the InfoWindow, so // we make one... - if (infoWindowOptions.content is HtmlElement) { - final content = infoWindowOptions.content as HtmlElement; + if (infoWindowOptions.content != null && + infoWindowOptions.content is HtmlElement) { + final HtmlElement content = infoWindowOptions.content! as HtmlElement; content.onClick.listen((_) { _onInfoWindowTap(marker.markerId); }); } } - final currentMarker = _markerIdToController[marker.markerId]?.marker; + final gmaps.Marker? currentMarker = + _markerIdToController[marker.markerId]?.marker; - final populationOptions = _markerOptionsFromMarker(marker, currentMarker); - gmaps.Marker gmMarker = gmaps.Marker(populationOptions); - gmMarker.map = googleMap; - MarkerController controller = MarkerController( + final gmaps.MarkerOptions markerOptions = + _markerOptionsFromMarker(marker, currentMarker); + final gmaps.Marker gmMarker = gmaps.Marker(markerOptions)..map = googleMap; + final MarkerController controller = MarkerController( marker: gmMarker, infoWindow: gmInfoWindow, consumeTapEvents: marker.consumeTapEvents, onTap: () { - this.showMarkerInfoWindow(marker.markerId); + showMarkerInfoWindow(marker.markerId); _onMarkerTap(marker.markerId); }, onDragStart: (gmaps.LatLng latLng) { @@ -81,13 +84,15 @@ class MarkersController extends GeometryController { } void _changeMarker(Marker marker) { - MarkerController? markerController = _markerIdToController[marker.markerId]; + final MarkerController? markerController = + _markerIdToController[marker.markerId]; if (markerController != null) { - final markerOptions = _markerOptionsFromMarker( + final gmaps.MarkerOptions markerOptions = _markerOptionsFromMarker( marker, markerController.marker, ); - final infoWindow = _infoWindowOptionsFromMarker(marker); + final gmaps.InfoWindowOptions? infoWindow = + _infoWindowOptionsFromMarker(marker); markerController.update( markerOptions, newInfoWindowContent: infoWindow?.content as HtmlElement?, @@ -113,7 +118,7 @@ class MarkersController extends GeometryController { /// See also [hideMarkerInfoWindow] and [isInfoWindowShown]. void showMarkerInfoWindow(MarkerId markerId) { _hideAllMarkerInfoWindow(); - MarkerController? markerController = _markerIdToController[markerId]; + final MarkerController? markerController = _markerIdToController[markerId]; markerController?.showInfoWindow(); } @@ -121,7 +126,7 @@ class MarkersController extends GeometryController { /// /// See also [showMarkerInfoWindow] and [isInfoWindowShown]. void hideMarkerInfoWindow(MarkerId markerId) { - MarkerController? markerController = _markerIdToController[markerId]; + final MarkerController? markerController = _markerIdToController[markerId]; markerController?.hideInfoWindow(); } @@ -129,7 +134,7 @@ class MarkersController extends GeometryController { /// /// See also [showMarkerInfoWindow] and [hideMarkerInfoWindow]. bool isInfoWindowShown(MarkerId markerId) { - MarkerController? markerController = _markerIdToController[markerId]; + final MarkerController? markerController = _markerIdToController[markerId]; return markerController?.infoWindowShown ?? false; } @@ -172,8 +177,10 @@ class MarkersController extends GeometryController { void _hideAllMarkerInfoWindow() { _markerIdToController.values - .where((controller) => - controller == null ? false : controller.infoWindowShown) - .forEach((controller) => controller.hideInfoWindow()); + .where((MarkerController? controller) => + controller?.infoWindowShown ?? false) + .forEach((MarkerController controller) { + controller.hideInfoWindow(); + }); } } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart index 9921d2ff3876..719eeeecdb43 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart @@ -6,10 +6,6 @@ part of google_maps_flutter_web; /// The `PolygonController` class wraps a [gmaps.Polygon] and its `onTap` behavior. class PolygonController { - gmaps.Polygon? _polygon; - - final bool _consumeTapEvents; - /// Creates a `PolygonController` that wraps a [gmaps.Polygon] object and its `onTap` behavior. PolygonController({ required gmaps.Polygon polygon, @@ -18,12 +14,16 @@ class PolygonController { }) : _polygon = polygon, _consumeTapEvents = consumeTapEvents { if (onTap != null) { - polygon.onClick.listen((event) { + polygon.onClick.listen((gmaps.PolyMouseEvent event) { onTap.call(); }); } } + gmaps.Polygon? _polygon; + + final bool _consumeTapEvents; + /// Returns the wrapped [gmaps.Polygon]. Only used for testing. @visibleForTesting gmaps.Polygon? get polygon => _polygon; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart index 8a9643156351..12e378cfc59c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart @@ -6,17 +6,17 @@ part of google_maps_flutter_web; /// This class manages a set of [PolygonController]s associated to a [GoogleMapController]. class PolygonsController extends GeometryController { + /// Initializes the cache. The [StreamController] comes from the [GoogleMapController], and is shared with other controllers. + PolygonsController({ + required StreamController> stream, + }) : _streamController = stream, + _polygonIdToController = {}; + // A cache of [PolygonController]s indexed by their [PolygonId]. final Map _polygonIdToController; // The stream over which polygons broadcast events - StreamController _streamController; - - /// Initializes the cache. The [StreamController] comes from the [GoogleMapController], and is shared with other controllers. - PolygonsController({ - required StreamController stream, - }) : _streamController = stream, - _polygonIdToController = Map(); + final StreamController> _streamController; /// Returns the cache of [PolygonController]s. Test only. @visibleForTesting @@ -27,9 +27,7 @@ class PolygonsController extends GeometryController { /// Wraps each Polygon into its corresponding [PolygonController]. void addPolygons(Set polygonsToAdd) { if (polygonsToAdd != null) { - polygonsToAdd.forEach((polygon) { - _addPolygon(polygon); - }); + polygonsToAdd.forEach(_addPolygon); } } @@ -38,10 +36,11 @@ class PolygonsController extends GeometryController { return; } - final populationOptions = _polygonOptionsFromPolygon(googleMap, polygon); - gmaps.Polygon gmPolygon = gmaps.Polygon(populationOptions); - gmPolygon.map = googleMap; - PolygonController controller = PolygonController( + final gmaps.PolygonOptions polygonOptions = + _polygonOptionsFromPolygon(googleMap, polygon); + final gmaps.Polygon gmPolygon = gmaps.Polygon(polygonOptions) + ..map = googleMap; + final PolygonController controller = PolygonController( polygon: gmPolygon, consumeTapEvents: polygon.consumeTapEvents, onTap: () { @@ -53,26 +52,27 @@ class PolygonsController extends GeometryController { /// Updates a set of [Polygon] objects with new options. void changePolygons(Set polygonsToChange) { if (polygonsToChange != null) { - polygonsToChange.forEach((polygonToChange) { - _changePolygon(polygonToChange); - }); + polygonsToChange.forEach(_changePolygon); } } void _changePolygon(Polygon polygon) { - PolygonController? polygonController = + final PolygonController? polygonController = _polygonIdToController[polygon.polygonId]; polygonController?.update(_polygonOptionsFromPolygon(googleMap, polygon)); } /// Removes a set of [PolygonId]s from the cache. void removePolygons(Set polygonIdsToRemove) { - polygonIdsToRemove.forEach((polygonId) { - final PolygonController? polygonController = - _polygonIdToController[polygonId]; - polygonController?.remove(); - _polygonIdToController.remove(polygonId); - }); + polygonIdsToRemove.forEach(_removePolygon); + } + + // Removes a polygon and its controller by its [PolygonId]. + void _removePolygon(PolygonId polygonId) { + final PolygonController? polygonController = + _polygonIdToController[polygonId]; + polygonController?.remove(); + _polygonIdToController.remove(polygonId); } // Handle internal events diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart index eb4b6d88b503..428bb7fce016 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart @@ -6,10 +6,6 @@ part of google_maps_flutter_web; /// The `PolygonController` class wraps a [gmaps.Polyline] and its `onTap` behavior. class PolylineController { - gmaps.Polyline? _polyline; - - final bool _consumeTapEvents; - /// Creates a `PolylineController` that wraps a [gmaps.Polyline] object and its `onTap` behavior. PolylineController({ required gmaps.Polyline polyline, @@ -18,12 +14,16 @@ class PolylineController { }) : _polyline = polyline, _consumeTapEvents = consumeTapEvents { if (onTap != null) { - polyline.onClick.listen((event) { + polyline.onClick.listen((gmaps.PolyMouseEvent event) { onTap.call(); }); } } + gmaps.Polyline? _polyline; + + final bool _consumeTapEvents; + /// Returns the wrapped [gmaps.Polyline]. Only used for testing. @visibleForTesting gmaps.Polyline? get line => _polyline; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart index 695b29554c04..2d3f1618b42c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart @@ -6,17 +6,17 @@ part of google_maps_flutter_web; /// This class manages a set of [PolylinesController]s associated to a [GoogleMapController]. class PolylinesController extends GeometryController { + /// Initializes the cache. The [StreamController] comes from the [GoogleMapController], and is shared with other controllers. + PolylinesController({ + required StreamController> stream, + }) : _streamController = stream, + _polylineIdToController = {}; + // A cache of [PolylineController]s indexed by their [PolylineId]. final Map _polylineIdToController; // The stream over which polylines broadcast their events - StreamController _streamController; - - /// Initializes the cache. The [StreamController] comes from the [GoogleMapController], and is shared with other controllers. - PolylinesController({ - required StreamController stream, - }) : _streamController = stream, - _polylineIdToController = Map(); + final StreamController> _streamController; /// Returns the cache of [PolylineContrller]s. Test only. @visibleForTesting @@ -26,9 +26,7 @@ class PolylinesController extends GeometryController { /// /// Wraps each line into its corresponding [PolylineController]. void addPolylines(Set polylinesToAdd) { - polylinesToAdd.forEach((polyline) { - _addPolyline(polyline); - }); + polylinesToAdd.forEach(_addPolyline); } void _addPolyline(Polyline polyline) { @@ -36,10 +34,11 @@ class PolylinesController extends GeometryController { return; } - final polylineOptions = _polylineOptionsFromPolyline(googleMap, polyline); - gmaps.Polyline gmPolyline = gmaps.Polyline(polylineOptions); - gmPolyline.map = googleMap; - PolylineController controller = PolylineController( + final gmaps.PolylineOptions polylineOptions = + _polylineOptionsFromPolyline(googleMap, polyline); + final gmaps.Polyline gmPolyline = gmaps.Polyline(polylineOptions) + ..map = googleMap; + final PolylineController controller = PolylineController( polyline: gmPolyline, consumeTapEvents: polyline.consumeTapEvents, onTap: () { @@ -50,13 +49,11 @@ class PolylinesController extends GeometryController { /// Updates a set of [Polyline] objects with new options. void changePolylines(Set polylinesToChange) { - polylinesToChange.forEach((polylineToChange) { - _changePolyline(polylineToChange); - }); + polylinesToChange.forEach(_changePolyline); } void _changePolyline(Polyline polyline) { - PolylineController? polylineController = + final PolylineController? polylineController = _polylineIdToController[polyline.polylineId]; polylineController ?.update(_polylineOptionsFromPolyline(googleMap, polyline)); @@ -64,12 +61,15 @@ class PolylinesController extends GeometryController { /// Removes a set of [PolylineId]s from the cache. void removePolylines(Set polylineIdsToRemove) { - polylineIdsToRemove.forEach((polylineId) { - final PolylineController? polylineController = - _polylineIdToController[polylineId]; - polylineController?.remove(); - _polylineIdToController.remove(polylineId); - }); + polylineIdsToRemove.forEach(_removePolyline); + } + + // Removes a polyline and its controller by its [PolylineId]. + void _removePolyline(PolylineId polylineId) { + final PolylineController? polylineController = + _polylineIdToController[polylineId]; + polylineController?.remove(); + _polylineIdToController.remove(polylineId); } // Handle internal events diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/shims/dart_ui.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/shims/dart_ui.dart index 5eacec5fe867..2b254a95b951 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/shims/dart_ui.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/shims/dart_ui.dart @@ -5,6 +5,6 @@ /// This file shims dart:ui in web-only scenarios, getting rid of the need to /// suppress analyzer warnings. -// TODO(flutter/flutter#55000) Remove this file once web-only dart:ui APIs +// TODO(ditman): Remove this file once web-only dart:ui APIs, https://github.com/flutter/flutter/issues/55000 // are exposed from a dedicated place. export 'dart_ui_fake.dart' if (dart.library.html) 'dart_ui_real.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/shims/dart_ui_fake.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/shims/dart_ui_fake.dart index f2862af8b704..8757ca22be17 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/shims/dart_ui_fake.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/shims/dart_ui_fake.dart @@ -7,13 +7,18 @@ import 'dart:html' as html; // Fake interface for the logic that this package needs from (web-only) dart:ui. // This is conditionally exported so the analyzer sees these methods as available. +// ignore_for_file: avoid_classes_with_only_static_members +// ignore_for_file: camel_case_types + /// Shim for web_ui engine.PlatformViewRegistry /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L62 class platformViewRegistry { /// Shim for registerViewFactory /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L72 - static registerViewFactory( - String viewTypeId, html.Element Function(int viewId) viewFactory) {} + static bool registerViewFactory( + String viewTypeId, html.Element Function(int viewId) viewFactory) { + return false; + } } /// Shim for web_ui engine.AssetManager. @@ -21,7 +26,7 @@ class platformViewRegistry { class webOnlyAssetManager { /// Shim for getAssetUrl. /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/src/engine/assets.dart#L45 - static getAssetUrl(String asset) {} + static String getAssetUrl(String asset) => ''; } /// Signature of callbacks that have no arguments and return no data. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/third_party/to_screen_location/to_screen_location.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/third_party/to_screen_location/to_screen_location.dart index 2963111fdcc3..fc25b18b43ec 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/third_party/to_screen_location/to_screen_location.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/third_party/to_screen_location/to_screen_location.dart @@ -29,9 +29,9 @@ import 'package:google_maps/google_maps.dart' as gmaps; /// /// See: https://developers.google.com/maps/documentation/android-sdk/reference/com/google/android/libraries/maps/Projection#public-point-toscreenlocation-latlng-location gmaps.Point toScreenLocation(gmaps.GMap map, gmaps.LatLng coords) { - final zoom = map.zoom; - final bounds = map.bounds; - final projection = map.projection; + final num? zoom = map.zoom; + final gmaps.LatLngBounds? bounds = map.bounds; + final gmaps.Projection? projection = map.projection; assert( bounds != null, 'Map Bounds required to compute screen x/y of LatLng.'); @@ -40,15 +40,15 @@ gmaps.Point toScreenLocation(gmaps.GMap map, gmaps.LatLng coords) { assert(zoom != null, 'Current map zoom level required to compute screen x/y of LatLng.'); - final ne = bounds!.northEast; - final sw = bounds.southWest; + final gmaps.LatLng ne = bounds!.northEast; + final gmaps.LatLng sw = bounds.southWest; - final topRight = projection!.fromLatLngToPoint!(ne)!; - final bottomLeft = projection.fromLatLngToPoint!(sw)!; + final gmaps.Point topRight = projection!.fromLatLngToPoint!(ne)!; + final gmaps.Point bottomLeft = projection.fromLatLngToPoint!(sw)!; - final scale = 1 << (zoom!.toInt()); // 2 ^ zoom + final int scale = 1 << (zoom!.toInt()); // 2 ^ zoom - final worldPoint = projection.fromLatLngToPoint!(coords)!; + final gmaps.Point worldPoint = projection.fromLatLngToPoint!(coords)!; return gmaps.Point( ((worldPoint.x! - bottomLeft.x!) * scale).toInt(), diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/types.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/types.dart index ff980eb4c34b..84c66264db7b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/types.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/types.dart @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; import 'package:google_maps/google_maps.dart' as gmaps; +import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; /// A void function that handles a [gmaps.LatLng] as a parameter. /// diff --git a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml index ca8af82dc2db..b46f7561e78d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter_web description: Web platform implementation of google_maps_flutter repository: https://github.com/flutter/plugins/tree/main/packages/google_maps_flutter/google_maps_flutter_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 0.3.2+2 +version: 0.3.3 environment: sdk: ">=2.12.0 <3.0.0" @@ -21,9 +21,8 @@ dependencies: sdk: flutter flutter_web_plugins: sdk: flutter + google_maps: ^6.1.0 google_maps_flutter_platform_interface: ^2.1.2 - google_maps: ^5.2.0 - pedantic: ^1.10.0 sanitize_html: ^2.0.0 stream_transform: ^2.0.0 diff --git a/script/configs/custom_analysis.yaml b/script/configs/custom_analysis.yaml index 23b7f7bbc35b..bcd7d37dea0a 100644 --- a/script/configs/custom_analysis.yaml +++ b/script/configs/custom_analysis.yaml @@ -12,4 +12,3 @@ # TODO(ecosystem): Remove everything from this list. See: # https://github.com/flutter/flutter/issues/76229 - google_maps_flutter/google_maps_flutter_platform_interface -- google_maps_flutter/google_maps_flutter_web From 5273dab486eec097cf82a075425ddfcc7fba3932 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 19 May 2022 21:23:11 -0400 Subject: [PATCH 590/600] [path_provider] Fix integration tests on macOS (#5773) --- packages/path_provider/path_provider/CHANGELOG.md | 4 ++++ .../example/integration_test/path_provider_test.dart | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/path_provider/path_provider/CHANGELOG.md b/packages/path_provider/path_provider/CHANGELOG.md index 990021671801..0567b7e7ae33 100644 --- a/packages/path_provider/path_provider/CHANGELOG.md +++ b/packages/path_provider/path_provider/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Fixes integration test permission issue on recent versions of macOS. + ## 2.0.10 * Removes unnecessary imports. diff --git a/packages/path_provider/path_provider/example/integration_test/path_provider_test.dart b/packages/path_provider/path_provider/example/integration_test/path_provider_test.dart index 27493a76012c..4f5a1873bfb8 100644 --- a/packages/path_provider/path_provider/example/integration_test/path_provider_test.dart +++ b/packages/path_provider/path_provider/example/integration_test/path_provider_test.dart @@ -92,7 +92,14 @@ void main() { expect(result, throwsA(isInstanceOf())); } else { final Directory? result = await getDownloadsDirectory(); - _verifySampleFile(result, 'downloads'); + if (Platform.isMacOS) { + // On recent versions of macOS, actually using the downloads directory + // requires a user prompt, so will fail on CI. Instead, just check that + // it returned a path with the expected directory name. + expect(result?.path, endsWith('Downloads')); + } else { + _verifySampleFile(result, 'downloads'); + } } }); } From f4ca73237db83f6ce207269d3000f58ad5b39000 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 20 May 2022 09:28:07 -0400 Subject: [PATCH 591/600] [video_player] Fix order-dependent tests (#5672) --- packages/video_player/video_player/CHANGELOG.md | 4 ++++ .../video_player/video_player/test/video_player_test.dart | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 1dbc4f73e9c2..bfefb9902768 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Fixes order-dependent unit tests. + ## 2.4.2 * Minor fixes for new analysis options. diff --git a/packages/video_player/video_player/test/video_player_test.dart b/packages/video_player/video_player/test/video_player_test.dart index 9eca7f921acb..83a49882ee9a 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -958,6 +958,13 @@ void main() { }); group('VideoPlayerOptions', () { + late FakeVideoPlayerPlatform fakeVideoPlayerPlatform; + + setUp(() { + fakeVideoPlayerPlatform = FakeVideoPlayerPlatform(); + VideoPlayerPlatform.instance = fakeVideoPlayerPlatform; + }); + test('setMixWithOthers', () async { final VideoPlayerController controller = VideoPlayerController.file( File(''), From aaaa81c67f8f3a2ecb4ee0e2f00929787c446464 Mon Sep 17 00:00:00 2001 From: Ahmed Ashour Date: Fri, 20 May 2022 17:28:11 +0200 Subject: [PATCH 592/600] [google_sign_in] Suppress `deprecation` warnings (#5049) --- packages/google_sign_in/google_sign_in_android/CHANGELOG.md | 4 ++++ .../io/flutter/plugins/googlesignin/GoogleSignInPlugin.java | 3 +++ packages/google_sign_in/google_sign_in_android/pubspec.yaml | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md index 90069d05f442..67fb08f66e17 100644 --- a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 5.2.8 + +* Suppresses `deprecation` warnings (for using Android V1 embedding). + ## 5.2.7 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java index a1237f0013a1..9bee8fad38d3 100644 --- a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java +++ b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java @@ -76,6 +76,7 @@ public void initInstance( } @VisibleForTesting + @SuppressWarnings("deprecation") public void setUpRegistrar(PluginRegistry.Registrar registrar) { delegate.setUpRegistrar(registrar); } @@ -267,6 +268,7 @@ public static class Delegate implements IDelegate, PluginRegistry.ActivityResult private final Context context; // Only set registrar for v1 embedder. + @SuppressWarnings("deprecation") private PluginRegistry.Registrar registrar; // Only set activity for v2 embedder. Always access activity from getActivity() method. private Activity activity; @@ -282,6 +284,7 @@ public Delegate(Context context, GoogleSignInWrapper googleSignInWrapper) { this.googleSignInWrapper = googleSignInWrapper; } + @SuppressWarnings("deprecation") public void setUpRegistrar(PluginRegistry.Registrar registrar) { this.registrar = registrar; registrar.addActivityResultListener(this); diff --git a/packages/google_sign_in/google_sign_in_android/pubspec.yaml b/packages/google_sign_in/google_sign_in_android/pubspec.yaml index d9b272320694..c4c30cd3e545 100644 --- a/packages/google_sign_in/google_sign_in_android/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_android/pubspec.yaml @@ -2,7 +2,7 @@ name: google_sign_in_android description: Android implementation of the google_sign_in plugin. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.2.7 +version: 5.2.8 environment: sdk: ">=2.14.0 <3.0.0" From 20669497505c1799c11443c938228a593b33cd48 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 20 May 2022 21:43:09 -0400 Subject: [PATCH 593/600] Roll Flutter from a4a8e73bce15 to 1e1f4bcfb561 (65 revisions) (#5795) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 822ceaa4a9b1..a77e197cf665 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -a4a8e73bce152ab39d6ae839ca51e447f87293fa +1e1f4bcfb56105912a90ffa1a3a317dfb724612b From 7f349dc9915cc29c4af6f185da5923933db19955 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 21 May 2022 05:18:08 -0400 Subject: [PATCH 594/600] Roll Flutter from 1e1f4bcfb561 to 1e10ceceb036 (6 revisions) (#5799) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index a77e197cf665..10fe2447e178 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -1e1f4bcfb56105912a90ffa1a3a317dfb724612b +1e10ceceb036fbd24774937b924741d0cdee1d6f From ea56ff0d58153ec5e2ac8b84d113c73607d6d2af Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 21 May 2022 08:28:08 -0400 Subject: [PATCH 595/600] Roll Flutter from 1e10ceceb036 to 6aaabf6d5695 (1 revision) (#5800) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 10fe2447e178..5821f2e5f8b2 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -1e10ceceb036fbd24774937b924741d0cdee1d6f +6aaabf6d569543b982b1d23ac3bf81d3b2667751 From 9459549522e8a2e740d17a3d66bb216843b01b58 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 21 May 2022 10:48:08 -0400 Subject: [PATCH 596/600] Roll Flutter from 6aaabf6d5695 to 4654fd011d13 (2 revisions) (#5802) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 5821f2e5f8b2..d8897d8f416f 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -6aaabf6d569543b982b1d23ac3bf81d3b2667751 +4654fd011d13c1d49d5fc21b60a5916ca8c4f2e3 From 407f4e30ab777229864c1122b1cbc4527cdc3db4 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 21 May 2022 14:58:08 -0400 Subject: [PATCH 597/600] Roll Flutter from 4654fd011d13 to b8b0c807bbe5 (1 revision) (#5803) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index d8897d8f416f..1e26cf421732 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -4654fd011d13c1d49d5fc21b60a5916ca8c4f2e3 +b8b0c807bbe54718c35d17c8df8afc712cef7ec2 From f7052de7bd0c5fcb87cb22acad7e69c2aa62e262 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 21 May 2022 16:03:08 -0400 Subject: [PATCH 598/600] Roll Flutter from b8b0c807bbe5 to de7c23e7e963 (1 revision) (#5804) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 1e26cf421732..1938f7f8a7c9 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b8b0c807bbe54718c35d17c8df8afc712cef7ec2 +de7c23e7e963265b6b10e785f26c83ef4df9168a From e360a5fe4da8756a8a59c13dc45a6a4decefa6b6 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 21 May 2022 17:33:07 -0400 Subject: [PATCH 599/600] Roll Flutter from de7c23e7e963 to ec20ea80ad98 (1 revision) (#5805) --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 1938f7f8a7c9..7e7222ac9f95 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -de7c23e7e963265b6b10e785f26c83ef4df9168a +ec20ea80ad98007bd022648168e7b3cc38b2e665 From ef9effec50812aaae75f189c43ec768ae7b3a0b6 Mon Sep 17 00:00:00 2001 From: Hwanseok Barth Kang Date: Mon, 23 May 2022 15:53:05 +0900 Subject: [PATCH 600/600] [google_sign_in, in_app_purchase_android] Add availability to mock models (#5642) --- AUTHORS | 1 + .../google_sign_in/CHANGELOG.md | 3 +- .../google_sign_in/lib/google_sign_in.dart | 2 +- .../google_sign_in/pubspec.yaml | 2 +- .../test/google_sign_in_test.dart | 5 +- .../in_app_purchase_android/CHANGELOG.md | 4 ++ .../sku_details_wrapper.dart | 6 +-- .../in_app_purchase_android/pubspec.yaml | 3 +- .../sku_details_wrapper_test.dart | 54 +++++++++++++++++++ 9 files changed, 72 insertions(+), 8 deletions(-) diff --git a/AUTHORS b/AUTHORS index 41a31ed39cd4..31402c79d54a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -67,3 +67,4 @@ Rahul Raj <64.rahulraj@gmail.com> Daniel Roek TheOneWithTheBraid Rulong Chen(陈汝龙) +Hwanseok Kang diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index 86a3b565da21..ad21ee245359 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 5.3.2 +* Enables mocking models by changing overridden operator == parameter type from `dynamic` to `Object`. * Updates tests to use a mock platform instead of relying on default method channel implementation internals. * Removes example workaround to build for arm64 iOS simulators. diff --git a/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart b/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart index 228f34b651c5..3c62e0e1a655 100644 --- a/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart +++ b/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart @@ -129,7 +129,7 @@ class GoogleSignInAccount implements GoogleIdentity { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { if (identical(this, other)) { return true; } diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index 2a287b1cccd4..6862a5560f83 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Google Sign-In, a secure authentication system for signing in with a Google account on Android and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.3.1 +version: 5.3.2 environment: diff --git a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart index 61acfd81bf09..2bc51b63d111 100644 --- a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart +++ b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart @@ -9,9 +9,12 @@ import 'package:google_sign_in/google_sign_in.dart'; import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; - import 'google_sign_in_test.mocks.dart'; +/// Verify that [GoogleSignInAccount] can be mocked even though it's unused +// ignore: must_be_immutable +class MockGoogleSignInAccount extends Mock implements GoogleSignInAccount {} + @GenerateMocks([GoogleSignInPlatform]) void main() { late MockGoogleSignInPlatform mockPlatform; diff --git a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md index 18284f29a2d9..05c2952fc615 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.2+6 + +* Enables mocking models by changing overridden operator == parameter type from `dynamic` to `Object`. + ## 0.2.2+5 * Minor fixes for new analysis options. diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/sku_details_wrapper.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/sku_details_wrapper.dart index 07f9d8f29abf..1c5c2d1fcee9 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/sku_details_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/sku_details_wrapper.dart @@ -135,7 +135,7 @@ class SkuDetailsWrapper { final int originalPriceAmountMicros; @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { if (other.runtimeType != runtimeType) { return false; } @@ -203,7 +203,7 @@ class SkuDetailsResponseWrapper { final List skuDetailsList; @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { if (other.runtimeType != runtimeType) { return false; } @@ -248,7 +248,7 @@ class BillingResultWrapper { final String? debugMessage; @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { if (other.runtimeType != runtimeType) { return false; } diff --git a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml index 277be296836c..1099c3a12bfa 100644 --- a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_android description: An implementation for the Android platform of the Flutter `in_app_purchase` plugin. This uses the Android BillingClient APIs. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.2.2+5 +version: 0.2.2+6 environment: sdk: ">=2.14.0 <3.0.0" @@ -28,4 +28,5 @@ dev_dependencies: flutter_test: sdk: flutter json_serializable: ^6.0.0 + mockito: ^5.1.0 test: ^1.16.0 diff --git a/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/sku_details_wrapper_test.dart b/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/sku_details_wrapper_test.dart index ecc399b27716..2d1436885427 100644 --- a/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/sku_details_wrapper_test.dart +++ b/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/sku_details_wrapper_test.dart @@ -125,6 +125,60 @@ void main() { expect(billingResult.debugMessage, kInvalidBillingResultErrorMessage); expect(billingResult.responseCode, BillingResponse.error); }); + + test('operator == of SkuDetailsWrapper works fine', () { + const SkuDetailsWrapper firstSkuDetailsInstance = SkuDetailsWrapper( + description: 'description', + freeTrialPeriod: 'freeTrialPeriod', + introductoryPrice: 'introductoryPrice', + introductoryPriceAmountMicros: 990000, + introductoryPriceCycles: 1, + introductoryPricePeriod: 'introductoryPricePeriod', + price: 'price', + priceAmountMicros: 1000, + priceCurrencyCode: 'priceCurrencyCode', + priceCurrencySymbol: r'$', + sku: 'sku', + subscriptionPeriod: 'subscriptionPeriod', + title: 'title', + type: SkuType.inapp, + originalPrice: 'originalPrice', + originalPriceAmountMicros: 1000, + ); + const SkuDetailsWrapper secondSkuDetailsInstance = SkuDetailsWrapper( + description: 'description', + freeTrialPeriod: 'freeTrialPeriod', + introductoryPrice: 'introductoryPrice', + introductoryPriceAmountMicros: 990000, + introductoryPriceCycles: 1, + introductoryPricePeriod: 'introductoryPricePeriod', + price: 'price', + priceAmountMicros: 1000, + priceCurrencyCode: 'priceCurrencyCode', + priceCurrencySymbol: r'$', + sku: 'sku', + subscriptionPeriod: 'subscriptionPeriod', + title: 'title', + type: SkuType.inapp, + originalPrice: 'originalPrice', + originalPriceAmountMicros: 1000, + ); + expect(firstSkuDetailsInstance == secondSkuDetailsInstance, isTrue); + }); + + test('operator == of BillingResultWrapper works fine', () { + const BillingResultWrapper firstBillingResultInstance = + BillingResultWrapper( + responseCode: BillingResponse.ok, + debugMessage: 'debugMessage', + ); + const BillingResultWrapper secondBillingResultInstance = + BillingResultWrapper( + responseCode: BillingResponse.ok, + debugMessage: 'debugMessage', + ); + expect(firstBillingResultInstance == secondBillingResultInstance, isTrue); + }); }); }

1^0g&nudQpNa6KwWbN3B6Bt+&wMeRX$m3 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 z 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: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ΞS~L5wT?=j7uFs-qZ!u^ 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

@8dpw4Zk}4* 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

1^0g&nudQpNa6KwWbN3B6Bt+&wMeRX$m3 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 z z$GterOhr=|@Ki|@L;@KisoNVMEUO>|hH>j*{*SeGJ!jUuPXX^w)$iHsTK6EcVvJoj zSpXYUo7=m0ldC2Yq8?Xp+FI~^l*1kOtO+InR10H0!QGFXm5&G1*q=^8r!>|Crr|1OB&)9fdou;zI^Y+oENgN7&u^!VLE zBbJI^sYPu`e!Y5f>TMRs(dw82Iq_qh1ELXdoaoY&DeH`6R)i+c#5`Z?wqd{_8kBXl zkc42N#K1P&>ME&QQ~_wIB>)OKE=h!Pl>`*A_UN|cJ}z?e_vfZKmN;(n+ver*4fO7a zDMl>`8lI}{F?ywqwpUe+Jk`^C&3$%~NP~Nv(pzS8T#x}^E2s&e%GLnJ{R3M+4dzHp z>@6Br`}blflKET;U&UM=Vz?*Yk=EF@M4fLM{~sgqc(YyegJ?bLmo>xw7$2n5p&fE< z?ny6HAcUzl%6@()sxp`2;qABcsyL~KItbsp;CY~;d!(G)L{U`MX$gXb69Aa5b*@fn zs__?E5{pUz4=+N%;(2QeX?G(lp?yLOdF(B6*b@VEiH9xAm2A%Ko#d%@U3-eneX(o` zIq{Zdr7eA7H-Sk{T(ef1C$q!D8)hBNL6qV$lv3Etg)Ny)T`O5DQ~)xhQdLWQX`CIeX| z1!V@ptw}OlaRZd2V4arGdg*|wdzg4#eNn)o*I#irP0Fu|B~8(_7+_bZ3&$)(@|rD? zcI_*;o&N;M2=dTNPl})!6#tB-J-B^!r_ZGRUkh~Q;BB9}&MjC!0^)F|Pb52QwYsO8 z6J#M-?*wHrn1xWF$bv8ujkNCaYz0UNKurL8HG%<+jQu64`nUz^&82E1$3k2Vt-RA! z*o1bk)(s_xeqzxq+9Owm`6bOQHRJiLHEhcdgap*tSwUN5VLo+LRlD<^gQSd(c~X|p zTSaKOeT1r{*Qmi14a>}+)L)T(J8`{=%02F&rTply$X? zjvrve^%-;@1Z_nF5 zko?|(@=u)~TSi~K_!LZdKOh&H4#vW?gH7^qxw6wB!;ABp+9OHZEQ;HsIZS??K`eXq zI7z27Kxc zAO4@I`aOo)qCUH^;;y9t%-bi%@5`OpK2+RWrMD@xOEx&oO){ay*fxoIklAOpR!N$M zY{^47y4nfQiuu2!z+j<4(pvmmS>)L)qw(rmWu42e_jo-B2g$g`y1xuyF;@l#rge=k z9^m@RSy#c;tAi<6#8=St!d~#pfP@4HP+nT`*(n)$_Og*@<1g>i*pzPE!W}2CPOGJU zcExLby_}F1qgAKQ7TLA*HU_WxW{c1imT}||aYn-{ zmUF;;)R0t0Ms!&E`{h)=O9pDjg|VldFbEoEb1A0~oa7NJ#R^EudIjR?Zi?Nc7J2B1 z!Gw;K1uF#X*PAd4PLXxrC@JrGNr*H9%(B#FEUAkbId}v??#E&l>USK1Le+!RI!LhQ zy8sx{)*k3Ry>;E)miRAk6M#NSL$mI#d1E17uue={avQ+uwT;R(}5K zUS2z!eUwsoTcT^{u#wO=)BfyaGCZ3l5_%$hE~pEhVd0$a!`kJbIP!dEYW;9(1fQBk z5&S&TmuR%l7Ad$&6~ps0-Q!}@VNm7Z7eW%W8XK&wslJ~ZESb?wso{t z@HM7vF{nT*L{@I&{yEu=Y_Ti8Yj&b2qR>i{xa}LpuKZHLx5dW6 z#vPkGm$K<-pJtDo>ptGglEsq7io|O~q*Ua{QpN{M;xW450g*shu80A@b;!4jOkDDa z7Cf4epiUnrmpYM%92LR`JtJL|lb#8l#8uMMv2Bpepod|rjP1|>L2_OO z5^hE;<<1{)+t;U1Nol^Xb382SViHS+gH0VqT6x_{+EiApbivODwphCYv@8-Z#+fFD zOLKvv`#beycF;2~rOUuT@TM}}f*7=XxF1v$| z4iUY_b53eEn@@%9{2I(qM^@r^Qg>gYJ3_+Ow@lLvUsaAR6r>zh!3kQm2!Vnk4knoP zAof<*`BZyrMmzg3cQPi00k932>Hkc*!X07uqtF}U#aCQDf{b`ghMEA=H}|9o3TT{_Oir~@X#P) zWKyOnX31>TdRu5ysNn6$RZ$Ei4-ynL5?0nuDiCghriNxC3v5CVB{o6DTWkX>0;`(o zRty-{=kS~zL~xDn)Pfa6EbfwwOJ*k!g7hE!dWO%D2xO*R@*ZL2t`Rt5Ecoq=NUR4 z;aV}An2f={;2^rveyxvpE$yGNh|#dplnM2PvdWl{(@_{qm#g=f6$U3oJiD4^@5gsK zbTw-DM3pxkV;OfsTEG}3!bn<#5!-xcD zicECHUMbNm<5h+#`Hz?0HByg?S|M1ZBf=%-?fWW>t}z$;`PJoPNp(R!$$(ghcmFPZ ztu~svPm6DS#HWvWJW6WTzXSfPx ztr|?i4^zgYwp#$`eL^mV#i;b9zgF@5|8riC*uedvCG?NU1=j_x3#gFjjZyj@0bP`N z$h8hDTes2an3WY?fc9=s%rkASN$uyXlRitJ1+^flg;OI*$M9P4#bnbVDohA0>2?`V z6%$Qnq6nZc9Jw|yMT8Kr2@1R7bAQ0wc&~;1SL^Nc5Oq%r!SgJl6zaWlF`_pYObnDK zaKAY+;x=k)_X6W!=A#H5knGj#!c6Y!^ADJ75n`P~w<;*n!UCjII_SSH*_35r@Y6`q z!Kfjq0&|!PMBJk!D88ze+a>5)A7!rwfd`@H!RQ16Sg8;rHXe>#uuD#oDzQ-E&;^=8 zMJ5b3s!#{tW3$~m{Y^ce&FI`2%sselMvB1z%o=`^1a{fP-(rggAsUojmX63ns8ED) z2nY(QSF13TR}8cQ02!|!57GWeoTBrkDY;`iJT^PwHLXhblzDGl@ANoZH0slAkLz6# z)NSnU;c32!+FR}w1+Z#_$c>^9XTNzJbr-6voJaP7hHDR`la}^hY1OyU7sfqJ`vY1q zGqH2Kj4c?~+a4)kZiFVL^~&|Cj1TI$1$n|e4eZ{W0O&y=0N9Z35h7CkoH=Ut&UO@d zg)$O?l%&B8$O@3K=+XlL%wS2>&CJJbRwV!;DiCHG9EK$UIo}ebdR3kQ5X}ivfCh~r zB#u$#et(^O9(RLl*SU@hK-ETtpL}{0)!OjRR+3HSivrh`W+-EAgM98C>8iZS(Q*eQ zN0v<>$woeFC`OoKtD3;-u)0tIvsGloruSpYu~-<+){ONq4z5N*SUyMuC#;($dC4`2Ks*gnZw`c>8o%F)M>@ZOn4nVF99 z??_mc%hk0ADKtbC()$Q58~^}qGG&N_08jD!7+?G(xC8FB3tnx$3{8de6+3+9P&}yy z{~9wg9A2_MdyZ8c_68IF!@~#$=5h{~)X%+E^O}?y4 zA1k+#UeM4Y=oP&5J2#&DlJ>~wc?hEJA(-VNy#yOhY|#`w>+MaAMx2P!;qN{;^fwyS zDz1PtcB=@%6h>toe?2G|G9+4B2v2WrGym5>dm|~2 z;$n%-uMx9Z{$%A6Y^XYWBA6g4U8{nK%1zvNvMr?9$Gh!Tt+L>eDZ$tjZQ)r;M(|{c zVl@NxdPWZ>4*8ylV0Ve#s@}~5gKBSkfLl(eo#j;9z5R+5`c^ECFfObEsSv>$pzdXT zCa;=IqJy(oBD2J(@z^4M|0{)Wyf3hqiONraC)Etezxeh5IjFbF?&=+;*Jdk+poI?sHa4 z%EOzYlK=MS2E41dy>6*@U8B)Mhc&RNjEz%1;M$J$_|m^uM2ih07oA=w)=;OBOM!}R zeNIkLQf;Nf1`L_BP0n4KmyDI87vFi(OKv?iEi4s;U!KGRja?`N>n2^yhY2Z7 zwO)oN=y(ah>PlnUMEjC7k57r3;y05+SAMqlzABHpCI*&oqUxwOysZU&+x6(jE?69_ z%81&&7Lii)T1>GRQ^?5_yV>PQO;u065g=C=tz6?vmK~JjhO%Nt{^$=@4A9t?Vo{gN zgn{{lt*lSz7FSN~1P0?eqH{OD&pB3$qlOCl6;C_k*+?f|SJ09-MDsTmXV)?1t#PfL zbq)!t6%KkEe$iTc$)fNnmn6wM$&1S<>hii z36;=*E6YTi5vw@+TBO?k_L490tCDyy&NlSkk9C7@vEZk*T$Cg)Z;~P(=Gn=^nR*z{ z2?+%GP@gtr`Rn8DJ)~UWU^>2eR_At2=gJM6ZB-d&qO8nvS7v}iC_=%3!$J~hu#Dt` zan)R1d$-YNu&2fPK;wbMDej!M)?I58+JVF%TAGGeI?Q3M$f(&6NkEU#8SBV(`7Q`3 zBZK?ZZ1K|=e2l_oOdKT4*n}(khG5dTT{p3kLG<+rNn|2V{s+biuJy3dZZK4Vh9LaK z0sL)MvF>oV&7$vlh0t|aP|;;{xS{h5dByUZu`n6;NLd&xLsER6pE1R#lUI?9+YgJi zzN`+J%RkH_RoX~hmcnY@g|~KtbhZRfi?H-jJv>^PE?fl_WQ2>a?gu&Ilesd&+|co% zIK&|;{8;u{$?{)Kxx}MvW`ftI`^gr#I1?=sPMP$Ggoa_mTCjI3NR}7n}MbPdaEy6Lv%VlyEH|^jg7SKu=7|FDWD$7En}* zlMB~%!z3oc72~C)U?WH`7~riMW%T zt#$D=v^C9(qOwRywe(90QNah^PQHzW>CPYBZT7UH+)6%F1S^a|R!O~W7kV~@-UtTa zb(5ztX!zD6-xK*QRh30F+-_u8aAWl5^(^SrHX$PM1lI)b9@=)AGOYL%B@1wO) z&Ea;1^$TXVW1G+cD8UUZ&HKr}@T9<`n_P->xj$6q1h+&JX{9;uk!0#xVTjWm_*}{U z=)q5C1`+$Fy|TQ$lg6I|d(4wY!HWCU5~iRpJcGP=5VV`@x)cPp+u4pL?wt&#*MZBw z9-U!^xBwVnVrJv4uV%F0fh0)?Q)y^&^V^jHGoU+S@d8wlD8-e17i>|8czN0XXGd4RSe+!P=JbsJ?Tjtc<6BcO36mtGf} zX;t&_aUgc)+aRkhSd4I0mcvT&9g#f|X0Rcq4GZ+;kxhS|?NT-msjWb@~NaJrIlIGm`=OGS_8OkdW!DzI?;1HPH>|H&)p zUgqH53+!~#s_ker&x`?j(|cu+j^R8NqzOMLJrc|%>hM;71!XqB6Go~q=B#u}4e!83uok&{`2~#B+2yw{YG;Os4yyea%R00lMov329qaEt&ki!M`$CL z7sRLfhEST;q0HBnbLu%Xa0bkgVvvywRS8^)S#;086y-l`pcET=EgZm9ovx>C9<(f9 zIb>XxIZ_*=*T`)5BQWx(d1DBRbw=R#QO=AU^G0#1;}i{d4Lagu59I0m=_}zd0|=Tn zMuA4y1Caq_31I2=nNvV84)86yk#%W1qt`(^mg26HVyOpM`dgq4#8+3XQT_(1uBc zuNC*I(4q;TzzaRw zZo8dSg`>+@7kMO*Wb3<-H^D^TtAKcEA}{D-WL&%9`|yus;}Vyu?;5TGQkiU+>NPqK zyau8JdB!=3DkiTaYNS)7q2SC6Tqe4CZs<%p4@FS>8! z1X{(^jKx`y{A|N@C4@NYQq8d%5 zRJiImccv!DE%bn7!tdXM6g*Xre|Z(UbELmZO#PLXZJ1uxz7|TU@OX7`ILHeqN*O+%-u&A{cz6p(?WEuS6aky+5desCLv|rseQ@ z(-0oQ4cQqAf`W&b{Dyx{HZ#`^s=Kq_q~#g^cAa7)XRBUq?Pmd19OfMsz{Hd~!#dI` z2?ykJCDp=L^G52iH`@}m`6O%kV?EPr(-@nF#eKYNwN+~Yp7=@D)$ol>N z+Z8^&?EYE)rnm#A!iJ%3!3NPtAijbFuBZv$`dS~{w}Ze-ic=?H5|9rhV)ubVP>@x; z|NGAnw!rP%qf}jZt8S!1+@}Uq^N1k+!QsZ745a3`Ft8M9?YHqegyd$Bd@*Q3&6ZlH zN&6QF;`Jv68psUWyR_?8TStAAjNaX8Kh=$)gkgL_e`9>vZ~C`ZnQNwJ&gqmu>sn+$GFq3XvzVKA=&}^5({%_ zaOQS0n^IGsEmigu_=`)LkM^?3BDC zIP>mc_ED*J{9^}QfKm;x0BTw0-MZbniAe$>yS8z|TAa(XHxH;0ncYo}xKWCS6L4-u z)aNtlce~0jsEez|s00awtWl)PHNKDP(z(8jIR{ejon0D_sQSY>2*mi$o!~N);0p{c zp152PkL7&2*l$3nvS9aKlcsY!=Usd{^VyL4rR43yG*ogJ5gI&sNHaMj3y> zZZR_Rl}xCHbhqy>@f_-H;IHo`cFj6GwEf$>>s43G!%dlo)KwKf z*bfQUd0aR_QohWM%$jgV1)_B|bGmbPxJwKajDhg=)Q;$%1Cq>ce+?z!gNI!D0uRXfOvAIf?n-I5USXP}* z3Z!oH49>3?Q?&~hxMxv&D2!QvReZcP4~O+FL>(}7VQBnB#d8NtZt8QUFccWRP4bo( zFFfq$p<9iqecy?1&OFeQb;9h-o!MW83T*vh0WOC(&i^4jxIDPx?kPPfJV&3Wn*&tF z-CJ9pVXTks9}oP8{Lh%GdQ$_ZdBEd)K{F4A^D(g-Cp3z19tU zBJ;N>>{az+_Gv|&JE7Tq(olJW+Sz6hInZ7Q{%iu}}?FYhAA5zij zI{hc*CVdHd&HLCo6ilKPcZgdafs9d}vuveBHK3bWSeb8^OFYm3*Ey1Vj#=-BVmU5; zr9wxeX%Zd~ZZ8BzLJDcH*ZsfdH-y0RaK+nCRaK6r(%z zWT?s#MM*zb6E^I6H`llTd2u^so&EGD^r0gkYTGe_@$Dq58lzO+a!2@fZ7+^(S!n-b zAl)Ls1o4(ihbaG#RPgR5hG>z+Uea&>8Q2hvbz#|sich!%xm1B-m8DqNs*o;lIGh#+ zVWy(?s|b)3134H=MwV4`KcLUjdD7JHIo#5sTc8k5vHO$ww|knBwFMhd=uenRb(Bet z|1Kd0s_5@KI-g-t`N%uoosK)*^>vcAiLm{=31mph>2HkpJ_+K~Z8v*_ps@PPc3mon zVF#b|^j>68hCIhfXj??pEX;G3e4U#djFuIh-ULAj{0$d; z>cas(UG%ybS3o>&56GfT%a@qJ59E7J(wG;K=TgtPK!%&gva>d%#w0`ns9-!4Oo1|I z#h5-D`5GL*mvFB8Q05qW21&G`#?H;=qW)Rq7y2uCf$ zd7!gTnvk@=K2GQWr6aaVdiOA;fe&yg|5srJp{~Vf?memXx7@+I}HET_xsxY zLA6+RDrcD^Rv-uNMjx|2SW%AF!8vkFrN5Q}Py=>U-Iw>0U6%}iPEIhZ;&9POB@t4y zP|T-+mY=&-+su72qXbn5YgSVU51a?J=G3jmT7h2nVjOw4FYvy%FMQK}N3+fDa=)Iw8LTYtQS$0S9C$`Oo=NP!yP? z`yP+)qH}K;8fL~~;x1N;0e^EV7wfU>uA#CdmU*3$brR~J<&5(Bsgwl>eD{xtk4;re z(*r0S;$3a>bsQq(qoX&Oi(EwQL-W^*R*R2)JrSK*JQHin9b*@ltup*WSvW|ZH4vYX zxxn7j6f^HxrKg(3Oka2;-~-S~Ed1{Ft~jK|lbOX~FxzK{t{BIEKhF)9A3o@)q)<*m z} z0R)7WN%VXPT23&cnj|siM&+DPgkllC5J!$vt{m! zo+6m&g6G0{vL>2kbrjz~7EV>U$&ZLwnfDCwsg#KS>}pf=y&!z+L%>hucb1desf@N?Et%5{(WP>nRo3Cr_O}q7-x} z;PJ4mXpR@}ceI=P7xYK+{P7y1Z8 z76u$c+??aZ5M7nk8VkQ{W%;3#oEm8qH%|jAwT!n9M3%*4*y8{ahj4b#8rTn)Ylp%1 z5S36GIPHM1E_Y95edM|qPU=8>N$s06FKs2d>m2Fbpu6JLNkM~TPpItXmq%sdc(<7z z!I{_xeJIxf$t)-M&0|}O^?4WUoh>qF75dZsvd;^h^s1o%8w#1-L7;7z##o(jkS25D zBPIqx zz}sAb5xCU<-e+6FTj+w?r9>mvv-52l0)rHqx~{nz|0Gaz>kHr5KG1@_gs3&qNhS{vOpG!~`ZRssoNA(E!sno8HLr{gA%s`QX5?f0lZ z*XVl4Z^e|OFba294>^GZ?`TNlh8`LHtASA0G7{tbbv{D zY*I|+kpaG}*MrARGG2vI&WJBarQX{g;r?VfgMusQPNZ_THa%X7hRqbH<9*k^)G{Sy zIPC^TE1slVk-3DL07!BszC#I&#-%D@K=5rr-g?GjUEjb)P1%=Q+IO>1zxe%+c*;{I zs(mKXQ-N#Al-iyw00t*3d}{ySwIV98I!i66KQ^QcVjpWIEfyc7`Z2)Gt&0;{8$MDo zmjAH4h$G$n&-Zl8LP0hx*U?TUW$%+d=H}MkEc!$_>=ax?<+nJK{1wsHVSpVLW&qGy zN)(g_9(pn3HVauBY^bUg`OUW31Ku5p_kr2$YuAbp)@>P;>+Ctqd^DJdBFFm;khL}Z zso5Gs500&V56-UE*<#+%IvQQD?*UnEuZ`Fmmo0?FE6;Ho$vL@sF^(@d85+25dYlpO z!RhRa4JyJn^^nSkokc1iq>Pb}(%`sj1$NC!Uh?qR zlUsWvV$?7SsOW3#osXpej?QmH)|O3O@x;4V*FOA4iIBl#ly%||kNF?MN`p_4U_GNU zaK1^Zp3J{~cU|Y%?=apyH zs@Uy9Q~}G4R@3Ao$P(UVpHF(1>knXFAS=NHJgL>nq<2KO+z+_dH)t^dUffL238iHH zwWK7Pd}ddK2*|{Jj{vAR_ALe;`F_%V$b%BkARyh}kW8iHXG@Z&nI$BhI*Qg+EcO5* zis5<3V9L5a>o0jlgfv3Z)N}C=%hGL&PTZKI8!Xbh2bCG;L_tPY^G0j7Eh7M#-J}9c zMr3?>cY^-6T{_ea5ETx`5xrx_=T+eotTC+4ds~zHTF0*5;e2+;N@y-Y`oc9&DMDwQ z)}}pzI=l!|JR~E&=~SISks%8E*dffOSZbXT<3TV zK79t$InaCY)UD0D{|mp9h-l!pH(b++8t&U*F!~B)G$!OOBeL- z2pB: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ΞS~L5wT?=j7uFs-qZ!u^ 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